diff options
Diffstat (limited to 'application/palemoon/base/content/tabbrowser.xml')
-rw-r--r-- | application/palemoon/base/content/tabbrowser.xml | 5403 |
1 files changed, 0 insertions, 5403 deletions
diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml deleted file mode 100644 index b5edd54b7..000000000 --- a/application/palemoon/base/content/tabbrowser.xml +++ /dev/null @@ -1,5403 +0,0 @@ -<?xml version="1.0"?> - -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - -<!DOCTYPE bindings [ -<!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" > -%tabBrowserDTD; -]> - -<bindings id="tabBrowserBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <binding id="tabbrowser"> - <resources> - <stylesheet src="chrome://browser/content/tabbrowser.css"/> - </resources> - - <content> - <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/> - <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox" - flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown" - onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();"> - <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer"> - <xul:notificationbox flex="1"> - <xul:hbox flex="1" class="browserSidebarContainer"> - <xul:vbox flex="1" class="browserContainer"> - <xul:stack flex="1" class="browserStack" anonid="browserStack"> - <xul:browser anonid="initialBrowser" type="content-primary" message="true" disablehistory="true" - xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,datetimepicker,authdosprotected"/> - </xul:stack> - </xul:vbox> - </xul:hbox> - </xul:notificationbox> - </xul:tabpanels> - </xul:tabbox> - <children/> - </content> - <implementation implements="nsIDOMEventListener, nsIMessageListener"> - - <property name="tabContextMenu" readonly="true" - onget="return this.tabContainer.contextMenu;"/> - - <field name="tabContainer" readonly="true"> - document.getElementById(this.getAttribute("tabcontainer")); - </field> - <field name="tabs" readonly="true"> - this.tabContainer.childNodes; - </field> - - <property name="visibleTabs" readonly="true"> - <getter><![CDATA[ - if (!this._visibleTabs) - this._visibleTabs = Array.filter(this.tabs, - function (tab) !tab.hidden && !tab.closing); - return this._visibleTabs; - ]]></getter> - </property> - - <field name="closingTabsEnum" readonly="true">({ ALL: 0, OTHER: 1, TO_END: 2 });</field> - - <field name="_visibleTabs">null</field> - - <field name="mURIFixup" readonly="true"> - Components.classes["@mozilla.org/docshell/urifixup;1"] - .getService(Components.interfaces.nsIURIFixup); - </field> - <field name="mFaviconService" readonly="true"> - Components.classes["@mozilla.org/browser/favicon-service;1"] - .getService(Components.interfaces.nsIFaviconService); - </field> - <field name="_placesAutocomplete" readonly="true"> - Components.classes["@mozilla.org/autocomplete/search;1?name=history"] - .getService(Components.interfaces.mozIPlacesAutoComplete); - </field> - <field name="mTabBox" readonly="true"> - document.getAnonymousElementByAttribute(this, "anonid", "tabbox"); - </field> - <field name="mPanelContainer" readonly="true"> - document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer"); - </field> - <field name="mStringBundle"> - document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle"); - </field> - <field name="mCurrentTab"> - null - </field> - <field name="_lastRelatedTab"> - null - </field> - <field name="mCurrentBrowser"> - null - </field> - <field name="mProgressListeners"> - [] - </field> - <field name="mTabsProgressListeners"> - [] - </field> - <field name="mTabListeners"> - [] - </field> - <field name="mTabFilters"> - [] - </field> - <field name="mIsBusy"> - false - </field> - <field name="_outerWindowIDBrowserMap"> - new Map(); - </field> - <field name="arrowKeysShouldWrap" readonly="true"> -#ifdef XP_MACOSX - true -#else - false -#endif - </field> - - <field name="_autoScrollPopup"> - null - </field> - - <field name="_previewMode"> - false - </field> - - <property name="_numPinnedTabs" readonly="true"> - <getter><![CDATA[ - for (var i = 0; i < this.tabs.length; i++) { - if (!this.tabs[i].pinned) - break; - } - return i; - ]]></getter> - </property> - - <property name="popupAnchor" readonly="true"> - <getter><![CDATA[ - if (this.mCurrentTab._popupAnchor) { - return this.mCurrentTab._popupAnchor; - } - let stack = this.mCurrentBrowser.parentNode; - // Create an anchor for the popup - const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - let popupAnchor = document.createElementNS(NS_XUL, "hbox"); - popupAnchor.className = "popup-anchor"; - popupAnchor.hidden = true; - stack.appendChild(popupAnchor); - return this.mCurrentTab._popupAnchor = popupAnchor; - ]]></getter> - </property> - - <method name="updateWindowResizers"> - <body><![CDATA[ - if (!window.gShowPageResizers) - return; - - var show = document.getElementById("addon-bar").collapsed && - window.windowState == window.STATE_NORMAL; - for (let i = 0; i < this.browsers.length; i++) { - this.browsers[i].showWindowResizer = show; - } - ]]></body> - </method> - - <method name="_setCloseKeyState"> - <parameter name="aEnabled"/> - <body><![CDATA[ - let keyClose = document.getElementById("key_close"); - let closeKeyEnabled = keyClose.getAttribute("disabled") != "true"; - if (closeKeyEnabled == aEnabled) - return; - - if (aEnabled) - keyClose.removeAttribute("disabled"); - else - keyClose.setAttribute("disabled", "true"); - - // We also want to remove the keyboard shortcut from the file menu - // when the shortcut is disabled, and bring it back when it's - // renabled. - // - // Fixing bug 630826 could make that happen automatically. - // Fixing bug 630830 could avoid the ugly hack below. - - let closeMenuItem = document.getElementById("menu_close"); - let parentPopup = closeMenuItem.parentNode; - let nextItem = closeMenuItem.nextSibling; - let clonedItem = closeMenuItem.cloneNode(true); - - parentPopup.removeChild(closeMenuItem); - - if (aEnabled) - clonedItem.setAttribute("key", "key_close"); - else - clonedItem.removeAttribute("key"); - - parentPopup.insertBefore(clonedItem, nextItem); - ]]></body> - </method> - - <method name="pinTab"> - <parameter name="aTab"/> - <body><![CDATA[ - if (aTab.pinned) - return; - - if (aTab.hidden) - this.showTab(aTab); - - this.moveTabTo(aTab, this._numPinnedTabs); - aTab.setAttribute("pinned", "true"); - this.tabContainer._unlockTabSizing(); - this.tabContainer._positionPinnedTabs(); - this.tabContainer.adjustTabstrip(); - - this.getBrowserForTab(aTab).docShell.isAppTab = true; - - if (aTab.selected) - this._setCloseKeyState(false); - - let event = document.createEvent("Events"); - event.initEvent("TabPinned", true, false); - aTab.dispatchEvent(event); - ]]></body> - </method> - - <method name="unpinTab"> - <parameter name="aTab"/> - <body><![CDATA[ - if (!aTab.pinned) - return; - - this.moveTabTo(aTab, this._numPinnedTabs - 1); - aTab.setAttribute("fadein", "true"); - aTab.removeAttribute("pinned"); - aTab.style.MozMarginStart = ""; - this.tabContainer._unlockTabSizing(); - this.tabContainer._positionPinnedTabs(); - this.tabContainer.adjustTabstrip(); - - this.getBrowserForTab(aTab).docShell.isAppTab = false; - - if (aTab.selected) - this._setCloseKeyState(true); - - let event = document.createEvent("Events"); - event.initEvent("TabUnpinned", true, false); - aTab.dispatchEvent(event); - ]]></body> - </method> - - <method name="previewTab"> - <parameter name="aTab"/> - <parameter name="aCallback"/> - <body> - <![CDATA[ - let currentTab = this.selectedTab; - try { - // Suppress focus, ownership and selected tab changes - this._previewMode = true; - this.selectedTab = aTab; - aCallback(); - } finally { - this.selectedTab = currentTab; - this._previewMode = false; - } - ]]> - </body> - </method> - - <method name="getBrowserAtIndex"> - <parameter name="aIndex"/> - <body> - <![CDATA[ - return this.browsers[aIndex]; - ]]> - </body> - </method> - - <method name="getBrowserIndexForDocument"> - <parameter name="aDocument"/> - <body> - <![CDATA[ - var tab = this._getTabForContentWindow(aDocument.defaultView); - return tab ? tab._tPos : -1; - ]]> - </body> - </method> - - <method name="getBrowserForDocument"> - <parameter name="aDocument"/> - <body> - <![CDATA[ - var tab = this._getTabForContentWindow(aDocument.defaultView); - return tab ? tab.linkedBrowser : null; - ]]> - </body> - </method> - - <method name="getBrowserForContentWindow"> - <parameter name="aWindow"/> - <body> - <![CDATA[ - var tab = this._getTabForContentWindow(aWindow); - return tab ? tab.linkedBrowser : null; - ]]> - </body> - </method> - - <method name="getBrowserForOuterWindowID"> - <parameter name="aID"/> - <body> - <![CDATA[ - return this._outerWindowIDBrowserMap.get(aID); - ]]> - </body> - </method> - - <method name="_getTabForContentWindow"> - <parameter name="aWindow"/> - <body> - <![CDATA[ - for (let i = 0; i < this.browsers.length; i++) { - if (this.browsers[i].contentWindow == aWindow) - return this.tabs[i]; - } - return null; - ]]> - </body> - </method> - - <!-- Binding from browser to tab --> - <field name="_tabForBrowser" readonly="true"> - <![CDATA[ - new WeakMap(); - ]]> - </field> - - <method name="_getTabForBrowser"> - <parameter name="aBrowser" /> - <body> - <![CDATA[ - let Deprecated = Components.utils.import("resource://gre/modules/Deprecated.jsm", {}).Deprecated; - let text = "_getTabForBrowser` is now deprecated, please use `getTabForBrowser"; - let url = "https://developer.mozilla.org/docs/Mozilla/Tech/XUL/Method/getTabForBrowser"; - Deprecated.warning(text, url); - return this.getTabForBrowser(aBrowser); - ]]> - </body> - </method> - - <method name="getTabForBrowser"> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - return this._tabForBrowser.get(aBrowser); - ]]> - </body> - </method> - - <method name="getNotificationBox"> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - return this.getSidebarContainer(aBrowser).parentNode; - ]]> - </body> - </method> - - <method name="getSidebarContainer"> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - return this.getBrowserContainer(aBrowser).parentNode; - ]]> - </body> - </method> - - <method name="getBrowserContainer"> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - return (aBrowser || this.mCurrentBrowser).parentNode.parentNode; - ]]> - </body> - </method> - - <method name="getTabModalPromptBox"> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - let browser = (aBrowser || this.mCurrentBrowser); - let stack = browser.parentNode; - let self = this; - - let promptBox = { - appendPrompt : function(args, onCloseCallback) { - let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt"); - // stack.appendChild(newPrompt); - stack.insertBefore(newPrompt, browser.nextSibling); - browser.setAttribute("tabmodalPromptShowing", true); - - newPrompt.clientTop; // style flush to assure binding is attached - - let prompts = this.listPrompts(); - if (prompts.length > 1) { - // Let's hide ourself behind the current prompt. - newPrompt.hidden = true; - } - - let tab = self._getTabForContentWindow(browser.contentWindow); - newPrompt.init(args, tab, onCloseCallback); - return newPrompt; - }, - - removePrompt : function(aPrompt) { - stack.removeChild(aPrompt); - - let prompts = this.listPrompts(); - if (prompts.length) { - let prompt = prompts[prompts.length - 1]; - prompt.hidden = false; - prompt.Dialog.setDefaultFocus(); - } else { - browser.removeAttribute("tabmodalPromptShowing"); - browser.focus(); - } - }, - - listPrompts : function(aPrompt) { - let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt"); - // NodeList --> real JS array - let prompts = Array.slice(els); - return prompts; - }, - }; - - return promptBox; - ]]> - </body> - </method> - - <method name="getTabFromAudioEvent"> - <parameter name="aEvent"/> - <body> - <![CDATA[ - if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") || - !aEvent.isTrusted) { - return null; - } - - var browser = aEvent.originalTarget; - var tab = this.getTabForBrowser(browser); - return tab; - ]]> - </body> - </method> - - <method name="_callProgressListeners"> - <parameter name="aBrowser"/> - <parameter name="aMethod"/> - <parameter name="aArguments"/> - <parameter name="aCallGlobalListeners"/> - <parameter name="aCallTabsListeners"/> - <body><![CDATA[ - var rv = true; - - if (!aBrowser) - aBrowser = this.mCurrentBrowser; - - if (aCallGlobalListeners != false && - aBrowser == this.mCurrentBrowser) { - this.mProgressListeners.forEach(function (p) { - if (aMethod in p) { - try { - if (!p[aMethod].apply(p, aArguments)) - rv = false; - } catch (e) { - // don't inhibit other listeners - Components.utils.reportError(e); - } - } - }); - } - - if (aCallTabsListeners != false) { - aArguments.unshift(aBrowser); - - this.mTabsProgressListeners.forEach(function (p) { - if (aMethod in p) { - try { - if (!p[aMethod].apply(p, aArguments)) - rv = false; - } catch (e) { - // don't inhibit other listeners - Components.utils.reportError(e); - } - } - }); - } - - return rv; - ]]></body> - </method> - - <!-- A web progress listener object definition for a given tab. --> - <method name="mTabProgressListener"> - <parameter name="aTab"/> - <parameter name="aBrowser"/> - <parameter name="aStartsBlank"/> - <body> - <![CDATA[ - return ({ - mTabBrowser: this, - mTab: aTab, - mBrowser: aBrowser, - mBlank: aStartsBlank, - - // cache flags for correct status UI update after tab switching - mStateFlags: 0, - mStatus: 0, - mMessage: "", - mTotalProgress: 0, - - // count of open requests (should always be 0 or 1) - mRequestCount: 0, - - destroy: function () { - delete this.mTab; - delete this.mBrowser; - delete this.mTabBrowser; - }, - - _callProgressListeners: function () { - Array.unshift(arguments, this.mBrowser); - return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments); - }, - - _shouldShowProgress: function (aRequest) { - if (this.mBlank) - return false; - - if (gMultiProcessBrowser) - return true; - - // Don't show progress indicators in tabs for about: URIs - // pointing to local resources. - try { - let channel = aRequest.QueryInterface(Ci.nsIChannel); - if (channel.originalURI.schemeIs("about") && - (channel.URI.schemeIs("jar") || channel.URI.schemeIs("file"))) - return false; - } catch (e) {} - - return true; - }, - - onProgressChange: function (aWebProgress, aRequest, - aCurSelfProgress, aMaxSelfProgress, - aCurTotalProgress, aMaxTotalProgress) { - this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0; - - if (!this._shouldShowProgress(aRequest)) - return; - - if (this.mTotalProgress) - this.mTab.setAttribute("progress", "true"); - - this._callProgressListeners("onProgressChange", - [aWebProgress, aRequest, - aCurSelfProgress, aMaxSelfProgress, - aCurTotalProgress, aMaxTotalProgress]); - }, - - onProgressChange64: function (aWebProgress, aRequest, - aCurSelfProgress, aMaxSelfProgress, - aCurTotalProgress, aMaxTotalProgress) { - return this.onProgressChange(aWebProgress, aRequest, - aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, - aMaxTotalProgress); - }, - - onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) { - if (!aRequest) - return; - - var oldBlank = this.mBlank; - - const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; - const nsIChannel = Components.interfaces.nsIChannel; - let location, originalLocation; - try { - aRequest.QueryInterface(nsIChannel) - location = aRequest.URI; - originalLocation = aRequest.originalURI; - } catch (ex) {} - - if (aStateFlags & nsIWebProgressListener.STATE_START) { - this.mRequestCount++; - } - else if (aStateFlags & nsIWebProgressListener.STATE_STOP) { - const NS_ERROR_UNKNOWN_HOST = 2152398878; - if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) { - // to prevent bug 235825: wait for the request handled - // by the automatic keyword resolver - return; - } - // since we (try to) only handle STATE_STOP of the last request, - // the count of open requests should now be 0 - this.mRequestCount = 0; - } - - if (aStateFlags & nsIWebProgressListener.STATE_START && - aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { - if (aWebProgress.isTopLevel) - this.mBrowser.urlbarChangeTracker.startedLoad(); - - if (this._shouldShowProgress(aRequest)) { - if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) { - this.mTab.setAttribute("busy", "true"); - if (!gMultiProcessBrowser) { - if (aWebProgress.isTopLevel && - !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD)) - this.mTabBrowser.setTabTitleLoading(this.mTab); - } - } - - if (this.mTab.selected) - this.mTabBrowser.mIsBusy = true; - } - } - else if (aStateFlags & nsIWebProgressListener.STATE_STOP && - aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { - - if (this.mTab.hasAttribute("busy")) { - this.mTab.removeAttribute("busy"); - this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]); - if (!this.mTab.selected) - this.mTab.setAttribute("unread", "true"); - } - this.mTab.removeAttribute("progress"); - - if (aWebProgress.isTopLevel) { - let isSuccessful = Components.isSuccessCode(aStatus); - if (!isSuccessful && !isTabEmpty(this.mTab)) { - // Restore the current document's location in case the - // request was stopped (possibly from a content script) - // before the location changed. - - this.mBrowser.userTypedValue = null; - - if (this.mTab.selected && gURLBar) - URLBarSetURI(); - } else if (isSuccessful) { - this.mBrowser.urlbarChangeTracker.finishedLoad(); - } - - if (!this.mBrowser.mIconURL) - this.mTabBrowser.useDefaultIcon(this.mTab); - } - - if (this.mBlank) - this.mBlank = false; - - // For keyword URIs clear the user typed value since they will be changed into real URIs - if (location.scheme == "keyword") - this.mBrowser.userTypedValue = null; - - if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.connecting")) - this.mTabBrowser.setTabTitle(this.mTab); - - if (this.mTab.selected) - this.mTabBrowser.mIsBusy = false; - } - - if (oldBlank) { - this._callProgressListeners("onUpdateCurrentBrowser", - [aStateFlags, aStatus, "", 0], - true, false); - } else { - this._callProgressListeners("onStateChange", - [aWebProgress, aRequest, aStateFlags, aStatus], - true, false); - } - - this._callProgressListeners("onStateChange", - [aWebProgress, aRequest, aStateFlags, aStatus], - false); - - if (aStateFlags & (nsIWebProgressListener.STATE_START | - nsIWebProgressListener.STATE_STOP)) { - // reset cached temporary values at beginning and end - this.mMessage = ""; - this.mTotalProgress = 0; - } - this.mStateFlags = aStateFlags; - this.mStatus = aStatus; - }, - - onLocationChange: function (aWebProgress, aRequest, aLocation, - aFlags) { - // OnLocationChange is called for both the top-level content - // and the subframes. - let topLevel = aWebProgress.isTopLevel; - - if (topLevel) { - let isSameDocument = - !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT); - // We need to clear the typed value - // if the document failed to load, to make sure the urlbar reflects the - // failed URI (particularly for SSL errors). However, don't clear the value - // if the error page's URI is about:blank, because that causes complete - // loss of urlbar contents for invalid URI errors (see bug 867957). - if (this.mBrowser.didStartLoadSinceLastUserTyping() || - ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) && - aLocation.spec != "about:blank")) - this.mBrowser.userTypedValue = null; - - // If the browser was playing audio, we should remove the playing state. - if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) { - clearTimeout(this.mTab._soundPlayingAttrRemovalTimer); - this.mTab._soundPlayingAttrRemovalTimer = 0; - this.mTab.removeAttribute("soundplaying"); - this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]); - } - - // If the browser was previously muted, we should restore the muted state. - if (this.mTab.hasAttribute("muted")) { - this.mTab.linkedBrowser.mute(); - } - - // Don't clear the favicon if this onLocationChange was - // triggered by a pushState or a replaceState. See bug 550565. - if (!gMultiProcessBrowser) { - if (aWebProgress.isLoadingDocument && - !(this.mBrowser.docShell.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) - this.mBrowser.mIconURL = null; - } - - let autocomplete = this.mTabBrowser._placesAutocomplete; - if (this.mBrowser.registeredOpenURI) { - autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI); - delete this.mBrowser.registeredOpenURI; - } - // Tabs in private windows aren't registered as "Open" so - // that they don't appear as switch-to-tab candidates. - if (!isBlankPageURL(aLocation.spec) && - (!PrivateBrowsingUtils.isWindowPrivate(window) || - PrivateBrowsingUtils.permanentPrivateBrowsing)) { - autocomplete.registerOpenPage(aLocation); - this.mBrowser.registeredOpenURI = aLocation; - } - } - - if (!this.mBlank) { - this._callProgressListeners("onLocationChange", - [aWebProgress, aRequest, aLocation, - aFlags]); - } - - if (topLevel) { - this.mBrowser.lastURI = aLocation; - this.mBrowser.lastLocationChange = Date.now(); - } - }, - - onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) { - if (this.mBlank) - return; - - this._callProgressListeners("onStatusChange", - [aWebProgress, aRequest, aStatus, aMessage]); - - this.mMessage = aMessage; - }, - - onSecurityChange: function (aWebProgress, aRequest, aState) { - this._callProgressListeners("onSecurityChange", - [aWebProgress, aRequest, aState]); - }, - - onRefreshAttempted: function (aWebProgress, aURI, aDelay, aSameURI) { - return this._callProgressListeners("onRefreshAttempted", - [aWebProgress, aURI, aDelay, aSameURI]); - }, - - QueryInterface: function (aIID) { - if (aIID.equals(Components.interfaces.nsIWebProgressListener) || - aIID.equals(Components.interfaces.nsIWebProgressListener2) || - aIID.equals(Components.interfaces.nsISupportsWeakReference) || - aIID.equals(Components.interfaces.nsISupports)) - return this; - throw Components.results.NS_NOINTERFACE; - } - }); - ]]> - </body> - </method> - - <method name="setIcon"> - <parameter name="aTab"/> - <parameter name="aURI"/> - <parameter name="aLoadingPrincipal"/> - <body> - <![CDATA[ - let browser = this.getBrowserForTab(aTab); - browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI; - - if (aURI && this.mFaviconService) { - if (!(aURI instanceof Ci.nsIURI)) { - aURI = makeURI(aURI); - } - // We do not serialize the principal from within SessionStore.jsm, - // hence if aLoadingPrincipal is null we default to the - // systemPrincipal which will allow the favicon to load. - let loadingPrincipal = aLoadingPrincipal - ? aLoadingPrincipal - : Services.scriptSecurityManager.getSystemPrincipal(); - let loadType = PrivateBrowsingUtils.isWindowPrivate(window) - ? this.mFaviconService.FAVICON_LOAD_PRIVATE - : this.mFaviconService.FAVICON_LOAD_NON_PRIVATE; - - this.mFaviconService.setAndFetchFaviconForPage( - browser.currentURI, aURI, false, loadType, null, loadingPrincipal); - } - - let sizedIconUrl = browser.mIconURL || ""; - if (sizedIconUrl != aTab.getAttribute("image")) { - if (sizedIconUrl) - aTab.setAttribute("image", sizedIconUrl); - else - aTab.removeAttribute("image"); - this._tabAttrModified(aTab, ["image"]); - } - - if (Services.prefs.getBoolPref("browser.chrome.favicons.process")) { - let favImage = new Image; - favImage.src = browser.mIconURL; - var tabBrowser = this; - favImage.onload = function () { - try { - // Draw the icon on a hidden canvas - var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - var tabImg = document.getAnonymousElementByAttribute(aTab, "anonid", "tab-icon"); - var w = tabImg.boxObject.width; - var h = tabImg.boxObject.height; - canvas.width = w; - canvas.height = h; - var ctx = canvas.getContext('2d'); - ctx.drawImage(favImage, 0, 0, w, h); - icon = canvas.toDataURL(); - browser.mIconURL = icon; - aTab.setAttribute("image", icon); - } - catch (e) { - console.warn("Processing of favicon failed."); - // Canvas failed: icon remains as it was - } - tabBrowser._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]); - } - } - - this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]); - ]]> - </body> - </method> - - <method name="getIcon"> - <parameter name="aTab"/> - <body> - <![CDATA[ - let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser; - return browser.mIconURL; - ]]> - </body> - </method> - - <method name="shouldLoadFavIcon"> - <parameter name="aURI"/> - <body> - <![CDATA[ - return (aURI && - Services.prefs.getBoolPref("browser.chrome.site_icons") && - Services.prefs.getBoolPref("browser.chrome.favicons") && - ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https"))); - ]]> - </body> - </method> - - <method name="useDefaultIcon"> - <parameter name="aTab"/> - <body> - <![CDATA[ - // Bug 691610 - e10s support for useDefaultIcon - if (gMultiProcessBrowser) - return; - - var browser = this.getBrowserForTab(aTab); - var docURIObject = browser.contentDocument.documentURIObject; - var icon = null; - <!-- Pale Moon: new image icon method, see bug #305986 --> - let req = browser.contentDocument.imageRequest; - let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size"); - if (browser.contentDocument instanceof ImageDocument && - req && req.image) { - if (Services.prefs.getBoolPref("browser.chrome.site_icons") && sz) { - try { - <!-- Main method: draw on a hidden canvas --> - var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - var tabImg = document.getAnonymousElementByAttribute(aTab, "anonid", "tab-icon"); - var w = tabImg.boxObject.width; - var h = tabImg.boxObject.height; - canvas.width = w; - canvas.height = h; - var ctx = canvas.getContext('2d'); - ctx.drawImage(browser.contentDocument.body.firstChild, 0, 0, w, h); - icon = canvas.toDataURL(); - } - catch (e) { - <!-- Fallback method in case canvas method fails, restricted by sz --> - try { - - if (req && - req.image && - req.image.width <= sz && - req.image.height <= sz) - icon = browser.currentURI; - } - catch (e) { - <!-- Both methods fail (very large or corrupt image): icon remains null --> - } - } - } - } - // Use documentURIObject in the check for shouldLoadFavIcon so that we - // do the right thing with about:-style error pages. Bug 453442 - else if (this.shouldLoadFavIcon(docURIObject)) { - let url = docURIObject.prePath + "/favicon.ico"; - if (!this.isFailedIcon(url)) - icon = url; - } - this.setIcon(aTab, icon, browser.contentPrincipal); - ]]> - </body> - </method> - - <method name="isFailedIcon"> - <parameter name="aURI"/> - <body> - <![CDATA[ - if (this.mFaviconService) { - if (!(aURI instanceof Ci.nsIURI)) - aURI = makeURI(aURI); - return this.mFaviconService.isFailedFavicon(aURI); - } - return null; - ]]> - </body> - </method> - - <method name="getWindowTitleForBrowser"> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - var newTitle = ""; - var docElement = this.ownerDocument.documentElement; - var sep = docElement.getAttribute("titlemenuseparator"); - - // Strip out any null bytes in the content title, since the - // underlying widget implementations of nsWindow::SetTitle pass - // null-terminated strings to system APIs. - var docTitle = aBrowser.contentTitle.replace("\0", "", "g"); - - if (!docTitle) - docTitle = docElement.getAttribute("titledefault"); - - var modifier = docElement.getAttribute("titlemodifier"); - if (docTitle) { - newTitle += docElement.getAttribute("titlepreface"); - newTitle += docTitle; - if (modifier) - newTitle += sep; - } - newTitle += modifier; - - // If location bar is hidden and the URL type supports a host, - // add the scheme and host to the title to prevent spoofing. - // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239 - try { - if (docElement.getAttribute("chromehidden").includes("location")) { - var uri = this.mURIFixup.createExposableURI( - aBrowser.currentURI); - if (uri.scheme == "about") - newTitle = uri.spec + sep + newTitle; - else - newTitle = uri.prePath + sep + newTitle; - } - } catch (e) {} - - return newTitle; - ]]> - </body> - </method> - - <method name="freezeTitlebar"> - <parameter name="aTitle"/> - <body> - <![CDATA[ - this._frozenTitle = aTitle || ""; - this.updateTitlebar(); - ]]> - </body> - </method> - - <method name="unfreezeTitlebar"> - <body> - <![CDATA[ - this._frozenTitle = ""; - this.updateTitlebar(); - ]]> - </body> - </method> - - <method name="updateTitlebar"> - <body> - <![CDATA[ - this.ownerDocument.title = this._frozenTitle || - this.getWindowTitleForBrowser(this.mCurrentBrowser); - ]]> - </body> - </method> - - <method name="updateCurrentBrowser"> - <parameter name="aForceUpdate"/> - <body> - <![CDATA[ - var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex); - if (this.mCurrentBrowser == newBrowser && !aForceUpdate) - return; - - if (!aForceUpdate) { - window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils) - .beginTabSwitch(); - } - - var oldTab = this.mCurrentTab; - - // Preview mode should not reset the owner - if (!this._previewMode && !oldTab.selected) - oldTab.owner = null; - - if (this._lastRelatedTab) { - if (!this._lastRelatedTab.selected) - this._lastRelatedTab.owner = null; - this._lastRelatedTab = null; - } - - var oldBrowser = this.mCurrentBrowser; - if (oldBrowser) { - oldBrowser.setAttribute("type", "content-targetable"); - oldBrowser.docShellIsActive = false; - this.finder.mListeners.forEach(l => oldBrowser.finder.removeResultListener(l)); - } - - var updateBlockedPopups = false; - if (!oldBrowser || - (oldBrowser.blockedPopups && !newBrowser.blockedPopups) || - (!oldBrowser.blockedPopups && newBrowser.blockedPopups)) - updateBlockedPopups = true; - - newBrowser.setAttribute("type", "content-primary"); - newBrowser.docShellIsActive = - (window.windowState != window.STATE_MINIMIZED); - this.mCurrentBrowser = newBrowser; - this.mCurrentTab = this.tabContainer.selectedItem; - this.finder.mListeners.forEach(l => this.mCurrentBrowser.finder.addResultListener(l)); - this.showTab(this.mCurrentTab); - - var backForwardContainer = document.getElementById("unified-back-forward-button"); - if (backForwardContainer) { - backForwardContainer.setAttribute("switchingtabs", "true"); - window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() { - window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr); - backForwardContainer.removeAttribute("switchingtabs"); - }); - } - - if (updateBlockedPopups) - this.mCurrentBrowser.updateBlockedPopups(); - - // Update the URL bar. - var loc = this.mCurrentBrowser.currentURI; - - // Bug 666809 - SecurityUI support for e10s - var webProgress = this.mCurrentBrowser.webProgress; - var securityUI = this.mCurrentBrowser.securityUI; - - // Update global findbar with new content browser - if (gFindBarInitialized) { - gFindBar.browser = newBrowser; - } - - this._callProgressListeners(null, "onLocationChange", - [webProgress, null, loc, 0], true, - false); - - if (securityUI) { - this._callProgressListeners(null, "onSecurityChange", - [webProgress, null, securityUI.state], true, false); - } - - var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null; - if (listener && listener.mStateFlags) { - this._callProgressListeners(null, "onUpdateCurrentBrowser", - [listener.mStateFlags, listener.mStatus, - listener.mMessage, listener.mTotalProgress], - true, false); - } - - if (!this._previewMode) { - this.mCurrentTab.removeAttribute("unread"); - this.selectedTab.lastAccessed = Date.now(); - - // Bug 666816 - TypeAheadFind support for e10s - if (!gMultiProcessBrowser) - this._fastFind.setDocShell(this.mCurrentBrowser.docShell); - - this.updateTitlebar(); - - this.mCurrentTab.removeAttribute("titlechanged"); - } - - // If the new tab is busy, and our current state is not busy, then - // we need to fire a start to all progress listeners. - const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; - if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) { - this.mIsBusy = true; - this._callProgressListeners(null, "onStateChange", - [webProgress, null, - nsIWebProgressListener.STATE_START | - nsIWebProgressListener.STATE_IS_NETWORK, 0], - true, false); - } - - // If the new tab is not busy, and our current state is busy, then - // we need to fire a stop to all progress listeners. - if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) { - this.mIsBusy = false; - this._callProgressListeners(null, "onStateChange", - [webProgress, null, - nsIWebProgressListener.STATE_STOP | - nsIWebProgressListener.STATE_IS_NETWORK, 0], - true, false); - } - - this._setCloseKeyState(!this.mCurrentTab.pinned); - - // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code - // that might rely upon the other changes suppressed. - // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window - if (!this._previewMode) { - // We've selected the new tab, so go ahead and notify listeners. - let event = new CustomEvent("TabSelect", { - bubbles: true, - cancelable: false, - detail: { - previousTab: oldTab - } - }); - this.mCurrentTab.dispatchEvent(event); - - this._tabAttrModified(oldTab, ["selected"]); - this._tabAttrModified(this.mCurrentTab, ["selected"]); - - // Adjust focus - oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused); - do { - // When focus is in the tab bar, retain it there. - if (document.activeElement == oldTab) { - // We need to explicitly focus the new tab, because - // tabbox.xml does this only in some cases. - this.mCurrentTab.focus(); - break; - } - - // If there's a tabmodal prompt showing, focus it. - if (newBrowser.hasAttribute("tabmodalPromptShowing")) { - let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - let prompts = newBrowser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt"); - let prompt = prompts[prompts.length - 1]; - prompt.Dialog.setDefaultFocus(); - break; - } - - // Focus the location bar if it was previously focused for that tab. - // In full screen mode, only bother making the location bar visible - // if the tab is a blank one. - if (newBrowser._urlbarFocused && gURLBar) { - - // Explicitly close the popup if the URL bar retains focus - gURLBar.closePopup(); - - if (!window.fullScreen) { - gURLBar.focus(); - break; - } else if (isTabEmpty(this.mCurrentTab)) { - focusAndSelectUrlBar(); - break; - } - } - - // If the find bar is focused, keep it focused. - if (gFindBarInitialized && - !gFindBar.hidden && - gFindBar.getElement("findbar-textbox").getAttribute("focused") == "true") - break; - - // Otherwise, focus the content area. - let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); - let focusFlags = fm.FLAG_NOSCROLL; - - if (!gMultiProcessBrowser) { - let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {}); - - // for anchors, use FLAG_SHOWRING so that it is clear what link was - // last clicked when switching back to that tab - if (newFocusedElement && - (newFocusedElement instanceof HTMLAnchorElement || - newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) - focusFlags |= fm.FLAG_SHOWRING; - } - fm.setFocus(newBrowser, focusFlags); - } while (false); - } - - this.tabContainer._setPositionalAttributes(); - ]]> - </body> - </method> - - <method name="_tabAttrModified"> - <parameter name="aTab"/> - <parameter name="aChanged"/> - <body><![CDATA[ - if (aTab.closing) - return; - - let event = new CustomEvent("TabAttrModified", { - bubbles: true, - cancelable: false, - detail: { - changed: aChanged, - } - }); - aTab.dispatchEvent(event); - ]]></body> - </method> - - <method name="setTabTitleLoading"> - <parameter name="aTab"/> - <body> - <![CDATA[ - aTab.label = this.mStringBundle.getString("tabs.connecting"); - aTab.crop = "end"; - this._tabAttrModified(aTab, ["label", "crop"]); - ]]> - </body> - </method> - - <method name="setTabTitle"> - <parameter name="aTab"/> - <body> - <![CDATA[ - var browser = this.getBrowserForTab(aTab); - var crop = "end"; - var title = browser.contentTitle; - - if (!title) { - if (browser.currentURI.spec) { - try { - title = this.mURIFixup.createExposableURI(browser.currentURI).spec; - } catch(ex) { - title = browser.currentURI.spec; - } - } - - if (title && !isBlankPageURL(title)) { - // At this point, we now have a URI. - // Let's try to unescape it using a character set - // in case the URI is not ASCII. - try { - var characterSet = browser.characterSet; - const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"] - .getService(Components.interfaces.nsITextToSubURI); - title = textToSubURI.unEscapeNonAsciiURI(characterSet, title); - } catch(ex) { /* Do nothing. */ } - - crop = "center"; - - } else // Still no title? Fall back to our untitled string. - title = this.mStringBundle.getString("tabs.emptyTabTitle"); - } - - if (aTab.label == title && - aTab.crop == crop) - return false; - - aTab.label = title; - aTab.crop = crop; - this._tabAttrModified(aTab, ["label", "crop"]); - - if (aTab.selected) - this.updateTitlebar(); - - return true; - ]]> - </body> - </method> - - <method name="loadOneTab"> - <parameter name="aURI"/> - <parameter name="aReferrerURI"/> - <parameter name="aCharset"/> - <parameter name="aPostData"/> - <parameter name="aLoadInBackground"/> - <parameter name="aAllowThirdPartyFixup"/> - <body> - <![CDATA[ - var aTriggeringPrincipal; - var aReferrerPolicy; - var aFromExternal; - var aRelatedToCurrent; - var aOriginPrincipal; - var aOpener; - if (arguments.length == 2 && - typeof arguments[1] == "object" && - !(arguments[1] instanceof Ci.nsIURI)) { - let params = arguments[1]; - aTriggeringPrincipal = params.triggeringPrincipal; - aReferrerURI = params.referrerURI; - aReferrerPolicy = params.referrerPolicy; - aCharset = params.charset; - aPostData = params.postData; - aLoadInBackground = params.inBackground; - aAllowThirdPartyFixup = params.allowThirdPartyFixup; - aFromExternal = params.fromExternal; - aRelatedToCurrent = params.relatedToCurrent; - aOriginPrincipal = params.originPrincipal; - aOpener = params.opener; - } - - var bgLoad = (aLoadInBackground != null) ? aLoadInBackground : - Services.prefs.getBoolPref("browser.tabs.loadInBackground"); - var owner = bgLoad ? null : this.selectedTab; - var tab = this.addTab(aURI, { - triggeringPrincipal: aTriggeringPrincipal, - referrerURI: aReferrerURI, - referrerPolicy: aReferrerPolicy, - charset: aCharset, - postData: aPostData, - ownerTab: owner, - allowThirdPartyFixup: aAllowThirdPartyFixup, - fromExternal: aFromExternal, - originPrincipal: aOriginPrincipal, - relatedToCurrent: aRelatedToCurrent, - opener: aOpener }); - if (!bgLoad) - this.selectedTab = tab; - - return tab; - ]]> - </body> - </method> - - <method name="loadTabs"> - <parameter name="aURIs"/> - <parameter name="aLoadInBackground"/> - <parameter name="aReplace"/> - <body><![CDATA[ - let aAllowThirdPartyFixup; - let aTargetTab; - let aNewIndex = -1; - let aPostDatas = []; - if (arguments.length == 2 && - typeof arguments[1] == "object") { - let params = arguments[1]; - aLoadInBackground = params.inBackground; - aReplace = params.replace; - aAllowThirdPartyFixup = params.allowThirdPartyFixup; - aTargetTab = params.targetTab; - aNewIndex = typeof params.newIndex === "number" ? - params.newIndex : aNewIndex; - aPostDatas = params.postDatas || aPostDatas; - } - - if (!aURIs.length) - return; - - // The tab selected after this new tab is closed (i.e. the new tab's - // "owner") is the next adjacent tab (i.e. not the previously viewed tab) - // when several urls are opened here (i.e. closing the first should select - // the next of many URLs opened) or if the pref to have UI links opened in - // the background is set (i.e. the link is not being opened modally) - // - // i.e. - // Number of URLs Load UI Links in BG Focus Last Viewed? - // == 1 false YES - // == 1 true NO - // > 1 false/true NO - var multiple = aURIs.length > 1; - var owner = multiple || aLoadInBackground ? null : this.selectedTab; - var firstTabAdded = null; - var targetTabIndex = -1; - - if (aReplace) { - let browser; - if (aTargetTab) { - browser = this.getBrowserForTab(aTargetTab); - targetTabIndex = aTargetTab._tPos; - } else { - browser = this.mCurrentBrowser; - targetTabIndex = this.tabContainer.selectedIndex; - } - let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; - if (aAllowThirdPartyFixup) { - flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | - Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; - } - try { - browser.loadURIWithFlags(aURIs[0], { - flags, postData: aPostDatas[0] - }); - } catch (e) { - // Ignore failure in case a URI is wrong, so we can continue - // opening the next ones. - } - } else { - firstTabAdded = this.addTab(aURIs[0], { - ownerTab: owner, - skipAnimation: multiple, - allowThirdPartyFixup: aAllowThirdPartyFixup, - postData: aPostDatas[0] - }); - if (aNewIndex !== -1) { - this.moveTabTo(firstTabAdded, aNewIndex); - targetTabIndex = firstTabAdded._tPos; - } - } - - let tabNum = targetTabIndex; - for (let i = 1; i < aURIs.length; ++i) { - let tab = this.addTab(aURIs[i], { - skipAnimation: true, - allowThirdPartyFixup: aAllowThirdPartyFixup, - postData: aPostDatas[i] - }); - if (targetTabIndex !== -1) - this.moveTabTo(tab, ++tabNum); - } - - if (!aLoadInBackground) { - if (firstTabAdded) { - // .selectedTab setter focuses the content area - this.selectedTab = firstTabAdded; - } - else - this.selectedBrowser.focus(); - } - ]]></body> - </method> - - <method name="addTab"> - <parameter name="aURI"/> - <parameter name="aReferrerURI"/> - <parameter name="aCharset"/> - <parameter name="aPostData"/> - <parameter name="aOwner"/> - <parameter name="aAllowThirdPartyFixup"/> - <body> - <![CDATA[ - const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - var aTriggeringPrincipal; - var aReferrerPolicy; - var aFromExternal; - var aRelatedToCurrent; - var aSkipAnimation; - var aOriginPrincipal; - var aSkipBackgroundNotify; - var aOpener; - if (arguments.length == 2 && - typeof arguments[1] == "object" && - !(arguments[1] instanceof Ci.nsIURI)) { - let params = arguments[1]; - aTriggeringPrincipal = params.triggeringPrincipal; - aReferrerURI = params.referrerURI; - aReferrerPolicy = params.referrerPolicy; - aCharset = params.charset; - aPostData = params.postData; - aOwner = params.ownerTab; - aAllowThirdPartyFixup = params.allowThirdPartyFixup; - aFromExternal = params.fromExternal; - aRelatedToCurrent = params.relatedToCurrent; - aSkipAnimation = params.skipAnimation; - aOriginPrincipal = params.originPrincipal; - aOpener = params.opener; - aSkipBackgroundNotify = params.skipBackgroundNotify; - } - - // if we're adding tabs, we're past interrupt mode, ditch the owner - if (this.mCurrentTab.owner) - this.mCurrentTab.owner = null; - - var t = document.createElementNS(NS_XUL, "tab"); - - let aURIObject = null; - try { - aURIObject = Services.io.newURI(aURI || "about:blank"); - } catch (ex) { /* we'll try to fix up this URL later */ } - - var uriIsAboutBlank = !aURI || aURI == "about:blank"; - - if (!aURI || isBlankPageURL(aURI)) - t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle")); - else - t.setAttribute("label", aURI); - - t.setAttribute("crop", "end"); - t.setAttribute("validate", "never"); //PMed - t.setAttribute("onerror", "this.removeAttribute('image');"); - - if (aSkipBackgroundNotify) { - t.setAttribute("skipbackgroundnotify", true); - } - - t.className = "tabbrowser-tab"; - - this.tabContainer._unlockTabSizing(); - - // When overflowing, new tabs are scrolled into view smoothly, which - // doesn't go well together with the width transition. So we skip the - // transition in that case. - let animate = !aSkipAnimation && - this.tabContainer.getAttribute("overflow") != "true" && - Services.prefs.getBoolPref("browser.tabs.animate"); - if (!animate) { - t.setAttribute("fadein", "true"); - setTimeout(function (tabContainer) { - tabContainer._handleNewTab(t); - }, 0, this.tabContainer); - } - - // invalidate caches - this._browsers = null; - this._visibleTabs = null; - - this.tabContainer.appendChild(t); - - // If this new tab is owned by another, assert that relationship - if (aOwner) - t.owner = aOwner; - - var b = document.createElementNS( - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - "browser"); - b.setAttribute("type", "content-targetable"); - b.setAttribute("message", "true"); - b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu")); - b.setAttribute("tooltip", this.getAttribute("contenttooltip")); - - if (Services.prefs.getPrefType("browser.tabs.remote") == Services.prefs.PREF_BOOL && - Services.prefs.getBoolPref("browser.tabs.remote")) { - b.setAttribute("remote", "true"); - } - - if (window.gShowPageResizers && document.getElementById("addon-bar").collapsed && - window.windowState == window.STATE_NORMAL) { - b.setAttribute("showresizer", "true"); - } - - if (aOpener) { - b.QueryInterface(Ci.nsIFrameLoaderOwner).presetOpenerWindow(aOpener); - } - - if (this.hasAttribute("autocompletepopup")) - b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup")); - b.setAttribute("autoscrollpopup", this._autoScrollPopup.id); - - if (this.hasAttribute("datetimepicker")) { - b.setAttribute("datetimepicker", this.getAttribute("datetimepicker")); - } - - if (this.hasAttribute("authdosprotected")) { - b.setAttribute("authdosprotected", this.getAttribute("authdosprotected")); - } - - // Create the browserStack container - var stack = document.createElementNS(NS_XUL, "stack"); - stack.className = "browserStack"; - stack.appendChild(b); - stack.setAttribute("flex", "1"); - - // Create the browserContainer - var browserContainer = document.createElementNS(NS_XUL, "vbox"); - browserContainer.className = "browserContainer"; - browserContainer.appendChild(stack); - browserContainer.setAttribute("flex", "1"); - - // Create the sidebar container - var browserSidebarContainer = document.createElementNS(NS_XUL, - "hbox"); - browserSidebarContainer.className = "browserSidebarContainer"; - browserSidebarContainer.appendChild(browserContainer); - browserSidebarContainer.setAttribute("flex", "1"); - - // Add the Message and the Browser to the box - var notificationbox = document.createElementNS(NS_XUL, - "notificationbox"); - notificationbox.setAttribute("flex", "1"); - notificationbox.appendChild(browserSidebarContainer); - - var position = this.tabs.length - 1; - var uniqueId = "panel" + Date.now() + position; - notificationbox.id = uniqueId; - t.linkedPanel = uniqueId; - t.linkedBrowser = b; - this._tabForBrowser.set(b, t); - t._tPos = position; - this.tabContainer._setPositionalAttributes(); - - // Prevent the superfluous initial load of a blank document - // if we're going to load something other than about:blank. - if (!uriIsAboutBlank) { - b.setAttribute("nodefaultsrc", "true"); - } - - // NB: this appendChild call causes us to run constructors for the - // browser element, which fires off a bunch of notifications. Some - // of those notifications can cause code to run that inspects our - // state, so it is important that the tab element is fully - // initialized by this point. - this.mPanelContainer.appendChild(notificationbox); - - this.tabContainer.updateVisibility(); - - // wire up a progress listener for the new browser object. - var tabListener = this.mTabProgressListener(t, b, uriIsAboutBlank); - const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"] - .createInstance(Components.interfaces.nsIWebProgress); - filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL); - b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL); - this.mTabListeners[position] = tabListener; - this.mTabFilters[position] = filter; - - b._fastFind = this.fastFind; - b.droppedLinkHandler = handleDroppedLink; - - // If we just created a new tab that loads the default - // newtab url, swap in a preloaded page if possible. - // Do nothing if we're a private window. - let docShellsSwapped = false; - if (aURI == BROWSER_NEW_TAB_URL && - !PrivateBrowsingUtils.isWindowPrivate(window)) { - docShellsSwapped = gBrowserNewTabPreloader.newTab(t); - } - - // Dispatch a new tab notification. We do this once we're - // entirely done, so that things are in a consistent state - // even if the event listener opens or closes tabs. - var evt = document.createEvent("Events"); - evt.initEvent("TabOpen", true, false); - t.dispatchEvent(evt); - - if (aOriginPrincipal && aURI) { - let {URI_INHERITS_SECURITY_CONTEXT} = Ci.nsIProtocolHandler; - // Unless we know for sure we're not inheriting principals, - // force the about:blank viewer to have the right principal: - if (!aURIObject || - (Services.io.getProtocolFlags(aURIObject.scheme) & URI_INHERITS_SECURITY_CONTEXT)) { - b.createAboutBlankContentViewer(aOriginPrincipal); - } - } - - // If we didn't swap docShells with a preloaded browser - // then let's just continue loading the page normally. - if (!docShellsSwapped && !uriIsAboutBlank) { - // pretend the user typed this so it'll be available till - // the document successfully loads - if (aURI && gInitialPages.indexOf(aURI) == -1) - b.userTypedValue = aURI; - - let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; - if (aAllowThirdPartyFixup) { - flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP; - flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; - } - if (aFromExternal) - flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL; - try { - b.loadURIWithFlags(aURI, { - flags: flags, - triggeringPrincipal: aTriggeringPrincipal, - referrerURI: aReferrerURI, - referrerPolicy: aReferrerPolicy, - charset: aCharset, - postData: aPostData, - }); - } catch (ex) { - Cu.reportError(ex); - } - } - - // We start our browsers out as inactive, and then maintain - // activeness in the tab switcher. - b.docShellIsActive = false; - - // When addTab() is called with an URL that is not "about:blank" we - // set the "nodefaultsrc" attribute that prevents a frameLoader - // from being created as soon as the linked <browser> is inserted - // into the DOM. We thus have to register the new outerWindowID - // for non-remote browsers after we have called browser.loadURI(). - // - // Note: Only do this of we still have a docShell. The TabOpen - // event was dispatched above and a gBrowser.removeTab() call from - // one of its listeners could cause us to fail here. - if (Services.prefs.getPrefType("browser.tabs.remote") == Services.prefs.PREF_BOOL && - !Services.prefs.getBoolPref("browser.tabs.remote") - && b.docShell) { - this._outerWindowIDBrowserMap.set(b.outerWindowID, b); - } - - // Check if we're opening a tab related to the current tab and - // move it to after the current tab. - // aReferrerURI is null or undefined if the tab is opened from - // an external application or bookmark, i.e. somewhere other - // than the current tab. - if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) && - Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) { - let newTabPos = (this._lastRelatedTab || - this.selectedTab)._tPos + 1; - if (this._lastRelatedTab) - this._lastRelatedTab.owner = null; - else - t.owner = this.selectedTab; - this.moveTabTo(t, newTabPos); - this._lastRelatedTab = t; - } - - if (animate) { - requestAnimationFrame(function () { - this.tabContainer._handleTabTelemetryStart(t, aURI); - - // kick the animation off - t.setAttribute("fadein", "true"); - - // This call to adjustTabstrip is redundant but needed so that - // when opening a second tab, the first tab's close buttons - // appears immediately rather than when the transition ends. - if (this.tabs.length - this._removingTabs.length == 2) - this.tabContainer.adjustTabstrip(); - }.bind(this)); - } - - return t; - ]]> - </body> - </method> - - <method name="warnAboutClosingTabs"> - <parameter name="aCloseTabs"/> - <parameter name="aTab"/> - <body> - <![CDATA[ - var tabsToClose; - switch (aCloseTabs) { - case this.closingTabsEnum.ALL: - // If there are multiple windows, pinned tabs will be closed, so - // we warn about them, too; if there is just one window, pinned - // tabs should come back on restart, so exclude them from warning. - var numberOfWindows = 0; - var browserEnum = Services.wm.getEnumerator("navigator:browser"); - while (browserEnum.hasMoreElements() && numberOfWindows < 2) { - numberOfWindows++; - browserEnum.getNext(); - } - if (numberOfWindows > 1) { - tabsToClose = this.tabs.length - this._removingTabs.length - } - else { - tabsToClose = this.tabs.length - this._removingTabs.length - - gBrowser._numPinnedTabs; - } - break; - case this.closingTabsEnum.OTHER: - tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs; - break; - case this.closingTabsEnum.TO_END: - if (!aTab) - throw new Error("Required argument missing: aTab"); - - tabsToClose = this.getTabsToTheEndFrom(aTab).length; - break; - default: - throw new Error("Invalid argument: " + aCloseTabs); - } - - if (tabsToClose <= 1) - return true; - - const pref = aCloseTabs == this.closingTabsEnum.ALL ? - "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs"; - var shouldPrompt = Services.prefs.getBoolPref(pref); - if (!shouldPrompt) - return true; - - var ps = Services.prompt; - - // default to true: if it were false, we wouldn't get this far - var warnOnClose = { value: true }; - var bundle = this.mStringBundle; - - // focus the window before prompting. - // this will raise any minimized window, which will - // make it obvious which window the prompt is for and will - // solve the problem of windows "obscuring" the prompt. - // see bug #350299 for more details - window.focus(); - var buttonPressed = - ps.confirmEx(window, - bundle.getString("tabs.closeWarningTitle"), - bundle.getFormattedString("tabs.closeWarningMultipleTabs", - [tabsToClose]), - (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) - + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1), - bundle.getString("tabs.closeButtonMultiple"), - null, null, - aCloseTabs == this.closingTabsEnum.ALL ? - bundle.getString("tabs.closeWarningPromptMe") : null, - warnOnClose); - var reallyClose = (buttonPressed == 0); - - // don't set the prefs unless they press OK and have unchecked the box - if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value) { - Services.prefs.setBoolPref("browser.tabs.warnOnClose", false); - Services.prefs.setBoolPref("browser.tabs.warnOnCloseOtherTabs", false); - } - return reallyClose; - ]]> - </body> - </method> - - <method name="getTabsToTheEndFrom"> - <parameter name="aTab"/> - <body> - <![CDATA[ - var tabsToEnd = []; - let tabs = this.visibleTabs; - for (let i = tabs.length - 1; tabs[i] != aTab && i >= 0; --i) { - tabsToEnd.push(tabs[i]); - } - return tabsToEnd.reverse(); - ]]> - </body> - </method> - - <method name="removeTabsToTheEndFrom"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) { - let tabs = this.getTabsToTheEndFrom(aTab); - for (let i = tabs.length - 1; i >= 0; --i) { - this.removeTab(tabs[i], {animate: true}); - } - } - ]]> - </body> - </method> - - <method name="removeAllTabsBut"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (aTab.pinned) - return; - - if (this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) { - let tabs = this.visibleTabs; - this.selectedTab = aTab; - - for (let i = tabs.length - 1; i >= 0; --i) { - if (tabs[i] != aTab && !tabs[i].pinned) - this.removeTab(tabs[i]); - } - } - ]]> - </body> - </method> - - <method name="removeCurrentTab"> - <parameter name="aParams"/> - <body> - <![CDATA[ - this.removeTab(this.mCurrentTab, aParams); - ]]> - </body> - </method> - - <field name="_removingTabs"> - [] - </field> - - <method name="removeTab"> - <parameter name="aTab"/> - <parameter name="aParams"/> - <body> - <![CDATA[ - if (aParams) { - var animate = aParams.animate; - var byMouse = aParams.byMouse; - } - - // Handle requests for synchronously removing an already - // asynchronously closing tab. - if (!animate && - aTab.closing) { - this._endRemoveTab(aTab); - return; - } - - var isLastTab = (this.tabs.length - this._removingTabs.length == 1); - - if (!this._beginRemoveTab(aTab, false, null, true)) - return; - - if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse) - this.tabContainer._lockTabSizing(aTab); - else - this.tabContainer._unlockTabSizing(); - - if (!animate /* the caller didn't opt in */ || - isLastTab || - aTab.pinned || - aTab.hidden || - this._removingTabs.length > 3 /* don't want lots of concurrent animations */ || - aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ || - window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ || - !Services.prefs.getBoolPref("browser.tabs.animate")) { - this._endRemoveTab(aTab); - return; - } - - this.tabContainer._handleTabTelemetryStart(aTab); - - this._blurTab(aTab); - aTab.style.maxWidth = ""; // ensure that fade-out transition happens - aTab.removeAttribute("fadein"); - - if (this.tabs.length - this._removingTabs.length == 1) { - // The second tab just got closed and we will end up with a single - // one. Remove the first tab's close button immediately (if needed) - // rather than after the tabclose animation ends. - this.tabContainer.adjustTabstrip(); - } - - setTimeout(function (tab, tabbrowser) { - if (tab.parentNode && - window.getComputedStyle(tab).maxWidth == "0.1px") { - NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)"); - tabbrowser._endRemoveTab(tab); - } - }, 3000, aTab, this); - ]]> - </body> - </method> - - <!-- Tab close requests are ignored if the window is closing anyway, - e.g. when holding Ctrl+W. --> - <field name="_windowIsClosing"> - false - </field> - - <method name="_beginRemoveTab"> - <parameter name="aTab"/> - <parameter name="aTabWillBeMoved"/> - <parameter name="aCloseWindowWithLastTab"/> - <parameter name="aCloseWindowFastpath"/> - <body> - <![CDATA[ - if (aTab.closing || - aTab._pendingPermitUnload || - this._windowIsClosing) - return false; - - var browser = this.getBrowserForTab(aTab); - - if (!aTabWillBeMoved) { - let ds = browser.docShell; - if (ds && ds.contentViewer) { - // We need to block while calling permitUnload() because it - // processes the event queue and may lead to another removeTab() - // call before permitUnload() even returned. - aTab._pendingPermitUnload = true; - let permitUnload = ds.contentViewer.permitUnload(); - delete aTab._pendingPermitUnload; - - if (!permitUnload) - return false; - } - } - - var closeWindow = false; - var newTab = false; - if (this.tabs.length - this._removingTabs.length == 1) { - closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab : - !window.toolbar.visible || - this.tabContainer._closeWindowWithLastTab; - - // Closing the tab and replacing it with a blank one is notably slower - // than closing the window right away. If the caller opts in, take - // the fast path. - if (closeWindow && - aCloseWindowFastpath && - this._removingTabs.length == 0 && - (this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow))) - return null; - - newTab = true; - } - - aTab.closing = true; - this._removingTabs.push(aTab); - this._visibleTabs = null; // invalidate cache - if (newTab) - this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true}); - else - this.tabContainer.updateVisibility(); - - // We're committed to closing the tab now. - // Dispatch a notification. - // We dispatch it before any teardown so that event listeners can - // inspect the tab that's about to close. - var evt = document.createEvent("UIEvent"); - evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0); - aTab.dispatchEvent(evt); - - if (!gMultiProcessBrowser) { - // Prevent this tab from showing further dialogs, since we're closing it - var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDOMWindowUtils); - windowUtils.disableDialogs(); - } - - // Remove the tab's filter and progress listener. - const filter = this.mTabFilters[aTab._tPos]; - - browser.webProgress.removeProgressListener(filter); - - filter.removeProgressListener(this.mTabListeners[aTab._tPos]); - this.mTabListeners[aTab._tPos].destroy(); - - if (browser.registeredOpenURI && !aTabWillBeMoved) { - this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI); - delete browser.registeredOpenURI; - } - - // We are no longer the primary content area. - browser.setAttribute("type", "content-targetable"); - - // Remove this tab as the owner of any other tabs, since it's going away. - Array.forEach(this.tabs, function (tab) { - if ("owner" in tab && tab.owner == aTab) - // |tab| is a child of the tab we're removing, make it an orphan - tab.owner = null; - }); - - aTab._endRemoveArgs = [closeWindow, newTab]; - return true; - ]]> - </body> - </method> - - <method name="_endRemoveTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (!aTab || !aTab._endRemoveArgs) - return; - - var [aCloseWindow, aNewTab] = aTab._endRemoveArgs; - aTab._endRemoveArgs = null; - - if (this._windowIsClosing) { - aCloseWindow = false; - aNewTab = false; - } - - this._lastRelatedTab = null; - - // update the UI early for responsiveness - aTab.collapsed = true; - this.tabContainer._fillTrailingGap(); - this._blurTab(aTab); - - this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1); - - if (aCloseWindow) { - this._windowIsClosing = true; - while (this._removingTabs.length) - this._endRemoveTab(this._removingTabs[0]); - } else if (!this._windowIsClosing) { - if (aNewTab) - focusAndSelectUrlBar(); - - // workaround for bug 345399 - this.tabContainer.mTabstrip._updateScrollButtonsDisabledState(); - } - - // We're going to remove the tab and the browser now. - // Clean up mTabFilters and mTabListeners now rather than in - // _beginRemoveTab, so that their size is always in sync with the - // number of tabs and browsers (the xbl destructor depends on this). - this.mTabFilters.splice(aTab._tPos, 1); - this.mTabListeners.splice(aTab._tPos, 1); - - var browser = this.getBrowserForTab(aTab); - this._outerWindowIDBrowserMap.delete(browser.outerWindowID); - - // Because of the way XBL works (fields just set JS - // properties on the element) and the code we have in place - // to preserve the JS objects for any elements that have - // JS properties set on them, the browser element won't be - // destroyed until the document goes away. So we force a - // cleanup ourselves. - // This has to happen before we remove the child so that the - // XBL implementation of nsIObserver still works. - browser.destroy(); - - if (browser == this.mCurrentBrowser) - this.mCurrentBrowser = null; - - var wasPinned = aTab.pinned; - - // Invalidate browsers cache, as the tab is removed from the - // tab container. - this._browsers = null; - - // Remove the tab ... - this.tabContainer.removeChild(aTab); - - // ... and fix up the _tPos properties immediately. - for (let i = aTab._tPos; i < this.tabs.length; i++) - this.tabs[i]._tPos = i; - - if (!this._windowIsClosing) { - if (wasPinned) - this.tabContainer._positionPinnedTabs(); - - // update tab close buttons state - this.tabContainer.adjustTabstrip(); - - setTimeout(function(tabs) { - tabs._lastTabClosedByMouse = false; - }, 0, this.tabContainer); - } - - // Pale Moon: if resizing immediately, select the tab immediately to the left - // instead of the right (if not leftmost) to prevent focus swap and - // "selected tab not under cursor" - // FIXME: Tabs must be sliding in from the left for this, or it'd unfocus - // in the other direction! Disabled for now. Is there an easier way? :hover? - // Is this even needed when resizing immediately?... - - //if (Services.prefs.getBoolPref("browser.tabs.resize_immediately")) { - // if (this.selectedTab._tPos > 1) { - // let newPos = this.selectedTab._tPos - 1; - // this.selectedTab = this.tabs[newPos]; - // } - //} - - // update tab positional properties and attributes - this.selectedTab._selected = true; - this.tabContainer._setPositionalAttributes(); - - // Removing the panel requires fixing up selectedPanel immediately - // (see below), which would be hindered by the potentially expensive - // browser removal. So we remove the browser and the panel in two - // steps. - - var panel = this.getNotificationBox(browser); - - // This will unload the document. An unload handler could remove - // dependant tabs, so it's important that the tabbrowser is now in - // a consistent state (tab removed, tab positions updated, etc.). - browser.parentNode.removeChild(browser); - - // Release the browser in case something is erroneously holding a - // reference to the tab after its removal. - this._tabForBrowser.delete(aTab.linkedBrowser); - aTab.linkedBrowser = null; - - // As the browser is removed, the removal of a dependent document can - // cause the whole window to close. So at this point, it's possible - // that the binding is destructed. - if (this.mTabBox) { - let selectedPanel = this.mTabBox.selectedPanel; - - this.mPanelContainer.removeChild(panel); - - // Under the hood, a selectedIndex attribute controls which panel - // is displayed. Removing a panel A which precedes the selected - // panel B makes selectedIndex point to the panel next to B. We - // need to explicitly preserve B as the selected panel. - this.mTabBox.selectedPanel = selectedPanel; - } - - if (aCloseWindow) - this._windowIsClosing = closeWindow(true, window.warnAboutClosingWindow); - ]]> - </body> - </method> - - <method name="_blurTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (!aTab.selected) - return; - - if (aTab.owner && - !aTab.owner.hidden && - !aTab.owner.closing && - Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) { - this.selectedTab = aTab.owner; - return; - } - - // Switch to a visible tab unless there aren't any others remaining - let remainingTabs = this.visibleTabs; - let numTabs = remainingTabs.length; - if (numTabs == 0 || numTabs == 1 && remainingTabs[0] == aTab) { - remainingTabs = Array.filter(this.tabs, function(tab) { - return !tab.closing; - }, this); - } - - // Try to find a remaining tab that comes after the given tab - var tab = aTab; - do { - tab = tab.nextSibling; - } while (tab && remainingTabs.indexOf(tab) == -1); - - if (!tab) { - tab = aTab; - - do { - tab = tab.previousSibling; - } while (tab && remainingTabs.indexOf(tab) == -1); - } - - this.selectedTab = tab; - ]]> - </body> - </method> - - <method name="swapNewTabWithBrowser"> - <parameter name="aNewTab"/> - <parameter name="aBrowser"/> - <body> - <![CDATA[ - // The browser must be standalone. - if (aBrowser.getTabBrowser()) - throw Cr.NS_ERROR_INVALID_ARG; - - // The tab is definitely not loading. - aNewTab.removeAttribute("busy"); - if (aNewTab.selected) { - this.mIsBusy = false; - } - - this._swapBrowserDocShells(aNewTab, aBrowser); - - // Update the new tab's title. - this.setTabTitle(aNewTab); - - if (aNewTab.selected) { - this.updateCurrentBrowser(true); - } - ]]> - </body> - </method> - - <method name="swapBrowsersAndCloseOther"> - <parameter name="aOurTab"/> - <parameter name="aOtherTab"/> - <body> - <![CDATA[ - // Do not allow transfering a private tab to a non-private window - // and vice versa. - if (PrivateBrowsingUtils.isWindowPrivate(window) != - PrivateBrowsingUtils.isWindowPrivate(aOtherTab.ownerDocument.defaultView)) - return; - - // That's gBrowser for the other window, not the tab's browser! - var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser; - var isPending = aOtherTab.hasAttribute("pending"); - - // Expedite the removal of the icon if it was already scheduled. - if (aOtherTab._soundPlayingAttrRemovalTimer) { - clearTimeout(aOtherTab._soundPlayingAttrRemovalTimer); - aOtherTab._soundPlayingAttrRemovalTimer = 0; - aOtherTab.removeAttribute("soundplaying"); - remoteBrowser._tabAttrModified(aOtherTab, ["soundplaying"]); - } - - // First, start teardown of the other browser. Make sure to not - // fire the beforeunload event in the process. Close the other - // window if this was its last tab. - if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true)) - return; - - let ourBrowser = this.getBrowserForTab(aOurTab); - let otherBrowser = aOtherTab.linkedBrowser; - - let modifiedAttrs = []; - if (aOtherTab.hasAttribute("muted")) { - aOurTab.setAttribute("muted", "true"); - aOurTab.muteReason = aOtherTab.muteReason; - ourBrowser.mute(); - modifiedAttrs.push("muted"); - } - if (aOtherTab.hasAttribute("soundplaying")) { - aOurTab.setAttribute("soundplaying", "true"); - modifiedAttrs.push("soundplaying"); - } - - // If the other tab is pending (i.e. has not been restored, yet) - // then do not switch docShells but retrieve the other tab's state - // and apply it to our tab. - if (isPending) { - let ss = Cc["@mozilla.org/browser/sessionstore;1"] - .getService(Ci.nsISessionStore) - ss.setTabState(aOurTab, ss.getTabState(aOtherTab)); - - // Make sure to unregister any open URIs. - this._swapRegisteredOpenURIs(ourBrowser, otherBrowser); - } else { - // Workarounds for bug 458697 - // Icon might have been set on DOMLinkAdded, don't override that. - if (!ourBrowser.mIconURL && otherBrowser.mIconURL) - this.setIcon(aOurTab, otherBrowser.mIconURL, otherBrowser.contentPrincipal); - var isBusy = aOtherTab.hasAttribute("busy"); - if (isBusy) { - aOurTab.setAttribute("busy", "true"); - modifiedAttrs.push("busy"); - if (aOurTab.selected) - this.mIsBusy = true; - } - - this._swapBrowserDocShells(aOurTab, otherBrowser); - } - - // Finish tearing down the tab that's going away. - remoteBrowser._endRemoveTab(aOtherTab); - - if (isBusy) - this.setTabTitleLoading(aOurTab); - else - this.setTabTitle(aOurTab); - - // If the tab was already selected (this happpens in the scenario - // of replaceTabWithWindow), notify onLocationChange, etc. - if (aOurTab.selected) - this.updateCurrentBrowser(true); - - if (modifiedAttrs.length) { - this._tabAttrModified(aOurTab, modifiedAttrs); - } - ]]> - </body> - </method> - - <method name="_swapBrowserDocShells"> - <parameter name="aOurTab"/> - <parameter name="aOtherBrowser"/> - <body> - <![CDATA[ - // Unhook our progress listener - let index = aOurTab._tPos; - const filter = this.mTabFilters[index]; - let tabListener = this.mTabListeners[index]; - let ourBrowser = this.getBrowserForTab(aOurTab); - ourBrowser.webProgress.removeProgressListener(filter); - filter.removeProgressListener(tabListener); - - // Make sure to unregister any open URIs. - this._swapRegisteredOpenURIs(ourBrowser, aOtherBrowser); - - // Unmap old outerWindowIDs. - this._outerWindowIDBrowserMap.delete(ourBrowser.outerWindowID); - let remoteBrowser = aOtherBrowser.ownerDocument.defaultView.gBrowser; - if (remoteBrowser) { - remoteBrowser._outerWindowIDBrowserMap.delete(aOtherBrowser.outerWindowID); - } - // Swap the docshells - ourBrowser.swapDocShells(aOtherBrowser); - - // Register new outerWindowIDs. - this._outerWindowIDBrowserMap.set(ourBrowser.outerWindowID, ourBrowser); - if (remoteBrowser) { - remoteBrowser._outerWindowIDBrowserMap.set(aOtherBrowser.outerWindowID, aOtherBrowser); - } - // Restore the progress listener - this.mTabListeners[index] = tabListener = - this.mTabProgressListener(aOurTab, ourBrowser, false); - - const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL; - filter.addProgressListener(tabListener, notifyAll); - ourBrowser.webProgress.addProgressListener(filter, notifyAll); - ]]> - </body> - </method> - - <method name="_swapRegisteredOpenURIs"> - <parameter name="aOurBrowser"/> - <parameter name="aOtherBrowser"/> - <body> - <![CDATA[ - // If the current URI is registered as open remove it from the list. - if (aOurBrowser.registeredOpenURI) { - this._placesAutocomplete.unregisterOpenPage(aOurBrowser.registeredOpenURI); - delete aOurBrowser.registeredOpenURI; - } - - // If the other/new URI is registered as open then copy it over. - if (aOtherBrowser.registeredOpenURI) { - aOurBrowser.registeredOpenURI = aOtherBrowser.registeredOpenURI; - delete aOtherBrowser.registeredOpenURI; - } - ]]> - </body> - </method> - - <method name="reloadAllTabs"> - <body> - <![CDATA[ - let tabs = this.visibleTabs; - let l = tabs.length; - for (var i = 0; i < l; i++) { - try { - this.getBrowserForTab(tabs[i]).reload(); - } catch (e) { - // ignore failure to reload so others will be reloaded - } - } - ]]> - </body> - </method> - - <method name="reloadTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - let browser = this.getBrowserForTab(aTab); - // Reset DOS mitigation for basic auth prompt - delete browser.authPromptCounter; - browser.reload(); - ]]> - </body> - </method> - - <method name="addProgressListener"> - <parameter name="aListener"/> - <body> - <![CDATA[ - if (arguments.length != 1) { - Components.utils.reportError("gBrowser.addProgressListener was " + - "called with a second argument, " + - "which is not supported. See bug " + - "608628."); - } - - this.mProgressListeners.push(aListener); - ]]> - </body> - </method> - - <method name="removeProgressListener"> - <parameter name="aListener"/> - <body> - <![CDATA[ - this.mProgressListeners = - this.mProgressListeners.filter(function (l) l != aListener); - ]]> - </body> - </method> - - <method name="addTabsProgressListener"> - <parameter name="aListener"/> - <body> - this.mTabsProgressListeners.push(aListener); - </body> - </method> - - <method name="removeTabsProgressListener"> - <parameter name="aListener"/> - <body> - <![CDATA[ - this.mTabsProgressListeners = - this.mTabsProgressListeners.filter(function (l) l != aListener); - ]]> - </body> - </method> - - <method name="getBrowserForTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - return aTab.linkedBrowser; - ]]> - </body> - </method> - - <method name="showOnlyTheseTabs"> - <parameter name="aTabs"/> - <body> - <![CDATA[ - Array.forEach(this.tabs, function(tab) { - if (aTabs.indexOf(tab) == -1) - this.hideTab(tab); - else - this.showTab(tab); - }, this); - - this.tabContainer._handleTabSelect(false); - ]]> - </body> - </method> - - <method name="showTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (aTab.hidden) { - aTab.removeAttribute("hidden"); - this._visibleTabs = null; // invalidate cache - - this.tabContainer.adjustTabstrip(); - - this.tabContainer._setPositionalAttributes(); - - let event = document.createEvent("Events"); - event.initEvent("TabShow", true, false); - aTab.dispatchEvent(event); - } - ]]> - </body> - </method> - - <method name="hideTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (!aTab.hidden && !aTab.pinned && !aTab.selected && - !aTab.closing) { - aTab.setAttribute("hidden", "true"); - this._visibleTabs = null; // invalidate cache - - this.tabContainer.adjustTabstrip(); - - this.tabContainer._setPositionalAttributes(); - - let event = document.createEvent("Events"); - event.initEvent("TabHide", true, false); - aTab.dispatchEvent(event); - } - ]]> - </body> - </method> - - <method name="selectTabAtIndex"> - <parameter name="aIndex"/> - <parameter name="aEvent"/> - <body> - <![CDATA[ - let tabs = this.visibleTabs; - - // count backwards for aIndex < 0 - if (aIndex < 0) - aIndex += tabs.length; - - if (aIndex >= 0 && aIndex < tabs.length) - this.selectedTab = tabs[aIndex]; - - if (aEvent) { - aEvent.preventDefault(); - aEvent.stopPropagation(); - } - ]]> - </body> - </method> - - <property name="selectedTab"> - <getter> - return this.mCurrentTab; - </getter> - <setter> - <![CDATA[ - if (gNavToolbox.collapsed) { - return this.mTabBox.selectedTab; - } - // Update the tab - this.mTabBox.selectedTab = val; - return val; - ]]> - </setter> - </property> - - <property name="selectedBrowser" - onget="return this.mCurrentBrowser;" - readonly="true"/> - - <property name="browsers" readonly="true"> - <getter> - <![CDATA[ - return this._browsers || - (this._browsers = Array.map(this.tabs, function (tab) tab.linkedBrowser)); - ]]> - </getter> - </property> - <field name="_browsers">null</field> - - <!-- Moves a tab to a new browser window, unless it's already the only tab - in the current window, in which case this will do nothing. --> - <method name="replaceTabWithWindow"> - <parameter name="aTab"/> - <parameter name="aOptions"/> - <body> - <![CDATA[ - if (this.tabs.length == 1) - return null; - - var options = "chrome,dialog=no,all"; - for (var name in aOptions) - options += "," + name + "=" + aOptions[name]; - - // tell a new window to take the "dropped" tab - return window.openDialog(getBrowserURL(), "_blank", options, aTab); - ]]> - </body> - </method> - - <method name="moveTabTo"> - <parameter name="aTab"/> - <parameter name="aIndex"/> - <body> - <![CDATA[ - var oldPosition = aTab._tPos; - if (oldPosition == aIndex) - return; - - // Don't allow mixing pinned and unpinned tabs. - if (aTab.pinned) - aIndex = Math.min(aIndex, this._numPinnedTabs - 1); - else - aIndex = Math.max(aIndex, this._numPinnedTabs); - if (oldPosition == aIndex) - return; - - this._lastRelatedTab = null; - - this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]); - this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]); - - let wasFocused = (document.activeElement == this.mCurrentTab); - - aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1; - this.mCurrentTab._selected = false; - - // invalidate caches - this._browsers = null; - this._visibleTabs = null; - - // use .item() instead of [] because dragging to the end of the strip goes out of - // bounds: .item() returns null (so it acts like appendChild), but [] throws - this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex)); - - for (let i = 0; i < this.tabs.length; i++) { - this.tabs[i]._tPos = i; - this.tabs[i]._selected = false; - } - this.mCurrentTab._selected = true; - - if (wasFocused) - this.mCurrentTab.focus(); - - this.tabContainer._handleTabSelect(false); - - if (aTab.pinned) - this.tabContainer._positionPinnedTabs(); - - this.tabContainer._setPositionalAttributes(); - - var evt = document.createEvent("UIEvents"); - evt.initUIEvent("TabMove", true, false, window, oldPosition); - aTab.dispatchEvent(evt); - ]]> - </body> - </method> - - <method name="moveTabForward"> - <body> - <![CDATA[ - let nextTab = this.mCurrentTab.nextSibling; - while (nextTab && nextTab.hidden) - nextTab = nextTab.nextSibling; - - if (nextTab) - this.moveTabTo(this.mCurrentTab, nextTab._tPos); - else if (this.arrowKeysShouldWrap) - this.moveTabToStart(); - ]]> - </body> - </method> - - <method name="moveTabBackward"> - <body> - <![CDATA[ - let previousTab = this.mCurrentTab.previousSibling; - while (previousTab && previousTab.hidden) - previousTab = previousTab.previousSibling; - - if (previousTab) - this.moveTabTo(this.mCurrentTab, previousTab._tPos); - else if (this.arrowKeysShouldWrap) - this.moveTabToEnd(); - ]]> - </body> - </method> - - <method name="moveTabToStart"> - <body> - <![CDATA[ - var tabPos = this.mCurrentTab._tPos; - if (tabPos > 0) - this.moveTabTo(this.mCurrentTab, 0); - ]]> - </body> - </method> - - <method name="moveTabToEnd"> - <body> - <![CDATA[ - var tabPos = this.mCurrentTab._tPos; - if (tabPos < this.browsers.length - 1) - this.moveTabTo(this.mCurrentTab, this.browsers.length - 1); - ]]> - </body> - </method> - - <method name="moveTabOver"> - <parameter name="aEvent"/> - <body> - <![CDATA[ - var direction = window.getComputedStyle(this.parentNode, null).direction; - if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) || - (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT)) - this.moveTabForward(); - else - this.moveTabBackward(); - ]]> - </body> - </method> - - <method name="duplicateTab"> - <parameter name="aTab"/><!-- can be from a different window as well --> - <body> - <![CDATA[ - return Cc["@mozilla.org/browser/sessionstore;1"] - .getService(Ci.nsISessionStore) - .duplicateTab(window, aTab); - ]]> - </body> - </method> - - <!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT - MAKE SURE TO ADD IT HERE AS WELL. --> - <property name="canGoBack" - onget="return this.mCurrentBrowser.canGoBack;" - readonly="true"/> - - <property name="canGoForward" - onget="return this.mCurrentBrowser.canGoForward;" - readonly="true"/> - - <method name="goBack"> - <body> - <![CDATA[ - return this.mCurrentBrowser.goBack(); - ]]> - </body> - </method> - - <method name="goForward"> - <body> - <![CDATA[ - return this.mCurrentBrowser.goForward(); - ]]> - </body> - </method> - - <method name="reload"> - <body> - <![CDATA[ - return this.mCurrentBrowser.reload(); - ]]> - </body> - </method> - - <method name="reloadWithFlags"> - <parameter name="aFlags"/> - <body> - <![CDATA[ - return this.mCurrentBrowser.reloadWithFlags(aFlags); - ]]> - </body> - </method> - - <method name="stop"> - <body> - <![CDATA[ - return this.mCurrentBrowser.stop(); - ]]> - </body> - </method> - - <!-- throws exception for unknown schemes --> - <method name="loadURI"> - <parameter name="aURI"/> - <parameter name="aReferrerURI"/> - <parameter name="aCharset"/> - <body> - <![CDATA[ - return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset); - ]]> - </body> - </method> - - <!-- throws exception for unknown schemes --> - <method name="loadURIWithFlags"> - <parameter name="aURI"/> - <parameter name="aFlags"/> - <parameter name="aReferrerURI"/> - <parameter name="aCharset"/> - <parameter name="aPostData"/> - <body> - <![CDATA[ - // Note - the callee understands both: - // (a) loadURIWithFlags(aURI, aFlags, ...) - // (b) loadURIWithFlags(aURI, { flags: aFlags, ... }) - // Forwarding it as (a) here actually supports both (a) and (b), - // so you can call us either way too. - return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData); - ]]> - </body> - </method> - - <method name="goHome"> - <body> - <![CDATA[ - return this.mCurrentBrowser.goHome(); - ]]> - </body> - </method> - - <property name="homePage"> - <getter> - <![CDATA[ - return this.mCurrentBrowser.homePage; - ]]> - </getter> - <setter> - <![CDATA[ - this.mCurrentBrowser.homePage = val; - return val; - ]]> - </setter> - </property> - - <method name="gotoIndex"> - <parameter name="aIndex"/> - <body> - <![CDATA[ - return this.mCurrentBrowser.gotoIndex(aIndex); - ]]> - </body> - </method> - - <method name="attachFormFill"> - <body><![CDATA[ - for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) { - var cb = this.getBrowserAtIndex(i); - cb.attachFormFill(); - } - ]]></body> - </method> - - <method name="detachFormFill"> - <body><![CDATA[ - for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) { - var cb = this.getBrowserAtIndex(i); - cb.detachFormFill(); - } - ]]></body> - </method> - - <property name="currentURI" - onget="return this.mCurrentBrowser.currentURI;" - readonly="true"/> - - <field name="_fastFind">null</field> - <property name="fastFind" - readonly="true"> - <getter> - <![CDATA[ - if (!this._fastFind) { - this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"] - .createInstance(Components.interfaces.nsITypeAheadFind); - this._fastFind.init(this.docShell); - } - return this._fastFind; - ]]> - </getter> - </property> - - <field name="_lastSearchString">null</field> - <field name="_lastSearchHighlight">false</field> - - <field name="finder"><![CDATA[ - ({ - mTabBrowser: this, - mListeners: new Set(), - get finder() { - return this.mTabBrowser.mCurrentBrowser.finder; - }, - destroy: function() { - this.finder.destroy(); - }, - addResultListener: function(aListener) { - this.mListeners.add(aListener); - this.finder.addResultListener(aListener); - }, - removeResultListener: function(aListener) { - this.mListeners.delete(aListener); - this.finder.removeResultListener(aListener); - }, - get searchString() { - return this.finder.searchString; - }, - get clipboardSearchString() { - return this.finder.clipboardSearchString; - }, - set clipboardSearchString(val) { - return this.finder.clipboardSearchString = val; - }, - set caseSensitive(val) { - return this.finder.caseSensitive = val; - }, - set entireWord(val) { - return this.finder.entireWord = val; - }, - get highlighter() { - return this.finder.highlighter; - }, - get matchesCountLimit() { - return this.finder.matchesCountLimit; - }, - fastFind: function(...args) { - this.finder.fastFind(...args); - }, - findAgain: function(...args) { - this.finder.findAgain(...args); - }, - setSearchStringToSelection: function() { - return this.finder.setSearchStringToSelection(); - }, - highlight: function(...args) { - this.finder.highlight(...args); - }, - getInitialSelection: function() { - this.finder.getInitialSelection(); - }, - getActiveSelectionText: function() { - return this.finder.getActiveSelectionText(); - }, - enableSelection: function() { - this.finder.enableSelection(); - }, - removeSelection: function() { - this.finder.removeSelection(); - }, - focusContent: function() { - this.finder.focusContent(); - }, - onFindbarClose: function() { - this.finder.onFindbarClose(); - }, - onFindbarOpen: function() { - this.finder.onFindbarOpen(); - }, - onModalHighlightChange: function(...args) { - return this.finder.onModalHighlightChange(...args); - }, - onHighlightAllChange: function(...args) { - return this.finder.onHighlightAllChange(...args); - }, - keyPress: function(aEvent) { - this.finder.keyPress(aEvent); - }, - requestMatchesCount: function(...args) { - this.finder.requestMatchesCount(...args); - }, - onIteratorRangeFound: function(...args) { - this.finder.onIteratorRangeFound(...args); - }, - onIteratorReset: function() { - this.finder.onIteratorReset(); - }, - onIteratorRestart: function(...args) { - this.finder.onIteratorRestart(...args); - }, - onIteratorStart: function(...args) { - this.finder.onIteratorStart(...args); - }, - onLocationChange: function(...args) { - this.finder.onLocationChange(...args); - } - }) - ]]></field> - - <property name="docShell" - onget="return this.mCurrentBrowser.docShell" - readonly="true"/> - - <property name="webNavigation" - onget="return this.mCurrentBrowser.webNavigation" - readonly="true"/> - - <property name="webBrowserFind" - readonly="true" - onget="return this.mCurrentBrowser.webBrowserFind"/> - - <property name="webProgress" - readonly="true" - onget="return this.mCurrentBrowser.webProgress"/> - - <property name="contentWindow" - readonly="true" - onget="return this.mCurrentBrowser.contentWindow"/> - - <property name="contentWindowAsCPOW" - readonly="true" - onget="return this.mCurrentBrowser.contentWindow;"/> - - <property name="sessionHistory" - onget="return this.mCurrentBrowser.sessionHistory;" - readonly="true"/> - - <property name="markupDocumentViewer" - onget="return this.mCurrentBrowser.markupDocumentViewer;" - readonly="true"/> - - <property name="contentViewerEdit" - onget="return this.mCurrentBrowser.contentViewerEdit;" - readonly="true"/> - - <property name="contentViewerFile" - onget="return this.mCurrentBrowser.contentViewerFile;" - readonly="true"/> - - <property name="contentDocument" - onget="return this.mCurrentBrowser.contentDocument;" - readonly="true"/> - - <property name="contentDocumentAsCPOW" - onget="return this.mCurrentBrowser.contentDocument;" - readonly="true"/> - - <property name="contentTitle" - onget="return this.mCurrentBrowser.contentTitle;" - readonly="true"/> - - <property name="contentPrincipal" - onget="return this.mCurrentBrowser.contentPrincipal;" - readonly="true"/> - - <property name="securityUI" - onget="return this.mCurrentBrowser.securityUI;" - readonly="true"/> - - <property name="fullZoom" - onget="return this.mCurrentBrowser.fullZoom;" - onset="this.mCurrentBrowser.fullZoom = val;"/> - - <property name="textZoom" - onget="return this.mCurrentBrowser.textZoom;" - onset="this.mCurrentBrowser.textZoom = val;"/> - - <property name="isSyntheticDocument" - onget="return this.mCurrentBrowser.isSyntheticDocument;" - readonly="true"/> - - <method name="_handleKeyEvent"> - <parameter name="aEvent"/> - <body><![CDATA[ - if (!aEvent.isTrusted) { - // Don't let untrusted events mess with tabs. - return; - } - - if (aEvent.altKey) - return; - - if (aEvent.ctrlKey && aEvent.shiftKey && !aEvent.metaKey) { - switch (aEvent.keyCode) { - case aEvent.DOM_VK_PAGE_UP: - this.moveTabBackward(); - aEvent.stopPropagation(); - aEvent.preventDefault(); - return; - case aEvent.DOM_VK_PAGE_DOWN: - this.moveTabForward(); - aEvent.stopPropagation(); - aEvent.preventDefault(); - return; - } - } - -#ifdef XP_MACOSX - if (!aEvent.metaKey) - return; - - var offset = 1; - switch (aEvent.charCode) { - case '}'.charCodeAt(0): - offset = -1; - case '{'.charCodeAt(0): - if (window.getComputedStyle(this, null).direction == "ltr") - offset *= -1; - this.tabContainer.advanceSelectedTab(offset, true); - aEvent.stopPropagation(); - aEvent.preventDefault(); - } -#else - if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey && - aEvent.keyCode == KeyEvent.DOM_VK_F4 && - !this.mCurrentTab.pinned) { - this.removeCurrentTab({animate: true}); - aEvent.stopPropagation(); - aEvent.preventDefault(); - } -#endif - ]]></body> - </method> - - <property name="userTypedValue" - onget="return this.mCurrentBrowser.userTypedValue;" - onset="return this.mCurrentBrowser.userTypedValue = val;"/> - - <method name="createTooltip"> - <parameter name="event"/> - <body><![CDATA[ - event.stopPropagation(); - var tab = document.tooltipNode; - if (tab.localName != "tab") { - event.preventDefault(); - return; - } - - var stringID, label; - if (tab.mOverCloseButton) { - stringID = "tabs.closeTab"; - } else if (tab._overPlayingIcon) { - if (tab.linkedBrowser.audioBlocked) { - stringID = "tabs.unblockAudio.tooltip"; - } else { - stringID = tab.linkedBrowser.audioMuted ? - "tabs.unmuteAudio.tooltip" : - "tabs.muteAudio.tooltip"; - } - } else { - label = tab.getAttribute("label"); - } - if (stringID && !label) { - label = this.mStringBundle.getString(stringID); - } - event.target.setAttribute("label", label); - ]]></body> - </method> - - <method name="handleEvent"> - <parameter name="aEvent"/> - <body><![CDATA[ - switch (aEvent.type) { - case "keypress": - this._handleKeyEvent(aEvent); - break; - case "sizemodechange": - if (aEvent.target == window) { - this.mCurrentBrowser.docShellIsActive = - (window.windowState != window.STATE_MINIMIZED); - } - break; - } - ]]></body> - </method> - - <method name="receiveMessage"> - <parameter name="aMessage"/> - <body><![CDATA[ - let json = aMessage.json; - let browser = aMessage.target; - - switch (aMessage.name) { - case "DOMTitleChanged": { - let tab = this.getTabForBrowser(browser); - if (!tab) - return; - let titleChanged = this.setTabTitle(tab); - if (titleChanged && !tab.selected && !tab.hasAttribute("busy")) - tab.setAttribute("titlechanged", "true"); - break; - } - case "DOMWebNotificationClicked": { - let tab = this.getTabForBrowser(browser); - if (!tab) - return; - this.selectedTab = tab; - window.focus(); - break; - } - } - ]]></body> - </method> - - <constructor> - <![CDATA[ - let browserStack = document.getAnonymousElementByAttribute(this, "anonid", "browserStack"); - this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser"); - if (Services.prefs.getBoolPref("browser.tabs.remote")) { - browserStack.removeChild(this.mCurrentBrowser); - this.mCurrentBrowser.setAttribute("remote", true); - browserStack.appendChild(this.mCurrentBrowser); - } - - this.mCurrentTab = this.tabContainer.firstChild; - document.addEventListener("keypress", this, false); - window.addEventListener("sizemodechange", this, false); - - var uniqueId = "panel" + Date.now(); - this.mPanelContainer.childNodes[0].id = uniqueId; - this.mCurrentTab.linkedPanel = uniqueId; - this.mCurrentTab._tPos = 0; - this.mCurrentTab._fullyOpen = true; - this.mCurrentTab.linkedBrowser = this.mCurrentBrowser; - this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab); - - // set up the shared autoscroll popup - this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup(); - this._autoScrollPopup.id = "autoscroller"; - this.appendChild(this._autoScrollPopup); - this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id); - this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink; - this.updateWindowResizers(); - - // Hook up the event listeners to the first browser - var tabListener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true); - const nsIWebProgress = Components.interfaces.nsIWebProgress; - const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"] - .createInstance(nsIWebProgress); - filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL); - this.mTabListeners[0] = tabListener; - this.mTabFilters[0] = filter; - - try { - // We assume this can only fail because mCurrentBrowser's docShell - // hasn't been created, yet. This may be caused by code accessing - // gBrowser before the window has finished loading. - this._addProgressListenerForInitialTab(); - } catch (e) { - // The binding was constructed too early, wait until the initial - // tab's document is ready, then add the progress listener. - this._waitForInitialContentDocument(); - } - - this.style.backgroundColor = - Services.prefs.getBoolPref("browser.display.use_system_colors") ? - "-moz-default-background-color" : - Services.prefs.getCharPref("browser.display.background_color"); - - if (Services.prefs.getBoolPref("browser.tabs.remote")) { - messageManager.addMessageListener("DOMTitleChanged", this); - } else { - this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID, - this.mCurrentBrowser); - } - messageManager.addMessageListener("DOMWebNotificationClicked", this); - ]]> - </constructor> - - <method name="_addProgressListenerForInitialTab"> - <body><![CDATA[ - this.webProgress.addProgressListener(this.mTabFilters[0], Ci.nsIWebProgress.NOTIFY_ALL); - ]]></body> - </method> - - <method name="_waitForInitialContentDocument"> - <body><![CDATA[ - let obs = (subject, topic) => { - if (this.browsers[0].contentWindow == subject) { - Services.obs.removeObserver(obs, topic); - this._addProgressListenerForInitialTab(); - } - }; - - // We use content-document-global-created as an approximation for - // "docShell is initialized". We can do this because in the - // mTabProgressListener we care most about the STATE_STOP notification - // that will reset mBlank. That means it's important to at least add - // the progress listener before the initial about:blank load stops - // if we can't do it before the load starts. - Services.obs.addObserver(obs, "content-document-global-created", false); - ]]></body> - </method> - - <destructor> - <![CDATA[ - for (var i = 0; i < this.mTabListeners.length; ++i) { - let browser = this.getBrowserAtIndex(i); - if (browser.registeredOpenURI) { - this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI); - delete browser.registeredOpenURI; - } - browser.webProgress.removeProgressListener(this.mTabFilters[i]); - this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]); - this.mTabFilters[i] = null; - this.mTabListeners[i].destroy(); - this.mTabListeners[i] = null; - } - document.removeEventListener("keypress", this, false); - window.removeEventListener("sizemodechange", this, false); - - if (Services.prefs.getBoolPref("browser.tabs.remote")) - messageManager.removeMessageListener("DOMTitleChanged", this); - ]]> - </destructor> - - <!-- Deprecated stuff, implemented for backwards compatibility. --> - <method name="enterTabbedMode"> - <body> - Application.console.log("enterTabbedMode is an obsolete method and " + - "will be removed in a future release."); - </body> - </method> - <field name="mTabbedMode" readonly="true">true</field> - <method name="setStripVisibilityTo"> - <parameter name="aShow"/> - <body> - this.tabContainer.visible = aShow; - </body> - </method> - <method name="getStripVisibility"> - <body> - return this.tabContainer.visible; - </body> - </method> - <property name="mContextTab" readonly="true" - onget="return TabContextMenu.contextTab;"/> - <property name="mPrefs" readonly="true" - onget="return Services.prefs;"/> - <property name="mTabContainer" readonly="true" - onget="return this.tabContainer;"/> - <property name="mTabs" readonly="true" - onget="return this.tabs;"/> - <!-- - - Compatibility hack: several extensions depend on this property to - - access the tab context menu or tab container, so keep that working for - - now. Ideally we can remove this once extensions are using - - tabbrowser.tabContextMenu and tabbrowser.tabContainer directly. - --> - <property name="mStrip" readonly="true"> - <getter> - <![CDATA[ - return ({ - self: this, - childNodes: [null, this.tabContextMenu, this.tabContainer], - firstChild: { nextSibling: this.tabContextMenu }, - getElementsByAttribute: function (attr, attrValue) { - if (attr == "anonid" && attrValue == "tabContextMenu") - return [this.self.tabContextMenu]; - return []; - }, - // Also support adding event listeners (forward to the tab container) - addEventListener: function (a,b,c) { this.self.tabContainer.addEventListener(a,b,c); }, - removeEventListener: function (a,b,c) { this.self.tabContainer.removeEventListener(a,b,c); } - }); - ]]> - </getter> - </property> - <field name="_soundPlayingAttrRemovalTimer">0</field> - </implementation> - - <handlers> - <handler event="DOMWindowClose" phase="capturing"> - <![CDATA[ - if (!event.isTrusted) - return; - - if (this.tabs.length == 1) - return; - - var tab = this._getTabForContentWindow(event.target); - if (tab) { - this.removeTab(tab); - event.preventDefault(); - } - ]]> - </handler> - <handler event="DOMWillOpenModalDialog" phase="capturing"> - <![CDATA[ - if (!event.isTrusted) - return; - - // We're about to open a modal dialog, make sure the opening - // tab is brought to the front. - this.selectedTab = this._getTabForContentWindow(event.target.top); - ]]> - </handler> - <handler event="DOMTitleChanged"> - <![CDATA[ - if (!event.isTrusted) - return; - - var contentWin = event.target.defaultView; - if (contentWin != contentWin.top) - return; - - var tab = this._getTabForContentWindow(contentWin); - if (!tab) { - return; - } - - var titleChanged = this.setTabTitle(tab); - if (titleChanged && !tab.selected && !tab.hasAttribute("busy")) - tab.setAttribute("titlechanged", "true"); - ]]> - </handler> - <handler event="DOMAudioPlaybackStarted"> - <![CDATA[ - var tab = getTabFromAudioEvent(event) - if (!tab) { - return; - } - - clearTimeout(tab._soundPlayingAttrRemovalTimer); - tab._soundPlayingAttrRemovalTimer = 0; - - let modifiedAttrs = []; - if (tab.hasAttribute("soundplaying-scheduledremoval")) { - tab.removeAttribute("soundplaying-scheduledremoval"); - modifiedAttrs.push("soundplaying-scheduledremoval"); - } - - if (!tab.hasAttribute("soundplaying")) { - tab.setAttribute("soundplaying", true); - modifiedAttrs.push("soundplaying"); - } - - this._tabAttrModified(tab, modifiedAttrs); - ]]> - </handler> - <handler event="DOMAudioPlaybackStopped"> - <![CDATA[ - var tab = getTabFromAudioEvent(event) - if (!tab) { - return; - } - - if (tab.hasAttribute("soundplaying")) { - let removalDelay = Services.prefs.getIntPref("browser.tabs.delayHidingAudioPlayingIconMS"); - - tab.style.setProperty("--soundplaying-removal-delay", `${removalDelay - 300}ms`); - tab.setAttribute("soundplaying-scheduledremoval", "true"); - this._tabAttrModified(tab, ["soundplaying-scheduledremoval"]); - - tab._soundPlayingAttrRemovalTimer = setTimeout(() => { - tab.removeAttribute("soundplaying-scheduledremoval"); - tab.removeAttribute("soundplaying"); - this._tabAttrModified(tab, ["soundplaying", "soundplaying-scheduledremoval"]); - }, removalDelay); - } - ]]> - </handler> - <handler event="DOMAudioPlaybackBlockStarted"> - <![CDATA[ - var tab = getTabFromAudioEvent(event) - if (!tab) { - return; - } - - if (!tab.hasAttribute("blocked")) { - tab.setAttribute("blocked", true); - this._tabAttrModified(tab, ["blocked"]); - } - ]]> - </handler> - <handler event="DOMAudioPlaybackBlockStopped"> - <![CDATA[ - var tab = getTabFromAudioEvent(event) - if (!tab) { - return; - } - - if (tab.hasAttribute("blocked")) { - tab.removeAttribute("blocked"); - this._tabAttrModified(tab, ["blocked"]); - } - ]]> - </handler> - </handlers> - </binding> - - <binding id="tabbrowser-tabbox" - extends="chrome://global/content/bindings/tabbox.xml#tabbox"> - <implementation> - <property name="tabs" readonly="true" - onget="return document.getBindingParent(this).tabContainer;"/> - </implementation> - </binding> - - <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll"> - <implementation> - <!-- Override scrollbox.xml method, since our scrollbox's children are - inherited from the binding parent --> - <method name="_getScrollableElements"> - <body><![CDATA[ - return Array.filter(document.getBindingParent(this).childNodes, - this._canScrollToElement, this); - ]]></body> - </method> - <method name="_canScrollToElement"> - <parameter name="tab"/> - <body><![CDATA[ - return !tab.pinned && !tab.hidden; - ]]></body> - </method> - </implementation> - - <handlers> - <handler event="underflow" phase="capturing"><![CDATA[ - if (event.detail == 0) - return; // Ignore vertical events - - var tabs = document.getBindingParent(this); - tabs.removeAttribute("overflow"); - - if (tabs._lastTabClosedByMouse) - tabs._expandSpacerBy(this._scrollButtonDown.clientWidth); - - tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab, - tabs.tabbrowser); - - tabs._positionPinnedTabs(); - ]]></handler> - <handler event="overflow"><![CDATA[ - if (event.detail == 0) - return; // Ignore vertical events - - var tabs = document.getBindingParent(this); - tabs.setAttribute("overflow", "true"); - tabs._positionPinnedTabs(); - tabs._handleTabSelect(false); - ]]></handler> - </handlers> - </binding> - - <binding id="tabbrowser-tabs" - extends="chrome://global/content/bindings/tabbox.xml#tabs"> - <resources> - <stylesheet src="chrome://browser/content/tabbrowser.css"/> - </resources> - - <content> - <xul:hbox align="end"> - <xul:image class="tab-drop-indicator" anonid="tab-drop-indicator" collapsed="true"/> - </xul:hbox> - <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1" - style="min-width: 1px;" -#ifndef XP_MACOSX - clicktoscroll="true" -#endif - class="tabbrowser-arrowscrollbox"> -# This is a hack to circumvent bug 472020, otherwise the tabs show up on the -# right of the newtab button. - <children includes="tab"/> -# This is to ensure anything extensions put here will go before the newtab -# button, necessary due to the previous hack. - <children/> - <xul:toolbarbutton class="tabs-newtab-button" - command="cmd_newNavigatorTab" - onclick="checkForMiddleClick(this, event);" - onmouseover="document.getBindingParent(this)._enterNewTab();" - onmouseout="document.getBindingParent(this)._leaveNewTab();" - tooltiptext="&newTabButton.tooltip;"/> - <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer" - style="width: 0;"/> - </xul:arrowscrollbox> - </content> - - <implementation implements="nsIDOMEventListener"> - <constructor> - <![CDATA[ - this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth"); - this.mCloseButtons = Services.prefs.getIntPref("browser.tabs.closeButtons"); - this._closeWindowWithLastTab = Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab"); - - var tab = this.firstChild; - tab.setAttribute("label", - this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle")); - tab.setAttribute("crop", "end"); - tab.setAttribute("onerror", "this.removeAttribute('image');"); - this.adjustTabstrip(); - - Services.prefs.addObserver("browser.tabs.", this._prefObserver, false); - window.addEventListener("resize", this, false); - window.addEventListener("load", this, false); - - this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled", false); - this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled"); - ]]> - </constructor> - - <destructor> - <![CDATA[ - Services.prefs.removeObserver("browser.tabs.", this._prefObserver); - ]]> - </destructor> - - <field name="tabbrowser" readonly="true"> - document.getElementById(this.getAttribute("tabbrowser")); - </field> - - <field name="tabbox" readonly="true"> - this.tabbrowser.mTabBox; - </field> - - <field name="contextMenu" readonly="true"> - document.getElementById("tabContextMenu"); - </field> - - <field name="mTabstripWidth">0</field> - - <field name="mTabstrip"> - document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox"); - </field> - - <field name="_firstTab">null</field> - <field name="_lastTab">null</field> - <field name="_afterSelectedTab">null</field> - <field name="_beforeHoveredTab">null</field> - <field name="_afterHoveredTab">null</field> - - <method name="_setPositionalAttributes"> - <body><![CDATA[ - let visibleTabs = this.tabbrowser.visibleTabs; - - if (!visibleTabs.length) - return; - - let selectedIndex = visibleTabs.indexOf(this.selectedItem); - - let lastVisible = visibleTabs.length - 1; - - if (this._afterSelectedTab) - this._afterSelectedTab.removeAttribute("afterselected-visible"); - if (this.selectedItem.closing || selectedIndex == lastVisible) { - this._afterSelectedTab = null; - } else { - this._afterSelectedTab = visibleTabs[selectedIndex + 1]; - this._afterSelectedTab.setAttribute("afterselected-visible", - "true"); - } - - if (this._firstTab) - this._firstTab.removeAttribute("first-visible-tab"); - this._firstTab = visibleTabs[0]; - this._firstTab.setAttribute("first-visible-tab", "true"); - if (this._lastTab) - this._lastTab.removeAttribute("last-visible-tab"); - this._lastTab = visibleTabs[lastVisible]; - this._lastTab.setAttribute("last-visible-tab", "true"); - ]]></body> - </method> - - <field name="_prefObserver"><![CDATA[({ - tabContainer: this, - - observe: function (subject, topic, data) { - switch (data) { - case "browser.tabs.closeButtons": - this.tabContainer.mCloseButtons = Services.prefs.getIntPref(data); - this.tabContainer.adjustTabstrip(); - break; - case "browser.tabs.autoHide": - this.tabContainer.updateVisibility(); - break; - case "browser.tabs.closeWindowWithLastTab": - this.tabContainer._closeWindowWithLastTab = Services.prefs.getBoolPref(data); - this.tabContainer.adjustTabstrip(); - break; - } - } - });]]></field> - <field name="_blockDblClick">false</field> - - <field name="_tabDropIndicator"> - document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator"); - </field> - - <field name="_dragOverDelay">350</field> - <field name="_dragTime">0</field> - - <field name="_container" readonly="true"><![CDATA[ - this.parentNode && this.parentNode.localName == "toolbar" ? this.parentNode : this; - ]]></field> - - <field name="_propagatedVisibilityOnce">false</field> - - <property name="visible" - onget="return !this._container.collapsed;"> - <setter><![CDATA[ - if (val == this.visible && - this._propagatedVisibilityOnce) - return val; - - this._container.collapsed = !val; - - this._propagateVisibility(); - this._propagatedVisibilityOnce = true; - - return val; - ]]></setter> - </property> - - <method name="_enterNewTab"> - <body><![CDATA[ - let visibleTabs = this.tabbrowser.visibleTabs; - let candidate = visibleTabs[visibleTabs.length - 1]; - if (!candidate.selected) { - this._beforeHoveredTab = candidate; - candidate.setAttribute("beforehovered", "true"); - } - ]]></body> - </method> - - <method name="_leaveNewTab"> - <body><![CDATA[ - if (this._beforeHoveredTab) { - this._beforeHoveredTab.removeAttribute("beforehovered"); - this._beforeHoveredTab = null; - } - ]]></body> - </method> - - <method name="_propagateVisibility"> - <body><![CDATA[ - let visible = this.visible; - - document.getElementById("menu_closeWindow").hidden = !visible; - document.getElementById("menu_close").setAttribute("label", - this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close")); - - goSetCommandEnabled("cmd_ToggleTabsOnTop", visible); - - TabsOnTop.syncUI(); - - TabsInTitlebar.allowedBy("tabs-visible", visible); - ]]></body> - </method> - - <method name="updateVisibility"> - <body><![CDATA[ - if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1) - this.visible = window.toolbar.visible && - !Services.prefs.getBoolPref("browser.tabs.autoHide"); - else - this.visible = true; - ]]></body> - </method> - - <method name="adjustTabstrip"> - <body><![CDATA[ - let numTabs = this.childNodes.length - - this.tabbrowser._removingTabs.length; - // modes for tabstrip - // 0 - button on active tab only - // 1 - close buttons on all tabs, if available space allows - // 2 - no close buttons at all - // 3 - close button at the end of the tabstrip - switch (this.mCloseButtons) { - case 0: - // If we decide we want to hide the close tab button on the last tab - // when closing the window with the last tab, then we should check - // if (numTabs == 1 && this._closeWindowWithLastTab) here and set - // this.setAttribute("closebuttons", "hidden") appropriately - this.setAttribute("closebuttons", "activetab"); - break; - case 1: - if (numTabs == 1) { - // See remark about potentially hiding the close tab button, above. - this.setAttribute("closebuttons", "alltabs"); - } else if (numTabs == 2) { - // This is an optimization to avoid layout flushes by calling - // getBoundingClientRect() when we just opened a second tab. In - // this case it's highly unlikely that the tab width is smaller - // than mTabClipWidth and the tab close button obscures too much - // of the tab's label. In the edge case of the window being too - // narrow (or if tabClipWidth has been set to a way higher value), - // we'll correct the 'closebuttons' attribute after the tabopen - // animation has finished. - this.setAttribute("closebuttons", "alltabs"); - } else { - let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs]; - if (tab && tab.getBoundingClientRect().width > this.mTabClipWidth) - this.setAttribute("closebuttons", "alltabs"); - else - this.setAttribute("closebuttons", "activetab"); - } - break; - case 2: - case 3: - this.setAttribute("closebuttons", "never"); - break; - } - var tabstripClosebutton = document.getElementById("tabs-closebutton"); - if (tabstripClosebutton && tabstripClosebutton.parentNode == this._container) - tabstripClosebutton.collapsed = this.mCloseButtons != 3; - ]]></body> - </method> - - <method name="_handleTabSelect"> - <parameter name="aSmoothScroll"/> - <body><![CDATA[ - if (this.getAttribute("overflow") == "true") - this.mTabstrip.ensureElementIsVisible(this.selectedItem, aSmoothScroll); - ]]></body> - </method> - - <method name="_fillTrailingGap"> - <body><![CDATA[ - try { - // if we're at the right side (and not the logical end, - // which is why this works for both LTR and RTL) - // of the tabstrip, we need to ensure that we stay - // completely scrolled to the right side - var tabStrip = this.mTabstrip; - if (tabStrip.scrollPosition + tabStrip.scrollClientSize > - tabStrip.scrollSize) - tabStrip.scrollByPixels(-1); - } catch (e) {} - ]]></body> - </method> - - <field name="_closingTabsSpacer"> - document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer"); - </field> - - <field name="_tabDefaultMaxWidth">NaN</field> - <field name="_lastTabClosedByMouse">false</field> - <field name="_hasTabTempMaxWidth">false</field> - - <!-- Try to keep the active tab's close button under the mouse cursor --> - <method name="_lockTabSizing"> - <parameter name="aTab"/> - <body><![CDATA[ - var tabs = this.tabbrowser.visibleTabs; - if (!tabs.length) - return; - - var isEndTab = (aTab._tPos > tabs[tabs.length-1]._tPos); - var tabWidth = aTab.getBoundingClientRect().width; - - if (!this._tabDefaultMaxWidth) - this._tabDefaultMaxWidth = - parseFloat(window.getComputedStyle(aTab).maxWidth); - this._lastTabClosedByMouse = true; - - if (this.getAttribute("overflow") == "true") { -#ifdef XP_WIN - // Don't need to do anything if we're in overflow mode and we're closing - // the last tab. - if (isEndTab) -#else - // Don't need to do anything if we're in overflow mode and aren't scrolled - // all the way to the right, or if we're closing the last tab. - if (isEndTab || !this.mTabstrip._scrollButtonDown.disabled) -#endif - return; - - // If the tab has an owner that will become the active tab, the owner will - // be to the left of it, so we actually want the left tab to slide over. - // This can't be done as easily in non-overflow mode, so we don't bother. - if (aTab.owner) - return; - - // Resize immediately if preffed - // XXX: we may want to make this a three-state pref to disable this early - // exit if people prefer a mix of behavior (don't resize in overflow, - // but resize if not overflowing) - if (Services.prefs.getBoolPref("browser.tabs.resize_immediately")) - return; - - this._expandSpacerBy(tabWidth); - } else { // non-overflow mode - // Locking is neither in effect nor needed, so let tabs expand normally. - if (isEndTab && !this._hasTabTempMaxWidth) - return; - - // Resize immediately if preffed - // XXX: we may want to make this a three-state pref to disable this early - // exit if people prefer a mix of behavior (don't resize in overflow, - // but resize if not overflowing) - if (Services.prefs.getBoolPref("browser.tabs.resize_immediately")) - return; - - let numPinned = this.tabbrowser._numPinnedTabs; - // Force tabs to stay the same width, unless we're closing the last tab, - // which case we need to let them expand just enough so that the overall - // tabbar width is the same. - if (isEndTab) { - let numNormalTabs = tabs.length - numPinned; - tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs; - if (tabWidth > this._tabDefaultMaxWidth) - tabWidth = this._tabDefaultMaxWidth; - } - tabWidth += "px"; - for (let i = numPinned; i < tabs.length; i++) { - let tab = tabs[i]; - tab.style.setProperty("max-width", tabWidth, "important"); - if (!isEndTab) { // keep tabs the same width - tab.style.transition = "none"; - tab.clientTop; // flush styles to skip animation; see bug 649247 - tab.style.transition = ""; - } - } - this._hasTabTempMaxWidth = true; - this.tabbrowser.addEventListener("mousemove", this, false); - window.addEventListener("mouseout", this, false); - } - ]]></body> - </method> - - <method name="_expandSpacerBy"> - <parameter name="pixels"/> - <body><![CDATA[ - let spacer = this._closingTabsSpacer; - spacer.style.width = parseFloat(spacer.style.width) + pixels + "px"; - this.setAttribute("using-closing-tabs-spacer", "true"); - this.tabbrowser.addEventListener("mousemove", this, false); - window.addEventListener("mouseout", this, false); - ]]></body> - </method> - - <method name="_unlockTabSizing"> - <body><![CDATA[ - this.tabbrowser.removeEventListener("mousemove", this, false); - window.removeEventListener("mouseout", this, false); - - if (this._hasTabTempMaxWidth) { - this._hasTabTempMaxWidth = false; - let tabs = this.tabbrowser.visibleTabs; - for (let i = 0; i < tabs.length; i++) - tabs[i].style.maxWidth = ""; - } - - if (this.hasAttribute("using-closing-tabs-spacer")) { - this.removeAttribute("using-closing-tabs-spacer"); - this._closingTabsSpacer.style.width = 0; - } - ]]></body> - </method> - - <field name="_lastNumPinned">0</field> - <method name="_positionPinnedTabs"> - <body><![CDATA[ - var numPinned = this.tabbrowser._numPinnedTabs; - var doPosition = this.getAttribute("overflow") == "true" && - numPinned > 0; - - if (doPosition) { - this.setAttribute("positionpinnedtabs", "true"); - - let scrollButtonWidth = this.mTabstrip._scrollButtonDown.getBoundingClientRect().width; - let paddingStart = this.mTabstrip.scrollboxPaddingStart; - let width = 0; - - for (let i = numPinned - 1; i >= 0; i--) { - let tab = this.childNodes[i]; - width += tab.getBoundingClientRect().width; - tab.style.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px"; - } - - this.style.MozPaddingStart = width + paddingStart + "px"; - - } else { - this.removeAttribute("positionpinnedtabs"); - - for (let i = 0; i < numPinned; i++) { - let tab = this.childNodes[i]; - tab.style.MozMarginStart = ""; - } - - this.style.MozPaddingStart = ""; - } - - if (this._lastNumPinned != numPinned) { - this._lastNumPinned = numPinned; - this._handleTabSelect(false); - } - ]]></body> - </method> - - <method name="_animateTabMove"> - <parameter name="event"/> - <body><![CDATA[ - let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0); - - if (this.getAttribute("movingtab") != "true") { - this.setAttribute("movingtab", "true"); - this.selectedItem = draggedTab; - } - - if (!("animLastScreenX" in draggedTab._dragData)) - draggedTab._dragData.animLastScreenX = draggedTab._dragData.screenX; - - let screenX = event.screenX; - if (screenX == draggedTab._dragData.animLastScreenX) - return; - - let draggingRight = screenX > draggedTab._dragData.animLastScreenX; - draggedTab._dragData.animLastScreenX = screenX; - - let rtl = (window.getComputedStyle(this).direction == "rtl"); - let pinned = draggedTab.pinned; - let numPinned = this.tabbrowser._numPinnedTabs; - let tabs = this.tabbrowser.visibleTabs - .slice(pinned ? 0 : numPinned, - pinned ? numPinned : undefined); - if (rtl) - tabs.reverse(); - let tabWidth = draggedTab.getBoundingClientRect().width; - - // Move the dragged tab based on the mouse position. - - let leftTab = tabs[0]; - let rightTab = tabs[tabs.length - 1]; - let tabScreenX = draggedTab.boxObject.screenX; - let translateX = screenX - draggedTab._dragData.screenX; - if (!pinned) - translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX; - let leftBound = leftTab.boxObject.screenX - tabScreenX; - let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) - - (tabScreenX + tabWidth); - translateX = Math.max(translateX, leftBound); - translateX = Math.min(translateX, rightBound); - draggedTab.style.transform = "translateX(" + translateX + "px)"; - - // Determine what tab we're dragging over. - // * Point of reference is the center of the dragged tab. If that - // point touches a background tab, the dragged tab would take that - // tab's position when dropped. - // * We're doing a binary search in order to reduce the amount of - // tabs we need to check. - - let tabCenter = tabScreenX + translateX + tabWidth / 2; - let newIndex = -1; - let oldIndex = "animDropIndex" in draggedTab._dragData ? - draggedTab._dragData.animDropIndex : draggedTab._tPos; - let low = 0; - let high = tabs.length - 1; - while (low <= high) { - let mid = Math.floor((low + high) / 2); - if (tabs[mid] == draggedTab && - ++mid > high) - break; - let boxObject = tabs[mid].boxObject; - let screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex); - if (screenX > tabCenter) { - high = mid - 1; - } else if (screenX + boxObject.width < tabCenter) { - low = mid + 1; - } else { - newIndex = tabs[mid]._tPos; - break; - } - } - if (newIndex >= oldIndex) - newIndex++; - if (newIndex < 0 || newIndex == oldIndex) - return; - draggedTab._dragData.animDropIndex = newIndex; - - // Shift background tabs to leave a gap where the dragged tab - // would currently be dropped. - - for (let tab of tabs) { - if (tab != draggedTab) { - let shift = getTabShift(tab, newIndex); - tab.style.transform = shift ? "translateX(" + shift + "px)" : ""; - } - } - - function getTabShift(tab, dropIndex) { - if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex) - return rtl ? -tabWidth : tabWidth; - if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex) - return rtl ? tabWidth : -tabWidth; - return 0; - } - ]]></body> - </method> - - <method name="_finishAnimateTabMove"> - <body><![CDATA[ - if (this.getAttribute("movingtab") != "true") - return; - - for (let tab of this.tabbrowser.visibleTabs) - tab.style.transform = ""; - - this.removeAttribute("movingtab"); - - this._handleTabSelect(); - ]]></body> - </method> - - <method name="handleEvent"> - <parameter name="aEvent"/> - <body><![CDATA[ - switch (aEvent.type) { - case "load": - this.updateVisibility(); - break; - case "resize": - if (aEvent.target != window) - break; - - let sizemode = document.documentElement.getAttribute("sizemode"); - TabsInTitlebar.allowedBy("sizemode", - sizemode == "maximized" || sizemode == "fullscreen"); - - var width = this.mTabstrip.boxObject.width; - if (width != this.mTabstripWidth) { - this.adjustTabstrip(); - this._fillTrailingGap(); - this._handleTabSelect(); - this.mTabstripWidth = width; - } - - this.tabbrowser.updateWindowResizers(); - break; - case "mouseout": - // If the "related target" (the node to which the pointer went) is not - // a child of the current document, the mouse just left the window. - let relatedTarget = aEvent.relatedTarget; - if (relatedTarget && relatedTarget.ownerDocument == document) - break; - case "mousemove": - if (document.getElementById("tabContextMenu").state != "open") - this._unlockTabSizing(); - break; - } - ]]></body> - </method> - - <field name="_animateElement"> - this.mTabstrip._scrollButtonDown; - </field> - - <method name="_notifyBackgroundTab"> - <parameter name="aTab"/> - <body><![CDATA[ - if (aTab.pinned) - return; - - var scrollRect = this.mTabstrip.scrollClientRect; - var tab = aTab.getBoundingClientRect(); - - // Is the new tab already completely visible? - if (scrollRect.left <= tab.left && tab.right <= scrollRect.right) - return; - - if (this.mTabstrip.smoothScroll) { - let selected = !this.selectedItem.pinned && - this.selectedItem.getBoundingClientRect(); - - // Can we make both the new tab and the selected tab completely visible? - if (!selected || - Math.max(tab.right - selected.left, selected.right - tab.left) <= - scrollRect.width) { - this.mTabstrip.ensureElementIsVisible(aTab); - return; - } - - this.mTabstrip._smoothScrollByPixels(this.mTabstrip._isRTLScrollbox ? - selected.right - scrollRect.right : - selected.left - scrollRect.left); - } - - if (!this._animateElement.hasAttribute("notifybgtab")) { - this._animateElement.setAttribute("notifybgtab", "true"); - setTimeout(function (ele) { - ele.removeAttribute("notifybgtab"); - }, 150, this._animateElement); - } - ]]></body> - </method> - - <method name="_getDragTargetTab"> - <parameter name="event"/> - <body><![CDATA[ - let tab = event.target.localName == "tab" ? event.target : null; - if (tab && - (event.type == "drop" || event.type == "dragover") && - event.dataTransfer.dropEffect == "link") { - let boxObject = tab.boxObject; - if (event.screenX < boxObject.screenX + boxObject.width * .25 || - event.screenX > boxObject.screenX + boxObject.width * .75) - return null; - } - return tab; - ]]></body> - </method> - - <method name="_getDropIndex"> - <parameter name="event"/> - <body><![CDATA[ - var tabs = this.childNodes; - var tab = this._getDragTargetTab(event); - if (window.getComputedStyle(this, null).direction == "ltr") { - for (let i = tab ? tab._tPos : 0; i < tabs.length; i++) - if (event.screenX < tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2) - return i; - } else { - for (let i = tab ? tab._tPos : 0; i < tabs.length; i++) - if (event.screenX > tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2) - return i; - } - return tabs.length; - ]]></body> - </method> - - <method name="_setEffectAllowedForDataTransfer"> - <parameter name="event"/> - <body><![CDATA[ - var dt = event.dataTransfer; - // Disallow dropping multiple items - if (dt.mozItemCount > 1) - return dt.effectAllowed = "none"; - - var types = dt.mozTypesAt(0); - var sourceNode = null; - // tabs are always added as the first type - if (types[0] == TAB_DROP_TYPE) { - var sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0); - if (sourceNode instanceof XULElement && - sourceNode.localName == "tab" && - sourceNode.ownerDocument.defaultView instanceof ChromeWindow && - sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" && - sourceNode.ownerDocument.defaultView.gBrowser.tabContainer == sourceNode.parentNode) { - // Do not allow transfering a private tab to a non-private window - // and vice versa. - if (PrivateBrowsingUtils.isWindowPrivate(window) != - PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerDocument.defaultView)) - return dt.effectAllowed = "none"; - -#ifdef XP_MACOSX - return dt.effectAllowed = event.altKey ? "copy" : "move"; -#else - return dt.effectAllowed = event.ctrlKey ? "copy" : "move"; -#endif - } - } - - if (browserDragAndDrop.canDropLink(event)) { - // Here we need to do this manually - return dt.effectAllowed = dt.dropEffect = "link"; - } - return dt.effectAllowed = "none"; - ]]></body> - </method> - - <method name="_handleNewTab"> - <parameter name="tab"/> - <body><![CDATA[ - if (tab.parentNode != this) - return; - tab._fullyOpen = true; - - this.adjustTabstrip(); - - if (tab.getAttribute("selected") == "true") { - this._fillTrailingGap(); - this._handleTabSelect(); - } else { - if (tab.hasAttribute("skipbackgroundnotify")) { - tab.removeAttribute("skipbackgroundnotify"); - } else { - this._notifyBackgroundTab(tab); - } - } - - // XXXmano: this is a temporary workaround for bug 345399 - // We need to manually update the scroll buttons disabled state - // if a tab was inserted to the overflow area or removed from it - // without any scrolling and when the tabbar has already - // overflowed. - this.mTabstrip._updateScrollButtonsDisabledState(); - ]]></body> - </method> - - <method name="_canAdvanceToTab"> - <parameter name="aTab"/> - <body> - <![CDATA[ - return !aTab.closing; - ]]> - </body> - </method> - - <method name="_handleTabTelemetryStart"> - <parameter name="aTab"/> - <parameter name="aURI"/> - <body> - <![CDATA[ - // Animation-smoothness telemetry/logging - if (this._tabAnimationLoggingEnabled) { - if (aURI == "about:newtab" && (aTab._tPos == 1 || aTab._tPos == 2)) { - // Indicate newtab page animation where other tabs are unaffected - // (for which case, the 2nd or 3rd tabs are good representatives, even if not absolute) - aTab._recordingTabOpenPlain = true; - } - aTab._recordingHandle = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .startFrameTimeRecording(); - } - - // Overall animation duration - aTab._animStartTime = Date.now(); - ]]> - </body> - </method> - - <method name="_handleTabTelemetryEnd"> - <parameter name="aTab"/> - <body> - <![CDATA[ - if (!aTab._animStartTime) { - return; - } - - aTab._animStartTime = 0; - - // Handle tab animation smoothness telemetry/logging of frame intervals and paint times - if (!("_recordingHandle" in aTab)) { - return; - } - - let paints = {}; - let intervals = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .stopFrameTimeRecording(aTab._recordingHandle, paints); - delete aTab._recordingHandle; - paints = paints.value; // The result array itself. - let frameCount = intervals.length; - - if (this._tabAnimationLoggingEnabled) { - let msg = "Tab " + (aTab.closing ? "close" : "open") + " (Frame-interval / paint-processing):\n"; - for (let i = 0; i < frameCount; i++) { - msg += Math.round(intervals[i]) + " / " + Math.round(paints[i]) + "\n"; - } - Services.console.logStringMessage(msg); - } - - // For telemetry, the first frame interval is not useful since it may represent an interval - // to a relatively old frame (prior to recording start). So we'll ignore it for the average. - // But if we recorded only 1 frame (very rare), then the first paint duration is a good - // representative of the first frame interval for our cause (indicates very bad animation). - // First paint duration is always useful for us. - if (frameCount > 0) { - let averageInterval = 0; - let averagePaint = paints[0]; - for (let i = 1; i < frameCount; i++) { - averageInterval += intervals[i]; - averagePaint += paints[i]; - }; - averagePaint /= frameCount; - averageInterval = (frameCount == 1) - ? averagePaint - : averageInterval / (frameCount - 1); - - if (aTab._recordingTabOpenPlain) { - delete aTab._recordingTabOpenPlain; - } - } - ]]> - </body> - </method> - - <!-- Deprecated stuff, implemented for backwards compatibility. --> - <property name="mTabstripClosebutton" readonly="true" - onget="return document.getElementById('tabs-closebutton');"/> - <property name="mAllTabsPopup" readonly="true" - onget="return document.getElementById('alltabs-popup');"/> - </implementation> - - <handlers> - <handler event="TabSelect" action="this._handleTabSelect();"/> - - <handler event="transitionend"><![CDATA[ - if (event.propertyName != "max-width") - return; - - var tab = event.target; - - this._handleTabTelemetryEnd(tab); - - if (tab.getAttribute("fadein") == "true") { - if (tab._fullyOpen) - this.adjustTabstrip(); - else - this._handleNewTab(tab); - } else if (tab.closing) { - this.tabbrowser._endRemoveTab(tab); - } - ]]></handler> - - <handler event="dblclick"><![CDATA[ -#ifndef XP_MACOSX - // When the tabbar has an unified appearance with the titlebar - // and menubar, a double-click in it should have the same behavior - // as double-clicking the titlebar - if (TabsInTitlebar.enabled || - (TabsOnTop.enabled && this.parentNode._dragBindingAlive)) - return; -#endif - - if (event.button != 0 || - event.originalTarget.localName != "box") - return; - - // See comments in the "mousedown" and "click" event handlers of the - // tabbrowser-tabs binding. - if (!this._blockDblClick) - BrowserOpenTab(); - - event.preventDefault(); - ]]></handler> - - <!-- Consider that the in-tab close button is only shown on the active - tab. When clicking on an inactive tab at the position where the - close button will appear during the click, no "click" event will be - dispatched, because the mousedown and mouseup events don't have the - same event target. For that reason use "mousedown" instead of "click" - to implement in-tab close button behavior. (Pale Moon UXP issue #775) - --> - <handler event="mousedown" button="0" phase="capturing"><![CDATA[ - /* The only sequence in which a second click event (i.e. dblclik) - * can be dispatched on an in-tab close button is when it is shown - * after the first click (i.e. the first click event was dispatched - * on the tab). This happens when we show the close button only on - * the active tab. (bug 352021) - * The only sequence in which a third click event can be dispatched - * on an in-tab close button is when the tab was opened with a - * double click on the tabbar. (bug 378344) - * In both cases, it is most likely that the close button area has - * been accidentally clicked, therefore we do not close the tab. - * - * We don't want to ignore processing of more than one click event, - * though, since the user might actually be repeatedly clicking to - * close many tabs at once. - * - * Also prevent errant doubleclick on the close button from opening - * a new tab (bug 343628): - * Since we're removing the event target, if the user double-clicks - * the button, the dblclick event will be dispatched with the tabbar - * as its event target (and explicit/originalTarget), which treats - * that as a mouse gesture for opening a new tab. - * In this context, we're manually blocking the dblclick event. - */ - - // Reset flags at the beginning of a series of clicks: - if (event.detail == 1) { - this.flagClickOnCloseButton = false; - this.flagActivateTabOrClickOnTabbar = false; - } - - this.blockCloseButtonOnDblclick = this.flagActivateTabOrClickOnTabbar; - this._blockDblClick = this.flagClickOnCloseButton; - - // Set flags: - let eventTargetIsCloseButton = - event.originalTarget.classList.contains("tab-close-button"); - this.flagClickOnCloseButton = eventTargetIsCloseButton; - this.flagActivateTabOrClickOnTabbar = - ((!eventTargetIsCloseButton && event.detail == 1) || - event.originalTarget.localName == "box"); - ]]></handler> - - <handler event="click" button="0"><![CDATA[ - // See comment in the "mousedown" event handler of the - // tabbrowser-tabs binding. - if (event.originalTarget.classList.contains("tab-close-button") && - !this.blockCloseButtonOnDblclick) { - gBrowser.removeTab(document.getBindingParent(event.originalTarget), - {animate: true, byMouse: true,}); - this._blockDblClick = true; - } - ]]></handler> - - <handler event="click" button="1"><![CDATA[ - if (event.target.localName == "tab") { - if (this.childNodes.length > 1 || !this._closeWindowWithLastTab) - this.tabbrowser.removeTab(event.target, {animate: true, byMouse: true}); - } else if (event.originalTarget.localName == "box") { - BrowserOpenTab(); - } else { - return; - } - - event.stopPropagation(); - ]]></handler> - - <handler event="keypress"><![CDATA[ - if (event.altKey || event.shiftKey || -#ifdef XP_MACOSX - !event.metaKey) -#else - !event.ctrlKey || event.metaKey) -#endif - return; - - switch (event.keyCode) { - case KeyEvent.DOM_VK_UP: - this.tabbrowser.moveTabBackward(); - break; - case KeyEvent.DOM_VK_DOWN: - this.tabbrowser.moveTabForward(); - break; - case KeyEvent.DOM_VK_RIGHT: - case KeyEvent.DOM_VK_LEFT: - this.tabbrowser.moveTabOver(event); - break; - case KeyEvent.DOM_VK_HOME: - this.tabbrowser.moveTabToStart(); - break; - case KeyEvent.DOM_VK_END: - this.tabbrowser.moveTabToEnd(); - break; - default: - // Stop the keypress event for the above keyboard - // shortcuts only. - return; - } - event.stopPropagation(); - event.preventDefault(); - ]]></handler> - - <handler event="dragstart"><![CDATA[ - var tab = this._getDragTargetTab(event); - if (!tab) - return; - - let dt = event.dataTransfer; - dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0); - let browser = tab.linkedBrowser; - - // We must not set text/x-moz-url or text/plain data here, - // otherwise trying to deatch the tab by dropping it on the desktop - // may result in an "internet shortcut" - dt.mozSetDataAt("text/x-moz-text-internal", browser.currentURI.spec, 0); - - // Set the cursor to an arrow during tab drags. - dt.mozCursor = "default"; - - // Create a canvas to which we capture the current tab. - // Until canvas is HiDPI-aware (bug 780362), we need to scale the desired - // canvas size (in CSS pixels) to the window's backing resolution in order - // to get a full-resolution drag image for use on HiDPI displays. - let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils); - let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom; - let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - canvas.mozOpaque = true; - canvas.width = 160 * scale; - canvas.height = 90 * scale; - PageThumbs.captureToCanvas(browser, canvas); - dt.setDragImage(canvas, -16 * scale, -16 * scale); - - // _dragData.offsetX/Y give the coordinates that the mouse should be - // positioned relative to the corner of the new window created upon - // dragend such that the mouse appears to have the same position - // relative to the corner of the dragged tab. - function clientX(ele) ele.getBoundingClientRect().left; - let tabOffsetX = clientX(tab) - clientX(this); - tab._dragData = { - offsetX: event.screenX - window.screenX - tabOffsetX, - offsetY: event.screenY - window.screenY, - scrollX: this.mTabstrip.scrollPosition, - screenX: event.screenX - }; - - event.stopPropagation(); - ]]></handler> - - <handler event="dragover"><![CDATA[ - var effects = this._setEffectAllowedForDataTransfer(event); - - var ind = this._tabDropIndicator; - if (effects == "" || effects == "none") { - ind.collapsed = true; - return; - } - event.preventDefault(); - event.stopPropagation(); - - var tabStrip = this.mTabstrip; - var ltr = (window.getComputedStyle(this, null).direction == "ltr"); - - // autoscroll the tab strip if we drag over the scroll - // buttons, even if we aren't dragging a tab, but then - // return to avoid drawing the drop indicator - var pixelsToScroll = 0; - if (this.getAttribute("overflow") == "true") { - var targetAnonid = event.originalTarget.getAttribute("anonid"); - switch (targetAnonid) { - case "scrollbutton-up": - pixelsToScroll = tabStrip.scrollIncrement * -1; - break; - case "scrollbutton-down": - pixelsToScroll = tabStrip.scrollIncrement; - break; - } - if (pixelsToScroll) - tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll); - } - - if (effects == "move" && - this == event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0).parentNode) { - ind.collapsed = true; - this._animateTabMove(event); - return; - } - - this._finishAnimateTabMove(); - - if (effects == "link") { - let tab = this._getDragTargetTab(event); - if (tab) { - if (!this._dragTime) - this._dragTime = Date.now(); - if (Date.now() >= this._dragTime + this._dragOverDelay) - this.selectedItem = tab; - ind.collapsed = true; - return; - } - } - - var rect = tabStrip.getBoundingClientRect(); - var newMargin; - if (pixelsToScroll) { - // if we are scrolling, put the drop indicator at the edge - // so that it doesn't jump while scrolling - let scrollRect = tabStrip.scrollClientRect; - let minMargin = scrollRect.left - rect.left; - let maxMargin = Math.min(minMargin + scrollRect.width, - scrollRect.right); - if (!ltr) - [minMargin, maxMargin] = [this.clientWidth - maxMargin, - this.clientWidth - minMargin]; - newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin; - } - else { - let newIndex = this._getDropIndex(event); - if (newIndex == this.childNodes.length) { - let tabRect = this.childNodes[newIndex-1].getBoundingClientRect(); - if (ltr) - newMargin = tabRect.right - rect.left; - else - newMargin = rect.right - tabRect.left; - } - else { - let tabRect = this.childNodes[newIndex].getBoundingClientRect(); - if (ltr) - newMargin = tabRect.left - rect.left; - else - newMargin = rect.right - tabRect.right; - } - } - - ind.collapsed = false; - - newMargin += ind.clientWidth / 2; - if (!ltr) - newMargin *= -1; - - ind.style.transform = "translate(" + Math.round(newMargin) + "px)"; - ind.style.MozMarginStart = (-ind.clientWidth) + "px"; - ]]></handler> - - <handler event="drop"><![CDATA[ - var dt = event.dataTransfer; - var dropEffect = dt.dropEffect; - var draggedTab; - if (dropEffect != "link") { // copy or move - draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); - // not our drop then - if (!draggedTab) - return; - } - - this._tabDropIndicator.collapsed = true; - event.stopPropagation(); - if (draggedTab && dropEffect == "copy") { - // copy the dropped tab (wherever it's from) - let newIndex = this._getDropIndex(event); - let newTab = this.tabbrowser.duplicateTab(draggedTab); - this.tabbrowser.moveTabTo(newTab, newIndex); - if (draggedTab.parentNode != this || event.shiftKey) - this.selectedItem = newTab; - } else if (draggedTab && draggedTab.parentNode == this) { - this._finishAnimateTabMove(); - - // actually move the dragged tab - if ("animDropIndex" in draggedTab._dragData) { - let newIndex = draggedTab._dragData.animDropIndex; - if (newIndex > draggedTab._tPos) - newIndex--; - this.tabbrowser.moveTabTo(draggedTab, newIndex); - } - } else if (draggedTab) { - // swap the dropped tab with a new one we create and then close - // it in the other window (making it seem to have moved between - // windows) - let newIndex = this._getDropIndex(event); - let newTab = this.tabbrowser.addTab("about:blank"); - let newBrowser = this.tabbrowser.getBrowserForTab(newTab); - // Stop the about:blank load - newBrowser.stop(); - // make sure it has a docshell - newBrowser.docShell; - - let numPinned = this.tabbrowser._numPinnedTabs; - if (newIndex < numPinned || draggedTab.pinned && newIndex == numPinned) - this.tabbrowser.pinTab(newTab); - this.tabbrowser.moveTabTo(newTab, newIndex); - - // We need to select the tab before calling swapBrowsersAndCloseOther - // so that window.content in chrome windows points to the right tab - // when pagehide/show events are fired. - this.tabbrowser.selectedTab = newTab; - - draggedTab.parentNode._finishAnimateTabMove(); - this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab); - - // Call updateCurrentBrowser to make sure the URL bar is up to date - // for our new tab after we've done swapBrowsersAndCloseOther. - this.tabbrowser.updateCurrentBrowser(true); - } else { - // Pass true to disallow dropping javascript: or data: urls - let links; - try { - links = browserDragAndDrop.dropLinks(event, true); - } catch (ex) {} - -// // valid urls don't contain spaces ' '; if we have a space it isn't a valid url. -// if (!url || url.includes(" ")) //PMed - if (!links || links.length === 0) //FF - return; - - let inBackground = Services.prefs.getBoolPref("browser.tabs.loadInBackground"); - - if (event.shiftKey) - inBackground = !inBackground; - - let targetTab = this._getDragTargetTab(event); - let replace = !(!targetTab || dropEffect == "copy"); - let newIndex = this._getDropIndex(event); - let urls = links.map(link => link.url); - this.tabbrowser.loadTabs(urls, { - inBackground, - replace, - allowThirdPartyFixup: true, - targetTab, - newIndex, - }); - } - - if (draggedTab) { - delete draggedTab._dragData; - } - ]]></handler> - - <handler event="dragend"><![CDATA[ - // Note: while this case is correctly handled here, this event - // isn't dispatched when the tab is moved within the tabstrip, - // see bug 460801. - - this._finishAnimateTabMove(); - - var dt = event.dataTransfer; - var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); - if (dt.mozUserCancelled || dt.dropEffect != "none") { - delete draggedTab._dragData; - return; - } - - // Disable detach within the browser toolbox - var eX = event.screenX; - var eY = event.screenY; - var wX = window.screenX; - // check if the drop point is horizontally within the window - if (eX > wX && eX < (wX + window.outerWidth)) { - let bo = this.mTabstrip.boxObject; - // also avoid detaching if the the tab was dropped too close to - // the tabbar (half a tab) - let endScreenY = bo.screenY + 1.5 * bo.height; - if (eY < endScreenY && eY > window.screenY) - return; - } - - // screen.availLeft et. al. only check the screen that this window is on, - // but we want to look at the screen the tab is being dropped onto. - var sX = {}, sY = {}, sWidth = {}, sHeight = {}; - Cc["@mozilla.org/gfx/screenmanager;1"] - .getService(Ci.nsIScreenManager) - .screenForRect(eX, eY, 1, 1) - .GetAvailRect(sX, sY, sWidth, sHeight); - // ensure new window entirely within screen - var winWidth = Math.min(window.outerWidth, sWidth.value); - var winHeight = Math.min(window.outerHeight, sHeight.value); - var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value), - sX.value + sWidth.value - winWidth); - var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value), - sY.value + sHeight.value - winHeight); - - delete draggedTab._dragData; - - if (this.tabbrowser.tabs.length == 1) { - // resize _before_ move to ensure the window fits the new screen. if - // the window is too large for its screen, the window manager may do - // automatic repositioning. - window.resizeTo(winWidth, winHeight); - window.moveTo(left, top); - window.focus(); - } else { - this.tabbrowser.replaceTabWithWindow(draggedTab, { screenX: left, - screenY: top, -#ifndef XP_WIN - outerWidth: winWidth, - outerHeight: winHeight -#endif - }); - } - event.stopPropagation(); - ]]></handler> - - <handler event="dragexit"><![CDATA[ - this._dragTime = 0; - - // This does not work at all (see bug 458613) - var target = event.relatedTarget; - while (target && target != this) - target = target.parentNode; - if (target) - return; - - this._tabDropIndicator.collapsed = true; - event.stopPropagation(); - ]]></handler> - </handlers> - </binding> - - <!-- close-tab-button binding - This binding relies on the structure of the tabbrowser binding. - Therefore it should only be used as a child of the tab or the tabs - element (in both cases, when they are anonymous nodes of <tabbrowser>). - --> - <binding id="tabbrowser-close-tab-button" - extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image"> - <handlers> - <handler event="dragstart"> - event.stopPropagation(); - </handler> - </handlers> - </binding> - - <binding id="tabbrowser-tab" display="xul:hbox" - extends="chrome://global/content/bindings/tabbox.xml#tab"> - <resources> - <stylesheet src="chrome://browser/content/tabbrowser.css"/> - </resources> - - <content context="tabContextMenu" closetabtext="&closeTab.label;"> - <xul:stack class="tab-stack" flex="1"> - <xul:hbox xbl:inherits="pinned,selected,titlechanged" - class="tab-background"> - <xul:hbox xbl:inherits="pinned,selected,titlechanged" - class="tab-background-start"/> - <xul:hbox xbl:inherits="pinned,selected,titlechanged" - class="tab-background-middle"/> - <xul:hbox xbl:inherits="pinned,selected,titlechanged" - class="tab-background-end"/> - </xul:hbox> - <xul:hbox xbl:inherits="pinned,selected,titlechanged" - class="tab-content" align="center"> - <xul:image xbl:inherits="fadein,pinned,busy,progress,selected" - class="tab-throbber" - role="presentation" - layer="true" /> - <xul:image xbl:inherits="validate,src=image,fadein,pinned,selected" - class="tab-icon-image" - role="presentation" - anonid="tab-icon"/> - <xul:image xbl:inherits="busy,soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected" - anonid="overlay-icon" - class="tab-icon-overlay" - role="presentation"/> - <xul:label flex="1" - xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected" - class="tab-text tab-label" - role="presentation"/> - <xul:image xbl:inherits="soundplaying,soundplaying-scheduledremoval,pinned,muted,blocked,selected" - anonid="soundplaying-icon" - class="tab-icon-sound" - role="presentation"/> - <xul:toolbarbutton anonid="close-button" - xbl:inherits="fadein,pinned,selected" - class="tab-close-button close-icon"/> - </xul:hbox> - </xul:stack> - </content> - - <implementation> - <property name="pinned" readonly="true"> - <getter> - return this.getAttribute("pinned") == "true"; - </getter> - </property> - <property name="hidden" readonly="true"> - <getter> - return this.getAttribute("hidden") == "true"; - </getter> - </property> - - <field name="mOverCloseButton">false</field> - <property name="_overPlayingIcon" readonly="true"> - <getter><![CDATA[ - let iconVisible = this.hasAttribute("soundplaying") || - this.hasAttribute("muted") || - this.hasAttribute("blocked"); - let soundPlayingIcon = - document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon"); - let overlayIcon = - document.getAnonymousElementByAttribute(this, "anonid", "overlay-icon"); - - return soundPlayingIcon && soundPlayingIcon.matches(":hover") || - (overlayIcon && overlayIcon.matches(":hover") && iconVisible); - ]]></getter> - </property> - <field name="mCorrespondingMenuitem">null</field> - <field name="closing">false</field> - <field name="lastAccessed">0</field> - - <method name="toggleMuteAudio"> - <parameter name="aMuteReason"/> - <body> - <![CDATA[ - let tabContainer = this.parentNode; - let browser = this.linkedBrowser; - let modifiedAttrs = []; - if (browser.audioBlocked) { - this.removeAttribute("blocked"); - modifiedAttrs.push("blocked"); - - // We don't want sound icon flickering between "blocked", "none" and - // "sound-playing", here adding the "soundplaying" is to keep the - // transition smoothly. - if (!this.hasAttribute("soundplaying")) { - this.setAttribute("soundplaying", true); - modifiedAttrs.push("soundplaying"); - } - - browser.resumeMedia(); - } else { - if (browser.audioMuted) { - browser.unmute(); - this.removeAttribute("muted"); - } else { - browser.mute(); - this.setAttribute("muted", "true"); - } - this.muteReason = aMuteReason || null; - modifiedAttrs.push("muted"); - } - tabContainer.tabbrowser._tabAttrModified(this, modifiedAttrs); - ]]> - </body> - </method> - </implementation> - - <handlers> - <handler event="mouseover"><![CDATA[ - let anonid = event.originalTarget.getAttribute("anonid"); - if (anonid == "close-button") - this.mOverCloseButton = true; - - let tab = event.target; - if (tab.closing) - return; - - let tabContainer = this.parentNode; - let visibleTabs = tabContainer.tabbrowser.visibleTabs; - let tabIndex = visibleTabs.indexOf(tab); - if (tabIndex == 0) { - tabContainer._beforeHoveredTab = null; - } else { - let candidate = visibleTabs[tabIndex - 1]; - if (!candidate.selected) { - tabContainer._beforeHoveredTab = candidate; - candidate.setAttribute("beforehovered", "true"); - } - } - - if (tabIndex == visibleTabs.length - 1) { - tabContainer._afterHoveredTab = null; - } else { - let candidate = visibleTabs[tabIndex + 1]; - if (!candidate.selected) { - tabContainer._afterHoveredTab = candidate; - candidate.setAttribute("afterhovered", "true"); - } - } - ]]></handler> - <handler event="mouseout"><![CDATA[ - let anonid = event.originalTarget.getAttribute("anonid"); - if (anonid == "close-button") - this.mOverCloseButton = false; - - let tabContainer = this.parentNode; - if (tabContainer._beforeHoveredTab) { - tabContainer._beforeHoveredTab.removeAttribute("beforehovered"); - tabContainer._beforeHoveredTab = null; - } - if (tabContainer._afterHoveredTab) { - tabContainer._afterHoveredTab.removeAttribute("afterhovered"); - tabContainer._afterHoveredTab = null; - } - ]]></handler> - <handler event="dragstart" phase="capturing"> - this.style.MozUserFocus = ''; - </handler> - <handler event="mousedown" phase="capturing"> - <![CDATA[ - if (this.selected) { - this.style.MozUserFocus = 'ignore'; - this.clientTop; // just using this to flush style updates - } else if (this.mOverCloseButton || - this._overPlayingIcon) { - // Prevent tabbox.xml from selecting the tab. - event.stopPropagation(); - } - ]]> - </handler> - <handler event="mouseup"> - this.style.MozUserFocus = ''; - </handler> - <handler event="click"> - <![CDATA[ - if (event.button != 0) { - return; - } - - if (this._overPlayingIcon) { - this.toggleMuteAudio(); - } - ]]> - </handler> - </handlers> - </binding> - - <binding id="tabbrowser-alltabs-popup" - extends="chrome://global/content/bindings/popup.xml#popup"> - <implementation implements="nsIDOMEventListener"> - <method name="_tabOnAttrModified"> - <parameter name="aEvent"/> - <body><![CDATA[ - var tab = aEvent.target; - if (tab.mCorrespondingMenuitem) - this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab); - ]]></body> - </method> - - <method name="_tabOnTabClose"> - <parameter name="aEvent"/> - <body><![CDATA[ - var tab = aEvent.target; - if (tab.mCorrespondingMenuitem) - this.removeChild(tab.mCorrespondingMenuitem); - ]]></body> - </method> - - <method name="handleEvent"> - <parameter name="aEvent"/> - <body><![CDATA[ - switch (aEvent.type) { - case "TabAttrModified": - this._tabOnAttrModified(aEvent); - break; - case "TabClose": - this._tabOnTabClose(aEvent); - break; - case "scroll": - this._updateTabsVisibilityStatus(); - break; - } - ]]></body> - </method> - - <method name="_updateTabsVisibilityStatus"> - <body><![CDATA[ - var tabContainer = gBrowser.tabContainer; - // We don't want menu item decoration unless there is overflow. - if (tabContainer.getAttribute("overflow") != "true") - return; - - var tabstripBO = tabContainer.mTabstrip.scrollBoxObject; - for (var i = 0; i < this.childNodes.length; i++) { - let curTab = this.childNodes[i].tab; - let curTabBO = curTab.boxObject; - if (curTabBO.screenX >= tabstripBO.screenX && - curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width) - this.childNodes[i].setAttribute("tabIsVisible", "true"); - else - this.childNodes[i].removeAttribute("tabIsVisible"); - } - ]]></body> - </method> - - <method name="_createTabMenuItem"> - <parameter name="aTab"/> - <body><![CDATA[ - var menuItem = document.createElementNS( - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - "menuitem"); - - menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon"); - - this._setMenuitemAttributes(menuItem, aTab); - - if (!aTab.mCorrespondingMenuitem) { - aTab.mCorrespondingMenuitem = menuItem; - menuItem.tab = aTab; - - this.appendChild(menuItem); - } - ]]></body> - </method> - - <method name="_setMenuitemAttributes"> - <parameter name="aMenuitem"/> - <parameter name="aTab"/> - <body><![CDATA[ - aMenuitem.setAttribute("label", aTab.label); - aMenuitem.setAttribute("crop", aTab.getAttribute("crop")); - - if (aTab.hasAttribute("busy")) { - aMenuitem.setAttribute("busy", aTab.getAttribute("busy")); - aMenuitem.removeAttribute("image"); - } else { - aMenuitem.setAttribute("image", aTab.getAttribute("image")); - aMenuitem.removeAttribute("busy"); - } - - if (aTab.hasAttribute("pending")) - aMenuitem.setAttribute("pending", aTab.getAttribute("pending")); - else - aMenuitem.removeAttribute("pending"); - - if (aTab.selected) - aMenuitem.setAttribute("selected", "true"); - else - aMenuitem.removeAttribute("selected"); - - function addEndImage() { - let endImage = document.createElement("image"); - endImage.setAttribute("class", "allTabs-endimage"); - let endImageContainer = document.createElement("hbox"); - endImageContainer.setAttribute("align", "center"); - endImageContainer.setAttribute("pack", "center"); - endImageContainer.appendChild(endImage); - aMenuitem.appendChild(endImageContainer); - return endImage; - } - - if (aMenuitem.firstChild) - aMenuitem.firstChild.remove(); - if (aTab.hasAttribute("muted")) - addEndImage().setAttribute("muted", "true"); - else if (aTab.hasAttribute("soundplaying")) - addEndImage().setAttribute("soundplaying", "true"); - ]]></body> - </method> - </implementation> - - <handlers> - <handler event="popupshowing"> - <![CDATA[ - var tabcontainer = gBrowser.tabContainer; - - // Listen for changes in the tab bar. - tabcontainer.addEventListener("TabAttrModified", this, false); - tabcontainer.addEventListener("TabClose", this, false); - tabcontainer.mTabstrip.addEventListener("scroll", this, false); - - let tabs = gBrowser.visibleTabs; - for (var i = 0; i < tabs.length; i++) { - if (!tabs[i].pinned) - this._createTabMenuItem(tabs[i]); - } - this._updateTabsVisibilityStatus(); - ]]></handler> - - <handler event="popuphidden"> - <![CDATA[ - // clear out the menu popup and remove the listeners - for (let i = this.childNodes.length - 1; i >= 0; i--) { - let menuItem = this.childNodes[i]; - if (menuItem.tab) { - menuItem.tab.mCorrespondingMenuitem = null; - this.removeChild(menuItem); - } - } - var tabcontainer = gBrowser.tabContainer; - tabcontainer.mTabstrip.removeEventListener("scroll", this, false); - tabcontainer.removeEventListener("TabAttrModified", this, false); - tabcontainer.removeEventListener("TabClose", this, false); - ]]></handler> - - <handler event="DOMMenuItemActive"> - <![CDATA[ - var tab = event.target.tab; - if (tab) { - let overLink = tab.linkedBrowser.currentURI.spec; - if (overLink == "about:blank") - overLink = ""; - XULBrowserWindow.setOverLink(overLink, null); - } - ]]></handler> - - <handler event="DOMMenuItemInactive"> - <![CDATA[ - XULBrowserWindow.setOverLink("", null); - ]]></handler> - - <handler event="command"><![CDATA[ - if (event.target.tab) - gBrowser.selectedTab = event.target.tab; - ]]></handler> - - </handlers> - </binding> - - <binding id="statuspanel" display="xul:hbox"> - <content> - <xul:hbox class="statuspanel-inner"> - <xul:label class="statuspanel-label" - role="status" - aria-live="off" - xbl:inherits="value=label,crop,mirror" - flex="1" - crop="end"/> - </xul:hbox> - </content> - - <implementation implements="nsIDOMEventListener"> - <constructor><![CDATA[ - window.addEventListener("resize", this, false); - ]]></constructor> - - <destructor><![CDATA[ - window.removeEventListener("resize", this, false); - MousePosTracker.removeListener(this); - ]]></destructor> - - <property name="label"> - <setter><![CDATA[ - if (!this.label) { - this.removeAttribute("mirror"); - this.removeAttribute("sizelimit"); - } - - this.style.minWidth = this.getAttribute("type") == "status" && - this.getAttribute("previoustype") == "status" - ? getComputedStyle(this).width : ""; - - if (val) { - this.setAttribute("label", val); - this.removeAttribute("inactive"); - this._calcMouseTargetRect(); - MousePosTracker.addListener(this); - } else { - this.setAttribute("inactive", "true"); - MousePosTracker.removeListener(this); - } - - return val; - ]]></setter> - <getter> - return this.hasAttribute("inactive") ? "" : this.getAttribute("label"); - </getter> - </property> - - <method name="getMouseTargetRect"> - <body><![CDATA[ - return this._mouseTargetRect; - ]]></body> - </method> - - <method name="onMouseEnter"> - <body> - this._mirror(); - </body> - </method> - - <method name="onMouseLeave"> - <body> - this._mirror(); - </body> - </method> - - <method name="handleEvent"> - <parameter name="event"/> - <body><![CDATA[ - if (!this.label) - return; - - switch (event.type) { - case "resize": - this._calcMouseTargetRect(); - break; - } - ]]></body> - </method> - - <method name="_calcMouseTargetRect"> - <body><![CDATA[ - let alignRight = false; - - if (getComputedStyle(document.documentElement).direction == "rtl") - alignRight = !alignRight; - - let rect = this.getBoundingClientRect(); - this._mouseTargetRect = { - top: rect.top, - bottom: rect.bottom, - left: alignRight ? window.innerWidth - rect.width : 0, - right: alignRight ? window.innerWidth : rect.width - }; - ]]></body> - </method> - - <method name="_mirror"> - <body> - if (this.hasAttribute("mirror")) - this.removeAttribute("mirror"); - else - this.setAttribute("mirror", "true"); - - if (!this.hasAttribute("sizelimit")) { - this.setAttribute("sizelimit", "true"); - this._calcMouseTargetRect(); - } - </body> - </method> - </implementation> - </binding> - -</bindings> |