home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / firefox-3.0.14 / chrome / browser.jar / content / browser / tabbrowser.xml < prev    next >
Encoding:
Extensible Markup Language  |  2008-04-14  |  119.2 KB  |  3,127 lines

  1. <?xml version="1.0"?>
  2.  
  3. <!-- ***** BEGIN LICENSE BLOCK *****
  4.    - Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5.    -
  6.    - The contents of this file are subject to the Mozilla Public License Version
  7.    - 1.1 (the "License"); you may not use this file except in compliance with
  8.    - the License. You may obtain a copy of the License at
  9.    - http://www.mozilla.org/MPL/
  10.    -
  11.    - Software distributed under the License is distributed on an "AS IS" basis,
  12.    - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13.    - for the specific language governing rights and limitations under the
  14.    - License.
  15.    -
  16.    - The Original Code is this file as it was released on March 28, 2001.
  17.    -
  18.    - The Initial Developer of the Original Code is
  19.    - David Hyatt.
  20.    - Portions created by the Initial Developer are Copyright (C) 2001
  21.    - the Initial Developer. All Rights Reserved.
  22.    -
  23.    - Contributor(s):
  24.    -   David Hyatt <hyatt@netscape.com> (Original Author of <tabbrowser>)
  25.    -   Mike Connor <mconnor@steelgryphon.com>
  26.    -   Peter Parente <parente@cs.unc.edu>
  27.    -   Giorgio Maone <g.maone@informaction.com>
  28.    -   Asaf Romano <mozilla.mano@sent.com>
  29.    -   Seth Spitzer <sspitzer@mozilla.org>
  30.    -   Simon B√ºnzli <zeniko@gmail.com>
  31.    -   Michael Ventnor <ventnor.bugzilla@yahoo.com.au>
  32.    -   Mark Pilgrim <pilgrim@gmail.com>
  33.    -
  34.    - Alternatively, the contents of this file may be used under the terms of
  35.    - either the GNU General Public License Version 2 or later (the "GPL"), or
  36.    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  37.    - in which case the provisions of the GPL or the LGPL are applicable instead
  38.    - of those above. If you wish to allow use of your version of this file only
  39.    - under the terms of either the GPL or the LGPL, and not to allow others to
  40.    - use your version of this file under the terms of the MPL, indicate your
  41.    - decision by deleting the provisions above and replace them with the notice
  42.    - and other provisions required by the GPL or the LGPL. If you do not delete
  43.    - the provisions above, a recipient may use your version of this file under
  44.    - the terms of any one of the MPL, the GPL or the LGPL.
  45.    -
  46.    - ***** END LICENSE BLOCK ***** -->
  47.  
  48. <!DOCTYPE bindings [
  49. <!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" >
  50. %tabBrowserDTD;
  51. <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
  52. %globalDTD;
  53. ]>
  54.  
  55. <bindings id="tabBrowserBindings"
  56.           xmlns="http://www.mozilla.org/xbl"
  57.           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  58.           xmlns:xbl="http://www.mozilla.org/xbl">
  59.  
  60.   <binding id="tabbrowser">
  61.     <resources>
  62.       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
  63.     </resources>
  64.  
  65.     <content>
  66.       <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
  67.       <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
  68.                   onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
  69.         <xul:hbox class="tab-drop-indicator-bar" collapsed="true" chromedir="&locale.dir;">
  70.           <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
  71.         </xul:hbox>
  72.         <xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
  73.                   anonid="strip"
  74.                   ondraggesture="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
  75.                   ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
  76.                   ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
  77.                   ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
  78.           <xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/>
  79.           <xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
  80.             <xul:menuitem id="context_newTab" label="&newTab.label;" accesskey="&newTab.accesskey;"
  81.                           xbl:inherits="oncommand=onnewtab"/>
  82.             <xul:menuseparator/>
  83.             <xul:menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
  84.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  85.                                      tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
  86.             <xul:menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
  87.                           tbattr="tabbrowser-multiple"
  88.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  89.                                      tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
  90.             <xul:menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
  91.                           tbattr="tabbrowser-multiple"
  92.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  93.                                      tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
  94.             <xul:menuseparator/>
  95.             <xul:menuitem id="context_bookmarkTab"
  96.                           label="&bookmarkThisTab.label;"
  97.                           accesskey="&bookmarkThisTab.accesskey;"
  98.                           oncommand="BookmarkThisTab();"/>
  99.             <xul:menuitem id="context_bookmarkAllTabs"
  100.                           label="&bookmarkAllTabs.label;"
  101.                           accesskey="&bookmarkAllTabs.accesskey;"
  102.                           command="Browser:BookmarkAllTabs"/>
  103.             <xul:menuitem id="context_undoCloseTab"
  104.                           label="&undoCloseTab.label;"
  105.                           accesskey="&undoCloseTab.accesskey;"
  106.                           command="History:UndoCloseTab"
  107.                           anonid="undoCloseTabMenuItem"/>
  108.             <xul:menuseparator/>
  109.             <xul:menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
  110.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  111.                                      tabbrowser.removeTab(tabbrowser.mContextTab);"/>
  112.           </xul:menupopup>
  113.  
  114.           <xul:tabs class="tabbrowser-tabs" flex="1"
  115.                     anonid="tabcontainer"
  116.                     setfocus="false"
  117.                     onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
  118.                     xbl:inherits="onnewtab"
  119.                     ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
  120.                     onclosetab="var node = this.parentNode;
  121.                                 while (node.localName != 'tabbrowser')
  122.                                   node = node.parentNode;
  123.                                 node.removeCurrentTab();">
  124.             <xul:tab selected="true" validate="never"
  125.                      onerror="this.removeAttribute('image');"
  126.                      maxwidth="250" width="0" minwidth="100" flex="100"
  127.                      class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
  128.           </xul:tabs>
  129.         </xul:hbox>
  130.         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
  131.           <xul:notificationbox flex="1">
  132.             <xul:browser flex="1" type="content-primary" message="true" disablehistory="true"
  133.                          xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
  134.           </xul:notificationbox>
  135.         </xul:tabpanels>
  136.       </xul:tabbox>
  137.       <children/>
  138.     </content>
  139.     <implementation>
  140.       <field name="mPrefs" readonly="true">
  141.         Components.classes['@mozilla.org/preferences-service;1']
  142.                   .getService(Components.interfaces.nsIPrefService)
  143.                   .getBranch(null);
  144.       </field>
  145.       <field name="mURIFixup" readonly="true">
  146.         Components.classes["@mozilla.org/docshell/urifixup;1"]
  147.                   .getService(Components.interfaces.nsIURIFixup);
  148.       </field>
  149.       <field name="mFaviconService" readonly="true">
  150.         Components.classes["@mozilla.org/browser/favicon-service;1"]
  151.                   .getService(Components.interfaces.nsIFaviconService);
  152.       </field>
  153.       <field name="mTabBox" readonly="true">
  154.         document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
  155.       </field>
  156.       <field name="mTabDropIndicatorBar">
  157.         this.mTabBox.childNodes[0]
  158.       </field>
  159.       <field name="mStrip" readonly="true">
  160.         document.getAnonymousElementByAttribute(this, "anonid", "strip");
  161.       </field>
  162.       <field name="mTabContainer" readonly="true">
  163.         document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
  164.       </field>
  165.       <field name="mPanelContainer" readonly="true">
  166.         document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
  167.       </field>
  168.       <field name="mTabs" readonly="true">
  169.         this.mTabContainer.childNodes
  170.       </field>
  171.       <field name="mStringBundle">
  172.         document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
  173.       </field>
  174.       <field name="mUndoCloseTabMenuItem">
  175.         document.getAnonymousElementByAttribute(this, "anonid", "undoCloseTabMenuItem");
  176.       </field>
  177.       <field name="mCurrentTab">
  178.         null
  179.       </field>
  180.       <field name="mCurrentBrowser">
  181.         null
  182.       </field>
  183.       <field name="mProgressListeners">
  184.         []
  185.       </field>
  186.       <field name="mTabListeners">
  187.         new Array()
  188.       </field>
  189.       <field name="mTabFilters">
  190.         new Array()
  191.       </field>
  192.       <field name="mTabbedMode">
  193.         false
  194.       </field>
  195.       <field name="mIsBusy">
  196.         false
  197.       </field>
  198.       <field name="mContextTab">
  199.         null
  200.       </field>
  201.       <field name="arrowKeysShouldWrap" readonly="true">
  202.         false
  203.       </field>
  204.       <field name="mAddProgressListenerWasCalled">
  205.         false
  206.       </field>
  207.       <field name="_browsers">
  208.         null
  209.       </field>
  210.  
  211.       <field name="_blockDblClick">
  212.         false
  213.       </field>
  214.       <field name="_autoScrollPopup">
  215.         null
  216.       </field>
  217.  
  218.       <method name="getBrowserAtIndex">
  219.         <parameter name="aIndex"/>
  220.         <body>
  221.           <![CDATA[
  222.             return this.browsers[aIndex];
  223.           ]]>
  224.         </body>
  225.       </method>
  226.  
  227.       <method name="getBrowserIndexForDocument">
  228.         <parameter name="aDocument"/>
  229.         <body>
  230.           <![CDATA[
  231.         var browsers = this.browsers;
  232.             for (var i = 0; i < browsers.length; i++)
  233.               if (browsers[i].contentDocument == aDocument)
  234.                 return i;
  235.             return -1;
  236.           ]]>
  237.         </body>
  238.       </method>
  239.  
  240.       <method name="getBrowserForDocument">
  241.         <parameter name="aDocument"/>
  242.         <body>
  243.           <![CDATA[
  244.             var index = this.getBrowserIndexForDocument(aDocument);
  245.             if (index < 0)
  246.               return null;
  247.             return this.getBrowserAtIndex(index);
  248.           ]]>
  249.         </body>
  250.       </method>
  251.  
  252.       <method name="getNotificationBox">
  253.         <parameter name="aBrowser"/>
  254.         <body>
  255.           <![CDATA[
  256.             if (aBrowser)
  257.               return aBrowser.parentNode;
  258.             else if (this.mCurrentBrowser)
  259.               return this.mCurrentBrowser.parentNode;
  260.             return null;
  261.           ]]>
  262.         </body>
  263.       </method>
  264.  
  265.       <!-- A web progress listener object definition for a given tab. -->
  266.       <method name="mTabProgressListener">
  267.         <parameter name="aTab"/>
  268.         <parameter name="aBrowser"/>
  269.         <parameter name="aStartsBlank"/>
  270.         <body>
  271.         <![CDATA[
  272.           return ({
  273.             mTabBrowser: this,
  274.             mTab: aTab,
  275.             mBrowser: aBrowser,
  276.             mBlank: aStartsBlank,
  277.             mLastURI: null,
  278.  
  279.             // cache flags for correct status bar update after tab switching
  280.             mStateFlags: 0,
  281.             mStatus: 0,
  282.             mMessage: "",
  283.             mTotalProgress: 0,
  284.  
  285.             // count of open requests (should always be 0 or 1)
  286.             mRequestCount: 0,
  287.  
  288.             onProgressChange : function (aWebProgress, aRequest,
  289.                                          aCurSelfProgress, aMaxSelfProgress,
  290.                                          aCurTotalProgress, aMaxTotalProgress)
  291.             {
  292.               if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
  293.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  294.                   var p = this.mTabBrowser.mProgressListeners[i];
  295.                   if (p)
  296.                     try {
  297.                       p.onProgressChange(aWebProgress, aRequest,
  298.                                          aCurSelfProgress, aMaxSelfProgress,
  299.                                          aCurTotalProgress, aMaxTotalProgress);
  300.                     } catch (e) {
  301.                       // don't inhibit other listeners or following code
  302.                       Components.utils.reportError(e);
  303.                     }
  304.                 }
  305.               }
  306.  
  307.               this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
  308.             },
  309.  
  310.             onProgressChange64 : function (aWebProgress, aRequest,
  311.                                          aCurSelfProgress, aMaxSelfProgress,
  312.                                          aCurTotalProgress, aMaxTotalProgress)
  313.             {
  314.               return this.onProgressChange(aWebProgress, aRequest,
  315.                 aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
  316.                 aMaxTotalProgress);
  317.             },
  318.  
  319.             onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
  320.             {
  321.               if (!aRequest)
  322.                 return;
  323.  
  324.               var oldBlank = this.mBlank;
  325.  
  326.               const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  327.               const nsIChannel = Components.interfaces.nsIChannel;
  328.  
  329.               if (aStateFlags & nsIWebProgressListener.STATE_START) {
  330.                 this.mRequestCount++;
  331.               }
  332.               else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
  333.                 const NS_ERROR_UNKNOWN_HOST = 2152398878;
  334.                 if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
  335.                   // to prevent bug 235825: wait for the request handled
  336.                   // by the automatic keyword resolver
  337.                   return;
  338.                 }
  339.                 // since we (try to) only handle STATE_STOP of the last request,
  340.                 // the count of open requests should now be 0
  341.                 this.mRequestCount = 0;
  342.               }
  343.  
  344.               if (aStateFlags & nsIWebProgressListener.STATE_START &&
  345.                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
  346.                 // It's okay to clear what the user typed when we start
  347.                 // loading a document. If the user types, this counter gets
  348.                 // set to zero, if the document load ends without an
  349.                 // onLocationChange, this counter gets decremented
  350.                 // (so we keep it while switching tabs after failed loads)
  351.                 // We need to add 2 because loadURIWithFlags may have
  352.                 // cancelled a pending load which would have cleared
  353.                 // its anchor scroll detection temporary increment.
  354.                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
  355.                   this.mBrowser.userTypedClear += 2;
  356.  
  357.                 if (!this.mBlank) {
  358.                   this.mTab.setAttribute("busy", "true");
  359.                   this.mTabBrowser.updateIcon(this.mTab);
  360.                   this.mTabBrowser.setTabTitleLoading(this.mTab);
  361.  
  362.                   if (this.mTabBrowser.mCurrentTab == this.mTab)
  363.                     this.mTabBrowser.mIsBusy = true;
  364.                 }
  365.               }
  366.               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
  367.                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
  368.                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow) {
  369.                   // The document is done loading, we no longer want the
  370.                   // value cleared.
  371.                   if (this.mBrowser.userTypedClear > 1)
  372.                     this.mBrowser.userTypedClear -= 2;
  373.                   else if (this.mBrowser.userTypedClear > 0)
  374.                     this.mBrowser.userTypedClear--;
  375.  
  376.                   if (!this.mBrowser.mIconURL)
  377.                     this.mTabBrowser.useDefaultIcon(this.mTab);
  378.                 }
  379.  
  380.                 if (this.mBlank)
  381.                   this.mBlank = false;
  382.  
  383.                 this.mTab.removeAttribute("busy");
  384.                 this.mTabBrowser.updateIcon(this.mTab);
  385.  
  386.                 var location = aRequest.QueryInterface(nsIChannel).URI;
  387.  
  388.                 // For keyword URIs clear the user typed value since they will be changed into real URIs
  389.                 if (location.scheme == "keyword")
  390.                   this.mBrowser.userTypedValue = null;
  391.  
  392.                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
  393.                   this.mTabBrowser.setTabTitle(this.mTab);
  394.  
  395.                 if (this.mTabBrowser.mCurrentTab == this.mTab)
  396.                   this.mTabBrowser.mIsBusy = false;
  397.               }
  398.  
  399.               if (this.mTabBrowser.mCurrentTab == this.mTab) {
  400.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  401.                   var p = this.mTabBrowser.mProgressListeners[i];
  402.                   if (p)
  403.                     try {
  404.                       if (!oldBlank)
  405.                         p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
  406.                       // make sure that the visible status of new blank tabs is correctly set
  407.                       else if ("onUpdateCurrentBrowser" in p)
  408.                         p.onUpdateCurrentBrowser(aStateFlags, aStatus, "", 0);
  409.                     } catch (e) {
  410.                       // don't inhibit other listeners or following code
  411.                       Components.utils.reportError(e);
  412.                     }
  413.                 }
  414.               }
  415.  
  416.               if (aStateFlags & (nsIWebProgressListener.STATE_START |
  417.                                  nsIWebProgressListener.STATE_STOP)) {
  418.                 // reset cached temporary values at beginning and end
  419.                 this.mMessage = "";
  420.                 this.mTotalProgress = 0;
  421.               }
  422.               this.mStateFlags = aStateFlags;
  423.               this.mStatus = aStatus;
  424.             },
  425.  
  426.             onLocationChange : function(aWebProgress, aRequest, aLocation)
  427.             {
  428.               // The document loaded correctly, clear the value if we should
  429.               if (this.mBrowser.userTypedClear > 0)
  430.                 this.mBrowser.userTypedValue = null;
  431.  
  432.               if (aWebProgress.DOMWindow == this.mBrowser.contentWindow &&
  433.                   aWebProgress.isLoadingDocument)
  434.                 this.mTabBrowser.setIcon(this.mTab, null);
  435.  
  436.               // changing location, clear out the missing plugins list
  437.               this.mBrowser.missingPlugins = null;
  438.  
  439.               if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
  440.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  441.                   var p = this.mTabBrowser.mProgressListeners[i];
  442.                   if (p)
  443.                     try {
  444.                       p.onLocationChange(aWebProgress, aRequest, aLocation);
  445.                     } catch (e) {
  446.                       // don't inhibit other listeners
  447.                       Components.utils.reportError(e);
  448.                     }
  449.                 }
  450.               }
  451.             },
  452.  
  453.             onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
  454.             {
  455.               if (this.mBlank)
  456.                 return;
  457.  
  458.               if (this.mTabBrowser.mCurrentTab == this.mTab) {
  459.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  460.                   var p = this.mTabBrowser.mProgressListeners[i];
  461.                   if (p)
  462.                     try {
  463.                       p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
  464.                     } catch (e) {
  465.                       // don't inhibit other listeners or following code
  466.                       Components.utils.reportError(e);
  467.                     }
  468.                 }
  469.               }
  470.  
  471.               this.mMessage = aMessage;
  472.             },
  473.  
  474.             onSecurityChange : function(aWebProgress, aRequest, aState)
  475.             {
  476.               if (this.mTabBrowser.mCurrentTab == this.mTab) {
  477.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  478.                   var p = this.mTabBrowser.mProgressListeners[i];
  479.                   if (p)
  480.                     try {
  481.                       p.onSecurityChange(aWebProgress, aRequest, aState);
  482.                     } catch (e) {
  483.                       // don't inhibit other listeners
  484.                       Components.utils.reportError(e);
  485.                     }
  486.                 }
  487.               }
  488.             },
  489.  
  490.              onRefreshAttempted : function(aWebProgress, aURI, aDelay, aSameURI)
  491.              {
  492.                var allowRefresh = true;
  493.                for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  494.                  var p = this.mTabBrowser.mProgressListeners[i];
  495.                  if (p && "onRefreshAttempted" in p) {
  496.                    try {
  497.                      if (!p.onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI))
  498.                        allowRefresh = false;
  499.                     } catch (e) {
  500.                       // don't inhibit other listeners or following code
  501.                       Components.utils.reportError(e);
  502.                     }
  503.                  }
  504.                }
  505.                return allowRefresh;
  506.              },
  507.  
  508.             QueryInterface : function(aIID)
  509.             {
  510.               if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  511.                   aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
  512.                   aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  513.                   aIID.equals(Components.interfaces.nsISupports))
  514.                 return this;
  515.               throw Components.results.NS_NOINTERFACE;
  516.             }
  517.           });
  518.         ]]>
  519.         </body>
  520.       </method>
  521.  
  522.       <method name="setIcon">
  523.         <parameter name="aTab"/>
  524.         <parameter name="aURI"/>
  525.         <body>
  526.           <![CDATA[
  527.             var browser = this.getBrowserForTab(aTab);
  528.             browser.mIconURL = aURI;
  529.  
  530.             if (aURI) {
  531.               if (!(aURI instanceof Components.interfaces.nsIURI)) {
  532.                 var ios = Components.classes["@mozilla.org/network/io-service;1"]
  533.                                     .getService(Components.interfaces.nsIIOService);
  534.                 aURI = ios.newURI(aURI, null, null);
  535.               }
  536.               this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
  537.                                                             aURI, false);
  538.             }
  539.  
  540.             this.updateIcon(aTab);
  541.  
  542.             for (var i = 0; i < this.mProgressListeners.length; i++) {
  543.               var p = this.mProgressListeners[i];
  544.               if ('onLinkIconAvailable' in p)
  545.                 try {
  546.                   p.onLinkIconAvailable(browser);
  547.                 } catch (e) {
  548.                   // don't inhibit other listeners
  549.                   Components.utils.reportError(e);
  550.                 }
  551.             }
  552.           ]]>
  553.         </body>
  554.       </method>
  555.  
  556.       <method name="updateIcon">
  557.         <parameter name="aTab"/>
  558.         <body>
  559.           <![CDATA[
  560.             var browser = this.getBrowserForTab(aTab);
  561.             if (!aTab.hasAttribute("busy") && browser.mIconURL)
  562.               aTab.setAttribute("image", browser.mIconURL);
  563.             else
  564.               aTab.removeAttribute("image");
  565.           ]]>
  566.         </body>
  567.       </method>
  568.  
  569.       <method name="shouldLoadFavIcon">
  570.         <parameter name="aURI"/>
  571.         <body>
  572.           <![CDATA[
  573.             return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
  574.                     this.mPrefs.getBoolPref("browser.chrome.favicons") &&
  575.                     ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
  576.           ]]>
  577.         </body>
  578.       </method>
  579.  
  580.       <method name="useDefaultIcon">
  581.         <parameter name="aTab"/>
  582.         <body>
  583.           <![CDATA[
  584.             var browser = this.getBrowserForTab(aTab);
  585.             if (browser.contentDocument instanceof ImageDocument) {
  586.               if (this.mPrefs.getBoolPref("browser.chrome.site_icons")) {
  587.                 try {
  588.                   var sz = this.mPrefs.getIntPref("browser.chrome.image_icons.max_size");
  589.                   if (!sz)
  590.                     return;
  591.  
  592.                   var req = browser.contentDocument.imageRequest;
  593.                   if (!req || !req.image ||
  594.                       req.image.width > sz ||
  595.                       req.image.height > sz)
  596.                     return;
  597.  
  598.                   this.setIcon(aTab, browser.currentURI.spec);
  599.                 } catch (e) { }
  600.               }
  601.             }
  602.             else if (this.shouldLoadFavIcon(browser.currentURI)) {
  603.               var url = browser.currentURI.prePath + "/favicon.ico";
  604.               if (!this.isFailedIcon(url))
  605.                 this.setIcon(aTab, url);
  606.             }
  607.           ]]>
  608.         </body>
  609.       </method>
  610.  
  611.       <method name="isFailedIcon">
  612.         <parameter name="aURI"/>
  613.         <body>
  614.           <![CDATA[
  615.             if (!(aURI instanceof Components.interfaces.nsIURI)) {
  616.               var ios = Components.classes["@mozilla.org/network/io-service;1"]
  617.                                   .getService(Components.interfaces.nsIIOService);
  618.               aURI = ios.newURI(aURI, null, null);
  619.             }
  620.  
  621.             return this.mFaviconService.isFailedFavicon(aURI);
  622.           ]]>
  623.         </body>
  624.       </method>
  625.  
  626.       <method name="updateTitlebar">
  627.         <body>
  628.           <![CDATA[
  629.             var newTitle = "";
  630.             var docTitle;
  631.             var docElement = this.ownerDocument.documentElement;
  632.             var sep = docElement.getAttribute("titlemenuseparator");
  633.  
  634.             if (this.docShell.contentViewer)
  635.               docTitle = this.contentTitle;
  636.  
  637.             if (!docTitle)
  638.               docTitle = docElement.getAttribute("titledefault");
  639.  
  640.             var modifier = docElement.getAttribute("titlemodifier");
  641.             if (docTitle) {
  642.               newTitle += docElement.getAttribute("titlepreface");
  643.               newTitle += docTitle;
  644.               if (modifier)
  645.                 newTitle += sep;
  646.             }
  647.             newTitle += modifier;
  648.  
  649.             // If location bar is hidden and the URL type supports a host,
  650.             // add the scheme and host to the title to prevent spoofing.
  651.             // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
  652.             try {
  653.               if (docElement.getAttribute("chromehidden").indexOf("location") != -1) {
  654.                 var uri = this.mURIFixup.createExposableURI(
  655.                             this.mCurrentBrowser.currentURI);
  656.                 if (uri.scheme == "about")
  657.                   newTitle = uri.spec + sep + newTitle;
  658.                 else
  659.                   newTitle = uri.prePath + sep + newTitle;
  660.               }
  661.             } catch (e) {}
  662.  
  663.             this.ownerDocument.title = newTitle;
  664.           ]]>
  665.         </body>
  666.       </method>
  667.  
  668.       <method name="updatePopupMenu">
  669.         <parameter name="aPopupMenu"/>
  670.         <body>
  671.           <![CDATA[
  672.             this.mContextTab = document.popupNode;
  673.             var disabled = this.mPanelContainer.childNodes.length == 1;
  674.             var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
  675.             for (var i = 0; i < menuItems.length; i++)
  676.               menuItems[i].disabled = disabled;
  677.  
  678.             // Session store
  679.             if (gPrefService.getBoolPref("browser.sessionstore.enabled")) {
  680.               this.mUndoCloseTabMenuItem.hidden =
  681.                 Cc["@mozilla.org/browser/sessionstore;1"].
  682.                 getService(Ci.nsISessionStore).
  683.                 getClosedTabCount(window) == 0;
  684.             }
  685.             else
  686.               this.mUndoCloseTabMenuItem.hidden = true;
  687.           ]]>
  688.         </body>
  689.       </method>
  690.  
  691.       <method name="updateCurrentBrowser">
  692.         <body>
  693.           <![CDATA[
  694.             var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
  695.             if (this.mCurrentBrowser == newBrowser)
  696.               return;
  697.  
  698.             if (this.mCurrentBrowser) {
  699.               // Only save the focused element if it is in our content window
  700.               // or in an ancestor window.
  701.               var focusedWindow = document.commandDispatcher.focusedWindow;
  702.               var saveFocus = false;
  703.  
  704.               if (focusedWindow && focusedWindow.top == window.content) {
  705.                 saveFocus = true;
  706.               } else {
  707.                 var contentWindow = window;
  708.  
  709.                 while (contentWindow) {
  710.                   if (contentWindow == focusedWindow) {
  711.                     saveFocus = true;
  712.                     break;
  713.                   }
  714.  
  715.                   if (contentWindow.parent == contentWindow) {
  716.                     break;
  717.                   }
  718.  
  719.                   contentWindow = contentWindow.parent;
  720.                 }
  721.               }
  722.  
  723.               if (saveFocus) {
  724.                 // Preserve the currently-focused element or DOM window for
  725.                 // this tab.
  726.  
  727.                 this.mCurrentBrowser.focusedWindow = focusedWindow;
  728.                 this.mCurrentBrowser.focusedElement = document.commandDispatcher.focusedElement;
  729.               }
  730.  
  731.               if (this.mCurrentBrowser.focusedElement &&
  732.                   this.mCurrentBrowser.focusedElement.parentNode !=
  733.                   this.mCurrentTab.parentNode) {
  734.                 // Clear focus outline before we draw on top of it.
  735.                 // Only blur the focused element if it isn't a tab, 
  736.                 // to avoid breaking keyboard tab navigation
  737.                 var elem = this.mCurrentBrowser.focusedElement;
  738.                 if (elem instanceof HTMLElement || elem instanceof XULElement) {
  739.                   elem.blur();
  740.                 }
  741.                 else {
  742.                   var content = elem.ownerDocument.defaultView;
  743.                   if (content instanceof Components.interfaces.nsIInterfaceRequestor)
  744.                     content.getInterface(Components.interfaces.nsIDOMWindowUtils).focus(null);
  745.                 }
  746.               }
  747.               this.mCurrentBrowser.setAttribute("type", "content-targetable");
  748.             }
  749.  
  750.             var updatePageReport = false;
  751.             if (!this.mCurrentBrowser ||
  752.                 (this.mCurrentBrowser.pageReport && !newBrowser.pageReport) ||
  753.                 (!this.mCurrentBrowser.pageReport && newBrowser.pageReport))
  754.               updatePageReport = true;
  755.  
  756.             newBrowser.setAttribute("type", "content-primary");
  757.             this.mCurrentBrowser = newBrowser;
  758.             this.mCurrentTab = this.selectedTab;
  759.  
  760.             if (updatePageReport)
  761.               this.mCurrentBrowser.updatePageReport();
  762.  
  763.             // Update the URL bar.
  764.             var loc = this.mCurrentBrowser.currentURI;
  765.  
  766.             var webProgress = this.mCurrentBrowser.webProgress;
  767.             var securityUI = this.mCurrentBrowser.securityUI;
  768.  
  769.             var i, p;
  770.             for (i = 0; i < this.mProgressListeners.length; i++) {
  771.               p = this.mProgressListeners[i];
  772.               if (p)
  773.                 try {
  774.                   p.onLocationChange(webProgress, null, loc);
  775.                   if (securityUI)
  776.                     p.onSecurityChange(webProgress, null, securityUI.state);
  777.  
  778.                   // make sure that all status indicators are properly updated
  779.                   if ("onUpdateCurrentBrowser" in p) {
  780.                     var listener = this.mTabListeners[this.mTabContainer.selectedIndex] || null;
  781.                     if (listener && listener.mStateFlags)
  782.                       p.onUpdateCurrentBrowser(listener.mStateFlags, listener.mStatus,
  783.                                                listener.mMessage, listener.mTotalProgress);
  784.                   }
  785.                 } catch (e) {
  786.                   // don't inhibit other listeners or following code
  787.                   Components.utils.reportError(e);
  788.                 }
  789.             }
  790.  
  791.             this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
  792.  
  793.             // Update the window title.
  794.             this.updateTitlebar();
  795.  
  796.             // If the new tab is busy, and our current state is not busy, then
  797.             // we need to fire a start to all progress listeners.
  798.             const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  799.             if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
  800.               this.mIsBusy = true;
  801.               webProgress = this.mCurrentBrowser.webProgress;
  802.               for (i = 0; i < this.mProgressListeners.length; i++) {
  803.                 p = this.mProgressListeners[i];
  804.                 if (p)
  805.                   try {
  806.                     p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
  807.                   } catch (e) {
  808.                     // don't inhibit other listeners or following code
  809.                     Components.utils.reportError(e);
  810.                   }
  811.               }
  812.             }
  813.  
  814.             // If the new tab is not busy, and our current state is busy, then
  815.             // we need to fire a stop to all progress listeners.
  816.             if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
  817.               this.mIsBusy = false;
  818.               webProgress = this.mCurrentBrowser.webProgress;
  819.               for (i = 0; i < this.mProgressListeners.length; i++) {
  820.                 p = this.mProgressListeners[i];
  821.                 if (p)
  822.                   try {
  823.                     p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
  824.                   } catch (e) {
  825.                     // don't inhibit other listeners or following code
  826.                     Components.utils.reportError(e);
  827.                   }
  828.               }
  829.             }
  830.  
  831.             // We've selected the new tab, so go ahead and notify listeners.
  832.             var event = document.createEvent("Events");
  833.             event.initEvent("TabSelect", true, false);
  834.             this.mCurrentTab.dispatchEvent(event);
  835.  
  836.             if (document.commandDispatcher.focusedElement &&
  837.                 document.commandDispatcher.focusedElement.parentNode ==
  838.                 this.mCurrentTab.parentNode) {
  839.               // The focus is on a tab in the same tab panel
  840.               return;  // If focus was on a tab, switching tabs focuses the new tab
  841.             }
  842.  
  843.             var whatToFocus = window.content;
  844.  
  845.             // Focus the previously focused element or window, but make sure
  846.             // the focused element is still part of the document
  847.             let focusedElem = newBrowser.focusedElement;
  848.             if (focusedElem && focusedElem.ownerDocument &&
  849.                 !(focusedElem.ownerDocument.compareDocumentPosition(focusedElem) &
  850.                   Node.DOCUMENT_POSITION_DISCONNECTED)) {
  851.               if (newBrowser.focusedElement.parentNode !=
  852.                   this.mCurrentTab.parentNode) {
  853.                 // Focus the remembered element unless it's in the current tab panel
  854.                 whatToFocus = newBrowser.focusedElement;
  855.               }
  856.             }
  857.             else if (newBrowser.focusedWindow) {
  858.               whatToFocus = newBrowser.focusedWindow;
  859.             }
  860.  
  861.             // Change focus for this window to |whatToFocus|, without
  862.             // focusing the window itself.
  863.             var cmdDispatcher = document.commandDispatcher;
  864.  
  865.             var ww =
  866.               Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  867.                         .getService(Components.interfaces.nsIWindowWatcher);
  868.             if (ww.activeWindow == window) {
  869.               cmdDispatcher.suppressFocusScroll = true;
  870.               if (whatToFocus instanceof HTMLElement ||
  871.                   whatToFocus instanceof XULElement ||
  872.                   whatToFocus instanceof Window) {
  873.                 whatToFocus.focus();
  874.               }
  875.               else if (whatToFocus instanceof Node) {
  876.                 var content = window.content;
  877.                 if (content instanceof Components.interfaces.nsIInterfaceRequestor)
  878.                   content.getInterface(Components.interfaces.nsIDOMWindowUtils).focus(whatToFocus);
  879.               }
  880.               cmdDispatcher.suppressFocusScroll = false;
  881.             }
  882.             else {
  883.               // set the element in command dispatcher so focus will restore
  884.               // properly when the window does become active
  885.               if (whatToFocus instanceof Window) {
  886.                 cmdDispatcher.focusedWindow = whatToFocus;
  887.                 cmdDispatcher.focusedElement = null;
  888.               }
  889.               else {
  890.                 cmdDispatcher.focusedWindow = whatToFocus.ownerDocument.defaultView;
  891.                 cmdDispatcher.focusedElement = whatToFocus;
  892.               }
  893.             }
  894.           ]]>
  895.         </body>
  896.       </method>
  897.  
  898.       <method name="onTabClick">
  899.         <parameter name="event"/>
  900.         <body>
  901.           <![CDATA[
  902.             if (event.button != 1 || event.target.localName != 'tab')
  903.               return;
  904.  
  905.             this.removeTab(event.target);
  906.             event.stopPropagation();
  907.           ]]>
  908.         </body>
  909.       </method>
  910.  
  911.       <method name="onTitleChanged">
  912.         <parameter name="evt"/>
  913.         <body>
  914.           <![CDATA[
  915.             if (evt.target != this.contentDocument)
  916.               return;
  917.  
  918.             var i = 0;
  919.             for ( ; i < this.parentNode.parentNode.childNodes.length; i++) {
  920.               if (this.parentNode.parentNode.childNodes[i].firstChild == this)
  921.                 break;
  922.             }
  923.  
  924.             var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;
  925.  
  926.             var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id);
  927.             tabBrowser.setTabTitle(tab);
  928.  
  929.             if (tab == tabBrowser.mCurrentTab)
  930.               tabBrowser.updateTitlebar();
  931.           ]]>
  932.         </body>
  933.       </method>
  934.  
  935.       <method name="setTabTitleLoading">
  936.         <parameter name="aTab"/>
  937.         <body>
  938.           <![CDATA[
  939.             aTab.label = this.mStringBundle.getString("tabs.loading");
  940.             aTab.setAttribute("crop", "end");
  941.           ]]>
  942.         </body>
  943.       </method>
  944.  
  945.       <method name="setTabTitle">
  946.         <parameter name="aTab"/>
  947.         <body>
  948.           <![CDATA[
  949.             var browser = this.getBrowserForTab(aTab);
  950.             var crop = "end";
  951.             var title = browser.contentTitle;
  952.  
  953.             if (!title) {
  954.               if (browser.currentURI.spec) {
  955.                 try {
  956.                   title = this.mURIFixup.createExposableURI(browser.currentURI).spec;
  957.                 } catch(ex) {
  958.                   title = browser.currentURI.spec;
  959.                 }
  960.               }
  961.  
  962.               if (title && title != "about:blank") {
  963.                 // At this point, we now have a URI.
  964.                 // Let's try to unescape it using a character set
  965.                 // in case the URI is not ASCII.
  966.                 try {
  967.                   var characterSet = browser.contentDocument.characterSet;
  968.                   const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  969.                                                  .getService(Components.interfaces.nsITextToSubURI);
  970.                   title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
  971.                 } catch(ex) { /* Do nothing. */ }
  972.  
  973.                 crop = "center";
  974.  
  975.               } else // Still no title?  Fall back to our untitled string.
  976.                 title = this.mStringBundle.getString("tabs.untitled");
  977.             }
  978.  
  979.             aTab.label = title;
  980.             aTab.setAttribute("crop", crop);
  981.           ]]>
  982.         </body>
  983.       </method>
  984.  
  985.       <method name="setStripVisibilityTo">
  986.         <parameter name="aShow"/>
  987.         <body>
  988.         <![CDATA[
  989.           this.mStrip.collapsed = !aShow;
  990.           if (aShow) {
  991.             // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
  992.             document.getElementById("menu_closeWindow").hidden = false;
  993.             document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
  994.             if (!this.mTabbedMode)
  995.               this.enterTabbedMode();
  996.           }
  997.           else {
  998.             // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
  999.             document.getElementById("menu_closeWindow").hidden = true;
  1000.             document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
  1001.           }
  1002.         ]]>
  1003.         </body>
  1004.       </method>
  1005.  
  1006.       <method name="getStripVisibility">
  1007.         <body>
  1008.           return !this.mStrip.collapsed;
  1009.         </body>
  1010.       </method>
  1011.  
  1012.       <method name="enterTabbedMode">
  1013.         <body>
  1014.           <![CDATA[
  1015.             this.mTabbedMode = true; // Welcome to multi-tabbed mode.
  1016.  
  1017.             // Get the first tab all hooked up with a title listener and popup blocking listener.
  1018.             this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
  1019.  
  1020.             var throbberElement = document.getElementById("navigator-throbber");
  1021.             if (throbberElement && throbberElement.hasAttribute("busy")) {
  1022.               this.mCurrentTab.setAttribute("busy", "true");
  1023.               this.mIsBusy = true;
  1024.               this.setTabTitleLoading(this.mCurrentTab);
  1025.               this.updateIcon(this.mCurrentTab);
  1026.             } else {
  1027.               this.setTabTitle(this.mCurrentTab);
  1028.               this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
  1029.             }
  1030.  
  1031.             var filter;
  1032.             if (this.mTabFilters.length > 0) {
  1033.               // Use the filter hooked up in our addProgressListener
  1034.               filter = this.mTabFilters[0];
  1035.             } else {
  1036.               // create a filter and hook it up to our first browser
  1037.               filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  1038.                                  .createInstance(Components.interfaces.nsIWebProgress);
  1039.               this.mTabFilters[0] = filter;
  1040.               this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1041.             }
  1042.  
  1043.             // Remove all our progress listeners from the active browser's filter.
  1044.             for (var i = 0; i < this.mProgressListeners.length; i++) {
  1045.               var p = this.mProgressListeners[i];
  1046.               if (p)
  1047.                 filter.removeProgressListener(p);
  1048.             }
  1049.  
  1050.             // Wire up a progress listener to our filter.
  1051.             const listener = this.mTabProgressListener(this.mCurrentTab,
  1052.                                                        this.mCurrentBrowser,
  1053.                                                        false);
  1054.             filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1055.             this.mTabListeners[0] = listener;
  1056.           ]]>
  1057.         </body>
  1058.       </method>
  1059.  
  1060.       <method name="loadOneTab">
  1061.         <parameter name="aURI"/>
  1062.         <parameter name="aReferrerURI"/>
  1063.         <parameter name="aCharset"/>
  1064.         <parameter name="aPostData"/>
  1065.         <parameter name="aLoadInBackground"/>
  1066.         <parameter name="aAllowThirdPartyFixup"/>
  1067.         <body>
  1068.           <![CDATA[
  1069.             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
  1070.                          this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
  1071.             var owner = bgLoad ? null : this.selectedTab;
  1072.             var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner,
  1073.                                   aAllowThirdPartyFixup);
  1074.             if (!bgLoad)
  1075.               this.selectedTab = tab;
  1076.  
  1077.             return tab;
  1078.          ]]>
  1079.         </body>
  1080.       </method>
  1081.  
  1082.       <method name="loadTabs">
  1083.         <parameter name="aURIs"/>
  1084.         <parameter name="aLoadInBackground"/>
  1085.         <parameter name="aReplace"/>
  1086.         <body><![CDATA[
  1087.           // The tab selected after this new tab is closed (i.e. the new tab's
  1088.           // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
  1089.           // when several urls are opened here (i.e. closing the first should select
  1090.           // the next of many URLs opened) or if the pref to have UI links opened in
  1091.           // the background is set (i.e. the link is not being opened modally)
  1092.           //
  1093.           // i.e.
  1094.           //    Number of URLs    Load UI Links in BG       Focus Last Viewed?
  1095.           //    == 1              false                     YES
  1096.           //    == 1              true                      NO
  1097.           //    > 1               false/true                NO
  1098.           var owner = (aURIs.length > 1) || aLoadInBackground ? null : gBrowser.selectedTab;
  1099.           var firstTabAdded = null;
  1100.           if (aReplace)
  1101.             this.loadURI(aURIs[0], null, null);
  1102.           else
  1103.             firstTabAdded = gBrowser.addTab(aURIs[0], null, null, null, owner, false);
  1104.  
  1105.           var tabNum = this.mTabContainer.selectedIndex;
  1106.           for (var i = 1; i < aURIs.length; ++i) {
  1107.             var tab = gBrowser.addTab(aURIs[i]);
  1108.             if (aReplace)
  1109.               this.moveTabTo(tab, ++tabNum);
  1110.           }
  1111.  
  1112.           if (!aLoadInBackground) {
  1113.             if (firstTabAdded) {
  1114.               // .selectedTab setter focuses the content area
  1115.               this.selectedTab = firstTabAdded;
  1116.             }
  1117.             else
  1118.               window.content.focus();
  1119.           }
  1120.         ]]></body>
  1121.       </method>
  1122.  
  1123.       <method name="addTab">
  1124.         <parameter name="aURI"/>
  1125.         <parameter name="aReferrerURI"/>
  1126.         <parameter name="aCharset"/>
  1127.         <parameter name="aPostData"/>
  1128.         <parameter name="aOwner"/>
  1129.         <parameter name="aAllowThirdPartyFixup"/>
  1130.         <body>
  1131.           <![CDATA[
  1132.             this._browsers = null; // invalidate cache
  1133.  
  1134.             if (!this.mTabbedMode)
  1135.               this.enterTabbedMode();
  1136.  
  1137.             // if we're adding tabs, we're past interrupt mode, ditch the owner
  1138.             if (this.mCurrentTab.owner)
  1139.               this.mCurrentTab.owner = null;
  1140.  
  1141.             var t = document.createElementNS(
  1142.               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  1143.                                              "tab");
  1144.  
  1145.             var blank = (aURI == "about:blank");
  1146.  
  1147.             if (blank)
  1148.               t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
  1149.             else
  1150.               t.setAttribute("label", aURI);
  1151.  
  1152.             t.setAttribute("crop", "end");
  1153.             t.maxWidth = this.mTabContainer.mTabMaxWidth;
  1154.             t.minWidth = this.mTabContainer.mTabMinWidth;
  1155.             t.width = 0;
  1156.             t.setAttribute("flex", "100");
  1157.             t.setAttribute("validate", "never");
  1158.             t.setAttribute("onerror", "this.removeAttribute('image');");
  1159.             t.className = "tabbrowser-tab";
  1160.  
  1161.             this.mTabContainer.appendChild(t);
  1162.  
  1163.             if (document.defaultView
  1164.                         .getComputedStyle(this.mTabContainer, "")
  1165.                         .direction == "rtl") {
  1166.               /* In RTL UI, the tab is visually added to the left side of the
  1167.                * tabstrip. This means the tabstip has to be scrolled back in
  1168.                * order to make sure the same set of tabs is visible before and
  1169.                * after the new tab is added */
  1170.  
  1171.               this.mTabContainer.mTabstrip.scrollBoxObject
  1172.                   .scrollBy(this.mTabContainer.firstChild.boxObject.width, 0);
  1173.             }
  1174.  
  1175.             // invalidate cache, because mTabContainer is about to change
  1176.             this._browsers = null; 
  1177.  
  1178.             // If this new tab is owned by another, assert that relationship
  1179.             if (aOwner !== undefined && aOwner !== null) {
  1180.               t.owner = aOwner;
  1181.  
  1182.               var self = this;
  1183.               function attrChanged(event) {
  1184.                 if (event.attrName == "selectedIndex" &&
  1185.                     event.prevValue != event.newValue)
  1186.                   self.resetOwner(parseInt(event.prevValue));
  1187.               }
  1188.               if (!this.mTabChangedListenerAdded) {
  1189.                 this.mTabBox.addEventListener("DOMAttrModified", attrChanged, false);
  1190.                 this.mTabChangedListenerAdded = true;
  1191.               }
  1192.             }
  1193.  
  1194.             var b = document.createElementNS(
  1195.               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  1196.                                              "browser");
  1197.             b.setAttribute("type", "content-targetable");
  1198.             b.setAttribute("message", "true");
  1199.             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
  1200.             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
  1201.             if (this.hasAttribute("autocompletepopup"))
  1202.               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
  1203.             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
  1204.  
  1205.             // Add the Message and the Browser to the box
  1206.             var notificationbox = document.createElementNS(
  1207.                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  1208.                                     "notificationbox");
  1209.             notificationbox.setAttribute("flex", "1");
  1210.             notificationbox.appendChild(b);
  1211.             b.setAttribute("flex", "1");
  1212.             this.mPanelContainer.appendChild(notificationbox);
  1213.  
  1214.             b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
  1215.  
  1216.             if (this.mStrip.collapsed)
  1217.               this.setStripVisibilityTo(true);
  1218.  
  1219.             this.mPrefs.setBoolPref("browser.tabs.forceHide", false);
  1220.  
  1221.             // wire up a progress listener for the new browser object.
  1222.             var position = this.mTabContainer.childNodes.length-1;
  1223.             var tabListener = this.mTabProgressListener(t, b, blank);
  1224.             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  1225.                                      .createInstance(Components.interfaces.nsIWebProgress);
  1226.             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1227.             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1228.             this.mTabListeners[position] = tabListener;
  1229.             this.mTabFilters[position] = filter;
  1230.  
  1231.             b._fastFind = this.fastFind;
  1232.  
  1233.             var uniqueId = "panel" + Date.now() + position;
  1234.             this.mPanelContainer.lastChild.id = uniqueId;
  1235.             t.linkedPanel = uniqueId;
  1236.             t.linkedBrowser = b;
  1237.             t._tPos = position;
  1238.             if (t.previousSibling.selected)
  1239.               t.setAttribute("afterselected", true);
  1240.  
  1241.             if (!blank) {
  1242.               // Stop the existing about:blank load.  Otherwise, if aURI
  1243.               // doesn't stop in-progress loads on its own, we'll get into
  1244.               // trouble with multiple parallel loads running at once.
  1245.               b.stop();
  1246.  
  1247.               // pretend the user typed this so it'll be available till
  1248.               // the document successfully loads
  1249.               b.userTypedValue = aURI;
  1250.  
  1251.               if (aPostData === undefined)
  1252.                 aPostData = null;
  1253.               const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  1254.               var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
  1255.               if (aAllowThirdPartyFixup) {
  1256.                 flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
  1257.               }
  1258.               try {
  1259.                 b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
  1260.               }
  1261.               catch (ex) {
  1262.               }
  1263.             }
  1264.  
  1265.             // |setTimeout| here to ensure we're post reflow
  1266.             var _delayedUpdate = function(aTabContainer) {
  1267.               aTabContainer.adjustTabstrip();
  1268.  
  1269.               if (aTabContainer.selectedItem != t)
  1270.                 aTabContainer._notifyBackgroundTab(t);
  1271.  
  1272.               // XXXmano: this is a temporary workaround to bug 343585
  1273.               // We need to manually update the scroll buttons disabled state
  1274.               // if a tab was inserted to the overflow area or removed from it
  1275.               // without any scrolling and when the tabbar has already
  1276.               // overflowed.
  1277.               aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
  1278.             }
  1279.             setTimeout(_delayedUpdate, 0, this.mTabContainer);
  1280.  
  1281.             // Dispatch a new tab notification.  We do this once we're
  1282.             // entirely done, so that things are in a consistent state
  1283.             // even if the event listener opens or closes tabs.
  1284.             var evt = document.createEvent("Events");
  1285.             evt.initEvent("TabOpen", true, false);
  1286.             t.dispatchEvent(evt);
  1287.  
  1288.             return t;
  1289.           ]]>
  1290.         </body>
  1291.       </method>
  1292.  
  1293.       <method name="warnAboutClosingTabs">
  1294.       <parameter name="aAll"/>
  1295.       <body>
  1296.         <![CDATA[
  1297.           var numTabs = this.mTabContainer.childNodes.length;
  1298.           var reallyClose = true;
  1299.           if (numTabs <= 1)
  1300.             return reallyClose;
  1301.  
  1302.           const pref = "browser.tabs.warnOnClose";
  1303.           var shouldPrompt = this.mPrefs.getBoolPref(pref);
  1304.  
  1305.           if (shouldPrompt) {
  1306.             var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  1307.                                           .getService(Components.interfaces.nsIPromptService);
  1308.  
  1309.             //default to true: if it were false, we wouldn't get this far
  1310.             var warnOnClose = { value:true };
  1311.             var bundle = this.mStringBundle;
  1312.             var tabsToClose = numTabs;  //number of tabs to be removed
  1313.             if (!aAll)
  1314.               --tabsToClose;
  1315.  
  1316.             var messageKey = (tabsToClose == 1) ? "tabs.closeWarningOneTab" : "tabs.closeWarningMultipleTabs";
  1317.             var closeKey = (tabsToClose == 1) ? "tabs.closeButtonOne" : "tabs.closeButtonMultiple";
  1318.             // focus the window before prompting.
  1319.             // this will raise any minimized window, which will
  1320.             // make it obvious which window the prompt is for and will
  1321.             // solve the problem of windows "obscuring" the prompt.
  1322.             // see bug #350299 for more details
  1323.             window.focus();
  1324.             var buttonPressed = promptService.confirmEx(window,
  1325.                                                         bundle.getString('tabs.closeWarningTitle'),
  1326.                                                         bundle.getFormattedString(messageKey, [tabsToClose]),
  1327.                                                         (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
  1328.                                                         + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
  1329.                                                         bundle.getString(closeKey),
  1330.                                                         null, null,
  1331.                                                         bundle.getString('tabs.closeWarningPromptMe'),
  1332.                                                         warnOnClose);
  1333.             reallyClose = (buttonPressed == 0);
  1334.             // don't set the pref unless they press OK and it's false
  1335.             if (reallyClose && !warnOnClose.value)
  1336.               this.mPrefs.setBoolPref(pref, false);
  1337.           }
  1338.           return reallyClose;
  1339.         ]]>
  1340.       </body>
  1341.       </method>
  1342.  
  1343.       <method name="removeAllTabsBut">
  1344.         <parameter name="aTab"/>
  1345.         <body>
  1346.           <![CDATA[
  1347.             if (this.warnAboutClosingTabs(false)) {
  1348.               if (aTab.localName != "tab")
  1349.                 aTab = this.mCurrentTab;
  1350.               else
  1351.                 this.mTabContainer.selectedItem = aTab;
  1352.  
  1353.               var childNodes = this.mTabContainer.childNodes;
  1354.  
  1355.               for (var i = childNodes.length - 1; i >= 0; --i) {
  1356.                 if (childNodes[i] != aTab)
  1357.                   this.removeTab(childNodes[i]);
  1358.               }
  1359.             }
  1360.           ]]>
  1361.         </body>
  1362.       </method>
  1363.  
  1364.       <method name="removeCurrentTab">
  1365.         <body>
  1366.           <![CDATA[
  1367.             return this.removeTab(this.mCurrentTab);
  1368.           ]]>
  1369.         </body>
  1370.       </method>
  1371.  
  1372.       <method name="resetOwner">
  1373.         <parameter name="oldIndex"/>
  1374.         <body>
  1375.           <![CDATA[
  1376.             // Reset the owner property, since we're leaving the modally opened
  1377.             // tab for another.
  1378.             if (oldIndex < this.mTabContainer.childNodes.length) {
  1379.               var tab = this.mTabContainer.childNodes[oldIndex];
  1380.               tab.owner = null;
  1381.             }
  1382.           ]]>
  1383.         </body>
  1384.       </method>
  1385.  
  1386.       <method name="removeTab">
  1387.         <parameter name="aTab"/>
  1388.         <body>
  1389.           <![CDATA[
  1390.             this._browsers = null; // invalidate cache
  1391.             if (aTab.localName != "tab")
  1392.               aTab = this.mCurrentTab;
  1393.  
  1394.             var l = this.mTabContainer.childNodes.length;
  1395.             if (l == 1 && this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
  1396.               // hide the tab bar
  1397.               this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
  1398.               this.setStripVisibilityTo(false);
  1399.               return;
  1400.             }
  1401.  
  1402.             var ds = this.getBrowserForTab(aTab).docShell;
  1403.             if (ds.contentViewer && !ds.contentViewer.permitUnload())
  1404.               return;
  1405.  
  1406.             // see notes in addTab
  1407.             var _delayedUpdate = function(aTabContainer) {
  1408.               aTabContainer.adjustTabstrip();
  1409.               aTabContainer.mTabstrip._updateScrollButtonsDisabledState();
  1410.             }
  1411.             setTimeout(_delayedUpdate, 0, this.mTabContainer);
  1412.  
  1413.             if (l == 1) {
  1414.               // add a new blank tab to replace the one we're about to close
  1415.               // (this ensures that the remaining tab is as good as new)
  1416.               this.addTab("about:blank");
  1417.               l++;
  1418.             }
  1419.             else if (l == 2) {
  1420.               var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
  1421.               var tabStripHide = !window.toolbar.visible;
  1422.               if (autohide || tabStripHide)
  1423.                 this.setStripVisibilityTo(false);
  1424.             }
  1425.  
  1426.             // We're committed to closing the tab now.  
  1427.             // Dispatch a notification.
  1428.             // We dispatch it before any teardown so that event listeners can
  1429.             // inspect the tab that's about to close.
  1430.             var evt = document.createEvent("Events");
  1431.             evt.initEvent("TabClose", true, false);
  1432.             aTab.dispatchEvent(evt);
  1433.  
  1434.             var index = -1;
  1435.             if (this.mCurrentTab == aTab)
  1436.               index = this.mTabContainer.selectedIndex;
  1437.             else {
  1438.               // Find and locate the tab in our list.
  1439.               for (var i = 0; i < l; i++)
  1440.                 if (this.mTabContainer.childNodes[i] == aTab)
  1441.                   index = i;
  1442.             }
  1443.  
  1444.             // Remove the tab's filter and progress listener.
  1445.             const filter = this.mTabFilters[index];
  1446.             var oldBrowser = this.getBrowserAtIndex(index);
  1447.             oldBrowser.webProgress.removeProgressListener(filter);
  1448.             filter.removeProgressListener(this.mTabListeners[index]);
  1449.             this.mTabFilters.splice(index, 1);
  1450.             this.mTabListeners.splice(index, 1);
  1451.  
  1452.             // Remove our title change and blocking listeners
  1453.             oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
  1454.  
  1455.             // We are no longer the primary content area.
  1456.             oldBrowser.setAttribute("type", "content-targetable");
  1457.  
  1458.             // Get the index of the tab we're removing before unselecting it
  1459.             var currentIndex = this.mTabContainer.selectedIndex;
  1460.  
  1461.             var oldTab = aTab;
  1462.  
  1463.             // clean up the before/afterselected attributes before removing the tab
  1464.             oldTab._selected = false;
  1465.  
  1466.             // Remove this tab as the owner of any other tabs, since it's going away.
  1467.             for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
  1468.               var tab = this.mTabContainer.childNodes[i];
  1469.               if ("owner" in tab && tab.owner == oldTab)
  1470.                 // |tab| is a child of the tab we're removing, make it an orphan
  1471.                 tab.owner = null;
  1472.             }
  1473.  
  1474.             // Because of the way XBL works (fields just set JS
  1475.             // properties on the element) and the code we have in place
  1476.             // to preserve the JS objects for any elements that have
  1477.             // JS properties set on them, the browser element won't be
  1478.             // destroyed until the document goes away.  So we force a
  1479.             // cleanup ourselves.
  1480.             // This has to happen before we remove the child so that the
  1481.             // XBL implementation of nsIObserver still works.  But
  1482.             // clearing focusedWindow happens below because it gets
  1483.             // reset by updateCurrentBrowser.
  1484.             oldBrowser.destroy();
  1485.  
  1486.             if (oldBrowser == this.mCurrentBrowser)
  1487.               this.mCurrentBrowser = null;
  1488.  
  1489.             // Remove the tab
  1490.             this.mTabContainer.removeChild(oldTab);
  1491.             // invalidate cache, because mTabContainer is about to change
  1492.             this._browsers = null; 
  1493.             this.mPanelContainer.removeChild(oldBrowser.parentNode);
  1494.  
  1495.             try {
  1496.               // if we're at the right side (and not the logical end,
  1497.               // which is why this works for both LTR and RTL)
  1498.               // of the tabstrip, we need to ensure that we stay 
  1499.               // completely scrolled to the right side
  1500.               var tabStrip = this.mTabContainer.mTabstrip;
  1501.               var scrollPos = {};
  1502.               tabStrip.scrollBoxObject.getPosition(scrollPos, {});
  1503.               var scrolledSize = {};
  1504.               tabStrip.scrollBoxObject.getScrolledSize(scrolledSize, {});
  1505.  
  1506.               if (scrollPos.value + tabStrip.boxObject.width >= 
  1507.                   scrolledSize.value) {
  1508.                 tabStrip.scrollByPixels(-1 * this.mTabContainer.firstChild
  1509.                                                  .boxObject.width);
  1510.               }
  1511.             }
  1512.             catch (ex) {
  1513.             }
  1514.  
  1515.             // Find the tab to select
  1516.             var newIndex = -1;
  1517.             if (currentIndex > index)
  1518.               newIndex = currentIndex-1;
  1519.             else if (currentIndex < index)
  1520.               newIndex = currentIndex;
  1521.             else {
  1522.               if ("owner" in oldTab && oldTab.owner &&
  1523.                   this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
  1524.                 for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
  1525.                   tab = this.mTabContainer.childNodes[i];
  1526.                   if (tab == oldTab.owner) {
  1527.                     newIndex = i;
  1528.                     break;
  1529.                   }
  1530.                 }
  1531.               }
  1532.               if (newIndex == -1)
  1533.                 newIndex = (index == l - 1) ? index - 1 : index;
  1534.             }
  1535.  
  1536.             // Select the new tab
  1537.             this.selectedTab = this.mTabContainer.childNodes[newIndex];
  1538.  
  1539.             for (i = oldTab._tPos; i < this.mTabContainer.childNodes.length; i++) {
  1540.               this.mTabContainer.childNodes[i]._tPos = i;
  1541.             }
  1542.             this.mTabBox.selectedPanel = this.getBrowserForTab(this.mCurrentTab).parentNode;
  1543.             this.mCurrentTab._selected = true;
  1544.  
  1545.             this.updateCurrentBrowser();
  1546.  
  1547.             // see comment above destroy above
  1548.             oldBrowser.focusedWindow = null;
  1549.             oldBrowser.focusedElement = null;
  1550.           ]]>
  1551.         </body>
  1552.       </method>
  1553.  
  1554.       <method name="reloadAllTabs">
  1555.         <body>
  1556.           <![CDATA[
  1557.             var l = this.mPanelContainer.childNodes.length;
  1558.             for (var i = 0; i < l; i++) {
  1559.               try {
  1560.                 this.getBrowserAtIndex(i).reload();
  1561.               } catch (e) {
  1562.                 // ignore failure to reload so others will be reloaded
  1563.               }
  1564.             }
  1565.           ]]>
  1566.         </body>
  1567.       </method>
  1568.  
  1569.       <method name="reloadTab">
  1570.         <parameter name="aTab"/>
  1571.         <body>
  1572.           <![CDATA[
  1573.             if (aTab.localName != "tab")
  1574.               aTab = this.mCurrentTab;
  1575.  
  1576.             this.getBrowserForTab(aTab).reload();
  1577.           ]]>
  1578.         </body>
  1579.       </method>
  1580.  
  1581.       <method name="onTabBarDblClick">
  1582.         <parameter name="aEvent"/>
  1583.         <body>
  1584.           <![CDATA[
  1585.             // See hack note in the tabbrowser-close-button binding
  1586.             if (!this._blockDblClick && aEvent.button == 0 &&
  1587.                 aEvent.originalTarget.localName == "box") {
  1588.               // xxx this needs to check that we're in the empty area of the tabstrip
  1589.               var e = document.createEvent("Events");
  1590.               e.initEvent("NewTab", true, true);
  1591.               this.dispatchEvent(e);
  1592.             }
  1593.           ]]>
  1594.         </body>
  1595.       </method>
  1596.  
  1597.       <method name="addProgressListener">
  1598.         <parameter name="aListener"/>
  1599.         <parameter name="aMask"/>
  1600.         <body>
  1601.           <![CDATA[
  1602.             if (!this.mAddProgressListenerWasCalled) {
  1603.               this.mAddProgressListenerWasCalled = true;
  1604.               var autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
  1605.               var forceHide = this.mPrefs.getBoolPref("browser.tabs.forceHide");
  1606.               var tabStripHide = !window.toolbar.visible;
  1607.               if (!autoHide && !forceHide && !tabStripHide)
  1608.                 this.setStripVisibilityTo(true);
  1609.             }
  1610.  
  1611.             if (!this.mTabbedMode && this.mProgressListeners.length == 1) {
  1612.               // If we are adding a 2nd progress listener, we need to enter tabbed mode
  1613.               // because the browser status filter can only handle one progress listener.
  1614.               // In tabbed mode, mTabProgressListener is used which will iterate over all listeners.
  1615.               this.enterTabbedMode();
  1616.             }
  1617.  
  1618.             this.mProgressListeners.push(aListener);
  1619.  
  1620.             if (!this.mTabbedMode) {
  1621.               // If someone does this:
  1622.               // addProgressListener, removeProgressListener, addProgressListener
  1623.               // don't create a new filter; reuse the existing filter.
  1624.               if (this.mTabFilters.length == 0) {
  1625.                 // hook a filter up to our first browser
  1626.                 const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  1627.                                          .createInstance(Components.interfaces.nsIWebProgress);
  1628.                 this.mTabFilters[0] = filter;
  1629.                 this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1630.               }
  1631.  
  1632.               // Directly hook the listener up to the filter for better performance
  1633.               this.mTabFilters[0].addProgressListener(aListener, aMask);
  1634.             }
  1635.           ]]>
  1636.         </body>
  1637.       </method>
  1638.  
  1639.       <method name="removeProgressListener">
  1640.         <parameter name="aListener"/>
  1641.         <body>
  1642.           <![CDATA[
  1643.             for (var i = 0; i < this.mProgressListeners.length; i++) {
  1644.               if (this.mProgressListeners[i] == aListener) {
  1645.                 this.mProgressListeners.splice(i, 1);
  1646.                 break;
  1647.               }
  1648.             }
  1649.  
  1650.             if (!this.mTabbedMode)
  1651.               // Don't forget to remove it from the filter we hooked it up to
  1652.               this.mTabFilters[0].removeProgressListener(aListener);
  1653.          ]]>
  1654.         </body>
  1655.       </method>
  1656.  
  1657.       <method name="getBrowserForTab">
  1658.         <parameter name="aTab"/>
  1659.         <body>
  1660.         <![CDATA[
  1661.           return aTab.linkedBrowser;
  1662.         ]]>
  1663.         </body>
  1664.       </method>
  1665.  
  1666.       <property name="tabContainer" readonly="true">
  1667.         <getter>
  1668.           return this.mTabContainer;
  1669.         </getter>
  1670.       </property>
  1671.  
  1672.       <property name="selectedTab">
  1673.         <getter>
  1674.           return this.mTabBox.selectedTab;
  1675.         </getter>
  1676.         <setter>
  1677.           <![CDATA[
  1678.           // Update the tab
  1679.           this.mTabBox.selectedTab = val;
  1680.           return val;
  1681.           ]]>
  1682.         </setter>
  1683.       </property>
  1684.  
  1685.       <property name="selectedBrowser"
  1686.                 onget="return this.mCurrentBrowser;"
  1687.                 readonly="true"/>
  1688.  
  1689.       <property name="browsers" readonly="true">
  1690.        <getter>
  1691.           <![CDATA[
  1692.             if (!this._browsers) {
  1693.               var browsers = [];
  1694.               var i;
  1695.               browsers.item = function(i) {return this[i];}
  1696.               for (i = 0; i < this.mTabContainer.childNodes.length; i++)
  1697.                 browsers.push(this.mTabContainer.childNodes[i].linkedBrowser);
  1698.               this._browsers = browsers;
  1699.             }
  1700.             return this._browsers;
  1701.           ]]>
  1702.         </getter>
  1703.       </property>
  1704.  
  1705.       <!-- Drag and drop observer API -->
  1706.       <method name="onDragStart">
  1707.         <parameter name="aEvent"/>
  1708.         <parameter name="aXferData"/>
  1709.         <parameter name="aDragAction"/>
  1710.         <body>
  1711.         <![CDATA[
  1712.           if (aEvent.target.localName == "tab" &&
  1713.               aEvent.originalTarget.localName != "toolbarbutton") {
  1714.             aXferData.data = new TransferData();
  1715.  
  1716.             var URI = this.getBrowserForTab(aEvent.target).currentURI;
  1717.             if (URI) {
  1718.               aXferData.data.addDataForFlavour("text/x-moz-url", URI.spec + "\n" + aEvent.target.label);
  1719.               aXferData.data.addDataForFlavour("text/unicode", URI.spec);
  1720.               aXferData.data.addDataForFlavour("text/html", '<a href="' + URI.spec + '">' + aEvent.target.label + '</a>');
  1721.             } else {
  1722.               aXferData.data.addDataForFlavour("text/unicode", "about:blank");
  1723.             }
  1724.           }
  1725.         ]]>
  1726.         </body>
  1727.       </method>
  1728.  
  1729.       <method name="canDrop">
  1730.         <parameter name="aEvent"/>
  1731.         <parameter name="aDragSession"/>
  1732.         <body>
  1733.           <![CDATA[
  1734.             if (aDragSession.sourceNode &&
  1735.                 aDragSession.sourceNode.parentNode == this.mTabContainer &&
  1736.                 (aEvent.screenX >= aDragSession.sourceNode.boxObject.screenX &&
  1737.                  aEvent.screenX <= (aDragSession.sourceNode.boxObject.screenX +
  1738.                                     aDragSession.sourceNode.boxObject.width)))
  1739.               return false;
  1740.             return true;
  1741.           ]]>
  1742.         </body>
  1743.       </method>
  1744.  
  1745.       <field name="mDragTime">0</field>
  1746.       <field name="mDragOverDelay">350</field>
  1747.  
  1748.       <method name="onDragOver">
  1749.         <parameter name="aEvent"/>
  1750.         <parameter name="aFlavour"/>
  1751.         <parameter name="aDragSession"/>
  1752.         <body>
  1753.           <![CDATA[
  1754.             var tabStrip = this.mTabContainer.mTabstrip;
  1755.             var ltr = (window.getComputedStyle(this.parentNode, null).direction
  1756.                        == "ltr");
  1757.  
  1758.             // autoscroll the tab strip if we drag over the scroll
  1759.             // buttons, even if we aren't dragging a tab, but then
  1760.             // return to avoid drawing the drop indicator
  1761.             var pixelsToScroll = 0;
  1762.             if (this.mTabContainer.getAttribute("overflow") == "true") {
  1763.               var targetAnonid = aEvent.originalTarget.getAttribute("anonid");
  1764.               switch (targetAnonid) {
  1765.                 case "scrollbutton-up":
  1766.                   pixelsToScroll = tabStrip.scrollIncrement * -1;
  1767.                   break;
  1768.                 case "scrollbutton-down":
  1769.                 case "alltabs-button":
  1770.                   pixelsToScroll = tabStrip.scrollIncrement;
  1771.                   break;
  1772.               }
  1773.               if (pixelsToScroll)
  1774.                 tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
  1775.             }
  1776.  
  1777.             var isTabDrag = (aDragSession.sourceNode && 
  1778.                              aDragSession.sourceNode.parentNode == this.mTabContainer);
  1779.             if (!isTabDrag && aEvent.target.localName == "tab") {
  1780.               if (!this.mDragTime) 
  1781.                 this.mDragTime = Date.now();
  1782.               if (Date.now() >= this.mDragTime + this.mDragOverDelay)
  1783.                 this.mTabContainer.selectedItem = aEvent.target;
  1784.               return;
  1785.             }
  1786.  
  1787.             var newIndex = this.getNewIndex(aEvent);
  1788.             var ib = this.mTabDropIndicatorBar;
  1789.             var ind = ib.firstChild;
  1790.             var tabStripBoxObject = tabStrip.scrollBoxObject;
  1791.             var minMargin = tabStripBoxObject.x - this.boxObject.x;
  1792.             // make sure we don't place the tab drop indicator past the
  1793.             // edge, or the containing box will flex and stretch
  1794.             // the tab drop indicator bar, which will flex the url bar.  
  1795.             // XXX todo
  1796.             // just use first value if you can figure out how to get
  1797.             // the tab drop indicator to crop instead of flex and stretch
  1798.             // the tab drop indicator bar.
  1799.             var maxMargin = Math.min(minMargin + tabStripBoxObject.width, 
  1800.                                      ib.boxObject.x + ib.boxObject.width -
  1801.                                      ind.boxObject.width);
  1802.             if (!ltr)
  1803.               [minMargin, maxMargin] = [this.boxObject.width - maxMargin,
  1804.                                         this.boxObject.width - minMargin];
  1805.             var newMargin, tabBoxObject;
  1806.             if (pixelsToScroll) {
  1807.               // if we are scrolling, put the drop indicator at the edge
  1808.               // so that it doesn't jump while scrolling
  1809.               newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
  1810.             }
  1811.             else {
  1812.               if (newIndex == this.mTabs.length) {
  1813.                 tabBoxObject =  this.mTabs[newIndex-1].boxObject;
  1814.                 if (ltr)
  1815.                   newMargin = tabBoxObject.screenX - this.boxObject.screenX
  1816.                               + tabBoxObject.width;
  1817.                 else
  1818.                   newMargin = this.boxObject.screenX - tabBoxObject.screenX
  1819.                               + this.boxObject.width;
  1820.               }
  1821.               else {
  1822.                 tabBoxObject =  this.mTabs[newIndex].boxObject;
  1823.                 if (ltr)
  1824.                   newMargin = tabBoxObject.screenX - this.boxObject.screenX;
  1825.                 else
  1826.                   newMargin = this.boxObject.screenX - tabBoxObject.screenX
  1827.                               + this.boxObject.width - tabBoxObject.width;
  1828.               }
  1829.               // ensure we never place the drop indicator beyond our limits
  1830.               if (newMargin < minMargin)
  1831.                 newMargin = minMargin;
  1832.               else if (newMargin > maxMargin)
  1833.                 newMargin = maxMargin;
  1834.             }
  1835.  
  1836.             ind.style.MozMarginStart = newMargin + 'px';
  1837.  
  1838.             ib.collapsed = !aDragSession.canDrop;
  1839.           ]]>
  1840.         </body>
  1841.       </method>
  1842.  
  1843.       <method name="onDrop">
  1844.         <parameter name="aEvent"/>
  1845.         <parameter name="aXferData"/>
  1846.         <parameter name="aDragSession"/>
  1847.         <body>
  1848.           <![CDATA[
  1849.             var accelKeyPressed = aEvent.ctrlKey;
  1850.             var draggedTab;
  1851.             if (aDragSession.sourceNode && aDragSession.sourceNode.localName == "tab" &&
  1852.                 (aDragSession.sourceNode.parentNode == this.mTabContainer ||
  1853.                  aDragSession.sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
  1854.                  aDragSession.sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser"))
  1855.               draggedTab = aDragSession.sourceNode;
  1856.             if (draggedTab && (accelKeyPressed || draggedTab.parentNode == this.mTabContainer)) {
  1857.               var newIndex = this.getNewIndex(aEvent);
  1858.               if (accelKeyPressed) {
  1859.                 // copy the dropped tab (wherever it's from)
  1860.                 var newTab = this.duplicateTab(draggedTab);
  1861.                 this.moveTabTo(newTab, newIndex);
  1862.                 if (draggedTab.parentNode != this.mTabContainer || aEvent.shiftKey)
  1863.                   this.selectedTab = newTab;
  1864.               }
  1865.               else {
  1866.                 // move the dropped tab
  1867.                 if (newIndex > draggedTab._tPos)
  1868.                   newIndex--;
  1869.                 if (newIndex != draggedTab._tPos)
  1870.                   this.moveTabTo(draggedTab, newIndex);
  1871.               }
  1872.             }
  1873.             else if (draggedTab) {
  1874.               // copy the dropped tab and remove it from the other window
  1875.               // (making it seem to have moved between windows)
  1876.               newIndex = this.getNewIndex(aEvent);
  1877.               newTab = this.duplicateTab(draggedTab);
  1878.               this.moveTabTo(newTab, newIndex);
  1879.               this.selectedTab = newTab;
  1880.               
  1881.               var remoteBrowser = draggedTab.ownerDocument.defaultView.getBrowser();
  1882.               var tabCount = remoteBrowser.tabContainer.childNodes.length;
  1883.               remoteBrowser.removeTab(draggedTab);
  1884.               // close the other window if this was its last tab
  1885.               if (tabCount == 1)
  1886.                 draggedTab.ownerDocument.defaultView.close();
  1887.             }
  1888.             else {
  1889.               var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
  1890.  
  1891.               // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
  1892.               // Also disallow dropping javascript: or data: urls--bail out
  1893.               if (!url || !url.length || url.indexOf(" ", 0) != -1 ||
  1894.                   /^\s*(javascript|data):/.test(url))
  1895.                 return;
  1896.  
  1897.               nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, url);
  1898.  
  1899.               var bgLoad = true;
  1900.               try {
  1901.                 bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
  1902.               }
  1903.               catch (e) { }
  1904.  
  1905.               if (aEvent.shiftKey)
  1906.                 bgLoad = !bgLoad;
  1907.  
  1908.               if (document.getBindingParent(aEvent.originalTarget).localName != "tab" || accelKeyPressed) {
  1909.                 // We're adding a new tab.
  1910.                 newIndex = this.getNewIndex(aEvent);
  1911.                 newTab = this.loadOneTab(getShortcutOrURI(url), null, null, null, bgLoad, false);
  1912.                 this.moveTabTo(newTab, newIndex);
  1913.               }
  1914.               else {
  1915.                 // Load in an existing tab.
  1916.                 var tab = aEvent.target;
  1917.                 try {
  1918.                   this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
  1919.                   if (this.mCurrentTab != tab && !bgLoad)
  1920.                     this.selectedTab = tab;
  1921.                 } catch(ex) {
  1922.                   // Just ignore invalid urls
  1923.                 }
  1924.               }
  1925.             }
  1926.           ]]>
  1927.         </body>
  1928.       </method>
  1929.  
  1930.       <method name="onDragExit">
  1931.         <parameter name="aEvent"/>
  1932.         <parameter name="aDragSession"/>
  1933.         <body>
  1934.           <![CDATA[
  1935.             this.mDragTime = 0;
  1936.  
  1937.             if (aDragSession.sourceNode &&
  1938.                 aDragSession.sourceNode.parentNode == this.mTabContainer &&
  1939.                 aDragSession.canDrop) {
  1940.               var target = aEvent.relatedTarget;
  1941.               while (target && target != this.mStrip)
  1942.                 target = target.parentNode;
  1943.               if (target)
  1944.                 return;
  1945.             }
  1946.             this.mTabDropIndicatorBar.collapsed = true;
  1947.           ]]>
  1948.         </body>
  1949.       </method>
  1950.  
  1951.       <method name="getSupportedFlavours">
  1952.         <body>
  1953.         <![CDATA[
  1954.           var flavourSet = new FlavourSet();
  1955.           flavourSet.appendFlavour("text/x-moz-url");
  1956.           flavourSet.appendFlavour("text/unicode");
  1957.           flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  1958.           return flavourSet;
  1959.         ]]>
  1960.         </body>
  1961.       </method>
  1962.  
  1963.       <method name="moveTabTo">
  1964.         <parameter name="aTab"/>
  1965.         <parameter name="aIndex"/>
  1966.         <body>
  1967.         <![CDATA[
  1968.           this._browsers = null; // invalidate cache
  1969.           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
  1970.           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
  1971.  
  1972.           var oldPosition = aTab._tPos;
  1973.  
  1974.           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
  1975.           this.mCurrentTab._selected = false;
  1976.           // use .item() instead of [] because dragging to the end of the strip goes out of
  1977.           // bounds: .item() returns null (so it acts like appendChild), but [] throws
  1978.           this.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes.item(aIndex));
  1979.           // invalidate cache, because mTabContainer is about to change
  1980.           this._browsers = null;
  1981.  
  1982.           var i;
  1983.           for (i = 0; i < this.mTabContainer.childNodes.length; i++) {
  1984.             this.mTabContainer.childNodes[i]._tPos = i;
  1985.             this.mTabContainer.childNodes[i]._selected = false;
  1986.           }
  1987.           this.mCurrentTab._selected = true;
  1988.           this.mTabContainer.mTabstrip.scrollBoxObject.ensureElementIsVisible(this.mCurrentTab);
  1989.  
  1990.           var evt = document.createEvent("UIEvents");
  1991.           evt.initUIEvent("TabMove", true, false, window, oldPosition);
  1992.           aTab.dispatchEvent(evt);
  1993.  
  1994.           return aTab;
  1995.         ]]>
  1996.         </body>
  1997.       </method>
  1998.  
  1999.       <method name="getNewIndex">
  2000.         <parameter name="aEvent"/>
  2001.         <body>
  2002.           <![CDATA[
  2003.             var i;
  2004.             if (window.getComputedStyle(this.parentNode, null).direction == "ltr") {
  2005.               for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
  2006.                 if (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2) 
  2007.                   return i;
  2008.             } else {
  2009.               for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
  2010.                 if (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
  2011.                   return i;
  2012.             }
  2013.             return this.mTabs.length;
  2014.           ]]>
  2015.         </body>
  2016.       </method>
  2017.  
  2018.  
  2019.       <method name="moveTabForward">
  2020.         <body>
  2021.           <![CDATA[
  2022.             var tabPos = this.mCurrentTab._tPos;
  2023.             if (tabPos < this.browsers.length - 1) {
  2024.               this.moveTabTo(this.mCurrentTab, tabPos + 1);
  2025.               this.mCurrentTab.focus();
  2026.             }
  2027.             else if (this.arrowKeysShouldWrap)
  2028.               this.moveTabToStart();
  2029.           ]]>
  2030.         </body>
  2031.       </method>
  2032.  
  2033.       <method name="moveTabBackward">
  2034.         <body>
  2035.           <![CDATA[
  2036.             var tabPos = this.mCurrentTab._tPos;
  2037.             if (tabPos > 0) {
  2038.               this.moveTabTo(this.mCurrentTab, tabPos - 1);
  2039.               this.mCurrentTab.focus();
  2040.             }
  2041.             else if (this.arrowKeysShouldWrap)
  2042.               this.moveTabToEnd();
  2043.           ]]>
  2044.         </body>
  2045.       </method>
  2046.  
  2047.       <method name="moveTabToStart">
  2048.         <body>
  2049.           <![CDATA[
  2050.             var tabPos = this.mCurrentTab._tPos;
  2051.             if (tabPos > 0) {
  2052.               this.moveTabTo(this.mCurrentTab, 0);
  2053.               this.mCurrentTab.focus();
  2054.             }
  2055.           ]]>
  2056.         </body>
  2057.       </method>
  2058.  
  2059.       <method name="moveTabToEnd">
  2060.         <body>
  2061.           <![CDATA[
  2062.             var tabPos = this.mCurrentTab._tPos;
  2063.             if (tabPos < this.browsers.length - 1) {
  2064.               this.moveTabTo(this.mCurrentTab,
  2065.                              this.browsers.length - 1);
  2066.               this.mCurrentTab.focus();
  2067.             }
  2068.           ]]>
  2069.         </body>
  2070.       </method>
  2071.  
  2072.       <method name="moveTabOver">
  2073.         <parameter name="aEvent"/>
  2074.         <body>
  2075.           <![CDATA[
  2076.             var direction = window.getComputedStyle(this.parentNode, null).direction;
  2077.             if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
  2078.                 (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT))
  2079.               this.moveTabForward();
  2080.             else
  2081.               this.moveTabBackward();
  2082.           ]]>
  2083.         </body>
  2084.       </method>
  2085.  
  2086.       <method name="duplicateTab">
  2087.         <parameter name="aTab"/><!-- can be from a different window as well -->
  2088.         <body>
  2089.           <![CDATA[
  2090.             // try to have SessionStore duplicate the given tab
  2091.             try {
  2092.               var ss = Components.classes["@mozilla.org/browser/sessionstore;1"]
  2093.                                  .getService(Components.interfaces.nsISessionStore);
  2094.               return ss.duplicateTab(window, aTab);
  2095.             } catch (ex) {
  2096.               // fall back to basic URL copying
  2097.               return this.loadOneTab(this.getBrowserForTab(aTab).currentURI.spec);
  2098.             }
  2099.           ]]>
  2100.         </body>
  2101.       </method>
  2102.  
  2103.       <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
  2104.            MAKE SURE TO ADD IT HERE AS WELL. -->
  2105.       <property name="canGoBack"
  2106.                 onget="return this.mCurrentBrowser.canGoBack;"
  2107.                 readonly="true"/>
  2108.  
  2109.       <property name="canGoForward"
  2110.                 onget="return this.mCurrentBrowser.canGoForward;"
  2111.                 readonly="true"/>
  2112.  
  2113.       <method name="goBack">
  2114.         <body>
  2115.           <![CDATA[
  2116.             return this.mCurrentBrowser.goBack();
  2117.           ]]>
  2118.         </body>
  2119.       </method>
  2120.  
  2121.       <method name="goForward">
  2122.         <body>
  2123.           <![CDATA[
  2124.             return this.mCurrentBrowser.goForward();
  2125.           ]]>
  2126.         </body>
  2127.       </method>
  2128.  
  2129.       <method name="reload">
  2130.         <body>
  2131.           <![CDATA[
  2132.             return this.mCurrentBrowser.reload();
  2133.           ]]>
  2134.         </body>
  2135.       </method>
  2136.  
  2137.       <method name="reloadWithFlags">
  2138.         <parameter name="aFlags"/>
  2139.         <body>
  2140.           <![CDATA[
  2141.             return this.mCurrentBrowser.reloadWithFlags(aFlags);
  2142.           ]]>
  2143.         </body>
  2144.       </method>
  2145.  
  2146.       <method name="stop">
  2147.         <body>
  2148.           <![CDATA[
  2149.             return this.mCurrentBrowser.stop();
  2150.           ]]>
  2151.         </body>
  2152.       </method>
  2153.  
  2154.       <!-- throws exception for unknown schemes -->
  2155.       <method name="loadURI">
  2156.         <parameter name="aURI"/>
  2157.         <parameter name="aReferrerURI"/>
  2158.         <parameter name="aCharset"/>
  2159.         <body>
  2160.           <![CDATA[
  2161.             return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
  2162.           ]]>
  2163.         </body>
  2164.       </method>
  2165.  
  2166.       <!-- throws exception for unknown schemes -->
  2167.       <method name="loadURIWithFlags">
  2168.         <parameter name="aURI"/>
  2169.         <parameter name="aFlags"/>
  2170.         <parameter name="aReferrerURI"/>
  2171.         <parameter name="aCharset"/>
  2172.         <parameter name="aPostData"/>
  2173.         <body>
  2174.           <![CDATA[
  2175.             return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
  2176.           ]]>
  2177.         </body>
  2178.       </method>
  2179.  
  2180.       <method name="goHome">
  2181.         <body>
  2182.           <![CDATA[
  2183.             return this.mCurrentBrowser.goHome();
  2184.           ]]>
  2185.         </body>
  2186.       </method>
  2187.  
  2188.       <property name="homePage">
  2189.         <getter>
  2190.           <![CDATA[
  2191.             return this.mCurrentBrowser.homePage;
  2192.           ]]>
  2193.         </getter>
  2194.         <setter>
  2195.           <![CDATA[
  2196.             this.mCurrentBrowser.homePage = val;
  2197.             return val;
  2198.           ]]>
  2199.         </setter>
  2200.       </property>
  2201.  
  2202.       <method name="gotoIndex">
  2203.         <parameter name="aIndex"/>
  2204.         <body>
  2205.           <![CDATA[
  2206.             return this.mCurrentBrowser.gotoIndex(aIndex);
  2207.           ]]>
  2208.         </body>
  2209.       </method>
  2210.  
  2211.       <method name="attachFormFill">
  2212.         <body><![CDATA[
  2213.           for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
  2214.             var cb = this.getBrowserAtIndex(i);
  2215.             cb.attachFormFill();
  2216.           }
  2217.         ]]></body>
  2218.       </method>
  2219.  
  2220.       <method name="detachFormFill">
  2221.         <body><![CDATA[
  2222.           for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
  2223.             var cb = this.getBrowserAtIndex(i);
  2224.             cb.detachFormFill();
  2225.           }
  2226.         ]]></body>
  2227.       </method>
  2228.  
  2229.       <property name="pageReport"
  2230.                 onget="return this.mCurrentBrowser.pageReport;"
  2231.                 readonly="true"/>
  2232.  
  2233.       <property name="currentURI"
  2234.                 onget="return this.mCurrentBrowser.currentURI;"
  2235.                 readonly="true"/>
  2236.  
  2237.       <field name="_fastFind">null</field>
  2238.       <property name="fastFind"
  2239.                 readonly="true">
  2240.         <getter>
  2241.         <![CDATA[
  2242.           if (!this._fastFind) {
  2243.             this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
  2244.                                        .createInstance(Components.interfaces.nsITypeAheadFind);
  2245.             this._fastFind.init(this.docShell);
  2246.           }
  2247.           return this._fastFind;
  2248.         ]]>
  2249.         </getter>
  2250.       </property>
  2251.  
  2252.       <property name="docShell"
  2253.                 onget="return this.mCurrentBrowser.docShell"
  2254.                 readonly="true"/>
  2255.  
  2256.       <property name="webNavigation"
  2257.                 onget="return this.mCurrentBrowser.webNavigation"
  2258.                 readonly="true"/>
  2259.  
  2260.       <property name="webBrowserFind"
  2261.                 readonly="true"
  2262.                 onget="return this.mCurrentBrowser.webBrowserFind"/>
  2263.  
  2264.       <property name="webProgress"
  2265.                 readonly="true"
  2266.                 onget="return this.mCurrentBrowser.webProgress"/>
  2267.  
  2268.       <property name="contentWindow"
  2269.                 readonly="true"
  2270.                 onget="return this.mCurrentBrowser.contentWindow"/>
  2271.  
  2272.       <property name="sessionHistory"
  2273.                 onget="return this.mCurrentBrowser.sessionHistory;"
  2274.                 readonly="true"/>
  2275.  
  2276.       <property name="markupDocumentViewer"
  2277.                 onget="return this.mCurrentBrowser.markupDocumentViewer;"
  2278.                 readonly="true"/>
  2279.  
  2280.       <property name="contentViewerEdit"
  2281.                 onget="return this.mCurrentBrowser.contentViewerEdit;"
  2282.                 readonly="true"/>
  2283.  
  2284.       <property name="contentViewerFile"
  2285.                 onget="return this.mCurrentBrowser.contentViewerFile;"
  2286.                 readonly="true"/>
  2287.  
  2288.       <property name="documentCharsetInfo"
  2289.                 onget="return this.mCurrentBrowser.documentCharsetInfo;"
  2290.                 readonly="true"/>
  2291.  
  2292.       <property name="contentDocument"
  2293.                 onget="return this.mCurrentBrowser.contentDocument;"
  2294.                 readonly="true"/>
  2295.  
  2296.       <property name="contentTitle"
  2297.                 onget="return this.mCurrentBrowser.contentTitle;"
  2298.                 readonly="true"/>
  2299.  
  2300.       <property name="contentPrincipal"
  2301.                 onget="return this.mCurrentBrowser.contentPrincipal;"
  2302.                 readonly="true"/>
  2303.  
  2304.       <property name="securityUI"
  2305.                 onget="return this.mCurrentBrowser.securityUI;"
  2306.                 readonly="true"/>
  2307.  
  2308.       <method name="dragDropSecurityCheck">
  2309.         <parameter name="aEvent"/>
  2310.         <parameter name="aDragSession"/>
  2311.         <parameter name="aUri"/>
  2312.         <body>
  2313.           <![CDATA[
  2314.             nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, aUri);
  2315.           ]]>
  2316.         </body>
  2317.       </method>
  2318.  
  2319.       <field name="_keyEventHandler" readonly="true">
  2320.       <![CDATA[({
  2321.         tabbrowser: this,
  2322.         handleEvent: function handleEvent(aEvent) {
  2323.           if (!aEvent.isTrusted) {
  2324.             // Don't let untrusted events mess with tabs.
  2325.             return;
  2326.           }
  2327.  
  2328.           if ('altKey' in aEvent && aEvent.altKey)
  2329.             return;
  2330.           if (('ctrlKey' in aEvent && aEvent.ctrlKey) &&
  2331.               !('shiftKey' in aEvent && aEvent.shiftKey) &&
  2332.               !('metaKey' in aEvent && aEvent.metaKey)) {
  2333.             if (aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
  2334.                 this.tabbrowser.mTabBox.handleCtrlPageUpDown) {
  2335.               this.tabbrowser.removeCurrentTab();
  2336.  
  2337.               aEvent.stopPropagation();
  2338.               aEvent.preventDefault();
  2339.               return;
  2340.             }
  2341.             if (aEvent.target.localName == "tabbrowser") {
  2342.               switch (aEvent.keyCode) {
  2343.                 case KeyEvent.DOM_VK_UP:
  2344.                   this.tabbrowser.moveTabBackward();
  2345.                   break;
  2346.                 case KeyEvent.DOM_VK_DOWN:
  2347.                   this.tabbrowser.moveTabForward();
  2348.                   break;
  2349.                 case KeyEvent.DOM_VK_RIGHT:
  2350.                 case KeyEvent.DOM_VK_LEFT:
  2351.                   this.tabbrowser.moveTabOver(aEvent);
  2352.                   break;
  2353.                 case KeyEvent.DOM_VK_HOME:
  2354.                   this.tabbrowser.moveTabToStart();
  2355.                   break;
  2356.                 case KeyEvent.DOM_VK_END:
  2357.                   this.tabbrowser.moveTabToEnd();
  2358.                   break;
  2359.                 default:
  2360.                   // Stop the keypress event for the above keyboard
  2361.                   // shortcuts only.
  2362.                   return;
  2363.               }
  2364.               aEvent.stopPropagation();
  2365.               aEvent.preventDefault();
  2366.             }
  2367.           }
  2368.         }
  2369.       })]]>
  2370.       </field>
  2371.  
  2372.       <property name="userTypedClear"
  2373.                 onget="return this.mCurrentBrowser.userTypedClear;"
  2374.                 onset="return this.mCurrentBrowser.userTypedClear = val;"/>
  2375.  
  2376.       <property name="userTypedValue"
  2377.                 onget="return this.mCurrentBrowser.userTypedValue;"
  2378.                 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
  2379.  
  2380.       <method name="createTooltip">
  2381.         <parameter name="event"/>
  2382.         <body>
  2383.           <![CDATA[
  2384.             event.stopPropagation();
  2385.             var tn = document.tooltipNode;
  2386.             if (tn.localName != "tab")
  2387.               return false; // Not a tab, so cancel the tooltip
  2388.             if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
  2389.               event.target.setAttribute("label", tn.getAttribute("closetabtext"));
  2390.               return true;
  2391.             }
  2392.             if (tn.hasAttribute("label")) {
  2393.               event.target.setAttribute("label", tn.getAttribute("label"));
  2394.               return true;
  2395.             }
  2396.             return false;
  2397.           ]]>
  2398.         </body>
  2399.       </method>
  2400.  
  2401.       <constructor>
  2402.         <![CDATA[
  2403.           this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
  2404.           this.mCurrentTab = this.mTabContainer.firstChild;
  2405.           document.addEventListener("keypress", this._keyEventHandler, false);
  2406.  
  2407.           var uniqueId = "panel" + Date.now();
  2408.           this.mPanelContainer.childNodes[0].id = uniqueId;
  2409.           this.mTabContainer.childNodes[0].linkedPanel = uniqueId;
  2410.           this.mTabContainer.childNodes[0]._tPos = 0;
  2411.           this.mTabContainer.childNodes[0].linkedBrowser = this.mPanelContainer.childNodes[0].firstChild;
  2412.  
  2413.           // set up the shared autoscroll popup
  2414.           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
  2415.           this.appendChild(this._autoScrollPopup);
  2416.           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
  2417.         ]]>
  2418.       </constructor>
  2419.  
  2420.       <destructor>
  2421.         <![CDATA[
  2422.           for (var i = 0; i < this.mTabListeners.length; ++i) {
  2423.             this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
  2424.             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
  2425.             this.mTabFilters[i] = null;
  2426.             this.mTabListeners[i] = null;
  2427.             this.getBrowserAtIndex(i).removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
  2428.           }
  2429.           document.removeEventListener("keypress", this._keyEventHandler, false);
  2430.         ]]>
  2431.       </destructor>
  2432.     </implementation>
  2433.  
  2434.     <handlers>
  2435.       <handler event="DOMWindowClose" phase="capturing">
  2436.         <![CDATA[
  2437.           if (!event.isTrusted)
  2438.             return;
  2439.  
  2440.           const browsers = this.mPanelContainer.childNodes;
  2441.           if (browsers.length == 1) {
  2442.             // There's only one browser left. If a window is being
  2443.             // closed and the window is *not* the window in the
  2444.             // browser that's still around, prevent the event's default
  2445.             // action to prevent closing a window that's being closed
  2446.             // already.
  2447.             if (this.getBrowserAtIndex(0).contentWindow != event.target)
  2448.               event.preventDefault();
  2449.  
  2450.             return;
  2451.           }
  2452.  
  2453.           var i = 0;
  2454.           for (; i < browsers.length; ++i) {
  2455.             if (this.getBrowserAtIndex(i).contentWindow == event.target) {
  2456.               this.removeTab(this.mTabContainer.childNodes[i]);
  2457.               event.preventDefault();
  2458.  
  2459.               break;
  2460.             }
  2461.           }
  2462.         ]]>
  2463.       </handler>
  2464.       <handler event="DOMWillOpenModalDialog" phase="capturing">
  2465.         <![CDATA[
  2466.           if (!event.isTrusted)
  2467.             return;
  2468.  
  2469.           // We're about to open a modal dialog, make sure the opening
  2470.           // tab is brought to the front.
  2471.  
  2472.           var targetTop = event.target.top;
  2473.  
  2474.           for (var i = 0; i < browsers.length; ++i) {
  2475.             if (this.getBrowserAtIndex(i).contentWindow == targetTop) {
  2476.               this.selectedTab = this.mTabContainer.childNodes[i];
  2477.  
  2478.               break;
  2479.             }
  2480.           }
  2481.         ]]>
  2482.       </handler>
  2483.     </handlers>
  2484.   </binding>
  2485.  
  2486.  
  2487.   <binding id="tabbrowser-tabs"
  2488.            extends="chrome://global/content/bindings/tabbox.xml#tabs">
  2489.     <content>
  2490.       <xul:stack flex="1" class="tabs-stack">
  2491.         <xul:vbox>
  2492.           <xul:spacer flex="1"/>
  2493.           <xul:hbox class="tabs-bottom" align="center"/>
  2494.         </xul:vbox>
  2495.         <xul:hbox xbl:inherits="overflow" class="tabs-container">
  2496.           <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
  2497.                               style="min-width: 1px;" chromedir="&locale.dir;"
  2498.                               clicktoscroll="true"
  2499.                               class="tabbrowser-arrowscrollbox">
  2500.             <children/>
  2501.           </xul:arrowscrollbox>
  2502.           <xul:stack align="center" pack="end" chromedir="&locale.dir;">
  2503.             <xul:hbox flex="1" class="tabs-alltabs-box" anonid="alltabs-box"/>
  2504.             <xul:hbox flex="1" class="tabs-alltabs-box-animate" anonid="alltabs-box-animate"/>
  2505.             <xul:toolbarbutton class="tabs-alltabs-button" type="menu" anonid="alltabs-button"
  2506.                                tooltipstring="&listAllTabs.label;">
  2507.               <xul:menupopup class="tabs-alltabs-popup" anonid="alltabs-popup"
  2508.                              position="after_end"/>
  2509.             </xul:toolbarbutton>
  2510.           </xul:stack>
  2511.           <xul:toolbarbutton anonid="tabs-closebutton"
  2512.                              class="close-button tabs-closebutton" chromedir="&locale.dir;"/>
  2513.         </xul:hbox>
  2514.       </xul:stack>
  2515.     </content>
  2516.     <implementation implements="nsITimerCallback, nsIDOMEventListener">
  2517.       <constructor>
  2518.         <![CDATA[
  2519.           var pb2 =
  2520.               Components.classes['@mozilla.org/preferences-service;1'].
  2521.               getService(Components.interfaces.nsIPrefBranch2);
  2522.  
  2523.           try {
  2524.             this.mTabMinWidth = pb2.getIntPref("browser.tabs.tabMinWidth");
  2525.           } catch (e) {
  2526.           }
  2527.           try {
  2528.             this.mTabMaxWidth = pb2.getIntPref("browser.tabs.tabMaxWidth");
  2529.           } catch (e) {
  2530.           }
  2531.           try {
  2532.             this.mTabClipWidth = pb2.getIntPref("browser.tabs.tabClipWidth");
  2533.           } catch (e) {
  2534.           }
  2535.           try {
  2536.             this.mCloseButtons = pb2.getIntPref("browser.tabs.closeButtons");
  2537.           } catch (e) {
  2538.           }
  2539.  
  2540.           this.firstChild.minWidth = this.mTabMinWidth;
  2541.           this.firstChild.maxWidth = this.mTabMaxWidth;
  2542.           this.adjustTabstrip();
  2543.  
  2544.           pb2.addObserver("browser.tabs.closeButtons", 
  2545.                           this._prefObserver, false);
  2546.  
  2547.           window.addEventListener("resize", this, false);
  2548.  
  2549.           // Listen to overflow/underflow events on the tabstrip,
  2550.           // we cannot put these as xbl handlers on the entire binding because
  2551.           // they would also get called for the all-tabs popup scrollbox.
  2552.           // Also, we can't rely on event.target becuase these are all
  2553.           // anonymous nodes.
  2554.           this.mTabstrip.addEventListener("overflow", this, false);
  2555.           this.mTabstrip.addEventListener("underflow", this, false);
  2556.         ]]>
  2557.       </constructor>
  2558.  
  2559.       <destructor>
  2560.         <![CDATA[
  2561.           var pb2 =
  2562.               Components.classes['@mozilla.org/preferences-service;1'].
  2563.               getService(Components.interfaces.nsIPrefBranch2);
  2564.           pb2.removeObserver("browser.tabs.closeButtons", this._prefObserver);
  2565.  
  2566.           // Release timer to avoid reference cycles.
  2567.           if (this._animateTimer) {
  2568.             this._animateTimer.cancel();
  2569.             this._animateTimer = null;
  2570.           }
  2571.  
  2572.           this.mTabstrip.removeEventListener("overflow", this, false);
  2573.           this.mTabstrip.removeEventListener("underflow", this, false);
  2574.         ]]>
  2575.       </destructor>
  2576.  
  2577.       <field name="mTabstripWidth">0</field>
  2578.  
  2579.       <field name="mTabstrip">
  2580.         document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
  2581.       </field>
  2582.  
  2583.       <field name="mTabstripClosebutton">
  2584.         document.getAnonymousElementByAttribute(this, "anonid", "tabs-closebutton");
  2585.       </field>
  2586.  
  2587.       <field name="_prefObserver">({
  2588.         tabbox: this,
  2589.   
  2590.         observe: function(subject, topic, data)
  2591.         {
  2592.           if (topic == "nsPref:changed") {
  2593.             switch (data) {
  2594.             case "browser.tabs.closeButtons":
  2595.               subject.QueryInterface(Components.interfaces.nsIPrefBranch);
  2596.               this.tabbox.mCloseButtons = subject.getIntPref("browser.tabs.closeButtons");
  2597.               this.tabbox.adjustTabstrip();
  2598.               break;
  2599.             }
  2600.           }
  2601.         },
  2602.   
  2603.         QueryInterface: function(aIID)
  2604.         {
  2605.           if (aIID.equals(Components.interfaces.nsIObserver) ||
  2606.               aIID.equals(Components.interfaces.nsISupports))
  2607.             return this;
  2608.           throw Components.results.NS_NOINTERFACE;
  2609.         }
  2610.         });
  2611.       </field>
  2612.       <field name="mTabMinWidth">100</field>
  2613.       <field name="mTabMaxWidth">250</field>
  2614.       <field name="mTabClipWidth">140</field>
  2615.       <field name="mCloseButtons">1</field>
  2616.  
  2617.       <method name="adjustTabstrip">
  2618.         <body><![CDATA[
  2619.           // modes for tabstrip
  2620.           // 0 - activetab  = close button on active tab only
  2621.           // 1 - alltabs    = close buttons on all tabs
  2622.           // 2 - noclose    = no close buttons at all
  2623.           // 3 - closeatend = close button at the end of the tabstrip
  2624.           switch (this.mCloseButtons) {
  2625.           case 0:
  2626.             this.setAttribute("closebuttons", "activetab");
  2627.             break;
  2628.           case 1:
  2629.             var width = this.firstChild.boxObject.width;
  2630.             // 0 width is an invalid value and indicates
  2631.             // an item without display, so ignore.
  2632.             if (width > this.mTabClipWidth || width == 0)
  2633.               this.setAttribute("closebuttons", "alltabs");
  2634.             else
  2635.               this.setAttribute("closebuttons", "activetab");
  2636.             break;
  2637.           case 2:
  2638.           case 3:
  2639.             this.setAttribute("closebuttons", "noclose");
  2640.             break;
  2641.           }
  2642.           this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
  2643.         ]]></body>
  2644.       </method>
  2645.         
  2646.       <field name="_mPrefs">null</field>
  2647.       <property name="mPrefs" readonly="true">
  2648.         <getter>
  2649.         <![CDATA[
  2650.           if (!this._mPrefs) {
  2651.             this._mPrefs =
  2652.               Components.classes['@mozilla.org/preferences-service;1'].
  2653.               getService(Components.interfaces.nsIPrefBranch2);
  2654.           }
  2655.           return this._mPrefs;
  2656.         ]]>
  2657.         </getter>
  2658.       </property>
  2659.         
  2660.       <method name="_handleTabSelect">
  2661.         <body><![CDATA[
  2662.           this.mTabstrip.ensureElementIsVisible(this.selectedItem);
  2663.         ]]></body>
  2664.       </method>
  2665.  
  2666.       <method name="handleEvent">
  2667.         <parameter name="aEvent"/>
  2668.         <body><![CDATA[
  2669.           switch (aEvent.type) {
  2670.             case "overflow":
  2671.               this.setAttribute("overflow", "true");
  2672.               this.mTabstrip.scrollBoxObject
  2673.                             .ensureElementIsVisible(this.selectedItem);
  2674.               break;
  2675.             case "underflow":
  2676.               this.removeAttribute("overflow");
  2677.               break;
  2678.             case "resize":
  2679.               var width = this.mTabstrip.boxObject.width;
  2680.               if (width != this.mTabstripWidth) {
  2681.                 this.adjustTabstrip();
  2682.                 // XXX without this line the tab bar won't budge
  2683.                 this.mTabstrip.scrollByPixels(1);
  2684.                 this._handleTabSelect();
  2685.                 this.mTabstripWidth = width;
  2686.               }
  2687.               break;
  2688.           }
  2689.         ]]></body>
  2690.       </method>
  2691.  
  2692.       <field name="mAllTabsPopup">
  2693.         document.getAnonymousElementByAttribute(this, 
  2694.                                                 "anonid", "alltabs-popup");
  2695.       </field>
  2696.  
  2697.       <field name="mAllTabsBoxAnimate">
  2698.         document.getAnonymousElementByAttribute(this, 
  2699.                                                 "anonid",
  2700.                                                 "alltabs-box-animate");
  2701.       </field>
  2702.  
  2703.  
  2704.       <field name="mAllTabsButton">
  2705.         document.getAnonymousElementByAttribute(this, 
  2706.                                                 "anonid", "alltabs-button");
  2707.       </field>
  2708.  
  2709.       <field name="_animateTimer">null</field>
  2710.       <field name="_animateStep">-1</field>
  2711.       <field name="_animateDelay">25</field>
  2712.       <field name="_animatePercents">
  2713.        [1.00, 0.85, 0.80, 0.75, 0.71, 0.68, 0.65, 0.62, 0.59, 0.57,
  2714.         0.54, 0.52, 0.50, 0.47, 0.45, 0.44, 0.42, 0.40, 0.38, 0.37,
  2715.         0.35, 0.34, 0.32, 0.31, 0.30, 0.29, 0.28, 0.27, 0.26, 0.25,
  2716.         0.24, 0.23, 0.23, 0.22, 0.22, 0.21, 0.21, 0.21, 0.20, 0.20,
  2717.         0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.19, 0.19, 0.19, 0.18,
  2718.         0.18, 0.17, 0.17, 0.16, 0.15, 0.14, 0.13, 0.11, 0.09, 0.06]
  2719.       </field>
  2720.  
  2721.       <method name="_stopAnimation">
  2722.         <body><![CDATA[
  2723.           if (this._animateStep != -1) {
  2724.             if (this._animateTimer)
  2725.               this._animateTimer.cancel();
  2726.  
  2727.             this._animateStep = -1;
  2728.             this.mAllTabsBoxAnimate.style.opacity = 0.0;
  2729.           }
  2730.         ]]></body>
  2731.       </method>
  2732.       
  2733.       <method name="_notifyBackgroundTab">
  2734.         <parameter name="aTab"/>
  2735.         <body><![CDATA[
  2736.           var tsbo = this.mTabstrip.scrollBoxObject;
  2737.           var tsboStart = tsbo.screenX;
  2738.           var tsboEnd = tsboStart + tsbo.width;
  2739.  
  2740.           var ctbo = aTab.boxObject;
  2741.           var ctboStart = ctbo.screenX;
  2742.           var ctboEnd = ctboStart + ctbo.width;
  2743.  
  2744.           // Is the new tab already completely visible?
  2745.           if (tsboStart <= ctboStart && ctboEnd <= tsboEnd)
  2746.             return;
  2747.  
  2748.           if (this.mTabstrip.smoothScroll) {
  2749.             var selStart = this.selectedItem.boxObject.screenX;
  2750.             var selEnd = selStart + this.selectedItem.boxObject.width;
  2751.  
  2752.             // Can we make both the new tab and the selected tab completely visible?
  2753.             if (Math.max(ctboEnd - selStart, selEnd - ctboStart) <= tsbo.width) {
  2754.               this.mTabstrip.ensureElementIsVisible(aTab);
  2755.               return;
  2756.             }
  2757.  
  2758.             this.mTabstrip._smoothScrollByPixels(this.mTabstrip._isLTRScrollbox ?
  2759.                                                  selStart - tsboStart : selEnd - tsboEnd);
  2760.           }
  2761.  
  2762.           // start the flash timer
  2763.           this._animateStep = 0;
  2764.  
  2765.           if (!this._animateTimer) 
  2766.             this._animateTimer =
  2767.               Components.classes["@mozilla.org/timer;1"]
  2768.                         .createInstance(Components.interfaces.nsITimer);
  2769.           else
  2770.              this._animateTimer.cancel();
  2771.  
  2772.           this._animateTimer.initWithCallback(this, this._animateDelay,
  2773.                                               this._animateTimer.TYPE_REPEATING_SLACK);
  2774.         ]]></body>
  2775.       </method>
  2776.       
  2777.       <method name="notify">
  2778.         <parameter name="aTimer"/>
  2779.         <body><![CDATA[
  2780.           if (!document)
  2781.             aTimer.cancel();
  2782.  
  2783.           var percent = this._animatePercents[this._animateStep];
  2784.           this.mAllTabsBoxAnimate.style.opacity = percent;
  2785.  
  2786.           if (this._animateStep < (this._animatePercents.length - 1))
  2787.             this._animateStep++;
  2788.           else
  2789.             this._stopAnimation();
  2790.         ]]></body>
  2791.       </method>
  2792.     </implementation>
  2793.     <handlers>
  2794.       <handler event="TabSelect" action="this._handleTabSelect();"/>
  2795.       <handler event="mouseover"><![CDATA[
  2796.         if (event.originalTarget == this.mAllTabsButton) {
  2797.           this.mAllTabsButton
  2798.               .setAttribute("tooltiptext",
  2799.                             this.mAllTabsButton.getAttribute("tooltipstring"));
  2800.         }
  2801.         else
  2802.           this.mAllTabsButton.removeAttribute("tooltiptext");
  2803.       ]]></handler>
  2804.     </handlers>
  2805.   </binding>
  2806.  
  2807.   <!-- alltabs-popup binding
  2808.        This binding relies on the structure of the tabbrowser binding.
  2809.        Therefore it should only be used as a child of the tabs element.
  2810.        This binding is exposed as a pseudo-public-API so themes can customize
  2811.        the tabbar appearance without having to be scriptable
  2812.        (see globalBindings.xml in Pinstripe for example).
  2813.   -->
  2814.   <binding id="tabbrowser-alltabs-popup"
  2815.            extends="chrome://global/content/bindings/popup.xml#popup">
  2816.     <implementation implements="nsIDOMEventListener">
  2817.       <field name="_xulWindow">
  2818.         null
  2819.       </field>
  2820.  
  2821.       <constructor><![CDATA[
  2822.         // We cannot cache the XULBrowserWindow object itself since it might
  2823.         // be set after this binding is constructed.
  2824.         try {
  2825.           this._xulWindow = 
  2826.             window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  2827.                   .getInterface(Components.interfaces.nsIWebNavigation)
  2828.                   .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
  2829.                   .treeOwner
  2830.                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  2831.                   .getInterface(Components.interfaces.nsIXULWindow);
  2832.         }
  2833.         catch(ex) { }
  2834.       ]]></constructor>
  2835.  
  2836.       <method name="_menuItemOnCommand">
  2837.         <parameter name="aEvent"/>
  2838.         <body><![CDATA[
  2839.           var tabcontainer = document.getBindingParent(this);
  2840.           tabcontainer.selectedItem = aEvent.target.tab;
  2841.         ]]></body>
  2842.       </method>
  2843.  
  2844.       <method name="_tabOnAttrModified">
  2845.         <parameter name="aEvent"/>
  2846.         <body><![CDATA[
  2847.           var menuItem = aEvent.target.mCorrespondingMenuitem;
  2848.           if (menuItem) {
  2849.             var attrName = aEvent.attrName;
  2850.             switch (attrName) {
  2851.               case "label":
  2852.               case "crop":
  2853.               case "busy":
  2854.               case "image":
  2855.               case "selected":
  2856.                 if (aEvent.attrChange == aEvent.REMOVAL)
  2857.                   menuItem.removeAttribute(attrName);
  2858.                 else
  2859.                   menuItem.setAttribute(attrName, aEvent.newValue);
  2860.             }
  2861.           }
  2862.         ]]></body>
  2863.       </method>
  2864.  
  2865.       <method name="_tabOnTabClose">
  2866.         <parameter name="aEvent"/>
  2867.         <body><![CDATA[
  2868.           var menuItem = aEvent.target.mCorrespondingMenuitem;
  2869.           if (menuItem)
  2870.             this.removeChild(menuItem);
  2871.         ]]></body>
  2872.       </method>
  2873.  
  2874.       <method name="handleEvent">
  2875.         <parameter name="aEvent"/>
  2876.         <body><![CDATA[
  2877.           if (!aEvent.isTrusted)
  2878.             return;
  2879.  
  2880.           switch (aEvent.type) {
  2881.             case "command":
  2882.               this._menuItemOnCommand(aEvent);
  2883.               break;
  2884.             case "DOMAttrModified":
  2885.               this._tabOnAttrModified(aEvent);
  2886.               break;
  2887.             case "TabClose":
  2888.               this._tabOnTabClose(aEvent);
  2889.               break;
  2890.             case "TabOpen":
  2891.               this._createTabMenuItem(aEvent.originalTarget);
  2892.               break;
  2893.             case "scroll":
  2894.               this._updateTabsVisibilityStatus();
  2895.               break;
  2896.           }
  2897.         ]]></body>
  2898.       </method>
  2899.  
  2900.       <method name="_updateTabsVisibilityStatus">
  2901.         <body><![CDATA[
  2902.           var tabContainer = document.getBindingParent(this);
  2903.           // We don't want menu item decoration unless there is overflow.
  2904.           if (tabContainer.getAttribute("overflow") != "true")
  2905.             return;
  2906.  
  2907.           var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
  2908.           for (var i = 0; i < this.childNodes.length; i++) {
  2909.             var curTabBO = this.childNodes[i].tab.boxObject;
  2910.             if (curTabBO.screenX >= tabstripBO.screenX &&
  2911.                 curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
  2912.               this.childNodes[i].setAttribute("tabIsVisible", "true"); 
  2913.             else
  2914.               this.childNodes[i].removeAttribute("tabIsVisible");
  2915.           }
  2916.         ]]></body>
  2917.       </method>
  2918.  
  2919.       <method name="_createTabMenuItem">
  2920.         <parameter name="aTab"/>
  2921.         <body><![CDATA[
  2922.           var menuItem = document.createElementNS(
  2923.             "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", 
  2924.             "menuitem");
  2925.  
  2926.           menuItem.setAttribute("class", "menuitem-iconic alltabs-item");
  2927.  
  2928.           menuItem.setAttribute("label", aTab.label);
  2929.           menuItem.setAttribute("crop", aTab.getAttribute("crop"));
  2930.           menuItem.setAttribute("image", aTab.getAttribute("image"));
  2931.  
  2932.           if (aTab.hasAttribute("busy"))
  2933.             menuItem.setAttribute("busy", aTab.getAttribute("busy"));
  2934.           if (aTab.selected)
  2935.             menuItem.setAttribute("selected", "true");
  2936.  
  2937.           // Keep some attributes of the menuitem in sync with its
  2938.           // corresponding tab (e.g. the tab label)
  2939.           aTab.mCorrespondingMenuitem = menuItem;
  2940.           aTab.addEventListener("DOMAttrModified", this, false);
  2941.           aTab.addEventListener("TabClose", this, false);
  2942.           menuItem.tab = aTab;
  2943.           menuItem.addEventListener("command", this, false);
  2944.  
  2945.           this.appendChild(menuItem);
  2946.           return menuItem;
  2947.         ]]></body>
  2948.       </method>
  2949.     </implementation>
  2950.  
  2951.     <handlers>
  2952.       <handler event="popupshowing">
  2953.       <![CDATA[
  2954.         // set up the menu popup
  2955.         var tabcontainer = document.getBindingParent(this);
  2956.         var tabs = tabcontainer.childNodes;
  2957.  
  2958.         // Listen for changes in the tab bar.
  2959.         var tabbrowser = document.getBindingParent(tabcontainer);
  2960.         tabbrowser.addEventListener("TabOpen", this, false);
  2961.         tabcontainer.mTabstrip.addEventListener("scroll", this, false);
  2962.  
  2963.         // if an animation is in progress and the user
  2964.         // clicks on the "all tabs" button, stop the animation
  2965.         tabcontainer._stopAnimation();
  2966.  
  2967.         for (var i = 0; i < tabs.length; i++) {
  2968.           this._createTabMenuItem(tabs[i]);
  2969.         }
  2970.         this._updateTabsVisibilityStatus();
  2971.       ]]></handler>
  2972.  
  2973.       <handler event="popuphiding">
  2974.       <![CDATA[
  2975.         // clear out the menu popup and remove the listeners
  2976.         while (this.hasChildNodes()) {
  2977.           var menuItem = this.lastChild;
  2978.           menuItem.removeEventListener("command", this, false);
  2979.           menuItem.tab.removeEventListener("DOMAttrModified", this, false);
  2980.           menuItem.tab.removeEventListener("TabClose", this, false);
  2981.           menuItem.tab.mCorrespondingMenuitem = null;
  2982.           this.removeChild(menuItem);
  2983.         }
  2984.         var tabcontainer = document.getBindingParent(this);
  2985.         tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
  2986.         document.getBindingParent(tabcontainer).removeEventListener("TabOpen", this, false);
  2987.       ]]></handler>
  2988.  
  2989.       <handler event="DOMMenuItemActive">
  2990.       <![CDATA[
  2991.         if (!this._xulWindow || !this._xulWindow.XULBrowserWindow)
  2992.           return;
  2993.  
  2994.         var tab = event.target.tab;
  2995.         if (tab) {
  2996.           var statusText = tab.linkedBrowser.currentURI.spec;
  2997.           if (statusText == "about:blank") {
  2998.             // XXXhack: Passing a space here (and not "")
  2999.             // to make sure the the browser implementation would
  3000.             // still consider it a hovered link.
  3001.             statusText = " ";
  3002.           }
  3003.  
  3004.           this._xulWindow.XULBrowserWindow.setOverLink(statusText, null);
  3005.         }
  3006.       ]]></handler>
  3007.  
  3008.       <handler event="DOMMenuItemInactive">
  3009.       <![CDATA[
  3010.         if (!this._xulWindow || !this._xulWindow.XULBrowserWindow)
  3011.           return;
  3012.  
  3013.         this._xulWindow.XULBrowserWindow.setOverLink("", null);
  3014.       ]]></handler>
  3015.     </handlers>
  3016.   </binding>
  3017.  
  3018.   <!-- close-tab-button binding
  3019.        This binding relies on the structure of the tabbrowser binding.
  3020.        Therefore it should only be used as a child of the tab or the tabs
  3021.        element (in both cases, when they are anonymous nodes of <tabbrowser>).
  3022.        This binding is exposed as a pseudo-public-API so themes can customize
  3023.        the tabbar appearance without having to be scriptable
  3024.        (see globalBindings.xml in Pinstripe for example).
  3025.   -->
  3026.   <binding id="tabbrowser-close-tab-button"
  3027.            extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image">
  3028.     <handlers>
  3029.       <handler event="click" button="0"><![CDATA[
  3030.         var bindingParent = document.getBindingParent(this);
  3031.         if (bindingParent) {
  3032.           var tabbedBrowser = document.getBindingParent(bindingParent);
  3033.           if (bindingParent.localName == "tab") {
  3034.             /* The only sequence in which a second click event (i.e. dblclik)
  3035.              * can be dispatched on an in-tab close button is when it is shown
  3036.              * after the first click (i.e. the first click event was dispatched
  3037.              * on the tab). This happens when we show the close button only on
  3038.              * the active tab. (bug 352021)
  3039.              * The only sequence in which a third click event can be dispatched
  3040.              * on an in-tab close button is when the tab was opened with a
  3041.              * double click on the tabbar. (bug 378344)
  3042.              * In both cases, it is most likely that the close button area has
  3043.              * been accidentally clicked, therefore we do not close the tab.
  3044.              *
  3045.              * We don't want to ignore processing of more than one click event,
  3046.              * though, since the user might actually be repeatedly clicking to
  3047.              * close many tabs at once.
  3048.              */
  3049.             if (event.detail > 1 && !this._ignoredClick) {
  3050.               this._ignoredClick = true;
  3051.               return;
  3052.             }
  3053.  
  3054.             // Reset the "ignored click" flag
  3055.             this._ignoredClick = false;
  3056.  
  3057.             tabbedBrowser.removeTab(bindingParent);
  3058.             tabbedBrowser._blockDblClick = true;
  3059.  
  3060.             /* XXXmano hack (see bug 343628):
  3061.              * Since we're removing the event target, if the user
  3062.              * double-clicks this button, the dblclick event will be dispatched
  3063.              * with the tabbar as its event target (and explicit/originalTarget),
  3064.              * which treats that as a mouse gesture for opening a new tab.
  3065.              * In this context, we're manually blocking the dblclick event
  3066.              * (see onTabBarDblClick).
  3067.              */
  3068.             var clickedOnce = false;
  3069.             function enableDblClick(event) {
  3070.               if (event.detail == 1 && !clickedOnce) {
  3071.                 clickedOnce = true;
  3072.                 return;
  3073.               }
  3074.               setTimeout(function() {
  3075.                 tabbedBrowser._blockDblClick = false;
  3076.               }, 0);
  3077.               tabbedBrowser.removeEventListener("click", enableDblClick, false);
  3078.             }
  3079.             tabbedBrowser.addEventListener("click", enableDblClick, false);
  3080.           }
  3081.           else // "tabs"
  3082.             tabbedBrowser.removeCurrentTab();
  3083.         }
  3084.       ]]></handler>
  3085.       <handler event="dblclick" button="0" phase="capturing">
  3086.         // for the one-close-button case
  3087.         event.stopPropagation();
  3088.       </handler>
  3089.     </handlers>
  3090.   </binding>
  3091.  
  3092.   <binding id="tabbrowser-tab" display="xul:hbox"
  3093.            extends="chrome://global/content/bindings/tabbox.xml#tab">
  3094.     <content chromedir="&locale.dir;"
  3095.              closetabtext="&closeTab.label;">
  3096.       <xul:image xbl:inherits="validate,src=image" class="tab-icon-image"/>
  3097.       <xul:label flex="1" xbl:inherits="value=label,crop,accesskey" class="tab-text"/>
  3098.       <xul:toolbarbutton anonid="close-button" tabindex="-1" class="tab-close-button"/>
  3099.     </content>
  3100.  
  3101.     <implementation>
  3102.       <field name="mOverCloseButton">false</field>
  3103.       <field name="mCorrespondingMenuitem">null</field>
  3104.     </implementation>
  3105.  
  3106.     <handlers>
  3107.       <handler event="mouseover">
  3108.         var anonid = event.originalTarget.getAttribute("anonid");
  3109.         if (anonid == "close-button")
  3110.           this.mOverCloseButton = true;
  3111.       </handler>
  3112.       <handler event="mouseout">
  3113.         var anonid = event.originalTarget.getAttribute("anonid");
  3114.         if (anonid == "close-button")
  3115.           this.mOverCloseButton = false;
  3116.       </handler>
  3117.       <handler event="mousedown" button="0" phase="capturing">
  3118.       <![CDATA[
  3119.         if (this.mOverCloseButton)
  3120.           event.stopPropagation();
  3121.       ]]>
  3122.       </handler>
  3123.     </handlers>
  3124.   </binding>
  3125.  
  3126. </bindings>
  3127.