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