treestyletab/modules/window.js
Piro / YUKI Hiroshi f4fb6174d2 Read value of custom attributes of tabs via TSTBase.getTabAttribute() safely.
Tab's ID and other attributes saved via setTabValue is not available before the tab is completely restored.
As the result, TST sometimes fail to restore tab positions. (See: #676)
This issue doesn't happen on Firefox 24ESR, because recent Firefox (29 and later) uses message manager to restore tabs and it causes such "delay" of initializations about custom attributes.
2014-07-02 04:29:49 +09:00

1780 lines
56 KiB
JavaScript

/* ***** 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) 2012-2014
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): YUKI "Piro" Hiroshi <piro.outsider.reflex@gmail.com>
* Tetsuharu OHZEKI <https://github.com/saneyuki>
*
* 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 = ['TreeStyleTabWindow'];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://treestyletab-modules/lib/inherit.jsm');
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;
});
XPCOMUtils.defineLazyModuleGetter(this, 'UninstallationListener',
'resource://treestyletab-modules/lib/UninstallationListener.js');
XPCOMUtils.defineLazyModuleGetter(this, 'Services', 'resource://gre/modules/Services.jsm');
Cu.import('resource://treestyletab-modules/base.js');
XPCOMUtils.defineLazyModuleGetter(this, 'TreeStyleTabBrowser', 'resource://treestyletab-modules/browser.js');
XPCOMUtils.defineLazyModuleGetter(this, 'utils', 'resource://treestyletab-modules/utils.js', 'TreeStyleTabUtils');
XPCOMUtils.defineLazyModuleGetter(this, 'AutoHideWindow', 'resource://treestyletab-modules/autoHide.js');
XPCOMUtils.defineLazyModuleGetter(this, 'TreeStyleTabThemeManager', 'resource://treestyletab-modules/themeManager.js');
XPCOMUtils.defineLazyModuleGetter(this, 'FullscreenObserver', 'resource://treestyletab-modules/fullscreenObserver.js');
XPCOMUtils.defineLazyModuleGetter(this, 'BrowserUIShowHideObserver', 'resource://treestyletab-modules/browserUIShowHideObserver.js');
function TreeStyleTabWindow(aWindow)
{
this.window = aWindow;
this.document = aWindow.document;
this._restoringTabs = [];
this._shownPopups = [];
this.restoringCount = 0;
aWindow.addEventListener('DOMContentLoaded', this, true);
aWindow.addEventListener('load', this, false);
aWindow.TreeStyleTabService = this;
XPCOMUtils.defineLazyModuleGetter(aWindow, 'TreeStyleTabBrowser', 'resource://treestyletab-modules/browser.js');
}
TreeStyleTabWindow.prototype = inherit(TreeStyleTabBase, {
base : TreeStyleTabBase,
window : null,
document : null,
/* API */
changeTabbarPosition : function TSTWindow_changeTabbarPosition(aNewPosition) /* PUBLIC API (obsolete, for backward compatibility) */
{
this.position = aNewPosition;
},
get position() /* PUBLIC API */
{
return this.preInitialized && this.browser.treeStyleTab ?
this.browser.treeStyleTab.position :
this.base.position ;
},
set position(aValue)
{
if ('UndoTabService' in this.window && this.window.UndoTabService.isUndoable()) {
var current = this.base.position;
var self = this;
this.window.UndoTabService.doOperation(
function() {
self.base.position = aValue;
},
{
label : utils.treeBundle.getString('undo_changeTabbarPosition_label'),
name : 'treestyletab-changeTabbarPosition',
data : {
oldPosition : current,
newPosition : aValue
}
}
);
}
else {
this.base.position = aValue;
}
return aValue;
},
undoChangeTabbarPosition : function TSTWindow_undoChangeTabbarPosition() /* PUBLIC API */
{
return this.base.undoChangeTabbarPosition();
},
redoChangeTabbarPosition : function TSTWindow_redoChangeTabbarPosition() /* PUBLIC API */
{
return this.base.redoChangeTabbarPosition();
},
get treeViewEnabled() /* PUBLIC API */
{
return this.base.treeViewEnabled;
},
set treeViewEnabled(aValue)
{
return this.base.treeViewEnabled = aValue;
},
get useTMPSessionAPI() /* PUBLIC API */
{
return this.base.useTMPSessionAPI;
},
set useTMPSessionAPI(aValue)
{
return this.base.useTMPSessionAPI = aValue;
},
get browser()
{
var w = this.window;
this.assertBeforeDestruction(w);
return 'SplitBrowser' in w ? w.SplitBrowser.activeBrowser :
w.gBrowser ;
},
get browserToolbox()
{
var w = this.window;
return w.gToolbox || w.gNavToolbox;
},
get browserBox()
{
return this.document.getElementById('browser');
},
get browserBottomBox()
{
return this.document.getElementById('browser-bottombox');
},
get isPopupWindow()
{
return (
this.document &&
this.document.documentElement.getAttribute('chromehidden') != '' &&
!this.window.gBrowser.treeStyleTab.isVisible
);
},
/* backward compatibility */
getTempTreeStyleTab : function TSTWindow_getTempTreeStyleTab(aTabBrowser)
{
return aTabBrowser.treeStyleTab || new TreeStyleTabBrowser(this, aTabBrowser);
},
initTabAttributes : function TSTWindow_initTabAttributes(aTab, aTabBrowser)
{
var b = aTabBrowser || this.getTabBrowserFromChild(aTab);
this.getTempTreeStyleTab(b).initTabAttributes(aTab);
},
initTabContents : function TSTWindow_initTabContents(aTab, aTabBrowser)
{
var b = aTabBrowser || this.getTabBrowserFromChild(aTab);
this.getTempTreeStyleTab(b).initTabContents(aTab);
},
initTabContentsOrder : function TSTWindow_initTabContentsOrder(aTab, aTabBrowser)
{
var b = aTabBrowser || this.getTabBrowserFromChild(aTab);
this.getTempTreeStyleTab(b).initTabContentsOrder(aTab);
},
/* Utilities */
stopRendering : function TSTWindow_stopRendering()
{
this.window['piro.sakura.ne.jp'].stopRendering.stop();
},
startRendering : function TSTWindow_startRendering()
{
this.window['piro.sakura.ne.jp'].stopRendering.start();
},
getPropertyPixelValue : function TSTWindow_getPropertyPixelValue(aElementOrStyle, aProp)
{
var style = aElementOrStyle instanceof Ci.nsIDOMCSSStyleDeclaration ?
aElementOrStyle :
this.window.getComputedStyle(aElementOrStyle, null) ;
return Number(style.getPropertyValue(aProp).replace(/px$/, ''));
},
get isToolbarCustomizing()
{
var toolbox = this.browserToolbox;
return toolbox && toolbox.customizing;
},
get maximized()
{
var sizemode = this.document.documentElement.getAttribute('sizemode');
return (
this.window.fullScreen ||
this.window.windowState == this.window.STATE_MAXIMIZED ||
sizemode == 'maximized' ||
sizemode == 'fullscreen'
);
},
maxTabbarWidth : function TSTWindow_maxTabbarWidth(aWidth, aTabBrowser)
{
aTabBrowser = aTabBrowser || this.browser;
var safePadding = 20; // for window border, etc.
var windowWidth = this.maximized ? this.window.screen.availWidth - safePadding : this.window.outerWidth ;
var rootWidth = parseInt(this.document.documentElement.getAttribute('width') || 0);
var max = Math.max(windowWidth, rootWidth);
return Math.max(0, Math.min(aWidth, max * this.MAX_TABBAR_SIZE_RATIO));
},
maxTabbarHeight : function TSTWindow_maxTabbarHeight(aHeight, aTabBrowser)
{
aTabBrowser = aTabBrowser || this.browser;
var safePadding = 20; // for window border, etc.
var windowHeight = this.maximized ? this.window.screen.availHeight - safePadding : this.window.outerHeight ;
var rootHeight = parseInt(this.document.documentElement.getAttribute('height') || 0);
var max = Math.max(windowHeight, rootHeight);
return Math.max(0, Math.min(aHeight, max * this.MAX_TABBAR_SIZE_RATIO));
},
shouldOpenSearchResultAsChild : function TSTWindow_shouldOpenSearchResultAsChild(aTerm)
{
aTerm = aTerm.trim();
var mode = utils.getTreePref('autoAttach.searchResult');
if (mode == this.kSEARCH_RESULT_ATTACH_ALWAYS) {
return true;
}
else if (!aTerm || mode == this.kSEARCH_RESULT_DO_NOT_ATTACH) {
return false;
}
var selection = this.window.getBrowserSelection();
return selection.trim() == aTerm;
},
kSEARCH_RESULT_DO_NOT_ATTACH : 0,
kSEARCH_RESULT_ATTACH_IF_SELECTED : 1,
kSEARCH_RESULT_ATTACH_ALWAYS : 2,
get isAutoHide()
{
return this.window.fullScreen ?
(
prefs.getPref('browser.fullscreen.autohide') &&
utils.getTreePref('tabbar.autoHide.mode.fullscreen')
) :
utils.getTreePref('tabbar.autoHide.mode');
},
get autoHideWindow()
{
if (!this._autoHideWindow) {
this._autoHideWindow = new AutoHideWindow(this.window);
}
return this._autoHideWindow;
},
get themeManager()
{
if (!this._themeManager) {
this._themeManager = new TreeStyleTabThemeManager(this.window);
}
return this._themeManager;
},
/* Initializing */
preInit : function TSTWindow_preInit()
{
if (this.preInitialized)
return;
this.preInitialized = true;
var w = this.window;
w.removeEventListener('DOMContentLoaded', this, true);
if (w.location.href.indexOf('chrome://browser/content/browser.xul') != 0)
return;
w.addEventListener('SSTabRestoring', this, true);
w.TreeStyleTabWindowHelper.preInit();
// initialize theme
this.onPrefChange('extensions.treestyletab.tabbar.style');
},
preInitialized : false,
init : function TSTWindow_init()
{
var w = this.window;
w.removeEventListener('load', this, false);
w.addEventListener('unload', this, false);
if (
w.location.href.indexOf('chrome://browser/content/browser.xul') != 0 ||
!this.browser
)
return;
if (this.initialized)
return;
if (!this.preInitialized) {
this.preInit();
}
w.removeEventListener('SSTabRestoring', this, true);
var d = this.document;
d.addEventListener('popupshowing', this, false);
d.addEventListener('popuphiding', this, true);
d.addEventListener(this.kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED, this, false);
d.addEventListener(this.kEVENT_TYPE_TABBAR_POSITION_CHANGED, this, false);
d.addEventListener(this.kEVENT_TYPE_TABBAR_STATE_CHANGED, this, false);
d.addEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, this, false);
w.addEventListener('beforecustomization', this, true);
w.addEventListener('aftercustomization', this, false);
// for Firefox 29 and later, after https://bugzilla.mozilla.org/show_bug.cgi?id=942374
if (w.messageManager && utils.shouldUseMessageManager)
w.messageManager.addMessageListener('SessionStore:restoreTabContentStarted', this);
this.fullscreenObserver = new FullscreenObserver(this.window);
this.initUIShowHideObserver();
var appcontent = d.getElementById('appcontent');
appcontent.addEventListener('SubBrowserAdded', this, false);
appcontent.addEventListener('SubBrowserRemoveRequest', this, false);
w.addEventListener('UIOperationHistoryUndo:TabbarOperations', this, false);
w.addEventListener('UIOperationHistoryRedo:TabbarOperations', this, false);
prefs.addPrefListener(this);
this.initUninstallationListener();
w.TreeStyleTabWindowHelper.onBeforeBrowserInit();
this.initTabBrowser(this.browser);
w.TreeStyleTabWindowHelper.onAfterBrowserInit();
this.processRestoredTabs();
this.updateTabsOnTop();
// Init autohide service only if it have to be activated.
if (this.isAutoHide)
this.onPrefChange('extensions.treestyletab.tabbar.autoHide.mode');
this.onPrefChange('extensions.treestyletab.autoCollapseExpandSubtreeOnSelect.whileFocusMovingByShortcut');
this.initialized = true;
},
initialized : false,
initUninstallationListener : function TSTWindow_initUninstallationListener()
{
var restorePrefs = function() {
if (prefs.getPref('extensions.treestyletab.tabsOnTop.originalState')) {
prefs.clearPref('extensions.treestyletab.tabsOnTop.originalState');
try {
this.browser.treeStyleTab.position = 'top';
}
catch(e) {
}
this.window.TabsOnTop.enabled = true;
}
}.bind(this);
new UninstallationListener({
id : 'treestyletab@piro.sakura.ne.jp',
onuninstalled : restorePrefs,
ondisabled : restorePrefs
});
},
initTabBrowser : function TSTWindow_initTabBrowser(aTabBrowser)
{
if (aTabBrowser.localName != 'tabbrowser')
return;
(new TreeStyleTabBrowser(this, aTabBrowser)).init();
},
updateAllTabsButton : function TSTWindow_updateAllTabsButton(aTabBrowser)
{
var d = this.document;
aTabBrowser = aTabBrowser || this.browser;
var allTabsButton = d.getElementById('alltabs-button') ||
( // Tab Mix Plus
utils.getTreePref('compatibility.TMP') &&
d.getAnonymousElementByAttribute(aTabBrowser.mTabContainer, 'anonid', 'alltabs-button')
);
if (allTabsButton && allTabsButton.hasChildNodes() && aTabBrowser.treeStyleTab)
allTabsButton.firstChild.setAttribute('position', aTabBrowser.treeStyleTab.isVertical ? 'before_start' : 'after_end' );
},
updateAllTabsPopup : function TSTWindow_updateAllTabsPopup(aEvent)
{
if (!utils.getTreePref('enableSubtreeIndent.allTabsPopup'))
return;
Array.forEach(aEvent.originalTarget.childNodes, function(aItem) {
if (aItem.classList.contains('alltabs-item') && 'tab' in aItem)
aItem.style.marginLeft = this.getTabAttribute(aItem.tab, this.kNEST) + 'em';
}, this);
},
initUIShowHideObserver : function TSTWindow_initUIShowHideObserver()
{
this.rootElementObserver = new BrowserUIShowHideObserver(this, this.document.documentElement);
var toolbox = this.browserToolbox;
if (toolbox)
this.browserToolboxObserver = new BrowserUIShowHideObserver(this, toolbox);
var browserBox = this.browserBox;
if (browserBox)
this.browserBoxObserver = new BrowserUIShowHideObserver(this, browserBox);
var bottomBox = this.browserBottomBox;
if (bottomBox)
this.browserBottomBoxObserver = new BrowserUIShowHideObserver(this, bottomBox);
},
destroy : function TSTWindow_destroy()
{
var w = this.window;
if (this.browser) {
this.base.inWindowDestoructionProcess = true;
try {
w.removeEventListener('unload', this, false);
this.autoHideWindow.destroy();
delete this._autoHideWindow;
this.themeManager.destroy();
delete this._themeManager;
this.browser.treeStyleTab.saveCurrentState();
this.destroyTabBrowser(this.browser);
this.endListenKeyEventsFor(this.LISTEN_FOR_AUTOHIDE);
this.endListenKeyEventsFor(this.LISTEN_FOR_AUTOEXPAND_BY_FOCUSCHANGE);
let d = this.document;
d.removeEventListener('popupshowing', this, false);
d.removeEventListener('popuphiding', this, true);
d.removeEventListener(this.kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED, this, false);
d.removeEventListener(this.kEVENT_TYPE_TABBAR_POSITION_CHANGED, this, false);
d.removeEventListener(this.kEVENT_TYPE_TABBAR_STATE_CHANGED, this, false);
d.removeEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, this, false);
w.removeEventListener('beforecustomization', this, true);
w.removeEventListener('aftercustomization', this, false);
// for Firefox 29 and later, after https://bugzilla.mozilla.org/show_bug.cgi?id=942374
if (w.messageManager && utils.shouldUseMessageManager)
w.messageManager.removeMessageListener('SessionStore:restoreTabContentStarted', this);
this.fullscreenObserver.destroy();
delete this.fullscreenObserver;
this.rootElementObserver.destroy();
delete this.rootElementObserver;
if (this.browserToolboxObserver) {
this.browserToolboxObserver.destroy();
delete this.browserToolboxObserver;
}
if (this.browserBoxObserver) {
this.browserBoxObserver.destroy();
delete this.browserBoxObserver;
}
if (this.browserBottomBoxObserver) {
this.browserBottomBoxObserver.destroy();
delete this.browserBottomBoxObserver;
}
for (let i = 0, maxi = this._tabFocusAllowance.length; i < maxi; i++)
{
w.removeEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, this._tabFocusAllowance[i], false);
}
var appcontent = d.getElementById('appcontent');
appcontent.removeEventListener('SubBrowserAdded', this, false);
appcontent.removeEventListener('SubBrowserRemoveRequest', this, false);
w.removeEventListener('UIOperationHistoryUndo:TabbarOperations', this, false);
w.removeEventListener('UIOperationHistoryRedo:TabbarOperations', this, false);
prefs.removePrefListener(this);
}
catch(e) {
throw e;
}
finally {
this.base.inWindowDestoructionProcess = false;
}
}
delete w.TreeStyleTabService;
delete this.window;
delete this.document;
},
destroyTabBrowser : function TSTWindow_destroyTabBrowser(aTabBrowser)
{
if (aTabBrowser.localName != 'tabbrowser')
return;
aTabBrowser.treeStyleTab.destroy();
delete aTabBrowser.treeStyleTab;
},
/* Event Handling */
handleEvent : function TSTWindow_handleEvent(aEvent)
{
switch (aEvent.type)
{
case 'DOMContentLoaded':
return this.preInit();
case 'load':
return this.init();
case 'unload':
return this.destroy();
case 'SSTabRestoring':
return this.onTabRestored(aEvent);
case 'popupshowing':
this.onPopupShown(aEvent.originalTarget);
if ((aEvent.originalTarget.getAttribute('anonid') || aEvent.originalTarget.id) == 'alltabs-popup')
this.updateAllTabsPopup(aEvent);
return;
case 'popuphiding':
return this.onPopupHidden(aEvent.originalTarget);
case this.kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED:
return this.updateAeroPeekPreviews();
case this.kEVENT_TYPE_TABBAR_POSITION_CHANGED:
case this.kEVENT_TYPE_TABBAR_STATE_CHANGED:
return this.updateTabsOnTop();
case this.kEVENT_TYPE_FOCUS_NEXT_TAB:
return this.onFocusNextTab(aEvent);
case 'keydown':
return this.onKeyDown(aEvent);
case 'keyup':
case 'keypress':
return this.onKeyRelease(aEvent);
case 'blur':
return this.simulateKeyRelease();
case 'mousedown':
return this.onTabbarResizeStart(aEvent);
case 'mouseup':
return this.onTabbarResizeEnd(aEvent);
case 'mousemove':
return this.onTabbarResizing(aEvent);
case 'dblclick':
return this.onTabbarReset(aEvent);
case 'click':
return this.handleNewTabActionOnButton(aEvent);
case 'beforecustomization':
this.window.TreeStyleTabWindowHelper.destroyToolbarItems();
return;
case 'aftercustomization':
this.window.TreeStyleTabWindowHelper.initToolbarItems();
return;
case 'SubBrowserAdded':
return this.initTabBrowser(aEvent.originalTarget.browser);
case 'SubBrowserRemoveRequest':
return this.destroyTabBrowser(aEvent.originalTarget.browser);
case 'UIOperationHistoryUndo:TabbarOperations':
switch (aEvent.entry.name)
{
case 'treestyletab-changeTabbarPosition':
this.position = aEvent.entry.data.oldPosition;
return;
case 'treestyletab-changeTabbarPosition-private':
aEvent.entry.data.target.treeStyleTab.position = aEvent.entry.data.oldPosition;
return;
}
return;
case 'UIOperationHistoryRedo:TabbarOperations':
switch (aEvent.entry.name)
{
case 'treestyletab-changeTabbarPosition':
this.position = aEvent.entry.data.newPosition;
return;
case 'treestyletab-changeTabbarPosition-private':
aEvent.entry.data.target.treeStyleTab.position = aEvent.entry.data.newPosition;
return;
}
return;
}
},
keyEventListening : false,
keyEventListeningFlags : 0,
LISTEN_FOR_AUTOHIDE : 1,
LISTEN_FOR_AUTOEXPAND_BY_FOCUSCHANGE : 2,
startListenKeyEventsFor : function TSTWindow_startListenKeyEventsFor(aReason)
{
if (this.keyEventListeningFlags & aReason)
return;
if (!this.keyEventListening) {
let w = this.window;
w.addEventListener('keydown', this, true);
w.addEventListener('keyup', this, true);
w.addEventListener('keypress', this, true);
w.addEventListener('blur', this, true);
this.keyEventListening = true;
}
this.keyEventListeningFlags |= aReason;
},
endListenKeyEventsFor : function TSTWindow_endListenKeyEventsFor(aReason)
{
if (!(this.keyEventListeningFlags & aReason))
return;
this.keyEventListeningFlags ^= aReason;
if (!this.keyEventListeningFlags && this.keyEventListening) {
let w = this.window;
w.removeEventListener('keydown', this, true);
w.removeEventListener('keyup', this, true);
w.removeEventListener('keypress', this, true);
w.removeEventListener('blur', this, true);
this.keyEventListening = false;
}
},
onKeyDown : function TSTWindow_onKeyDown(aEvent)
{
/**
* On Mac OS X, default accel key is the Command key (metaKey), but
* Cmd-Tab is used to switch applications by the OS itself. So Firefox
* uses Ctrl-Tab to switch tabs on all platforms.
*/
// this.accelKeyPressed = this.isAccelKeyPressed(aEvent);
this.accelKeyPressed = aEvent.ctrlKey || aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL;
var left = aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_LEFT;
var right = aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RIGHT;
var up = aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_UP;
var down = aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_DOWN;
if (
Services.focus.focusedElement == this.browser.selectedTab &&
(up || down || left || right)
)
this.arrowKeyEventOnTab = {
keyCode : aEvent.keyCode,
left : left,
right : right,
up : up,
down : down,
altKey : aEvent.altKey,
ctrlKey : aEvent.ctrlKey,
metaKey : aEvent.metaKey,
shiftKey : aEvent.shiftKey
};
var b = this.browser;
var data = {
sourceEvent : aEvent
};
/* PUBLIC API */
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_KEY_DOWN, b, true, false, data);
// for backward compatibility
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_KEY_DOWN.replace(/^nsDOM/, ''), b, true, false, data);
},
accelKeyPressed : false,
arrowKeyEventOnTab : null,
onKeyRelease : function TSTWindow_onKeyRelease(aEvent)
{
var b = this.browser;
if (!b || !b.treeStyleTab)
return;
var sv = b.treeStyleTab;
var scrollDown,
scrollUp;
// this.accelKeyPressed = this.isAccelKeyPressed(aEvent);
this.accelKeyPressed = aEvent.ctrlKey || aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_CONTROL;
this.window.setTimeout(function(aSelf) {
aSelf.arrowKeyEventOnTab = null;
}, 10, this);
var standBy = scrollDown = scrollUp = (!aEvent.altKey && this.accelKeyPressed);
scrollDown = scrollDown && (
!aEvent.shiftKey &&
(
aEvent.keyCode == aEvent.DOM_VK_TAB ||
aEvent.keyCode == aEvent.DOM_VK_PAGE_DOWN
)
);
scrollUp = scrollUp && (
aEvent.shiftKey ? (aEvent.keyCode == aEvent.DOM_VK_TAB) : (aEvent.keyCode == aEvent.DOM_VK_PAGE_UP)
);
var onlyShiftKey = (!aEvent.shiftKey && aEvent.keyCode == 16 && (aEvent.type == 'keyup' || aEvent.charCode == 0));
var data = {
scrollDown : scrollDown,
scrollUp : scrollUp,
standBy : standBy,
onlyShiftKey : onlyShiftKey,
sourceEvent : aEvent
};
if (
scrollDown ||
scrollUp ||
( // when you release "shift" key
standBy && onlyShiftKey
)
) {
/* PUBLIC API */
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_START, b, true, false, data);
// for backward compatibility
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_START.replace(/^nsDOM/, ''), b, true, false, data);
return;
}
if (aEvent.type == 'keypress' ?
// ignore keypress on Ctrl-R, Ctrl-T, etc.
aEvent.charCode != 0 :
// ignore keyup not on the Ctrl key
aEvent.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_CONTROL
)
return;
// when you just release accel key...
/* PUBLIC API */
let (event) {
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END, b, true, false, data);
// for backward compatibility
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END.replace(/^nsDOM/, ''), b, true, false, data);
}
if (this._tabShouldBeExpandedAfterKeyReleased) {
let tab = this._tabShouldBeExpandedAfterKeyReleased;
if (this.hasChildTabs(tab) &&
this.isSubtreeCollapsed(tab)) {
this.getTabBrowserFromChild(tab)
.treeStyleTab
.collapseExpandTreesIntelligentlyFor(tab);
}
}
this._tabShouldBeExpandedAfterKeyReleased = null;
},
// When the window lose its focus, we cannot detect any key-release events.
// So we have to simulate key-release event manually.
// See: https://github.com/piroor/treestyletab/issues/654
simulateKeyRelease : function TSTWindow_simulateKeyRelease()
{
if (!this.accelKeyPressed)
return;
this.accelKeyPressed = false;
var data = {
scrollDown : false,
scrollUp : false,
standBy : false,
onlyShiftKey : false,
sourceEvent : null
};
/* PUBLIC API */
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END, this.browser, true, false, data);
// for backward compatibility
this.fireCustomEvent(this.kEVENT_TYPE_TAB_FOCUS_SWITCHING_END.replace(/^nsDOM/, ''), this.browser, true, false, data);
},
get shouldListenKeyEventsForAutoExpandByFocusChange()
{
return !this.ctrlTabPreviewsEnabled &&
(
utils.getTreePref('autoExpandSubtreeOnSelect.whileFocusMovingByShortcut') ||
utils.getTreePref('autoCollapseExpandSubtreeOnSelect')
);
},
get ctrlTabPreviewsEnabled()
{
return 'allTabs' in this.window &&
prefs.getPref('browser.ctrlTab.previews');
},
// for Firefox 29 and later, after https://bugzilla.mozilla.org/show_bug.cgi?id=942374
receiveMessage : function TSTWindow_receiveMessage(aMessage)
{
var browser = aMessage.target;
var tabbrowser = this.getTabBrowserFromChild(browser);
if (!tabbrowser)
return;
var tab = tabbrowser.treeStyleTab.getTabFromBrowser(browser);
if (!tab)
return;
switch (aMessage.name)
{
case 'SessionStore:restoreTabContentStarted':
return tabbrowser.treeStyleTab.onRestoreTabContentStarted(tab);
}
},
onTabbarResizeStart : function TSTWindow_onTabbarResizeStart(aEvent)
{
if (aEvent.button != 0)
return;
if (!this.isEventFiredOnGrippy(aEvent))
aEvent.stopPropagation();
if ('setCapture' in aEvent.currentTarget)
aEvent.currentTarget.setCapture(true);
aEvent.currentTarget.addEventListener('mousemove', this, false);
var b = this.getTabBrowserFromChild(aEvent.currentTarget);
var box = aEvent.currentTarget.id == 'treestyletab-tabbar-resizer-splitter' ?
this.getTabStrip(b) :
b.treeStyleTab.tabStripPlaceHolder || b.tabContainer ;
this.tabbarResizeStartWidth = box.boxObject.width;
this.tabbarResizeStartHeight = box.boxObject.height;
this.tabbarResizeStartX = aEvent.screenX;
this.tabbarResizeStartY = aEvent.screenY;
},
onTabbarResizeEnd : function TSTWindow_onTabbarResizeEnd(aEvent)
{
if (this.tabbarResizeStartWidth < 0)
return;
var target = aEvent.currentTarget;
var b = this.getTabBrowserFromChild(target);
aEvent.stopPropagation();
if ('releaseCapture' in target)
target.releaseCapture();
target.removeEventListener('mousemove', this, false);
this.tabbarResizeStartWidth = -1;
this.tabbarResizeStartHeight = -1;
this.tabbarResizeStartX = -1;
this.tabbarResizeStartY = -1;
this.Deferred.next(function() {
b.treeStyleTab.fixTooNarrowTabbar();
}).error(this.defaultDeferredErrorHandler);
},
onTabbarResizing : function TSTWindow_onTabbarResizing(aEvent)
{
var target = aEvent.currentTarget;
var b = this.getTabBrowserFromChild(target);
var expanded = target.id == 'treestyletab-tabbar-resizer-splitter';
if (expanded)
aEvent.stopPropagation();
var width = this.tabbarResizeStartWidth;
var height = this.tabbarResizeStartHeight;
var pos = b.treeStyleTab.position;
if (b.treeStyleTab.isVertical) {
let delta = aEvent.screenX - this.tabbarResizeStartX;
width += (pos == 'left' ? delta : -delta );
width = this.maxTabbarWidth(width, b);
if (expanded || b.treeStyleTab.autoHide.expanded) {
this.setPrefForActiveWindow(function() {
utils.setTreePref('tabbar.width', width);
});
if (b.treeStyleTab.autoHide.mode == b.treeStyleTab.autoHide.kMODE_SHRINK &&
b.treeStyleTab.tabStripPlaceHolder)
b.treeStyleTab.tabStripPlaceHolder.setAttribute('width', utils.getTreePref('tabbar.shrunkenWidth'));
}
else {
this.setPrefForActiveWindow(function() {
utils.setTreePref('tabbar.shrunkenWidth', width);
});
}
}
else {
let delta = aEvent.screenY - this.tabbarResizeStartY;
height += (pos == 'top' ? delta : -delta );
this.setPrefForActiveWindow(function() {
utils.setTreePref('tabbar.height', this.maxTabbarHeight(height, b));
});
}
b.treeStyleTab.updateFloatingTabbar(this.kTABBAR_UPDATE_BY_TABBAR_RESIZE);
},
tabbarResizeStartWidth : -1,
tabbarResizeStartHeight : -1,
tabbarResizeStartX : -1,
tabbarResizeStartY : -1,
onTabbarReset : function TSTWindow_onTabbarReset(aEvent)
{
if (aEvent.button != 0)
return;
var b = this.getTabBrowserFromChild(aEvent.currentTarget);
if (b) {
b.treeStyleTab.resetTabbarSize();
aEvent.stopPropagation();
}
},
onFocusNextTab : function TSTWindow_onFocusNextTab(aEvent)
{
var tab = aEvent.originalTarget;
var b = this.getTabBrowserFromChild(tab);
if (
prefs.getPref('browser.tabs.selectOwnerOnClose') &&
tab.owner &&
(
!b._removingTabs ||
b._removingTabs.indexOf(tab.owner) < 0
)
)
aEvent.preventDefault();
},
showHideSubtreeMenuItem : function TSTWindow_showHideSubtreeMenuItem(aMenuItem, aTabs)
{
if (!aMenuItem ||
aMenuItem.getAttribute('hidden') == 'true' ||
!aTabs ||
!aTabs.length)
return;
var hasSubtree = false;
for (var i = 0, maxi = aTabs.length; i < maxi; i++)
{
if (!this.hasChildTabs(aTabs[i]))
continue;
hasSubtree = true;
break;
}
if (hasSubtree)
aMenuItem.removeAttribute('hidden');
else
aMenuItem.setAttribute('hidden', true);
},
showHideSubTreeMenuItem : function(...aArgs) {
return this.showHideSubtreeMenuItem.apply(this, aArgs);
}, // obsolete, for backward compatibility
updateAeroPeekPreviews : function TSTWindow_updateAeroPeekPreviews()
{
var w = this.window;
if (
this.updateAeroPeekPreviewsTimer ||
!prefs.getPref('browser.taskbar.previews.enable') ||
!utils.getTreePref('taskbarPreviews.hideCollapsedTabs') ||
!('Win7Features' in w) ||
!w.Win7Features ||
!this.AeroPeek ||
!this.AeroPeek.windows
)
return;
this.updateAeroPeekPreviewsTimer = w.setTimeout(function(aSelf) {
aSelf.updateAeroPeekPreviewsTimer = null;
try {
aSelf.updateAeroPeekPreviewsInternal();
}
catch(e) {
dump(e+'\n');
aSelf.updateAeroPeekPreviews();
}
}, 250, this);
},
updateAeroPeekPreviewsTimer : null,
updateAeroPeekPreviewsInternal : function TSTWindow_updateAeroPeekPreviewsInternal()
{
if (
!prefs.getPref('browser.taskbar.previews.enable') ||
!utils.getTreePref('taskbarPreviews.hideCollapsedTabs')
)
return;
this.AeroPeek.windows.some(function(aTabWindow) {
if (aTabWindow.win == this.window) {
let previews = aTabWindow.previews;
for (let i = 0, maxi = previews.length; i < maxi; i++)
{
let preview = previews[i];
if (!preview)
continue;
let tab = preview.controller.wrappedJSObject.tab;
preview.visible = !this.isCollapsed(tab);
}
this.AeroPeek.checkPreviewCount();
return true;
}
return false;
}, this);
},
updateTabsOnTop : function TSTWindow_updateTabsOnTop()
{
if (
this.isPopupWindow ||
this.tabsOnTopChangingByUI ||
this.tabsOnTopChangingByTST
)
return;
var TabsOnTop = this.window.TabsOnTop;
var TabsInTitlebar = this.window.TabsInTitlebar;
var isTopTabbar = this.browser.treeStyleTab.position == 'top';
this.tabsOnTopChangingByTST = true;
try {
if (TabsOnTop) {
let originalState = utils.getTreePref('tabsOnTop.originalState');
if (originalState === null) {
let current = prefs.getDefaultPref('browser.tabs.onTop') === null ?
TabsOnTop.enabled :
prefs.getPref('browser.tabs.onTop') ;
utils.setTreePref('tabsOnTop.originalState', originalState = current);
}
if (!isTopTabbar || !this.browser.treeStyleTab.fixed) {
if (TabsOnTop.enabled)
TabsOnTop.enabled = false;
}
else {
if (TabsOnTop.enabled != originalState)
TabsOnTop.enabled = originalState;
utils.clearTreePref('tabsOnTop.originalState');
}
}
if (TabsInTitlebar) {
let allowed = isTopTabbar && this.browser.treeStyleTab.fixed;
if (
(this.window.TabsOnBottom && utils.getTreePref('compatibility.TabsOnBottom')) ||
('navbarontop' in this.window && utils.getTreePref('compatibility.NavbarOnTitlebar')) ||
('classicthemerestorerjs' in this.window && utils.getTreePref('compatibility.ClassicThemeRestorer'))
)
allowed = true;
TabsInTitlebar.allowedBy('TreeStyleTab-tabsOnTop', allowed);
}
}
finally {
this.tabsOnTopChangingByTST = false;
}
},
onPopupShown : function TSTWindow_onPopupShown(aPopup)
{
if (!aPopup.boxObject ||
this.evaluateXPath(
'parent::*/ancestor-or-self::*[local-name()="tooltip" or local-name()="panel" or local-name()="popup" or local-name()="menupopup"]',
aPopup,
Ci.nsIDOMXPathResult.BOOLEAN_TYPE
).booleanValue)
return;
this.window.setTimeout(function(aSelf) {
if ((!aPopup.boxObject.width && !aPopup.boxObject.height) ||
aPopup.boxObject.popupState == 'closed')
return;
var id = aPopup.id;
var item = id && aSelf.document.getElementById(id) ? id : aPopup ;
var index = aSelf._shownPopups.indexOf(item);
if (index < 0)
aSelf._shownPopups.push(item);
}, 10, this);
},
onPopupHidden : function TSTWindow_onPopupHidden(aPopup)
{
var id = aPopup.id;
aPopup = id && this.document.getElementById(id) ? id : aPopup ;
var index = this._shownPopups.indexOf(aPopup);
if (index > -1)
this._shownPopups.splice(index, 1);
},
isPopupShown : function TSTWindow_isPopupShown()
{
this._shownPopups = this._shownPopups.filter(function(aItem) {
if (typeof aItem == 'string')
aItem = this.document.getElementById(aItem);
return (
aItem &&
aItem.getAttribute(this.kIGNORE_POPUP_STATE) != 'true' &&
aItem.boxObject &&
(aItem.boxObject.width || aItem.boxObject.height) &&
aItem.state != 'closed'
);
}, this);
return this._shownPopups.length > 0;
},
onBeforeNewTabCommand : function TSTWindow_onBeforeNewTabCommand(aTabBrowser)
{
var self = this.windowService || this;
if (self._clickEventOnNewTabButtonHandled)
return;
var b = aTabBrowser || this.browser;
this.readyToOpenRelatedTabAs(b.selectedTab, utils.getTreePref('autoAttach.newTabCommand'));
},
handleNewTabActionOnButton : function TSTWindow_handleNewTabActionOnButton(aEvent)
{
// ignore non new-tab commands (middle click, Ctrl-click)
if (aEvent.button != 1 && (aEvent.button != 0 || !this.isAccelKeyPressed(aEvent)))
return;
var newTabButton = this.getNewTabButtonFromEvent(aEvent);
if (newTabButton) {
this.readyToOpenRelatedTabAs(this.browser.selectedTab, utils.getTreePref('autoAttach.newTabButton'));
let self = this.windowService || this;
self._clickEventOnNewTabButtonHandled = true;
this.Deferred.next(function() {
self._clickEventOnNewTabButtonHandled = false;
});
}
else if (aEvent.target.id == 'urlbar-go-button' || aEvent.target.id == 'go-button') {
this.readyToOpenRelatedTabAs(this.browser.selectedTab, utils.getTreePref('autoAttach.goButton'));
}
},
_clickEventOnNewTabButtonHandled : false,
onBeforeTabDuplicate : function TSTWindow_onBeforeTabDuplicate(aTab, aWhere, aDelta)
{
if (aWhere && aWhere.indexOf('tab') != 0)
return;
var b = this.getTabBrowserFromChild(aTab) || this.browser;
var behaviorPref = !aDelta ? 'autoAttach.duplicateTabCommand' :
aDelta < 0 ? 'autoAttach.duplicateTabCommand.back' :
'autoAttach.duplicateTabCommand.forward'
var behavior = utils.getTreePref(behaviorPref);
this.readyToOpenRelatedTabAs(aTab || b.selectedTab, behavior);
},
onBeforeOpenLink : function TSTWindow_onBeforeOpenLink(aWhere, aOwner)
{
if (aWhere == 'tab' || aWhere == 'tabshifted')
this.handleNewTabFromCurrent(aOwner);
},
onBeforeOpenLinkWithParams : function TSTWindow_onBeforeOpenLinkWithParams(aParams)
{
if (aParams.linkNode &&
!this.checkToOpenChildTab(aParams.linkNode.ownerDocument.defaultView))
this.handleNewTabFromCurrent(aParams.linkNode.ownerDocument.defaultView);
},
onBeforeOpenNewTabByThirdParty : function TSTWindow_onBeforeOpenNewTabByThirdParty(aOwner)
{
if (!this.checkToOpenChildTab(aOwner))
this.handleNewTabFromCurrent(aOwner);
},
onBeforeBrowserAccessOpenURI : function TSTWindow_onBeforeBrowserAccessOpenURI(aOpener, aWhere)
{
if (aOpener &&
this.getTabFromFrame(aOpener.top) &&
aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB)
this.handleNewTabFromCurrent(aOpener);
},
onBeforeViewMedia : function TSTWindow_onBeforeViewMedia(aEvent, aOwner)
{
if (String(this.window.whereToOpenLink(aEvent, false, true)).indexOf('tab') == 0)
this.handleNewTabFromCurrent(aOwner);
},
onBeforeBrowserSearch : function TSTWindow_onBeforeBrowserSearch(aTerm, aForceNewTab)
{
if ((arguments.length == 1 || aForceNewTab) &&
this.shouldOpenSearchResultAsChild(aTerm))
this.handleNewTabFromCurrent();
},
/* Tree Style Tabの初期化が行われる前に復元されたセッションについてツリー構造を復元 */
onTabRestored : function TSTWindow_onTabRestored(aEvent)
{
this._restoringTabs.push(aEvent.originalTarget);
},
processRestoredTabs : function TSTWindow_processRestoredTabs()
{
for (let i = 0, maxi = this._restoringTabs.length; i < maxi; i++)
{
let tab = this._restoringTabs[i];
try {
let b = this.getTabBrowserFromChild(aTab);
if (b)
b.treeStyleTab.handleRestoredTab(aTab);
}
catch(e) {
}
}
this._restoringTabs = [];
},
/* Commands */
setTabbarWidth : function TSTWindow_setTabbarWidth(aWidth, aForceExpanded) /* PUBLIC API */
{
this.browser.treeStyleTab.autoHide.setWidth(aWidth, aForceExpanded);
},
setContentWidth : function TSTWindow_setContentWidth(aWidth, aKeepWindowSize) /* PUBLIC API */
{
var w = this.window;
var treeStyleTab = this.browser.treeStyleTab;
var strip = treeStyleTab.tabStrip;
var tabbarWidth = treeStyleTab.splitterWidth + (treeStyleTab.isVertical ? strip.boxObject.width : 0 );
var contentWidth = this.browser.boxObject.width - tabbarWidth;
if (aKeepWindowSize ||
w.fullScreen ||
w.windowState != Ci.nsIDOMChromeWindow.STATE_NORMAL) {
this.setTabbarWidth(Math.max(10, this.browser.boxObject.width - aWidth));
}
else if (tabbarWidth + aWidth <= w.screen.availWidth) {
w.resizeBy(aWidth - contentWidth, 0);
}
else {
w.resizeBy(w.screen.availWidth - w.outerWidth, 0);
this.setTabbarWidth(this.browser.boxObject.width - aWidth);
}
},
toggleAutoHide : function TSTWindow_toggleAutoHide(aTabBrowser) /* PUBLIC API, for backward compatibility */
{
this.autoHideWindow.toggleMode(aTabBrowser || this.browser);
},
toggleFixed : function TSTWindow_toggleFixed(aTabBrowser) /* PUBLIC API */
{
var b = aTabBrowser || this.browser;
var orient = b.treeStyleTab.isVertical ? 'vertical' : 'horizontal' ;
var newFixed = b.getAttribute(this.kFIXED+'-'+orient) != 'true';
this.setTabbrowserAttribute(this.kFIXED+'-'+orient, newFixed || null, b);
this.setPrefForActiveWindow(function() {
b.treeStyleTab.fixed = newFixed;
utils.setTreePref('tabbar.fixed.'+orient, newFixed);
});
b.treeStyleTab.updateTabbarState();
},
removeTabSubtree : function TSTWindow_removeTabSubtree(aTabOrTabs, aOnlyChildren)
{
var tabs = this.gatherSubtreeMemberTabs(aTabOrTabs, aOnlyChildren);
if (!this.warnAboutClosingTabs(tabs.length))
return;
if (aOnlyChildren)
tabs = this.gatherSubtreeMemberTabs(aTabOrTabs);
var allSubtrees = this.splitTabsToSubtrees(tabs);
for (let i = 0, maxi = allSubtrees.length; i < maxi; i++)
{
let subtreeTabs = allSubtrees[i];
if (!this.fireTabSubtreeClosingEvent(subtreeTabs[0], subtreeTabs))
continue;
let b = this.getTabBrowserFromChild(subtreeTabs[0]);
if (aOnlyChildren)
subtreeTabs = subtreeTabs.slice(1);
if (!subtreeTabs.length)
continue;
this.stopRendering();
this.markAsClosedSet(subtreeTabs);
for (let i = subtreeTabs.length-1; i > -1; i--)
{
b.removeTab(subtreeTabs[i], { animate : true });
}
this.startRendering();
this.fireTabSubtreeClosedEvent(b, subtreeTabs[0], subtreeTabs)
}
},
removeTabSubTree : function(...aArgs) {
return this.removeTabSubtree.apply(this, aArgs);
}, // obsolete, for backward compatibility
fireTabSubtreeClosingEvent : function TSTWindow_fireTabSubtreeClosingEvent(aParentTab, aClosedTabs)
{
var b = this.getTabBrowserFromChild(aParentTab);
var data = {
parent : aParentTab,
tabs : aClosedTabs
};
var canClose = (
/* PUBLIC API */
this.fireCustomEvent(this.kEVENT_TYPE_SUBTREE_CLOSING, b, true, true, data) &&
// for backward compatibility
this.fireCustomEvent(this.kEVENT_TYPE_SUBTREE_CLOSING.replace(/^nsDOM/, ''), b, true, true, data)
);
return canClose;
},
fireTabSubtreeClosedEvent : function TSTWindow_fireTabSubtreeClosedEvent(aTabBrowser, aParentTab, aClosedTabs)
{
aClosedTabs = aClosedTabs.filter(function(aTab) { return !aTab.parentNode; });
var data = {
parent : aParentTab,
tabs : aClosedTabs
};
/* PUBLIC API */
this.fireCustomEvent(this.kEVENT_TYPE_SUBTREE_CLOSED, aTabBrowser, true, false, data);
// for backward compatibility
this.fireCustomEvent(this.kEVENT_TYPE_SUBTREE_CLOSED.replace(/^nsDOM/, ''), aTabBrowser, true, false, data);
},
warnAboutClosingTabSubtreeOf : function TSTWindow_warnAboutClosingTabSubtreeOf(aTab)
{
if (!this.shouldCloseTabSubtreeOf(aTab))
return true;
var tabs = [aTab].concat(this.getDescendantTabs(aTab));
return this.warnAboutClosingTabs(tabs.length);
},
warnAboutClosingTabSubTreeOf : function(...aArgs) {
return this.warnAboutClosingTabSubtreeOf.apply(this, aArgs);
}, // obsolete, for backward compatibility
warnAboutClosingTabs : function TSTWindow_warnAboutClosingTabs(aTabsCount)
{
if (
aTabsCount <= 1 ||
!prefs.getPref('browser.tabs.warnOnClose')
)
return true;
var checked = { value:true };
var w = this.window;
w.focus();
var message = utils.tabbrowserBundle.getFormattedString('tabs.closeWarningMultipleTabs', [aTabsCount]) || // Firefox 28 and older
w.PluralForm.get(aTabsCount, utils.tabbrowserBundle.getString('tabs.closeWarningMultiple')).replace('#1', aTabsCount) ; // Firefox 29 and later
var shouldClose = Services.prompt.confirmEx(w,
utils.tabbrowserBundle.getString('tabs.closeWarningTitle'),
message,
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
(Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
utils.tabbrowserBundle.getString('tabs.closeButtonMultiple'),
null, null,
utils.tabbrowserBundle.getString('tabs.closeWarningPromptMe'),
checked
) == 0;
if (shouldClose && !checked.value)
prefs.setPref('browser.tabs.warnOnClose', false);
return shouldClose;
},
reloadTabSubtree : function TSTWindow_reloadTabSubtree(aTabOrTabs, aOnlyChildren)
{
var tabs = this.gatherSubtreeMemberTabs(aTabOrTabs, aOnlyChildren);
var b = this.getTabBrowserFromChild(tabs[0]);
for (var i = tabs.length-1; i > -1; i--)
{
b.reloadTab(tabs[i]);
}
},
reloadTabSubTree : function(...aArgs) {
return this.reloadTabSubtree.apply(this, aArgs);
}, // obsolete, for backward compatibility
createSubtree : function TSTWindow_createSubtree(aTabs)
{
var rootTabs = this.getRootTabs(aTabs);
var parent = this.getParentTab(aTabs[0]);
var next = aTabs[0];
while (
(next = this.getNextSiblingTab(next)) &&
aTabs.indexOf(next) > -1
);
var b = this.getTabBrowserFromChild(aTabs[0]);
rootTabs.forEach(function(aRootTab) {
var parentTab = this.getParentTab(aRootTab);
var descendantTabs = this.getDescendantTabs(aRootTab);
descendantTabs.reverse().forEach(function(aDescendantTab) {
var inTargets = aTabs.indexOf(aDescendantTab) > -1;
var parentInTargets = aTabs.indexOf(this.getParentTab(aDescendantTab)) > -1;
if (inTargets || (inTargets == parentInTargets))
return;
if (parentTab)
b.treeStyleTab.attachTabTo(aDescendantTab, parentTab, {
dontExpand : true,
dontMove : true,
insertBefore : this.getNextSiblingTab(aRootTab)
});
else
b.treeStyleTab.detachTab(aDescendantTab);
}, this);
}, this);
aTabs = rootTabs;
var shouldCreateGroup = aTabs.length > 1 && utils.getTreePref('createSubtree.underParent');
var root = shouldCreateGroup ?
b.addTab(this.getGroupTabURI({
temporary: utils.getTreePref('createSubtree.underParent.temporaryGroup')
})) :
aTabs.shift() ;
var self = this;
this.Deferred.next(function(self) {
if (shouldCreateGroup) {
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
let tab = aTabs[i];
b.treeStyleTab.attachTabTo(tab, root);
b.treeStyleTab.collapseExpandTab(tab, false);
}
}
if (parent) {
b.treeStyleTab.attachTabTo(root, parent, {
insertBefore : next
});
}
else if (next) {
b.treeStyleTab.moveTabSubtreeTo(root, next._tPos);
}
}).error(this.defaultDeferredErrorHandler);
},
createSubTree : function(...aArgs) {
return this.createSubtree.apply(this, aArgs);
}, // obsolete, for backward compatibility
canCreateSubtree : function TSTWindow_canCreateSubtree(aTabs)
{
var rootTabs = this.getRootTabs(aTabs);
if (rootTabs.length == 1) {
let descendants = this.getDescendantTabs(rootTabs[0]);
// are they already grouped?
// if it is a partial selection, I can create new group.
return (descendants.some(function(aDescendantTab) {
return aTabs.indexOf(aDescendantTab) < 0;
}, this));
}
return true;
},
canCreateSubTree : function(...aArgs) {
return this.canCreateSubtree.apply(this, aArgs);
}, // obsolete, for backward compatibility
getRootTabs : function TSTWindow_getRootTabs(aTabs)
{
var roots = [];
if (!aTabs || !aTabs.length)
return roots;
aTabs = this.cleanUpTabsArray(aTabs);
for (let i = 0, maxi = aTabs.length; i < maxi; i++)
{
let tab = aTabs[i];
let parent = this.getParentTab(tab);
if (parent && aTabs.indexOf(parent) > -1)
continue;
roots.push(tab);
}
return roots;
},
collapseExpandAllSubtree : function TSTWindow_collapseExpandAllSubtree(aCollapse)
{
Services.obs.notifyObservers(
this.window,
this.kTOPIC_COLLAPSE_EXPAND_ALL,
(aCollapse ? 'collapse' : 'open' )
);
},
promoteTab : function TSTWindow_promoteTab(aTab) /* PUBLIC API */
{
var b = this.getTabBrowserFromChild(aTab);
var sv = b.treeStyleTab;
var parent = sv.getParentTab(aTab);
if (!parent)
return;
var nextSibling = sv.getNextSiblingTab(parent);
var grandParent = sv.getParentTab(parent);
if (grandParent) {
sv.attachTabTo(aTab, grandParent, {
insertBefore : nextSibling
});
}
else {
sv.detachTab(aTab);
let index = nextSibling ? nextSibling._tPos : b.mTabContainer.childNodes.length ;
if (index > aTab._tPos)
index--;
b.moveTabTo(aTab, index);
}
},
promoteCurrentTab : function TSTWindow_promoteCurrentTab() /* PUBLIC API */
{
this.promoteTab(this.browser.selectedTab);
},
demoteTab : function TSTWindow_demoteTab(aTab) /* PUBLIC API */
{
var b = this.getTabBrowserFromChild(aTab);
var sv = b.treeStyleTab;
var previous = this.getPreviousSiblingTab(aTab);
if (previous)
sv.attachTabTo(aTab, previous);
},
demoteCurrentTab : function TSTWindow_demoteCurrentTab() /* PUBLIC API */
{
this.demoteTab(this.browser.selectedTab);
},
expandTreeAfterKeyReleased : function TSTWindow_expandTreeAfterKeyReleased(aTab)
{
if (utils.getTreePref('autoCollapseExpandSubtreeOnSelect.whileFocusMovingByShortcut'))
return;
this._tabShouldBeExpandedAfterKeyReleased = aTab || null;
},
_tabShouldBeExpandedAfterKeyReleased : null,
removeAllTabsBut : function TSTWindow_removeAllTabsBut(aTab)
{
var keepTabs = [aTab].concat(this.getDescendantTabs(aTab));
var b = this.getTabBrowserFromChild(aTab);
var closeTabs = this.getTabs(b).filter(function(aTab) {
return keepTabs.indexOf(aTab) < 0 && !aTab.hasAttribute('pinned');
});
if (!this.warnAboutClosingTabs(closeTabs.length))
return;
this.stopRendering();
this.markAsClosedSet(closeTabs);
var tabs = closeTabs.reverse();
for (let i = 0, maxi = tabs.length; i < maxi; i++)
{
b.removeTab(tabs[i]);
}
this.startRendering();
},
// For backward compatibility. You should use DOM event to block TST's focus handling.
registerTabFocusAllowance : function TSTWindow_registerTabFocusAllowance(aProcess) /* PUBLIC API */
{
var listener = {
process : aProcess,
handleEvent : function(aEvent) {
var tab = aEvent.originalTarget;
var b = tab.__treestyletab__linkedTabBrowser;
if (!this.process.call(b.treeStyleTab, b))
aEvent.preventDefault();
}
};
this.window.addEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, listener, false);
this._tabFocusAllowance.push(listener);
},
_tabFocusAllowance : [],
tearOffSubtreeFromRemote : function TSTWindow_tearOffSubtreeFromRemote()
{
var w = this.window;
var remoteTab = w.arguments[0];
var remoteWindow = remoteTab.ownerDocument.defaultView;
var remoteService = remoteWindow.TreeStyleTabService;
var remoteMultipleTabService = remoteWindow.MultipleTabService;
if (remoteService.hasChildTabs(remoteTab) ||
(remoteMultipleTabService && remoteMultipleTabService.isSelected(remoteTab))) {
let remoteBrowser = remoteService.getTabBrowserFromChild(remoteTab);
if (remoteBrowser.treeStyleTab.tabbarDNDObserver.isDraggingAllTabs(remoteTab)) {
w.close();
}
else {
let actionInfo = {
action : remoteTab.__treestyletab__toBeDuplicated ? this.kACTION_DUPLICATE : this.kACTION_IMPORT
};
let b = this.browser;
let blankTab;
this.Deferred
.next(function() {
var blankTab = b.selectedTab;
b.treeStyleTab.tabbarDNDObserver.performDrop(actionInfo, remoteTab);
return blankTab;
})
.next(function(aBlankTab) {
b.removeTab(aBlankTab);
remoteTab = null;
remoteBrowser = null;
remoteWindow = null
remoteService = null;
remoteMultipleTabService = null;
})
.error(this.defaultDeferredErrorHandler);
}
return true;
}
return false;
},
tearOffSubTreeFromRemote : function(...aArgs) {
return this.tearOffSubtreeFromRemote.apply(this, aArgs);
}, // obsolete, for backward compatibility
onPrintPreviewEnter : function TSTWindow_onPrintPreviewEnter()
{
var d = this.document;
var event = d.createEvent('Events');
event.initEvent(this.kEVENT_TYPE_PRINT_PREVIEW_ENTERED, true, false);
d.documentElement.dispatchEvent(event);
// for backward compatibility
event = d.createEvent('Events');
event.initEvent(this.kEVENT_TYPE_PRINT_PREVIEW_ENTERED.replace(/^nsDOM/, ''), true, false);
d.documentElement.dispatchEvent(event);
},
onPrintPreviewExit : function TSTWindow_onPrintPreviewExit()
{
var d = this.document;
var event = d.createEvent('Events');
event.initEvent(this.kEVENT_TYPE_PRINT_PREVIEW_EXITED, true, false);
d.documentElement.dispatchEvent(event);
// for backward compatibility
event = d.createEvent('Events');
event.initEvent(this.kEVENT_TYPE_PRINT_PREVIEW_EXITED.replace(/^nsDOM/, ''), true, false);
d.documentElement.dispatchEvent(event);
},
observe : function TSTWindow_observe(aSubject, aTopic, aData)
{
switch (aTopic)
{
case 'nsPref:changed':
this.onPrefChange(aData);
return;
}
},
get restoringTree() {
if (this._restoringTree || !!this.restoringCount)
return true;
var count = 0;
this.browser.visibleTabs.some(function(aTab) {
if (aTab.linkedBrowser.__treestyletab__toBeRestored)
count++;
return count > 1;
});
return count > 1;
},
set restoringTree(aValue) {
return this._restoringTree = !!aValue;
},
_restoringTree : false,
/* Pref Listener */
domains : [
'extensions.treestyletab',
'browser.ctrlTab.previews'
],
onPrefChange : function TSTWindow_onPrefChange(aPrefName)
{
var value = prefs.getPref(aPrefName);
switch (aPrefName)
{
case 'extensions.treestyletab.tabbar.autoHide.mode':
// don't set on this time, because appearance of all tabbrowsers are not updated yet.
// this.autoHide.mode = utils.getTreePref('tabbar.autoHide.mode');
case 'extensions.treestyletab.tabbar.autoShow.accelKeyDown':
case 'extensions.treestyletab.tabbar.autoShow.tabSwitch':
case 'extensions.treestyletab.tabbar.autoShow.feedback':
this.autoHideWindow.updateKeyListeners(this.window);
break;
case 'extensions.treestyletab.tabbar.style':
case 'extensions.treestyletab.tabbar.position':
this.themeManager.set(prefs.getPref('extensions.treestyletab.tabbar.style'), this.position);
break;
case 'browser.ctrlTab.previews':
this.autoHideWindow.updateKeyListeners(this.window);
case 'extensions.treestyletab.autoCollapseExpandSubtreeOnSelect.whileFocusMovingByShortcut':
case 'extensions.treestyletab.autoCollapseExpandSubtreeOnSelect':
if (this.shouldListenKeyEventsForAutoExpandByFocusChange)
this.startListenKeyEventsFor(this.LISTEN_FOR_AUTOEXPAND_BY_FOCUSCHANGE);
else
this.endListenKeyEventsFor(this.LISTEN_FOR_AUTOEXPAND_BY_FOCUSCHANGE);
break;
default:
break;
}
}
});