treestyletab/modules/window.js
YUKI Hiroshi 4b2fae85b4 Add secret preference to activate Tabs in Titlebar feature without any other addon.
This will make it possible to customize appearance around the navigation bar and the title bar by users.
2016-08-26 17:03:07 +09:00

2056 lines
64 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-2016
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): YUKI "Piro" Hiroshi <piro.outsider.reflex@gmail.com>
* Tetsuharu OHZEKI <https://github.com/saneyuki>
* J. Ryan Stinnett <https://github.com/jryans>
* Ohnuma <https://github.com/lv7777>
*
* 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 ******/
var 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://gre/modules/Timer.jsm');
Cu.import('resource://treestyletab-modules/lib/inherit.jsm');
Cu.import('resource://treestyletab-modules/ReferenceCounter.js');
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');
XPCOMUtils.defineLazyModuleGetter(this, 'ContentBridge', 'resource://treestyletab-modules/contentBridge.js');
XPCOMUtils.defineLazyModuleGetter(this, 'getHashString', 'resource://treestyletab-modules/getHashString.js');
XPCOMUtils.defineLazyServiceGetter(this, 'SessionStore',
'@mozilla.org/browser/sessionstore;1', 'nsISessionStore');
function log(...aArgs) {
utils.log.apply(utils, ['window'].concat(aArgs));
}
function logWithStackTrace(...aArgs) {
utils.logWithStackTrace.apply(utils, ['window'].concat(aArgs));
}
function TreeStyleTabWindow(aWindow)
{
this.window = aWindow;
this.document = aWindow.document;
this._restoringTabs = [];
this._shownPopups = [];
this.restoringCount = 0;
aWindow.addEventListener('DOMContentLoaded', this, true);
ReferenceCounter.add('w,DOMContentLoaded,TSTWindow,true');
aWindow.addEventListener('load', this, false);
ReferenceCounter.add('w,load,TSTWindow,false');
aWindow.TreeStyleTabService = this;
XPCOMUtils.defineLazyModuleGetter(aWindow, 'TreeStyleTabBrowser', 'resource://treestyletab-modules/browser.js');
var isDevEdition = this.window.AppConstants.MOZ_DEV_EDITION;
if (isDevEdition) {
let rootelem = this.document.documentElement;
rootelem.setAttribute('treestyletab-devedition', true);
}
}
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)
{
var setPosition = (function() {
if (this.preInitialized && this.browser.treeStyleTab)
this.browser.treeStyleTab.position = aValue;
else
this.base.position = aValue;
}).bind(this);
if ('UndoTabService' in this.window && this.window.UndoTabService.isUndoable()) {
var current = this.position;
var self = this;
this.window.UndoTabService.doOperation(
setPosition,
{
label : utils.treeBundle.getString('undo_changeTabbarPosition_label'),
name : 'treestyletab-changeTabbarPosition',
data : {
oldPosition : current,
newPosition : aValue
}
}
);
}
else {
setPosition();
}
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 socialBox()
{
return this.document.getElementById('social-sidebar-box');
},
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 */
getPropertyPixelValue : function TSTWindow_getPropertyPixelValue(aElementOrStyle, aProp)
{
var style = aElementOrStyle instanceof this.window.CSSStyleDeclaration ?
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 = getHashString(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 = '';
var contextMenuContentData = this.window.gContextMenuContentData;
log('shouldOpenSearchResultAsChild: contextMenuContentData =', contextMenuContentData);
if (contextMenuContentData && contextMenuContentData.selectionInfo) {
selection = contextMenuContentData.selectionInfo.text;
if (selection) {
selection = getHashString(selection.trim());
log('selection (contextMenuContentData) => ', selection);
}
}
else {
let tab = this.window.gBrowser.selectedTab;
selection = tab.__treestyletab__lastContentSelectionText || '';
log('selection (selectionchange) => ', selection);
// for old Firefox without selectionchange event
if (selection === '' &&
typeof this.window.getBrowserSelection === 'function' &&
tab.linkedBrowser.getAttribute('remote') !== 'true') {
selection = getHashString(this.window.getBrowserSelection().trim());
log('selection (getBrowserSelection) => ', selection);
}
}
return selection == aTerm;
},
kSEARCH_RESULT_DO_NOT_ATTACH : 0,
kSEARCH_RESULT_ATTACH_IF_SELECTED : 1,
kSEARCH_RESULT_ATTACH_ALWAYS : 2,
get isFullscreenAutoHide()
{
return Boolean(
this.window.fullScreen &&
prefs.getPref('browser.fullscreen.autohide') &&
utils.getTreePref('tabbar.autoHide.mode.fullscreen') != AutoHideWindow.prototype.kMODE_DISABLED
);
},
get autoHideWindow()
{
if (!('_autoHideWindow' in this)) {
this._autoHideWindow = new AutoHideWindow(this.window);
}
return this._autoHideWindow;
},
get themeManager()
{
if (!('_themeManager' in this)) {
this._themeManager = new TreeStyleTabThemeManager(this.window);
}
return this._themeManager;
},
getWindowValue : function TSTWindow_getWindowValue(aKey)
{
var value = '';
try {
value = SessionStore.getWindowValue(this.window, aKey);
}
catch(e) {
}
return value;
},
setWindowValue : function TSTWindow_setWindowValue(aKey, aValue)
{
if (aValue === null || aValue === undefined || aValue === '')
return this.deleteWindowValue(this.window, aKey);
try {
SessionStore.setWindowValue(this.window, aKey, String(aValue));
}
catch(e) {
}
return aValue;
},
deleteWindowValue : function TSTWindow_deleteWindowValue(aKey)
{
aTab.removeAttribute(aKey);
try {
SessionStore.setWindowValue(this.window, aKey, '');
SessionStore.deleteWindowValue(this.window, aKey);
}
catch(e) {
}
},
/* Initializing */
preInit : function TSTWindow_preInit()
{
if (this.preInitialized)
return;
this.preInitialized = true;
var w = this.window;
w.removeEventListener('DOMContentLoaded', this, true);
ReferenceCounter.remove('w,DOMContentLoaded,TSTWindow,true');
if (w.location.href.indexOf('chrome://browser/content/browser.xul') != 0)
return;
w.addEventListener('SSTabRestoring', this, true);
ReferenceCounter.add('w,SSTabRestoring,TSTWindow,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);
ReferenceCounter.remove('w,load,TSTWindow,false');
w.addEventListener('unload', this, false);
ReferenceCounter.add('w,unload,TSTWindow,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);
ReferenceCounter.remove('w,SSTabRestoring,TSTWindow,true');
var d = this.document;
d.addEventListener('popupshowing', this, false);
ReferenceCounter.add('d,popupshowing,TSTWindow,false');
d.addEventListener('popuphiding', this, true);
ReferenceCounter.add('d,popuphiding,TSTWindow,true');
d.addEventListener(this.kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED, this, false);
ReferenceCounter.add('d,kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED,TSTWindow,false');
d.addEventListener(this.kEVENT_TYPE_TABBAR_POSITION_CHANGED, this, false);
ReferenceCounter.add('d,kEVENT_TYPE_TABBAR_POSITION_CHANGED,TSTWindow,false');
d.addEventListener(this.kEVENT_TYPE_TABBAR_STATE_CHANGED, this, false);
ReferenceCounter.add('d,kEVENT_TYPE_TABBAR_STATE_CHANGED,TSTWindow,false');
d.addEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, this, false);
ReferenceCounter.add('d,kEVENT_TYPE_FOCUS_NEXT_TAB,TSTWindow,false');
w.addEventListener('beforecustomization', this, true);
ReferenceCounter.add('w,beforecustomization,TSTWindow,true');
w.addEventListener('aftercustomization', this, false);
ReferenceCounter.add('w,aftercustomization,TSTWindow,false');
w.messageManager.addMessageListener('SessionStore:restoreTabContentStarted', this);
this.fullscreenObserver = new FullscreenObserver(this.window);
this.initUIShowHideObserver();
if (!this.isMac)
this.initMenubarShowHideObserver();
var appcontent = d.getElementById('appcontent');
appcontent.addEventListener('SubBrowserAdded', this, false);
ReferenceCounter.add('appcontent,SubBrowserAdded,TSTWindow,false');
appcontent.addEventListener('SubBrowserRemoveRequest', this, false);
ReferenceCounter.add('appcontent,SubBrowserRemoveRequest,TSTWindow,false');
w.addEventListener('UIOperationHistoryUndo:TabbarOperations', this, false);
ReferenceCounter.add('w,UIOperationHistoryUndo:TabbarOperations,TSTWindow,false');
w.addEventListener('UIOperationHistoryRedo:TabbarOperations', this, false);
ReferenceCounter.add('w,UIOperationHistoryRedo:TabbarOperations,TSTWindow,false');
prefs.addPrefListener(this);
this.initUninstallationListener();
ContentBridge.install(w);
w.TreeStyleTabWindowHelper.onBeforeBrowserInit();
this.initTabBrowser(this.browser);
w.TreeStyleTabWindowHelper.onAfterBrowserInit();
this.processRestoredTabs();
this.updateTabsInTitlebar();
this.autoHideWindow; // initialize
this.onPrefChange('extensions.treestyletab.autoCollapseExpandSubtreeOnSelect.whileFocusMovingByShortcut');
this.initialized = true;
},
initialized : false,
initUninstallationListener : function TSTWindow_initUninstallationListener()
{
var restorePrefs = function() {
}.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 = aItem.tab.getAttribute(this.kNEST) + 'em';
}, this);
},
initUIShowHideObserver : function TSTWindow_initUIShowHideObserver()
{
this.rootElementObserver = new BrowserUIShowHideObserver(this, this.document.documentElement, {
childList : false,
subtree : false
});
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);
var socialBox = this.socialBox;
if (socialBox)
this.socialBoxObserver = new BrowserUIShowHideObserver(this, socialBox);
},
initMenubarShowHideObserver : function TSTWindow_initMenubarShowHideObserver()
{
var w = this.window;
var MutationObserver = w.MutationObserver || w.MozMutationObserver;
this.menubarShowHideObserver = new MutationObserver((function(aMutations, aObserver) {
this.updateTabsInTitlebar();
}).bind(this));
this.menubarShowHideObserver.observe(w.document.getElementById('toolbar-menubar'), {
attributes : true,
attributeFilter : ['autohide']
});
},
destroy : function TSTWindow_destroy()
{
var w = this.window;
if (this.browser) {
this.base.inWindowDestoructionProcess = true;
try {
w.removeEventListener('unload', this, false);
ReferenceCounter.remove('w,unload,TSTWindow,false');
w.TreeStyleTabWindowHelper.destroyToolbarItems();
this.autoHideWindow.destroy();
this._autoHideWindow = undefined;
this.themeManager.destroy();
this._themeManager = undefined;
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);
ReferenceCounter.remove('d,popupshowing,TSTWindow,false');
d.removeEventListener('popuphiding', this, true);
ReferenceCounter.remove('d,popuphiding,TSTWindow,true');
d.removeEventListener(this.kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED, this, false);
ReferenceCounter.remove('d,kEVENT_TYPE_TAB_COLLAPSED_STATE_CHANGED,TSTWindow,false');
d.removeEventListener(this.kEVENT_TYPE_TABBAR_POSITION_CHANGED, this, false);
ReferenceCounter.remove('d,kEVENT_TYPE_TABBAR_POSITION_CHANGED,TSTWindow,false');
d.removeEventListener(this.kEVENT_TYPE_TABBAR_STATE_CHANGED, this, false);
ReferenceCounter.remove('d,kEVENT_TYPE_TABBAR_STATE_CHANGED,TSTWindow,false');
d.removeEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, this, false);
ReferenceCounter.remove('d,kEVENT_TYPE_FOCUS_NEXT_TAB,TSTWindow,false');
w.removeEventListener('beforecustomization', this, true);
ReferenceCounter.remove('w,beforecustomization,TSTWindow,true');
w.removeEventListener('aftercustomization', this, false);
ReferenceCounter.remove('w,aftercustomization,TSTWindow,false');
w.messageManager.removeMessageListener('SessionStore:restoreTabContentStarted', this);
ContentBridge.uninstall(w);
this.fullscreenObserver.destroy();
delete this.fullscreenObserver;
if (this.rootElementObserver) {
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;
}
if (this.socialBoxObserver) {
this.socialBoxObserver.destroy();
delete this.socialBoxObserver;
}
if (this.menubarShowHideObserver) {
this.menubarShowHideObserver.disconnect();
delete this.menubarShowHideObserver;
}
for (let i = 0, maxi = this._tabFocusAllowance.length; i < maxi; i++)
{
w.removeEventListener(this.kEVENT_TYPE_FOCUS_NEXT_TAB, this._tabFocusAllowance[i], false);
ReferenceCounter.remove('w,kEVENT_TYPE_FOCUS_NEXT_TAB,_tabFocusAllowance['+i+'],false');
}
var appcontent = d.getElementById('appcontent');
appcontent.removeEventListener('SubBrowserAdded', this, false);
ReferenceCounter.remove('appcontent,SubBrowserAdded,TSTWindow,false');
appcontent.removeEventListener('SubBrowserRemoveRequest', this, false);
ReferenceCounter.remove('appcontent,SubBrowserRemoveRequest,TSTWindow,false');
w.removeEventListener('UIOperationHistoryUndo:TabbarOperations', this, false);
ReferenceCounter.remove('w,UIOperationHistoryUndo:TabbarOperations,TSTWindow,false');
w.removeEventListener('UIOperationHistoryRedo:TabbarOperations', this, false);
ReferenceCounter.remove('w,UIOperationHistoryRedo:TabbarOperations,TSTWindow,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.updateTabsInTitlebar();
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':
let activeWindow = Cc['@mozilla.org/focus-manager;1']
.getService(Ci.nsIFocusManager)
.activeWindow;
if (!activeWindow || activeWindow != this.window)
this.simulateKeyRelease();
return;
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':
if (aEvent.currentTarget.localName == 'splitter')
this.onTabbarSplitterClick(aEvent);
else
this.handleNewTabActionOnButton(aEvent);
return;
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);
ReferenceCounter.add('w,keydown,TSTWindow,true');
w.addEventListener('keyup', this, true);
ReferenceCounter.add('w,keyup,TSTWindow,true');
w.addEventListener('keypress', this, true);
ReferenceCounter.add('w,keypress,TSTWindow,true');
w.addEventListener('blur', this, true);
ReferenceCounter.add('w,blur,TSTWindow,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);
ReferenceCounter.remove('w,keydown,TSTWindow,true');
w.removeEventListener('keyup', this, true);
ReferenceCounter.remove('w,keyup,TSTWindow,true');
w.removeEventListener('keypress', this, true);
ReferenceCounter.remove('w,keypress,TSTWindow,true');
w.removeEventListener('blur', this, true);
ReferenceCounter.remove('w,blur,TSTWindow,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;
setTimeout((function() {
this.arrowKeyEventOnTab = null;
}).bind(this), 10);
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 */
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 (
utils.getTreePref('autoExpandSubtreeOnSelect.whileFocusMovingByShortcut') ||
utils.getTreePref('autoCollapseExpandSubtreeOnSelect')
);
},
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);
ReferenceCounter.add('currentTarget,mousemove,TSTWindow,false');
var b = this.getTabBrowserFromChild(aEvent.currentTarget);
var box;
if (aEvent.currentTarget.id == 'treestyletab-tabbar-resizer-splitter') {
box = this.getTabStrip(b);
}
else {
box = b.treeStyleTab.tabStripPlaceHolder || b.tabContainer;
}
b.treeStyleTab.tabStripPlaceHolder.setAttribute('maxwidth', b.boxObject.width * this.MAX_TABBAR_SIZE_RATIO);
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);
ReferenceCounter.remove('currentTarget,mousemove,TSTWindow,false');
this.tabbarResizeStartWidth = -1;
this.tabbarResizeStartHeight = -1;
this.tabbarResizeStartX = -1;
this.tabbarResizeStartY = -1;
setTimeout((function() {
try {
b.treeStyleTab.fixTooNarrowTabbar();
}
catch(e) {
this.defaultErrorHandler(e);
}
b.treeStyleTab.tabStripPlaceHolder.removeAttribute('maxwidth');
}).bind(this), 0);
},
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) {
log('onTabbarResizing: setting expanded width to '+width);
// b.treeStyleTab.tabbarWidth = width;
b.treeStyleTab.autoHide.expandedWidth = width;
if (b.treeStyleTab.autoHide.mode == b.treeStyleTab.autoHide.kMODE_SHRINK &&
b.treeStyleTab.tabStripPlaceHolder)
b.treeStyleTab.tabStripPlaceHolder.setAttribute('width', b.treeStyleTab.autoHide.shrunkenWidth);
}
else {
log('onTabbarResizing: setting shrunken width to '+width);
b.treeStyleTab.autoHide.shrunkenWidth = width;
}
}
else {
let delta = aEvent.screenY - this.tabbarResizeStartY;
height += (pos == 'top' ? delta : -delta );
height = this.maxTabbarHeight(height, b);
log('onTabbarResizing: setting height to '+height);
b.treeStyleTab.tabbarHeight = height;
}
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();
}
},
onTabbarSplitterClick : function TSTWindow_onTabbarSplitterClick(aEvent)
{
if (
aEvent.button != 1 ||
(aEvent.button == 0 && !isAccelKeyPressed(aEvent))
)
return;
var grippy = utils.evaluateXPath(
'ancestor-or-self::*[local-name()="grippy"]',
aEvent.originalTarget || aEvent.target,
Ci.nsIDOMXPathResult.BOOLEAN_TYPE
).booleanValue;
if (grippy && aEvent.button == 0)
return;
this.onTabbarToggleCollapsed(aEvent.currentTarget);
},
onTabbarToggleCollapsed : function TSTWindow_onTabbarToggleCollapsed(aTarget)
{
var b = this.getTabBrowserFromChild(aTarget);
var splitter = b.treeStyleTab.splitter;
var state = splitter.getAttribute('state');
var newState = state == 'collapsed' ? 'open' : 'collapsed';
splitter.setAttribute('state', newState);
// Workaround for bugs:
// * https://github.com/piroor/treestyletab/issues/593
// * https://github.com/piroor/treestyletab/issues/783
b.ownerDocument.defaultView.setTimeout(function() {
var visible = splitter.getAttribute('state') != 'collapsed';
var tabContainer = b.tabContainer;
if (visible != tabContainer.visible)
tabContainer.visible = visible;
}, 0);
},
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() {
this.updateAeroPeekPreviewsTimer = null;
try {
this.updateAeroPeekPreviewsInternal();
}
catch(e) {
dump(e+'\n');
this.updateAeroPeekPreviews();
}
}).bind(this), 250);
},
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);
},
updateTabsInTitlebar : function TSTWindow_updateTabsInTitlebar()
{
if (
this.isPopupWindow ||
this.tabsInTitlebarChanging
)
return;
this.tabsInTitlebarChanging = true;
// We have to do this with delay, because the tab bar is always on top
// for the toolbar customizing and returned to left or right after a delay.
setTimeout(this.updateTabsInTitlebarInternal.bind(this), 0);
},
updateTabsInTitlebarInternal : function TSTWindow_updateTabsInTitlebarInternal()
{
var TabsInTitlebar = this.window.TabsInTitlebar;
var isTopTabbar = this.browser.treeStyleTab.position == 'top';
try {
if (TabsInTitlebar) {
let menubar = this.window.document.getElementById('toolbar-menubar');
let allowed = (
!utils.getTreePref('blockTabsInTitlebar') ||
(isTopTabbar && this.browser.treeStyleTab.fixed) ||
(!this.isMac && menubar.getAttribute('autohide') !== 'true')
);
if (
(this.window.TabsOnBottom && utils.getTreePref('compatibility.TabsOnBottom')) ||
('classicthemerestorerjs' in this.window && utils.getTreePref('compatibility.ClassicThemeRestorer'))
)
allowed = true;
TabsInTitlebar.allowedBy('TreeStyleTab-tabsInTitlebar', allowed);
}
}
finally {
this.tabsInTitlebarChanging = false;
}
},
onPopupShown : function TSTWindow_onPopupShown(aPopup)
{
if (!aPopup.boxObject ||
utils.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;
setTimeout((function() {
if ((!aPopup.boxObject.width && !aPopup.boxObject.height) ||
aPopup.boxObject.popupState == 'closed')
return;
var id = aPopup.id;
var item = id && this.document.getElementById(id) ? id : aPopup ;
var index = this._shownPopups.indexOf(item);
if (index < 0)
this._shownPopups.push(item);
}).bind(this), 10);
},
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;
setTimeout(function() {
self._clickEventOnNewTabButtonHandled = false;
}, 0);
}
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(aWindow, aTab, aDelta)
{
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);
},
onBeforeOpenLinkWithTab : function TSTWindow_onBeforeOpenLinkWithTab(aTab, aParams)
{
if (!aTab)
return;
log('onBeforeOpenLinkWithTab: ', [aTab, aParams, this.checkToOpenChildTab(aTab)]);
if (!this.checkToOpenChildTab(aTab)) {
if (!aParams.fromChrome)
this.handleNewTabFromCurrent(aTab);
else if (!aParams.relatedToCurrent && !aParams.referrerURI)
this.readyToOpenOrphanTabNow(aTab);
}
},
onBeforeOpenNewTabByThirdParty : function TSTWindow_onBeforeOpenNewTabByThirdParty(aOwner)
{
log('onBeforeOpenNewTabByThirdParty: ', [aOwner, this.checkToOpenChildTab(aTab)]);
if (!this.checkToOpenChildTab(aOwner)) {
this.handleNewTabFromCurrent(aOwner);
}
},
onBeforeBrowserAccessOpenURI : function TSTWindow_onBeforeBrowserAccessOpenURI(aParamsOrOpener, aWhere, aContext)
{
var hasOwnerTab = false;
var opener = null;
if (aParamsOrOpener) {
if (aParamsOrOpener instanceof Ci.nsIDOMWindow) {
log('onBeforeBrowserAccessOpenURI: opener is DOMWindow');
opener = aParamsOrOpener;
hasOwnerTab = this.getTabFromFrame(opener.top);
log(' opener =>', [opener, hasOwnerTab]);
}
else if (Ci.nsIOpenURIInFrameParams &&
aParamsOrOpener instanceof Ci.nsIOpenURIInFrameParams) {
log('TSTWindow_onBeforeBrowserAccessOpenURI: opener is nsIOpenURIInFrameParams');
log(' params => ', aParamsOrOpener);
// from remote contents, we have to detect its opener from the URI.
let referrer = aParamsOrOpener.referrer;
if (referrer) {
let referrerHash = getHashString(referrer);
let activeTab = this.browser.selectedTab;
let possibleOwners = [activeTab].concat(this.getAncestorTabs(activeTab));
for (let i = 0, maxi = possibleOwners.length; i < maxi; i++) {
let possibleOwner = possibleOwners[i];
let contentLocations = possibleOwner.__treestyletab__contentLocations ||
[getHashString(possibleOwner.linkedBrowser.currentURI.spec)];
if (contentLocations.indexOf(referrerHash) < 0)
continue;
hasOwnerTab = true;
opener = possibleOwner.linkedBrowser;
break;
}
}
log(' opener =>', [opener, hasOwnerTab]);
}
}
else {
log('onBeforeBrowserAccessOpenURI: no params is given');
}
if (aParamsOrOpener &&
hasOwnerTab &&
aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB)
this.handleNewTabFromCurrent(opener);
else
this.readyToOpenOrphanTabNow(opener);
},
onBeforeGoHome : function TSTWindow_onBeforeGoHome(aEvent, aTabBrowser)
{
if (!aEvent || aEvent.button === 2 || !aTabBrowser)
return aEvent;
var where = this.window.whereToOpenLink(aEvent, false, true);
if (where == 'current' && aTabBrowser.selectedTab.pinned)
where = 'tab';
var openAsFlatTabs = where === 'current';
// Loading home pages into the current tab will replaces the current
// tab with the first home page and others are opened as child tabs.
// To avoid such odd behavior, we always open multiple home pages as
// a new group.
// See also: https://github.com/piroor/treestyletab/issues/1063
var homePages = this.window.gHomeButton.getHomePage().split('|').filter(function(aURI) {
return aURI;
});
if (where.indexOf('tab') !== 0 &&
homePages.length > 1) {
where = 'tab';
aEvent = utils.wrapEventAsNewTabAction(aEvent);
openAsFlatTabs = true;
}
if (openAsFlatTabs) {
this.readyToOpenOrphanTabNow(aTabBrowser);
aTabBrowser.treeStyleTab.nextOpenedTabToBeParent = false;
}
else {
if (where.indexOf('tab') === 0)
this.readyToOpenNewTabGroupNow(aTabBrowser);
else
this.readyToOpenOrphanTabNow(aTabBrowser);
}
return aEvent;
},
onBeforeViewMedia : function TSTWindow_onBeforeViewMedia(aEvent, aOwner)
{
var where = String(this.window.whereToOpenLink(aEvent, false, true));
log('onBeforeViewMedia: ', [aEvent, aOwner, where]);
if (where.indexOf('tab') == 0)
this.handleNewTabFromCurrent(aOwner);
else
this.readyToOpenOrphanTabNow(aOwner);
},
onBeforeBrowserSearch : function TSTWindow_onBeforeBrowserSearch(aTerm, aForceNewTab)
{
log('onBeforeBrowserSearch: ', [aTerm, aForceNewTab, this.shouldOpenSearchResultAsChild(aTerm)]);
if ((arguments.length == 1 || aForceNewTab) &&
this.shouldOpenSearchResultAsChild(aTerm))
this.handleNewTabFromCurrent();
else
this.readyToOpenOrphanTabNow();
},
/* 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);
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.markAsClosedSet(subtreeTabs);
for (let i = subtreeTabs.length-1; i > -1; i--)
{
b.removeTab(subtreeTabs[i], { animate : true });
}
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 = w.PluralForm.get(aTabsCount, utils.tabbrowserBundle.getString('tabs.closeWarningMultiple')).replace('#1', aTabsCount);
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)
{
log('TSTWindow_createSubtree\n'+aTabs.map(function(aTab) {
return ' '+aTab._tPos+': '+aTab.linkedBrowser.currentURI.spec;
}).join('\n'));
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;
log(' detaching unselected descendant: '+aDescendantTab._tPos+': '+aDescendantTab.linkedBrowser.currentURI.spec);
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(utils.getGroupTabURI({
temporary: utils.getTreePref('createSubtree.underParent.temporaryGroup')
})) :
aTabs.shift() ;
setTimeout((function() {
try {
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);
}
}
catch(e) {
this.defaultErrorHandler(e);
}
}).bind(this), 0);
},
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.markAsClosedSet(closeTabs);
var tabs = closeTabs.reverse();
for (let i = 0, maxi = tabs.length; i < maxi; i++)
{
b.removeTab(tabs[i]);
}
},
// 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);
ReferenceCounter.add('window,kEVENT_TYPE_FOCUS_NEXT_TAB,listener,false');
this._tabFocusAllowance.push(listener);
},
_tabFocusAllowance : [],
tearOffSubtreeFromRemote : function TSTWindow_tearOffSubtreeFromRemote(aRemoteTab)
{
log('tearOffSubtreeFromRemote');
var w = this.window;
var remoteWindow = aRemoteTab.ownerDocument.defaultView;
var remoteService = remoteWindow.TreeStyleTabService;
var remoteMultipleTabService = remoteWindow.MultipleTabService;
if (remoteService.hasChildTabs(aRemoteTab) ||
(remoteMultipleTabService && remoteMultipleTabService.isSelected(aRemoteTab))) {
let remoteBrowser = remoteService.getTabBrowserFromChild(aRemoteTab);
if (remoteBrowser.treeStyleTab.tabbarDNDObserver.isDraggingAllTabs(aRemoteTab)) {
w.close();
}
else {
let actionInfo = {
action : aRemoteTab.__treestyletab__toBeDuplicated ? this.kACTION_DUPLICATE : this.kACTION_IMPORT
};
let b = this.browser;
setTimeout((function() {
try {
var blankTab = b.selectedTab;
b.treeStyleTab.tabbarDNDObserver.performDrop(actionInfo, aRemoteTab);
setTimeout((function() {
try {
b.removeTab(blankTab);
aRemoteTab = null;
remoteBrowser = null;
remoteWindow = null
remoteService = null;
remoteMultipleTabService = null;
}
catch(e) {
this.defaultErrorHandler(e);
}
}).bind(this), 0);
}
catch(e) {
this.defaultErrorHandler(e);
}
}).bind(this), 0);
}
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'
],
onPrefChange : function TSTWindow_onPrefChange(aPrefName)
{
var value = prefs.getPref(aPrefName);
switch (aPrefName)
{
case 'extensions.treestyletab.tabbar.style':
case 'extensions.treestyletab.tabbar.position':
this.themeManager.set(prefs.getPref('extensions.treestyletab.tabbar.style'), this.position);
break;
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;
}
}
});