2007 lines
62 KiB
JavaScript
2007 lines
62 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>
|
|
*
|
|
* 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.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');
|
|
}
|
|
|
|
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 = 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;
|
|
if (contextMenuContentData && contextMenuContentData.selectionInfo) {
|
|
selection = contextMenuContentData.selectionInfo.text;
|
|
}
|
|
else {
|
|
let tab = this.window.gBrowser.selectedTab;
|
|
selection = tab.__treestyletab__lastContentSelectionText || '';
|
|
// for old Firefox without selectionchange event
|
|
if (selection === '' &&
|
|
typeof this.window.getBrowserSelection === 'function' &&
|
|
tab.linkedBrowser.getAttribute('remote') !== 'true')
|
|
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 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) {
|
|
// 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 {
|
|
b.treeStyleTab.autoHide.shrunkenWidth = width;
|
|
}
|
|
}
|
|
else {
|
|
let delta = aEvent.screenY - this.tabbarResizeStartY;
|
|
height += (pos == 'top' ? delta : -delta );
|
|
height = this.maxTabbarHeight(height, b);
|
|
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 = (
|
|
(isTopTabbar && this.browser.treeStyleTab.fixed) ||
|
|
(!this.isMac && menubar.getAttribute('autohide') !== 'true')
|
|
);
|
|
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-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(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);
|
|
},
|
|
|
|
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(aOpener, aWhere)
|
|
{
|
|
var hasOwnerTab = false;
|
|
var opener = null;
|
|
if (aOpener) {
|
|
if (aOpener instanceof Ci.nsIDOMWindow) {
|
|
log('onBeforeBrowserAccessOpenURI: opener is DOMWindow');
|
|
opener = aOpener;
|
|
hasOwnerTab = this.getTabFromFrame(opener.top);
|
|
log(' opener =>', [opener, hasOwnerTab]);
|
|
}
|
|
else if (Ci.nsIOpenURIInFrameParams &&
|
|
aOpener instanceof Ci.nsIOpenURIInFrameParams) {
|
|
log('TSTWindow_onBeforeBrowserAccessOpenURI: opener is nsIOpenURIInFrameParams');
|
|
// from remote contents, we have to detect its opener from the URI.
|
|
let referrer = aOpener.referrer;
|
|
if (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];
|
|
if (possibleOwner.linkedBrowser.currentURI.spec != referrer)
|
|
continue;
|
|
hasOwnerTab = true;
|
|
opener = possibleOwner.linkedBrowser;
|
|
break;
|
|
}
|
|
}
|
|
log(' opener =>', [opener, hasOwnerTab]);
|
|
}
|
|
}
|
|
if (aOpener &&
|
|
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;
|
|
|
|
var where = this.window.whereToOpenLink(aEvent, false, true);
|
|
if (where == 'current' && aTabBrowser.selectedTab.pinned)
|
|
where = 'tab';
|
|
|
|
if (where.indexOf('tab') === 0)
|
|
this.readyToOpenNewTabGroupNow(aTabBrowser);
|
|
else
|
|
this.readyToOpenOrphanTabNow(aTabBrowser);
|
|
},
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
});
|
|
|