<?xml version="1.0"?>

<!--
   - The contents of this file are subject to the Mozilla Public
   - License Version 1.1 (the "License"); you may not use this file
   - except in compliance with the License. You may obtain a copy of
   - the License at http://www.mozilla.org/MPL/
   -
   - Software distributed under the License is distributed on an "AS
   - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
   - implied. See the License for the specific language governing
   - rights and limitations under the License.
   -
   - The Original Code is this file as it was released on
   - March 28, 2001.
   -
   - The Initial Developer of the Original Code is David Hyatt
   - Portions created by David Hyatt are Copyright (C) 2001
   - David Hyatt.  All Rights Reserved.
   -
   - Contributor(s):
   -   David Hyatt <hyatt@netscape.com> (Original Author of <tabbrowser>)
   -
   - Alternatively, the contents of this file may be used under the
   - terms of the GNU General Public License Version 2 or later (the
   - "GPL"), in which case the provisions of the GPL are applicable
   - instead of those above.  If you wish to allow use of your
   - version of this file only under the terms of the GPL and not to
   - allow others to use your version of this file under the MPL,
   - indicate your decision by deleting the provisions above and
   - replace them with the notice and other provisions required by
   - the GPL.  If you do not delete the provisions above, a recipient
   - may use your version of this file under either the MPL or the
   - GPL.
  -->

<!DOCTYPE bindings [
<!ENTITY % tabBrowserDTD SYSTEM "chrome://global/locale/tabbrowser.dtd" >
%tabBrowserDTD;
]>

<bindings id="tabBrowserBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <binding id="tabbrowser">
    <resources>
      <stylesheet src="chrome://global/skin/browser.css"/>
    </resources>

    <content>
      <xul:stringbundle src="chrome://global/locale/tabbrowser.properties"/>
      <xul:tabbox flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                  onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
        <xul:hbox class="tabbrowser-strip chromeclass-toolbar" collapsed="true" tooltip="_child" context="_child">
          <xul:tooltip onpopupshowing="event.preventBubble(); if (document.tooltipNode.hasAttribute('label')) { this.setAttribute('label', document.tooltipNode.getAttribute('label')); return true; } return false;"/>
          <xul:menupopup onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
            <xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;"
                          xbl:inherits="oncommand=onnewtab"/>
            <xul:menuseparator/>
            <xul:menuitem label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
                                     tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
            <xul:menuitem label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
                          tbattr="tabbrowser-multiple"
                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
                                     tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
            <xul:menuitem label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
                          tbattr="tabbrowser-multiple"
                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
                                     tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
            <xul:menuseparator/>
            <xul:menuitem label="&closeTab.label;" accesskey="&closeTab.accesskey;"
                          tbattr="tabbrowser-multiple"
                          oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
                                     tabbrowser.removeTab(tabbrowser.mContextTab);"/>
          </xul:menupopup>

          <xul:tabs class="tabbrowser-tabs" closebutton="true" flex="1"
                    setfocus="false"
                    onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
                    onmousedown="this.parentNode.parentNode.parentNode.updateContextTab(event);"
                    ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode.parentNode);
                                event.stopPropagation();"
                    ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode.parentNode);
                                event.stopPropagation();"
                    xbl:inherits="onnewtab"
                    ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
                    onclosetab="var node = this.parentNode;
                                while (node.localName != 'tabbrowser')
                                  node = node.parentNode;
                                node.removeCurrentTab();">
            <xul:tab validate="never" 
                     onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image'));
                              this.removeAttribute('image');"
                     maxwidth="250" width="0" minwidth="30" flex="100"
                     class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
          </xul:tabs>
        </xul:hbox>
        <xul:tabpanels flex="1" class="plain">
          <xul:vbox flex="1">
            <xul:browsermessage hidden="true" type="top"/>
            <xul:browser flex="1" type="content-primary" message="true" disablehistory="true" xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
            <xul:browsermessage hidden="true" type="bottom"/>
          </xul:vbox>
        </xul:tabpanels>
      </xul:tabbox>
      <children/>
    </content>
    <implementation>
      <field name="mPrefs" readonly="true">
        Components.classes['@mozilla.org/preferences-service;1']
                  .getService(Components.interfaces.nsIPrefService)
                  .getBranch(null);
      </field>
      <field name="mTabBox">
        document.getAnonymousNodes(this)[1]
      </field>
      <field name="mStrip">
        this.mTabBox.firstChild
      </field>
      <field name="mTabContainer">
        this.mStrip.childNodes[2]
      </field>
      <field name="mPanelContainer">
        this.mTabBox.childNodes[1]
      </field>
      <field name="mStringBundle">
        document.getAnonymousNodes(this)[0]
      </field>
      <field name="mCurrentTab">
        null
      </field>
      <field name="mCurrentBrowser">
        null
      </field>
      <field name="mProgressListeners">
        null
      </field>
      <field name="mTabListeners">
        new Array()
      </field>
      <field name="mTabFilters">
        new Array()
      </field>
      <field name="mTabbedMode">
        false
      </field>
      <field name="mIsBusy">
        false
      </field>
      <field name="mMissedIconCache">
        null
      </field>
      <field name="mContextTab">
        null
      </field>
      <field name="mModalDialogShowing">
        false
      </field>
      
      <method name="getBrowserAtIndex">
        <parameter name="aIndex"/>
        <body>
          <![CDATA[
            return this.mPanelContainer.childNodes[aIndex].firstChild.nextSibling;
          ]]>
        </body>
      </method>

      <method name="getBrowserIndexForDocument">
        <parameter name="aDocument"/>
        <body>
          <![CDATA[
            for (var i = 0; i < this.mPanelContainer.childNodes.length; i++) {
              if (this.getBrowserAtIndex(i).contentDocument == aDocument) {
                return i;
              }
            }
            return -1;
          ]]>
        </body>
      </method>
        
      <method name="getMessageForBrowser">
        <parameter name="aBrowser"/>
        <parameter name="aTopBottom"/>
        <body>
          <![CDATA[
            return aBrowser[aTopBottom == "top" ? "previousSibling" : "nextSibling"];
          ]]>
        </body>
      </method>
      
      <method name="showMessage">
        <parameter name="aBrowser"/>
        <parameter name="aIconURL"/>
        <parameter name="aMessage"/>
        <parameter name="aButtonLabel"/>
        <parameter name="aDocShell"/>
        <parameter name="aSource"/>
        <parameter name="aPopup"/>
        <parameter name="aTopBottom"/>
        <parameter name="aShowCloseButton"/>
        <body>
          <![CDATA[
            var message = this.getMessageForBrowser(aBrowser, aTopBottom);
            message.image = aIconURL;
            message.text = aMessage;
            message.buttonText = aButtonLabel;
            message.hidden = false;
            if (aSource) {
              message.source = aSource;
              message.popup = null;
            }
            else if (aPopup) {
              message.popup = aPopup;
              message.source = null;
            }
            message.docShell = aDocShell;
            message.closeButton = aShowCloseButton;
            aBrowser.isShowingMessage = true;
          ]]>
        </body>
      </method>
            
      <method name="hideMessage">
        <parameter name="aBrowser"/>
        <parameter name="aTopBottom"/>
        <body>
          <![CDATA[
            if (aTopBottom != "both")
              this.getMessageForBrowser(aBrowser, aTopBottom).hidden = true;
            else {
              this.getMessageForBrowser(aBrowser, "top").hidden = true;
              this.getMessageForBrowser(aBrowser, "bottom").hidden = true;
            }
          ]]>
        </body>
      </method>

      <!-- A web progress listener object definition for a given tab. -->
      <method name="mTabProgressListener">
        <parameter name="aTab"/>
        <parameter name="aBrowser"/>
        <parameter name="aStartsBlank"/>
        <body>
        <![CDATA[
          return ({
            mTabBrowser: this,
            mTab: aTab,
            mBrowser: aBrowser,
            mBlank: aStartsBlank,
            mIcon: null,
            mLastURI: null,

            onProgressChange : function (aWebProgress, aRequest,
                                         aCurSelfProgress, aMaxSelfProgress,
                                         aCurTotalProgress, aMaxTotalProgress)
            {
              if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
                for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
                  var p = this.mTabBrowser.mProgressListeners[i];
                  if (p)
                    p.onProgressChange(aWebProgress, aRequest,
                                       aCurSelfProgress, aMaxSelfProgress,
                                       aCurTotalProgress, aMaxTotalProgress);
                }
              }
            },

            onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
            {
              if (!aRequest)
                return;

              var oldBlank = this.mBlank;

              const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
              const nsIChannel = Components.interfaces.nsIChannel;

              if (aStateFlags & nsIWebProgressListener.STATE_START)
                  this.mBrowser.mFavIconURL = null;

              if (aStateFlags & nsIWebProgressListener.STATE_START && 
                  aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
                // It's okay to clear what the user typed when we start
                // loading a document. If the user types, this flag gets
                // set to false, if the document load ends without an
                // onLocationChange, this flag also gets set to false
                // (so we keep it while switching tabs after failed load
                if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
                  this.mBrowser.userTypedClear = true;

                if (!this.mBlank) {
                  this.mTab.setAttribute("busy", "true");
                  this.mTab.label = this.mTabBrowser.mStringBundle.getString("tabs.loading");
                  this.mTab.removeAttribute("image"); 
                  this.mIcon = null;

                  if (this.mTabBrowser.mCurrentTab == this.mTab)
                    this.mTabBrowser.mIsBusy = true;
                }
              }
              else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
                       aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
                // The document is done loading, it's okay to clear
                // the value again.
                if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
                  this.mBrowser.userTypedClear = false;

                if (this.mBlank)
                  this.mBlank = false;

                this.mTab.removeAttribute("busy");

                var location = aRequest.QueryInterface(nsIChannel).URI;

                // For keyword URIs clear the user typed value since they will be changed into real URIs
                if (location.scheme == "keyword")
                  this.mBrowser.userTypedValue = null;

                if (this.mTabBrowser.shouldLoadFavIcon(location)) {
                    if (this.mIcon)
                      this.mTab.setAttribute("image", this.mIcon);
                    else
                      this.mTabBrowser.loadFavIcon(location, "image", this.mTab);
                }

                if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
                  this.mTabBrowser.setTabTitle(this.mTab);

                if (this.mTabBrowser.mCurrentTab == this.mTab)
                  this.mTabBrowser.mIsBusy = false;
              }

              if (!oldBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
                for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
                  var p = this.mTabBrowser.mProgressListeners[i];
                  if (p)
                    p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
                }
              }
            }
            ,

            onLocationChange : function(aWebProgress, aRequest, aLocation) {
              // The document loaded correctly, clear the value if we should
              if (this.mBrowser.userTypedClear)
                this.mBrowser.userTypedValue = null;

              if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
                for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
                  var p = this.mTabBrowser.mProgressListeners[i];
                  if (p)
                    p.onLocationChange(aWebProgress, aRequest, aLocation);
                }
              }
            },

            onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {
              if (this.mBlank)
                return;

              if (this.mTabBrowser.mCurrentTab == this.mTab) {
                for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
                  var p = this.mTabBrowser.mProgressListeners[i];
                  if (p)
                    p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
                }
              }
            },

            onSecurityChange : function(aWebProgress, aRequest, aState)
            {
              if (this.mTabBrowser.mCurrentTab == this.mTab) {
                for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
                  var p = this.mTabBrowser.mProgressListeners[i];
                  if (p)
                    p.onSecurityChange(aWebProgress, aRequest, aState);
                }
              }
            },

            QueryInterface : function(aIID)
            {
              if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
                  aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
                  aIID.equals(Components.interfaces.nsISupports))
                return this;
              throw Components.results.NS_NOINTERFACE;
            }
                  
            });
        ]]>
        </body>
      </method>
      
      <method name="buildFavIconString">
        <parameter name="aURI"/>
        <body>
          <![CDATA[
            var end = (aURI.port == -1) ? "/favicon.ico" : (":" + aURI.port + "/favicon.ico");
            return aURI.scheme + "://" + aURI.host + end;
          ]]>
        </body>
      </method>

      <method name="shouldLoadFavIcon">
        <parameter name="aURI"/>
        <body>
          <![CDATA[
            return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
                    this.mPrefs.getBoolPref("browser.chrome.favicons") && 
                    ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
          ]]>
        </body>
      </method>

      <method name="loadFavIcon">
        <parameter name="aURI"/>
        <parameter name="aAttr"/>
        <parameter name="aElt"/>
        <body>
          <![CDATA[
            var iconURL = this.buildFavIconString(aURI);
            if (!this.isFavIconKnownMissing(iconURL))
              aElt.setAttribute(aAttr, iconURL);
          ]]>
        </body>
      </method>

      <method name="addToMissedIconCache">
        <parameter name="aURI"/>
        <body>
          <![CDATA[
            var entry = this.openCacheEntry(aURI, Components.interfaces.nsICache.ACCESS_READ_WRITE);
            if (!entry)
              return;

            if (entry.accessGranted == Components.interfaces.nsICache.ACCESS_WRITE)
              // It's a new entry.  Just write a bit of metadata in to the entry.
              entry.setMetaDataElement("Icon", "Missed");
            entry.markValid();
            entry.close();
          ]]>
        </body>
      </method>

      <method name="openCacheEntry">
        <parameter name="key"/>
        <parameter name="access"/>
        <body>
          <![CDATA[
            try {
              if (!this.mMissedIconCache) {
                var cacheService = Components.classes['@mozilla.org/network/cache-service;1'].getService(Components.interfaces.nsICacheService);
                this.mMissedIconCache = cacheService.createSession("MissedIconCache", Components.interfaces.nsICache.STORE_ANYWHERE, true);
                if (!this.mMissedIconCache)
                  return null;
              }
              return this.mMissedIconCache.openCacheEntry(key, access, true);
            }
            catch (e) {
              return null;
            }
          ]]>
        </body>
      </method>

      <method name="isFavIconKnownMissing">
        <parameter name="key"/>
        <body>
          <![CDATA[
            var e = this.openCacheEntry(key, Components.interfaces.nsICache.ACCESS_READ);
            if (e) {
                e.close();
                return true;
            }
            return false;
          ]]>
        </body>
      </method>

      <method name="updateTitlebar">
        <body>
          <![CDATA[
            var newTitle = "";
            var docTitle;
            if (this.docShell.contentViewer)
              docTitle = this.contentTitle;
            
	          if (!docTitle)
	            docTitle = this.ownerDocument.documentElement.getAttribute("titledefault");

            var modifier = this.ownerDocument.documentElement.getAttribute("titlemodifier");
            if (docTitle) {
              newTitle += this.ownerDocument.documentElement.getAttribute("titlepreface");
              newTitle += docTitle;
              var sep = this.ownerDocument.documentElement.getAttribute("titlemenuseparator");
              if (modifier)
                newTitle += sep;
            }
            newTitle += modifier;
            window.title = newTitle;
          ]]>
        </body>
      </method>

      <method name="updateContextTab">
        <parameter name="aEvent"/>
        <body>
          <![CDATA[
            if (aEvent.originalTarget.localName == "tab")
              this.mContextTab = aEvent.originalTarget;
            else
              this.mContextTab = document.popupNode;
          ]]>
        </body>
      </method>

      <method name="updatePopupMenu">
        <parameter name="aPopupMenu"/>
        <body>
          <![CDATA[
            var disabled = this.mPanelContainer.childNodes.length == 1;
            var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
            for (var i = 0; i < menuItems.length; i++)
              menuItems[i].setAttribute("disabled", disabled);
          ]]>
        </body>
      </method>

      <method name="updateCurrentBrowser">
        <body>
          <![CDATA[
            var newBrowser = this.getBrowserAtIndex(this.mPanelContainer.selectedIndex);
            if (this.mCurrentBrowser == newBrowser)
              return;

            if (this.mCurrentBrowser) {
              this.mCurrentBrowser.focusedWindow = document.commandDispatcher.focusedWindow;
              this.mCurrentBrowser.focusedElement = document.commandDispatcher.focusedElement;
               this.mCurrentBrowser.setAttribute("type", "content");
            }
            
            var updatePageReport = false;
            if ((this.mCurrentBrowser.pageReport && !newBrowser.pageReport) ||
                (!this.mCurrentBrowser.pageReport && newBrowser.pageReport))
              updatePageReport = true;

            newBrowser.setAttribute("type", "content-primary");
            this.mCurrentBrowser = newBrowser;
            this.mCurrentTab = this.selectedTab;
            
            if (updatePageReport)
              this.mCurrentBrowser.updatePageReport();

            // Update the URL bar.
            var loc = this.mCurrentBrowser.currentURI;
            if (!loc)
              loc = ({ spec: "" });

            var webProgress = this.mCurrentBrowser.webProgress;
            var securityUI = this.mCurrentBrowser.securityUI;
            // Remember the current clear state, then set it to false
            // so we don't clear the userTypedValue when just switching
            // tabs. Set it back to its old state after we're done.
            var userTypedClear = this.mCurrentBrowser.userTypedClear;
            this.mCurrentBrowser.userTypedClear = false;
            var i, p;
            for (i = 0; i < this.mProgressListeners.length; i++) {
              p = this.mProgressListeners[i];
              if (p) {
                p.onLocationChange(webProgress, null, loc);
                if (securityUI)
                  p.onSecurityChange(webProgress, null, securityUI.state);
                var listener = this.mTabListeners[this.mPanelContainer.selectedIndex];
                if (listener.mIcon) {
                  if (this.isFavIconKnownMissing(listener.mIcon))
                    listener.mIcon = null;
                  else
                    p.onLinkIconAvailable(newBrowser, listener.mIcon);
                }
              }
            }
            this.mCurrentBrowser.userTypedClear = userTypedClear;
            
            this._fastFind.setDocShell(this.mCurrentBrowser.docShell);

            // Update the window title.
            this.updateTitlebar();
            
            // If the new tab is busy, and our current state is not busy, then
            // we need to fire a start to all progress listeners.
            const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
            if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
              this.mIsBusy = true;
              webProgress = this.mCurrentBrowser.webProgress;
              for (i = 0; i < this.mProgressListeners.length; i++) {
                p = this.mProgressListeners[i];
                if (p)
                  p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
              }
            }

            // If the new tab is not busy, and our current state is busy, then
            // we need to fire a stop to all progress listeners.
            if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
              this.mIsBusy = false;
              webProgress = this.mCurrentBrowser.webProgress;
              for (i = 0; i < this.mProgressListeners.length; i++) {
                p = this.mProgressListeners[i];
                if (p)
                  p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
              }
            }

            function setFocus(element) {
              Components.lookupMethod(element, "focus").call(element);
            }

            // Focus the previously focused element or window
            document.commandDispatcher.suppressFocusScroll = true;
            if (newBrowser.focusedElement) {
              try {
                setFocus(newBrowser.focusedElement);
              } catch (e) {
                setFocus(newBrowser.focusedWindow);
              }
            }
            else if (newBrowser.focusedWindow)
              setFocus(newBrowser.focusedWindow);
            else // new tab, focus our new content area
              setTimeout(setFocus, 0, window.content);
            document.commandDispatcher.suppressFocusScroll = false;    
          ]]>
        </body>
      </method>

      <method name="onTabClick">
        <parameter name="event"/>
        <body>
          <![CDATA[
            if (event.button != 1 || event.target.localName != 'tab' ||
                this.mPrefs.getBoolPref("middlemouse.contentLoadURL"))
              return;

            this.removeTab(event.target);
            event.stopPropagation();
          ]]>
        </body>
      </method>

      <method name="onLinkAdded">
        <parameter name="event"/>
        <body>
          <![CDATA[
            // mechanism for reading properties of the underlying XPCOM object
            // (ignoring potential getters/setters added by malicious content)
            var safeGetProperty = function(obj, propname) {
              return Components.lookupMethod(obj, propname).call(obj);
            };

            var tabBrowser = this.parentNode.parentNode;
            if (!tabBrowser.mPrefs.getBoolPref("browser.chrome.site_icons"))
              return;

            if (!event.target.rel.match((/(?:^|\s)icon(?:\s|$)/i)))
              return;

            // We have an icon.
            var href = event.target.href;
            if (!href)
              return;

            const nsIContentPolicy = Components.interfaces.nsIContentPolicy;
            try {
              var contentPolicy =
                Components.classes['@mozilla.org/layout/content-policy;1']
                          .getService(nsIContentPolicy);
            } catch(e) {
              return; // Refuse to load if we can't do a security check.
            }

            // Verify that the load of this icon is legal.
            // We check first with the security manager
            const secMan =
              Components.classes["@mozilla.org/scriptsecuritymanager;1"]
                        .getService(Components.interfaces.nsIScriptSecurityManager);

            // Get the IOService so we can make URIs
            const ioService =
              Components.classes["@mozilla.org/network/io-service;1"]
                        .getService(Components.interfaces.nsIIOService);

            const targetDoc = safeGetProperty(event.target, "ownerDocument");
            // Make a URI out of our href.
            var docCharset = safeGetProperty(targetDoc, "characterSet");
            var uri = ioService.newURI(href, docCharset, null);

            var origURIStr = safeGetProperty(targetDoc, "documentURI");
            var origURI = ioService.newURI(origURIStr, docCharset, null);

            const nsIScriptSecMan =
              Components.interfaces.nsIScriptSecurityManager;

            try {
              secMan.checkLoadURI(origURI, uri, nsIScriptSecMan.STANDARD);
            } catch(e) {
              return;
            }

            // Security says okay, now ask content policy
            if (contentPolicy.shouldLoad(nsIContentPolicy.TYPE_IMAGE,
                                         uri, origURI, event.target,
                                         safeGetProperty(event.target, "type"),
                                         null) != nsIContentPolicy.ACCEPT)
              return;

            var browserIndex = tabBrowser.getBrowserIndexForDocument(targetDoc);
            // no browser? no favicon.
            if (browserIndex == -1)
              return;

            var listener = tabBrowser.mTabListeners[browserIndex];
            // there's no tab listener for non-tabbed mode browser 0

            if (tabBrowser.isFavIconKnownMissing(href)) {
              if (listener)
                listener.mIcon = null;
              return;
            }

            if (listener)
              listener.mIcon = href;

            if (tabBrowser.mProgressListeners) {
              var targetBrowser = tabBrowser.getBrowserAtIndex(browserIndex);
              for (i = 0; i < tabBrowser.mProgressListeners.length; i++) {
                var p = tabBrowser.mProgressListeners[i];
                if (p)
                  p.onLinkIconAvailable(targetBrowser, href);
              }
            }
          ]]>
        </body>
      </method>

      <method name="onTitleChanged">
        <parameter name="evt"/>
        <body>
          <![CDATA[
            if (evt.target != this.contentDocument)
              return;

            var i = 0;
            for ( ; i < this.parentNode.parentNode.childNodes.length; i++) {
              if (this.parentNode.parentNode.childNodes[i].firstChild.nextSibling == this)
                break;
            }
            
            var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;

            var tab = tabBrowser.mTabContainer.childNodes[i];
            tabBrowser.setTabTitle(tab);

            if (tab == tabBrowser.mCurrentTab)
              tabBrowser.updateTitlebar();
          ]]>
        </body>
      </method>

      <method name="setTabTitle">
        <parameter name="aTab"/>
        <body>
          <![CDATA[
            var browser = this.getBrowserForTab(aTab);
            var crop = "end";
            var titleViaGetter = browser.contentDocument.__proto__.__lookupGetter__('title').call(browser.contentDocument);
            var title;
            if (titleViaGetter)
              title = titleViaGetter
            else if (browser.currentURI.spec && browser.currentURI.spec != "about:blank") {
              title = browser.currentURI.spec;
              crop  = "center";
            }
            else
              title = this.mStringBundle.getString("tabs.untitled");

            aTab.label = title;
            aTab.setAttribute("crop", crop);
          ]]>
        </body>
      </method>

      <method name="setStripVisibilityTo">
        <parameter name="aShow"/>
        <body>
        <![CDATA[
          this.mStrip.collapsed = !aShow;
          if (aShow) {
            // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
            document.getElementById("menu_closeWindow").hidden = false;
            document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
            if (!this.mTabbedMode)
              this.enterTabbedMode();
          }
          else {
            // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
            document.getElementById("menu_closeWindow").hidden = true;
            document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
          }
        ]]>
        </body>
      </method>

      <method name="getStripVisibility">
        <body>
          return !this.mStrip.collapsed;
        </body>
      </method>

      <method name="enterTabbedMode">
        <body>
          <![CDATA[
            this.mTabbedMode = true; // Welcome to multi-tabbed mode.

            // Get the first tab all hooked up with a title listener and popup blocking listener.
            this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, false);

            this.setTabTitle(this.mCurrentTab);

            // Hook up our favicon.
            var uri = this.mCurrentBrowser.currentURI;
            if (this.shouldLoadFavIcon(uri))
              this.loadFavIcon(uri, "image", this.mCurrentTab);

            var filter;
            if (this.mTabFilters.length > 0) {
              // Use the filter hooked up in our addProgressListener
              filter = this.mTabFilters[0];
            } else {
              // create a filter and hook it up to our first browser
              filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                 .createInstance(Components.interfaces.nsIWebProgress);
              this.mTabFilters[0] = filter;
              this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
            }

            // Remove all our progress listeners from the active browser's filter.
            if (this.mProgressListeners) {
              for (var i = 0; i < this.mProgressListeners.length; i++) {
                var p = this.mProgressListeners[i];
                if (p)
                  filter.removeProgressListener(p);
              }
            }

            // Wire up a progress listener to our filter.
            const listener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, false);
            filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
            this.mTabListeners[0] = listener;
          ]]>
        </body>
      </method>
      
      <method name="_createMessage">
        <parameter name="aType"/>
        <body>
          <![CDATA[
            var message = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                                   "browsermessage"); 
            message.hidden = true;
            message.setAttribute("type", aType);
            return message;
          ]]>
        </body>
      </method>

      <method name="addTab">
        <parameter name="aURI"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <parameter name="aPostData"/>
        <body>
          <![CDATA[
            var blank = (aURI == "about:blank");

            if (!this.mTabbedMode)
              this.enterTabbedMode();

            var b = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                             "browser"); 
            var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                             "tab");

            if (blank)
              t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
            else
              t.setAttribute("label", aURI);

            t.setAttribute("crop", "end");
            t.maxWidth = 250;
            t.minWidth = 30;
            t.width = 0;
            t.setAttribute("flex", "100");
            t.setAttribute("validate", "never");
            t.setAttribute("onerror", "this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); this.removeAttribute('image');");
            this.mTabContainer.appendChild(t);

            b.setAttribute("type", "content");
            b.setAttribute("message", "true");
            b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
            b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
            b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
            
            // Add the Message and the Browser to the box            
            var vbox = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                                "vbox"); 
            vbox.setAttribute("flex", "1");
            vbox.appendChild(this._createMessage("top"));
            vbox.appendChild(b);
            vbox.appendChild(this._createMessage("bottom"));
            b.setAttribute("flex", "1");
            this.mPanelContainer.appendChild(vbox);

            b.addEventListener("DOMTitleChanged", this.onTitleChanged, false);

            if (this.mStrip.collapsed)
              this.setStripVisibilityTo(true);

            this.mPrefs.setBoolPref("browser.tabs.forceHide", false);

            // wire up a progress listener for the new browser object.
            var position = this.mTabContainer.childNodes.length-1;
            var tabListener = this.mTabProgressListener(t, b, blank);
            const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                     .createInstance(Components.interfaces.nsIWebProgress);
            filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
            b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
            this.mTabListeners[position] = tabListener;
            this.mTabFilters[position] = filter;
            
            b._fastFind = this.fastFind;

            if (!blank) {
              // pretend the user typed this so it'll be available till
              // the document successfully loads
              b.userTypedValue = aURI;

              if (aPostData === undefined)
                aPostData = null;
              b.loadURIWithFlags(aURI, nsIWebNavigation.LOAD_FLAGS_NONE,
                                 aReferrerURI, aPostData, null);
            }

            return t;
          ]]>
        </body>
      </method>

      <method name="warnAboutClosingTabs">
      <parameter name="aAll"/>
      <body>
        <![CDATA[          
          var numTabs = this.mTabContainer.childNodes.length;
          var reallyClose = true;
          if (numTabs <= 1)
            return reallyClose;
            
          const pref = "browser.tabs.warnOnClose";
          var shouldPrompt = this.mPrefs.getBoolPref(pref);
          var reallyClose = true;            

          if (shouldPrompt) {
            var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                          .getService(Components.interfaces.nsIPromptService);

            //default to true: if it were false, we wouldn't get this far
            var warnOnClose = { value:true };
            var bundle = this.mStringBundle;
            var tabsToClose = numTabs;  //number of tabs to be removed
            if (!aAll)
              --tabsToClose; 

            var messageKey = (tabsToClose == 1) ? "tabs.closeWarningOne" : "tabs.closeWarningMultiple";
            var closeKey = (tabsToClose == 1) ? "tabs.closeButtonOne" : "tabs.closeButtonMultiple";
            var buttonPressed = promptService.confirmEx(window,
                                                        bundle.getString('tabs.closeWarningTitle'),
                                                        bundle.getFormattedString(messageKey, [tabsToClose]),
                                                        (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
                                                        + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
                                                        bundle.getString(closeKey),
                                                        null, null,
                                                        bundle.getString('tabs.closeWarningPromptMe'),
                                                        warnOnClose);
            reallyClose = (buttonPressed == 0);
            // don't set the pref unless they press OK and it's false
            if (reallyClose && !warnOnClose.value)
              this.mPrefs.setBoolPref(pref, false);
          }
          return reallyClose;
        ]]>
      </body>
      </method>
      
      <method name="removeAllTabsBut">
        <parameter name="aTab"/>
        <body>
          <![CDATA[
              if (this.warnAboutClosingTabs(false)) {
                if (aTab.localName != "tab")
                  aTab = this.mCurrentTab;
                else
                  this.mTabContainer.selectedItem = aTab;

                var childNodes = this.mTabContainer.childNodes;

                for (var i = childNodes.length - 1; i >= 0; --i) {
                  if (childNodes[i] != aTab)
                    this.removeTab(childNodes[i]);
                }
              }
            }
          ]]>
        </body>
      </method>

      <method name="removeCurrentTab">
        <body>
          <![CDATA[
            return this.removeTab(this.mCurrentTab);
          ]]>
        </body>
      </method>

      <method name="removeTab">
        <parameter name="aTab"/>
        <body>
          <![CDATA[
            if (aTab.localName != "tab")
              aTab = this.mCurrentTab;

            var l = this.mTabContainer.childNodes.length;
            if (l == 1) {
              if (!this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
                // blank the tab
                this.loadURI("about:blank");
                return;
              }
              // hide the tab bar
              this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
              this.setStripVisibilityTo(false);
              return;
            }

            var ds = this.getBrowserForTab(aTab).docShell;

            if (ds.contentViewer && !ds.contentViewer.permitUnload())
              return;

            if (l == 2) {
              var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
              if (autohide)
                this.setStripVisibilityTo(false);
            }

            var index = -1;
            if (this.mCurrentTab == aTab)
              index = this.mPanelContainer.selectedIndex;
            else {
              // Find and locate the tab in our list.
              for (var i = 0; i < l; i++)
                if (this.mTabContainer.childNodes[i] == aTab)
                  index = i;
            }

            // Remove the tab's filter and progress listener.
            const filter = this.mTabFilters[index];
            var oldBrowser = this.getBrowserAtIndex(index);
            oldBrowser.webProgress.removeProgressListener(filter);
            filter.removeProgressListener(this.mTabListeners[index]);
            this.mTabFilters.splice(index, 1);
            this.mTabListeners.splice(index, 1);

            // Remove our title change and blocking listeners
            oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, false);

            // We are no longer the primary content area.
            oldBrowser.setAttribute("type", "content");

            // Now select the new tab before nuking the old one.
            var currentIndex = this.mPanelContainer.selectedIndex;

            var newIndex = -1;
            if (currentIndex > index)
              newIndex = currentIndex-1;
            else if (currentIndex < index)
              newIndex = currentIndex;
            else if (index == l - 1)
              newIndex = index-1;
            else
              newIndex = index;

            var oldTab = aTab;

            // clean up the before/afterselected attributes before removing the tab
            oldTab.selected = false;

            // XXX browser's destructor isn't always called, so we force a cleanup ourselves
            oldBrowser.destroy();
            
            this.mTabContainer.removeChild(oldTab);
            this.mPanelContainer.removeChild(this.mPanelContainer.childNodes[index]);

            this.selectedTab = this.mTabContainer.childNodes[newIndex];
            this.mPanelContainer.selectedIndex = newIndex;
            
            this.updateCurrentBrowser();
            }
          ]]>
        </body>
      </method>

      <method name="reloadAllTabs">
        <body>
          <![CDATA[
            var l = this.mPanelContainer.childNodes.length;
            for (var i = 0; i < l; i++) {
              try {
                this.getBrowserAtIndex(i).reload();
              } catch (e) {
                // ignore failure to reload so others will be reloaded
              }
            }
          ]]>
        </body>
      </method>

      <method name="reloadTab">
        <parameter name="aTab"/>
        <body>
          <![CDATA[
            if (aTab.localName != "tab")
              aTab = this.mCurrentTab;

            this.getBrowserForTab(aTab).reload();
          ]]>
        </body>
      </method>

      <method name="onTabBarDblClick">
        <parameter name="aEvent"/>
        <body>
          <![CDATA[
            if (aEvent.originalTarget.localName != 'tab' && aEvent.button == 0)
              this.selectedTab = this.addTab();
          ]]>
        </body>
      </method>

      <method name="addProgressListener">
        <parameter name="aListener"/>
        <parameter name="aMask"/>
        <body>
          <![CDATA[
            if (!this.mProgressListeners) {
              this.mProgressListeners = [];
              const autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
              const forceHide = this.mPrefs.getBoolPref("browser.tabs.forceHide");
              if (!autoHide && !forceHide)
                this.setStripVisibilityTo(true);

              // Hook up a listener for <link>s.
              this.mPanelContainer.addEventListener("DOMLinkAdded", this.onLinkAdded, false);
            }

            if (!this.mTabbedMode && this.mProgressListeners.length == 1) {
              // If we are adding a 2nd progress listener, we need to enter tabbed mode
              // because the browser status filter can only handle one progress listener.
              // In tabbed mode, mTabProgressListener is used which will iterate over all listeners.
              this.enterTabbedMode();
            }

            this.mProgressListeners.push(aListener);

            if (!this.mTabbedMode) {
              // If someone does this:
              // addProgressListener, removeProgressListener, addProgressListener
              // don't create a new filter; reuse the existing filter.
              if (this.mTabFilters.length == 0) {
                // hook a filter up to our first browser
                const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                         .createInstance(Components.interfaces.nsIWebProgress);
                this.mTabFilters[0] = filter;
                this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
              }

              // Directly hook the listener up to the filter for better performance
              this.mTabFilters[0].addProgressListener(aListener, aMask);
            }
          ]]>
        </body>
      </method>

      <method name="removeProgressListener">
        <parameter name="aListener"/>
        <body>
          <![CDATA[
            if (!this.mProgressListeners) return;
            for (var i = 0; i < this.mProgressListeners.length; i++) {
              if (this.mProgressListeners[i] == aListener) {
                this.mProgressListeners[i] = null;
                break;
              }
            }

            if (!this.mTabbedMode)
              // Don't forget to remove it from the filter we hooked it up to
              this.mTabFilters[0].removeProgressListener(aListener);
         ]]>
        </body>
      </method>

      <method name="getBrowserForTab">
        <parameter name="aTab"/>
        <body>
        <![CDATA[
          if (this.mCurrentTab == aTab)
            return this.mCurrentBrowser;

          for (var i = 0; i < this.mTabContainer.childNodes.length; i++) {
            if (this.mTabContainer.childNodes[i] == aTab) {
              return this.getBrowserAtIndex(i);
            }
          }

          return null;
        ]]>
        </body>
      </method>

      <property name="tabContainer">
        <getter>
          return this.mTabContainer;
        </getter>
      </property>

      <property name="selectedTab">
        <getter>
          return this.mTabBox.selectedTab;
        </getter>
        <setter>
          <![CDATA[
          // Update the tab
          this.mTabBox.selectedTab = val;
          return val;
          ]]>
        </setter>
      </property>

      <property name="selectedBrowser"
                onget="return this.mCurrentBrowser;"
                readonly="true"/>


      <property name="browsers"
                onget="return this.mPanelContainer.getElementsByTagName('browser');"
                readonly="true"/>

      <!-- Drag and drop observer API -->
      <!--<method name="onDragStart">
        <parameter name="aEvent"/>
        <parameter name="aXferData"/>
        <parameter name="aDragAction"/>
        <body/>
      </method>-->

      <method name="onDragOver">
        <parameter name="aEvent"/>
        <parameter name="aFlavour"/>
        <parameter name="aDragSession"/>
        <body>
        <![CDATA[
          return; // Just having this makes our feedback correct.
        ]]>
        </body>
      </method>

      <method name="onDrop">
        <parameter name="aEvent"/>
        <parameter name="aXferData"/>
        <parameter name="aDragSession"/>
        <body>
          <![CDATA[
            var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);

            // valid urls don't contain spaces ' '; if we have a space it isn't a valid url so bail out
            if (!url || !url.length || url.indexOf(" ", 0) != -1)
              return;

            var bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");

            var tab = null;
            if (aEvent.originalTarget.localName != "tab") {
              // We're adding a new tab.
              tab = this.addTab(getShortcutOrURI(url));
            }
            else {
              // Load in an existing tab.
              tab = aEvent.originalTarget;
              this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
            }
            if (this.mCurrentTab != tab && !bgLoad)
              this.selectedTab = tab;
          ]]>
        </body>
      </method>

      <method name="getSupportedFlavours">
        <body>
        <![CDATA[
          var flavourSet = new FlavourSet();
          flavourSet.appendFlavour("text/x-moz-url");
          flavourSet.appendFlavour("text/unicode");
          flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
          return flavourSet;
        ]]>
        </body>
      </method>
                                                               
      <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
           MAKE SURE TO ADD IT HERE AS WELL. -->
      <property name="canGoBack"
                onget="return this.mCurrentBrowser.canGoBack;"
                readonly="true"/>

      <property name="canGoForward"
                onget="return this.mCurrentBrowser.canGoForward;"
                readonly="true"/>

      <method name="goBack">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.goBack();
          ]]>
        </body>
      </method>

      <method name="goForward">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.goForward();
          ]]>
        </body>
      </method>

      <method name="reload">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.reload();
          ]]>
        </body>
      </method>

      <method name="reloadWithFlags">
        <parameter name="aFlags"/>
        <body>
          <![CDATA[
            return this.mCurrentBrowser.reloadWithFlags(aFlags);
          ]]>
        </body>
      </method>

      <method name="stop">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.stop();
          ]]>
        </body>
      </method>

      <!-- throws exception for unknown schemes -->
      <method name="loadURI">
        <parameter name="aURI"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <body>
          <![CDATA[
            return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
          ]]>
        </body>
      </method>

      <!-- throws exception for unknown schemes -->
      <method name="loadURIWithFlags">
        <parameter name="aURI"/>
        <parameter name="aFlags"/>
        <parameter name="aReferrerURI"/>
        <parameter name="aCharset"/>
        <body>
          <![CDATA[
            return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset);
          ]]>
        </body>
      </method>

      <method name="goHome">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.goHome();
          ]]>
        </body>
      </method>

      <property name="homePage">
        <getter>
          <![CDATA[
            return this.mCurrentBrowser.homePage;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            this.mCurrentBrowser.homePage = val;
            return val;
          ]]>
        </setter>
      </property>

      <method name="gotoIndex">
        <parameter name="aIndex"/>
        <body>
          <![CDATA[
            return this.mCurrentBrowser.gotoIndex(aIndex);
          ]]>
        </body>
      </method>
      
      <method name="attachFormFill">
        <body><![CDATA[
          for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
            var cb = this.getBrowserAtIndex(i);
            cb.attachFormFill();
          }
        ]]></body>
      </method>

      <method name="detachFormFill">
        <body><![CDATA[
          for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
            var cb = this.getBrowserAtIndex(i);
            cb.detachFormFill();
          }
        ]]></body>
      </method>

      <property name="pageReport"
                onget="return this.mCurrentBrowser.pageReport;"
                readonly="true"/>

      <property name="currentURI"
                onget="return this.mCurrentBrowser.currentURI;"
                readonly="true"/>

      <field name="_fastFind">null</field>
      <property name="fastFind"
                readonly="true">
        <getter>
        <![CDATA[
          if (!this._fastFind) {
            this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
                                       .createInstance(Components.interfaces.nsITypeAheadFind);                                
            this._fastFind.init(this.docShell);
          }
          return this._fastFind;
        ]]>
        </getter>
      </property>
                      
      <property name="findString"
                onget="return this.mCurrentBrowser.findString;"
                readonly="true"/>
                
      <property name="docShell"
                onget="return this.mCurrentBrowser.docShell"
                readonly="true"/>

      <property name="webNavigation"
                onget="return this.mCurrentBrowser.webNavigation"
                readonly="true"/>

      <property name="webBrowserFind"
                readonly="true"
                onget="return this.mCurrentBrowser.webBrowserFind"/>

      <property name="webProgress"
                readonly="true"
                onget="return this.mCurrentBrowser.webProgress"/>

      <property name="contentWindow"
                readonly="true"
                onget="return this.mCurrentBrowser.contentWindow"/>

      <property name="sessionHistory"
                onget="return this.mCurrentBrowser.sessionHistory;"
                readonly="true"/>

      <property name="markupDocumentViewer"
                onget="return this.mCurrentBrowser.markupDocumentViewer;"
                readonly="true"/>

      <property name="contentViewerEdit"
                onget="return this.mCurrentBrowser.contentViewerEdit;"
                readonly="true"/>

      <property name="contentViewerFile"
                onget="return this.mCurrentBrowser.contentViewerFile;"
                readonly="true"/>

      <property name="documentCharsetInfo"
                onget="return this.mCurrentBrowser.documentCharsetInfo;"
                readonly="true"/>

      <property name="contentDocument"
                onget="return this.mCurrentBrowser.contentDocument;"
                readonly="true"/>

      <property name="contentTitle"
                onget="return this.mCurrentBrowser.contentTitle;"
                readonly="true"/>

      <property name="securityUI"
                onget="return this.mCurrentBrowser.securityUI;"
                readonly="true"/>

      <method name="find">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.find();
          ]]>
        </body>
      </method>

      <method name="findAgain">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.findAgain();
          ]]>
        </body>
      </method>

      <method name="findPrevious">
        <body>
          <![CDATA[
            return this.mCurrentBrowser.findPrevious();
          ]]>
        </body>
      </method>
      <field name="_keyEventHandler" readonly="true">
      <![CDATA[({
        tabbrowser: this,
        handleEvent: function handleEvent(aEvent) {
	  if (!aEvent.isTrusted) {
	    // Don't let untrusted events mess with tabs.
	    return;
	  }

          if (aEvent.ctrlKey && aEvent.keyCode == KeyEvent.DOM_VK_F4 && this.tabbrowser.mTabBox.handleCtrlPageUpDown)
            this.tabbrowser.removeCurrentTab();
        }
      })]]>
      </field>      

      <property name="canFindAgain"
                onget="return this.mCurrentBrowser.canFindAgain;"
                readonly="true"/>

      <property name="userTypedClear"
                onget="return this.mCurrentBrowser.userTypedClear;"
                onset="return this.mCurrentBrowser.userTypedClear = val;"/>

      <property name="userTypedValue"
                onget="return this.mCurrentBrowser.userTypedValue;"
                onset="return this.mCurrentBrowser.userTypedValue = val;"/>

      <property name="forceSyncURLBarUpdate"
                onget="return this.mModalDialogShowing;"/>

      <constructor>
        <![CDATA[
          this.mCurrentBrowser = this.getBrowserAtIndex(0);
          this.mCurrentTab = this.mTabContainer.firstChild;
          document.addEventListener("keypress", this._keyEventHandler, false);
        ]]>
      </constructor>

      <destructor>
        <![CDATA[
          for (var i = 0; i < this.mTabListeners.length; ++i) {
            this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
            this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
            this.mTabFilters[i] = null;
            this.mTabListeners[i] = null;
            this.getBrowserAtIndex(i).removeEventListener("DOMTitleChanged", this.onTitleChanged, false);
          }
          this.mPanelContainer.removeEventListener("DOMLinkAdded", this.onLinkAdded, false);
          document.removeEventListener("keypress", this._keyEventHandler, false);
        ]]>
      </destructor>
    </implementation>

    <handlers>
      <handler event="DOMWindowClose">
        <![CDATA[
          if (!event.isTrusted)
            return;

          const browsers = this.mPanelContainer.childNodes;
          if (browsers.length == 1) {
            // There's only one browser left. If a window is being
            // closed and the window is *not* the window in the
            // browser that's still around, prevent the event's default
            // action to prevent closing a window that's being closed
            // already.
            if (this.getBrowserAtIndex(0).contentWindow != event.target)
              event.preventDefault();

            return;
          }

          var i = 0;
          for (; i < browsers.length; ++i) {
            if (this.getBrowserAtIndex(i).contentWindow == event.target) {
              this.removeTab(this.mTabContainer.childNodes[i]);
              event.preventDefault();

              break;
            }
          }
        ]]>
      </handler>
      <handler event="DOMWillOpenModalDialog">
        <![CDATA[
          if (!event.isTrusted)
            return;

          // We're about to open a modal dialog, make sure the opening
          // tab is brought to the front.

          for (var i = 0; i < browsers.length; ++i) {
            if (this.getBrowserAtIndex(i).contentWindow == event.target) {
              this.mModalDialogShowing = true;
              this.selectedTab = this.mTabContainer.childNodes[i];

              break;
            }
          }
        ]]>
      </handler>
      <handler event="DOMModalDialogClosed">
        <![CDATA[
          if (!event.isTrusted)
            return;

          this.mModalDialogShowing = false;
        ]]>
      </handler>
    </handlers>
  </binding>

</bindings>
