home *** CD-ROM | disk | FTP | other *** search
- <?xml version="1.0"?>
-
- <!-- ***** BEGIN LICENSE BLOCK *****
- - Version: MPL 1.1/GPL 2.0/LGPL 2.1
- -
- - The contents of this file are subject to the Mozilla Public License Version
- - 1.1 (the "License"); you may not use this file except in compliance with
- - the License. You may obtain a copy of the License at
- - http://www.mozilla.org/MPL/
- -
- - Software distributed under the License is distributed on an "AS IS" basis,
- - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- - for the specific language governing rights and limitations under the
- - License.
- -
- - The Original Code is this file as it was released on March 28, 2001.
- -
- - The Initial Developer of the Original Code is
- - David Hyatt.
- - Portions created by the Initial Developer are Copyright (C) 2001
- - the Initial Developer. All Rights Reserved.
- -
- - Contributor(s):
- - David Hyatt <hyatt@netscape.com> (Original Author of <tabbrowser>)
- - Mike Connor <mconnor@steelgryphon.com>
- - Peter Parente <parente@cs.unc.edu>
- - Giorgio Maone <g.maone@informaction.com>
- - Asaf Romano <mozilla.mano@sent.com>
- - Seth Spitzer <sspitzer@mozilla.org>
- - Simon Bünzli <zeniko@gmail.com>
- - Michael Ventnor <ventnor.bugzilla@yahoo.com.au>
- - Mark Pilgrim <pilgrim@gmail.com>
- -
- - Alternatively, the contents of this file may be used under the terms of
- - either the GNU General Public License Version 2 or later (the "GPL"), or
- - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- - in which case the provisions of the GPL or the LGPL are applicable instead
- - of those above. If you wish to allow use of your version of this file only
- - under the terms of either the GPL or the LGPL, and not to allow others to
- - use your version of this file under the terms of the MPL, indicate your
- - decision by deleting the provisions above and replace them with the notice
- - and other provisions required by the GPL or the LGPL. If you do not delete
- - the provisions above, a recipient may use your version of this file under
- - the terms of any one of the MPL, the GPL or the LGPL.
- -
- - ***** END LICENSE BLOCK ***** -->
-
- <!DOCTYPE bindings [
- <!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" >
- %tabBrowserDTD;
- <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
- %globalDTD;
- ]>
-
- <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" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
- onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
- <xul:hbox class="tab-drop-indicator-bar" collapsed="true" chromedir="&locale.dir;">
- <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
- </xul:hbox>
- <xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
- anonid="strip"
- ondraggesture="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
- ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
- ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
- ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
- <xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/>
- <xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
- <xul:menuitem id="context_newTab" label="&newTab.label;" accesskey="&newTab.accesskey;"
- xbl:inherits="oncommand=onnewtab"/>
- <xul:menuseparator/>
- <xul:menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
- oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
- tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
- <xul:menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
- tbattr="tabbrowser-multiple"
- oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
- tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
- <xul:menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
- tbattr="tabbrowser-multiple"
- oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
- tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
- <xul:menuseparator/>
- <xul:menuitem id="context_bookmarkTab"
- label="&bookmarkThisTab.label;"
- accesskey="&bookmarkThisTab.accesskey;"
- oncommand="BookmarkThisTab();"/>
- <xul:menuitem id="context_bookmarkAllTabs"
- label="&bookmarkAllTabs.label;"
- accesskey="&bookmarkAllTabs.accesskey;"
- command="Browser:BookmarkAllTabs"/>
- <xul:menuitem id="context_undoCloseTab"
- label="&undoCloseTab.label;"
- accesskey="&undoCloseTab.accesskey;"
- command="History:UndoCloseTab"
- anonid="undoCloseTabMenuItem"/>
- <xul:menuseparator/>
- <xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
- oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
- tabbrowser.removeTab(tabbrowser.mContextTab);"/>
- </xul:menupopup>
-
- <xul:tabs class="tabbrowser-tabs" flex="1"
- anonid="tabcontainer"
- setfocus="false"
- onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
- xbl:inherits="onnewtab"
- ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
- onclosetab="var node = this.parentNode;
- while (node.localName != 'tabbrowser')
- node = node.parentNode;
- node.removeCurrentTab();">
- <xul:tab selected="true" validate="never"
- onerror="this.removeAttribute('image');"
- maxwidth="250" width="0" minwidth="100" flex="100"
- class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
- </xul:tabs>
- </xul:hbox>
- <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
- <xul:notificationbox flex="1">
- <xul:browser flex="1" type="content-primary" message="true" disablehistory="true"
- xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
- </xul:notificationbox>
- </xul:tabpanels>
- </xul:tabbox>
- <children/>
- </content>
- <implementation>
- <field name="mPrefs" readonly="true">
- Components.classes['@mozilla.org/preferences-service;1']
- .getService(Components.interfaces.nsIPrefService)
- .getBranch(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="mTabBox" readonly="true">
- document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
- </field>
- <field name="mTabDropIndicatorBar">
- this.mTabBox.childNodes[0]
- </field>
- <field name="mStrip" readonly="true">
- document.getAnonymousElementByAttribute(this, "anonid", "strip");
- </field>
- <field name="mTabContainer" readonly="true">
- document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
- </field>
- <field name="mPanelContainer" readonly="true">
- document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
- </field>
- <field name="mTabs" readonly="true">
- this.mTabContainer.childNodes
- </field>
- <field name="mStringBundle">
- document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
- </field>
- <field name="mUndoCloseTabMenuItem">
- document.getAnonymousElementByAttribute(this, "anonid", "undoCloseTabMenuItem");
- </field>
- <field name="mCurrentTab">
- null
- </field>
- <field name="mCurrentBrowser">
- null
- </field>
- <field name="mProgressListeners">
- []
- </field>
- <field name="mTabListeners">
- new Array()
- </field>
- <field name="mTabFilters">
- new Array()
- </field>
- <field name="mTabbedMode">
- false
- </field>
- <field name="mIsBusy">
- false
- </field>
- <field name="mContextTab">
- null
- </field>
- <field name="arrowKeysShouldWrap" readonly="true">
- false
- </field>
- <field name="mAddProgressListenerWasCalled">
- false
- </field>
- <field name="_browsers">
- null
- </field>
-
- <field name="_blockDblClick">
- false
- </field>
- <field name="_autoScrollPopup">
- null
- </field>
-
- <method name="getBrowserAtIndex">
- <parameter name="aIndex"/>
- <body>
- <![CDATA[
- return this.browsers[aIndex];
- ]]>
- </body>
- </method>
-
- <method name="getBrowserIndexForDocument">
- <parameter name="aDocument"/>
- <body>
- <![CDATA[
- var browsers = this.browsers;
- for (var i = 0; i < browsers.length; i++)
- if (browsers[i].contentDocument == aDocument)
- return i;
- return -1;
- ]]>
- </body>
- </method>
-
- <method name="getBrowserForDocument">
- <parameter name="aDocument"/>
- <body>
- <![CDATA[
- var index = this.getBrowserIndexForDocument(aDocument);
- if (index < 0)
- return null;
- return this.getBrowserAtIndex(index);
- ]]>
- </body>
- </method>
-
- <method name="getNotificationBox">
- <parameter name="aBrowser"/>
- <body>
- <![CDATA[
- if (aBrowser)
- return aBrowser.parentNode;
- else if (this.mCurrentBrowser)
- return this.mCurrentBrowser.parentNode;
- return null;
- ]]>
- </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,
- mLastURI: null,
-
- // cache flags for correct status bar update after tab switching
- mStateFlags: 0,
- mStatus: 0,
- mMessage: "",
- mTotalProgress: 0,
-
- // count of open requests (should always be 0 or 1)
- mRequestCount: 0,
-
- onProgressChange : function (aWebProgress, aRequest,
- aCurSelfProgress, aMaxSelfProgress,
- aCurTotalProgress, aMaxTotalProgress)
- {
- if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
- for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
- var p = this.mTabBrowser.mProgressListeners[i];
- if (p)
- try {
- p.onProgressChange(aWebProgress, aRequest,
- aCurSelfProgress, aMaxSelfProgress,
- aCurTotalProgress, aMaxTotalProgress);
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
- }
-
- this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
- },
-
- 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;
-
- 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) {
- // It's okay to clear what the user typed when we start
- // loading a document. If the user types, this counter gets
- // set to zero, if the document load ends without an
- // onLocationChange, this counter gets decremented
- // (so we keep it while switching tabs after failed loads)
- // We need to add 2 because loadURIWithFlags may have
- // cancelled a pending load which would have cleared
- // its anchor scroll detection temporary increment.
- if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
- this.mBrowser.userTypedClear += 2;
-
- if (!this.mBlank) {
- this.mTab.setAttribute("busy", "true");
- this.mTabBrowser.updateIcon(this.mTab);
- this.mTabBrowser.setTabTitleLoading(this.mTab);
-
- if (this.mTabBrowser.mCurrentTab == this.mTab)
- this.mTabBrowser.mIsBusy = true;
- }
- }
- else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
- aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
- if (aWebProgress.DOMWindow == this.mBrowser.contentWindow) {
- // The document is done loading, we no longer want the
- // value cleared.
- if (this.mBrowser.userTypedClear > 1)
- this.mBrowser.userTypedClear -= 2;
- else if (this.mBrowser.userTypedClear > 0)
- this.mBrowser.userTypedClear--;
-
- if (!this.mBrowser.mIconURL)
- this.mTabBrowser.useDefaultIcon(this.mTab);
- }
-
- if (this.mBlank)
- this.mBlank = false;
-
- this.mTab.removeAttribute("busy");
- this.mTabBrowser.updateIcon(this.mTab);
-
- var location = aRequest.QueryInterface(nsIChannel).URI;
-
- // 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.loading"))
- this.mTabBrowser.setTabTitle(this.mTab);
-
- if (this.mTabBrowser.mCurrentTab == this.mTab)
- this.mTabBrowser.mIsBusy = false;
- }
-
- if (this.mTabBrowser.mCurrentTab == this.mTab) {
- for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
- var p = this.mTabBrowser.mProgressListeners[i];
- if (p)
- try {
- if (!oldBlank)
- p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
- // make sure that the visible status of new blank tabs is correctly set
- else if ("onUpdateCurrentBrowser" in p)
- p.onUpdateCurrentBrowser(aStateFlags, aStatus, "", 0);
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
- }
-
- 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)
- {
- // The document loaded correctly, clear the value if we should
- if (this.mBrowser.userTypedClear > 0)
- this.mBrowser.userTypedValue = null;
-
- if (aWebProgress.DOMWindow == this.mBrowser.contentWindow &&
- aWebProgress.isLoadingDocument)
- this.mTabBrowser.setIcon(this.mTab, null);
-
- // changing location, clear out the missing plugins list
- this.mBrowser.missingPlugins = null;
-
- if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
- for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
- var p = this.mTabBrowser.mProgressListeners[i];
- if (p)
- try {
- p.onLocationChange(aWebProgress, aRequest, aLocation);
- } catch (e) {
- // don't inhibit other listeners
- Components.utils.reportError(e);
- }
- }
- }
- },
-
- onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
- {
- if (this.mBlank)
- return;
-
- if (this.mTabBrowser.mCurrentTab == this.mTab) {
- for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
- var p = this.mTabBrowser.mProgressListeners[i];
- if (p)
- try {
- p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
- }
-
- this.mMessage = aMessage;
- },
-
- onSecurityChange : function(aWebProgress, aRequest, aState)
- {
- if (this.mTabBrowser.mCurrentTab == this.mTab) {
- for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
- var p = this.mTabBrowser.mProgressListeners[i];
- if (p)
- try {
- p.onSecurityChange(aWebProgress, aRequest, aState);
- } catch (e) {
- // don't inhibit other listeners
- Components.utils.reportError(e);
- }
- }
- }
- },
-
- onRefreshAttempted : function(aWebProgress, aURI, aDelay, aSameURI)
- {
- var allowRefresh = true;
- for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
- var p = this.mTabBrowser.mProgressListeners[i];
- if (p && "onRefreshAttempted" in p) {
- try {
- if (!p.onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI))
- allowRefresh = false;
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
- }
- return allowRefresh;
- },
-
- 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"/>
- <body>
- <![CDATA[
- var browser = this.getBrowserForTab(aTab);
- browser.mIconURL = aURI;
-
- if (aURI) {
- if (!(aURI instanceof Components.interfaces.nsIURI)) {
- var ios = Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService);
- aURI = ios.newURI(aURI, null, null);
- }
- this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
- aURI, false);
- }
-
- this.updateIcon(aTab);
-
- for (var i = 0; i < this.mProgressListeners.length; i++) {
- var p = this.mProgressListeners[i];
- if ('onLinkIconAvailable' in p)
- try {
- p.onLinkIconAvailable(browser);
- } catch (e) {
- // don't inhibit other listeners
- Components.utils.reportError(e);
- }
- }
- ]]>
- </body>
- </method>
-
- <method name="updateIcon">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- var browser = this.getBrowserForTab(aTab);
- if (!aTab.hasAttribute("busy") && browser.mIconURL)
- aTab.setAttribute("image", browser.mIconURL);
- else
- aTab.removeAttribute("image");
- ]]>
- </body>
- </method>
-
- <method name="shouldLoadFavIcon">
- <parameter name="aURI"/>
- <body>
- <![CDATA[
- return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
- this.mPrefs.getBoolPref("browser.chrome.favicons") &&
- ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
- ]]>
- </body>
- </method>
-
- <method name="useDefaultIcon">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- var browser = this.getBrowserForTab(aTab);
- if (browser.contentDocument instanceof ImageDocument) {
- if (this.mPrefs.getBoolPref("browser.chrome.site_icons")) {
- try {
- var sz = this.mPrefs.getIntPref("browser.chrome.image_icons.max_size");
- if (!sz)
- return;
-
- var req = browser.contentDocument.imageRequest;
- if (!req || !req.image ||
- req.image.width > sz ||
- req.image.height > sz)
- return;
-
- this.setIcon(aTab, browser.currentURI.spec);
- } catch (e) { }
- }
- }
- else if (this.shouldLoadFavIcon(browser.currentURI)) {
- var url = browser.currentURI.prePath + "/favicon.ico";
- if (!this.isFailedIcon(url))
- this.setIcon(aTab, url);
- }
- ]]>
- </body>
- </method>
-
- <method name="isFailedIcon">
- <parameter name="aURI"/>
- <body>
- <![CDATA[
- if (!(aURI instanceof Components.interfaces.nsIURI)) {
- var ios = Components.classes["@mozilla.org/network/io-service;1"]
- .getService(Components.interfaces.nsIIOService);
- aURI = ios.newURI(aURI, null, null);
- }
-
- return this.mFaviconService.isFailedFavicon(aURI);
- ]]>
- </body>
- </method>
-
- <method name="updateTitlebar">
- <body>
- <![CDATA[
- var newTitle = "";
- var docTitle;
- var docElement = this.ownerDocument.documentElement;
- var sep = docElement.getAttribute("titlemenuseparator");
-
- if (this.docShell.contentViewer)
- docTitle = this.contentTitle;
-
- 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").indexOf("location") != -1) {
- var uri = this.mURIFixup.createExposableURI(
- this.mCurrentBrowser.currentURI);
- if (uri.scheme == "about")
- newTitle = uri.spec + sep + newTitle;
- else
- newTitle = uri.prePath + sep + newTitle;
- }
- } catch (e) {}
-
- this.ownerDocument.title = newTitle;
- ]]>
- </body>
- </method>
-
- <method name="updatePopupMenu">
- <parameter name="aPopupMenu"/>
- <body>
- <![CDATA[
- this.mContextTab = document.popupNode;
- var disabled = this.mPanelContainer.childNodes.length == 1;
- var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
- for (var i = 0; i < menuItems.length; i++)
- menuItems[i].disabled = disabled;
-
- // Session store
- if (gPrefService.getBoolPref("browser.sessionstore.enabled")) {
- this.mUndoCloseTabMenuItem.hidden =
- Cc["@mozilla.org/browser/sessionstore;1"].
- getService(Ci.nsISessionStore).
- getClosedTabCount(window) == 0;
- }
- else
- this.mUndoCloseTabMenuItem.hidden = true;
- ]]>
- </body>
- </method>
-
- <method name="updateCurrentBrowser">
- <body>
- <![CDATA[
- var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
- if (this.mCurrentBrowser == newBrowser)
- return;
-
- if (this.mCurrentBrowser) {
- // Only save the focused element if it is in our content window
- // or in an ancestor window.
- var focusedWindow = document.commandDispatcher.focusedWindow;
- var saveFocus = false;
-
- if (focusedWindow && focusedWindow.top == window.content) {
- saveFocus = true;
- } else {
- var contentWindow = window;
-
- while (contentWindow) {
- if (contentWindow == focusedWindow) {
- saveFocus = true;
- break;
- }
-
- if (contentWindow.parent == contentWindow) {
- break;
- }
-
- contentWindow = contentWindow.parent;
- }
- }
-
- if (saveFocus) {
- // Preserve the currently-focused element or DOM window for
- // this tab.
-
- this.mCurrentBrowser.focusedWindow = focusedWindow;
- this.mCurrentBrowser.focusedElement = document.commandDispatcher.focusedElement;
- }
-
- if (this.mCurrentBrowser.focusedElement &&
- this.mCurrentBrowser.focusedElement.parentNode !=
- this.mCurrentTab.parentNode) {
- // Clear focus outline before we draw on top of it.
- // Only blur the focused element if it isn't a tab,
- // to avoid breaking keyboard tab navigation
- var elem = this.mCurrentBrowser.focusedElement;
- if (elem instanceof HTMLElement || elem instanceof XULElement) {
- elem.blur();
- }
- else {
- var content = elem.ownerDocument.defaultView;
- if (content instanceof Components.interfaces.nsIInterfaceRequestor)
- content.getInterface(Components.interfaces.nsIDOMWindowUtils).focus(null);
- }
- }
- this.mCurrentBrowser.setAttribute("type", "content-targetable");
- }
-
- var updatePageReport = false;
- if (!this.mCurrentBrowser ||
- (this.mCurrentBrowser.pageReport && !newBrowser.pageReport) ||
- (!this.mCurrentBrowser.pageReport && newBrowser.pageReport))
- updatePageReport = true;
-
- newBrowser.setAttribute("type", "content-primary");
- this.mCurrentBrowser = newBrowser;
- this.mCurrentTab = this.selectedTab;
-
- if (updatePageReport)
- this.mCurrentBrowser.updatePageReport();
-
- // Update the URL bar.
- var loc = this.mCurrentBrowser.currentURI;
-
- var webProgress = this.mCurrentBrowser.webProgress;
- var securityUI = this.mCurrentBrowser.securityUI;
-
- var i, p;
- for (i = 0; i < this.mProgressListeners.length; i++) {
- p = this.mProgressListeners[i];
- if (p)
- try {
- p.onLocationChange(webProgress, null, loc);
- if (securityUI)
- p.onSecurityChange(webProgress, null, securityUI.state);
-
- // make sure that all status indicators are properly updated
- if ("onUpdateCurrentBrowser" in p) {
- var listener = this.mTabListeners[this.mTabContainer.selectedIndex] || null;
- if (listener && listener.mStateFlags)
- p.onUpdateCurrentBrowser(listener.mStateFlags, listener.mStatus,
- listener.mMessage, listener.mTotalProgress);
- }
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
-
- this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
-
- // Update the window title.
- this.updateTitlebar();
-
- // 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;
- webProgress = this.mCurrentBrowser.webProgress;
- for (i = 0; i < this.mProgressListeners.length; i++) {
- p = this.mProgressListeners[i];
- if (p)
- try {
- p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
- }
-
- // 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;
- webProgress = this.mCurrentBrowser.webProgress;
- for (i = 0; i < this.mProgressListeners.length; i++) {
- p = this.mProgressListeners[i];
- if (p)
- try {
- p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
- } catch (e) {
- // don't inhibit other listeners or following code
- Components.utils.reportError(e);
- }
- }
- }
-
- // We've selected the new tab, so go ahead and notify listeners.
- var event = document.createEvent("Events");
- event.initEvent("TabSelect", true, false);
- this.mCurrentTab.dispatchEvent(event);
-
- if (document.commandDispatcher.focusedElement &&
- document.commandDispatcher.focusedElement.parentNode ==
- this.mCurrentTab.parentNode) {
- // The focus is on a tab in the same tab panel
- return; // If focus was on a tab, switching tabs focuses the new tab
- }
-
- var whatToFocus = window.content;
-
- // Focus the previously focused element or window, but make sure
- // the focused element is still part of the document
- let focusedElem = newBrowser.focusedElement;
- if (focusedElem && focusedElem.ownerDocument &&
- !(focusedElem.ownerDocument.compareDocumentPosition(focusedElem) &
- Node.DOCUMENT_POSITION_DISCONNECTED)) {
- if (newBrowser.focusedElement.parentNode !=
- this.mCurrentTab.parentNode) {
- // Focus the remembered element unless it's in the current tab panel
- whatToFocus = newBrowser.focusedElement;
- }
- }
- else if (newBrowser.focusedWindow) {
- whatToFocus = newBrowser.focusedWindow;
- }
-
- // Change focus for this window to |whatToFocus|, without
- // focusing the window itself.
- var cmdDispatcher = document.commandDispatcher;
-
- var ww =
- Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
- .getService(Components.interfaces.nsIWindowWatcher);
- if (ww.activeWindow == window) {
- cmdDispatcher.suppressFocusScroll = true;
- if (whatToFocus instanceof HTMLElement ||
- whatToFocus instanceof XULElement ||
- whatToFocus instanceof Window) {
- whatToFocus.focus();
- }
- else if (whatToFocus instanceof Node) {
- var content = window.content;
- if (content instanceof Components.interfaces.nsIInterfaceRequestor)
- content.getInterface(Components.interfaces.nsIDOMWindowUtils).focus(whatToFocus);
- }
- cmdDispatcher.suppressFocusScroll = false;
- }
- else {
- // set the element in command dispatcher so focus will restore
- // properly when the window does become active
- if (whatToFocus instanceof Window) {
- cmdDispatcher.focusedWindow = whatToFocus;
- cmdDispatcher.focusedElement = null;
- }
- else {
- cmdDispatcher.focusedWindow = whatToFocus.ownerDocument.defaultView;
- cmdDispatcher.focusedElement = whatToFocus;
- }
- }
- ]]>
- </body>
- </method>
-
- <method name="onTabClick">
- <parameter name="event"/>
- <body>
- <![CDATA[
- if (event.button != 1 || event.target.localName != 'tab')
- return;
-
- this.removeTab(event.target);
- event.stopPropagation();
- ]]>
- </body>
- </method>
-
- <method name="onTitleChanged">
- <parameter name="evt"/>
- <body>
- <![CDATA[
- if (evt.target != this.contentDocument)
- return;
-
- var i = 0;
- for ( ; i < this.parentNode.parentNode.childNodes.length; i++) {
- if (this.parentNode.parentNode.childNodes[i].firstChild == this)
- break;
- }
-
- var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;
-
- var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id);
- tabBrowser.setTabTitle(tab);
-
- if (tab == tabBrowser.mCurrentTab)
- tabBrowser.updateTitlebar();
- ]]>
- </body>
- </method>
-
- <method name="setTabTitleLoading">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- aTab.label = this.mStringBundle.getString("tabs.loading");
- aTab.setAttribute("crop", "end");
- ]]>
- </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 && title != "about:blank") {
- // 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.contentDocument.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.untitled");
- }
-
- aTab.label = title;
- aTab.setAttribute("crop", crop);
- ]]>
- </body>
- </method>
-
- <method name="setStripVisibilityTo">
- <parameter name="aShow"/>
- <body>
- <![CDATA[
- this.mStrip.collapsed = !aShow;
- if (aShow) {
- // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
- document.getElementById("menu_closeWindow").hidden = false;
- document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
- if (!this.mTabbedMode)
- this.enterTabbedMode();
- }
- else {
- // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
- document.getElementById("menu_closeWindow").hidden = true;
- document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
- }
- ]]>
- </body>
- </method>
-
- <method name="getStripVisibility">
- <body>
- return !this.mStrip.collapsed;
- </body>
- </method>
-
- <method name="enterTabbedMode">
- <body>
- <![CDATA[
- this.mTabbedMode = true; // Welcome to multi-tabbed mode.
-
- // Get the first tab all hooked up with a title listener and popup blocking listener.
- this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
- var throbberElement = document.getElementById("navigator-throbber");
- if (throbberElement && throbberElement.hasAttribute("busy")) {
- this.mCurrentTab.setAttribute("busy", "true");
- this.mIsBusy = true;
- this.setTabTitleLoading(this.mCurrentTab);
- this.updateIcon(this.mCurrentTab);
- } else {
- this.setTabTitle(this.mCurrentTab);
- this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
- }
-
- var filter;
- if (this.mTabFilters.length > 0) {
- // Use the filter hooked up in our addProgressListener
- filter = this.mTabFilters[0];
- } else {
- // create a filter and hook it up to our first browser
- filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
- .createInstance(Components.interfaces.nsIWebProgress);
- this.mTabFilters[0] = filter;
- this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
- }
-
- // Remove all our progress listeners from the active browser's filter.
- for (var i = 0; i < this.mProgressListeners.length; i++) {
- var p = this.mProgressListeners[i];
- if (p)
- filter.removeProgressListener(p);
- }
-
- // Wire up a progress listener to our filter.
- const listener = this.mTabProgressListener(this.mCurrentTab,
- this.mCurrentBrowser,
- false);
- filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
- this.mTabListeners[0] = listener;
- ]]>
- </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 bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
- this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
- var owner = bgLoad ? null : this.selectedTab;
- var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner,
- aAllowThirdPartyFixup);
- if (!bgLoad)
- this.selectedTab = tab;
-
- return tab;
- ]]>
- </body>
- </method>
-
- <method name="loadTabs">
- <parameter name="aURIs"/>
- <parameter name="aLoadInBackground"/>
- <parameter name="aReplace"/>
- <body><![CDATA[
- // 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 owner = (aURIs.length > 1) || aLoadInBackground ? null : gBrowser.selectedTab;
- var firstTabAdded = null;
- if (aReplace)
- this.loadURI(aURIs[0], null, null);
- else
- firstTabAdded = gBrowser.addTab(aURIs[0], null, null, null, owner, false);
-
- var tabNum = this.mTabContainer.selectedIndex;
- for (var i = 1; i < aURIs.length; ++i) {
- var tab = gBrowser.addTab(aURIs[i]);
- if (aReplace)
- this.moveTabTo(tab, ++tabNum);
- }
-
- if (!aLoadInBackground) {
- if (firstTabAdded) {
- // .selectedTab setter focuses the content area
- this.selectedTab = firstTabAdded;
- }
- else
- window.content.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[
- this._browsers = null; // invalidate cache
-
- if (!this.mTabbedMode)
- this.enterTabbedMode();
-
- // 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(
- "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
- "tab");
-
- var blank = (aURI == "about:blank");
-
- if (blank)
- t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
- else
- t.setAttribute("label", aURI);
-
- t.setAttribute("crop", "end");
- t.maxWidth = this.mTabContainer.mTabMaxWidth;
- t.minWidth = this.mTabContainer.mTabMinWidth;
- t.width = 0;
- t.setAttribute("flex", "100");
- t.setAttribute("validate", "never");
- t.setAttribute("onerror", "this.removeAttribute('image');");
- t.className = "tabbrowser-tab";
-
- this.mTabContainer.appendChild(t);
-
- if (document.defaultView
- .getComputedStyle(this.mTabContainer, "")
- .direction == "rtl") {
- /* In RTL UI, the tab is visually added to the left side of the
- * tabstrip. This means the tabstip has to be scrolled back in
- * order to make sure the same set of tabs is visible before and
- * after the new tab is added */
-
- this.mTabContainer.mTabstrip.scrollBoxObject
- .scrollBy(this.mTabContainer.firstChild.boxObject.width, 0);
- }
-
- // invalidate cache, because mTabContainer is about to change
- this._browsers = null;
-
- // If this new tab is owned by another, assert that relationship
- if (aOwner !== undefined && aOwner !== null) {
- t.owner = aOwner;
-
- var self = this;
- function attrChanged(event) {
- if (event.attrName == "selectedIndex" &&
- event.prevValue != event.newValue)
- self.resetOwner(parseInt(event.prevValue));
- }
- if (!this.mTabChangedListenerAdded) {
- this.mTabBox.addEventListener("DOMAttrModified", attrChanged, false);
- this.mTabChangedListenerAdded = true;
- }
- }
-
- 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 (this.hasAttribute("autocompletepopup"))
- b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
- b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
-
- // Add the Message and the Browser to the box
- var notificationbox = document.createElementNS(
- "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
- "notificationbox");
- notificationbox.setAttribute("flex", "1");
- notificationbox.appendChild(b);
- b.setAttribute("flex", "1");
- this.mPanelContainer.appendChild(notificationbox);
-
- b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
- if (this.mStrip.collapsed)
- this.setStripVisibilityTo(true);
-
- this.mPrefs.setBoolPref("browser.tabs.forceHide", false);
-
- // wire up a progress listener for the new browser object.
- var position = this.mTabContainer.childNodes.length-1;
- var tabListener = this.mTabProgressListener(t, b, blank);
- 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;
-
- var uniqueId = "panel" + Date.now() + position;
- this.mPanelContainer.lastChild.id = uniqueId;
- t.linkedPanel = uniqueId;
- t.linkedBrowser = b;
- t._tPos = position;
- if (t.previousSibling.selected)
- t.setAttribute("afterselected", true);
-
- if (!blank) {
- // Stop the existing about:blank load. Otherwise, if aURI
- // doesn't stop in-progress loads on its own, we'll get into
- // trouble with multiple parallel loads running at once.
- b.stop();
-
- // pretend the user typed this so it'll be available till
- // the document successfully loads
- b.userTypedValue = aURI;
-
- if (aPostData === undefined)
- aPostData = null;
- const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
- var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
- if (aAllowThirdPartyFixup) {
- flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
- }
- try {
- b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
- }
- catch (ex) {
- }
- }
-
- // |setTimeout| here to ensure we're post reflow
- var _delayedUpdate = function(aTabContainer) {
- aTabContainer.adjustTabstrip();
-
- if (aTabContainer.selectedItem != t)
- aTabContainer._notifyBackgroundTab(t);
-
- // XXXmano: this is a temporary workaround to bug 343585
- // 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.
- aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
- }
- setTimeout(_delayedUpdate, 0, this.mTabContainer);
-
- // 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);
-
- return t;
- ]]>
- </body>
- </method>
-
- <method name="warnAboutClosingTabs">
- <parameter name="aAll"/>
- <body>
- <![CDATA[
- var numTabs = this.mTabContainer.childNodes.length;
- var reallyClose = true;
- if (numTabs <= 1)
- return reallyClose;
-
- const pref = "browser.tabs.warnOnClose";
- var shouldPrompt = this.mPrefs.getBoolPref(pref);
-
- if (shouldPrompt) {
- var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
- .getService(Components.interfaces.nsIPromptService);
-
- //default to true: if it were false, we wouldn't get this far
- var warnOnClose = { value:true };
- var bundle = this.mStringBundle;
- var tabsToClose = numTabs; //number of tabs to be removed
- if (!aAll)
- --tabsToClose;
-
- var messageKey = (tabsToClose == 1) ? "tabs.closeWarningOneTab" : "tabs.closeWarningMultipleTabs";
- var closeKey = (tabsToClose == 1) ? "tabs.closeButtonOne" : "tabs.closeButtonMultiple";
- // 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 = promptService.confirmEx(window,
- bundle.getString('tabs.closeWarningTitle'),
- bundle.getFormattedString(messageKey, [tabsToClose]),
- (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
- + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
- bundle.getString(closeKey),
- null, null,
- bundle.getString('tabs.closeWarningPromptMe'),
- warnOnClose);
- reallyClose = (buttonPressed == 0);
- // don't set the pref unless they press OK and it's false
- if (reallyClose && !warnOnClose.value)
- this.mPrefs.setBoolPref(pref, false);
- }
- return reallyClose;
- ]]>
- </body>
- </method>
-
- <method name="removeAllTabsBut">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- if (this.warnAboutClosingTabs(false)) {
- if (aTab.localName != "tab")
- aTab = this.mCurrentTab;
- else
- this.mTabContainer.selectedItem = aTab;
-
- var childNodes = this.mTabContainer.childNodes;
-
- for (var i = childNodes.length - 1; i >= 0; --i) {
- if (childNodes[i] != aTab)
- this.removeTab(childNodes[i]);
- }
- }
- ]]>
- </body>
- </method>
-
- <method name="removeCurrentTab">
- <body>
- <![CDATA[
- return this.removeTab(this.mCurrentTab);
- ]]>
- </body>
- </method>
-
- <method name="resetOwner">
- <parameter name="oldIndex"/>
- <body>
- <![CDATA[
- // Reset the owner property, since we're leaving the modally opened
- // tab for another.
- if (oldIndex < this.mTabContainer.childNodes.length) {
- var tab = this.mTabContainer.childNodes[oldIndex];
- tab.owner = null;
- }
- ]]>
- </body>
- </method>
-
- <method name="removeTab">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- this._browsers = null; // invalidate cache
- if (aTab.localName != "tab")
- aTab = this.mCurrentTab;
-
- var l = this.mTabContainer.childNodes.length;
- if (l == 1 && this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
- // hide the tab bar
- this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
- this.setStripVisibilityTo(false);
- return;
- }
-
- var ds = this.getBrowserForTab(aTab).docShell;
- if (ds.contentViewer && !ds.contentViewer.permitUnload())
- return;
-
- // see notes in addTab
- var _delayedUpdate = function(aTabContainer) {
- aTabContainer.adjustTabstrip();
- aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
- }
- setTimeout(_delayedUpdate, 0, this.mTabContainer);
-
- if (l == 1) {
- // add a new blank tab to replace the one we're about to close
- // (this ensures that the remaining tab is as good as new)
- this.addTab("about:blank");
- l++;
- }
- else if (l == 2) {
- var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
- var tabStripHide = !window.toolbar.visible;
- if (autohide || tabStripHide)
- this.setStripVisibilityTo(false);
- }
-
- // 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("Events");
- evt.initEvent("TabClose", true, false);
- aTab.dispatchEvent(evt);
-
- var index = -1;
- if (this.mCurrentTab == aTab)
- index = this.mTabContainer.selectedIndex;
- else {
- // Find and locate the tab in our list.
- for (var i = 0; i < l; i++)
- if (this.mTabContainer.childNodes[i] == aTab)
- index = i;
- }
-
- // Remove the tab's filter and progress listener.
- const filter = this.mTabFilters[index];
- var oldBrowser = this.getBrowserAtIndex(index);
- oldBrowser.webProgress.removeProgressListener(filter);
- filter.removeProgressListener(this.mTabListeners[index]);
- this.mTabFilters.splice(index, 1);
- this.mTabListeners.splice(index, 1);
-
- // Remove our title change and blocking listeners
- oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
-
- // We are no longer the primary content area.
- oldBrowser.setAttribute("type", "content-targetable");
-
- // Get the index of the tab we're removing before unselecting it
- var currentIndex = this.mTabContainer.selectedIndex;
-
- var oldTab = aTab;
-
- // clean up the before/afterselected attributes before removing the tab
- oldTab._selected = false;
-
- // Remove this tab as the owner of any other tabs, since it's going away.
- for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
- var tab = this.mTabContainer.childNodes[i];
- if ("owner" in tab && tab.owner == oldTab)
- // |tab| is a child of the tab we're removing, make it an orphan
- tab.owner = null;
- }
-
- // 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. But
- // clearing focusedWindow happens below because it gets
- // reset by updateCurrentBrowser.
- oldBrowser.destroy();
-
- if (oldBrowser == this.mCurrentBrowser)
- this.mCurrentBrowser = null;
-
- // Remove the tab
- this.mTabContainer.removeChild(oldTab);
- // invalidate cache, because mTabContainer is about to change
- this._browsers = null;
- this.mPanelContainer.removeChild(oldBrowser.parentNode);
-
- 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.mTabContainer.mTabstrip;
- var scrollPos = {};
- tabStrip.scrollBoxObject.getPosition(scrollPos, {});
- var scrolledSize = {};
- tabStrip.scrollBoxObject.getScrolledSize(scrolledSize, {});
-
- if (scrollPos.value + tabStrip.boxObject.width >=
- scrolledSize.value) {
- tabStrip.scrollByPixels(-1 * this.mTabContainer.firstChild
- .boxObject.width);
- }
- }
- catch (ex) {
- }
-
- // Find the tab to select
- var newIndex = -1;
- if (currentIndex > index)
- newIndex = currentIndex-1;
- else if (currentIndex < index)
- newIndex = currentIndex;
- else {
- if ("owner" in oldTab && oldTab.owner &&
- this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
- for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
- tab = this.mTabContainer.childNodes[i];
- if (tab == oldTab.owner) {
- newIndex = i;
- break;
- }
- }
- }
- if (newIndex == -1)
- newIndex = (index == l - 1) ? index - 1 : index;
- }
-
- // Select the new tab
- this.selectedTab = this.mTabContainer.childNodes[newIndex];
-
- for (i = oldTab._tPos; i < this.mTabContainer.childNodes.length; i++) {
- this.mTabContainer.childNodes[i]._tPos = i;
- }
- this.mTabBox.selectedPanel = this.getBrowserForTab(this.mCurrentTab).parentNode;
- this.mCurrentTab._selected = true;
-
- this.updateCurrentBrowser();
-
- // see comment above destroy above
- oldBrowser.focusedWindow = null;
- oldBrowser.focusedElement = null;
- ]]>
- </body>
- </method>
-
- <method name="reloadAllTabs">
- <body>
- <![CDATA[
- var l = this.mPanelContainer.childNodes.length;
- for (var i = 0; i < l; i++) {
- try {
- this.getBrowserAtIndex(i).reload();
- } catch (e) {
- // ignore failure to reload so others will be reloaded
- }
- }
- ]]>
- </body>
- </method>
-
- <method name="reloadTab">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- if (aTab.localName != "tab")
- aTab = this.mCurrentTab;
-
- this.getBrowserForTab(aTab).reload();
- ]]>
- </body>
- </method>
-
- <method name="onTabBarDblClick">
- <parameter name="aEvent"/>
- <body>
- <![CDATA[
- // See hack note in the tabbrowser-close-button binding
- if (!this._blockDblClick && aEvent.button == 0 &&
- aEvent.originalTarget.localName == "box") {
- // xxx this needs to check that we're in the empty area of the tabstrip
- var e = document.createEvent("Events");
- e.initEvent("NewTab", true, true);
- this.dispatchEvent(e);
- }
- ]]>
- </body>
- </method>
-
- <method name="addProgressListener">
- <parameter name="aListener"/>
- <parameter name="aMask"/>
- <body>
- <![CDATA[
- if (!this.mAddProgressListenerWasCalled) {
- this.mAddProgressListenerWasCalled = true;
- var autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
- var forceHide = this.mPrefs.getBoolPref("browser.tabs.forceHide");
- var tabStripHide = !window.toolbar.visible;
- if (!autoHide && !forceHide && !tabStripHide)
- this.setStripVisibilityTo(true);
- }
-
- if (!this.mTabbedMode && this.mProgressListeners.length == 1) {
- // If we are adding a 2nd progress listener, we need to enter tabbed mode
- // because the browser status filter can only handle one progress listener.
- // In tabbed mode, mTabProgressListener is used which will iterate over all listeners.
- this.enterTabbedMode();
- }
-
- this.mProgressListeners.push(aListener);
-
- if (!this.mTabbedMode) {
- // If someone does this:
- // addProgressListener, removeProgressListener, addProgressListener
- // don't create a new filter; reuse the existing filter.
- if (this.mTabFilters.length == 0) {
- // hook a filter up to our first browser
- const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
- .createInstance(Components.interfaces.nsIWebProgress);
- this.mTabFilters[0] = filter;
- this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
- }
-
- // Directly hook the listener up to the filter for better performance
- this.mTabFilters[0].addProgressListener(aListener, aMask);
- }
- ]]>
- </body>
- </method>
-
- <method name="removeProgressListener">
- <parameter name="aListener"/>
- <body>
- <![CDATA[
- for (var i = 0; i < this.mProgressListeners.length; i++) {
- if (this.mProgressListeners[i] == aListener) {
- this.mProgressListeners.splice(i, 1);
- break;
- }
- }
-
- if (!this.mTabbedMode)
- // Don't forget to remove it from the filter we hooked it up to
- this.mTabFilters[0].removeProgressListener(aListener);
- ]]>
- </body>
- </method>
-
- <method name="getBrowserForTab">
- <parameter name="aTab"/>
- <body>
- <![CDATA[
- return aTab.linkedBrowser;
- ]]>
- </body>
- </method>
-
- <property name="tabContainer" readonly="true">
- <getter>
- return this.mTabContainer;
- </getter>
- </property>
-
- <property name="selectedTab">
- <getter>
- return this.mTabBox.selectedTab;
- </getter>
- <setter>
- <![CDATA[
- // 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[
- if (!this._browsers) {
- var browsers = [];
- var i;
- browsers.item = function(i) {return this[i];}
- for (i = 0; i < this.mTabContainer.childNodes.length; i++)
- browsers.push(this.mTabContainer.childNodes[i].linkedBrowser);
- this._browsers = browsers;
- }
- return this._browsers;
- ]]>
- </getter>
- </property>
-
- <!-- Drag and drop observer API -->
- <method name="onDragStart">
- <parameter name="aEvent"/>
- <parameter name="aXferData"/>
- <parameter name="aDragAction"/>
- <body>
- <![CDATA[
- if (aEvent.target.localName == "tab" &&
- aEvent.originalTarget.localName != "toolbarbutton") {
- aXferData.data = new TransferData();
-
- var URI = this.getBrowserForTab(aEvent.target).currentURI;
- if (URI) {
- aXferData.data.addDataForFlavour("text/x-moz-url", URI.spec + "\n" + aEvent.target.label);
- aXferData.data.addDataForFlavour("text/unicode", URI.spec);
- aXferData.data.addDataForFlavour("text/html", '<a href="' + URI.spec + '">' + aEvent.target.label + '</a>');
- } else {
- aXferData.data.addDataForFlavour("text/unicode", "about:blank");
- }
- }
- ]]>
- </body>
- </method>
-
- <method name="canDrop">
- <parameter name="aEvent"/>
- <parameter name="aDragSession"/>
- <body>
- <![CDATA[
- if (aDragSession.sourceNode &&
- aDragSession.sourceNode.parentNode == this.mTabContainer &&
- (aEvent.screenX >= aDragSession.sourceNode.boxObject.screenX &&
- aEvent.screenX <= (aDragSession.sourceNode.boxObject.screenX +
- aDragSession.sourceNode.boxObject.width)))
- return false;
- return true;
- ]]>
- </body>
- </method>
-
- <field name="mDragTime">0</field>
- <field name="mDragOverDelay">350</field>
-
- <method name="onDragOver">
- <parameter name="aEvent"/>
- <parameter name="aFlavour"/>
- <parameter name="aDragSession"/>
- <body>
- <![CDATA[
- var tabStrip = this.mTabContainer.mTabstrip;
- var ltr = (window.getComputedStyle(this.parentNode, 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.mTabContainer.getAttribute("overflow") == "true") {
- var targetAnonid = aEvent.originalTarget.getAttribute("anonid");
- switch (targetAnonid) {
- case "scrollbutton-up":
- pixelsToScroll = tabStrip.scrollIncrement * -1;
- break;
- case "scrollbutton-down":
- case "alltabs-button":
- pixelsToScroll = tabStrip.scrollIncrement;
- break;
- }
- if (pixelsToScroll)
- tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
- }
-
- var isTabDrag = (aDragSession.sourceNode &&
- aDragSession.sourceNode.parentNode == this.mTabContainer);
- if (!isTabDrag && aEvent.target.localName == "tab") {
- if (!this.mDragTime)
- this.mDragTime = Date.now();
- if (Date.now() >= this.mDragTime + this.mDragOverDelay)
- this.mTabContainer.selectedItem = aEvent.target;
- return;
- }
-
- var newIndex = this.getNewIndex(aEvent);
- var ib = this.mTabDropIndicatorBar;
- var ind = ib.firstChild;
- var tabStripBoxObject = tabStrip.scrollBoxObject;
- var minMargin = tabStripBoxObject.x - this.boxObject.x;
- // make sure we don't place the tab drop indicator past the
- // edge, or the containing box will flex and stretch
- // the tab drop indicator bar, which will flex the url bar.
- // XXX todo
- // just use first value if you can figure out how to get
- // the tab drop indicator to crop instead of flex and stretch
- // the tab drop indicator bar.
- var maxMargin = Math.min(minMargin + tabStripBoxObject.width,
- ib.boxObject.x + ib.boxObject.width -
- ind.boxObject.width);
- if (!ltr)
- [minMargin, maxMargin] = [this.boxObject.width - maxMargin,
- this.boxObject.width - minMargin];
- var newMargin, tabBoxObject;
- if (pixelsToScroll) {
- // if we are scrolling, put the drop indicator at the edge
- // so that it doesn't jump while scrolling
- newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
- }
- else {
- if (newIndex == this.mTabs.length) {
- tabBoxObject = this.mTabs[newIndex-1].boxObject;
- if (ltr)
- newMargin = tabBoxObject.screenX - this.boxObject.screenX
- + tabBoxObject.width;
- else
- newMargin = this.boxObject.screenX - tabBoxObject.screenX
- + this.boxObject.width;
- }
- else {
- tabBoxObject = this.mTabs[newIndex].boxObject;
- if (ltr)
- newMargin = tabBoxObject.screenX - this.boxObject.screenX;
- else
- newMargin = this.boxObject.screenX - tabBoxObject.screenX
- + this.boxObject.width - tabBoxObject.width;
- }
- // ensure we never place the drop indicator beyond our limits
- if (newMargin < minMargin)
- newMargin = minMargin;
- else if (newMargin > maxMargin)
- newMargin = maxMargin;
- }
-
- ind.style.MozMarginStart = newMargin + 'px';
-
- ib.collapsed = !aDragSession.canDrop;
- ]]>
- </body>
- </method>
-
- <method name="onDrop">
- <parameter name="aEvent"/>
- <parameter name="aXferData"/>
- <parameter name="aDragSession"/>
- <body>
- <![CDATA[
- var accelKeyPressed = aEvent.ctrlKey;
- var draggedTab;
- if (aDragSession.sourceNode && aDragSession.sourceNode.localName == "tab" &&
- (aDragSession.sourceNode.parentNode == this.mTabContainer ||
- aDragSession.sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
- aDragSession.sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser"))
- draggedTab = aDragSession.sourceNode;
- if (draggedTab && (accelKeyPressed || draggedTab.parentNode == this.mTabContainer)) {
- var newIndex = this.getNewIndex(aEvent);
- if (accelKeyPressed) {
- // copy the dropped tab (wherever it's from)
- var newTab = this.duplicateTab(draggedTab);
- this.moveTabTo(newTab, newIndex);
- if (draggedTab.parentNode != this.mTabContainer || aEvent.shiftKey)
- this.selectedTab = newTab;
- }
- else {
- // move the dropped tab
- if (newIndex > draggedTab._tPos)
- newIndex--;
- if (newIndex != draggedTab._tPos)
- this.moveTabTo(draggedTab, newIndex);
- }
- }
- else if (draggedTab) {
- // copy the dropped tab and remove it from the other window
- // (making it seem to have moved between windows)
- newIndex = this.getNewIndex(aEvent);
- newTab = this.duplicateTab(draggedTab);
- this.moveTabTo(newTab, newIndex);
- this.selectedTab = newTab;
-
- var remoteBrowser = draggedTab.ownerDocument.defaultView.getBrowser();
- var tabCount = remoteBrowser.tabContainer.childNodes.length;
- remoteBrowser.removeTab(draggedTab);
- // close the other window if this was its last tab
- if (tabCount == 1)
- draggedTab.ownerDocument.defaultView.close();
- }
- else {
- var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
-
- // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
- // Also disallow dropping javascript: or data: urls--bail out
- if (!url || !url.length || url.indexOf(" ", 0) != -1 ||
- /^\s*(javascript|data):/.test(url))
- return;
-
- nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, url);
-
- var bgLoad = true;
- try {
- bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
- }
- catch (e) { }
-
- if (aEvent.shiftKey)
- bgLoad = !bgLoad;
-
- if (document.getBindingParent(aEvent.originalTarget).localName != "tab" || accelKeyPressed) {
- // We're adding a new tab.
- newIndex = this.getNewIndex(aEvent);
- newTab = this.loadOneTab(getShortcutOrURI(url), null, null, null, bgLoad, false);
- this.moveTabTo(newTab, newIndex);
- }
- else {
- // Load in an existing tab.
- var tab = aEvent.target;
- try {
- this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
- if (this.mCurrentTab != tab && !bgLoad)
- this.selectedTab = tab;
- } catch(ex) {
- // Just ignore invalid urls
- }
- }
- }
- ]]>
- </body>
- </method>
-
- <method name="onDragExit">
- <parameter name="aEvent"/>
- <parameter name="aDragSession"/>
- <body>
- <![CDATA[
- this.mDragTime = 0;
-
- if (aDragSession.sourceNode &&
- aDragSession.sourceNode.parentNode == this.mTabContainer &&
- aDragSession.canDrop) {
- var target = aEvent.relatedTarget;
- while (target && target != this.mStrip)
- target = target.parentNode;
- if (target)
- return;
- }
- this.mTabDropIndicatorBar.collapsed = true;
- ]]>
- </body>
- </method>
-
- <method name="getSupportedFlavours">
- <body>
- <![CDATA[
- var flavourSet = new FlavourSet();
- flavourSet.appendFlavour("text/x-moz-url");
- flavourSet.appendFlavour("text/unicode");
- flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
- return flavourSet;
- ]]>
- </body>
- </method>
-
- <method name="moveTabTo">
- <parameter name="aTab"/>
- <parameter name="aIndex"/>
- <body>
- <![CDATA[
- this._browsers = null; // invalidate cache
- this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
- this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
-
- var oldPosition = aTab._tPos;
-
- aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
- this.mCurrentTab._selected = false;
- // 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.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes.item(aIndex));
- // invalidate cache, because mTabContainer is about to change
- this._browsers = null;
-
- var i;
- for (i = 0; i < this.mTabContainer.childNodes.length; i++) {
- this.mTabContainer.childNodes[i]._tPos = i;
- this.mTabContainer.childNodes[i]._selected = false;
- }
- this.mCurrentTab._selected = true;
- this.mTabContainer.mTabstrip.scrollBoxObject.ensureElementIsVisible(this.mCurrentTab);
-
- var evt = document.createEvent("UIEvents");
- evt.initUIEvent("TabMove", true, false, window, oldPosition);
- aTab.dispatchEvent(evt);
-
- return aTab;
- ]]>
- </body>
- </method>
-
- <method name="getNewIndex">
- <parameter name="aEvent"/>
- <body>
- <![CDATA[
- var i;
- if (window.getComputedStyle(this.parentNode, null).direction == "ltr") {
- for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
- if (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
- return i;
- } else {
- for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
- if (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
- return i;
- }
- return this.mTabs.length;
- ]]>
- </body>
- </method>
-
-
- <method name="moveTabForward">
- <body>
- <![CDATA[
- var tabPos = this.mCurrentTab._tPos;
- if (tabPos < this.browsers.length - 1) {
- this.moveTabTo(this.mCurrentTab, tabPos + 1);
- this.mCurrentTab.focus();
- }
- else if (this.arrowKeysShouldWrap)
- this.moveTabToStart();
- ]]>
- </body>
- </method>
-
- <method name="moveTabBackward">
- <body>
- <![CDATA[
- var tabPos = this.mCurrentTab._tPos;
- if (tabPos > 0) {
- this.moveTabTo(this.mCurrentTab, tabPos - 1);
- this.mCurrentTab.focus();
- }
- 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);
- this.mCurrentTab.focus();
- }
- ]]>
- </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);
- this.mCurrentTab.focus();
- }
- ]]>
- </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[
- // try to have SessionStore duplicate the given tab
- try {
- var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]
- .getService(Components.interfaces.nsISessionStore);
- return ss.duplicateTab(window, aTab);
- } catch (ex) {
- // fall back to basic URL copying
- return this.loadOneTab(this.getBrowserForTab(aTab).currentURI.spec);
- }
- ]]>
- </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[
- 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="pageReport"
- onget="return this.mCurrentBrowser.pageReport;"
- readonly="true"/>
-
- <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>
-
- <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="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="documentCharsetInfo"
- onget="return this.mCurrentBrowser.documentCharsetInfo;"
- readonly="true"/>
-
- <property name="contentDocument"
- 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"/>
-
- <method name="dragDropSecurityCheck">
- <parameter name="aEvent"/>
- <parameter name="aDragSession"/>
- <parameter name="aUri"/>
- <body>
- <![CDATA[
- nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, aUri);
- ]]>
- </body>
- </method>
-
- <field name="_keyEventHandler" readonly="true">
- <![CDATA[({
- tabbrowser: this,
- handleEvent: function handleEvent(aEvent) {
- if (!aEvent.isTrusted) {
- // Don't let untrusted events mess with tabs.
- return;
- }
-
- if ('altKey' in aEvent && aEvent.altKey)
- return;
- if (('ctrlKey' in aEvent && aEvent.ctrlKey) &&
- !('shiftKey' in aEvent && aEvent.shiftKey) &&
- !('metaKey' in aEvent && aEvent.metaKey)) {
- if (aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
- this.tabbrowser.mTabBox.handleCtrlPageUpDown) {
- this.tabbrowser.removeCurrentTab();
-
- aEvent.stopPropagation();
- aEvent.preventDefault();
- return;
- }
- if (aEvent.target.localName == "tabbrowser") {
- switch (aEvent.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(aEvent);
- 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;
- }
- aEvent.stopPropagation();
- aEvent.preventDefault();
- }
- }
- }
- })]]>
- </field>
-
- <property name="userTypedClear"
- onget="return this.mCurrentBrowser.userTypedClear;"
- onset="return this.mCurrentBrowser.userTypedClear = val;"/>
-
- <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 tn = document.tooltipNode;
- if (tn.localName != "tab")
- return false; // Not a tab, so cancel the tooltip
- if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
- event.target.setAttribute("label", tn.getAttribute("closetabtext"));
- return true;
- }
- if (tn.hasAttribute("label")) {
- event.target.setAttribute("label", tn.getAttribute("label"));
- return true;
- }
- return false;
- ]]>
- </body>
- </method>
-
- <constructor>
- <![CDATA[
- this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
- this.mCurrentTab = this.mTabContainer.firstChild;
- document.addEventListener("keypress", this._keyEventHandler, false);
-
- var uniqueId = "panel" + Date.now();
- this.mPanelContainer.childNodes[0].id = uniqueId;
- this.mTabContainer.childNodes[0].linkedPanel = uniqueId;
- this.mTabContainer.childNodes[0]._tPos = 0;
- this.mTabContainer.childNodes[0].linkedBrowser = this.mPanelContainer.childNodes[0].firstChild;
-
- // set up the shared autoscroll popup
- this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
- this.appendChild(this._autoScrollPopup);
- this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
- ]]>
- </constructor>
-
- <destructor>
- <![CDATA[
- for (var i = 0; i < this.mTabListeners.length; ++i) {
- this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
- this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
- this.mTabFilters[i] = null;
- this.mTabListeners[i] = null;
- this.getBrowserAtIndex(i).removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
- }
- document.removeEventListener("keypress", this._keyEventHandler, false);
- ]]>
- </destructor>
- </implementation>
-
- <handlers>
- <handler event="DOMWindowClose" phase="capturing">
- <![CDATA[
- if (!event.isTrusted)
- return;
-
- const browsers = this.mPanelContainer.childNodes;
- if (browsers.length == 1) {
- // There's only one browser left. If a window is being
- // closed and the window is *not* the window in the
- // browser that's still around, prevent the event's default
- // action to prevent closing a window that's being closed
- // already.
- if (this.getBrowserAtIndex(0).contentWindow != event.target)
- event.preventDefault();
-
- return;
- }
-
- var i = 0;
- for (; i < browsers.length; ++i) {
- if (this.getBrowserAtIndex(i).contentWindow == event.target) {
- this.removeTab(this.mTabContainer.childNodes[i]);
- event.preventDefault();
-
- break;
- }
- }
- ]]>
- </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.
-
- var targetTop = event.target.top;
-
- for (var i = 0; i < browsers.length; ++i) {
- if (this.getBrowserAtIndex(i).contentWindow == targetTop) {
- this.selectedTab = this.mTabContainer.childNodes[i];
-
- break;
- }
- }
- ]]>
- </handler>
- </handlers>
- </binding>
-
-
- <binding id="tabbrowser-tabs"
- extends="chrome://global/content/bindings/tabbox.xml#tabs">
- <content>
- <xul:stack flex="1" class="tabs-stack">
- <xul:vbox>
- <xul:spacer flex="1"/>
- <xul:hbox class="tabs-bottom" align="center"/>
- </xul:vbox>
- <xul:hbox xbl:inherits="overflow" class="tabs-container">
- <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
- style="min-width: 1px;" chromedir="&locale.dir;"
- clicktoscroll="true"
- class="tabbrowser-arrowscrollbox">
- <children/>
- </xul:arrowscrollbox>
- <xul:stack align="center" pack="end" chromedir="&locale.dir;">
- <xul:hbox flex="1" class="tabs-alltabs-box" anonid="alltabs-box"/>
- <xul:hbox flex="1" class="tabs-alltabs-box-animate" anonid="alltabs-box-animate"/>
- <xul:toolbarbutton class="tabs-alltabs-button" type="menu" anonid="alltabs-button"
- tooltipstring="&listAllTabs.label;">
- <xul:menupopup class="tabs-alltabs-popup" anonid="alltabs-popup"
- position="after_end"/>
- </xul:toolbarbutton>
- </xul:stack>
- <xul:toolbarbutton anonid="tabs-closebutton"
- class="close-button tabs-closebutton" chromedir="&locale.dir;"/>
- </xul:hbox>
- </xul:stack>
- </content>
- <implementation implements="nsITimerCallback, nsIDOMEventListener">
- <constructor>
- <![CDATA[
- var pb2 =
- Components.classes['@mozilla.org/preferences-service;1'].
- getService(Components.interfaces.nsIPrefBranch2);
-
- try {
- this.mTabMinWidth = pb2.getIntPref("browser.tabs.tabMinWidth");
- } catch (e) {
- }
- try {
- this.mTabMaxWidth = pb2.getIntPref("browser.tabs.tabMaxWidth");
- } catch (e) {
- }
- try {
- this.mTabClipWidth = pb2.getIntPref("browser.tabs.tabClipWidth");
- } catch (e) {
- }
- try {
- this.mCloseButtons = pb2.getIntPref("browser.tabs.closeButtons");
- } catch (e) {
- }
-
- this.firstChild.minWidth = this.mTabMinWidth;
- this.firstChild.maxWidth = this.mTabMaxWidth;
- this.adjustTabstrip();
-
- pb2.addObserver("browser.tabs.closeButtons",
- this._prefObserver, false);
-
- window.addEventListener("resize", this, false);
-
- // Listen to overflow/underflow events on the tabstrip,
- // we cannot put these as xbl handlers on the entire binding because
- // they would also get called for the all-tabs popup scrollbox.
- // Also, we can't rely on event.target becuase these are all
- // anonymous nodes.
- this.mTabstrip.addEventListener("overflow", this, false);
- this.mTabstrip.addEventListener("underflow", this, false);
- ]]>
- </constructor>
-
- <destructor>
- <![CDATA[
- var pb2 =
- Components.classes['@mozilla.org/preferences-service;1'].
- getService(Components.interfaces.nsIPrefBranch2);
- pb2.removeObserver("browser.tabs.closeButtons", this._prefObserver);
-
- // Release timer to avoid reference cycles.
- if (this._animateTimer) {
- this._animateTimer.cancel();
- this._animateTimer = null;
- }
-
- this.mTabstrip.removeEventListener("overflow", this, false);
- this.mTabstrip.removeEventListener("underflow", this, false);
- ]]>
- </destructor>
-
- <field name="mTabstripWidth">0</field>
-
- <field name="mTabstrip">
- document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
- </field>
-
- <field name="mTabstripClosebutton">
- document.getAnonymousElementByAttribute(this, "anonid", "tabs-closebutton");
- </field>
-
- <field name="_prefObserver">({
- tabbox: this,
-
- observe: function(subject, topic, data)
- {
- if (topic == "nsPref:changed") {
- switch (data) {
- case "browser.tabs.closeButtons":
- subject.QueryInterface(Components.interfaces.nsIPrefBranch);
- this.tabbox.mCloseButtons = subject.getIntPref("browser.tabs.closeButtons");
- this.tabbox.adjustTabstrip();
- break;
- }
- }
- },
-
- QueryInterface: function(aIID)
- {
- if (aIID.equals(Components.interfaces.nsIObserver) ||
- aIID.equals(Components.interfaces.nsISupports))
- return this;
- throw Components.results.NS_NOINTERFACE;
- }
- });
- </field>
- <field name="mTabMinWidth">100</field>
- <field name="mTabMaxWidth">250</field>
- <field name="mTabClipWidth">140</field>
- <field name="mCloseButtons">1</field>
-
- <method name="adjustTabstrip">
- <body><![CDATA[
- // modes for tabstrip
- // 0 - activetab = close button on active tab only
- // 1 - alltabs = close buttons on all tabs
- // 2 - noclose = no close buttons at all
- // 3 - closeatend = close button at the end of the tabstrip
- switch (this.mCloseButtons) {
- case 0:
- this.setAttribute("closebuttons", "activetab");
- break;
- case 1:
- var width = this.firstChild.boxObject.width;
- // 0 width is an invalid value and indicates
- // an item without display, so ignore.
- if (width > this.mTabClipWidth || width == 0)
- this.setAttribute("closebuttons", "alltabs");
- else
- this.setAttribute("closebuttons", "activetab");
- break;
- case 2:
- case 3:
- this.setAttribute("closebuttons", "noclose");
- break;
- }
- this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
- ]]></body>
- </method>
-
- <field name="_mPrefs">null</field>
- <property name="mPrefs" readonly="true">
- <getter>
- <![CDATA[
- if (!this._mPrefs) {
- this._mPrefs =
- Components.classes['@mozilla.org/preferences-service;1'].
- getService(Components.interfaces.nsIPrefBranch2);
- }
- return this._mPrefs;
- ]]>
- </getter>
- </property>
-
- <method name="_handleTabSelect">
- <body><![CDATA[
- this.mTabstrip.ensureElementIsVisible(this.selectedItem);
- ]]></body>
- </method>
-
- <method name="handleEvent">
- <parameter name="aEvent"/>
- <body><![CDATA[
- switch (aEvent.type) {
- case "overflow":
- this.setAttribute("overflow", "true");
- this.mTabstrip.scrollBoxObject
- .ensureElementIsVisible(this.selectedItem);
- break;
- case "underflow":
- this.removeAttribute("overflow");
- break;
- case "resize":
- var width = this.mTabstrip.boxObject.width;
- if (width != this.mTabstripWidth) {
- this.adjustTabstrip();
- // XXX without this line the tab bar won't budge
- this.mTabstrip.scrollByPixels(1);
- this._handleTabSelect();
- this.mTabstripWidth = width;
- }
- break;
- }
- ]]></body>
- </method>
-
- <field name="mAllTabsPopup">
- document.getAnonymousElementByAttribute(this,
- "anonid", "alltabs-popup");
- </field>
-
- <field name="mAllTabsBoxAnimate">
- document.getAnonymousElementByAttribute(this,
- "anonid",
- "alltabs-box-animate");
- </field>
-
-
- <field name="mAllTabsButton">
- document.getAnonymousElementByAttribute(this,
- "anonid", "alltabs-button");
- </field>
-
- <field name="_animateTimer">null</field>
- <field name="_animateStep">-1</field>
- <field name="_animateDelay">25</field>
- <field name="_animatePercents">
- [1.00, 0.85, 0.80, 0.75, 0.71, 0.68, 0.65, 0.62, 0.59, 0.57,
- 0.54, 0.52, 0.50, 0.47, 0.45, 0.44, 0.42, 0.40, 0.38, 0.37,
- 0.35, 0.34, 0.32, 0.31, 0.30, 0.29, 0.28, 0.27, 0.26, 0.25,
- 0.24, 0.23, 0.23, 0.22, 0.22, 0.21, 0.21, 0.21, 0.20, 0.20,
- 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.19, 0.19, 0.19, 0.18,
- 0.18, 0.17, 0.17, 0.16, 0.15, 0.14, 0.13, 0.11, 0.09, 0.06]
- </field>
-
- <method name="_stopAnimation">
- <body><![CDATA[
- if (this._animateStep != -1) {
- if (this._animateTimer)
- this._animateTimer.cancel();
-
- this._animateStep = -1;
- this.mAllTabsBoxAnimate.style.opacity = 0.0;
- }
- ]]></body>
- </method>
-
- <method name="_notifyBackgroundTab">
- <parameter name="aTab"/>
- <body><![CDATA[
- var tsbo = this.mTabstrip.scrollBoxObject;
- var tsboStart = tsbo.screenX;
- var tsboEnd = tsboStart + tsbo.width;
-
- var ctbo = aTab.boxObject;
- var ctboStart = ctbo.screenX;
- var ctboEnd = ctboStart + ctbo.width;
-
- // Is the new tab already completely visible?
- if (tsboStart <= ctboStart && ctboEnd <= tsboEnd)
- return;
-
- if (this.mTabstrip.smoothScroll) {
- var selStart = this.selectedItem.boxObject.screenX;
- var selEnd = selStart + this.selectedItem.boxObject.width;
-
- // Can we make both the new tab and the selected tab completely visible?
- if (Math.max(ctboEnd - selStart, selEnd - ctboStart) <= tsbo.width) {
- this.mTabstrip.ensureElementIsVisible(aTab);
- return;
- }
-
- this.mTabstrip._smoothScrollByPixels(this.mTabstrip._isLTRScrollbox ?
- selStart - tsboStart : selEnd - tsboEnd);
- }
-
- // start the flash timer
- this._animateStep = 0;
-
- if (!this._animateTimer)
- this._animateTimer =
- Components.classes["@mozilla.org/timer;1"]
- .createInstance(Components.interfaces.nsITimer);
- else
- this._animateTimer.cancel();
-
- this._animateTimer.initWithCallback(this, this._animateDelay,
- this._animateTimer.TYPE_REPEATING_SLACK);
- ]]></body>
- </method>
-
- <method name="notify">
- <parameter name="aTimer"/>
- <body><![CDATA[
- if (!document)
- aTimer.cancel();
-
- var percent = this._animatePercents[this._animateStep];
- this.mAllTabsBoxAnimate.style.opacity = percent;
-
- if (this._animateStep < (this._animatePercents.length - 1))
- this._animateStep++;
- else
- this._stopAnimation();
- ]]></body>
- </method>
- </implementation>
- <handlers>
- <handler event="TabSelect" action="this._handleTabSelect();"/>
- <handler event="mouseover"><![CDATA[
- if (event.originalTarget == this.mAllTabsButton) {
- this.mAllTabsButton
- .setAttribute("tooltiptext",
- this.mAllTabsButton.getAttribute("tooltipstring"));
- }
- else
- this.mAllTabsButton.removeAttribute("tooltiptext");
- ]]></handler>
- </handlers>
- </binding>
-
- <!-- alltabs-popup binding
- This binding relies on the structure of the tabbrowser binding.
- Therefore it should only be used as a child of the tabs element.
- This binding is exposed as a pseudo-public-API so themes can customize
- the tabbar appearance without having to be scriptable
- (see globalBindings.xml in Pinstripe for example).
- -->
- <binding id="tabbrowser-alltabs-popup"
- extends="chrome://global/content/bindings/popup.xml#popup">
- <implementation implements="nsIDOMEventListener">
- <field name="_xulWindow">
- null
- </field>
-
- <constructor><![CDATA[
- // We cannot cache the XULBrowserWindow object itself since it might
- // be set after this binding is constructed.
- try {
- this._xulWindow =
- window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
- .getInterface(Components.interfaces.nsIWebNavigation)
- .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
- .treeOwner
- .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
- .getInterface(Components.interfaces.nsIXULWindow);
- }
- catch(ex) { }
- ]]></constructor>
-
- <method name="_menuItemOnCommand">
- <parameter name="aEvent"/>
- <body><![CDATA[
- var tabcontainer = document.getBindingParent(this);
- tabcontainer.selectedItem = aEvent.target.tab;
- ]]></body>
- </method>
-
- <method name="_tabOnAttrModified">
- <parameter name="aEvent"/>
- <body><![CDATA[
- var menuItem = aEvent.target.mCorrespondingMenuitem;
- if (menuItem) {
- var attrName = aEvent.attrName;
- switch (attrName) {
- case "label":
- case "crop":
- case "busy":
- case "image":
- case "selected":
- if (aEvent.attrChange == aEvent.REMOVAL)
- menuItem.removeAttribute(attrName);
- else
- menuItem.setAttribute(attrName, aEvent.newValue);
- }
- }
- ]]></body>
- </method>
-
- <method name="_tabOnTabClose">
- <parameter name="aEvent"/>
- <body><![CDATA[
- var menuItem = aEvent.target.mCorrespondingMenuitem;
- if (menuItem)
- this.removeChild(menuItem);
- ]]></body>
- </method>
-
- <method name="handleEvent">
- <parameter name="aEvent"/>
- <body><![CDATA[
- if (!aEvent.isTrusted)
- return;
-
- switch (aEvent.type) {
- case "command":
- this._menuItemOnCommand(aEvent);
- break;
- case "DOMAttrModified":
- this._tabOnAttrModified(aEvent);
- break;
- case "TabClose":
- this._tabOnTabClose(aEvent);
- break;
- case "TabOpen":
- this._createTabMenuItem(aEvent.originalTarget);
- break;
- case "scroll":
- this._updateTabsVisibilityStatus();
- break;
- }
- ]]></body>
- </method>
-
- <method name="_updateTabsVisibilityStatus">
- <body><![CDATA[
- var tabContainer = document.getBindingParent(this);
- // 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++) {
- var curTabBO = this.childNodes[i].tab.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.setAttribute("label", aTab.label);
- menuItem.setAttribute("crop", aTab.getAttribute("crop"));
- menuItem.setAttribute("image", aTab.getAttribute("image"));
-
- if (aTab.hasAttribute("busy"))
- menuItem.setAttribute("busy", aTab.getAttribute("busy"));
- if (aTab.selected)
- menuItem.setAttribute("selected", "true");
-
- // Keep some attributes of the menuitem in sync with its
- // corresponding tab (e.g. the tab label)
- aTab.mCorrespondingMenuitem = menuItem;
- aTab.addEventListener("DOMAttrModified", this, false);
- aTab.addEventListener("TabClose", this, false);
- menuItem.tab = aTab;
- menuItem.addEventListener("command", this, false);
-
- this.appendChild(menuItem);
- return menuItem;
- ]]></body>
- </method>
- </implementation>
-
- <handlers>
- <handler event="popupshowing">
- <![CDATA[
- // set up the menu popup
- var tabcontainer = document.getBindingParent(this);
- var tabs = tabcontainer.childNodes;
-
- // Listen for changes in the tab bar.
- var tabbrowser = document.getBindingParent(tabcontainer);
- tabbrowser.addEventListener("TabOpen", this, false);
- tabcontainer.mTabstrip.addEventListener("scroll", this, false);
-
- // if an animation is in progress and the user
- // clicks on the "all tabs" button, stop the animation
- tabcontainer._stopAnimation();
-
- for (var i = 0; i < tabs.length; i++) {
- this._createTabMenuItem(tabs[i]);
- }
- this._updateTabsVisibilityStatus();
- ]]></handler>
-
- <handler event="popuphiding">
- <![CDATA[
- // clear out the menu popup and remove the listeners
- while (this.hasChildNodes()) {
- var menuItem = this.lastChild;
- menuItem.removeEventListener("command", this, false);
- menuItem.tab.removeEventListener("DOMAttrModified", this, false);
- menuItem.tab.removeEventListener("TabClose", this, false);
- menuItem.tab.mCorrespondingMenuitem = null;
- this.removeChild(menuItem);
- }
- var tabcontainer = document.getBindingParent(this);
- tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
- document.getBindingParent(tabcontainer).removeEventListener("TabOpen", this, false);
- ]]></handler>
-
- <handler event="DOMMenuItemActive">
- <![CDATA[
- if (!this._xulWindow || !this._xulWindow.XULBrowserWindow)
- return;
-
- var tab = event.target.tab;
- if (tab) {
- var statusText = tab.linkedBrowser.currentURI.spec;
- if (statusText == "about:blank") {
- // XXXhack: Passing a space here (and not "")
- // to make sure the the browser implementation would
- // still consider it a hovered link.
- statusText = " ";
- }
-
- this._xulWindow.XULBrowserWindow.setOverLink(statusText, null);
- }
- ]]></handler>
-
- <handler event="DOMMenuItemInactive">
- <![CDATA[
- if (!this._xulWindow || !this._xulWindow.XULBrowserWindow)
- return;
-
- this._xulWindow.XULBrowserWindow.setOverLink("", null);
- ]]></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>).
- This binding is exposed as a pseudo-public-API so themes can customize
- the tabbar appearance without having to be scriptable
- (see globalBindings.xml in Pinstripe for example).
- -->
- <binding id="tabbrowser-close-tab-button"
- extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image">
- <handlers>
- <handler event="click" button="0"><![CDATA[
- var bindingParent = document.getBindingParent(this);
- if (bindingParent) {
- var tabbedBrowser = document.getBindingParent(bindingParent);
- if (bindingParent.localName == "tab") {
- /* 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.
- */
- if (event.detail > 1 && !this._ignoredClick) {
- this._ignoredClick = true;
- return;
- }
-
- // Reset the "ignored click" flag
- this._ignoredClick = false;
-
- tabbedBrowser.removeTab(bindingParent);
- tabbedBrowser._blockDblClick = true;
-
- /* XXXmano hack (see bug 343628):
- * Since we're removing the event target, if the user
- * double-clicks this 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
- * (see onTabBarDblClick).
- */
- var clickedOnce = false;
- function enableDblClick(event) {
- if (event.detail == 1 && !clickedOnce) {
- clickedOnce = true;
- return;
- }
- setTimeout(function() {
- tabbedBrowser._blockDblClick = false;
- }, 0);
- tabbedBrowser.removeEventListener("click", enableDblClick, false);
- }
- tabbedBrowser.addEventListener("click", enableDblClick, false);
- }
- else // "tabs"
- tabbedBrowser.removeCurrentTab();
- }
- ]]></handler>
- <handler event="dblclick" button="0" phase="capturing">
- // for the one-close-button case
- event.stopPropagation();
- </handler>
- </handlers>
- </binding>
-
- <binding id="tabbrowser-tab" display="xul:hbox"
- extends="chrome://global/content/bindings/tabbox.xml#tab">
- <content chromedir="&locale.dir;"
- closetabtext="&closeTab.label;">
- <xul:image xbl:inherits="validate,src=image" class="tab-icon-image"/>
- <xul:label flex="1" xbl:inherits="value=label,crop,accesskey" class="tab-text"/>
- <xul:toolbarbutton anonid="close-button" tabindex="-1" class="tab-close-button"/>
- </content>
-
- <implementation>
- <field name="mOverCloseButton">false</field>
- <field name="mCorrespondingMenuitem">null</field>
- </implementation>
-
- <handlers>
- <handler event="mouseover">
- var anonid = event.originalTarget.getAttribute("anonid");
- if (anonid == "close-button")
- this.mOverCloseButton = true;
- </handler>
- <handler event="mouseout">
- var anonid = event.originalTarget.getAttribute("anonid");
- if (anonid == "close-button")
- this.mOverCloseButton = false;
- </handler>
- <handler event="mousedown" button="0" phase="capturing">
- <![CDATA[
- if (this.mOverCloseButton)
- event.stopPropagation();
- ]]>
- </handler>
- </handlers>
- </binding>
-
- </bindings>
-