summaryrefslogtreecommitdiffstats
path: root/base/content/tabbrowser.xml
diff options
context:
space:
mode:
authorThomas Groman <tgroman@nuegia.net>2020-04-20 20:49:37 -0700
committerThomas Groman <tgroman@nuegia.net>2020-04-20 20:49:37 -0700
commitf9cab004186edb425a9b88ad649726605080a17c (patch)
treee2dae51d3144e83d097a12e7a1499e3ea93f90be /base/content/tabbrowser.xml
parentf428692de8b59ab89a66502c079e1823dfda8aeb (diff)
downloadwebbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.gz
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.lz
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.xz
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.zip
move browser to webbrowser/
Diffstat (limited to 'base/content/tabbrowser.xml')
-rw-r--r--base/content/tabbrowser.xml5403
1 files changed, 0 insertions, 5403 deletions
diff --git a/base/content/tabbrowser.xml b/base/content/tabbrowser.xml
deleted file mode 100644
index b5edd54..0000000
--- a/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>