/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Tree Style Tab. * * The Initial Developer of the Original Code is YUKI "Piro" Hiroshi. * Portions created by the Initial Developer are Copyright (C) 2010-2015 * the Initial Developer. All Rights Reserved. * * Contributor(s): YUKI "Piro" Hiroshi * Tetsuharu OHZEKI * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ******/ const EXPORTED_SYMBOLS = ['AutoHideBrowser', 'AutoHideWindow']; const DEBUG = false; const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://treestyletab-modules/lib/inherit.jsm'); Cu.import('resource://treestyletab-modules/constants.js'); XPCOMUtils.defineLazyModuleGetter(this, 'utils', 'resource://treestyletab-modules/utils.js', 'TreeStyleTabUtils'); XPCOMUtils.defineLazyGetter(this, 'window', function() { Cu.import('resource://treestyletab-modules/lib/namespace.jsm'); return getNamespaceFor('piro.sakura.ne.jp'); }); XPCOMUtils.defineLazyGetter(this, 'prefs', function() { Cu.import('resource://treestyletab-modules/lib/prefs.js'); return window['piro.sakura.ne.jp'].prefs; }); const AutoHideConstants = Object.freeze(inherit(TreeStyleTabConstants, { kMODE : 'treestyletab-tabbar-autohide-mode', kMODE_DISABLED : 0, kMODE_HIDE : 1, kMODE_SHRINK : 2, kAUTOHIDE : 'treestyletab-tabbar-autohide', kSTATE : 'treestyletab-tabbar-autohide-state', kSTATE_HIDDEN : 'hidden', kSTATE_EXPANDED : 'expanded', kSTATE_SHRUNKEN : 'shrunken', kSHOWN_BY_UNKNOWN : 0, kSHOWN_BY_SHORTCUT : 1 << 0, kSHOWN_BY_MOUSEMOVE : 1 << 1, kSHOWN_BY_FEEDBACK : 1 << 2, kSHOWN_BY_ANY_REASON : (1 << 0) | (1 << 1) | (1 << 2), kSHOWHIDE_BY_START : 1 << 3, kSHOWHIDE_BY_END : 1 << 4, kSHOWHIDE_BY_POSITION_CHANGE : 1 << 5, kSHOWHIDE_BY_RESIZE : 1 << 6, kSHOWHIDE_BY_API : 1 << 8, kHIDDEN_BY_CLICK : 1 << 7, CLOSE_BUTTONS_ONLY_ON_CURRENT_TAB : 0, CLOSE_BUTTONS_ON_ALL_TABS : 1, CLOSE_BUTTONS_DISABLED : 2, CLOSE_BUTTONS_ON_TABBAR : 3, MOUSE_POSITION_UNKNOWN : 0, MOUSE_POSITION_OUTSIDE : (1 << 0), MOUSE_POSITION_INSIDE : (1 << 1), MOUSE_POSITION_NEAR : (1 << 2), MOUSE_POSITION_SENSITIVE : (1 << 1) | (1 << 2) })); function AutoHideBase(aTabBrowser) { } AutoHideBase.prototype = inherit(AutoHideConstants, { window : null, browser : null, treeStyleTab : null, getMode : function AHB_getMode(aTabBrowser) { var b = aTabBrowser || this.browser; var mode = b.getAttribute(this.kMODE); return mode ? parseInt(mode) : this.kMODE_DISABLED ; }, getModeForNormal : function AHB_getModeForNormal(aTabBrowser) { var b = aTabBrowser || this.browser; return parseInt(b.getAttribute(this.kMODE+'-normal') || this.lastNormalMode); }, getModeForFullScreen : function AHB_getModeForFullScreen(aTabBrowser) { var b = aTabBrowser || this.browser; return parseInt(b.getAttribute(this.kMODE+'-fullscreen') || this.lastFullscreenMode); }, get mode() /* PUBLIC API */ { return this.getMode(this.browser); }, set mode(aValue) { this.browser.setAttribute(this.kMODE, aValue); return aValue; }, get lastNormalMode() { var lastMode = this.treeStyleTab.getWindowValue(this.kMODE + '-normal'); if (lastMode !== '') return parseInt(lastMode); return utils.getTreePref('tabbar.autoHide.mode'); }, set lastNormalMode(aValue) { this.treeStyleTab.setWindowValue(this.kMODE + '-normal', aValue); return aValue; }, get lastFullscreenMode() { var lastMode = this.treeStyleTab.getWindowValue(this.kMODE + '-fullscreen'); if (lastMode !== '') return parseInt(lastMode); return utils.getTreePref('tabbar.autoHide.mode.fullscreen'); }, set lastFullscreenMode(aValue) { this.treeStyleTab.setWindowValue(this.kMODE + '-fullscreen', aValue); return aValue; } }); function AutoHideBrowser(aTabBrowser) { this.init(aTabBrowser); } AutoHideBrowser.prototype = inherit(AutoHideBase.prototype, { get state() { return this.browser.getAttribute(this.kSTATE) || this.kSTATE_EXPANDED; }, get expanded() { return this.state == this.kSTATE_EXPANDED; }, get shrunken() { return this.state == this.kSTATE_SHRUNKEN; }, get hidden() { return this.state == this.kSTATE_HIDDEN; }, get toggler() { return this.document.getAnonymousElementByAttribute(this.browser, 'class', this.treeStyleTab.kTABBAR_TOGGLER); }, updateMode : function AHB_updateMode(aNewMode) { if (aNewMode === undefined) aNewMode = this.mode; var suffix = this.treeStyleTab.isFullscreenAutoHide ? 'fullscreen' : 'normal' ; this.treeStyleTab.setWindowValue(this.kMODE + '-' + suffix, aNewMode); this.end(); // update internal property after the appearance of the tab bar is updated. var w = this.window; w.setTimeout(function(aSelf) { aSelf.mode = aNewMode; if (aSelf.mode != aSelf.kMODE_DISABLED) aSelf.start(); }, 0, this); }, togglerSize : 0, sensitiveArea : 7, contentAreaScreenEnabled : true, closeButtonsMode : -1, get XOffset() { var sv = this.treeStyleTab; switch (this.mode) { case this.kMODE_DISABLED: return 0; case this.kMODE_HIDE: let offset = this.width + this.splitterWidth; let resizer = this.resizer; if (sv.position == 'left') { offset += this.togglerSize; if (resizer) offset += resizer.boxObject.width; } else if (sv.position == 'right') { offset -= this.togglerSize; if (resizer) offset -= resizer.boxObject.width; } return offset; default: case this.kMODE_SHRINK: return this.expandedWidth - this.shrunkenWidth; } }, get YOffset() { return this.height; }, extraXOffset : 0, extraYOffset : 0, get currentXOffset() { var sv = this.treeStyleTab; return ( sv.position == 'left' && this.mode != this.kMODE_DISABLED && this.expanded ) ? this.XOffset : 0 ; }, get currentYOffset() { var sv = this.treeStyleTab; return ( sv.position == 'top' && this.mode != this.kMODE_DISABLED && this.expanded ) ? this.YOffset : 0 ; }, get tabbarWidth() { return this.expanded ? this.expandedWidth : this.shrunkenWidth ; }, set tabbarWidth(aValue) { if (this.expanded) return this.expandedWidth = aValue; else return this.shrunkenWidth = aValue; }, get expandedWidth() { var lastWidth = this.treeStyleTab.getWindowValue(this.kTABBAR_EXPANDED_WIDTH); return lastWidth === '' ? utils.getTreePref('tabbar.width') : parseInt(lastWidth); }, set expandedWidth(aValue) { var newWidth = this.treeStyleTab.calculateCorrectExpandedAndShrunkenWidth({ expanded : aValue, shrunken : this.shrunkenWidth }, 'expanded'); if (newWidth.corrected) { this.shrunkenWidth = newWidth.shrunken; aValue = newWidth.expanded; } this.treeStyleTab.setWindowValue(this.kTABBAR_EXPANDED_WIDTH, aValue); utils.setTreePref('tabbar.width', aValue); return aValue; }, get shrunkenWidth() { var lastWidth = this.treeStyleTab.getWindowValue(this.kTABBAR_SHRUNKEN_WIDTH); return lastWidth === '' ? utils.getTreePref('tabbar.shrunkenWidth') : parseInt(lastWidth); }, set shrunkenWidth(aValue) { var newWidth = this.treeStyleTab.calculateCorrectExpandedAndShrunkenWidth({ expanded : this.expandedWidth, shrunken : aValue }, 'shrunken'); if (newWidth.corrected) { this.expandedWidth = newWidth.expanded; aValue = newWidth.shrunken; } this.treeStyleTab.setWindowValue(this.kTABBAR_SHRUNKEN_WIDTH, aValue); utils.setTreePref('tabbar.shrunkenWidth', aValue); return aValue; }, resetWidth : function AHB_resetWidth() { this.expandedWidth = utils.getTreePref('tabbar.width.default'); this.shrunkenWidth = utils.getTreePref('tabbar.shrunkenWidth.default'); }, get screen() { return this.document.getElementById('treestyletab-autohide-content-area-screen'); }, get resizer() { return this.document.getElementById('treestyletab-tabbar-resizer-splitter'); }, start : function AHB_start(aReason) { if (this.enabled) return; this.enabled = true; aReason = aReason || 0; var sv = this.treeStyleTab; var b = this.browser; var w = this.window; this.screen.hidePopup(); sv.setTabbrowserAttribute(this.kSTATE, this.kSTATE_EXPANDED); if (!(aReason & this.kSHOWHIDE_BY_API)) { b.addEventListener('dragover', this, true); b.addEventListener('dragleave', this, true); sv.tabStripPlaceHolder.addEventListener('mousedown', this, true); sv.tabStripPlaceHolder.addEventListener('mouseup', this, true); sv.tabStrip.addEventListener('mousedown', this, true); sv.tabStrip.addEventListener('mouseup', this, true); if (this.shouldListenMouseMove) this.startListenMouseMove(); if (b == w.gBrowser && sv.shouldListenKeyEventsForAutoHide) w.TreeStyleTabService.startListenKeyEventsFor(sv.LISTEN_FOR_AUTOHIDE); this.userActionListening = true; this.notifyStatusToAllTabs(); } w.addEventListener(sv.kEVENT_TYPE_PRINT_PREVIEW_ENTERED, this, false); w.addEventListener(sv.kEVENT_TYPE_PRINT_PREVIEW_EXITED, this, false); this.updateTransparency(); this.showHideInternal(this.kSHOWHIDE_BY_START | aReason); b.treeStyleTab.fixTooNarrowTabbar(); }, end : function AHB_end() { if (!this.enabled) return; this.enabled = false; var sv = this.treeStyleTab; var b = this.browser; var w = this.window; this.show(this.kSHOWHIDE_BY_END); this.screen.hidePopup(); if (this.userActionListening) { b.removeEventListener('dragover', this, true); b.removeEventListener('dragleave', this, true); sv.tabStripPlaceHolder.removeEventListener('mousedown', this, true); sv.tabStripPlaceHolder.removeEventListener('mouseup', this, true); sv.tabStrip.removeEventListener('mousedown', this, true); sv.tabStrip.removeEventListener('mouseup', this, true); this.endListenMouseMove(); if (b == w.gBrowser) w.TreeStyleTabService.endListenKeyEventsFor(sv.LISTEN_FOR_AUTOHIDE); this.userActionListening = false; this.notifyStatusToAllTabs(); } w.removeEventListener(sv.kEVENT_TYPE_PRINT_PREVIEW_ENTERED, this, false); w.removeEventListener(sv.kEVENT_TYPE_PRINT_PREVIEW_EXITED, this, false); this.updateTransparency(); sv.removeTabbrowserAttribute(this.kAUTOHIDE); sv.removeTabbrowserAttribute(this.kSTATE); if (sv.isVertical) sv.setTabStripAttribute('width', this.widthFromMode); }, notifyStatusToAllTabs : function AHB_notifyStatusToAllTabs() { let tabs = this.treeStyleTab.getTabs(this.browser); tabs.forEach(this.notifyStatusToTab, this); }, notifyStatusToTab : function AHB_notifyStatusToTab(aTab) { aTab.__treestyletab__contentBridge.sendAsyncCommand(this.COMMAND_NOTIFY_AUTOHIDE_STATUS, { basicListening : this.mouseMoveListening || this.userActionListening, advanceListening : this.mouseMoveListening }); }, // fullscreen startForFullScreen : function AHB_startForFullScreen() { this.mode = this.getMode(); this.end(); this.mode = prefs.getPref('browser.fullscreen.autohide') ? this.getModeForFullScreen() : this.kMODE_DISABLED ; if (this.mode != this.kMODE_DISABLED) { this.start(); this.treeStyleTab.removeTabbrowserAttribute('moz-collapsed'); } }, endForFullScreen : function AHB_endForFullScreen() { this.mode = this.getModeForFullScreen(); this.end(); this.mode = utils.getTreePref('tabbar.autoHide.mode'); this.treeStyleTab.checkTabsIndentOverflow(); if (this.mode != this.kMODE_DISABLED) this.start(); }, // mousemove startListenMouseMove : function AHB_startListenMouseMove() { if (this.mouseMoveListening) return; this.screen.addEventListener('mousemove', this, true); this.treeStyleTab.tabStripPlaceHolder.addEventListener('mousemove', this, true); this.treeStyleTab.tabStrip.addEventListener('mousemove', this, true); this.toggler.addEventListener('mousemove', this, true); this.window.addEventListener('TabRemotenessChange', this, false); this.mouseMoveListening = true; this.notifyStatusToAllTabs(); }, endListenMouseMove : function AHB_endListenMouseMove() { if (!this.mouseMoveListening) return; this.screen.removeEventListener('mousemove', this, true); this.treeStyleTab.tabStripPlaceHolder.removeEventListener('mousemove', this, true); this.treeStyleTab.tabStrip.removeEventListener('mousemove', this, true); this.toggler.removeEventListener('mousemove', this, true); this.window.removeEventListener('TabRemotenessChange', this, false); this.mouseMoveListening = false; this.notifyStatusToAllTabs(); }, get shouldListenMouseMove() { return utils.getTreePref('tabbar.autoShow.mousemove'); }, get shouldListenKeyEventsForAutoHide() { return utils.getTreePref('tabbar.autoShow.accelKeyDown') || utils.getTreePref('tabbar.autoShow.tabSwitch'); }, showHideOnMouseMove : function AHB_showHideOnMouseMove(aCoordinates) { var position = this.getMousePosition(aCoordinates); if (DEBUG) { let humanReadablePosition = []; if (position & this.MOUSE_POSITION_OUTSIDE) humanReadablePosition.push('outside'); if (position & this.MOUSE_POSITION_INSIDE) humanReadablePosition.push('inside'); if (position & this.MOUSE_POSITION_NEAR) humanReadablePosition.push('near'); if (position & this.MOUSE_POSITION_SENSITIVE) humanReadablePosition.push('sensitive'); dump('showHideOnMouseMove: ' + '('+aCoordinates.screenX + ', ' + aCoordinates.screenY + ') => ' + humanReadablePosition.join(', ') + '\n'); } if (position == this.MOUSE_POSITION_UNKNOWN) return; this.cancelShowHideOnMouseMove(); this.showHideContentsAreaScreen(); var sv = this.treeStyleTab; var b = this.browser; var w = this.window; var shouldShow = position & this.MOUSE_POSITION_SENSITIVE; if (this.expanded) { // currently shown, let's hide it. if (shouldShow) { this.show(this.kSHOWN_BY_MOUSEMOVE); this.cancelDelayedShowForShortcut(); } else if ( !shouldShow && utils.getTreePref('tabbar.autoShow.mousemove') ) { this.showHideOnMouseMoveTimer = w.setTimeout( function(aSelf) { aSelf.cancelDelayedShowForShortcut(); aSelf.hide(aSelf.kSHOWN_BY_MOUSEMOVE); }, utils.getTreePref('tabbar.autoHide.delay'), this ); } } else if (shouldShow) { // currently shown, let's show it. this.showHideOnMouseMoveTimer = w.setTimeout( function(aSelf) { aSelf.cancelDelayedShowForShortcut(); aSelf.show(aSelf.kSHOWN_BY_MOUSEMOVE); }, utils.getTreePref('tabbar.autoHide.delay'), this ); } b = null; }, getMousePosition : function AHB_getMousePosition(aCoordinates) { var w = this.window; if ('gestureInProgress' in w && w.gestureInProgress) return this.MOUSE_POSITION_UNKNOWN; var sv = this.treeStyleTab; var b = this.browser; var pos = sv.position; var box = this.getContentsAreaBox(); var sensitiveArea = this.sensitiveArea; if (this.shrunken) { let clickable; let resizable = !sv.fixed; if (resizable & this.widthFromMode > 24 && (clickable = this.getNearestClickableBox(aCoordinates))) { /* For resizing of shrunken tab bar and clicking closeboxes, we have to shrink sensitive area. */ sensitiveArea = -(clickable.width + clickable.padding); } else if (resizable && this.resizer) sensitiveArea = -this.resizer.boxObject.width; else sensitiveArea = 0; } if ( pos == 'left' ? (aCoordinates.screenX > box.screenX + sensitiveArea) : pos == 'right' ? (aCoordinates.screenX < box.screenX + box.width - sensitiveArea) : pos == 'bottom' ? (aCoordinates.screenY < box.screenY + box.height - sensitiveArea) : (aCoordinates.screenY > box.screenY + sensitiveArea) ) { return this.MOUSE_POSITION_OUTSIDE; } if ( pos == 'left' ? (aCoordinates.screenX <= box.screenX - sensitiveArea) : pos == 'right' ? (aCoordinates.screenX >= box.screenX + box.width + sensitiveArea) : pos == 'bottom' ? (aCoordinates.screenY >= box.screenY + box.height + sensitiveArea) : (aCoordinates.screenY <= box.screenY - sensitiveArea) ) { return this.MOUSE_POSITION_INSIDE; } return this.MOUSE_POSITION_NEAR; }, getContentsAreaBox : function AHB_getContentsAreaBox() { var sv = this.treeStyleTab; var b = this.browser; var box = b.mCurrentBrowser.boxObject; var xoffset = (this.shrunken || this.hidden) ? 0 : this.XOffset ; box = { screenX : box.screenX + (sv.position == 'left' ? xoffset : 0 ), screenY : box.screenY, width : box.width - xoffset, height : box.height }; return box; }, getNearestClickableBox : function AHB_getNearestClickableBox(aCoordinates) { var sv = this.treeStyleTab; var tab = sv.getTabFromCoordinates(aCoordinates); if (!tab) return null; var position = sv.invertedScreenPositionProp; var size = sv.invertedSizeProp; var coordinate = aCoordinates[sv.invertedScreenPositionProp]; var tabbox = tab.boxObject; var closebox; if (this.closeButtonsMode != this.CLOSE_BUTTONS_DISABLED && this.closeButtonsMode != this.CLOSE_BUTTONS_ON_TABBAR && (closebox = sv.getTabClosebox(tab)) && (closebox = closebox.boxObject) && closebox.width && closebox.height) { let padding = Math.min( closebox[position] - tabbox[position], (tabbox[position] + tabbox[size]) - (closebox[position] + closebox[size]) ); if (closebox[position] - padding <= coordinate && closebox[position] + closebox[size] + padding >= coordinate) return this.cloneBoxObject(closebox, { padding : padding }); } var twisty; if (sv.canCollapseSubtree(tab) && (twisty = sv.getTabTwisty(tab)) && (twisty = twisty.boxObject) && twisty.width && twisty.height) { let padding = Math.min( twisty[position] - tabbox[position], (tabbox[position] + tabbox[size]) - (twisty[position] + twisty[size]) ); if (twisty[position] - padding <= coordinate && twisty[position] + twisty[size] + padding >= coordinate) return this.cloneBoxObject(twisty, { padding : padding }); } return null; }, cloneBoxObject : function AHB_cloneBoxObject(aBoxObject, aOverride) { var box = {}; for (let i in aBoxObject) { if (typeof aBoxObject[i] != 'function') box[i] = aBoxObject[i]; } Object.keys(aOverride).forEach(function(aKey) { box[aKey] = aOverride[aKey]; }); return box; }, cancelShowHideOnMouseMove : function AHB_cancelShowHideOnMouseMove() { if (this.showHideOnMouseMoveTimer) { this.window.clearTimeout(this.showHideOnMouseMoveTimer); this.showHideOnMouseMoveTimer = null; } }, // feedback showForFeedback : function AHB_showForFeedback() { if (!this.enabled || !utils.getTreePref('tabbar.autoShow.feedback')) return; var w = this.window; if (this.delayedShowForFeedbackTimer) { w.clearTimeout(this.delayedShowForFeedbackTimer); this.delayedShowForFeedbackTimer = null; } this.cancelHideForFeedback(); this.delayedShowForFeedbackTimer = w.setTimeout( function(aSelf) { aSelf.delayedShowForFeedbackTimer = null; aSelf.delayedShowForFeedback(); }, 100, this ); }, delayedShowForFeedback : function AHB_delayedShowForFeedback() { this.show(this.kSHOWN_BY_FEEDBACK); this.cancelHideForFeedback(); this.delayedHideTabbarForFeedbackTimer = this.window.setTimeout( function(aSelf) { aSelf.delayedHideTabbarForFeedbackTimer = null; aSelf.hide(aSelf.kSHOWN_BY_FEEDBACK); }, utils.getTreePref('tabbar.autoShow.feedback.delay'), this ); }, cancelHideForFeedback : function AHB_cancelHideForFeedback() { if (this.delayedHideTabbarForFeedbackTimer) { this.window.clearTimeout(this.delayedHideTabbarForFeedbackTimer); this.delayedHideTabbarForFeedbackTimer = null; } }, setWidth : function AHB_setWidth(aWidth, aForceExpanded) { aWidth = this.treeStyleTab.maxTabbarWidth(aWidth); if (aForceExpanded || this.expanded || this.mode != this.kMODE_SHRINK) this.expandedWidth = aWidth; else this.shrunkenWidth = aWidth; }, updateMenuItem : function AHB_updateMenuItem(aNode) { if (!aNode) return; if (this.mode != this.kMODE_DISABLED) aNode.setAttribute('checked', true); else aNode.removeAttribute('checked'); }, // show/hide tabbar get width() { if (this.expanded) { this._width = this.treeStyleTab.tabStrip.boxObject.width || this._width; } return this._width; }, set width(aNewWidth) { this._width = aNewWidth; return this._width; }, _width : 0, get widthFromMode() { return this.shrunken ? this.shrunkenWidth : this.expandedWidth ; }, get placeHolderWidthFromMode() { return (this.mode == this.kMODE_SHRINK) ? this.shrunkenWidth : this.expandedWidth ; }, get height() { if (this.expanded) { this._height = this.treeStyleTab.tabStrip.boxObject.height; } return this._height; }, set height(aNewHeight) { this._height = aNewHeight; return this._height; }, _height : 0, get splitterWidth() { if (this.expanded) { var splitter = this.document.getAnonymousElementByAttribute(this.browser, 'class', this.treeStyleTab.kSPLITTER); this._splitterWidth = (splitter ? splitter.boxObject.width : 0 ); } return this._splitterWidth; }, set splitterWidth(aNewWidth) { this._splitterWidth = aNewWidth; return this._splitterWidth; }, _splitterWidth : 0, showHideInternal : function AHB_showHideInternal(aReason) { var sv = this.treeStyleTab; var b = this.browser; var pos = sv.position; aReason = aReason || 0; if (this.expanded) { // to be hidden or shrunken let reason = this.kSHOWN_BY_UNKNOWN; if (aReason & this.kSHOWHIDE_BY_API) reason = aReason; this.onHiding(); this.showHideReason = reason; } else { // to be shown or expanded this.onShowing(); this.showHideReason = aReason || this.showHideReason || this.kSHOWN_BY_UNKNOWN; } if (DEBUG) { let givenReason = this._getHumanReadableReason(aReason); let unifiedReason = this._getHumanReadableReason(this.showHideReason); if (this.expanded) dump('autoHide: show by ' + aReason + '(' + givenReason + ' / ' + unifiedReason + ')\n'); else dump('autoHide: hide by ' + aReason + '(' + givenReason + ' / ' + unifiedReason + ')\n'); dump((new Error()).stack.replace(/^/gm, ' ').replace(/\s+$/, '') + '\n'); } this.fireStateChangingEvent(); if (this.expanded) sv.setTabbrowserAttribute(this.kAUTOHIDE, 'show'); b.mTabContainer.adjustTabstrip(); sv.checkTabsIndentOverflow(); this.window.setTimeout(function(aSelf) { aSelf.fireStateChangeEvent(); aSelf.showHideContentsAreaScreen(); }, 0, this); }, _getHumanReadableReason: function AHB_getHumanReadableReason(aReason) { let humanReadableReason = (aReason & this.kSHOWN_BY_SHORTCUT ? 'shortcut ' : '' ) + (aReason & this.kSHOWN_BY_MOUSEMOVE ? 'mousemove ' : '' ) + (aReason & this.kSHOWN_BY_FEEDBACK ? 'feedback ' : '' ) + (aReason & this.kSHOWHIDE_BY_START ? 'start ' : '' ) + (aReason & this.kSHOWHIDE_BY_END ? 'end ' : '' ) + (aReason & this.kSHOWHIDE_BY_POSITION_CHANGE ? 'positionchange ' : '' ) + (aReason & this.kSHOWHIDE_BY_RESIZE ? 'resize ' : '' ) + (aReason & this.kHIDDEN_BY_CLICK ? 'click ' : '' ); return humanReadableReason.replace(/\s+$/, ''); }, showHideContentsAreaScreen : function AHB_showHideContentsAreaScreen() { if ( this.expanded && this.contentAreaScreenEnabled && Services.focus.activeWindow && Services.focus.activeWindow.top == this.window ) { this.browser.selectedTab.__treestyletab__contentBridge.checkPluginAreaExistence() .then((function(aExistence) { if (aExistence) this.showContentsAreaScreen(); else this.hideContentsAreaScreen(); }).bind(this)) .catch((function(aError) { this.hideContentsAreaScreen(); Components.utils.reportError(aError); }).bind(this)); } else { this.hideContentsAreaScreen(); } }, showContentsAreaScreen : function AHB_showContentsAreaScreen() { let box = this.getContentsAreaBox(); let style = this.screen.style; let width = Math.min(box.width, this.window.screen.availWidth - box.screenX); let height = Math.min(box.height, this.window.screen.availHeight - box.screenY); style.width = width+'px'; style.height = height+'px'; if (this.screen.state == 'open') this.screen.moveTo(box.screenX, box.screenY); else this.screen.openPopupAtScreen(box.screenX, box.screenY, false); this.screen.setAttribute('popup-shown', true); }, hideContentsAreaScreen : function AHB_hideContentsAreaScreen() { this.screen.removeAttribute('popup-shown'); if (this.screen.state != 'close') this.screen.hidePopup(); }, show : function AHB_show(aReason) /* PUBLIC API */ { if (this.showHideReason & this.kSHOWHIDE_BY_API) { this.end(); return; } if (aReason) { this.showHideReason |= aReason; } if (!this.expanded) this.showHideInternal(aReason); }, hide : function AHB_hide(aReason) /* PUBLIC API */ { if (!this.enabled) { this.start(aReason | this.kSHOWHIDE_BY_API); return; } if (aReason) { if (aReason == this.kSHOWN_BY_ANY_REASON) this.showHideReason &= ~this.kSHOWN_BY_ANY_REASON; else if (this.showHideReason & aReason) this.showHideReason ^= aReason; if (this.showHideReason & this.kSHOWN_BY_ANY_REASON) return; } if (this.expanded) this.showHideInternal(aReason); }, onShowing : function AHB_onShowing() { var sv = this.treeStyleTab; var b = this.browser; var pos = sv.position; sv.setTabbrowserAttribute(this.kSTATE, this.kSTATE_EXPANDED); switch (this.mode) { case this.kMODE_DISABLED: break; case this.kMODE_HIDE: sv.updateFloatingTabbar(sv.kTABBAR_UPDATE_BY_AUTOHIDE); break; default: case this.kMODE_SHRINK: if (pos == 'left' || pos == 'right') { let width = sv.maxTabbarWidth(this.expandedWidth); sv.updateFloatingTabbar(sv.kTABBAR_UPDATE_BY_AUTOHIDE); } break; } }, onHiding : function AHB_onHiding() { var sv = this.treeStyleTab; var b = this.browser; var pos = sv.position; var box = (sv.tabStripPlaceHolder || sv.tabStrip).boxObject; this.tabbarHeight = box.height; this.width = box.width || this.width; var splitter = this.document.getAnonymousElementByAttribute(b, 'class', sv.kSPLITTER); this.splitterWidth = (splitter ? splitter.boxObject.width : 0 ); switch (this.mode) { case this.kMODE_DISABLED: sv.setTabbrowserAttribute(this.kAUTOHIDE, 'hidden'); sv.setTabbrowserAttribute(this.kSTATE, this.kSTATE_HIDDEN); break; case this.kMODE_HIDE: sv.updateLastScrollPosition(); sv.setTabbrowserAttribute(this.kAUTOHIDE, 'hidden'); sv.setTabbrowserAttribute(this.kSTATE, this.kSTATE_HIDDEN); sv.updateFloatingTabbar(sv.kTABBAR_UPDATE_BY_AUTOHIDE); break; default: case this.kMODE_SHRINK: sv.setTabbrowserAttribute(this.kAUTOHIDE, 'show'); sv.setTabbrowserAttribute(this.kSTATE, this.kSTATE_SHRUNKEN); if (pos == 'left' || pos == 'right') sv.setTabStripAttribute('width', this.shrunkenWidth); sv.updateFloatingTabbar(sv.kTABBAR_UPDATE_BY_AUTOHIDE); break; } }, fireStateChangingEvent : function AHB_fireStateChangingEvent() { var data = { shown : this.expanded, state : this.state }; /* PUBLIC API */ this.treeStyleTab.fireCustomEvent(this.treeStyleTab.kEVENT_TYPE_AUTO_HIDE_STATE_CHANGING, this.browser, true, false, data); // for backward compatibility this.treeStyleTab.fireCustomEvent(this.treeStyleTab.kEVENT_TYPE_AUTO_HIDE_STATE_CHANGING.replace(/^nsDOM/, ''), this.browser, true, false, data); }, fireStateChangeEvent : function AHB_fireStateChangeEvent() { var data = { shown : this.expanded, state : this.state, xOffset : this.XOffset, yOffset : this.YOffset }; /* PUBLIC API */ this.treeStyleTab.fireCustomEvent(this.treeStyleTab.kEVENT_TYPE_AUTO_HIDE_STATE_CHANGE, this.browser, true, false, data); // for backward compatibility this.treeStyleTab.fireCustomEvent(this.treeStyleTab.kEVENT_TYPE_AUTO_HIDE_STATE_CHANGE.replace(/^nsDOM/, ''), this.browser, true, false, data); }, updateTransparency : function AHB_updateTransparency() { var sv = this.treeStyleTab; sv.updateFloatingTabbar(sv.kTABBAR_UPDATE_BY_APPEARANCE_CHANGE); }, // event handling observe : function AHB_observe(aSubject, aTopic, aData) { switch (aTopic) { case 'nsPref:changed': this.onPrefChange(aData); break; default: break; } }, domains : [ 'extensions.treestyletab.', 'browser.fullscreen.autohide', 'browser.tabs.closeButtons' ], onPrefChange : function AHB_onPrefChange(aPrefName) { // ignore after destruction if (!this.window || !this.window.TreeStyleTabService) return; var value = prefs.getPref(aPrefName); switch (aPrefName) { case 'extensions.treestyletab.tabbar.autoHide.mode': if (!this.window.TreeStyleTabService.shouldApplyNewPref('tabbar.autoHide.mode')) return; this.browser.setAttribute(this.kMODE+'-normal', value); this.updateMode(value); return; case 'extensions.treestyletab.tabbar.autoHide.mode.fullscreen': if (!this.window.TreeStyleTabService.shouldApplyNewPref('tabbar.autoHide.mode.fullscreen')) return; this.browser.setAttribute(this.kMODE+'-fullscreen', value); this.updateMode(value); return; case 'extensions.treestyletab.tabbar.autoShow.mousemove': case 'extensions.treestyletab.tabbar.autoShow.accelKeyDown': case 'extensions.treestyletab.tabbar.autoShow.feedback': if (this.enabled && this.shouldListenMouseMove) this.startListenMouseMove(); else this.endListenMouseMove(); return; case 'extensions.treestyletab.tabbar.autoHide.area': this.sensitiveArea = value; return; case 'extensions.treestyletab.tabbar.togglerSize': this.togglerSize = value; { let toggler = this.toggler; toggler.style.minWidth = toggler.style.minHeight = value+'px'; if (this.togglerSize <= 0) toggler.setAttribute('collapsed', true); else toggler.removeAttribute('collapsed'); } return; case 'extensions.treestyletab.tabbar.autoHide.contentAreaScreen.enabled': return this.contentAreaScreenEnabled = value; case 'browser.fullscreen.autohide': if (!this.window.fullScreen) return; this.end(); this.mode = value ? this.getModeForFullScreen() : this.kMODE_DISABLED ; if (this.mode != this.kMODE_DISABLED) this.start(); return; case 'browser.tabs.closeButtons': return this.closeButtonsMode = value; default: return; } }, handleEvent : function AHB_handleEvent(aEvent) { switch (aEvent.type) { case 'mousedown': return this.onMouseDown(aEvent); case 'mouseup': return this.onMouseUp(aEvent); case 'mousemove': return this.handleMouseMove(aEvent); case 'TabRemotenessChange': return this.notifyStatusToAllTabs(aEvent.target); case 'TabOpen': case 'TabClose': return this.showForFeedback(); case 'TabMove': if (!this.treeStyleTab.subTreeMovingCount && !this.treeStyleTab.internallyTabMovingCount) this.showForFeedback(); return; case 'select': if (!this.window.TreeStyleTabService.accelKeyPressed) this.showForFeedback(); return; case 'dragover': return this.onDragOver(aEvent); case 'dragleave': return this.onDragLeave(aEvent); case this.treeStyleTab.kEVENT_TYPE_TABBAR_POSITION_CHANGING: this.isResizing = false; return; case this.treeStyleTab.kEVENT_TYPE_TABBAR_POSITION_CHANGED: if (this.enabled) this.window.setTimeout(function(aSelf) { aSelf.show(this.kSHOWHIDE_BY_POSITION_CHANGE); aSelf.hide(this.kSHOWHIDE_BY_POSITION_CHANGE); }, 0, this); this.updateTransparency(); return; case this.treeStyleTab.kEVENT_TYPE_TAB_FOCUS_SWITCHING_KEY_DOWN: return this.onKeyDown(aEvent.detail.sourceEvent); case this.treeStyleTab.kEVENT_TYPE_TAB_FOCUS_SWITCHING_START: this.cancelDelayedShowForShortcut(); if (this.enabled && utils.getTreePref('tabbar.autoShow.tabSwitch') && ( aEvent.detail.scrollDown || aEvent.detail.scrollUp || ( // when you release "shift" key this.expanded && aEvent.detail.standBy && aEvent.detail.onlyShiftKey ) )) this.show(this.kSHOWN_BY_SHORTCUT); return; case this.treeStyleTab.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END: this.cancelDelayedShowForShortcut(); if (this.enabled) this.hide(this.kSHOWN_BY_SHORTCUT); return; case this.treeStyleTab.kEVENT_TYPE_PRINT_PREVIEW_ENTERED: this.hide(this.kSHOWHIDE_BY_END); this.endListenMouseMove(); return; case this.treeStyleTab.kEVENT_TYPE_PRINT_PREVIEW_EXITED: if (this.enabled && this.shouldListenMouseMove) this.startListenMouseMove(); return; } }, onMouseDown : function AHB_onMouseDown(aEvent) { var sv = this.treeStyleTab; var w = this.window; if ( aEvent.target && !this.isResizing && sv.evaluateXPath( 'ancestor-or-self::*[@class="'+sv.kSPLITTER+'"]', aEvent.originalTarget || aEvent.target, Ci.nsIDOMXPathResult.BOOLEAN_TYPE ).booleanValue ) { this.isResizing = true; sv.setTabbrowserAttribute(sv.kRESIZING, true); } this.cancelShowHideOnMouseMove(); if ( this.enabled && this.expanded && ( !aEvent.originalTarget || aEvent.originalTarget.ownerDocument != this.document || !sv.getTabBrowserFromChild(aEvent.originalTarget) ) ) this.hide(this.kHIDDEN_BY_CLICK); this.lastMouseDownTarget = ( aEvent.originalTargetLocalName || (aEvent.originalTarget && aEvent.originalTarget.localName) || '' ); }, onMouseUp : function AHB_onMouseUp(aEvent) { var sv = this.treeStyleTab; if (aEvent.originalTarget && sv.evaluateXPath( 'ancestor-or-self::*[@class="'+sv.kSPLITTER+'"]', aEvent.originalTarget, Ci.nsIDOMXPathResult.BOOLEAN_TYPE ).booleanValue) { this.isResizing = false; sv.removeTabbrowserAttribute(sv.kRESIZING); } this.cancelShowHideOnMouseMove(); this.lastMouseDownTarget = null; }, handleMouseMove : function AHB_handleMouseMove(aEvent) { var sv = this.treeStyleTab; if (this.isResizing && /^(scrollbar|thumb|slider|scrollbarbutton)$/i.test(this.lastMouseDownTarget || '')) return true; if ( !aEvent.shiftKey && !sv.isPopupShown() && ( !this.expanded || this.showHideReason & this.kSHOWN_BY_ANY_REASON ) && !this.lastMouseDownTarget ) this.showHideOnMouseMove(aEvent); return true; }, onDragOver : function AHB_onDragOver(aEvent) { if (this.expanded) return; var position = this.getMousePosition(aEvent); if (!(position & this.MOUSE_POSITION_SENSITIVE)) return; var draggedTabs = this.window['piro.sakura.ne.jp'].tabsDragUtils.getSelectedTabs(aEvent); if ( draggedTabs.length || this.treeStyleTab.tabbarDNDObserver.retrieveURLsFromDataTransfer(aEvent.dataTransfer).length ) { this.show(this.kSHOWN_BY_MOUSEMOVE); if (this._autoHideOnDragLeaveTimer) { this.window.clearTimeout(this._autoHideOnDragLeaveTimer); delete this._autoHideOnDragLeaveTimer; } } }, onDragLeave : function AHB_onDragLeave(aEvent) { if (!this.expanded) return; if (this._autoHideOnDragLeaveTimer) this.window.clearTimeout(this._autoHideOnDragLeaveTimer); var position = this.getMousePosition(aEvent); if (position & this.MOUSE_POSITION_SENSITIVE) return; this._autoHideOnDragLeaveTimer = this.window.setTimeout(function(aSelf) { delete aSelf._autoHideOnDragLeaveTimer; aSelf.hide(aSelf.kSHOWN_BY_MOUSEMOVE); }, 100, this); }, onKeyDown : function AHB_onKeyDown(aEvent) { var sv = this.treeStyleTab; var b = this.browser; var w = this.window; if (this.delayedShowForShortcutDone) this.cancelDelayedShowForShortcut(); if ( sv.getTabs(b).length > 1 && !aEvent.altKey && w.TreeStyleTabService.accelKeyPressed ) { if (this.enabled && utils.getTreePref('tabbar.autoShow.accelKeyDown') && !this.delayedAutoShowTimer && !this.delayedShowForShortcutTimer) { this.delayedShowForShortcutTimer = w.setTimeout( function(aSelf) { aSelf.delayedShowForShortcutDone = true; aSelf.show(aSelf.kSHOWN_BY_SHORTCUT); sv = null; b = null; }, utils.getTreePref('tabbar.autoShow.accelKeyDown.delay'), this ); this.delayedShowForShortcutDone = false; } } else { if (this.enabled) this.hide(this.kSHOWN_BY_SHORTCUT); } }, cancelDelayedShowForShortcut : function AHB_cancelDelayedShowForShortcut() { if (this.delayedShowForShortcutTimer) { this.window.clearTimeout(this.delayedShowForShortcutTimer); this.delayedShowForShortcutTimer = null; } }, delayedShowForShortcutTimer : null, delayedShowForShortcutDone : true, init : function AHB_init(aTabBrowser) { this.browser = aTabBrowser; this.document = aTabBrowser.ownerDocument; this.window = this.document.defaultView; this.treeStyleTab = aTabBrowser.treeStyleTab; var sv = this.treeStyleTab; var b = this.browser; this.enabled = false; this.mouseMoveListening = false; this.showHideReason = this.kSHOWN_BY_UNKNOWN; this.lastMouseDownTarget = null; this.isResizing = false; this.showHideOnMouseMoveTimer = null; this.delayedShowForFeedbackTimer = null; b.setAttribute(this.kMODE+'-normal', this.lastNormalMode); b.setAttribute(this.kMODE+'-fullscreen', this.lastFullscreenMode); prefs.addPrefListener(this); this.onPrefChange('browser.tabs.closeButtons'); this.onPrefChange('extensions.treestyletab.tabbar.autoHide.area'); this.onPrefChange('extensions.treestyletab.tabbar.togglerSize'); this.onPrefChange('extensions.treestyletab.tabbar.autoHide.contentAreaScreen.enabled'); b.mTabContainer.addEventListener('TabOpen', this, false); b.mTabContainer.addEventListener('TabClose', this, false); b.mTabContainer.addEventListener('TabMove', this, false); b.mTabContainer.addEventListener('select', this, false); b.addEventListener(sv.kEVENT_TYPE_TABBAR_POSITION_CHANGING, this, false); b.addEventListener(sv.kEVENT_TYPE_TABBAR_POSITION_CHANGED, this, false); b.addEventListener(sv.kEVENT_TYPE_TAB_FOCUS_SWITCHING_KEY_DOWN, this, false); b.addEventListener(sv.kEVENT_TYPE_TAB_FOCUS_SWITCHING_START, this, false); b.addEventListener(sv.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END, this, false); }, destroy : function AHB_destroy() { this.end(); prefs.removePrefListener(this); var sv = this.treeStyleTab; var b = this.browser; b.mTabContainer.removeEventListener('TabOpen', this, false); b.mTabContainer.removeEventListener('TabClose', this, false); b.mTabContainer.removeEventListener('TabMove', this, false); b.mTabContainer.removeEventListener('select', this, false); b.removeEventListener(sv.kEVENT_TYPE_TABBAR_POSITION_CHANGING, this, false); b.removeEventListener(sv.kEVENT_TYPE_TABBAR_POSITION_CHANGED, this, false); b.removeEventListener(sv.kEVENT_TYPE_TAB_FOCUS_SWITCHING_KEY_DOWN, this, false); b.removeEventListener(sv.kEVENT_TYPE_TAB_FOCUS_SWITCHING_START, this, false); b.removeEventListener(sv.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END, this, false); delete this.treeStyleTab; delete this.browser; delete this.document; delete this.window; } }); function AutoHideWindow(aWindow) { this.init(aWindow); } AutoHideWindow.prototype = inherit(AutoHideBase.prototype, { get browser() { return this.treeStyleTab.browser; }, // mode get mode() /* PUBLIC API, overrides base class's one */ { var mode = this.getMode(); if (mode == this.kMODE_SHRINK && !this.treeStyleTab.isVertical) return this.kMODE_HIDE; return mode; }, toggleMode : function AHW_toggleMode(aTabBrowser) /* PUBLIC API */ { var b = aTabBrowser || this.browser; var w = this.window; var key = 'tabbar.autoHide.mode'; var toggleKey = 'tabbar.autoHide.mode.toggle'; if (w.fullScreen) { key += '.fullscreen'; toggleKey += '.fullscreen'; } var mode = this.getMode(b) == AutoHideBrowser.prototype.kMODE_DISABLED ? utils.getTreePref(toggleKey) : AutoHideBrowser.prototype.kMODE_DISABLED ; utils.setTreePref(key, mode); b.setAttribute(AutoHideBrowser.prototype.kMODE+'-'+(w.fullScreen ? 'fullscreen' : 'normal' ), mode); b.treeStyleTab.autoHide.updateMode(mode); }, initMode : function AHW_initMode() { // save current state for this window's last state for new (clean) window this.lastNormalMode = this.lastNormalMode; this.lastFullscreenMode = this.lastFullscreenMode; var mode = this.treeStyleTab.isFullscreenAutoHide ? this.lastFullscreenMode : this.lastNormalMode; if (mode == this.mode) return; this.mode = mode; if (mode != this.kMODE_DISABLED) this.updateKeyListeners(this.window); this.window.setTimeout(function(aSelf) { aSelf.window.gBrowser.treeStyleTab.autoHide.updateMode(mode); }, 0, this); }, // for shortcuts updateKeyListeners : function AHW_updateKeyListeners() { // ignore after destruction if (!this.window || !this.window.TreeStyleTabService) return; if ( this.getMode() && this.shouldListenKeyEvents ) { this.treeStyleTab.startListenKeyEventsFor(this.treeStyleTab.LISTEN_FOR_AUTOHIDE); } else { this.treeStyleTab.endListenKeyEventsFor(this.treeStyleTab.LISTEN_FOR_AUTOHIDE); } var w = this.window; w.setTimeout(function() { if (w.windowState != Ci.nsIDOMChromeWindow.STATE_NORMAL) return; var count = 0; var resizeTimer = w.setInterval(function(){ if (w.windowState != Ci.nsIDOMChromeWindow.STATE_NORMAL) return w.clearInterval(resizeTimer); if (++count > 100 || w.innerHeight > 0) { w.clearInterval(resizeTimer); w.resizeBy(-1,-1); w.resizeBy(1,1); } }, 250); }, 0); }, get shouldListenKeyEvents() { return !this.treeStyleTab.ctrlTabPreviewsEnabled && ( utils.getTreePref('tabbar.autoShow.accelKeyDown') || utils.getTreePref('tabbar.autoShow.tabSwitch') || utils.getTreePref('tabbar.autoShow.feedback') ); }, init : function AHW_init(aWindow) { this.window = aWindow; this.document = aWindow.document; this.treeStyleTab = aWindow.TreeStyleTabService; prefs.addPrefListener(this); this.waitForWindowReady(); }, destroy : function AHW_destroy() { this.endWaitForWindowReady(); prefs.removePrefListener(this); delete this.treeStyleTab; delete this.document; delete this.window; }, waitForWindowReady : function AHW_waitForWindowReady() { if (this.waitingForWindowReady) return; this.waitingForWindowReady = true; this.window.addEventListener('SSWindowStateReady', this, false); Services.obs.addObserver(this, 'browser-delayed-startup-finished', false); }, endWaitForWindowReady : function AHW_endWaitForWindowReady() { if (!this.waitingForWindowReady) return; this.waitingForWindowReady = false; this.window.removeEventListener('SSWindowStateReady', this, false); Services.obs.removeObserver(this, 'browser-delayed-startup-finished'); }, handleEvent : function AHW_handleEvent(aEvent) { switch (aEvent.type) { case 'SSWindowStateReady': this.endWaitForWindowReady(); this.initMode(); return; } }, observe : function AHW_observe(aSubject, aTopic, aData) { if (aSubject != this.window) return; this.endWaitForWindowReady(); this.window.setTimeout(this.initMode.bind(this), 0); }, domains : [ 'extensions.treestyletab.', 'browser.ctrlTab.previews' ], onPrefChange : function AHW_onPrefChange(aPrefName) { var value = prefs.getPref(aPrefName); switch (aPrefName) { case 'extensions.treestyletab.tabbar.autoHide.mode': case 'extensions.treestyletab.tabbar.autoShow.accelKeyDown': case 'extensions.treestyletab.tabbar.autoShow.tabSwitch': case 'extensions.treestyletab.tabbar.autoShow.feedback': case 'browser.ctrlTab.previews': this.updateKeyListeners(this.window); default: return; } } });