home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 May / PCWorld_2003-05_cd.bin / Komunik / phoenix / chrome / toolkit.jar / content / global / bindings / tabbrowser.xml < prev    next >
Extensible Markup Language  |  2002-11-27  |  46KB  |  1,214 lines

  1. <?xml version="1.0"?>
  2.  
  3. <!--
  4.    - The contents of this file are subject to the Mozilla Public
  5.    - License Version 1.1 (the "License"); you may not use this file
  6.    - except in compliance with the License. You may obtain a copy of
  7.    - the License at http://www.mozilla.org/MPL/
  8.    -
  9.    - Software distributed under the License is distributed on an "AS
  10.    - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11.    - implied. See the License for the specific language governing
  12.    - rights and limitations under the License.
  13.    -
  14.    - The Original Code is this file as it was released on
  15.    - March 28, 2001.
  16.    -
  17.    - The Initial Developer of the Original Code is Peter Annema.
  18.    - Portions created by Peter Annema are Copyright (C) 2001
  19.    - Peter Annema.  All Rights Reserved.
  20.    -
  21.    - Contributor(s):
  22.    -   David Hyatt <hyatt@netscape.com> (Original Author of <tabbrowser>)
  23.    -
  24.    - Alternatively, the contents of this file may be used under the
  25.    - terms of the GNU General Public License Version 2 or later (the
  26.    - "GPL"), in which case the provisions of the GPL are applicable
  27.    - instead of those above.  If you wish to allow use of your
  28.    - version of this file only under the terms of the GPL and not to
  29.    - allow others to use your version of this file under the MPL,
  30.    - indicate your decision by deleting the provisions above and
  31.    - replace them with the notice and other provisions required by
  32.    - the GPL.  If you do not delete the provisions above, a recipient
  33.    - may use your version of this file under either the MPL or the
  34.    - GPL.
  35.   -->
  36.  
  37. <!DOCTYPE bindings [
  38. <!ENTITY % tabBrowserDTD SYSTEM "chrome://global/locale/tabbrowser.dtd" >
  39. %tabBrowserDTD;
  40. ]>
  41.  
  42. <bindings id="tabBrowserBindings"
  43.           xmlns="http://www.mozilla.org/xbl"
  44.           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  45.           xmlns:xbl="http://www.mozilla.org/xbl">
  46.  
  47.   <binding id="tabbrowser">
  48.     <resources>
  49.       <stylesheet src="chrome://global/skin/browser.css"/>
  50.     </resources>
  51.  
  52.     <content>
  53.       <xul:stringbundle src="chrome://global/locale/tabbrowser.properties"/>
  54.       <xul:tabbox flex="1" eventnode="document">
  55.         <xul:hbox class="tabbrowser-strip chromeclass-toolbar" collapsed="true" tooltip="_child" context="_child">
  56.           <xul:tooltip onpopupshowing="event.preventBubble(); if (document.tooltipNode.hasAttribute('label')) { this.setAttribute('label', document.tooltipNode.getAttribute('label')); return true; } return false;"/>
  57.           <xul:menupopup onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
  58.             <xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;"
  59.                           xbl:inherits="oncommand=onnewtab"/>
  60.             <xul:menuseparator/>
  61.             <xul:menuitem label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
  62.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  63.                                      tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
  64.             <xul:menuitem label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
  65.                           tbattr="tabbrowser-multiple"
  66.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  67.                                      tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
  68.             <xul:menuseparator/>
  69.             <xul:menuitem label="&closeTab.label;" accesskey="&closeTab.accesskey;"
  70.                           tbattr="tabbrowser-multiple"
  71.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  72.                                      tabbrowser.removeTab(tabbrowser.mContextTab);"/>
  73.             <xul:menuseparator/>
  74.             <xul:menuitem label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
  75.                           tbattr="tabbrowser-multiple"
  76.                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
  77.                                      tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
  78.           </xul:menupopup>
  79.  
  80.           <xul:tabs class="tabbrowser-tabs" closebutton="true" flex="1"
  81.                     tooltiptextnew="&newTabButton.tooltip;"
  82.                     onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
  83.                     ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode.parentNode);
  84.                                 event.stopPropagation();"
  85.                     ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode.parentNode);
  86.                                 event.stopPropagation();"
  87.                     xbl:inherits="onnewtab"
  88.                     onclosetab="var node = this.parentNode;
  89.                                 while (node.localName != 'tabbrowser')
  90.                                   node = node.parentNode;
  91.                                 node.removeCurrentTab();">
  92.             <xul:tab validate="never" 
  93.                      onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image'));
  94.                               this.removeAttribute('image');"
  95.                      maxwidth="250" width="0" minwidth="30" flex="100"
  96.                      class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
  97.           </xul:tabs>
  98.         </xul:hbox>
  99.         <xul:tabpanels flex="1" class="plain">
  100.           <xul:browser type="content-primary" xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu"/>
  101.         </xul:tabpanels>
  102.       </xul:tabbox>
  103.       <children/>
  104.     </content>
  105.     <implementation>
  106.       <field name="mPrefs" readonly="true">
  107.         Components.classes['@mozilla.org/preferences-service;1']
  108.                   .getService(Components.interfaces.nsIPrefService)
  109.                   .getBranch(null);
  110.       </field>
  111.       <field name="mURIFixup" readonly="true">
  112.         Components.classes["@mozilla.org/docshell/urifixup;1"]
  113.                   .getService(Components.interfaces.nsIURIFixup);
  114.       </field>
  115.       <field name="mTabBox">
  116.         document.getAnonymousNodes(this)[1]
  117.       </field>
  118.       <field name="mStrip">
  119.         this.mTabBox.firstChild
  120.       </field>
  121.       <field name="mTabContainer">
  122.         this.mStrip.childNodes[2]
  123.       </field>
  124.       <field name="mPanelContainer">
  125.         this.mTabBox.childNodes[1]
  126.       </field>
  127.       <field name="mStringBundle">
  128.         document.getAnonymousNodes(this)[0]
  129.       </field>
  130.       <field name="mCurrentTab">
  131.         null
  132.       </field>
  133.       <field name="mCurrentBrowser">
  134.         null
  135.       </field>
  136.       <field name="mProgressListeners">
  137.         null
  138.       </field>
  139.       <field name="mTabListeners">
  140.         new Array()
  141.       </field>
  142.       <field name="mTabFilters">
  143.         new Array()
  144.       </field>
  145.       <field name="mTabbedMode">
  146.         false
  147.       </field>
  148.       <field name="mIsBusy">
  149.         false
  150.       </field>
  151.       <field name="mMissedIconCache">
  152.         null
  153.       </field>
  154.       <field name="mContextTab">
  155.         null
  156.       </field>
  157.  
  158.       <!-- A web progress listener object definition for a given tab. --> 
  159.       <method name="mTabProgressListener">
  160.         <parameter name="aTab"/>
  161.         <parameter name="aStartsBlank"/>
  162.         <body>
  163.         <![CDATA[
  164.  
  165.           //XXXjrgm do we need this? Is the check of 'aStatus' below ever true?
  166.           const NS_ERROR_MODULE_NETWORK = 0x804b0000;
  167.           const NS_NET_STATUS_READ_FROM = NS_ERROR_MODULE_NETWORK + 8;
  168.           const NS_NET_STATUS_WROTE_TO  = NS_ERROR_MODULE_NETWORK + 9;
  169.  
  170.           return ({
  171.             mTabBrowser: this,
  172.             mTab: aTab,
  173.             mBlank: aStartsBlank,
  174.             mIcon: "", 
  175.  
  176.             onProgressChange : function (aWebProgress, aRequest,
  177.                                          aCurSelfProgress, aMaxSelfProgress,
  178.                                          aCurTotalProgress, aMaxTotalProgress) {
  179.               if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
  180.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  181.                   var p = this.mTabBrowser.mProgressListeners[i];
  182.                   if (p)
  183.                     p.onProgressChange(aWebProgress, aRequest,
  184.                                        aCurSelfProgress, aMaxSelfProgress,
  185.                                        aCurTotalProgress, aMaxTotalProgress);
  186.                 }
  187.               }
  188.             },
  189.             
  190.             onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
  191.             {
  192.               if (!aRequest)
  193.                 return;
  194.       
  195.               //XXXjrgm do we need this? Is the check of 'aStatus' below ever true?
  196.               //ignore local/resource:/chrome: files
  197.               if (aStatus == NS_NET_STATUS_READ_FROM || aStatus == NS_NET_STATUS_WROTE_TO)
  198.                 return;
  199.  
  200.               var oldBlank = this.mBlank;
  201.  
  202.               const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  203.               const nsIChannel = Components.interfaces.nsIChannel;
  204.               if (!this.mBlank && aStateFlags & nsIWebProgressListener.STATE_START && 
  205.                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
  206.                 this.mTab.setAttribute("busy", "true"); 
  207.                 this.mTab.label = this.mTabBrowser.mStringBundle.getString("tabs.loading");
  208.                 this.mTab.removeAttribute("image");
  209.                 this.mIcon = "";
  210.  
  211.                 if (this.mTabBrowser.mCurrentTab == this.mTab)
  212.                   this.mTabBrowser.mIsBusy = true;
  213.               }
  214.               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
  215.                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
  216.                 if (this.mBlank)
  217.                   this.mBlank = false;
  218.                 
  219.                 this.mTab.removeAttribute("busy");
  220.                   
  221.                 var location = aRequest.QueryInterface(nsIChannel).URI;
  222.                 if (this.mIcon) {
  223.                   this.mTab.setAttribute("image", this.mIcon);
  224.                   mIcon = "";
  225.                 }
  226.                 else if (this.mTabBrowser.shouldLoadFavIcon(location))
  227.                   this.mTabBrowser.loadFavIcon(location, "image", this.mTab); 
  228.  
  229.                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
  230.                   this.mTabBrowser.setTabTitle(this.mTab);
  231.  
  232.                 if (this.mTabBrowser.mCurrentTab == this.mTab)
  233.                   this.mTabBrowser.mIsBusy = false;
  234.               }
  235.  
  236.               if (!oldBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
  237.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  238.                   var p = this.mTabBrowser.mProgressListeners[i];
  239.                   if (p)
  240.                     p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
  241.                 }
  242.               }
  243.             }
  244.             ,
  245.  
  246.             onLocationChange : function(aWebProgress, aRequest, aLocation) {
  247.               if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
  248.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  249.                   var p = this.mTabBrowser.mProgressListeners[i];
  250.                   if (p)
  251.                     p.onLocationChange(aWebProgress, aRequest, aLocation);
  252.                 }
  253.               }
  254.             },
  255.  
  256.             onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {
  257.               //XXXjrgm do we need this? Is the check of 'aStatus' below ever true?
  258.               //ignore local/resource:/chrome: files
  259.               if (this.mBlank || aStatus == NS_NET_STATUS_READ_FROM || aStatus == NS_NET_STATUS_WROTE_TO)
  260.                 return;
  261.               
  262.               if (this.mTabBrowser.mCurrentTab == this.mTab) {
  263.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  264.                   var p = this.mTabBrowser.mProgressListeners[i];
  265.                   if (p)
  266.                     p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
  267.                 }
  268.               }
  269.             },
  270.  
  271.             onSecurityChange : function(aWebProgress, aRequest, aState) { 
  272.               if (this.mTabBrowser.mCurrentTab == this.mTab) {
  273.                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
  274.                   var p = this.mTabBrowser.mProgressListeners[i];
  275.                   if (p)
  276.                     p.onSecurityChange(aWebProgress, aRequest, aState);
  277.                 }
  278.               }
  279.             },
  280.  
  281.             QueryInterface : function(aIID)
  282.             {
  283.               if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  284.                   aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  285.                   aIID.equals(Components.interfaces.nsISupports))
  286.                 return this;
  287.               throw Components.results.NS_NOINTERFACE;
  288.             }
  289.                   
  290.             });
  291.         ]]>
  292.         </body>
  293.       </method>
  294.  
  295.       <method name="buildFavIconString">
  296.         <parameter name="aURI"/>
  297.         <body>
  298.           <![CDATA[
  299.             var end = (aURI.port == -1) ? "/favicon.ico" : (":" + aURI.port + "/favicon.ico");
  300.             return aURI.scheme + "://" + aURI.host + end;
  301.           ]]>
  302.         </body>
  303.       </method>
  304.  
  305.       <method name="shouldLoadFavIcon">
  306.         <parameter name="aURI"/>
  307.         <body>
  308.           <![CDATA[
  309.             return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
  310.                     this.mPrefs.getBoolPref("browser.chrome.favicons") && 
  311.                     ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
  312.           ]]>
  313.         </body>
  314.       </method>
  315.  
  316.       <method name="loadFavIcon">
  317.         <parameter name="aURI"/>
  318.         <parameter name="aAttr"/>
  319.         <parameter name="aElt"/>
  320.         <body>
  321.           <![CDATA[
  322.             var iconURL = this.buildFavIconString(aURI);
  323.             if (!this.mMissedIconCache) {
  324.               var cacheService = Components.classes['@mozilla.org/network/cache-service;1'].getService(Components.interfaces.nsICacheService);
  325.               this.mMissedIconCache = cacheService.createSession("MissedIconCache", Components.interfaces.nsICache.STORE_ANYWHERE, true);
  326.               if (!this.mMissedIconCache)
  327.                 return;
  328.             }
  329.         
  330.             try {
  331.               var entry = this.mMissedIconCache.openCacheEntry(iconURL, Components.interfaces.nsICache.ACCESS_READ, true);
  332.             }
  333.             catch (exc) {}
  334.             if (!entry)
  335.               aElt.setAttribute(aAttr, iconURL);
  336.             else {
  337.               entry.close();
  338.               entry = null;
  339.             }
  340.           ]]>
  341.         </body>
  342.       </method>
  343.  
  344.       <method name="addToMissedIconCache">
  345.         <parameter name="aURI"/>
  346.         <body>
  347.           <![CDATA[
  348.             var entry = this.mMissedIconCache.openCacheEntry(aURI, Components.interfaces.nsICache.ACCESS_READ_WRITE, true);
  349.             if (entry.accessGranted == Components.interfaces.nsICache.ACCESS_WRITE)
  350.               // It's a new entry.  Just write a bit of metadata in to the entry.
  351.               entry.setMetaDataElement("Icon", "Missed");
  352.             entry.markValid();
  353.             entry.close();
  354.           ]]>
  355.         </body>
  356.       </method>
  357.  
  358.       <method name="updateTitlebar">
  359.         <body>
  360.           <![CDATA[
  361.             var newTitle = "";
  362.             var docTitle;
  363.             if (this.docShell.contentViewer)
  364.               docTitle = this.contentTitle;
  365.             
  366.             if (docTitle) {
  367.               newTitle += this.ownerDocument.documentElement.getAttribute("titlepreface");
  368.               newTitle += docTitle;
  369.               newTitle += this.ownerDocument.documentElement.getAttribute("titlemenuseparator");
  370.             }
  371.             newTitle += this.ownerDocument.documentElement.getAttribute("titlemodifier");
  372.             window.title = newTitle;
  373.           ]]>
  374.         </body>
  375.       </method>
  376.  
  377.       <method name="updatePopupMenu">
  378.         <parameter name="aPopupMenu"/>
  379.         <body>
  380.           <![CDATA[
  381.             this.mContextTab = document.popupNode;
  382.             var disabled = this.mPanelContainer.childNodes.length == 1;
  383.             var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
  384.             for (var i = 0; i < menuItems.length; i++)
  385.               menuItems[i].setAttribute("disabled", disabled);
  386.           ]]>
  387.         </body>
  388.       </method>
  389.  
  390.       <method name="updateCurrentBrowser">
  391.         <body>
  392.           <![CDATA[
  393.             var newBrowser = this.mPanelContainer.childNodes[this.mPanelContainer.selectedIndex];
  394.             if (this.mCurrentBrowser)
  395.               this.mCurrentBrowser.setAttribute("type", "content");
  396.             
  397.             newBrowser.setAttribute("type", "content-primary");
  398.             this.mCurrentBrowser = newBrowser;
  399.             this.mCurrentTab = this.selectedTab;
  400.  
  401.             // Update the URL bar.
  402.             var loc = this.mCurrentBrowser.currentURI;
  403.             var webProgress = this.mCurrentBrowser.webProgress;
  404.             var securityUI = this.mCurrentBrowser.securityUI;
  405.             var i, p;
  406.             for (i = 0; i < this.mProgressListeners.length; i++) {
  407.               p = this.mProgressListeners[i];
  408.               if (p) {
  409.                 p.onLocationChange(webProgress, null, loc);
  410.                 if (securityUI)
  411.                   p.onSecurityChange(webProgress, null, securityUI.state);
  412.                 var listener = this.mTabListeners[this.mPanelContainer.selectedIndex];
  413.                 if (listener.mIcon)
  414.                   p.onLinkIconAvailable(listener.mIcon);
  415.               }
  416.             }
  417.  
  418.             // Update the window title.
  419.             this.updateTitlebar();
  420.             
  421.             // If the new tab is busy, and our current state is not busy, then
  422.             // we need to fire a start to all progress listeners.
  423.             const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  424.             if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
  425.               this.mIsBusy = true;
  426.               webProgress = this.mCurrentBrowser.webProgress;
  427.               for (i = 0; i < this.mProgressListeners.length; i++) {
  428.                 p = this.mProgressListeners[i];
  429.                 if (p)
  430.                   p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
  431.               }
  432.             }
  433.  
  434.             // If the new tab is not busy, and our current state is busy, then
  435.             // we need to fire a stop to all progress listeners.
  436.             if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
  437.               this.mIsBusy = false;
  438.               webProgress = this.mCurrentBrowser.webProgress;
  439.               for (i = 0; i < this.mProgressListeners.length; i++) {
  440.                 p = this.mProgressListeners[i];
  441.                 if (p)
  442.                   p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
  443.               }
  444.             }
  445.  
  446.             // Focus our new content area.
  447.             setTimeout("window._content.focus()", 0);
  448.           ]]>
  449.         </body>
  450.       </method>
  451.  
  452.       <method name="onTabClick">
  453.         <parameter name="event"/>
  454.         <body>
  455.           <![CDATA[
  456.             if (event.button != 1 || event.target.localName != 'tab' ||
  457.                 this.mPrefs.getBoolPref("middlemouse.contentLoadURL"))
  458.               return; 
  459.  
  460.             this.removeTab(event.target);
  461.             event.stopPropagation();
  462.           ]]>
  463.         </body>
  464.       </method>
  465.  
  466.       <method name="onLinkAdded">
  467.         <parameter name="event"/>
  468.         <body>
  469.           <![CDATA[
  470.             var tabBrowser = this.parentNode.parentNode;
  471.             if (!tabBrowser.mPrefs.getBoolPref("browser.chrome.site_icons"))
  472.               return;
  473.  
  474.             if (!event.target.rel.match((/(?:^|\s)icon(?:\s|$)/i)))
  475.               return;
  476.  
  477.             // We have an icon.
  478.             var href = event.target.href;
  479.             if (!href)
  480.               return;
  481.             
  482.             // Verify that the load of this icon is legal.  We use the same
  483.             // content policy that is used for a Web page loading images.
  484.             var contentPolicy = Components.classes['@mozilla.org/layout/content-policy;1'].getService(Components.interfaces.nsIContentPolicy);
  485.             if (!contentPolicy)
  486.               return; // Refuse to load if we can't do a security check.
  487.             
  488.             // Make a URI out of our href.
  489.             var uri = Components.classes['@mozilla.org/network/standard-url;1'].createInstance();
  490.             uri = uri.QueryInterface(Components.interfaces.nsIURI);
  491.   
  492.             var notifyListeners = true;
  493.             var i;
  494.             
  495.             if (tabBrowser.mTabbedMode) {
  496.               // We need to update a tab.
  497.               for (i = 0; i < this.childNodes.length; i++) {
  498.                 if (this.childNodes[i].contentDocument == event.target.ownerDocument) {
  499.                   if (!contentPolicy.shouldLoad(Components.interfaces.nsIContentPolicy.IMAGE,
  500.                                                 uri, event.target, this.childNodes[i].contentWindow))
  501.                     return;
  502.                                                  
  503.                   var listener = tabBrowser.mTabListeners[i];
  504.                   listener.mIcon = href;
  505.                   break;
  506.                 }
  507.               }
  508.  
  509.               notifyListeners = (this.childNodes[i] == tabBrowser.mCurrentBrowser);
  510.             }
  511.             else if (!contentPolicy.shouldLoad(Components.interfaces.nsIContentPolicy.IMAGE,
  512.                                                uri, event.target, tabBrowser.mCurrentBrowser.contentWindow))
  513.               return;
  514.  
  515.             if (notifyListeners && tabBrowser.mProgressListeners) {
  516.               for (i = 0; i < tabBrowser.mProgressListeners.length; i++) {
  517.                 var p = tabBrowser.mProgressListeners[i];
  518.                 if (p)
  519.                   p.onLinkIconAvailable(href);
  520.               }
  521.             }
  522.           ]]>
  523.         </body>
  524.       </method>
  525.  
  526.       <method name="onTitleChanged">
  527.         <parameter name="evt"/>
  528.         <body>
  529.           <![CDATA[
  530.             if (evt.target != this.contentDocument)
  531.               return;
  532.  
  533.             var i = 0;
  534.             for ( ; i < this.parentNode.childNodes.length; i++) {
  535.               if (this.parentNode.childNodes[i] == this)
  536.                 break;
  537.             }
  538.  
  539.             var tabBrowser = this.parentNode.parentNode.parentNode;
  540.             var tab = tabBrowser.mTabContainer.childNodes[i];
  541.  
  542.             tabBrowser.setTabTitle(tab);
  543.  
  544.             if (tab == tabBrowser.mCurrentTab)
  545.               tabBrowser.updateTitlebar();
  546.           ]]> 
  547.         </body>
  548.       </method>
  549.  
  550.       <method name="setTabTitle">
  551.         <parameter name="aTab"/>
  552.         <body>
  553.           <![CDATA[
  554.             var browser = this.getBrowserForTab(aTab);
  555.             var title = browser.contentTitle;
  556.             var crop = "end";
  557.  
  558.             if (!title) {
  559.               if (browser.currentURI.spec) {
  560.                 try {
  561.                   title = this.mURIFixup.createExposableURI(browser.currentURI).spec;
  562.                 }
  563.                 catch(ex) {
  564.                   title = browser.currentURI.spec;
  565.                 }
  566.               }
  567.  
  568.               if (title && title != "about:blank") {
  569.                 // At this point, we now have a URI.
  570.                 // Let's try to unescape it using a character set
  571.                 // in case the URI is not ASCII.
  572.                 try {
  573.                   var characterSet = Components.lookupMethod(browser.contentDocument, 'characterSet')
  574.                                                .call(browser.contentDocument);
  575.                   const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  576.                                                  .getService(Components.interfaces.nsITextToSubURI);
  577.                   title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
  578.                 }
  579.                 catch(ex) {
  580.                   // Do nothing.
  581.                 }
  582.  
  583.                 crop = "center";
  584.               } else // Still no title?  Fall back to our untitled string.
  585.                 title = this.mStringBundle.getString("tabs.untitled");
  586.             }
  587.  
  588.             aTab.label = title;
  589.             aTab.setAttribute("crop", crop);
  590.           ]]>
  591.         </body>
  592.       </method>
  593.  
  594.       <method name="setStripVisibilityTo">
  595.         <parameter name="aShow"/>
  596.         <body>
  597.         <![CDATA[
  598.           this.mStrip.collapsed = !aShow;
  599.           if (aShow) {
  600.             // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
  601.             document.getElementById("menu_closeWindow").hidden = false;
  602.             document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
  603.             if (!this.mTabbedMode)
  604.               this.enterTabbedMode();
  605.           }
  606.           else {
  607.             // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
  608.             document.getElementById("menu_closeWindow").hidden = true;
  609.             document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
  610.           }
  611.         ]]>
  612.         </body>
  613.       </method>
  614.  
  615.       <method name="getStripVisibility">
  616.         <body>
  617.           return !this.mStrip.collapsed;
  618.         </body>
  619.       </method>
  620.  
  621.       <method name="enterTabbedMode">
  622.         <body>
  623.           <![CDATA[
  624.             this.mTabbedMode = true; // Welcome to multi-tabbed mode.
  625.  
  626.             // Get the first tab all hooked up with a title listener.
  627.             this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, false);
  628.  
  629.             this.setTabTitle(this.mCurrentTab);
  630.  
  631.             // Hook up our favicon.
  632.             var uri = this.mCurrentBrowser.currentURI;
  633.             if (this.shouldLoadFavIcon(uri))
  634.               this.loadFavIcon(uri, "image", this.mCurrentTab);
  635.  
  636.             var filter;
  637.             if (this.mTabFilters.length > 0) {
  638.               // Use the filter hooked up in our addProgressListener
  639.               filter = this.mTabFilters[0];
  640.             } else {
  641.               // create a filter and hook it up to our first browser
  642.               filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  643.                                  .createInstance(Components.interfaces.nsIWebProgress);
  644.               this.mTabFilters[0] = filter;
  645.               this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  646.             }
  647.  
  648.             // Remove all our progress listeners from the active browser's filter.
  649.             if (this.mProgressListeners) {
  650.               for (var i = 0; i < this.mProgressListeners.length; i++) {
  651.                 var p = this.mProgressListeners[i];
  652.                 if (p)
  653.                   filter.removeProgressListener(p);
  654.               }
  655.             }
  656.  
  657.             // Wire up a progress listener to our filter.
  658.             const listener = this.mTabProgressListener(this.mCurrentTab, false);
  659.             filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  660.             this.mTabListeners[0] = listener;
  661.           ]]>
  662.         </body>
  663.       </method>
  664.  
  665.       <method name="addTab">
  666.         <parameter name="aURI"/>
  667.         <parameter name="aReferrerURI"/>
  668.         <body>
  669.           <![CDATA[
  670.             var blank = (aURI == "about:blank");
  671.  
  672.             if (!this.mTabbedMode)
  673.               this.enterTabbedMode();
  674.  
  675.             var b = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  676.                                              "browser"); 
  677.             var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  678.                                              "tab");
  679.  
  680.             if (blank)
  681.               t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
  682.             else
  683.               t.setAttribute("label", aURI);
  684.             
  685.             t.setAttribute("crop", "end");
  686.             t.maxWidth = 250;
  687.             t.minWidth = 30;
  688.             t.width = 0;
  689.             t.setAttribute("flex", "100");
  690.             t.setAttribute("validate", "never");
  691.             t.setAttribute("onerror", "this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); this.removeAttribute('image');");
  692.             this.mTabContainer.appendChild(t);
  693.  
  694.             b.setAttribute("type", "content");
  695.             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
  696.             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
  697.             
  698.             this.mPanelContainer.appendChild(b);
  699.  
  700.             b.addEventListener("DOMTitleChanged", this.onTitleChanged, false);
  701.             
  702.             if (this.mStrip.collapsed)
  703.               this.setStripVisibilityTo(true);
  704.  
  705.             this.mPrefs.setBoolPref("browser.tabs.forceHide", false);
  706.             
  707.             // wire up a progress listener for the new browser object.
  708.             var position = this.mTabContainer.childNodes.length-1;
  709.             var tabListener = this.mTabProgressListener(t, blank);
  710.             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  711.                                      .createInstance(Components.interfaces.nsIWebProgress);
  712.             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  713.             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  714.             this.mTabListeners[position] = tabListener;
  715.             this.mTabFilters[position] = filter;
  716.  
  717.             if (!blank)
  718.               b.loadURIWithFlags(aURI, nsIWebNavigation.LOAD_FLAGS_NONE,
  719.                                  aReferrerURI, null, null);
  720.  
  721.             return t;
  722.           ]]>
  723.         </body>
  724.       </method>
  725.       
  726.       <method name="removeAllTabsBut">
  727.         <parameter name="aTab"/>
  728.         <body>
  729.           <![CDATA[
  730.             if (aTab.localName != "tab")
  731.               aTab = this.mCurrentTab;
  732.             
  733.             var l = this.mTabContainer.childNodes.length;
  734.             if (l == 1)
  735.               return;
  736.  
  737.             for (var i = 0; i < l; i++) {
  738.               var tab = this.mTabContainer.childNodes[i];
  739.               if (tab != aTab) {
  740.                 this.removeTab(tab);
  741.                 i--;
  742.                 l--;
  743.               }
  744.             }
  745.           ]]>
  746.         </body>
  747.       </method>
  748.  
  749.       <method name="removeCurrentTab">
  750.         <body>
  751.           <![CDATA[
  752.             return this.removeTab(this.mCurrentTab);
  753.           ]]>
  754.         </body>
  755.       </method>
  756.          
  757.       <method name="removeTab">
  758.         <parameter name="aTab"/>
  759.         <body>
  760.           <![CDATA[
  761.             if (aTab.localName != "tab")
  762.               aTab = this.mCurrentTab;
  763.  
  764.             var l = this.mTabContainer.childNodes.length;
  765.             if (l == 1) {
  766.               // hide the tab bar
  767.               this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
  768.               this.setStripVisibilityTo(false);
  769.               return;
  770.             }
  771.  
  772.             if (l == 2) {
  773.               var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
  774.               if (autohide)
  775.                 this.setStripVisibilityTo(false);
  776.             }
  777.  
  778.             var index = -1;
  779.             if (this.mCurrentTab == aTab)
  780.               index = this.mPanelContainer.selectedIndex;
  781.             else {
  782.               // Find and locate the tab in our list.
  783.               for (var i = 0; i < l; i++)
  784.                 if (this.mTabContainer.childNodes[i] == aTab)
  785.                   index = i;
  786.             }
  787.  
  788.             // Remove the tab's filter and progress listener.
  789.             const filter = this.mTabFilters[index];
  790.             var oldBrowser = this.mPanelContainer.childNodes[index];
  791.             oldBrowser.webProgress.removeProgressListener(filter);
  792.             filter.removeProgressListener(this.mTabListeners[index]);
  793.             this.mTabFilters.splice(index, 1);
  794.             this.mTabListeners.splice(index, 1);
  795.  
  796.             // Remove our title change listener
  797.             oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, false);
  798.             
  799.             // We are no longer the primary content area.
  800.             oldBrowser.setAttribute("type", "content");
  801.  
  802.             // Now select the new tab before nuking the old one.
  803.             var currentIndex = this.mPanelContainer.selectedIndex;
  804.             
  805.             var newIndex = -1;
  806.             if (currentIndex > index)
  807.               newIndex = currentIndex-1;
  808.             else if (currentIndex < index)
  809.               newIndex = currentIndex;
  810.             else if (index == l - 1)
  811.               newIndex = index-1;
  812.             else
  813.               newIndex = index;
  814.  
  815.             var oldTab = aTab;
  816.             
  817.             // clean up the before/afterselected attributes before removing the tab
  818.             oldTab.selected = false;
  819.  
  820.             this.mTabContainer.removeChild(oldTab);
  821.             this.mPanelContainer.removeChild(oldBrowser);
  822.  
  823.             // When the current tab is removed select a new tab
  824.             // and fire select events on tabpanels and tabs
  825.             this.mTabContainer.selectedIndex = newIndex;
  826.  
  827.             // When removing a tab to the left of the current tab
  828.             // fix up the panel index without firing any events
  829.             this.mPanelContainer.selectedIndex = newIndex;
  830.           ]]>
  831.         </body>
  832.       </method>
  833.  
  834.       <method name="reloadAllTabs">
  835.         <body>
  836.           <![CDATA[
  837.             var l = this.mPanelContainer.childNodes.length;
  838.             for (var i = 0; i < l; i++)
  839.               this.mPanelContainer.childNodes[i].webNavigation.reload(true);
  840.           ]]>
  841.         </body>
  842.       </method>
  843.  
  844.       <method name="reloadTab">
  845.         <parameter name="aTab"/>
  846.         <body>
  847.           <![CDATA[
  848.             var l = this.mPanelContainer.childNodes.length;
  849.             for (var i = 0; i < l; i++)
  850.               if (this.mTabContainer.childNodes[i] == aTab)
  851.                 this.mPanelContainer.childNodes[i].webNavigation.reload(true);
  852.           ]]>
  853.         </body>
  854.       </method>
  855.  
  856.       <method name="addProgressListener">
  857.         <parameter name="aListener"/>
  858.         <parameter name="aMask"/>
  859.         <body>
  860.           <![CDATA[
  861.             if (!this.mProgressListeners) {
  862.               this.mProgressListeners = [];
  863.               const autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
  864.               const forceHide = this.mPrefs.getBoolPref("browser.tabs.forceHide");
  865.               if (!autoHide && !forceHide)
  866.                 this.setStripVisibilityTo(true);
  867.  
  868.               // Hook up a listener for <link>s.
  869.               this.mPanelContainer.addEventListener("DOMLinkAdded", this.onLinkAdded, false);
  870.             }
  871.  
  872.             this.mProgressListeners.push(aListener);
  873.  
  874.             if (!this.mTabbedMode) {
  875.               // hook a filter up to our first browser
  876.               const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  877.                                        .createInstance(Components.interfaces.nsIWebProgress);
  878.               this.mTabFilters[0] = filter;
  879.               this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  880.  
  881.               // Directly hook the listener up to the filter for better performance
  882.               this.mTabFilters[0].addProgressListener(aListener, aMask);
  883.             }
  884.           ]]>
  885.         </body>
  886.       </method>
  887.  
  888.       <method name="removeProgressListener">
  889.         <parameter name="aListener"/>
  890.         <body>
  891.           <![CDATA[
  892.             if (!this.mProgressListeners) return;
  893.             for (var i = 0; i < this.mProgressListeners.length; i++) {
  894.               if (this.mProgressListeners[i] == aListener) {
  895.                 this.mProgressListeners[i] = null;
  896.                 break;
  897.               }
  898.             }
  899.  
  900.             if (!this.mTabbedMode)
  901.               // Don't forget to remove it from the filter we hooked it up to
  902.               this.mTabFilters[0].removeProgressListener(aListener);
  903.          ]]>
  904.         </body>
  905.       </method>
  906.  
  907.       <method name="getBrowserForTab">
  908.         <parameter name="aTab"/>
  909.         <body>
  910.         <![CDATA[
  911.           if (this.mCurrentTab == aTab)
  912.             return this.mCurrentBrowser;
  913.  
  914.           for (var i = 0; i < this.mTabContainer.childNodes.length; i++) {
  915.             if (this.mTabContainer.childNodes[i] == aTab) {
  916.               return this.mPanelContainer.childNodes[i];
  917.             }
  918.           }
  919.  
  920.           return null;
  921.         ]]>
  922.         </body>
  923.       </method>
  924.  
  925.       <property name="selectedTab">
  926.         <getter>
  927.           return this.mTabBox.selectedTab;
  928.         </getter>
  929.         <setter>
  930.           <![CDATA[
  931.           // Update the tab
  932.           this.mTabBox.selectedTab = val;
  933.           return val;
  934.           ]]>
  935.         </setter>
  936.       </property>
  937.  
  938.       <property name="selectedBrowser"
  939.                 onget="return this.mCurrentBrowser;"
  940.                 readonly="true"/>
  941.  
  942.  
  943.       <property name="browsers"
  944.                 onget="return this.mPanelContainer.childNodes;"
  945.                 readonly="true"/>
  946.  
  947.       <!-- Drag and drop observer API -->
  948.       <!--<method name="onDragStart">
  949.         <parameter name="aEvent"/>
  950.         <parameter name="aXferData"/>
  951.         <parameter name="aDragAction"/>
  952.         <body/>
  953.       </method>-->
  954.  
  955.       <method name="onDragOver">
  956.         <parameter name="aEvent"/>
  957.         <parameter name="aFlavour"/>
  958.         <parameter name="aDragSession"/>
  959.         <body>
  960.         <![CDATA[
  961.           return; // Just having this makes our feedback correct.
  962.         ]]>
  963.         </body>
  964.       </method>
  965.  
  966.       <method name="onDrop">
  967.         <parameter name="aEvent"/>
  968.         <parameter name="aXferData"/>
  969.         <parameter name="aDragSession"/>
  970.         <body>
  971.           <![CDATA[
  972.             var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
  973.  
  974.             // valid urls don't contain spaces ' '; if we have a space it isn't a valid url so bail out
  975.             if (!url || !url.length || url.indexOf(" ", 0) != -1) 
  976.               return;
  977.             
  978.             var bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
  979.  
  980.             if (aEvent.target.localName == "tabs") {
  981.               // We're adding a new tab.
  982.               var tab = this.addTab(getShortcutOrURI(url));
  983.               if (!bgLoad)
  984.                 this.selectedTab = tab;
  985.             }
  986.             else if (aEvent.target.localName == "tab") {
  987.               // Load in an existing tab.
  988.               this.getBrowserForTab(aEvent.target).loadURI(getShortcutOrURI(url));
  989.               if (this.mCurrentTab != aEvent.target && !bgLoad)
  990.                 this.selectedTab = aEvent.target;
  991.             }
  992.           ]]>
  993.         </body>
  994.       </method>
  995.  
  996.       <method name="getSupportedFlavours">
  997.         <body>
  998.         <![CDATA[
  999.           var flavourSet = new FlavourSet();
  1000.           flavourSet.appendFlavour("text/x-moz-url");
  1001.           flavourSet.appendFlavour("text/unicode");
  1002.           flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  1003.           return flavourSet;
  1004.         ]]>
  1005.         </body>
  1006.       </method>
  1007.                                                                
  1008.       <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
  1009.            MAKE SURE TO ADD IT HERE AS WELL. -->
  1010.       <property name="canGoBack"
  1011.                 onget="return this.mCurrentBrowser.canGoBack;"
  1012.                 readonly="true"/>
  1013.  
  1014.       <property name="canGoForward"
  1015.                 onget="return this.mCurrentBrowser.canGoForward;"
  1016.                 readonly="true"/>
  1017.  
  1018.       <method name="goBack">
  1019.         <body>
  1020.           <![CDATA[
  1021.             return this.mCurrentBrowser.goBack();
  1022.           ]]>
  1023.         </body>
  1024.       </method>
  1025.  
  1026.       <method name="goForward">
  1027.         <body>
  1028.           <![CDATA[
  1029.             return this.mCurrentBrowser.goForward();
  1030.           ]]>
  1031.         </body>
  1032.       </method>
  1033.  
  1034.       <method name="reload">
  1035.         <body>
  1036.           <![CDATA[
  1037.             return this.mCurrentBrowser.reload();
  1038.           ]]>
  1039.         </body>
  1040.       </method>
  1041.  
  1042.       <method name="reloadWithFlags">
  1043.         <parameter name="aFlags"/>
  1044.         <body>
  1045.           <![CDATA[
  1046.             return this.mCurrentBrowser.reloadWithFlags(aFlags);
  1047.           ]]>
  1048.         </body>
  1049.       </method>
  1050.  
  1051.       <method name="stop">
  1052.         <body>
  1053.           <![CDATA[
  1054.             return this.mCurrentBrowser.stop();
  1055.           ]]>
  1056.         </body>
  1057.       </method>
  1058.  
  1059.       <!-- throws exception for unknown schemes -->
  1060.       <method name="loadURI">
  1061.         <parameter name="aURI"/>
  1062.         <parameter name="aReferrerURI"/>
  1063.         <body>
  1064.           <![CDATA[
  1065.             return this.mCurrentBrowser.loadURI(aURI, aReferrerURI);
  1066.           ]]>
  1067.         </body>
  1068.       </method>
  1069.  
  1070.       <!-- throws exception for unknown schemes -->
  1071.       <method name="loadURIWithFlags">
  1072.         <parameter name="aURI"/>
  1073.         <parameter name="aFlags"/>
  1074.         <parameter name="aReferrerURI"/>
  1075.         <body>
  1076.           <![CDATA[
  1077.             return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI);
  1078.           ]]>
  1079.         </body>
  1080.       </method>
  1081.  
  1082.       <method name="goHome">
  1083.         <body>
  1084.           <![CDATA[
  1085.             return this.mCurrentBrowser.goHome();
  1086.           ]]>
  1087.         </body>
  1088.       </method>
  1089.  
  1090.       <property name="homePage">
  1091.         <getter>
  1092.           <![CDATA[
  1093.             return this.mCurrentBrowser.homePage;
  1094.           ]]>
  1095.         </getter>
  1096.         <setter>
  1097.           <![CDATA[
  1098.             this.mCurrentBrowser.homePage = val;
  1099.             return val;
  1100.           ]]>
  1101.         </setter>
  1102.       </property>
  1103.  
  1104.       <method name="gotoIndex">
  1105.         <parameter name="aIndex"/>
  1106.         <body>
  1107.           <![CDATA[
  1108.             return this.mCurrentBrowser.gotoIndex(aIndex);
  1109.           ]]>
  1110.         </body>
  1111.       </method>
  1112.  
  1113.       <property name="currentURI"
  1114.                 onget="return this.mCurrentBrowser.currentURI;"
  1115.                 readonly="true"/>
  1116.  
  1117.       <property name="docShell"
  1118.                 onget="return this.mCurrentBrowser.docShell"
  1119.                 readonly="true"/>
  1120.  
  1121.       <property name="webNavigation"
  1122.                 onget="return this.mCurrentBrowser.webNavigation"
  1123.                 readonly="true"/>
  1124.  
  1125.       <property name="webBrowserFind"
  1126.                 readonly="true"
  1127.                 onget="return this.mCurrentBrowser.webBrowserFind"/>
  1128.  
  1129.       <property name="webProgress"
  1130.                 readonly="true"
  1131.                 onget="return this.mCurrentBrowser.webProgress"/>
  1132.  
  1133.       <property name="contentWindow"
  1134.                 readonly="true"
  1135.                 onget="return this.mCurrentBrowser.contentWindow"/>
  1136.  
  1137.       <property name="sessionHistory"
  1138.                 onget="return this.mCurrentBrowser.sessionHistory;"
  1139.                 readonly="true"/>
  1140.  
  1141.       <property name="markupDocumentViewer"
  1142.                 onget="return this.mCurrentBrowser.markupDocumentViewer;"
  1143.                 readonly="true"/>
  1144.  
  1145.       <property name="contentViewerEdit"
  1146.                 onget="return this.mCurrentBrowser.contentViewerEdit;"
  1147.                 readonly="true"/>
  1148.  
  1149.       <property name="contentViewerFile"
  1150.                 onget="return this.mCurrentBrowser.contentViewerFile;"
  1151.                 readonly="true"/>
  1152.  
  1153.       <property name="documentCharsetInfo"
  1154.                 onget="return this.mCurrentBrowser.documentCharsetInfo;"
  1155.                 readonly="true"/>
  1156.  
  1157.       <property name="contentDocument"
  1158.                 onget="return this.mCurrentBrowser.contentDocument;"
  1159.                 readonly="true"/>
  1160.  
  1161.       <property name="contentTitle"
  1162.                 onget="return this.mCurrentBrowser.contentTitle;"
  1163.                 readonly="true"/>
  1164.  
  1165.       <property name="securityUI"
  1166.                 onget="return this.mCurrentBrowser.securityUI;"
  1167.                 readonly="true"/>
  1168.  
  1169.       <constructor>
  1170.         <![CDATA[
  1171.         this.mCurrentBrowser = this.mPanelContainer.firstChild;
  1172.         this.mCurrentTab = this.mTabContainer.firstChild;
  1173.         this.mTabBox.handleCtrlTab = !/Mac/.test(navigator.platform);
  1174.         ]]>
  1175.       </constructor>
  1176.  
  1177.       <destructor>
  1178.         <![CDATA[
  1179.           for (var i = 0; i < this.mTabListeners.length; ++i) {
  1180.             this.mPanelContainer.childNodes[i].webProgress.removeProgressListener(this.mTabFilters[i]);
  1181.             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
  1182.             this.mTabFilters[i] = null;
  1183.             this.mTabListeners[i] = null;
  1184.             this.mPanelContainer.childNodes[i].removeEventListener("DOMTitleChanged", this.onTitleChanged, false);
  1185.           }
  1186.           this.mPanelContainer.removeEventListener("DOMLinkAdded", this.onLinkAdded, false);
  1187.         ]]>
  1188.       </destructor>
  1189.     </implementation>
  1190.  
  1191.     <handlers>
  1192.       <handler event="select" action="if (event.originalTarget == this.mPanelContainer) this.updateCurrentBrowser();"/>
  1193.  
  1194.       <handler event="keypress" modifiers="control" keycode="vk_f4" action="this.removeCurrentTab();"/>
  1195.  
  1196.       <handler event="DOMWindowClose">
  1197.         <![CDATA[
  1198.           const browsers = this.browsers;
  1199.           if (browsers.length == 1)
  1200.             return;
  1201.           var i = 0;
  1202.           for (; i < browsers.length; ++i) {
  1203.             if (browsers[i].contentWindow == event.target)
  1204.               break;
  1205.           }
  1206.           this.removeTab(this.mTabContainer.childNodes[i]);
  1207.           event.preventDefault();
  1208.         ]]>
  1209.       </handler>
  1210.     </handlers>
  1211.   </binding>
  1212.  
  1213. </bindings>
  1214.