browser/base/content/tabbrowser.xml

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/base/content/tabbrowser.xml	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,5077 @@
     1.4 +<?xml version="1.0"?>
     1.5 +
     1.6 +<!-- This Source Code Form is subject to the terms of the Mozilla Public
     1.7 +   - License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 +   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     1.9 +
    1.10 +<!DOCTYPE bindings [
    1.11 +<!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" >
    1.12 +%tabBrowserDTD;
    1.13 +]>
    1.14 +
    1.15 +# MAKE_E10S_WORK surrounds code needed to have the front-end try to be smart
    1.16 +# about using non-remote browsers for loading certain URIs when remote tabs
    1.17 +# (browser.tabs.remote) are enabled.
    1.18 +#define MAKE_E10S_WORK 1
    1.19 +
    1.20 +<bindings id="tabBrowserBindings"
    1.21 +          xmlns="http://www.mozilla.org/xbl"
    1.22 +          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    1.23 +          xmlns:xbl="http://www.mozilla.org/xbl">
    1.24 +
    1.25 +  <binding id="tabbrowser">
    1.26 +    <resources>
    1.27 +      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
    1.28 +    </resources>
    1.29 +
    1.30 +    <content>
    1.31 +      <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
    1.32 +      <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
    1.33 +                  flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
    1.34 +                  onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
    1.35 +        <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
    1.36 +          <xul:notificationbox flex="1">
    1.37 +            <xul:hbox flex="1" class="browserSidebarContainer">
    1.38 +              <xul:vbox flex="1" class="browserContainer">
    1.39 +                <xul:stack flex="1" class="browserStack" anonid="browserStack">
    1.40 +                  <xul:browser anonid="initialBrowser" type="content-primary" message="true"
    1.41 +                               xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectpopup"/>
    1.42 +                </xul:stack>
    1.43 +              </xul:vbox>
    1.44 +            </xul:hbox>
    1.45 +          </xul:notificationbox>
    1.46 +        </xul:tabpanels>
    1.47 +      </xul:tabbox>
    1.48 +      <children/>
    1.49 +    </content>
    1.50 +    <implementation implements="nsIDOMEventListener, nsIMessageListener">
    1.51 +
    1.52 +      <property name="tabContextMenu" readonly="true"
    1.53 +                onget="return this.tabContainer.contextMenu;"/>
    1.54 +
    1.55 +      <field name="tabContainer" readonly="true">
    1.56 +        document.getElementById(this.getAttribute("tabcontainer"));
    1.57 +      </field>
    1.58 +      <field name="tabs" readonly="true">
    1.59 +        this.tabContainer.childNodes;
    1.60 +      </field>
    1.61 +
    1.62 +      <property name="visibleTabs" readonly="true">
    1.63 +        <getter><![CDATA[
    1.64 +          if (!this._visibleTabs)
    1.65 +            this._visibleTabs = Array.filter(this.tabs,
    1.66 +                                             function (tab) !tab.hidden && !tab.closing);
    1.67 +          return this._visibleTabs;
    1.68 +        ]]></getter>
    1.69 +      </property>
    1.70 +
    1.71 +      <field name="closingTabsEnum" readonly="true">({ ALL: 0, OTHER: 1, TO_END: 2 });</field>
    1.72 +
    1.73 +      <field name="_visibleTabs">null</field>
    1.74 +
    1.75 +      <field name="mURIFixup" readonly="true">
    1.76 +        Components.classes["@mozilla.org/docshell/urifixup;1"]
    1.77 +                  .getService(Components.interfaces.nsIURIFixup);
    1.78 +      </field>
    1.79 +      <field name="mFaviconService" readonly="true">
    1.80 +        Components.classes["@mozilla.org/browser/favicon-service;1"]
    1.81 +                  .getService(Components.interfaces.nsIFaviconService);
    1.82 +      </field>
    1.83 +      <field name="_placesAutocomplete" readonly="true">
    1.84 +         Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
    1.85 +                   .getService(Components.interfaces.mozIPlacesAutoComplete);
    1.86 +      </field>
    1.87 +      <field name="_unifiedComplete" readonly="true">
    1.88 +         Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
    1.89 +                   .getService(Components.interfaces.mozIPlacesAutoComplete);
    1.90 +      </field>
    1.91 +      <field name="mTabBox" readonly="true">
    1.92 +        document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
    1.93 +      </field>
    1.94 +      <field name="mPanelContainer" readonly="true">
    1.95 +        document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
    1.96 +      </field>
    1.97 +      <field name="mStringBundle">
    1.98 +        document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
    1.99 +      </field>
   1.100 +      <field name="mCurrentTab">
   1.101 +        null
   1.102 +      </field>
   1.103 +      <field name="_lastRelatedTab">
   1.104 +        null
   1.105 +      </field>
   1.106 +      <field name="mCurrentBrowser">
   1.107 +        null
   1.108 +      </field>
   1.109 +      <field name="mProgressListeners">
   1.110 +        []
   1.111 +      </field>
   1.112 +      <field name="mTabsProgressListeners">
   1.113 +        []
   1.114 +      </field>
   1.115 +      <field name="mTabListeners">
   1.116 +        []
   1.117 +      </field>
   1.118 +      <field name="mTabFilters">
   1.119 +        []
   1.120 +      </field>
   1.121 +      <field name="mIsBusy">
   1.122 +        false
   1.123 +      </field>
   1.124 +      <field name="arrowKeysShouldWrap" readonly="true">
   1.125 +#ifdef XP_MACOSX
   1.126 +        true
   1.127 +#else
   1.128 +        false
   1.129 +#endif
   1.130 +      </field>
   1.131 +
   1.132 +      <field name="_autoScrollPopup">
   1.133 +        null
   1.134 +      </field>
   1.135 +
   1.136 +      <field name="_previewMode">
   1.137 +        false
   1.138 +      </field>
   1.139 +
   1.140 +      <field name="_lastFindValue">
   1.141 +        ""
   1.142 +      </field>
   1.143 +
   1.144 +      <property name="_numPinnedTabs" readonly="true">
   1.145 +        <getter><![CDATA[
   1.146 +          for (var i = 0; i < this.tabs.length; i++) {
   1.147 +            if (!this.tabs[i].pinned)
   1.148 +              break;
   1.149 +          }
   1.150 +          return i;
   1.151 +        ]]></getter>
   1.152 +      </property>
   1.153 +
   1.154 +      <method name="isFindBarInitialized">
   1.155 +        <parameter name="aTab"/>
   1.156 +        <body><![CDATA[
   1.157 +          return (aTab || this.selectedTab)._findBar != undefined;
   1.158 +        ]]></body>
   1.159 +      </method>
   1.160 +
   1.161 +      <method name="getFindBar">
   1.162 +        <parameter name="aTab"/>
   1.163 +        <body><![CDATA[
   1.164 +          if (!aTab)
   1.165 +            aTab = this.selectedTab;
   1.166 +
   1.167 +          if (aTab._findBar)
   1.168 +            return aTab._findBar;
   1.169 +
   1.170 +          let findBar = document.createElementNS(this.namespaceURI, "findbar");
   1.171 +          let browser = this.getBrowserForTab(aTab);
   1.172 +          let browserContainer = this.getBrowserContainer(browser);
   1.173 +          browserContainer.appendChild(findBar);
   1.174 +
   1.175 +          // Force a style flush to ensure that our binding is attached.
   1.176 +          findBar.clientTop;
   1.177 +
   1.178 +          findBar.browser = browser;
   1.179 +          findBar._findField.value = this._lastFindValue;
   1.180 +
   1.181 +          aTab._findBar = findBar;
   1.182 +
   1.183 +          let event = document.createEvent("Events");
   1.184 +          event.initEvent("TabFindInitialized", true, false);
   1.185 +          aTab.dispatchEvent(event);
   1.186 +
   1.187 +          return findBar;
   1.188 +        ]]></body>
   1.189 +      </method>
   1.190 +
   1.191 +      <method name="getStatusPanel">
   1.192 +        <body><![CDATA[
   1.193 +          if (!this._statusPanel) {
   1.194 +            this._statusPanel = document.createElementNS(this.namespaceURI, "statuspanel");
   1.195 +            this._statusPanel.setAttribute("inactive", "true");
   1.196 +            this._statusPanel.setAttribute("layer", "true");
   1.197 +            this._appendStatusPanel();
   1.198 +          }
   1.199 +          return this._statusPanel;
   1.200 +        ]]></body>
   1.201 +      </method>
   1.202 +
   1.203 +      <method name="_appendStatusPanel">
   1.204 +        <body><![CDATA[
   1.205 +          if (this._statusPanel) {
   1.206 +            let browser = this.selectedBrowser;
   1.207 +            let browserContainer = this.getBrowserContainer(browser);
   1.208 +            browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling);
   1.209 +          }
   1.210 +        ]]></body>
   1.211 +      </method>
   1.212 +
   1.213 +      <method name="updateWindowResizers">
   1.214 +        <body><![CDATA[
   1.215 +          if (!window.gShowPageResizers)
   1.216 +            return;
   1.217 +
   1.218 +          var show = window.windowState == window.STATE_NORMAL;
   1.219 +          for (let i = 0; i < this.browsers.length; i++) {
   1.220 +            this.browsers[i].showWindowResizer = show;
   1.221 +          }
   1.222 +        ]]></body>
   1.223 +      </method>
   1.224 +
   1.225 +      <method name="_setCloseKeyState">
   1.226 +        <parameter name="aEnabled"/>
   1.227 +        <body><![CDATA[
   1.228 +          let keyClose = document.getElementById("key_close");
   1.229 +          let closeKeyEnabled = keyClose.getAttribute("disabled") != "true";
   1.230 +          if (closeKeyEnabled == aEnabled)
   1.231 +            return;
   1.232 +
   1.233 +          if (aEnabled)
   1.234 +            keyClose.removeAttribute("disabled");
   1.235 +          else
   1.236 +            keyClose.setAttribute("disabled", "true");
   1.237 +
   1.238 +          // We also want to remove the keyboard shortcut from the file menu
   1.239 +          // when the shortcut is disabled, and bring it back when it's
   1.240 +          // renabled.
   1.241 +          //
   1.242 +          // Fixing bug 630826 could make that happen automatically.
   1.243 +          // Fixing bug 630830 could avoid the ugly hack below.
   1.244 +
   1.245 +          let closeMenuItem = document.getElementById("menu_close");
   1.246 +          let parentPopup = closeMenuItem.parentNode;
   1.247 +          let nextItem = closeMenuItem.nextSibling;
   1.248 +          let clonedItem = closeMenuItem.cloneNode(true);
   1.249 +
   1.250 +          parentPopup.removeChild(closeMenuItem);
   1.251 +
   1.252 +          if (aEnabled)
   1.253 +            clonedItem.setAttribute("key", "key_close");
   1.254 +          else
   1.255 +            clonedItem.removeAttribute("key");
   1.256 +
   1.257 +          parentPopup.insertBefore(clonedItem, nextItem);
   1.258 +        ]]></body>
   1.259 +      </method>
   1.260 +
   1.261 +      <method name="pinTab">
   1.262 +        <parameter name="aTab"/>
   1.263 +        <body><![CDATA[
   1.264 +          if (aTab.pinned)
   1.265 +            return;
   1.266 +
   1.267 +          if (aTab.hidden)
   1.268 +            this.showTab(aTab);
   1.269 +
   1.270 +          this.moveTabTo(aTab, this._numPinnedTabs);
   1.271 +          aTab.setAttribute("pinned", "true");
   1.272 +          this.tabContainer._unlockTabSizing();
   1.273 +          this.tabContainer._positionPinnedTabs();
   1.274 +          this.tabContainer.adjustTabstrip();
   1.275 +
   1.276 +          // Bug 961867 - [e10s] Implement the logic for app tabs
   1.277 +          if (!gMultiProcessBrowser)
   1.278 +            this.getBrowserForTab(aTab).docShell.isAppTab = true;
   1.279 +
   1.280 +          if (aTab.selected)
   1.281 +            this._setCloseKeyState(false);
   1.282 +
   1.283 +          let event = document.createEvent("Events");
   1.284 +          event.initEvent("TabPinned", true, false);
   1.285 +          aTab.dispatchEvent(event);
   1.286 +        ]]></body>
   1.287 +      </method>
   1.288 +
   1.289 +      <method name="unpinTab">
   1.290 +        <parameter name="aTab"/>
   1.291 +        <body><![CDATA[
   1.292 +          if (!aTab.pinned)
   1.293 +            return;
   1.294 +
   1.295 +          this.moveTabTo(aTab, this._numPinnedTabs - 1);
   1.296 +          aTab.removeAttribute("pinned");
   1.297 +          aTab.style.MozMarginStart = "";
   1.298 +          this.tabContainer._unlockTabSizing();
   1.299 +          this.tabContainer._positionPinnedTabs();
   1.300 +          this.tabContainer.adjustTabstrip();
   1.301 +
   1.302 +          // Bug 961867 - [e10s] Implement the logic for app tabs
   1.303 +          if (!gMultiProcessBrowser)
   1.304 +            this.getBrowserForTab(aTab).docShell.isAppTab = false;
   1.305 +
   1.306 +          if (aTab.selected)
   1.307 +            this._setCloseKeyState(true);
   1.308 +
   1.309 +          let event = document.createEvent("Events");
   1.310 +          event.initEvent("TabUnpinned", true, false);
   1.311 +          aTab.dispatchEvent(event);
   1.312 +        ]]></body>
   1.313 +      </method>
   1.314 +
   1.315 +      <method name="previewTab">
   1.316 +        <parameter name="aTab"/>
   1.317 +        <parameter name="aCallback"/>
   1.318 +        <body>
   1.319 +          <![CDATA[
   1.320 +            let currentTab = this.selectedTab;
   1.321 +            try {
   1.322 +              // Suppress focus, ownership and selected tab changes
   1.323 +              this._previewMode = true;
   1.324 +              this.selectedTab = aTab;
   1.325 +              aCallback();
   1.326 +            } finally {
   1.327 +              this.selectedTab = currentTab;
   1.328 +              this._previewMode = false;
   1.329 +            }
   1.330 +          ]]>
   1.331 +        </body>
   1.332 +      </method>
   1.333 +
   1.334 +      <method name="getBrowserAtIndex">
   1.335 +        <parameter name="aIndex"/>
   1.336 +        <body>
   1.337 +          <![CDATA[
   1.338 +            return this.browsers[aIndex];
   1.339 +          ]]>
   1.340 +        </body>
   1.341 +      </method>
   1.342 +
   1.343 +      <method name="getBrowserIndexForDocument">
   1.344 +        <parameter name="aDocument"/>
   1.345 +        <body>
   1.346 +          <![CDATA[
   1.347 +            var tab = this._getTabForContentWindow(aDocument.defaultView);
   1.348 +            return tab ? tab._tPos : -1;
   1.349 +          ]]>
   1.350 +        </body>
   1.351 +      </method>
   1.352 +
   1.353 +      <method name="getBrowserForDocument">
   1.354 +        <parameter name="aDocument"/>
   1.355 +        <body>
   1.356 +          <![CDATA[
   1.357 +            var tab = this._getTabForContentWindow(aDocument.defaultView);
   1.358 +            return tab ? tab.linkedBrowser : null;
   1.359 +          ]]>
   1.360 +        </body>
   1.361 +      </method>
   1.362 +
   1.363 +      <method name="_getTabForContentWindow">
   1.364 +        <parameter name="aWindow"/>
   1.365 +        <body>
   1.366 +        <![CDATA[
   1.367 +          for (let i = 0; i < this.browsers.length; i++) {
   1.368 +            if (this.browsers[i].contentWindow == aWindow)
   1.369 +              return this.tabs[i];
   1.370 +          }
   1.371 +          return null;
   1.372 +        ]]>
   1.373 +        </body>
   1.374 +      </method>
   1.375 +
   1.376 +      <method name="_getTabForBrowser">
   1.377 +        <parameter name="aBrowser"/>
   1.378 +        <body>
   1.379 +        <![CDATA[
   1.380 +          for (let i = 0; i < this.tabs.length; i++) {
   1.381 +            if (this.tabs[i].linkedBrowser == aBrowser)
   1.382 +              return this.tabs[i];
   1.383 +          }
   1.384 +          return null;
   1.385 +        ]]>
   1.386 +        </body>
   1.387 +      </method>
   1.388 +
   1.389 +      <method name="getNotificationBox">
   1.390 +        <parameter name="aBrowser"/>
   1.391 +        <body>
   1.392 +          <![CDATA[
   1.393 +            return this.getSidebarContainer(aBrowser).parentNode;
   1.394 +          ]]>
   1.395 +        </body>
   1.396 +      </method>
   1.397 +
   1.398 +      <method name="getSidebarContainer">
   1.399 +        <parameter name="aBrowser"/>
   1.400 +        <body>
   1.401 +          <![CDATA[
   1.402 +            return this.getBrowserContainer(aBrowser).parentNode;
   1.403 +          ]]>
   1.404 +        </body>
   1.405 +      </method>
   1.406 +
   1.407 +      <method name="getBrowserContainer">
   1.408 +        <parameter name="aBrowser"/>
   1.409 +        <body>
   1.410 +          <![CDATA[
   1.411 +            return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
   1.412 +          ]]>
   1.413 +        </body>
   1.414 +      </method>
   1.415 +
   1.416 +      <method name="getTabModalPromptBox">
   1.417 +        <parameter name="aBrowser"/>
   1.418 +        <body>
   1.419 +          <![CDATA[
   1.420 +            const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
   1.421 +            let browser = (aBrowser || this.mCurrentBrowser);
   1.422 +            let stack = browser.parentNode;
   1.423 +            let self = this;
   1.424 +
   1.425 +            let promptBox = {
   1.426 +              appendPrompt : function(args, onCloseCallback) {
   1.427 +                let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
   1.428 +                stack.appendChild(newPrompt);
   1.429 +                browser.setAttribute("tabmodalPromptShowing", true);
   1.430 +
   1.431 +                newPrompt.clientTop; // style flush to assure binding is attached
   1.432 +
   1.433 +                let tab = self._getTabForBrowser(browser);
   1.434 +                newPrompt.init(args, tab, onCloseCallback);
   1.435 +                return newPrompt;
   1.436 +              },
   1.437 +
   1.438 +              removePrompt : function(aPrompt) {
   1.439 +                stack.removeChild(aPrompt);
   1.440 +
   1.441 +                let prompts = this.listPrompts();
   1.442 +                if (prompts.length) {
   1.443 +                  let prompt = prompts[prompts.length - 1];
   1.444 +                  prompt.Dialog.setDefaultFocus();
   1.445 +                } else {
   1.446 +                  browser.removeAttribute("tabmodalPromptShowing");
   1.447 +                  browser.focus();
   1.448 +                }
   1.449 +              },
   1.450 +
   1.451 +              listPrompts : function(aPrompt) {
   1.452 +                let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
   1.453 +                // NodeList --> real JS array
   1.454 +                let prompts = Array.slice(els);
   1.455 +                return prompts;
   1.456 +              },
   1.457 +            };
   1.458 +
   1.459 +            return promptBox;
   1.460 +          ]]>
   1.461 +        </body>
   1.462 +      </method>
   1.463 +
   1.464 +      <method name="_callProgressListeners">
   1.465 +        <parameter name="aBrowser"/>
   1.466 +        <parameter name="aMethod"/>
   1.467 +        <parameter name="aArguments"/>
   1.468 +        <parameter name="aCallGlobalListeners"/>
   1.469 +        <parameter name="aCallTabsListeners"/>
   1.470 +        <body><![CDATA[
   1.471 +          var rv = true;
   1.472 +
   1.473 +          if (!aBrowser)
   1.474 +            aBrowser = this.mCurrentBrowser;
   1.475 +
   1.476 +          if (aCallGlobalListeners != false &&
   1.477 +              aBrowser == this.mCurrentBrowser) {
   1.478 +            this.mProgressListeners.forEach(function (p) {
   1.479 +              if (aMethod in p) {
   1.480 +                try {
   1.481 +                  if (!p[aMethod].apply(p, aArguments))
   1.482 +                    rv = false;
   1.483 +                } catch (e) {
   1.484 +                  // don't inhibit other listeners
   1.485 +                  Components.utils.reportError(e);
   1.486 +                }
   1.487 +              }
   1.488 +            });
   1.489 +          }
   1.490 +
   1.491 +          if (aCallTabsListeners != false) {
   1.492 +            aArguments.unshift(aBrowser);
   1.493 +
   1.494 +            this.mTabsProgressListeners.forEach(function (p) {
   1.495 +              if (aMethod in p) {
   1.496 +                try {
   1.497 +                  if (!p[aMethod].apply(p, aArguments))
   1.498 +                    rv = false;
   1.499 +                } catch (e) {
   1.500 +                  // don't inhibit other listeners
   1.501 +                  Components.utils.reportError(e);
   1.502 +                }
   1.503 +              }
   1.504 +            });
   1.505 +          }
   1.506 +
   1.507 +          return rv;
   1.508 +        ]]></body>
   1.509 +      </method>
   1.510 +
   1.511 +      <!-- A web progress listener object definition for a given tab. -->
   1.512 +      <method name="mTabProgressListener">
   1.513 +        <parameter name="aTab"/>
   1.514 +        <parameter name="aBrowser"/>
   1.515 +        <parameter name="aStartsBlank"/>
   1.516 +        <body>
   1.517 +        <![CDATA[
   1.518 +          return ({
   1.519 +            mTabBrowser: this,
   1.520 +            mTab: aTab,
   1.521 +            mBrowser: aBrowser,
   1.522 +            mBlank: aStartsBlank,
   1.523 +
   1.524 +            // cache flags for correct status UI update after tab switching
   1.525 +            mStateFlags: 0,
   1.526 +            mStatus: 0,
   1.527 +            mMessage: "",
   1.528 +            mTotalProgress: 0,
   1.529 +
   1.530 +            // count of open requests (should always be 0 or 1)
   1.531 +            mRequestCount: 0,
   1.532 +
   1.533 +            destroy: function () {
   1.534 +              delete this.mTab;
   1.535 +              delete this.mBrowser;
   1.536 +              delete this.mTabBrowser;
   1.537 +            },
   1.538 +
   1.539 +            _callProgressListeners: function () {
   1.540 +              Array.unshift(arguments, this.mBrowser);
   1.541 +              return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
   1.542 +            },
   1.543 +
   1.544 +            _shouldShowProgress: function (aRequest) {
   1.545 +              if (this.mBlank)
   1.546 +                return false;
   1.547 +
   1.548 +              if (gMultiProcessBrowser)
   1.549 +                return true;
   1.550 +
   1.551 +              // Don't show progress indicators in tabs for about: URIs
   1.552 +              // pointing to local resources.
   1.553 +              try {
   1.554 +                let channel = aRequest.QueryInterface(Ci.nsIChannel);
   1.555 +                if (channel.originalURI.schemeIs("about") &&
   1.556 +                    (channel.URI.schemeIs("jar") || channel.URI.schemeIs("file")))
   1.557 +                  return false;
   1.558 +              } catch (e) {}
   1.559 +
   1.560 +              return true;
   1.561 +            },
   1.562 +
   1.563 +            onProgressChange: function (aWebProgress, aRequest,
   1.564 +                                        aCurSelfProgress, aMaxSelfProgress,
   1.565 +                                        aCurTotalProgress, aMaxTotalProgress) {
   1.566 +              this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
   1.567 +
   1.568 +              if (!this._shouldShowProgress(aRequest))
   1.569 +                return;
   1.570 +
   1.571 +              if (this.mTotalProgress)
   1.572 +                this.mTab.setAttribute("progress", "true");
   1.573 +
   1.574 +              this._callProgressListeners("onProgressChange",
   1.575 +                                          [aWebProgress, aRequest,
   1.576 +                                           aCurSelfProgress, aMaxSelfProgress,
   1.577 +                                           aCurTotalProgress, aMaxTotalProgress]);
   1.578 +            },
   1.579 +
   1.580 +            onProgressChange64: function (aWebProgress, aRequest,
   1.581 +                                          aCurSelfProgress, aMaxSelfProgress,
   1.582 +                                          aCurTotalProgress, aMaxTotalProgress) {
   1.583 +              return this.onProgressChange(aWebProgress, aRequest,
   1.584 +                aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
   1.585 +                aMaxTotalProgress);
   1.586 +            },
   1.587 +
   1.588 +            onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
   1.589 +              if (!aRequest)
   1.590 +                return;
   1.591 +
   1.592 +              var oldBlank = this.mBlank;
   1.593 +
   1.594 +              const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
   1.595 +              const nsIChannel = Components.interfaces.nsIChannel;
   1.596 +
   1.597 +              if (aStateFlags & nsIWebProgressListener.STATE_START) {
   1.598 +                this.mRequestCount++;
   1.599 +              }
   1.600 +              else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
   1.601 +                const NS_ERROR_UNKNOWN_HOST = 2152398878;
   1.602 +                if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
   1.603 +                  // to prevent bug 235825: wait for the request handled
   1.604 +                  // by the automatic keyword resolver
   1.605 +                  return;
   1.606 +                }
   1.607 +                // since we (try to) only handle STATE_STOP of the last request,
   1.608 +                // the count of open requests should now be 0
   1.609 +                this.mRequestCount = 0;
   1.610 +              }
   1.611 +
   1.612 +              if (aStateFlags & nsIWebProgressListener.STATE_START &&
   1.613 +                  aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
   1.614 +                // It's okay to clear what the user typed when we start
   1.615 +                // loading a document. If the user types, this counter gets
   1.616 +                // set to zero, if the document load ends without an
   1.617 +                // onLocationChange, this counter gets decremented
   1.618 +                // (so we keep it while switching tabs after failed loads)
   1.619 +                // We need to add 2 because loadURIWithFlags may have
   1.620 +                // cancelled a pending load which would have cleared
   1.621 +                // its anchor scroll detection temporary increment.
   1.622 +                if (aWebProgress.isTopLevel)
   1.623 +                  this.mBrowser.userTypedClear += 2;
   1.624 +
   1.625 +                if (this._shouldShowProgress(aRequest)) {
   1.626 +                  if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
   1.627 +                    this.mTab.setAttribute("busy", "true");
   1.628 +                    if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
   1.629 +                      this.mTabBrowser.setTabTitleLoading(this.mTab);
   1.630 +                  }
   1.631 +
   1.632 +                  if (this.mTab.selected)
   1.633 +                    this.mTabBrowser.mIsBusy = true;
   1.634 +                }
   1.635 +              }
   1.636 +              else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
   1.637 +                       aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
   1.638 +
   1.639 +                if (this.mTab.hasAttribute("busy")) {
   1.640 +                  this.mTab.removeAttribute("busy");
   1.641 +                  this.mTabBrowser._tabAttrModified(this.mTab);
   1.642 +                  if (!this.mTab.selected)
   1.643 +                    this.mTab.setAttribute("unread", "true");
   1.644 +                }
   1.645 +                this.mTab.removeAttribute("progress");
   1.646 +
   1.647 +                if (aWebProgress.isTopLevel) {
   1.648 +                  if (!Components.isSuccessCode(aStatus) &&
   1.649 +                      !isTabEmpty(this.mTab)) {
   1.650 +                    // Restore the current document's location in case the
   1.651 +                    // request was stopped (possibly from a content script)
   1.652 +                    // before the location changed.
   1.653 +
   1.654 +                    this.mBrowser.userTypedValue = null;
   1.655 +
   1.656 +                    if (this.mTab.selected && gURLBar)
   1.657 +                      URLBarSetURI();
   1.658 +                  } else {
   1.659 +                    // The document is done loading, we no longer want the
   1.660 +                    // value cleared.
   1.661 +
   1.662 +                    if (this.mBrowser.userTypedClear > 1)
   1.663 +                      this.mBrowser.userTypedClear -= 2;
   1.664 +                    else if (this.mBrowser.userTypedClear > 0)
   1.665 +                      this.mBrowser.userTypedClear--;
   1.666 +                  }
   1.667 +
   1.668 +                  if (!this.mBrowser.mIconURL)
   1.669 +                    this.mTabBrowser.useDefaultIcon(this.mTab);
   1.670 +                }
   1.671 +
   1.672 +                if (this.mBlank)
   1.673 +                  this.mBlank = false;
   1.674 +
   1.675 +                var location = aRequest.QueryInterface(nsIChannel).URI;
   1.676 +
   1.677 +                // For keyword URIs clear the user typed value since they will be changed into real URIs
   1.678 +                if (location.scheme == "keyword")
   1.679 +                  this.mBrowser.userTypedValue = null;
   1.680 +
   1.681 +                if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.connecting"))
   1.682 +                  this.mTabBrowser.setTabTitle(this.mTab);
   1.683 +
   1.684 +                if (this.mTab.selected)
   1.685 +                  this.mTabBrowser.mIsBusy = false;
   1.686 +              }
   1.687 +
   1.688 +              if (oldBlank) {
   1.689 +                this._callProgressListeners("onUpdateCurrentBrowser",
   1.690 +                                            [aStateFlags, aStatus, "", 0],
   1.691 +                                            true, false);
   1.692 +              } else {
   1.693 +                this._callProgressListeners("onStateChange",
   1.694 +                                            [aWebProgress, aRequest, aStateFlags, aStatus],
   1.695 +                                            true, false);
   1.696 +              }
   1.697 +
   1.698 +              this._callProgressListeners("onStateChange",
   1.699 +                                          [aWebProgress, aRequest, aStateFlags, aStatus],
   1.700 +                                          false);
   1.701 +
   1.702 +              if (aStateFlags & (nsIWebProgressListener.STATE_START |
   1.703 +                                 nsIWebProgressListener.STATE_STOP)) {
   1.704 +                // reset cached temporary values at beginning and end
   1.705 +                this.mMessage = "";
   1.706 +                this.mTotalProgress = 0;
   1.707 +              }
   1.708 +              this.mStateFlags = aStateFlags;
   1.709 +              this.mStatus = aStatus;
   1.710 +            },
   1.711 +
   1.712 +            onLocationChange: function (aWebProgress, aRequest, aLocation,
   1.713 +                                        aFlags) {
   1.714 +              // OnLocationChange is called for both the top-level content
   1.715 +              // and the subframes.
   1.716 +              let topLevel = aWebProgress.isTopLevel;
   1.717 +
   1.718 +              if (topLevel) {
   1.719 +                // If userTypedClear > 0, the document loaded correctly and we should be
   1.720 +                // clearing the user typed value. We also need to clear the typed value
   1.721 +                // if the document failed to load, to make sure the urlbar reflects the
   1.722 +                // failed URI (particularly for SSL errors). However, don't clear the value
   1.723 +                // if the error page's URI is about:blank, because that causes complete
   1.724 +                // loss of urlbar contents for invalid URI errors (see bug 867957).
   1.725 +                if (this.mBrowser.userTypedClear > 0 ||
   1.726 +                    ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
   1.727 +                     aLocation.spec != "about:blank"))
   1.728 +                  this.mBrowser.userTypedValue = null;
   1.729 +
   1.730 +                // Clear out the missing plugins list since it's related to the
   1.731 +                // previous location.
   1.732 +                this.mBrowser.missingPlugins = null;
   1.733 +
   1.734 +                if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
   1.735 +                  let findBar = this.mTabBrowser.getFindBar(this.mTab);
   1.736 +
   1.737 +                  // Close the Find toolbar if we're in old-style TAF mode
   1.738 +                  if (findBar.findMode != findBar.FIND_NORMAL)
   1.739 +                    findBar.close();
   1.740 +
   1.741 +                  // fix bug 253793 - turn off highlight when page changes
   1.742 +                  findBar.getElement("highlight").checked = false;
   1.743 +                }
   1.744 +
   1.745 +                // Don't clear the favicon if this onLocationChange was
   1.746 +                // triggered by a pushState or a replaceState.  See bug 550565.
   1.747 +                if (aWebProgress.isLoadingDocument &&
   1.748 +                    !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
   1.749 +                  this.mBrowser.mIconURL = null;
   1.750 +                }
   1.751 +
   1.752 +                let autocomplete = this.mTabBrowser._placesAutocomplete;
   1.753 +                let unifiedComplete = this.mTabBrowser._unifiedComplete;
   1.754 +                if (this.mBrowser.registeredOpenURI) {
   1.755 +                  autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
   1.756 +                  unifiedComplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
   1.757 +                  delete this.mBrowser.registeredOpenURI;
   1.758 +                }
   1.759 +                // Tabs in private windows aren't registered as "Open" so
   1.760 +                // that they don't appear as switch-to-tab candidates.
   1.761 +                if (!isBlankPageURL(aLocation.spec) &&
   1.762 +                    (!PrivateBrowsingUtils.isWindowPrivate(window) ||
   1.763 +                    PrivateBrowsingUtils.permanentPrivateBrowsing)) {
   1.764 +                  autocomplete.registerOpenPage(aLocation);
   1.765 +                  unifiedComplete.registerOpenPage(aLocation);
   1.766 +                  this.mBrowser.registeredOpenURI = aLocation;
   1.767 +                }
   1.768 +              }
   1.769 +
   1.770 +              if (!this.mBlank) {
   1.771 +                this._callProgressListeners("onLocationChange",
   1.772 +                                            [aWebProgress, aRequest, aLocation,
   1.773 +                                             aFlags]);
   1.774 +              }
   1.775 +
   1.776 +              if (topLevel) {
   1.777 +                this.mBrowser.lastURI = aLocation;
   1.778 +                this.mBrowser.lastLocationChange = Date.now();
   1.779 +              }
   1.780 +            },
   1.781 +
   1.782 +            onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
   1.783 +              if (this.mBlank)
   1.784 +                return;
   1.785 +
   1.786 +              this._callProgressListeners("onStatusChange",
   1.787 +                                          [aWebProgress, aRequest, aStatus, aMessage]);
   1.788 +
   1.789 +              this.mMessage = aMessage;
   1.790 +            },
   1.791 +
   1.792 +            onSecurityChange: function (aWebProgress, aRequest, aState) {
   1.793 +              this._callProgressListeners("onSecurityChange",
   1.794 +                                          [aWebProgress, aRequest, aState]);
   1.795 +            },
   1.796 +
   1.797 +            onRefreshAttempted: function (aWebProgress, aURI, aDelay, aSameURI) {
   1.798 +              return this._callProgressListeners("onRefreshAttempted",
   1.799 +                                                 [aWebProgress, aURI, aDelay, aSameURI]);
   1.800 +            },
   1.801 +
   1.802 +            QueryInterface: function (aIID) {
   1.803 +              if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
   1.804 +                  aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
   1.805 +                  aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
   1.806 +                  aIID.equals(Components.interfaces.nsISupports))
   1.807 +                return this;
   1.808 +              throw Components.results.NS_NOINTERFACE;
   1.809 +            }
   1.810 +          });
   1.811 +        ]]>
   1.812 +        </body>
   1.813 +      </method>
   1.814 +
   1.815 +      <method name="setIcon">
   1.816 +        <parameter name="aTab"/>
   1.817 +        <parameter name="aURI"/>
   1.818 +        <body>
   1.819 +          <![CDATA[
   1.820 +            var browser = this.getBrowserForTab(aTab);
   1.821 +            browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
   1.822 +
   1.823 +            if (aURI && this.mFaviconService) {
   1.824 +              if (!(aURI instanceof Ci.nsIURI))
   1.825 +                aURI = makeURI(aURI);
   1.826 +              this.mFaviconService.setAndFetchFaviconForPage(browser.currentURI,
   1.827 +                                                             aURI, false,
   1.828 +                                                             PrivateBrowsingUtils.isWindowPrivate(window) ?
   1.829 +                                                               this.mFaviconService.FAVICON_LOAD_PRIVATE :
   1.830 +                                                               this.mFaviconService.FAVICON_LOAD_NON_PRIVATE);
   1.831 +            }
   1.832 +
   1.833 +            let sizedIconUrl = browser.mIconURL || "";
   1.834 +            if (sizedIconUrl) {
   1.835 +              let size = Math.round(16 * window.devicePixelRatio);
   1.836 +              sizedIconUrl += (sizedIconUrl.contains("#") ? "&" : "#") +
   1.837 +                              "-moz-resolution=" + size + "," + size;
   1.838 +            }
   1.839 +            if (sizedIconUrl != aTab.getAttribute("image")) {
   1.840 +              if (sizedIconUrl)
   1.841 +                aTab.setAttribute("image", sizedIconUrl);
   1.842 +              else
   1.843 +                aTab.removeAttribute("image");
   1.844 +              this._tabAttrModified(aTab);
   1.845 +            }
   1.846 +
   1.847 +            this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
   1.848 +          ]]>
   1.849 +        </body>
   1.850 +      </method>
   1.851 +
   1.852 +      <method name="getIcon">
   1.853 +        <parameter name="aTab"/>
   1.854 +        <body>
   1.855 +          <![CDATA[
   1.856 +            let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
   1.857 +            return browser.mIconURL;
   1.858 +          ]]>
   1.859 +        </body>
   1.860 +      </method>
   1.861 +
   1.862 +      <method name="shouldLoadFavIcon">
   1.863 +        <parameter name="aURI"/>
   1.864 +        <body>
   1.865 +          <![CDATA[
   1.866 +            return (aURI &&
   1.867 +                    Services.prefs.getBoolPref("browser.chrome.site_icons") &&
   1.868 +                    Services.prefs.getBoolPref("browser.chrome.favicons") &&
   1.869 +                    ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
   1.870 +          ]]>
   1.871 +        </body>
   1.872 +      </method>
   1.873 +
   1.874 +      <method name="useDefaultIcon">
   1.875 +        <parameter name="aTab"/>
   1.876 +        <body>
   1.877 +          <![CDATA[
   1.878 +            var browser = this.getBrowserForTab(aTab);
   1.879 +            var documentURI = browser.documentURI;
   1.880 +            var icon = null;
   1.881 +
   1.882 +            if (browser.imageDocument) {
   1.883 +              if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
   1.884 +                let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
   1.885 +                if (browser.imageDocument.width <= sz &&
   1.886 +                    browser.imageDocument.height <= sz) {
   1.887 +                  icon = browser.currentURI;
   1.888 +                }
   1.889 +              }
   1.890 +            }
   1.891 +            // Use documentURIObject in the check for shouldLoadFavIcon so that we
   1.892 +            // do the right thing with about:-style error pages.  Bug 453442
   1.893 +            else if (this.shouldLoadFavIcon(documentURI)) {
   1.894 +              let url = documentURI.prePath + "/favicon.ico";
   1.895 +              if (!this.isFailedIcon(url))
   1.896 +                icon = url;
   1.897 +            }
   1.898 +            this.setIcon(aTab, icon);
   1.899 +          ]]>
   1.900 +        </body>
   1.901 +      </method>
   1.902 +
   1.903 +      <method name="isFailedIcon">
   1.904 +        <parameter name="aURI"/>
   1.905 +        <body>
   1.906 +          <![CDATA[
   1.907 +            if (this.mFaviconService) {
   1.908 +              if (!(aURI instanceof Ci.nsIURI))
   1.909 +                aURI = makeURI(aURI);
   1.910 +              return this.mFaviconService.isFailedFavicon(aURI);
   1.911 +            }
   1.912 +            return null;
   1.913 +          ]]>
   1.914 +        </body>
   1.915 +      </method>
   1.916 +
   1.917 +      <method name="getWindowTitleForBrowser">
   1.918 +        <parameter name="aBrowser"/>
   1.919 +        <body>
   1.920 +          <![CDATA[
   1.921 +            var newTitle = "";
   1.922 +            var docElement = this.ownerDocument.documentElement;
   1.923 +            var sep = docElement.getAttribute("titlemenuseparator");
   1.924 +
   1.925 +            // Strip out any null bytes in the content title, since the
   1.926 +            // underlying widget implementations of nsWindow::SetTitle pass
   1.927 +            // null-terminated strings to system APIs.
   1.928 +            var docTitle = aBrowser.contentTitle.replace("\0", "", "g");
   1.929 +
   1.930 +            if (!docTitle)
   1.931 +              docTitle = docElement.getAttribute("titledefault");
   1.932 +
   1.933 +            var modifier = docElement.getAttribute("titlemodifier");
   1.934 +            if (docTitle) {
   1.935 +              newTitle += docElement.getAttribute("titlepreface");
   1.936 +              newTitle += docTitle;
   1.937 +              if (modifier)
   1.938 +                newTitle += sep;
   1.939 +            }
   1.940 +            newTitle += modifier;
   1.941 +
   1.942 +            // If location bar is hidden and the URL type supports a host,
   1.943 +            // add the scheme and host to the title to prevent spoofing.
   1.944 +            // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
   1.945 +            try {
   1.946 +              if (docElement.getAttribute("chromehidden").contains("location")) {
   1.947 +                var uri = this.mURIFixup.createExposableURI(
   1.948 +                            aBrowser.currentURI);
   1.949 +                if (uri.scheme == "about")
   1.950 +                  newTitle = uri.spec + sep + newTitle;
   1.951 +                else
   1.952 +                  newTitle = uri.prePath + sep + newTitle;
   1.953 +              }
   1.954 +            } catch (e) {}
   1.955 +
   1.956 +            return newTitle;
   1.957 +          ]]>
   1.958 +        </body>
   1.959 +      </method>
   1.960 +
   1.961 +      <method name="updateTitlebar">
   1.962 +        <body>
   1.963 +          <![CDATA[
   1.964 +            if ("TabView" in window && TabView.isVisible()) {
   1.965 +              // ToDo: this will be removed when we gain ability to draw to the menu bar.
   1.966 +              // Bug 586175
   1.967 +              this.ownerDocument.title = TabView.windowTitle;
   1.968 +            }
   1.969 +            else {
   1.970 +              this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
   1.971 +            }
   1.972 +          ]]>
   1.973 +        </body>
   1.974 +      </method>
   1.975 +
   1.976 +      <method name="updateCurrentBrowser">
   1.977 +        <parameter name="aForceUpdate"/>
   1.978 +        <body>
   1.979 +          <![CDATA[
   1.980 +            var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
   1.981 +            if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
   1.982 +              return;
   1.983 +
   1.984 +            if (!aForceUpdate) {
   1.985 +              TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
   1.986 +              window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
   1.987 +                                                             .beginTabSwitch();
   1.988 +            }
   1.989 +
   1.990 +            var oldTab = this.mCurrentTab;
   1.991 +
   1.992 +            // Preview mode should not reset the owner
   1.993 +            if (!this._previewMode && !oldTab.selected)
   1.994 +              oldTab.owner = null;
   1.995 +
   1.996 +            if (this._lastRelatedTab) {
   1.997 +              if (!this._lastRelatedTab.selected)
   1.998 +                this._lastRelatedTab.owner = null;
   1.999 +              this._lastRelatedTab = null;
  1.1000 +            }
  1.1001 +
  1.1002 +            var oldBrowser = this.mCurrentBrowser;
  1.1003 +            oldBrowser.setAttribute("type", "content-targetable");
  1.1004 +            oldBrowser.docShellIsActive = false;
  1.1005 +
  1.1006 +            var updateBlockedPopups = false;
  1.1007 +            if ((oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
  1.1008 +                (!oldBrowser.blockedPopups && newBrowser.blockedPopups))
  1.1009 +              updateBlockedPopups = true;
  1.1010 +
  1.1011 +            newBrowser.setAttribute("type", "content-primary");
  1.1012 +            newBrowser.docShellIsActive =
  1.1013 +              (window.windowState != window.STATE_MINIMIZED);
  1.1014 +            this.mCurrentBrowser = newBrowser;
  1.1015 +            this.mCurrentTab = this.tabContainer.selectedItem;
  1.1016 +            this.showTab(this.mCurrentTab);
  1.1017 +
  1.1018 +            var forwardButtonContainer = document.getElementById("urlbar-wrapper");
  1.1019 +            if (forwardButtonContainer) {
  1.1020 +              forwardButtonContainer.setAttribute("switchingtabs", "true");
  1.1021 +              window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
  1.1022 +                window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
  1.1023 +                forwardButtonContainer.removeAttribute("switchingtabs");
  1.1024 +              });
  1.1025 +            }
  1.1026 +
  1.1027 +            this._appendStatusPanel();
  1.1028 +
  1.1029 +            if (updateBlockedPopups)
  1.1030 +              this.mCurrentBrowser.updateBlockedPopups(false);
  1.1031 +
  1.1032 +            // Update the URL bar.
  1.1033 +            var loc = this.mCurrentBrowser.currentURI;
  1.1034 +
  1.1035 +            // Bug 666809 - SecurityUI support for e10s
  1.1036 +            var webProgress = this.mCurrentBrowser.webProgress;
  1.1037 +            var securityUI = this.mCurrentBrowser.securityUI;
  1.1038 +
  1.1039 +            this._callProgressListeners(null, "onLocationChange",
  1.1040 +                                        [webProgress, null, loc, 0], true,
  1.1041 +                                        false);
  1.1042 +
  1.1043 +            if (securityUI) {
  1.1044 +              this._callProgressListeners(null, "onSecurityChange",
  1.1045 +                                          [webProgress, null, securityUI.state], true, false);
  1.1046 +            }
  1.1047 +
  1.1048 +            var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
  1.1049 +            if (listener && listener.mStateFlags) {
  1.1050 +              this._callProgressListeners(null, "onUpdateCurrentBrowser",
  1.1051 +                                          [listener.mStateFlags, listener.mStatus,
  1.1052 +                                           listener.mMessage, listener.mTotalProgress],
  1.1053 +                                          true, false);
  1.1054 +            }
  1.1055 +
  1.1056 +            if (!this._previewMode) {
  1.1057 +              this.mCurrentTab.removeAttribute("unread");
  1.1058 +              oldTab.lastAccessed = Date.now();
  1.1059 +
  1.1060 +              let oldFindBar = oldTab._findBar;
  1.1061 +              if (oldFindBar &&
  1.1062 +                  oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
  1.1063 +                  !oldFindBar.hidden)
  1.1064 +                this._lastFindValue = oldFindBar._findField.value;
  1.1065 +
  1.1066 +              this.updateTitlebar();
  1.1067 +
  1.1068 +              this.mCurrentTab.removeAttribute("titlechanged");
  1.1069 +            }
  1.1070 +
  1.1071 +            // If the new tab is busy, and our current state is not busy, then
  1.1072 +            // we need to fire a start to all progress listeners.
  1.1073 +            const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  1.1074 +            if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
  1.1075 +              this.mIsBusy = true;
  1.1076 +              this._callProgressListeners(null, "onStateChange",
  1.1077 +                                          [webProgress, null,
  1.1078 +                                           nsIWebProgressListener.STATE_START |
  1.1079 +                                           nsIWebProgressListener.STATE_IS_NETWORK, 0],
  1.1080 +                                          true, false);
  1.1081 +            }
  1.1082 +
  1.1083 +            // If the new tab is not busy, and our current state is busy, then
  1.1084 +            // we need to fire a stop to all progress listeners.
  1.1085 +            if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
  1.1086 +              this.mIsBusy = false;
  1.1087 +              this._callProgressListeners(null, "onStateChange",
  1.1088 +                                          [webProgress, null,
  1.1089 +                                           nsIWebProgressListener.STATE_STOP |
  1.1090 +                                           nsIWebProgressListener.STATE_IS_NETWORK, 0],
  1.1091 +                                          true, false);
  1.1092 +            }
  1.1093 +
  1.1094 +            this._setCloseKeyState(!this.mCurrentTab.pinned);
  1.1095 +
  1.1096 +            // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
  1.1097 +            // that might rely upon the other changes suppressed.
  1.1098 +            // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
  1.1099 +            if (!this._previewMode) {
  1.1100 +              // We've selected the new tab, so go ahead and notify listeners.
  1.1101 +              let event = new CustomEvent("TabSelect", {
  1.1102 +                bubbles: true,
  1.1103 +                cancelable: false,
  1.1104 +                detail: {
  1.1105 +                  previousTab: oldTab
  1.1106 +                }
  1.1107 +              });
  1.1108 +              this.mCurrentTab.dispatchEvent(event);
  1.1109 +
  1.1110 +              this._tabAttrModified(oldTab);
  1.1111 +              this._tabAttrModified(this.mCurrentTab);
  1.1112 +
  1.1113 +              if (oldBrowser != newBrowser &&
  1.1114 +                  oldBrowser.docShell &&
  1.1115 +                  oldBrowser.docShell.contentViewer.inPermitUnload) {
  1.1116 +                // Since the user is switching away from a tab that has
  1.1117 +                // a beforeunload prompt active, we remove the prompt.
  1.1118 +                // This prevents confusing user flows like the following:
  1.1119 +                //   1. User attempts to close Firefox
  1.1120 +                //   2. User switches tabs (ingoring a beforeunload prompt)
  1.1121 +                //   3. User returns to tab, presses "Leave page"
  1.1122 +                let promptBox = this.getTabModalPromptBox(oldBrowser);
  1.1123 +                let prompts = promptBox.listPrompts();
  1.1124 +                // NB: This code assumes that the beforeunload prompt
  1.1125 +                //     is the top-most prompt on the tab.
  1.1126 +                promptBox.removePrompt(prompts[prompts.length - 1]);
  1.1127 +              }
  1.1128 +
  1.1129 +              // Adjust focus
  1.1130 +              oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
  1.1131 +              if (this.isFindBarInitialized(oldTab)) {
  1.1132 +                let findBar = this.getFindBar(oldTab);
  1.1133 +                oldTab._findBarFocused = (!findBar.hidden &&
  1.1134 +                  findBar._findField.getAttribute("focused") == "true");
  1.1135 +              }
  1.1136 +              do {
  1.1137 +                // When focus is in the tab bar, retain it there.
  1.1138 +                if (document.activeElement == oldTab) {
  1.1139 +                  // We need to explicitly focus the new tab, because
  1.1140 +                  // tabbox.xml does this only in some cases.
  1.1141 +                  this.mCurrentTab.focus();
  1.1142 +                  break;
  1.1143 +                }
  1.1144 +
  1.1145 +                // If there's a tabmodal prompt showing, focus it.
  1.1146 +                if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
  1.1147 +                  let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  1.1148 +                  let prompts = newBrowser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
  1.1149 +                  let prompt = prompts[prompts.length - 1];
  1.1150 +                  prompt.Dialog.setDefaultFocus();
  1.1151 +                  break;
  1.1152 +                }
  1.1153 +
  1.1154 +                // Focus the location bar if it was previously focused for that tab.
  1.1155 +                // In full screen mode, only bother making the location bar visible
  1.1156 +                // if the tab is a blank one.
  1.1157 +                if (newBrowser._urlbarFocused && gURLBar) {
  1.1158 +
  1.1159 +                  // Explicitly close the popup if the URL bar retains focus
  1.1160 +                  gURLBar.closePopup();
  1.1161 +
  1.1162 +                  if (!window.fullScreen) {
  1.1163 +                    gURLBar.focus();
  1.1164 +                    break;
  1.1165 +                  } else if (isTabEmpty(this.mCurrentTab)) {
  1.1166 +                    focusAndSelectUrlBar();
  1.1167 +                    break;
  1.1168 +                  }
  1.1169 +                }
  1.1170 +
  1.1171 +                // Focus the find bar if it was previously focused for that tab.
  1.1172 +                if (gFindBarInitialized && !gFindBar.hidden &&
  1.1173 +                    this.selectedTab._findBarFocused) {
  1.1174 +                  gFindBar._findField.focus();
  1.1175 +                  break;
  1.1176 +                }
  1.1177 +
  1.1178 +                // Otherwise, focus the content area.
  1.1179 +                let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
  1.1180 +                let focusFlags = fm.FLAG_NOSCROLL;
  1.1181 +
  1.1182 +                if (!gMultiProcessBrowser) {
  1.1183 +                  let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
  1.1184 +
  1.1185 +                  // for anchors, use FLAG_SHOWRING so that it is clear what link was
  1.1186 +                  // last clicked when switching back to that tab
  1.1187 +                  if (newFocusedElement &&
  1.1188 +                      (newFocusedElement instanceof HTMLAnchorElement ||
  1.1189 +                       newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
  1.1190 +                    focusFlags |= fm.FLAG_SHOWRING;
  1.1191 +                }
  1.1192 +                fm.setFocus(newBrowser, focusFlags);
  1.1193 +              } while (false);
  1.1194 +            }
  1.1195 +
  1.1196 +            this.tabContainer._setPositionalAttributes();
  1.1197 +
  1.1198 +            if (!aForceUpdate)
  1.1199 +              TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
  1.1200 +          ]]>
  1.1201 +        </body>
  1.1202 +      </method>
  1.1203 +
  1.1204 +      <method name="_tabAttrModified">
  1.1205 +        <parameter name="aTab"/>
  1.1206 +        <body><![CDATA[
  1.1207 +          if (aTab.closing)
  1.1208 +            return;
  1.1209 +
  1.1210 +          // This event should be dispatched when any of these attributes change:
  1.1211 +          // label, crop, busy, image, selected
  1.1212 +          var event = document.createEvent("Events");
  1.1213 +          event.initEvent("TabAttrModified", true, false);
  1.1214 +          aTab.dispatchEvent(event);
  1.1215 +        ]]></body>
  1.1216 +      </method>
  1.1217 +
  1.1218 +      <method name="setTabTitleLoading">
  1.1219 +        <parameter name="aTab"/>
  1.1220 +        <body>
  1.1221 +          <![CDATA[
  1.1222 +            aTab.label = this.mStringBundle.getString("tabs.connecting");
  1.1223 +            aTab.crop = "end";
  1.1224 +            this._tabAttrModified(aTab);
  1.1225 +          ]]>
  1.1226 +        </body>
  1.1227 +      </method>
  1.1228 +
  1.1229 +      <method name="setTabTitle">
  1.1230 +        <parameter name="aTab"/>
  1.1231 +        <body>
  1.1232 +          <![CDATA[
  1.1233 +            var browser = this.getBrowserForTab(aTab);
  1.1234 +            var crop = "end";
  1.1235 +            var title = browser.contentTitle;
  1.1236 +
  1.1237 +            if (!title) {
  1.1238 +              if (browser.currentURI.spec) {
  1.1239 +                try {
  1.1240 +                  title = this.mURIFixup.createExposableURI(browser.currentURI).spec;
  1.1241 +                } catch(ex) {
  1.1242 +                  title = browser.currentURI.spec;
  1.1243 +                }
  1.1244 +              }
  1.1245 +
  1.1246 +              if (title && !isBlankPageURL(title)) {
  1.1247 +                // At this point, we now have a URI.
  1.1248 +                // Let's try to unescape it using a character set
  1.1249 +                // in case the URI is not ASCII.
  1.1250 +                try {
  1.1251 +                  var characterSet = browser.characterSet;
  1.1252 +                  const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
  1.1253 +                                                 .getService(Components.interfaces.nsITextToSubURI);
  1.1254 +                  title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
  1.1255 +                } catch(ex) { /* Do nothing. */ }
  1.1256 +
  1.1257 +                crop = "center";
  1.1258 +
  1.1259 +              } else // Still no title?  Fall back to our untitled string.
  1.1260 +                title = this.mStringBundle.getString("tabs.emptyTabTitle");
  1.1261 +            }
  1.1262 +
  1.1263 +            if (aTab.label == title &&
  1.1264 +                aTab.crop == crop)
  1.1265 +              return false;
  1.1266 +
  1.1267 +            aTab.label = title;
  1.1268 +            aTab.crop = crop;
  1.1269 +            this._tabAttrModified(aTab);
  1.1270 +
  1.1271 +            if (aTab.selected)
  1.1272 +              this.updateTitlebar();
  1.1273 +
  1.1274 +            return true;
  1.1275 +          ]]>
  1.1276 +        </body>
  1.1277 +      </method>
  1.1278 +
  1.1279 +      <method name="loadOneTab">
  1.1280 +        <parameter name="aURI"/>
  1.1281 +        <parameter name="aReferrerURI"/>
  1.1282 +        <parameter name="aCharset"/>
  1.1283 +        <parameter name="aPostData"/>
  1.1284 +        <parameter name="aLoadInBackground"/>
  1.1285 +        <parameter name="aAllowThirdPartyFixup"/>
  1.1286 +        <body>
  1.1287 +          <![CDATA[
  1.1288 +            var aFromExternal;
  1.1289 +            var aRelatedToCurrent;
  1.1290 +            var aDisableMCB;
  1.1291 +            var aSkipAnimation;
  1.1292 +            if (arguments.length == 2 &&
  1.1293 +                typeof arguments[1] == "object" &&
  1.1294 +                !(arguments[1] instanceof Ci.nsIURI)) {
  1.1295 +              let params = arguments[1];
  1.1296 +              aReferrerURI          = params.referrerURI;
  1.1297 +              aCharset              = params.charset;
  1.1298 +              aPostData             = params.postData;
  1.1299 +              aLoadInBackground     = params.inBackground;
  1.1300 +              aAllowThirdPartyFixup = params.allowThirdPartyFixup;
  1.1301 +              aFromExternal         = params.fromExternal;
  1.1302 +              aRelatedToCurrent     = params.relatedToCurrent;
  1.1303 +              aDisableMCB           = params.disableMCB;
  1.1304 +              aSkipAnimation        = params.skipAnimation;
  1.1305 +            }
  1.1306 +
  1.1307 +            var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
  1.1308 +                         Services.prefs.getBoolPref("browser.tabs.loadInBackground");
  1.1309 +            var owner = bgLoad ? null : this.selectedTab;
  1.1310 +            var tab = this.addTab(aURI, {
  1.1311 +                                  referrerURI: aReferrerURI,
  1.1312 +                                  charset: aCharset,
  1.1313 +                                  postData: aPostData,
  1.1314 +                                  ownerTab: owner,
  1.1315 +                                  allowThirdPartyFixup: aAllowThirdPartyFixup,
  1.1316 +                                  fromExternal: aFromExternal,
  1.1317 +                                  relatedToCurrent: aRelatedToCurrent,
  1.1318 +                                  skipAnimation: aSkipAnimation,
  1.1319 +                                  disableMCB: aDisableMCB});
  1.1320 +            if (!bgLoad)
  1.1321 +              this.selectedTab = tab;
  1.1322 +
  1.1323 +            return tab;
  1.1324 +         ]]>
  1.1325 +        </body>
  1.1326 +      </method>
  1.1327 +
  1.1328 +      <method name="loadTabs">
  1.1329 +        <parameter name="aURIs"/>
  1.1330 +        <parameter name="aLoadInBackground"/>
  1.1331 +        <parameter name="aReplace"/>
  1.1332 +        <body><![CDATA[
  1.1333 +          if (!aURIs.length)
  1.1334 +            return;
  1.1335 +
  1.1336 +          // The tab selected after this new tab is closed (i.e. the new tab's
  1.1337 +          // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
  1.1338 +          // when several urls are opened here (i.e. closing the first should select
  1.1339 +          // the next of many URLs opened) or if the pref to have UI links opened in
  1.1340 +          // the background is set (i.e. the link is not being opened modally)
  1.1341 +          //
  1.1342 +          // i.e.
  1.1343 +          //    Number of URLs    Load UI Links in BG       Focus Last Viewed?
  1.1344 +          //    == 1              false                     YES
  1.1345 +          //    == 1              true                      NO
  1.1346 +          //    > 1               false/true                NO
  1.1347 +          var multiple = aURIs.length > 1;
  1.1348 +          var owner = multiple || aLoadInBackground ? null : this.selectedTab;
  1.1349 +          var firstTabAdded = null;
  1.1350 +
  1.1351 +          if (aReplace) {
  1.1352 +            try {
  1.1353 +              this.loadURI(aURIs[0], null, null);
  1.1354 +            } catch (e) {
  1.1355 +              // Ignore failure in case a URI is wrong, so we can continue
  1.1356 +              // opening the next ones.
  1.1357 +            }
  1.1358 +          }
  1.1359 +          else
  1.1360 +            firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: multiple});
  1.1361 +
  1.1362 +          var tabNum = this.tabContainer.selectedIndex;
  1.1363 +          for (let i = 1; i < aURIs.length; ++i) {
  1.1364 +            let tab = this.addTab(aURIs[i], {skipAnimation: true});
  1.1365 +            if (aReplace)
  1.1366 +              this.moveTabTo(tab, ++tabNum);
  1.1367 +          }
  1.1368 +
  1.1369 +          if (!aLoadInBackground) {
  1.1370 +            if (firstTabAdded) {
  1.1371 +              // .selectedTab setter focuses the content area
  1.1372 +              this.selectedTab = firstTabAdded;
  1.1373 +            }
  1.1374 +            else
  1.1375 +              this.selectedBrowser.focus();
  1.1376 +          }
  1.1377 +        ]]></body>
  1.1378 +      </method>
  1.1379 +
  1.1380 +#ifdef MAKE_E10S_WORK
  1.1381 +      <method name="updateBrowserRemoteness">
  1.1382 +        <parameter name="aBrowser"/>
  1.1383 +        <parameter name="aURL"/>
  1.1384 +        <body>
  1.1385 +          <![CDATA[
  1.1386 +            let shouldBeRemote = this._shouldBrowserBeRemote(aURL);
  1.1387 +
  1.1388 +            let isRemote = aBrowser.getAttribute("remote") == "true";
  1.1389 +            if (isRemote == shouldBeRemote)
  1.1390 +              return false;
  1.1391 +
  1.1392 +            let wasActive = document.activeElement == aBrowser;
  1.1393 +
  1.1394 +            // Unhook our progress listener.
  1.1395 +            let tab = this._getTabForBrowser(aBrowser);
  1.1396 +            let index = tab._tPos;
  1.1397 +            let filter = this.mTabFilters[index];
  1.1398 +            aBrowser.webProgress.removeProgressListener(filter);
  1.1399 +
  1.1400 +            // Change the "remote" attribute.
  1.1401 +            let parent = aBrowser.parentNode;
  1.1402 +            parent.removeChild(aBrowser);
  1.1403 +            aBrowser.setAttribute("remote", shouldBeRemote ? "true" : "false");
  1.1404 +            parent.appendChild(aBrowser);
  1.1405 +
  1.1406 +            // Restore the progress listener.
  1.1407 +            aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
  1.1408 +
  1.1409 +            if (shouldBeRemote)
  1.1410 +              tab.setAttribute("remote", "true");
  1.1411 +            else
  1.1412 +              tab.removeAttribute("remote");
  1.1413 +
  1.1414 +            if (wasActive)
  1.1415 +              aBrowser.focus();
  1.1416 +
  1.1417 +            return true;
  1.1418 +          ]]>
  1.1419 +        </body>
  1.1420 +      </method>
  1.1421 +
  1.1422 +      <!--
  1.1423 +        Returns true if we want to load the content for this URL in a
  1.1424 +        remote process. Eventually this should just check whether aURL
  1.1425 +        is unprivileged. Right now, though, we would like to load
  1.1426 +        some unprivileged URLs (like about:neterror) in the main
  1.1427 +        process since they interact with chrome code through
  1.1428 +        BrowserOnClick.
  1.1429 +      -->
  1.1430 +      <method name="_shouldBrowserBeRemote">
  1.1431 +        <parameter name="aURL"/>
  1.1432 +        <body>
  1.1433 +          <![CDATA[
  1.1434 +            if (!gMultiProcessBrowser)
  1.1435 +              return false;
  1.1436 +
  1.1437 +            // loadURI in browser.xml treats null as about:blank
  1.1438 +            if (!aURL)
  1.1439 +              aURL = "about:blank";
  1.1440 +
  1.1441 +            if (aURL.startsWith("about:") &&
  1.1442 +                aURL.toLowerCase() != "about:home" &&
  1.1443 +                aURL.toLowerCase() != "about:blank") {
  1.1444 +              return false;
  1.1445 +            }
  1.1446 +
  1.1447 +            if (aURL.startsWith("chrome:"))
  1.1448 +              return false;
  1.1449 +
  1.1450 +            return true;
  1.1451 +          ]]>
  1.1452 +        </body>
  1.1453 +      </method>
  1.1454 +#endif
  1.1455 +
  1.1456 +      <method name="addTab">
  1.1457 +        <parameter name="aURI"/>
  1.1458 +        <parameter name="aReferrerURI"/>
  1.1459 +        <parameter name="aCharset"/>
  1.1460 +        <parameter name="aPostData"/>
  1.1461 +        <parameter name="aOwner"/>
  1.1462 +        <parameter name="aAllowThirdPartyFixup"/>
  1.1463 +        <body>
  1.1464 +          <![CDATA[
  1.1465 +            const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  1.1466 +            var aFromExternal;
  1.1467 +            var aRelatedToCurrent;
  1.1468 +            var aSkipAnimation;
  1.1469 +            var aDisableMCB;
  1.1470 +            if (arguments.length == 2 &&
  1.1471 +                typeof arguments[1] == "object" &&
  1.1472 +                !(arguments[1] instanceof Ci.nsIURI)) {
  1.1473 +              let params = arguments[1];
  1.1474 +              aReferrerURI          = params.referrerURI;
  1.1475 +              aCharset              = params.charset;
  1.1476 +              aPostData             = params.postData;
  1.1477 +              aOwner                = params.ownerTab;
  1.1478 +              aAllowThirdPartyFixup = params.allowThirdPartyFixup;
  1.1479 +              aFromExternal         = params.fromExternal;
  1.1480 +              aRelatedToCurrent     = params.relatedToCurrent;
  1.1481 +              aSkipAnimation        = params.skipAnimation;
  1.1482 +              aDisableMCB           = params.disableMCB;
  1.1483 +            }
  1.1484 +
  1.1485 +            // if we're adding tabs, we're past interrupt mode, ditch the owner
  1.1486 +            if (this.mCurrentTab.owner)
  1.1487 +              this.mCurrentTab.owner = null;
  1.1488 +
  1.1489 +            var t = document.createElementNS(NS_XUL, "tab");
  1.1490 +
  1.1491 +            var uriIsAboutBlank = !aURI || aURI == "about:blank";
  1.1492 +
  1.1493 +            t.setAttribute("crop", "end");
  1.1494 +            t.setAttribute("onerror", "this.removeAttribute('image');");
  1.1495 +            t.className = "tabbrowser-tab";
  1.1496 +
  1.1497 +#ifdef MAKE_E10S_WORK
  1.1498 +            let remote = this._shouldBrowserBeRemote(aURI);
  1.1499 +#else
  1.1500 +            let remote = gMultiProcessBrowser;
  1.1501 +#endif
  1.1502 +            if (remote)
  1.1503 +              t.setAttribute("remote", "true");
  1.1504 +
  1.1505 +            this.tabContainer._unlockTabSizing();
  1.1506 +
  1.1507 +            // When overflowing, new tabs are scrolled into view smoothly, which
  1.1508 +            // doesn't go well together with the width transition. So we skip the
  1.1509 +            // transition in that case.
  1.1510 +            let animate = !aSkipAnimation &&
  1.1511 +                          this.tabContainer.getAttribute("overflow") != "true" &&
  1.1512 +                          Services.prefs.getBoolPref("browser.tabs.animate");
  1.1513 +            if (!animate) {
  1.1514 +              t.setAttribute("fadein", "true");
  1.1515 +              setTimeout(function (tabContainer) {
  1.1516 +                tabContainer._handleNewTab(t);
  1.1517 +              }, 0, this.tabContainer);
  1.1518 +            }
  1.1519 +
  1.1520 +            // invalidate caches
  1.1521 +            this._browsers = null;
  1.1522 +            this._visibleTabs = null;
  1.1523 +
  1.1524 +            this.tabContainer.appendChild(t);
  1.1525 +
  1.1526 +            // If this new tab is owned by another, assert that relationship
  1.1527 +            if (aOwner)
  1.1528 +              t.owner = aOwner;
  1.1529 +
  1.1530 +            var b = document.createElementNS(
  1.1531 +              "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  1.1532 +                                             "browser");
  1.1533 +            b.setAttribute("type", "content-targetable");
  1.1534 +            b.setAttribute("message", "true");
  1.1535 +            b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
  1.1536 +            b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
  1.1537 +
  1.1538 +            if (remote)
  1.1539 +              b.setAttribute("remote", "true");
  1.1540 +
  1.1541 +            if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
  1.1542 +              b.setAttribute("showresizer", "true");
  1.1543 +            }
  1.1544 +
  1.1545 +            if (this.hasAttribute("autocompletepopup"))
  1.1546 +              b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
  1.1547 +
  1.1548 +            if (this.hasAttribute("selectpopup"))
  1.1549 +              b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
  1.1550 +
  1.1551 +            b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
  1.1552 +
  1.1553 +            // Create the browserStack container
  1.1554 +            var stack = document.createElementNS(NS_XUL, "stack");
  1.1555 +            stack.className = "browserStack";
  1.1556 +            stack.appendChild(b);
  1.1557 +            stack.setAttribute("flex", "1");
  1.1558 +
  1.1559 +            // Create the browserContainer
  1.1560 +            var browserContainer = document.createElementNS(NS_XUL, "vbox");
  1.1561 +            browserContainer.className = "browserContainer";
  1.1562 +            browserContainer.appendChild(stack);
  1.1563 +            browserContainer.setAttribute("flex", "1");
  1.1564 +
  1.1565 +            // Create the sidebar container
  1.1566 +            var browserSidebarContainer = document.createElementNS(NS_XUL,
  1.1567 +                                                                   "hbox");
  1.1568 +            browserSidebarContainer.className = "browserSidebarContainer";
  1.1569 +            browserSidebarContainer.appendChild(browserContainer);
  1.1570 +            browserSidebarContainer.setAttribute("flex", "1");
  1.1571 +
  1.1572 +            // Add the Message and the Browser to the box
  1.1573 +            var notificationbox = document.createElementNS(NS_XUL,
  1.1574 +                                                           "notificationbox");
  1.1575 +            notificationbox.setAttribute("flex", "1");
  1.1576 +            notificationbox.appendChild(browserSidebarContainer);
  1.1577 +
  1.1578 +            var position = this.tabs.length - 1;
  1.1579 +            var uniqueId = this._generateUniquePanelID();
  1.1580 +            notificationbox.id = uniqueId;
  1.1581 +            t.linkedPanel = uniqueId;
  1.1582 +            t.linkedBrowser = b;
  1.1583 +            t._tPos = position;
  1.1584 +            this.tabContainer._setPositionalAttributes();
  1.1585 +
  1.1586 +            // Prevent the superfluous initial load of a blank document
  1.1587 +            // if we're going to load something other than about:blank.
  1.1588 +            if (!uriIsAboutBlank) {
  1.1589 +              b.setAttribute("nodefaultsrc", "true");
  1.1590 +            }
  1.1591 +
  1.1592 +            // NB: this appendChild call causes us to run constructors for the
  1.1593 +            // browser element, which fires off a bunch of notifications. Some
  1.1594 +            // of those notifications can cause code to run that inspects our
  1.1595 +            // state, so it is important that the tab element is fully
  1.1596 +            // initialized by this point.
  1.1597 +            this.mPanelContainer.appendChild(notificationbox);
  1.1598 +
  1.1599 +            // We've waited until the tab is in the DOM to set the label. This
  1.1600 +            // allows the TabLabelModified event to be properly dispatched.
  1.1601 +            if (!aURI || isBlankPageURL(aURI)) {
  1.1602 +              t.label = this.mStringBundle.getString("tabs.emptyTabTitle");
  1.1603 +            } else {
  1.1604 +              t.label = aURI;
  1.1605 +            }
  1.1606 +
  1.1607 +            this.tabContainer.updateVisibility();
  1.1608 +
  1.1609 +            // wire up a progress listener for the new browser object.
  1.1610 +            var tabListener = this.mTabProgressListener(t, b, uriIsAboutBlank);
  1.1611 +            const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  1.1612 +                                     .createInstance(Components.interfaces.nsIWebProgress);
  1.1613 +            filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1.1614 +            b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1.1615 +            this.mTabListeners[position] = tabListener;
  1.1616 +            this.mTabFilters[position] = filter;
  1.1617 +
  1.1618 +            b.droppedLinkHandler = handleDroppedLink;
  1.1619 +
  1.1620 +            // If we just created a new tab that loads the default
  1.1621 +            // newtab url, swap in a preloaded page if possible.
  1.1622 +            // Do nothing if we're a private window.
  1.1623 +            let docShellsSwapped = false;
  1.1624 +            if (aURI == BROWSER_NEW_TAB_URL &&
  1.1625 +                !PrivateBrowsingUtils.isWindowPrivate(window) &&
  1.1626 +                !gMultiProcessBrowser) {
  1.1627 +              docShellsSwapped = gBrowserNewTabPreloader.newTab(t);
  1.1628 +            } else if (aURI == "about:customizing") {
  1.1629 +              docShellsSwapped = gCustomizationTabPreloader.newTab(t);
  1.1630 +            }
  1.1631 +
  1.1632 +            // Dispatch a new tab notification.  We do this once we're
  1.1633 +            // entirely done, so that things are in a consistent state
  1.1634 +            // even if the event listener opens or closes tabs.
  1.1635 +            var evt = document.createEvent("Events");
  1.1636 +            evt.initEvent("TabOpen", true, false);
  1.1637 +            t.dispatchEvent(evt);
  1.1638 +
  1.1639 +            // If we didn't swap docShells with a preloaded browser
  1.1640 +            // then let's just continue loading the page normally.
  1.1641 +            if (!docShellsSwapped && !uriIsAboutBlank) {
  1.1642 +              // pretend the user typed this so it'll be available till
  1.1643 +              // the document successfully loads
  1.1644 +              if (aURI && gInitialPages.indexOf(aURI) == -1)
  1.1645 +                b.userTypedValue = aURI;
  1.1646 +
  1.1647 +              let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
  1.1648 +              if (aAllowThirdPartyFixup) {
  1.1649 +                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
  1.1650 +                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
  1.1651 +              }
  1.1652 +              if (aFromExternal)
  1.1653 +                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
  1.1654 +              if (aDisableMCB)
  1.1655 +                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
  1.1656 +              try {
  1.1657 +                b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
  1.1658 +              } catch (ex) {
  1.1659 +                Cu.reportError(ex);
  1.1660 +              }
  1.1661 +            }
  1.1662 +
  1.1663 +            // We start our browsers out as inactive, and then maintain
  1.1664 +            // activeness in the tab switcher.
  1.1665 +            b.docShellIsActive = false;
  1.1666 +
  1.1667 +            // Check if we're opening a tab related to the current tab and
  1.1668 +            // move it to after the current tab.
  1.1669 +            // aReferrerURI is null or undefined if the tab is opened from
  1.1670 +            // an external application or bookmark, i.e. somewhere other
  1.1671 +            // than the current tab.
  1.1672 +            if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
  1.1673 +                Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
  1.1674 +              let newTabPos = (this._lastRelatedTab ||
  1.1675 +                               this.selectedTab)._tPos + 1;
  1.1676 +              if (this._lastRelatedTab)
  1.1677 +                this._lastRelatedTab.owner = null;
  1.1678 +              else
  1.1679 +                t.owner = this.selectedTab;
  1.1680 +              this.moveTabTo(t, newTabPos);
  1.1681 +              this._lastRelatedTab = t;
  1.1682 +            }
  1.1683 +
  1.1684 +            if (animate) {
  1.1685 +              mozRequestAnimationFrame(function () {
  1.1686 +                this.tabContainer._handleTabTelemetryStart(t, aURI);
  1.1687 +
  1.1688 +                // kick the animation off
  1.1689 +                t.setAttribute("fadein", "true");
  1.1690 +              }.bind(this));
  1.1691 +            }
  1.1692 +
  1.1693 +            return t;
  1.1694 +          ]]>
  1.1695 +        </body>
  1.1696 +      </method>
  1.1697 +
  1.1698 +      <method name="warnAboutClosingTabs">
  1.1699 +      <parameter name="aCloseTabs"/>
  1.1700 +      <parameter name="aTab"/>
  1.1701 +      <body>
  1.1702 +        <![CDATA[
  1.1703 +          var tabsToClose;
  1.1704 +          switch (aCloseTabs) {
  1.1705 +            case this.closingTabsEnum.ALL:
  1.1706 +              tabsToClose = this.tabs.length - this._removingTabs.length -
  1.1707 +                            gBrowser._numPinnedTabs;
  1.1708 +              break;
  1.1709 +            case this.closingTabsEnum.OTHER:
  1.1710 +              tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs;
  1.1711 +              break;
  1.1712 +            case this.closingTabsEnum.TO_END:
  1.1713 +              if (!aTab)
  1.1714 +                throw new Error("Required argument missing: aTab");
  1.1715 +
  1.1716 +              tabsToClose = this.getTabsToTheEndFrom(aTab).length;
  1.1717 +              break;
  1.1718 +            default:
  1.1719 +              throw new Error("Invalid argument: " + aCloseTabs);
  1.1720 +          }
  1.1721 +
  1.1722 +          if (tabsToClose <= 1)
  1.1723 +            return true;
  1.1724 +
  1.1725 +          const pref = aCloseTabs == this.closingTabsEnum.ALL ?
  1.1726 +                       "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
  1.1727 +          var shouldPrompt = Services.prefs.getBoolPref(pref);
  1.1728 +          if (!shouldPrompt)
  1.1729 +            return true;
  1.1730 +
  1.1731 +          var ps = Services.prompt;
  1.1732 +
  1.1733 +          // default to true: if it were false, we wouldn't get this far
  1.1734 +          var warnOnClose = { value: true };
  1.1735 +          var bundle = this.mStringBundle;
  1.1736 +
  1.1737 +          // focus the window before prompting.
  1.1738 +          // this will raise any minimized window, which will
  1.1739 +          // make it obvious which window the prompt is for and will
  1.1740 +          // solve the problem of windows "obscuring" the prompt.
  1.1741 +          // see bug #350299 for more details
  1.1742 +          window.focus();
  1.1743 +          var warningMessage =
  1.1744 +            PluralForm.get(tabsToClose, bundle.getString("tabs.closeWarningMultiple"))
  1.1745 +                      .replace("#1", tabsToClose);
  1.1746 +          var buttonPressed =
  1.1747 +            ps.confirmEx(window,
  1.1748 +                         bundle.getString("tabs.closeWarningTitle"),
  1.1749 +                         warningMessage,
  1.1750 +                         (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0)
  1.1751 +                         + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
  1.1752 +                         bundle.getString("tabs.closeButtonMultiple"),
  1.1753 +                         null, null,
  1.1754 +                         aCloseTabs == this.closingTabsEnum.ALL ?
  1.1755 +                           bundle.getString("tabs.closeWarningPromptMe") : null,
  1.1756 +                         warnOnClose);
  1.1757 +          var reallyClose = (buttonPressed == 0);
  1.1758 +
  1.1759 +          // don't set the pref unless they press OK and it's false
  1.1760 +          if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
  1.1761 +            Services.prefs.setBoolPref(pref, false);
  1.1762 +
  1.1763 +          return reallyClose;
  1.1764 +        ]]>
  1.1765 +      </body>
  1.1766 +      </method>
  1.1767 +
  1.1768 +      <method name="getTabsToTheEndFrom">
  1.1769 +        <parameter name="aTab"/>
  1.1770 +        <body>
  1.1771 +          <![CDATA[
  1.1772 +            var tabsToEnd = [];
  1.1773 +            let tabs = this.visibleTabs;
  1.1774 +            for (let i = tabs.length - 1; tabs[i] != aTab && i >= 0; --i) {
  1.1775 +              tabsToEnd.push(tabs[i]);
  1.1776 +            }
  1.1777 +            return tabsToEnd.reverse();
  1.1778 +          ]]>
  1.1779 +        </body>
  1.1780 +      </method>
  1.1781 +
  1.1782 +      <method name="removeTabsToTheEndFrom">
  1.1783 +        <parameter name="aTab"/>
  1.1784 +        <body>
  1.1785 +          <![CDATA[
  1.1786 +            if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) {
  1.1787 +              let tabs = this.getTabsToTheEndFrom(aTab);
  1.1788 +              for (let i = tabs.length - 1; i >= 0; --i) {
  1.1789 +                this.removeTab(tabs[i], {animate: true});
  1.1790 +              }
  1.1791 +            }
  1.1792 +          ]]>
  1.1793 +        </body>
  1.1794 +      </method>
  1.1795 +
  1.1796 +      <method name="removeAllTabsBut">
  1.1797 +        <parameter name="aTab"/>
  1.1798 +        <body>
  1.1799 +          <![CDATA[
  1.1800 +            if (aTab.pinned)
  1.1801 +              return;
  1.1802 +
  1.1803 +            if (this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
  1.1804 +              let tabs = this.visibleTabs;
  1.1805 +              this.selectedTab = aTab;
  1.1806 +
  1.1807 +              for (let i = tabs.length - 1; i >= 0; --i) {
  1.1808 +                if (tabs[i] != aTab && !tabs[i].pinned)
  1.1809 +                  this.removeTab(tabs[i], {animate: true});
  1.1810 +              }
  1.1811 +            }
  1.1812 +          ]]>
  1.1813 +        </body>
  1.1814 +      </method>
  1.1815 +
  1.1816 +      <method name="removeCurrentTab">
  1.1817 +        <parameter name="aParams"/>
  1.1818 +        <body>
  1.1819 +          <![CDATA[
  1.1820 +            this.removeTab(this.mCurrentTab, aParams);
  1.1821 +          ]]>
  1.1822 +        </body>
  1.1823 +      </method>
  1.1824 +
  1.1825 +      <field name="_removingTabs">
  1.1826 +        []
  1.1827 +      </field>
  1.1828 +
  1.1829 +      <method name="removeTab">
  1.1830 +        <parameter name="aTab"/>
  1.1831 +        <parameter name="aParams"/>
  1.1832 +        <body>
  1.1833 +          <![CDATA[
  1.1834 +            if (aParams) {
  1.1835 +              var animate = aParams.animate;
  1.1836 +              var byMouse = aParams.byMouse;
  1.1837 +            }
  1.1838 +
  1.1839 +            // Handle requests for synchronously removing an already
  1.1840 +            // asynchronously closing tab.
  1.1841 +            if (!animate &&
  1.1842 +                aTab.closing) {
  1.1843 +              this._endRemoveTab(aTab);
  1.1844 +              return;
  1.1845 +            }
  1.1846 +
  1.1847 +            var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
  1.1848 +
  1.1849 +            if (!this._beginRemoveTab(aTab, false, null, true))
  1.1850 +              return;
  1.1851 +
  1.1852 +            if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
  1.1853 +              this.tabContainer._lockTabSizing(aTab);
  1.1854 +            else
  1.1855 +              this.tabContainer._unlockTabSizing();
  1.1856 +
  1.1857 +            if (!animate /* the caller didn't opt in */ ||
  1.1858 +                isLastTab ||
  1.1859 +                aTab.pinned ||
  1.1860 +                aTab.hidden ||
  1.1861 +                this._removingTabs.length > 3 /* don't want lots of concurrent animations */ ||
  1.1862 +                aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
  1.1863 +                window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
  1.1864 +                !Services.prefs.getBoolPref("browser.tabs.animate")) {
  1.1865 +              this._endRemoveTab(aTab);
  1.1866 +              return;
  1.1867 +            }
  1.1868 +
  1.1869 +            this.tabContainer._handleTabTelemetryStart(aTab);
  1.1870 +
  1.1871 +            this._blurTab(aTab);
  1.1872 +            aTab.style.maxWidth = ""; // ensure that fade-out transition happens
  1.1873 +            aTab.removeAttribute("fadein");
  1.1874 +
  1.1875 +            setTimeout(function (tab, tabbrowser) {
  1.1876 +              if (tab.parentNode &&
  1.1877 +                  window.getComputedStyle(tab).maxWidth == "0.1px") {
  1.1878 +                NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
  1.1879 +                tabbrowser._endRemoveTab(tab);
  1.1880 +              }
  1.1881 +            }, 3000, aTab, this);
  1.1882 +          ]]>
  1.1883 +        </body>
  1.1884 +      </method>
  1.1885 +
  1.1886 +      <!-- Tab close requests are ignored if the window is closing anyway,
  1.1887 +           e.g. when holding Ctrl+W. -->
  1.1888 +      <field name="_windowIsClosing">
  1.1889 +        false
  1.1890 +      </field>
  1.1891 +
  1.1892 +      <method name="_beginRemoveTab">
  1.1893 +        <parameter name="aTab"/>
  1.1894 +        <parameter name="aTabWillBeMoved"/>
  1.1895 +        <parameter name="aCloseWindowWithLastTab"/>
  1.1896 +        <parameter name="aCloseWindowFastpath"/>
  1.1897 +        <body>
  1.1898 +          <![CDATA[
  1.1899 +            if (aTab.closing ||
  1.1900 +                this._windowIsClosing)
  1.1901 +              return false;
  1.1902 +
  1.1903 +            var browser = this.getBrowserForTab(aTab);
  1.1904 +
  1.1905 +            if (!aTabWillBeMoved) {
  1.1906 +              let ds = browser.docShell;
  1.1907 +              if (ds &&
  1.1908 +                  ds.contentViewer &&
  1.1909 +                  !ds.contentViewer.permitUnload()) {
  1.1910 +                return false;
  1.1911 +              }
  1.1912 +            }
  1.1913 +
  1.1914 +            var closeWindow = false;
  1.1915 +            var newTab = false;
  1.1916 +            if (this.tabs.length - this._removingTabs.length == 1) {
  1.1917 +              closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
  1.1918 +                            !window.toolbar.visible ||
  1.1919 +                              Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
  1.1920 +
  1.1921 +              // Closing the tab and replacing it with a blank one is notably slower
  1.1922 +              // than closing the window right away. If the caller opts in, take
  1.1923 +              // the fast path.
  1.1924 +              if (closeWindow &&
  1.1925 +                  aCloseWindowFastpath &&
  1.1926 +                  this._removingTabs.length == 0) {
  1.1927 +                // This call actually closes the window, unless the user
  1.1928 +                // cancels the operation.  We are finished here in both cases.
  1.1929 +                this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
  1.1930 +                return null;
  1.1931 +              }
  1.1932 +
  1.1933 +              newTab = true;
  1.1934 +            }
  1.1935 +
  1.1936 +            aTab.closing = true;
  1.1937 +            this._removingTabs.push(aTab);
  1.1938 +            this._visibleTabs = null; // invalidate cache
  1.1939 +
  1.1940 +            // Invalidate hovered tab state tracking for this closing tab.
  1.1941 +            if (this.tabContainer._hoveredTab == aTab)
  1.1942 +              aTab._mouseleave();
  1.1943 +
  1.1944 +            if (newTab)
  1.1945 +              this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
  1.1946 +            else
  1.1947 +              this.tabContainer.updateVisibility();
  1.1948 +
  1.1949 +            // We're committed to closing the tab now.
  1.1950 +            // Dispatch a notification.
  1.1951 +            // We dispatch it before any teardown so that event listeners can
  1.1952 +            // inspect the tab that's about to close.
  1.1953 +            var evt = document.createEvent("UIEvent");
  1.1954 +            evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
  1.1955 +            aTab.dispatchEvent(evt);
  1.1956 +
  1.1957 +            if (!aTabWillBeMoved && !gMultiProcessBrowser) {
  1.1958 +              // Prevent this tab from showing further dialogs, since we're closing it
  1.1959 +              var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
  1.1960 +                                getInterface(Ci.nsIDOMWindowUtils);
  1.1961 +              windowUtils.disableDialogs();
  1.1962 +            }
  1.1963 +
  1.1964 +            // Remove the tab's filter and progress listener.
  1.1965 +            const filter = this.mTabFilters[aTab._tPos];
  1.1966 +
  1.1967 +            browser.webProgress.removeProgressListener(filter);
  1.1968 +
  1.1969 +            filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
  1.1970 +            this.mTabListeners[aTab._tPos].destroy();
  1.1971 +
  1.1972 +            if (browser.registeredOpenURI && !aTabWillBeMoved) {
  1.1973 +              this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
  1.1974 +              this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
  1.1975 +              delete browser.registeredOpenURI;
  1.1976 +            }
  1.1977 +
  1.1978 +            // We are no longer the primary content area.
  1.1979 +            browser.setAttribute("type", "content-targetable");
  1.1980 +
  1.1981 +            // Remove this tab as the owner of any other tabs, since it's going away.
  1.1982 +            Array.forEach(this.tabs, function (tab) {
  1.1983 +              if ("owner" in tab && tab.owner == aTab)
  1.1984 +                // |tab| is a child of the tab we're removing, make it an orphan
  1.1985 +                tab.owner = null;
  1.1986 +            });
  1.1987 +
  1.1988 +            aTab._endRemoveArgs = [closeWindow, newTab];
  1.1989 +            return true;
  1.1990 +          ]]>
  1.1991 +        </body>
  1.1992 +      </method>
  1.1993 +
  1.1994 +      <method name="_endRemoveTab">
  1.1995 +        <parameter name="aTab"/>
  1.1996 +        <body>
  1.1997 +          <![CDATA[
  1.1998 +            if (!aTab || !aTab._endRemoveArgs)
  1.1999 +              return;
  1.2000 +
  1.2001 +            var [aCloseWindow, aNewTab] = aTab._endRemoveArgs;
  1.2002 +            aTab._endRemoveArgs = null;
  1.2003 +
  1.2004 +            if (this._windowIsClosing) {
  1.2005 +              aCloseWindow = false;
  1.2006 +              aNewTab = false;
  1.2007 +            }
  1.2008 +
  1.2009 +            this._lastRelatedTab = null;
  1.2010 +
  1.2011 +            // update the UI early for responsiveness
  1.2012 +            aTab.collapsed = true;
  1.2013 +            this.tabContainer._fillTrailingGap();
  1.2014 +            this._blurTab(aTab);
  1.2015 +
  1.2016 +            this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1);
  1.2017 +
  1.2018 +            if (aCloseWindow) {
  1.2019 +              this._windowIsClosing = true;
  1.2020 +              while (this._removingTabs.length)
  1.2021 +                this._endRemoveTab(this._removingTabs[0]);
  1.2022 +            } else if (!this._windowIsClosing) {
  1.2023 +              if (aNewTab)
  1.2024 +                focusAndSelectUrlBar();
  1.2025 +
  1.2026 +              // workaround for bug 345399
  1.2027 +              this.tabContainer.mTabstrip._updateScrollButtonsDisabledState();
  1.2028 +            }
  1.2029 +
  1.2030 +            // We're going to remove the tab and the browser now.
  1.2031 +            // Clean up mTabFilters and mTabListeners now rather than in
  1.2032 +            // _beginRemoveTab, so that their size is always in sync with the
  1.2033 +            // number of tabs and browsers (the xbl destructor depends on this).
  1.2034 +            this.mTabFilters.splice(aTab._tPos, 1);
  1.2035 +            this.mTabListeners.splice(aTab._tPos, 1);
  1.2036 +
  1.2037 +            var browser = this.getBrowserForTab(aTab);
  1.2038 +
  1.2039 +            // Because of the way XBL works (fields just set JS
  1.2040 +            // properties on the element) and the code we have in place
  1.2041 +            // to preserve the JS objects for any elements that have
  1.2042 +            // JS properties set on them, the browser element won't be
  1.2043 +            // destroyed until the document goes away.  So we force a
  1.2044 +            // cleanup ourselves.
  1.2045 +            // This has to happen before we remove the child so that the
  1.2046 +            // XBL implementation of nsIObserver still works.
  1.2047 +            browser.destroy();
  1.2048 +
  1.2049 +            var wasPinned = aTab.pinned;
  1.2050 +
  1.2051 +            // Invalidate browsers cache, as the tab is removed from the
  1.2052 +            // tab container.
  1.2053 +            this._browsers = null;
  1.2054 +
  1.2055 +            // Remove the tab ...
  1.2056 +            this.tabContainer.removeChild(aTab);
  1.2057 +
  1.2058 +            // ... and fix up the _tPos properties immediately.
  1.2059 +            for (let i = aTab._tPos; i < this.tabs.length; i++)
  1.2060 +              this.tabs[i]._tPos = i;
  1.2061 +
  1.2062 +            if (!this._windowIsClosing) {
  1.2063 +              if (wasPinned)
  1.2064 +                this.tabContainer._positionPinnedTabs();
  1.2065 +
  1.2066 +              // update tab close buttons state
  1.2067 +              this.tabContainer.adjustTabstrip();
  1.2068 +
  1.2069 +              setTimeout(function(tabs) {
  1.2070 +                tabs._lastTabClosedByMouse = false;
  1.2071 +              }, 0, this.tabContainer);
  1.2072 +            }
  1.2073 +
  1.2074 +            // update tab positional properties and attributes
  1.2075 +            this.selectedTab._selected = true;
  1.2076 +            this.tabContainer._setPositionalAttributes();
  1.2077 +
  1.2078 +            // Removing the panel requires fixing up selectedPanel immediately
  1.2079 +            // (see below), which would be hindered by the potentially expensive
  1.2080 +            // browser removal. So we remove the browser and the panel in two
  1.2081 +            // steps.
  1.2082 +
  1.2083 +            var panel = this.getNotificationBox(browser);
  1.2084 +
  1.2085 +            // This will unload the document. An unload handler could remove
  1.2086 +            // dependant tabs, so it's important that the tabbrowser is now in
  1.2087 +            // a consistent state (tab removed, tab positions updated, etc.).
  1.2088 +            browser.parentNode.removeChild(browser);
  1.2089 +
  1.2090 +            // Release the browser in case something is erroneously holding a
  1.2091 +            // reference to the tab after its removal.
  1.2092 +            aTab.linkedBrowser = null;
  1.2093 +
  1.2094 +            // As the browser is removed, the removal of a dependent document can
  1.2095 +            // cause the whole window to close. So at this point, it's possible
  1.2096 +            // that the binding is destructed.
  1.2097 +            if (this.mTabBox) {
  1.2098 +              let selectedPanel = this.mTabBox.selectedPanel;
  1.2099 +
  1.2100 +              this.mPanelContainer.removeChild(panel);
  1.2101 +
  1.2102 +              // Under the hood, a selectedIndex attribute controls which panel
  1.2103 +              // is displayed. Removing a panel A which precedes the selected
  1.2104 +              // panel B makes selectedIndex point to the panel next to B. We
  1.2105 +              // need to explicitly preserve B as the selected panel.
  1.2106 +              this.mTabBox.selectedPanel = selectedPanel;
  1.2107 +            }
  1.2108 +
  1.2109 +            if (aCloseWindow)
  1.2110 +              this._windowIsClosing = closeWindow(true, window.warnAboutClosingWindow);
  1.2111 +          ]]>
  1.2112 +        </body>
  1.2113 +      </method>
  1.2114 +
  1.2115 +      <method name="_blurTab">
  1.2116 +        <parameter name="aTab"/>
  1.2117 +        <body>
  1.2118 +          <![CDATA[
  1.2119 +            if (!aTab.selected)
  1.2120 +              return;
  1.2121 +
  1.2122 +            if (aTab.owner &&
  1.2123 +                !aTab.owner.hidden &&
  1.2124 +                !aTab.owner.closing &&
  1.2125 +                Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
  1.2126 +              this.selectedTab = aTab.owner;
  1.2127 +              return;
  1.2128 +            }
  1.2129 +
  1.2130 +            // Switch to a visible tab unless there aren't any others remaining
  1.2131 +            let remainingTabs = this.visibleTabs;
  1.2132 +            let numTabs = remainingTabs.length;
  1.2133 +            if (numTabs == 0 || numTabs == 1 && remainingTabs[0] == aTab) {
  1.2134 +              remainingTabs = Array.filter(this.tabs, function(tab) {
  1.2135 +                return !tab.closing;
  1.2136 +              }, this);
  1.2137 +            }
  1.2138 +
  1.2139 +            // Try to find a remaining tab that comes after the given tab
  1.2140 +            var tab = aTab;
  1.2141 +            do {
  1.2142 +              tab = tab.nextSibling;
  1.2143 +            } while (tab && remainingTabs.indexOf(tab) == -1);
  1.2144 +
  1.2145 +            if (!tab) {
  1.2146 +              tab = aTab;
  1.2147 +
  1.2148 +              do {
  1.2149 +                tab = tab.previousSibling;
  1.2150 +              } while (tab && remainingTabs.indexOf(tab) == -1);
  1.2151 +            }
  1.2152 +
  1.2153 +            this.selectedTab = tab;
  1.2154 +          ]]>
  1.2155 +        </body>
  1.2156 +      </method>
  1.2157 +
  1.2158 +      <method name="swapNewTabWithBrowser">
  1.2159 +        <parameter name="aNewTab"/>
  1.2160 +        <parameter name="aBrowser"/>
  1.2161 +        <body>
  1.2162 +          <![CDATA[
  1.2163 +            // The browser must be standalone.
  1.2164 +            if (aBrowser.getTabBrowser())
  1.2165 +              throw Cr.NS_ERROR_INVALID_ARG;
  1.2166 +
  1.2167 +            // The tab is definitely not loading.
  1.2168 +            aNewTab.removeAttribute("busy");
  1.2169 +            if (aNewTab.selected) {
  1.2170 +              this.mIsBusy = false;
  1.2171 +            }
  1.2172 +
  1.2173 +            this._swapBrowserDocShells(aNewTab, aBrowser);
  1.2174 +
  1.2175 +            // Update the new tab's title.
  1.2176 +            this.setTabTitle(aNewTab);
  1.2177 +
  1.2178 +            if (aNewTab.selected) {
  1.2179 +              this.updateCurrentBrowser(true);
  1.2180 +            }
  1.2181 +          ]]>
  1.2182 +        </body>
  1.2183 +      </method>
  1.2184 +
  1.2185 +      <method name="swapBrowsersAndCloseOther">
  1.2186 +        <parameter name="aOurTab"/>
  1.2187 +        <parameter name="aOtherTab"/>
  1.2188 +        <body>
  1.2189 +          <![CDATA[
  1.2190 +            // Do not allow transfering a private tab to a non-private window
  1.2191 +            // and vice versa.
  1.2192 +            if (PrivateBrowsingUtils.isWindowPrivate(window) !=
  1.2193 +                PrivateBrowsingUtils.isWindowPrivate(aOtherTab.ownerDocument.defaultView))
  1.2194 +              return;
  1.2195 +
  1.2196 +            // That's gBrowser for the other window, not the tab's browser!
  1.2197 +            var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser;
  1.2198 +            var isPending = aOtherTab.hasAttribute("pending");
  1.2199 +
  1.2200 +            // First, start teardown of the other browser.  Make sure to not
  1.2201 +            // fire the beforeunload event in the process.  Close the other
  1.2202 +            // window if this was its last tab.
  1.2203 +            if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true))
  1.2204 +              return;
  1.2205 +
  1.2206 +            let ourBrowser = this.getBrowserForTab(aOurTab);
  1.2207 +            let otherBrowser = aOtherTab.linkedBrowser;
  1.2208 +
  1.2209 +            // If the other tab is pending (i.e. has not been restored, yet)
  1.2210 +            // then do not switch docShells but retrieve the other tab's state
  1.2211 +            // and apply it to our tab.
  1.2212 +            if (isPending) {
  1.2213 +              SessionStore.setTabState(aOurTab, SessionStore.getTabState(aOtherTab));
  1.2214 +
  1.2215 +              // Make sure to unregister any open URIs.
  1.2216 +              this._swapRegisteredOpenURIs(ourBrowser, otherBrowser);
  1.2217 +            } else {
  1.2218 +              // Workarounds for bug 458697
  1.2219 +              // Icon might have been set on DOMLinkAdded, don't override that.
  1.2220 +              if (!ourBrowser.mIconURL && otherBrowser.mIconURL)
  1.2221 +                this.setIcon(aOurTab, otherBrowser.mIconURL);
  1.2222 +              var isBusy = aOtherTab.hasAttribute("busy");
  1.2223 +              if (isBusy) {
  1.2224 +                aOurTab.setAttribute("busy", "true");
  1.2225 +                this._tabAttrModified(aOurTab);
  1.2226 +                if (aOurTab.selected)
  1.2227 +                  this.mIsBusy = true;
  1.2228 +              }
  1.2229 +
  1.2230 +              this._swapBrowserDocShells(aOurTab, otherBrowser);
  1.2231 +            }
  1.2232 +
  1.2233 +            // Handle findbar data (if any)
  1.2234 +            let otherFindBar = aOtherTab._findBar;
  1.2235 +            if (otherFindBar &&
  1.2236 +                otherFindBar.findMode == otherFindBar.FIND_NORMAL) {
  1.2237 +              let ourFindBar = this.getFindBar(aOurTab);
  1.2238 +              ourFindBar._findField.value = otherFindBar._findField.value;
  1.2239 +              if (!otherFindBar.hidden)
  1.2240 +                ourFindBar.onFindCommand();
  1.2241 +            }
  1.2242 +
  1.2243 +            // Finish tearing down the tab that's going away.
  1.2244 +            remoteBrowser._endRemoveTab(aOtherTab);
  1.2245 +
  1.2246 +            if (isBusy)
  1.2247 +              this.setTabTitleLoading(aOurTab);
  1.2248 +            else
  1.2249 +              this.setTabTitle(aOurTab);
  1.2250 +
  1.2251 +            // If the tab was already selected (this happpens in the scenario
  1.2252 +            // of replaceTabWithWindow), notify onLocationChange, etc.
  1.2253 +            if (aOurTab.selected)
  1.2254 +              this.updateCurrentBrowser(true);
  1.2255 +          ]]>
  1.2256 +        </body>
  1.2257 +      </method>
  1.2258 +
  1.2259 +      <method name="_swapBrowserDocShells">
  1.2260 +        <parameter name="aOurTab"/>
  1.2261 +        <parameter name="aOtherBrowser"/>
  1.2262 +        <body>
  1.2263 +          <![CDATA[
  1.2264 +            // Unhook our progress listener
  1.2265 +            let index = aOurTab._tPos;
  1.2266 +            const filter = this.mTabFilters[index];
  1.2267 +            let tabListener = this.mTabListeners[index];
  1.2268 +            let ourBrowser = this.getBrowserForTab(aOurTab);
  1.2269 +            ourBrowser.webProgress.removeProgressListener(filter);
  1.2270 +            filter.removeProgressListener(tabListener);
  1.2271 +
  1.2272 +            // Make sure to unregister any open URIs.
  1.2273 +            this._swapRegisteredOpenURIs(ourBrowser, aOtherBrowser);
  1.2274 +
  1.2275 +            // Give others a chance to swap state.
  1.2276 +            let event = new CustomEvent("SwapDocShells", {"detail": aOtherBrowser});
  1.2277 +            ourBrowser.dispatchEvent(event);
  1.2278 +
  1.2279 +            // Swap the docshells
  1.2280 +            ourBrowser.swapDocShells(aOtherBrowser);
  1.2281 +
  1.2282 +            // Restore the progress listener
  1.2283 +            this.mTabListeners[index] = tabListener =
  1.2284 +              this.mTabProgressListener(aOurTab, ourBrowser, false);
  1.2285 +
  1.2286 +            const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
  1.2287 +            filter.addProgressListener(tabListener, notifyAll);
  1.2288 +            ourBrowser.webProgress.addProgressListener(filter, notifyAll);
  1.2289 +          ]]>
  1.2290 +        </body>
  1.2291 +      </method>
  1.2292 +
  1.2293 +      <method name="_swapRegisteredOpenURIs">
  1.2294 +        <parameter name="aOurBrowser"/>
  1.2295 +        <parameter name="aOtherBrowser"/>
  1.2296 +        <body>
  1.2297 +          <![CDATA[
  1.2298 +            // If the current URI is registered as open remove it from the list.
  1.2299 +            if (aOurBrowser.registeredOpenURI) {
  1.2300 +              this._placesAutocomplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
  1.2301 +              this._unifiedComplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
  1.2302 +              delete aOurBrowser.registeredOpenURI;
  1.2303 +            }
  1.2304 +
  1.2305 +            // If the other/new URI is registered as open then copy it over.
  1.2306 +            if (aOtherBrowser.registeredOpenURI) {
  1.2307 +              aOurBrowser.registeredOpenURI = aOtherBrowser.registeredOpenURI;
  1.2308 +              delete aOtherBrowser.registeredOpenURI;
  1.2309 +            }
  1.2310 +          ]]>
  1.2311 +        </body>
  1.2312 +      </method>
  1.2313 +
  1.2314 +      <method name="reloadAllTabs">
  1.2315 +        <body>
  1.2316 +          <![CDATA[
  1.2317 +            let tabs = this.visibleTabs;
  1.2318 +            let l = tabs.length;
  1.2319 +            for (var i = 0; i < l; i++) {
  1.2320 +              try {
  1.2321 +                this.getBrowserForTab(tabs[i]).reload();
  1.2322 +              } catch (e) {
  1.2323 +                // ignore failure to reload so others will be reloaded
  1.2324 +              }
  1.2325 +            }
  1.2326 +          ]]>
  1.2327 +        </body>
  1.2328 +      </method>
  1.2329 +
  1.2330 +      <method name="reloadTab">
  1.2331 +        <parameter name="aTab"/>
  1.2332 +        <body>
  1.2333 +          <![CDATA[
  1.2334 +            this.getBrowserForTab(aTab).reload();
  1.2335 +          ]]>
  1.2336 +        </body>
  1.2337 +      </method>
  1.2338 +
  1.2339 +      <method name="addProgressListener">
  1.2340 +        <parameter name="aListener"/>
  1.2341 +        <body>
  1.2342 +          <![CDATA[
  1.2343 +            if (arguments.length != 1) {
  1.2344 +              Components.utils.reportError("gBrowser.addProgressListener was " +
  1.2345 +                                           "called with a second argument, " +
  1.2346 +                                           "which is not supported. See bug " +
  1.2347 +                                           "608628. Call stack: " + new Error().stack);
  1.2348 +            }
  1.2349 +
  1.2350 +            this.mProgressListeners.push(aListener);
  1.2351 +          ]]>
  1.2352 +        </body>
  1.2353 +      </method>
  1.2354 +
  1.2355 +      <method name="removeProgressListener">
  1.2356 +        <parameter name="aListener"/>
  1.2357 +        <body>
  1.2358 +          <![CDATA[
  1.2359 +            this.mProgressListeners =
  1.2360 +              this.mProgressListeners.filter(function (l) l != aListener);
  1.2361 +         ]]>
  1.2362 +        </body>
  1.2363 +      </method>
  1.2364 +
  1.2365 +      <method name="addTabsProgressListener">
  1.2366 +        <parameter name="aListener"/>
  1.2367 +        <body>
  1.2368 +          this.mTabsProgressListeners.push(aListener);
  1.2369 +        </body>
  1.2370 +      </method>
  1.2371 +
  1.2372 +      <method name="removeTabsProgressListener">
  1.2373 +        <parameter name="aListener"/>
  1.2374 +        <body>
  1.2375 +        <![CDATA[
  1.2376 +          this.mTabsProgressListeners =
  1.2377 +            this.mTabsProgressListeners.filter(function (l) l != aListener);
  1.2378 +        ]]>
  1.2379 +        </body>
  1.2380 +      </method>
  1.2381 +
  1.2382 +      <method name="getBrowserForTab">
  1.2383 +        <parameter name="aTab"/>
  1.2384 +        <body>
  1.2385 +        <![CDATA[
  1.2386 +          return aTab.linkedBrowser;
  1.2387 +        ]]>
  1.2388 +        </body>
  1.2389 +      </method>
  1.2390 +
  1.2391 +      <method name="showOnlyTheseTabs">
  1.2392 +        <parameter name="aTabs"/>
  1.2393 +        <body>
  1.2394 +        <![CDATA[
  1.2395 +          Array.forEach(this.tabs, function(tab) {
  1.2396 +            if (aTabs.indexOf(tab) == -1)
  1.2397 +              this.hideTab(tab);
  1.2398 +            else
  1.2399 +              this.showTab(tab);
  1.2400 +          }, this);
  1.2401 +
  1.2402 +          this.tabContainer._handleTabSelect(false);
  1.2403 +        ]]>
  1.2404 +        </body>
  1.2405 +      </method>
  1.2406 +
  1.2407 +      <method name="showTab">
  1.2408 +        <parameter name="aTab"/>
  1.2409 +        <body>
  1.2410 +        <![CDATA[
  1.2411 +          if (aTab.hidden) {
  1.2412 +            aTab.removeAttribute("hidden");
  1.2413 +            this._visibleTabs = null; // invalidate cache
  1.2414 +
  1.2415 +            this.tabContainer.adjustTabstrip();
  1.2416 +
  1.2417 +            this.tabContainer._setPositionalAttributes();
  1.2418 +
  1.2419 +            let event = document.createEvent("Events");
  1.2420 +            event.initEvent("TabShow", true, false);
  1.2421 +            aTab.dispatchEvent(event);
  1.2422 +          }
  1.2423 +        ]]>
  1.2424 +        </body>
  1.2425 +      </method>
  1.2426 +
  1.2427 +      <method name="hideTab">
  1.2428 +        <parameter name="aTab"/>
  1.2429 +        <body>
  1.2430 +        <![CDATA[
  1.2431 +          if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
  1.2432 +              !aTab.closing) {
  1.2433 +            aTab.setAttribute("hidden", "true");
  1.2434 +            this._visibleTabs = null; // invalidate cache
  1.2435 +
  1.2436 +            this.tabContainer.adjustTabstrip();
  1.2437 +
  1.2438 +            this.tabContainer._setPositionalAttributes();
  1.2439 +
  1.2440 +            let event = document.createEvent("Events");
  1.2441 +            event.initEvent("TabHide", true, false);
  1.2442 +            aTab.dispatchEvent(event);
  1.2443 +          }
  1.2444 +        ]]>
  1.2445 +        </body>
  1.2446 +      </method>
  1.2447 +
  1.2448 +      <method name="selectTabAtIndex">
  1.2449 +        <parameter name="aIndex"/>
  1.2450 +        <parameter name="aEvent"/>
  1.2451 +        <body>
  1.2452 +        <![CDATA[
  1.2453 +          let tabs = this.visibleTabs;
  1.2454 +
  1.2455 +          // count backwards for aIndex < 0
  1.2456 +          if (aIndex < 0)
  1.2457 +            aIndex += tabs.length;
  1.2458 +
  1.2459 +          if (aIndex >= 0 && aIndex < tabs.length)
  1.2460 +            this.selectedTab = tabs[aIndex];
  1.2461 +
  1.2462 +          if (aEvent) {
  1.2463 +            aEvent.preventDefault();
  1.2464 +            aEvent.stopPropagation();
  1.2465 +          }
  1.2466 +        ]]>
  1.2467 +        </body>
  1.2468 +      </method>
  1.2469 +
  1.2470 +      <property name="selectedTab">
  1.2471 +        <getter>
  1.2472 +          return this.mCurrentTab;
  1.2473 +        </getter>
  1.2474 +        <setter>
  1.2475 +          <![CDATA[
  1.2476 +          // Update the tab
  1.2477 +          this.mTabBox.selectedTab = val;
  1.2478 +          return val;
  1.2479 +          ]]>
  1.2480 +        </setter>
  1.2481 +      </property>
  1.2482 +
  1.2483 +      <property name="selectedBrowser"
  1.2484 +                onget="return this.mCurrentBrowser;"
  1.2485 +                readonly="true"/>
  1.2486 +
  1.2487 +      <property name="browsers" readonly="true">
  1.2488 +       <getter>
  1.2489 +          <![CDATA[
  1.2490 +            return this._browsers ||
  1.2491 +                   (this._browsers = Array.map(this.tabs, function (tab) tab.linkedBrowser));
  1.2492 +          ]]>
  1.2493 +        </getter>
  1.2494 +      </property>
  1.2495 +      <field name="_browsers">null</field>
  1.2496 +
  1.2497 +      <!-- Moves a tab to a new browser window, unless it's already the only tab
  1.2498 +           in the current window, in which case this will do nothing. -->
  1.2499 +      <method name="replaceTabWithWindow">
  1.2500 +        <parameter name="aTab"/>
  1.2501 +        <parameter name="aOptions"/>
  1.2502 +        <body>
  1.2503 +          <![CDATA[
  1.2504 +            if (this.tabs.length == 1)
  1.2505 +              return null;
  1.2506 +
  1.2507 +            let event = new CustomEvent("TabBecomingWindow", {
  1.2508 +              bubbles: true,
  1.2509 +              cancelable: true
  1.2510 +            });
  1.2511 +            aTab.dispatchEvent(event);
  1.2512 +            if (event.defaultPrevented) {
  1.2513 +              return null;
  1.2514 +            }
  1.2515 +
  1.2516 +            var options = "chrome,dialog=no,all";
  1.2517 +            for (var name in aOptions)
  1.2518 +              options += "," + name + "=" + aOptions[name];
  1.2519 +
  1.2520 +            // tell a new window to take the "dropped" tab
  1.2521 +            return window.openDialog(getBrowserURL(), "_blank", options, aTab);
  1.2522 +          ]]>
  1.2523 +        </body>
  1.2524 +      </method>
  1.2525 +
  1.2526 +      <method name="moveTabTo">
  1.2527 +        <parameter name="aTab"/>
  1.2528 +        <parameter name="aIndex"/>
  1.2529 +        <body>
  1.2530 +        <![CDATA[
  1.2531 +          var oldPosition = aTab._tPos;
  1.2532 +          if (oldPosition == aIndex)
  1.2533 +            return;
  1.2534 +
  1.2535 +          // Don't allow mixing pinned and unpinned tabs.
  1.2536 +          if (aTab.pinned)
  1.2537 +            aIndex = Math.min(aIndex, this._numPinnedTabs - 1);
  1.2538 +          else
  1.2539 +            aIndex = Math.max(aIndex, this._numPinnedTabs);
  1.2540 +          if (oldPosition == aIndex)
  1.2541 +            return;
  1.2542 +
  1.2543 +          this._lastRelatedTab = null;
  1.2544 +
  1.2545 +          this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
  1.2546 +          this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
  1.2547 +
  1.2548 +          let wasFocused = (document.activeElement == this.mCurrentTab);
  1.2549 +
  1.2550 +          aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
  1.2551 +          this.mCurrentTab._selected = false;
  1.2552 +
  1.2553 +          // invalidate caches
  1.2554 +          this._browsers = null;
  1.2555 +          this._visibleTabs = null;
  1.2556 +
  1.2557 +          // use .item() instead of [] because dragging to the end of the strip goes out of
  1.2558 +          // bounds: .item() returns null (so it acts like appendChild), but [] throws
  1.2559 +          this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
  1.2560 +
  1.2561 +          for (let i = 0; i < this.tabs.length; i++) {
  1.2562 +            this.tabs[i]._tPos = i;
  1.2563 +            this.tabs[i]._selected = false;
  1.2564 +          }
  1.2565 +          this.mCurrentTab._selected = true;
  1.2566 +
  1.2567 +          if (wasFocused)
  1.2568 +            this.mCurrentTab.focus();
  1.2569 +
  1.2570 +          this.tabContainer._handleTabSelect(false);
  1.2571 +
  1.2572 +          if (aTab.pinned)
  1.2573 +            this.tabContainer._positionPinnedTabs();
  1.2574 +
  1.2575 +          this.tabContainer._setPositionalAttributes();
  1.2576 +
  1.2577 +          var evt = document.createEvent("UIEvents");
  1.2578 +          evt.initUIEvent("TabMove", true, false, window, oldPosition);
  1.2579 +          aTab.dispatchEvent(evt);
  1.2580 +        ]]>
  1.2581 +        </body>
  1.2582 +      </method>
  1.2583 +
  1.2584 +      <method name="moveTabForward">
  1.2585 +        <body>
  1.2586 +          <![CDATA[
  1.2587 +            let nextTab = this.mCurrentTab.nextSibling;
  1.2588 +            while (nextTab && nextTab.hidden)
  1.2589 +              nextTab = nextTab.nextSibling;
  1.2590 +
  1.2591 +            if (nextTab)
  1.2592 +              this.moveTabTo(this.mCurrentTab, nextTab._tPos);
  1.2593 +            else if (this.arrowKeysShouldWrap)
  1.2594 +              this.moveTabToStart();
  1.2595 +          ]]>
  1.2596 +        </body>
  1.2597 +      </method>
  1.2598 +
  1.2599 +      <method name="moveTabBackward">
  1.2600 +        <body>
  1.2601 +          <![CDATA[
  1.2602 +            let previousTab = this.mCurrentTab.previousSibling;
  1.2603 +            while (previousTab && previousTab.hidden)
  1.2604 +              previousTab = previousTab.previousSibling;
  1.2605 +
  1.2606 +            if (previousTab)
  1.2607 +              this.moveTabTo(this.mCurrentTab, previousTab._tPos);
  1.2608 +            else if (this.arrowKeysShouldWrap)
  1.2609 +              this.moveTabToEnd();
  1.2610 +          ]]>
  1.2611 +        </body>
  1.2612 +      </method>
  1.2613 +
  1.2614 +      <method name="moveTabToStart">
  1.2615 +        <body>
  1.2616 +          <![CDATA[
  1.2617 +            var tabPos = this.mCurrentTab._tPos;
  1.2618 +            if (tabPos > 0)
  1.2619 +              this.moveTabTo(this.mCurrentTab, 0);
  1.2620 +          ]]>
  1.2621 +        </body>
  1.2622 +      </method>
  1.2623 +
  1.2624 +      <method name="moveTabToEnd">
  1.2625 +        <body>
  1.2626 +          <![CDATA[
  1.2627 +            var tabPos = this.mCurrentTab._tPos;
  1.2628 +            if (tabPos < this.browsers.length - 1)
  1.2629 +              this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
  1.2630 +          ]]>
  1.2631 +        </body>
  1.2632 +      </method>
  1.2633 +
  1.2634 +      <method name="moveTabOver">
  1.2635 +        <parameter name="aEvent"/>
  1.2636 +        <body>
  1.2637 +          <![CDATA[
  1.2638 +            var direction = window.getComputedStyle(this.parentNode, null).direction;
  1.2639 +            if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
  1.2640 +                (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT))
  1.2641 +              this.moveTabForward();
  1.2642 +            else
  1.2643 +              this.moveTabBackward();
  1.2644 +          ]]>
  1.2645 +        </body>
  1.2646 +      </method>
  1.2647 +
  1.2648 +      <method name="duplicateTab">
  1.2649 +        <parameter name="aTab"/><!-- can be from a different window as well -->
  1.2650 +        <body>
  1.2651 +          <![CDATA[
  1.2652 +            return SessionStore.duplicateTab(window, aTab);
  1.2653 +          ]]>
  1.2654 +        </body>
  1.2655 +      </method>
  1.2656 +
  1.2657 +      <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
  1.2658 +           MAKE SURE TO ADD IT HERE AS WELL. -->
  1.2659 +      <property name="canGoBack"
  1.2660 +                onget="return this.mCurrentBrowser.canGoBack;"
  1.2661 +                readonly="true"/>
  1.2662 +
  1.2663 +      <property name="canGoForward"
  1.2664 +                onget="return this.mCurrentBrowser.canGoForward;"
  1.2665 +                readonly="true"/>
  1.2666 +
  1.2667 +      <method name="goBack">
  1.2668 +        <body>
  1.2669 +          <![CDATA[
  1.2670 +            return this.mCurrentBrowser.goBack();
  1.2671 +          ]]>
  1.2672 +        </body>
  1.2673 +      </method>
  1.2674 +
  1.2675 +      <method name="goForward">
  1.2676 +        <body>
  1.2677 +          <![CDATA[
  1.2678 +            return this.mCurrentBrowser.goForward();
  1.2679 +          ]]>
  1.2680 +        </body>
  1.2681 +      </method>
  1.2682 +
  1.2683 +      <method name="reload">
  1.2684 +        <body>
  1.2685 +          <![CDATA[
  1.2686 +            return this.mCurrentBrowser.reload();
  1.2687 +          ]]>
  1.2688 +        </body>
  1.2689 +      </method>
  1.2690 +
  1.2691 +      <method name="reloadWithFlags">
  1.2692 +        <parameter name="aFlags"/>
  1.2693 +        <body>
  1.2694 +          <![CDATA[
  1.2695 +            return this.mCurrentBrowser.reloadWithFlags(aFlags);
  1.2696 +          ]]>
  1.2697 +        </body>
  1.2698 +      </method>
  1.2699 +
  1.2700 +      <method name="stop">
  1.2701 +        <body>
  1.2702 +          <![CDATA[
  1.2703 +            return this.mCurrentBrowser.stop();
  1.2704 +          ]]>
  1.2705 +        </body>
  1.2706 +      </method>
  1.2707 +
  1.2708 +      <!-- throws exception for unknown schemes -->
  1.2709 +      <method name="loadURI">
  1.2710 +        <parameter name="aURI"/>
  1.2711 +        <parameter name="aReferrerURI"/>
  1.2712 +        <parameter name="aCharset"/>
  1.2713 +        <body>
  1.2714 +          <![CDATA[
  1.2715 +#ifdef MAKE_E10S_WORK
  1.2716 +            this.updateBrowserRemoteness(this.mCurrentBrowser, aURI);
  1.2717 +            try {
  1.2718 +#endif
  1.2719 +            return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
  1.2720 +#ifdef MAKE_E10S_WORK
  1.2721 +            } catch (e) {
  1.2722 +              let url = this.mCurrentBrowser.currentURI.spec;
  1.2723 +              this.updateBrowserRemoteness(this.mCurrentBrowser, url);
  1.2724 +              throw e;
  1.2725 +            }
  1.2726 +#endif
  1.2727 +          ]]>
  1.2728 +        </body>
  1.2729 +      </method>
  1.2730 +
  1.2731 +      <!-- throws exception for unknown schemes -->
  1.2732 +      <method name="loadURIWithFlags">
  1.2733 +        <parameter name="aURI"/>
  1.2734 +        <parameter name="aFlags"/>
  1.2735 +        <parameter name="aReferrerURI"/>
  1.2736 +        <parameter name="aCharset"/>
  1.2737 +        <parameter name="aPostData"/>
  1.2738 +        <body>
  1.2739 +          <![CDATA[
  1.2740 +#ifdef MAKE_E10S_WORK
  1.2741 +            this.updateBrowserRemoteness(this.mCurrentBrowser, aURI);
  1.2742 +            try {
  1.2743 +#endif
  1.2744 +            return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
  1.2745 +#ifdef MAKE_E10S_WORK
  1.2746 +            } catch (e) {
  1.2747 +              let url = this.mCurrentBrowser.currentURI.spec;
  1.2748 +              this.updateBrowserRemoteness(this.mCurrentBrowser, url);
  1.2749 +              throw e;
  1.2750 +            }
  1.2751 +#endif
  1.2752 +          ]]>
  1.2753 +        </body>
  1.2754 +      </method>
  1.2755 +
  1.2756 +      <method name="goHome">
  1.2757 +        <body>
  1.2758 +          <![CDATA[
  1.2759 +            return this.mCurrentBrowser.goHome();
  1.2760 +          ]]>
  1.2761 +        </body>
  1.2762 +      </method>
  1.2763 +
  1.2764 +      <property name="homePage">
  1.2765 +        <getter>
  1.2766 +          <![CDATA[
  1.2767 +            return this.mCurrentBrowser.homePage;
  1.2768 +          ]]>
  1.2769 +        </getter>
  1.2770 +        <setter>
  1.2771 +          <![CDATA[
  1.2772 +            this.mCurrentBrowser.homePage = val;
  1.2773 +            return val;
  1.2774 +          ]]>
  1.2775 +        </setter>
  1.2776 +      </property>
  1.2777 +
  1.2778 +      <method name="gotoIndex">
  1.2779 +        <parameter name="aIndex"/>
  1.2780 +        <body>
  1.2781 +          <![CDATA[
  1.2782 +            return this.mCurrentBrowser.gotoIndex(aIndex);
  1.2783 +          ]]>
  1.2784 +        </body>
  1.2785 +      </method>
  1.2786 +
  1.2787 +      <method name="attachFormFill">
  1.2788 +        <body><![CDATA[
  1.2789 +          for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
  1.2790 +            var cb = this.getBrowserAtIndex(i);
  1.2791 +            cb.attachFormFill();
  1.2792 +          }
  1.2793 +        ]]></body>
  1.2794 +      </method>
  1.2795 +
  1.2796 +      <method name="detachFormFill">
  1.2797 +        <body><![CDATA[
  1.2798 +          for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
  1.2799 +            var cb = this.getBrowserAtIndex(i);
  1.2800 +            cb.detachFormFill();
  1.2801 +          }
  1.2802 +        ]]></body>
  1.2803 +      </method>
  1.2804 +
  1.2805 +      <property name="currentURI"
  1.2806 +                onget="return this.mCurrentBrowser.currentURI;"
  1.2807 +                readonly="true"/>
  1.2808 +
  1.2809 +      <property name="finder"
  1.2810 +                onget="return this.mCurrentBrowser.finder"
  1.2811 +                readonly="true"/>
  1.2812 +
  1.2813 +      <property name="docShell"
  1.2814 +                onget="return this.mCurrentBrowser.docShell"
  1.2815 +                readonly="true"/>
  1.2816 +
  1.2817 +      <property name="webNavigation"
  1.2818 +                onget="return this.mCurrentBrowser.webNavigation"
  1.2819 +                readonly="true"/>
  1.2820 +
  1.2821 +      <property name="webBrowserFind"
  1.2822 +                readonly="true"
  1.2823 +                onget="return this.mCurrentBrowser.webBrowserFind"/>
  1.2824 +
  1.2825 +      <property name="webProgress"
  1.2826 +                readonly="true"
  1.2827 +                onget="return this.mCurrentBrowser.webProgress"/>
  1.2828 +
  1.2829 +      <property name="contentWindow"
  1.2830 +                readonly="true"
  1.2831 +                onget="return this.mCurrentBrowser.contentWindow"/>
  1.2832 +
  1.2833 +      <property name="sessionHistory"
  1.2834 +                onget="return this.mCurrentBrowser.sessionHistory;"
  1.2835 +                readonly="true"/>
  1.2836 +
  1.2837 +      <property name="markupDocumentViewer"
  1.2838 +                onget="return this.mCurrentBrowser.markupDocumentViewer;"
  1.2839 +                readonly="true"/>
  1.2840 +
  1.2841 +      <property name="contentViewerEdit"
  1.2842 +                onget="return this.mCurrentBrowser.contentViewerEdit;"
  1.2843 +                readonly="true"/>
  1.2844 +
  1.2845 +      <property name="contentViewerFile"
  1.2846 +                onget="return this.mCurrentBrowser.contentViewerFile;"
  1.2847 +                readonly="true"/>
  1.2848 +
  1.2849 +      <property name="contentDocument"
  1.2850 +                onget="return this.mCurrentBrowser.contentDocument;"
  1.2851 +                readonly="true"/>
  1.2852 +
  1.2853 +      <property name="contentTitle"
  1.2854 +                onget="return this.mCurrentBrowser.contentTitle;"
  1.2855 +                readonly="true"/>
  1.2856 +
  1.2857 +      <property name="contentPrincipal"
  1.2858 +                onget="return this.mCurrentBrowser.contentPrincipal;"
  1.2859 +                readonly="true"/>
  1.2860 +
  1.2861 +      <property name="securityUI"
  1.2862 +                onget="return this.mCurrentBrowser.securityUI;"
  1.2863 +                readonly="true"/>
  1.2864 +
  1.2865 +      <property name="fullZoom"
  1.2866 +                onget="return this.mCurrentBrowser.fullZoom;"
  1.2867 +                onset="this.mCurrentBrowser.fullZoom = val;"/>
  1.2868 +
  1.2869 +      <property name="textZoom"
  1.2870 +                onget="return this.mCurrentBrowser.textZoom;"
  1.2871 +                onset="this.mCurrentBrowser.textZoom = val;"/>
  1.2872 +
  1.2873 +      <property name="isSyntheticDocument"
  1.2874 +                onget="return this.mCurrentBrowser.isSyntheticDocument;"
  1.2875 +                readonly="true"/>
  1.2876 +
  1.2877 +      <method name="_handleKeyEvent">
  1.2878 +        <parameter name="aEvent"/>
  1.2879 +        <body><![CDATA[
  1.2880 +          if (!aEvent.isTrusted) {
  1.2881 +            // Don't let untrusted events mess with tabs.
  1.2882 +            return;
  1.2883 +          }
  1.2884 +
  1.2885 +          if (aEvent.altKey)
  1.2886 +            return;
  1.2887 +
  1.2888 +          if (aEvent.ctrlKey && aEvent.shiftKey && !aEvent.metaKey) {
  1.2889 +            switch (aEvent.keyCode) {
  1.2890 +              case aEvent.DOM_VK_PAGE_UP:
  1.2891 +                this.moveTabBackward();
  1.2892 +                aEvent.stopPropagation();
  1.2893 +                aEvent.preventDefault();
  1.2894 +                return;
  1.2895 +              case aEvent.DOM_VK_PAGE_DOWN:
  1.2896 +                this.moveTabForward();
  1.2897 +                aEvent.stopPropagation();
  1.2898 +                aEvent.preventDefault();
  1.2899 +                return;
  1.2900 +            }
  1.2901 +          }
  1.2902 +
  1.2903 +          // We need to take care of FAYT-watching as long as the findbar
  1.2904 +          // isn't initialized.  The checks on aEvent are copied from
  1.2905 +          // _shouldFastFind (see findbar.xml).
  1.2906 +          if (!gFindBarInitialized &&
  1.2907 +              !(aEvent.ctrlKey || aEvent.metaKey) &&
  1.2908 +              !aEvent.defaultPrevented) {
  1.2909 +            let charCode = aEvent.charCode;
  1.2910 +            if (charCode) {
  1.2911 +              let char = String.fromCharCode(charCode);
  1.2912 +              if (char == "'" || char == "/" ||
  1.2913 +                  Services.prefs.getBoolPref("accessibility.typeaheadfind")) {
  1.2914 +                gFindBar._onBrowserKeypress(aEvent);
  1.2915 +                return;
  1.2916 +              }
  1.2917 +            }
  1.2918 +          }
  1.2919 +
  1.2920 +#ifdef XP_MACOSX
  1.2921 +          if (!aEvent.metaKey)
  1.2922 +            return;
  1.2923 +
  1.2924 +          var offset = 1;
  1.2925 +          switch (aEvent.charCode) {
  1.2926 +            case '}'.charCodeAt(0):
  1.2927 +              offset = -1;
  1.2928 +            case '{'.charCodeAt(0):
  1.2929 +              if (window.getComputedStyle(this, null).direction == "ltr")
  1.2930 +                offset *= -1;
  1.2931 +              this.tabContainer.advanceSelectedTab(offset, true);
  1.2932 +              aEvent.stopPropagation();
  1.2933 +              aEvent.preventDefault();
  1.2934 +          }
  1.2935 +#else
  1.2936 +          if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
  1.2937 +              aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
  1.2938 +              !this.mCurrentTab.pinned) {
  1.2939 +            this.removeCurrentTab({animate: true});
  1.2940 +            aEvent.stopPropagation();
  1.2941 +            aEvent.preventDefault();
  1.2942 +          }
  1.2943 +#endif
  1.2944 +        ]]></body>
  1.2945 +      </method>
  1.2946 +
  1.2947 +      <property name="userTypedClear"
  1.2948 +                onget="return this.mCurrentBrowser.userTypedClear;"
  1.2949 +                onset="return this.mCurrentBrowser.userTypedClear = val;"/>
  1.2950 +
  1.2951 +      <property name="userTypedValue"
  1.2952 +                onget="return this.mCurrentBrowser.userTypedValue;"
  1.2953 +                onset="return this.mCurrentBrowser.userTypedValue = val;"/>
  1.2954 +
  1.2955 +      <method name="createTooltip">
  1.2956 +        <parameter name="event"/>
  1.2957 +        <body><![CDATA[
  1.2958 +          event.stopPropagation();
  1.2959 +          var tab = document.tooltipNode;
  1.2960 +          if (tab.localName != "tab") {
  1.2961 +            event.preventDefault();
  1.2962 +            return;
  1.2963 +          }
  1.2964 +          event.target.setAttribute("label", tab.mOverCloseButton ?
  1.2965 +                                             tab.getAttribute("closetabtext") :
  1.2966 +                                             tab.getAttribute("label"));
  1.2967 +        ]]></body>
  1.2968 +      </method>
  1.2969 +
  1.2970 +      <method name="handleEvent">
  1.2971 +        <parameter name="aEvent"/>
  1.2972 +        <body><![CDATA[
  1.2973 +          switch (aEvent.type) {
  1.2974 +            case "keypress":
  1.2975 +              this._handleKeyEvent(aEvent);
  1.2976 +              break;
  1.2977 +            case "sizemodechange":
  1.2978 +              if (aEvent.target == window) {
  1.2979 +                this.mCurrentBrowser.docShellIsActive =
  1.2980 +                  (window.windowState != window.STATE_MINIMIZED);
  1.2981 +              }
  1.2982 +              break;
  1.2983 +          }
  1.2984 +        ]]></body>
  1.2985 +      </method>
  1.2986 +
  1.2987 +      <method name="receiveMessage">
  1.2988 +        <parameter name="aMessage"/>
  1.2989 +        <body><![CDATA[
  1.2990 +          let json = aMessage.json;
  1.2991 +          let browser = aMessage.target;
  1.2992 +
  1.2993 +          switch (aMessage.name) {
  1.2994 +            case "DOMTitleChanged": {
  1.2995 +              let tab = this._getTabForBrowser(browser);
  1.2996 +              if (!tab || tab.hasAttribute("pending"))
  1.2997 +                return;
  1.2998 +              let titleChanged = this.setTabTitle(tab);
  1.2999 +              if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
  1.3000 +                tab.setAttribute("titlechanged", "true");
  1.3001 +              break;
  1.3002 +            }
  1.3003 +            case "DOMWindowClose": {
  1.3004 +              if (this.tabs.length == 1) {
  1.3005 +                window.close();
  1.3006 +                return;
  1.3007 +              }
  1.3008 +
  1.3009 +              let tab = this._getTabForBrowser(browser);
  1.3010 +              if (tab) {
  1.3011 +                this.removeTab(tab);
  1.3012 +              }
  1.3013 +              break;
  1.3014 +            }
  1.3015 +            case "contextmenu": {
  1.3016 +              gContextMenuContentData = { event: aMessage.objects.event,
  1.3017 +                                          browser: browser };
  1.3018 +              let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
  1.3019 +              let event = gContextMenuContentData.event;
  1.3020 +              let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);
  1.3021 +              popup.openPopupAtScreen(pos.x, pos.y, true);
  1.3022 +              break;
  1.3023 +            }
  1.3024 +            case "DOMWebNotificationClicked": {
  1.3025 +              let tab = this._getTabForBrowser(browser);
  1.3026 +              if (!tab)
  1.3027 +                return;
  1.3028 +              this.selectedTab = tab;
  1.3029 +              window.focus();
  1.3030 +              break;
  1.3031 +            }
  1.3032 +          }
  1.3033 +        ]]></body>
  1.3034 +      </method>
  1.3035 +
  1.3036 +      <constructor>
  1.3037 +        <![CDATA[
  1.3038 +          let browserStack = document.getAnonymousElementByAttribute(this, "anonid", "browserStack");
  1.3039 +          this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
  1.3040 +
  1.3041 +          this.mCurrentTab = this.tabContainer.firstChild;
  1.3042 +          document.addEventListener("keypress", this, false);
  1.3043 +          window.addEventListener("sizemodechange", this, false);
  1.3044 +
  1.3045 +          var uniqueId = this._generateUniquePanelID();
  1.3046 +          this.mPanelContainer.childNodes[0].id = uniqueId;
  1.3047 +          this.mCurrentTab.linkedPanel = uniqueId;
  1.3048 +          this.mCurrentTab._tPos = 0;
  1.3049 +          this.mCurrentTab._fullyOpen = true;
  1.3050 +          this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
  1.3051 +
  1.3052 +          // set up the shared autoscroll popup
  1.3053 +          this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
  1.3054 +          this._autoScrollPopup.id = "autoscroller";
  1.3055 +          this.appendChild(this._autoScrollPopup);
  1.3056 +          this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
  1.3057 +          this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
  1.3058 +          this.updateWindowResizers();
  1.3059 +
  1.3060 +          // Hook up the event listeners to the first browser
  1.3061 +          var tabListener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true);
  1.3062 +          const nsIWebProgress = Components.interfaces.nsIWebProgress;
  1.3063 +          const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  1.3064 +                                   .createInstance(nsIWebProgress);
  1.3065 +          filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL);
  1.3066 +          this.mTabListeners[0] = tabListener;
  1.3067 +          this.mTabFilters[0] = filter;
  1.3068 +          this.webProgress.addProgressListener(filter, nsIWebProgress.NOTIFY_ALL);
  1.3069 +
  1.3070 +          this.style.backgroundColor =
  1.3071 +            Services.prefs.getBoolPref("browser.display.use_system_colors") ?
  1.3072 +              "-moz-default-background-color" :
  1.3073 +              Services.prefs.getCharPref("browser.display.background_color");
  1.3074 +
  1.3075 +          let remote = window.QueryInterface(Ci.nsIInterfaceRequestor)
  1.3076 +            .getInterface(Ci.nsIWebNavigation)
  1.3077 +            .QueryInterface(Ci.nsILoadContext)
  1.3078 +            .useRemoteTabs;
  1.3079 +          if (remote) {
  1.3080 +            messageManager.addMessageListener("DOMTitleChanged", this);
  1.3081 +            messageManager.addMessageListener("DOMWindowClose", this);
  1.3082 +            messageManager.addMessageListener("contextmenu", this);
  1.3083 +          }
  1.3084 +          messageManager.addMessageListener("DOMWebNotificationClicked", this);
  1.3085 +        ]]>
  1.3086 +      </constructor>
  1.3087 +
  1.3088 +      <method name="_generateUniquePanelID">
  1.3089 +        <body><![CDATA[
  1.3090 +          if (!this._uniquePanelIDCounter) {
  1.3091 +            this._uniquePanelIDCounter = 0;
  1.3092 +          }
  1.3093 +
  1.3094 +          let outerID = window.QueryInterface(Ci.nsIInterfaceRequestor)
  1.3095 +                              .getInterface(Ci.nsIDOMWindowUtils)
  1.3096 +                              .outerWindowID;
  1.3097 +
  1.3098 +          // We want panel IDs to be globally unique, that's why we include the
  1.3099 +          // window ID. We switched to a monotonic counter as Date.now() lead
  1.3100 +          // to random failures because of colliding IDs.
  1.3101 +          return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
  1.3102 +        ]]></body>
  1.3103 +      </method>
  1.3104 +
  1.3105 +      <destructor>
  1.3106 +        <![CDATA[
  1.3107 +          for (var i = 0; i < this.mTabListeners.length; ++i) {
  1.3108 +            let browser = this.getBrowserAtIndex(i);
  1.3109 +            if (browser.registeredOpenURI) {
  1.3110 +              this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
  1.3111 +              this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
  1.3112 +              delete browser.registeredOpenURI;
  1.3113 +            }
  1.3114 +            browser.webProgress.removeProgressListener(this.mTabFilters[i]);
  1.3115 +            this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
  1.3116 +            this.mTabFilters[i] = null;
  1.3117 +            this.mTabListeners[i].destroy();
  1.3118 +            this.mTabListeners[i] = null;
  1.3119 +          }
  1.3120 +          document.removeEventListener("keypress", this, false);
  1.3121 +          window.removeEventListener("sizemodechange", this, false);
  1.3122 +
  1.3123 +          if (gMultiProcessBrowser) {
  1.3124 +            messageManager.removeMessageListener("DOMTitleChanged", this);
  1.3125 +            messageManager.removeMessageListener("contextmenu", this);
  1.3126 +          }
  1.3127 +        ]]>
  1.3128 +      </destructor>
  1.3129 +
  1.3130 +      <!-- Deprecated stuff, implemented for backwards compatibility. -->
  1.3131 +      <method name="enterTabbedMode">
  1.3132 +        <body>
  1.3133 +          Application.console.log("enterTabbedMode is an obsolete method and " +
  1.3134 +                                  "will be removed in a future release.");
  1.3135 +        </body>
  1.3136 +      </method>
  1.3137 +      <field name="mTabbedMode" readonly="true">true</field>
  1.3138 +      <method name="setStripVisibilityTo">
  1.3139 +        <parameter name="aShow"/>
  1.3140 +        <body>
  1.3141 +          this.tabContainer.visible = aShow;
  1.3142 +        </body>
  1.3143 +      </method>
  1.3144 +      <method name="getStripVisibility">
  1.3145 +        <body>
  1.3146 +          return this.tabContainer.visible;
  1.3147 +        </body>
  1.3148 +      </method>
  1.3149 +      <property name="mContextTab" readonly="true"
  1.3150 +                onget="return TabContextMenu.contextTab;"/>
  1.3151 +      <property name="mPrefs" readonly="true"
  1.3152 +                onget="return Services.prefs;"/>
  1.3153 +      <property name="mTabContainer" readonly="true"
  1.3154 +                onget="return this.tabContainer;"/>
  1.3155 +      <property name="mTabs" readonly="true"
  1.3156 +                onget="return this.tabs;"/>
  1.3157 +      <!--
  1.3158 +        - Compatibility hack: several extensions depend on this property to
  1.3159 +        - access the tab context menu or tab container, so keep that working for
  1.3160 +        - now. Ideally we can remove this once extensions are using
  1.3161 +        - tabbrowser.tabContextMenu and tabbrowser.tabContainer directly.
  1.3162 +        -->
  1.3163 +      <property name="mStrip" readonly="true">
  1.3164 +        <getter>
  1.3165 +        <![CDATA[
  1.3166 +          return ({
  1.3167 +            self: this,
  1.3168 +            childNodes: [null, this.tabContextMenu, this.tabContainer],
  1.3169 +            firstChild: { nextSibling: this.tabContextMenu },
  1.3170 +            getElementsByAttribute: function (attr, attrValue) {
  1.3171 +              if (attr == "anonid" && attrValue == "tabContextMenu")
  1.3172 +                return [this.self.tabContextMenu];
  1.3173 +              return [];
  1.3174 +            },
  1.3175 +            // Also support adding event listeners (forward to the tab container)
  1.3176 +            addEventListener: function (a,b,c) { this.self.tabContainer.addEventListener(a,b,c); },
  1.3177 +            removeEventListener: function (a,b,c) { this.self.tabContainer.removeEventListener(a,b,c); }
  1.3178 +          });
  1.3179 +        ]]>
  1.3180 +        </getter>
  1.3181 +      </property>
  1.3182 +    </implementation>
  1.3183 +
  1.3184 +    <handlers>
  1.3185 +      <handler event="DOMWindowClose" phase="capturing">
  1.3186 +        <![CDATA[
  1.3187 +          if (!event.isTrusted)
  1.3188 +            return;
  1.3189 +
  1.3190 +          if (this.tabs.length == 1)
  1.3191 +            return;
  1.3192 +
  1.3193 +          var tab = this._getTabForContentWindow(event.target);
  1.3194 +          if (tab) {
  1.3195 +            this.removeTab(tab);
  1.3196 +            event.preventDefault();
  1.3197 +          }
  1.3198 +        ]]>
  1.3199 +      </handler>
  1.3200 +      <handler event="DOMWillOpenModalDialog" phase="capturing">
  1.3201 +        <![CDATA[
  1.3202 +          if (!event.isTrusted)
  1.3203 +            return;
  1.3204 +
  1.3205 +          // We're about to open a modal dialog, make sure the opening
  1.3206 +          // tab is brought to the front.
  1.3207 +          // If this is a same-process modal dialog, then we're given its DOM
  1.3208 +          // window as the event's target. For remote dialogs, we're given the
  1.3209 +          // browser, but that's in the originalTarget.
  1.3210 +          // XXX Why originalTarget for the browser?
  1.3211 +          this.selectedTab = (event.target instanceof Window) ?
  1.3212 +                               this._getTabForContentWindow(event.target.top) :
  1.3213 +                               this._getTabForBrowser(event.originalTarget);
  1.3214 +        ]]>
  1.3215 +      </handler>
  1.3216 +      <handler event="DOMTitleChanged">
  1.3217 +        <![CDATA[
  1.3218 +          if (!event.isTrusted)
  1.3219 +            return;
  1.3220 +
  1.3221 +          var contentWin = event.target.defaultView;
  1.3222 +          if (contentWin != contentWin.top)
  1.3223 +            return;
  1.3224 +
  1.3225 +          var tab = this._getTabForContentWindow(contentWin);
  1.3226 +          if (tab.hasAttribute("pending"))
  1.3227 +            return;
  1.3228 +
  1.3229 +          var titleChanged = this.setTabTitle(tab);
  1.3230 +          if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
  1.3231 +            tab.setAttribute("titlechanged", "true");
  1.3232 +        ]]>
  1.3233 +      </handler>
  1.3234 +      <handler event="oop-browser-crashed">
  1.3235 +        <![CDATA[
  1.3236 +          if (!event.isTrusted)
  1.3237 +            return;
  1.3238 +
  1.3239 +          let browser = event.originalTarget;
  1.3240 +          let title = browser.contentTitle;
  1.3241 +          let uri = browser.currentURI;
  1.3242 +          let icon = browser.mIconURL;
  1.3243 +
  1.3244 +          this.updateBrowserRemoteness(browser, "about:tabcrashed");
  1.3245 +
  1.3246 +          browser.setAttribute("crashedPageTitle", title);
  1.3247 +          browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
  1.3248 +          browser.removeAttribute("crashedPageTitle");
  1.3249 +          let tab = this._getTabForBrowser(browser);
  1.3250 +          this.setIcon(tab, icon);
  1.3251 +        ]]>
  1.3252 +      </handler>
  1.3253 +    </handlers>
  1.3254 +  </binding>
  1.3255 +
  1.3256 +  <binding id="tabbrowser-tabbox"
  1.3257 +           extends="chrome://global/content/bindings/tabbox.xml#tabbox">
  1.3258 +    <implementation>
  1.3259 +      <property name="tabs" readonly="true"
  1.3260 +                onget="return document.getBindingParent(this).tabContainer;"/>
  1.3261 +    </implementation>
  1.3262 +  </binding>
  1.3263 +
  1.3264 +  <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
  1.3265 +    <implementation>
  1.3266 +      <!-- Override scrollbox.xml method, since our scrollbox's children are
  1.3267 +           inherited from the binding parent -->
  1.3268 +      <method name="_getScrollableElements">
  1.3269 +        <body><![CDATA[
  1.3270 +          return Array.filter(document.getBindingParent(this).childNodes,
  1.3271 +                              this._canScrollToElement, this);
  1.3272 +        ]]></body>
  1.3273 +      </method>
  1.3274 +      <method name="_canScrollToElement">
  1.3275 +        <parameter name="tab"/>
  1.3276 +        <body><![CDATA[
  1.3277 +          return !tab.pinned && !tab.hidden;
  1.3278 +        ]]></body>
  1.3279 +      </method>
  1.3280 +      <field name="_tabMarginLeft">null</field>
  1.3281 +      <field name="_tabMarginRight">null</field>
  1.3282 +      <method name="_calcTabMargins">
  1.3283 +        <parameter name="aTab"/>
  1.3284 +        <body><![CDATA[
  1.3285 +          if (this._tabMarginLeft === null || this._tabMarginRight === null) {
  1.3286 +            let tabMiddle = document.getAnonymousElementByAttribute(aTab, "class", "tab-background-middle");
  1.3287 +            let tabMiddleStyle = window.getComputedStyle(tabMiddle, null);
  1.3288 +            this._tabMarginLeft = parseFloat(tabMiddleStyle.marginLeft);
  1.3289 +            this._tabMarginRight = parseFloat(tabMiddleStyle.marginRight);
  1.3290 +          }
  1.3291 +        ]]></body>
  1.3292 +      </method>
  1.3293 +      <method name="_adjustElementStartAndEnd">
  1.3294 +        <parameter name="aTab"/>
  1.3295 +        <parameter name="tabStart"/>
  1.3296 +        <parameter name="tabEnd"/>
  1.3297 +        <body><![CDATA[
  1.3298 +          this._calcTabMargins(aTab);
  1.3299 +          if (this._tabMarginLeft < 0) {
  1.3300 +            tabStart = tabStart + this._tabMarginLeft;
  1.3301 +          }
  1.3302 +          if (this._tabMarginRight < 0) {
  1.3303 +            tabEnd = tabEnd - this._tabMarginRight;
  1.3304 +          }
  1.3305 +          return [tabStart, tabEnd];
  1.3306 +        ]]></body>
  1.3307 +      </method>
  1.3308 +    </implementation>
  1.3309 +
  1.3310 +    <handlers>
  1.3311 +      <handler event="underflow" phase="capturing"><![CDATA[
  1.3312 +        if (event.detail == 0)
  1.3313 +          return; // Ignore vertical events
  1.3314 +
  1.3315 +        var tabs = document.getBindingParent(this);
  1.3316 +        tabs.removeAttribute("overflow");
  1.3317 +
  1.3318 +        if (tabs._lastTabClosedByMouse)
  1.3319 +          tabs._expandSpacerBy(this._scrollButtonDown.clientWidth);
  1.3320 +
  1.3321 +        tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab,
  1.3322 +                                              tabs.tabbrowser);
  1.3323 +
  1.3324 +        tabs._positionPinnedTabs();
  1.3325 +      ]]></handler>
  1.3326 +      <handler event="overflow"><![CDATA[
  1.3327 +        if (event.detail == 0)
  1.3328 +          return; // Ignore vertical events
  1.3329 +        var tabs = document.getBindingParent(this);
  1.3330 +        var numberOfTabs = tabs.tabbrowser.visibleTabs.length;
  1.3331 +        if (numberOfTabs == 1)
  1.3332 +          return;
  1.3333 +
  1.3334 +        tabs.setAttribute("overflow", "true");
  1.3335 +        tabs._positionPinnedTabs();
  1.3336 +        tabs._handleTabSelect(false);
  1.3337 +      ]]></handler>
  1.3338 +    </handlers>
  1.3339 +  </binding>
  1.3340 +
  1.3341 +  <binding id="tabbrowser-tabs"
  1.3342 +           extends="chrome://global/content/bindings/tabbox.xml#tabs">
  1.3343 +    <resources>
  1.3344 +      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
  1.3345 +    </resources>
  1.3346 +
  1.3347 +    <content>
  1.3348 +      <xul:hbox align="end">
  1.3349 +        <xul:image class="tab-drop-indicator" anonid="tab-drop-indicator" collapsed="true"/>
  1.3350 +      </xul:hbox>
  1.3351 +      <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
  1.3352 +                          style="min-width: 1px;"
  1.3353 +#ifndef XP_MACOSX
  1.3354 +                          clicktoscroll="true"
  1.3355 +#endif
  1.3356 +                          class="tabbrowser-arrowscrollbox">
  1.3357 +# This is a hack to circumvent bug 472020, otherwise the tabs show up on the
  1.3358 +# right of the newtab button.
  1.3359 +        <children includes="tab"/>
  1.3360 +# This is to ensure anything extensions put here will go before the newtab
  1.3361 +# button, necessary due to the previous hack.
  1.3362 +        <children/>
  1.3363 +        <xul:toolbarbutton class="tabs-newtab-button"
  1.3364 +                           command="cmd_newNavigatorTab"
  1.3365 +                           onclick="checkForMiddleClick(this, event);"
  1.3366 +                           onmouseover="document.getBindingParent(this)._enterNewTab();"
  1.3367 +                           onmouseout="document.getBindingParent(this)._leaveNewTab();"
  1.3368 +                           tooltiptext="&newTabButton.tooltip;"/>
  1.3369 +        <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
  1.3370 +                    style="width: 0;"/>
  1.3371 +      </xul:arrowscrollbox>
  1.3372 +    </content>
  1.3373 +
  1.3374 +    <implementation implements="nsIDOMEventListener">
  1.3375 +      <constructor>
  1.3376 +        <![CDATA[
  1.3377 +          this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
  1.3378 +
  1.3379 +          var tab = this.firstChild;
  1.3380 +          tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
  1.3381 +          tab.setAttribute("crop", "end");
  1.3382 +          tab.setAttribute("onerror", "this.removeAttribute('image');");
  1.3383 +
  1.3384 +          window.addEventListener("resize", this, false);
  1.3385 +          window.addEventListener("load", this, false);
  1.3386 +
  1.3387 +          try {
  1.3388 +            this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled");
  1.3389 +          } catch (ex) {
  1.3390 +            this._tabAnimationLoggingEnabled = false;
  1.3391 +          }
  1.3392 +          this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled");
  1.3393 +        ]]>
  1.3394 +      </constructor>
  1.3395 +
  1.3396 +      <field name="tabbrowser" readonly="true">
  1.3397 +        document.getElementById(this.getAttribute("tabbrowser"));
  1.3398 +      </field>
  1.3399 +
  1.3400 +      <field name="tabbox" readonly="true">
  1.3401 +        this.tabbrowser.mTabBox;
  1.3402 +      </field>
  1.3403 +
  1.3404 +      <field name="contextMenu" readonly="true">
  1.3405 +        document.getElementById("tabContextMenu");
  1.3406 +      </field>
  1.3407 +
  1.3408 +      <field name="mTabstripWidth">0</field>
  1.3409 +
  1.3410 +      <field name="mTabstrip">
  1.3411 +        document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
  1.3412 +      </field>
  1.3413 +
  1.3414 +      <field name="_firstTab">null</field>
  1.3415 +      <field name="_lastTab">null</field>
  1.3416 +      <field name="_afterSelectedTab">null</field>
  1.3417 +      <field name="_beforeHoveredTab">null</field>
  1.3418 +      <field name="_afterHoveredTab">null</field>
  1.3419 +      <field name="_hoveredTab">null</field>
  1.3420 +
  1.3421 +      <property name="_isCustomizing" readonly="true">
  1.3422 +        <getter>
  1.3423 +          let root = document.documentElement;
  1.3424 +          return root.getAttribute("customizing") == "true" ||
  1.3425 +                 root.getAttribute("customize-exiting") == "true";
  1.3426 +        </getter>
  1.3427 +      </property>
  1.3428 +
  1.3429 +      <method name="_setPositionalAttributes">
  1.3430 +        <body><![CDATA[
  1.3431 +          let visibleTabs = this.tabbrowser.visibleTabs;
  1.3432 +
  1.3433 +          if (!visibleTabs.length)
  1.3434 +            return;
  1.3435 +
  1.3436 +          let selectedIndex = visibleTabs.indexOf(this.selectedItem);
  1.3437 +
  1.3438 +          let lastVisible = visibleTabs.length - 1;
  1.3439 +
  1.3440 +          if (this._afterSelectedTab)
  1.3441 +            this._afterSelectedTab.removeAttribute("afterselected-visible");
  1.3442 +          if (this.selectedItem.closing || selectedIndex == lastVisible) {
  1.3443 +            this._afterSelectedTab = null;
  1.3444 +          } else {
  1.3445 +            this._afterSelectedTab = visibleTabs[selectedIndex + 1];
  1.3446 +            this._afterSelectedTab.setAttribute("afterselected-visible",
  1.3447 +                                                "true");
  1.3448 +          }
  1.3449 +
  1.3450 +          if (this._firstTab)
  1.3451 +            this._firstTab.removeAttribute("first-visible-tab");
  1.3452 +          this._firstTab = visibleTabs[0];
  1.3453 +          this._firstTab.setAttribute("first-visible-tab", "true");
  1.3454 +          if (this._lastTab)
  1.3455 +            this._lastTab.removeAttribute("last-visible-tab");
  1.3456 +          this._lastTab = visibleTabs[lastVisible];
  1.3457 +          this._lastTab.setAttribute("last-visible-tab", "true");
  1.3458 +
  1.3459 +          let hoveredTab = this._hoveredTab;
  1.3460 +          if (hoveredTab) {
  1.3461 +            hoveredTab._mouseleave();
  1.3462 +            hoveredTab._mouseenter();
  1.3463 +          }
  1.3464 +        ]]></body>
  1.3465 +      </method>
  1.3466 +
  1.3467 +      <field name="_blockDblClick">false</field>
  1.3468 +
  1.3469 +      <field name="_tabDropIndicator">
  1.3470 +        document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
  1.3471 +      </field>
  1.3472 +
  1.3473 +      <field name="_dragOverDelay">350</field>
  1.3474 +      <field name="_dragTime">0</field>
  1.3475 +
  1.3476 +      <field name="_container" readonly="true"><![CDATA[
  1.3477 +        this.parentNode && this.parentNode.localName == "toolbar" ? this.parentNode : this;
  1.3478 +      ]]></field>
  1.3479 +
  1.3480 +      <field name="_propagatedVisibilityOnce">false</field>
  1.3481 +
  1.3482 +      <property name="visible"
  1.3483 +                onget="return !this._container.collapsed;">
  1.3484 +        <setter><![CDATA[
  1.3485 +          if (val == this.visible &&
  1.3486 +              this._propagatedVisibilityOnce)
  1.3487 +            return val;
  1.3488 +
  1.3489 +          this._container.collapsed = !val;
  1.3490 +
  1.3491 +          this._propagateVisibility();
  1.3492 +          this._propagatedVisibilityOnce = true;
  1.3493 +
  1.3494 +          return val;
  1.3495 +        ]]></setter>
  1.3496 +      </property>
  1.3497 +
  1.3498 +      <method name="_enterNewTab">
  1.3499 +        <body><![CDATA[
  1.3500 +          let visibleTabs = this.tabbrowser.visibleTabs;
  1.3501 +          let candidate = visibleTabs[visibleTabs.length - 1];
  1.3502 +          if (!candidate.selected) {
  1.3503 +            this._beforeHoveredTab = candidate;
  1.3504 +            candidate.setAttribute("beforehovered", "true");
  1.3505 +          }
  1.3506 +        ]]></body>
  1.3507 +      </method>
  1.3508 +
  1.3509 +      <method name="_leaveNewTab">
  1.3510 +        <body><![CDATA[
  1.3511 +          if (this._beforeHoveredTab) {
  1.3512 +            this._beforeHoveredTab.removeAttribute("beforehovered");
  1.3513 +            this._beforeHoveredTab = null;
  1.3514 +          }
  1.3515 +        ]]></body>
  1.3516 +      </method>
  1.3517 +
  1.3518 +      <method name="_propagateVisibility">
  1.3519 +        <body><![CDATA[
  1.3520 +          let visible = this.visible;
  1.3521 +
  1.3522 +          document.getElementById("menu_closeWindow").hidden = !visible;
  1.3523 +          document.getElementById("menu_close").setAttribute("label",
  1.3524 +            this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
  1.3525 +
  1.3526 +          TabsInTitlebar.allowedBy("tabs-visible", visible);
  1.3527 +        ]]></body>
  1.3528 +      </method>
  1.3529 +
  1.3530 +      <method name="updateVisibility">
  1.3531 +        <body><![CDATA[
  1.3532 +          if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
  1.3533 +            this.visible = window.toolbar.visible;
  1.3534 +          else
  1.3535 +            this.visible = true;
  1.3536 +        ]]></body>
  1.3537 +      </method>
  1.3538 +
  1.3539 +      <method name="adjustTabstrip">
  1.3540 +        <body><![CDATA[
  1.3541 +          let numTabs = this.childNodes.length -
  1.3542 +                        this.tabbrowser._removingTabs.length;
  1.3543 +          if (numTabs > 2) {
  1.3544 +            // This is an optimization to avoid layout flushes by calling
  1.3545 +            // getBoundingClientRect() when we just opened a second tab. In
  1.3546 +            // this case it's highly unlikely that the tab width is smaller
  1.3547 +            // than mTabClipWidth and the tab close button obscures too much
  1.3548 +            // of the tab's label. In the edge case of the window being too
  1.3549 +            // narrow (or if tabClipWidth has been set to a way higher value),
  1.3550 +            // we'll correct the 'closebuttons' attribute after the tabopen
  1.3551 +            // animation has finished.
  1.3552 +
  1.3553 +            let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
  1.3554 +            if (tab && tab.getBoundingClientRect().width <= this.mTabClipWidth) {
  1.3555 +              this.setAttribute("closebuttons", "activetab");
  1.3556 +              return;
  1.3557 +            }
  1.3558 +          }
  1.3559 +          this.removeAttribute("closebuttons");
  1.3560 +        ]]></body>
  1.3561 +      </method>
  1.3562 +
  1.3563 +      <method name="_handleTabSelect">
  1.3564 +        <parameter name="aSmoothScroll"/>
  1.3565 +        <body><![CDATA[
  1.3566 +          if (this.getAttribute("overflow") == "true")
  1.3567 +            this.mTabstrip.ensureElementIsVisible(this.selectedItem, aSmoothScroll);
  1.3568 +        ]]></body>
  1.3569 +      </method>
  1.3570 +
  1.3571 +      <method name="_fillTrailingGap">
  1.3572 +        <body><![CDATA[
  1.3573 +          try {
  1.3574 +            // if we're at the right side (and not the logical end,
  1.3575 +            // which is why this works for both LTR and RTL)
  1.3576 +            // of the tabstrip, we need to ensure that we stay
  1.3577 +            // completely scrolled to the right side
  1.3578 +            var tabStrip = this.mTabstrip;
  1.3579 +            if (tabStrip.scrollPosition + tabStrip.scrollClientSize >
  1.3580 +                tabStrip.scrollSize)
  1.3581 +              tabStrip.scrollByPixels(-1);
  1.3582 +          } catch (e) {}
  1.3583 +        ]]></body>
  1.3584 +      </method>
  1.3585 +
  1.3586 +      <field name="_closingTabsSpacer">
  1.3587 +        document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer");
  1.3588 +      </field>
  1.3589 +
  1.3590 +      <field name="_tabDefaultMaxWidth">NaN</field>
  1.3591 +      <field name="_lastTabClosedByMouse">false</field>
  1.3592 +      <field name="_hasTabTempMaxWidth">false</field>
  1.3593 +
  1.3594 +      <!-- Try to keep the active tab's close button under the mouse cursor -->
  1.3595 +      <method name="_lockTabSizing">
  1.3596 +        <parameter name="aTab"/>
  1.3597 +        <body><![CDATA[
  1.3598 +          var tabs = this.tabbrowser.visibleTabs;
  1.3599 +          if (!tabs.length)
  1.3600 +            return;
  1.3601 +
  1.3602 +          var isEndTab = (aTab._tPos > tabs[tabs.length-1]._tPos);
  1.3603 +          var tabWidth = aTab.getBoundingClientRect().width;
  1.3604 +
  1.3605 +          if (!this._tabDefaultMaxWidth)
  1.3606 +            this._tabDefaultMaxWidth =
  1.3607 +              parseFloat(window.getComputedStyle(aTab).maxWidth);
  1.3608 +          this._lastTabClosedByMouse = true;
  1.3609 +
  1.3610 +          if (this.getAttribute("overflow") == "true") {
  1.3611 +            // Don't need to do anything if we're in overflow mode and aren't scrolled
  1.3612 +            // all the way to the right, or if we're closing the last tab.
  1.3613 +            if (isEndTab || !this.mTabstrip._scrollButtonDown.disabled)
  1.3614 +              return;
  1.3615 +
  1.3616 +            // If the tab has an owner that will become the active tab, the owner will
  1.3617 +            // be to the left of it, so we actually want the left tab to slide over.
  1.3618 +            // This can't be done as easily in non-overflow mode, so we don't bother.
  1.3619 +            if (aTab.owner)
  1.3620 +              return;
  1.3621 +
  1.3622 +            this._expandSpacerBy(tabWidth);
  1.3623 +          } else { // non-overflow mode
  1.3624 +            // Locking is neither in effect nor needed, so let tabs expand normally.
  1.3625 +            if (isEndTab && !this._hasTabTempMaxWidth)
  1.3626 +              return;
  1.3627 +
  1.3628 +            let numPinned = this.tabbrowser._numPinnedTabs;
  1.3629 +            // Force tabs to stay the same width, unless we're closing the last tab,
  1.3630 +            // which case we need to let them expand just enough so that the overall
  1.3631 +            // tabbar width is the same.
  1.3632 +            if (isEndTab) {
  1.3633 +              let numNormalTabs = tabs.length - numPinned;
  1.3634 +              tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs;
  1.3635 +              if (tabWidth > this._tabDefaultMaxWidth)
  1.3636 +                tabWidth = this._tabDefaultMaxWidth;
  1.3637 +            }
  1.3638 +            tabWidth += "px";
  1.3639 +            for (let i = numPinned; i < tabs.length; i++) {
  1.3640 +              let tab = tabs[i];
  1.3641 +              tab.style.setProperty("max-width", tabWidth, "important");
  1.3642 +              if (!isEndTab) { // keep tabs the same width
  1.3643 +                tab.style.transition = "none";
  1.3644 +                tab.clientTop; // flush styles to skip animation; see bug 649247
  1.3645 +                tab.style.transition = "";
  1.3646 +              }
  1.3647 +            }
  1.3648 +            this._hasTabTempMaxWidth = true;
  1.3649 +            this.tabbrowser.addEventListener("mousemove", this, false);
  1.3650 +            window.addEventListener("mouseout", this, false);
  1.3651 +          }
  1.3652 +        ]]></body>
  1.3653 +      </method>
  1.3654 +
  1.3655 +      <method name="_expandSpacerBy">
  1.3656 +        <parameter name="pixels"/>
  1.3657 +        <body><![CDATA[
  1.3658 +          let spacer = this._closingTabsSpacer;
  1.3659 +          spacer.style.width = parseFloat(spacer.style.width) + pixels + "px";
  1.3660 +          this.setAttribute("using-closing-tabs-spacer", "true");
  1.3661 +          this.tabbrowser.addEventListener("mousemove", this, false);
  1.3662 +          window.addEventListener("mouseout", this, false);
  1.3663 +        ]]></body>
  1.3664 +      </method>
  1.3665 +
  1.3666 +      <method name="_unlockTabSizing">
  1.3667 +        <body><![CDATA[
  1.3668 +          this.tabbrowser.removeEventListener("mousemove", this, false);
  1.3669 +          window.removeEventListener("mouseout", this, false);
  1.3670 +
  1.3671 +          if (this._hasTabTempMaxWidth) {
  1.3672 +            this._hasTabTempMaxWidth = false;
  1.3673 +            let tabs = this.tabbrowser.visibleTabs;
  1.3674 +            for (let i = 0; i < tabs.length; i++)
  1.3675 +              tabs[i].style.maxWidth = "";
  1.3676 +          }
  1.3677 +
  1.3678 +          if (this.hasAttribute("using-closing-tabs-spacer")) {
  1.3679 +            this.removeAttribute("using-closing-tabs-spacer");
  1.3680 +            this._closingTabsSpacer.style.width = 0;
  1.3681 +          }
  1.3682 +        ]]></body>
  1.3683 +      </method>
  1.3684 +
  1.3685 +      <field name="_lastNumPinned">0</field>
  1.3686 +      <method name="_positionPinnedTabs">
  1.3687 +        <body><![CDATA[
  1.3688 +          var numPinned = this.tabbrowser._numPinnedTabs;
  1.3689 +          var doPosition = this.getAttribute("overflow") == "true" &&
  1.3690 +                           numPinned > 0;
  1.3691 +
  1.3692 +          if (doPosition) {
  1.3693 +            this.setAttribute("positionpinnedtabs", "true");
  1.3694 +
  1.3695 +            let scrollButtonWidth = this.mTabstrip._scrollButtonDown.getBoundingClientRect().width;
  1.3696 +            let paddingStart = this.mTabstrip.scrollboxPaddingStart;
  1.3697 +            let width = 0;
  1.3698 +
  1.3699 +            for (let i = numPinned - 1; i >= 0; i--) {
  1.3700 +              let tab = this.childNodes[i];
  1.3701 +              width += tab.getBoundingClientRect().width;
  1.3702 +              tab.style.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px";
  1.3703 +            }
  1.3704 +
  1.3705 +            this.style.MozPaddingStart = width + paddingStart + "px";
  1.3706 +
  1.3707 +          } else {
  1.3708 +            this.removeAttribute("positionpinnedtabs");
  1.3709 +
  1.3710 +            for (let i = 0; i < numPinned; i++) {
  1.3711 +              let tab = this.childNodes[i];
  1.3712 +              tab.style.MozMarginStart = "";
  1.3713 +            }
  1.3714 +
  1.3715 +            this.style.MozPaddingStart = "";
  1.3716 +          }
  1.3717 +
  1.3718 +          if (this._lastNumPinned != numPinned) {
  1.3719 +            this._lastNumPinned = numPinned;
  1.3720 +            this._handleTabSelect(false);
  1.3721 +          }
  1.3722 +        ]]></body>
  1.3723 +      </method>
  1.3724 +
  1.3725 +      <method name="_animateTabMove">
  1.3726 +        <parameter name="event"/>
  1.3727 +        <body><![CDATA[
  1.3728 +          let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
  1.3729 +
  1.3730 +          if (this.getAttribute("movingtab") != "true") {
  1.3731 +            this.setAttribute("movingtab", "true");
  1.3732 +            this.selectedItem = draggedTab;
  1.3733 +          }
  1.3734 +
  1.3735 +          if (!("animLastScreenX" in draggedTab._dragData))
  1.3736 +            draggedTab._dragData.animLastScreenX = draggedTab._dragData.screenX;
  1.3737 +
  1.3738 +          let screenX = event.screenX;
  1.3739 +          if (screenX == draggedTab._dragData.animLastScreenX)
  1.3740 +            return;
  1.3741 +
  1.3742 +          let draggingRight = screenX > draggedTab._dragData.animLastScreenX;
  1.3743 +          draggedTab._dragData.animLastScreenX = screenX;
  1.3744 +
  1.3745 +          let rtl = (window.getComputedStyle(this).direction == "rtl");
  1.3746 +          let pinned = draggedTab.pinned;
  1.3747 +          let numPinned = this.tabbrowser._numPinnedTabs;
  1.3748 +          let tabs = this.tabbrowser.visibleTabs
  1.3749 +                                    .slice(pinned ? 0 : numPinned,
  1.3750 +                                           pinned ? numPinned : undefined);
  1.3751 +          if (rtl)
  1.3752 +            tabs.reverse();
  1.3753 +          let tabWidth = draggedTab.getBoundingClientRect().width;
  1.3754 +
  1.3755 +          // Move the dragged tab based on the mouse position.
  1.3756 +
  1.3757 +          let leftTab = tabs[0];
  1.3758 +          let rightTab = tabs[tabs.length - 1];
  1.3759 +          let tabScreenX = draggedTab.boxObject.screenX;
  1.3760 +          let translateX = screenX - draggedTab._dragData.screenX;
  1.3761 +          if (!pinned)
  1.3762 +            translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX;
  1.3763 +          let leftBound = leftTab.boxObject.screenX - tabScreenX;
  1.3764 +          let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
  1.3765 +                           (tabScreenX + tabWidth);
  1.3766 +          translateX = Math.max(translateX, leftBound);
  1.3767 +          translateX = Math.min(translateX, rightBound);
  1.3768 +          draggedTab.style.transform = "translateX(" + translateX + "px)";
  1.3769 +
  1.3770 +          // Determine what tab we're dragging over.
  1.3771 +          // * Point of reference is the center of the dragged tab. If that
  1.3772 +          //   point touches a background tab, the dragged tab would take that
  1.3773 +          //   tab's position when dropped.
  1.3774 +          // * We're doing a binary search in order to reduce the amount of
  1.3775 +          //   tabs we need to check.
  1.3776 +
  1.3777 +          let tabCenter = tabScreenX + translateX + tabWidth / 2;
  1.3778 +          let newIndex = -1;
  1.3779 +          let oldIndex = "animDropIndex" in draggedTab._dragData ?
  1.3780 +                         draggedTab._dragData.animDropIndex : draggedTab._tPos;
  1.3781 +          let low = 0;
  1.3782 +          let high = tabs.length - 1;
  1.3783 +          while (low <= high) {
  1.3784 +            let mid = Math.floor((low + high) / 2);
  1.3785 +            if (tabs[mid] == draggedTab &&
  1.3786 +                ++mid > high)
  1.3787 +              break;
  1.3788 +            let boxObject = tabs[mid].boxObject;
  1.3789 +            let screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
  1.3790 +            if (screenX > tabCenter) {
  1.3791 +              high = mid - 1;
  1.3792 +            } else if (screenX + boxObject.width < tabCenter) {
  1.3793 +              low = mid + 1;
  1.3794 +            } else {
  1.3795 +              newIndex = tabs[mid]._tPos;
  1.3796 +              break;
  1.3797 +            }
  1.3798 +          }
  1.3799 +          if (newIndex >= oldIndex)
  1.3800 +            newIndex++;
  1.3801 +          if (newIndex < 0 || newIndex == oldIndex)
  1.3802 +            return;
  1.3803 +          draggedTab._dragData.animDropIndex = newIndex;
  1.3804 +
  1.3805 +          // Shift background tabs to leave a gap where the dragged tab
  1.3806 +          // would currently be dropped.
  1.3807 +
  1.3808 +          for (let tab of tabs) {
  1.3809 +            if (tab != draggedTab) {
  1.3810 +              let shift = getTabShift(tab, newIndex);
  1.3811 +              tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
  1.3812 +            }
  1.3813 +          }
  1.3814 +
  1.3815 +          function getTabShift(tab, dropIndex) {
  1.3816 +            if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
  1.3817 +              return rtl ? -tabWidth : tabWidth;
  1.3818 +            if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
  1.3819 +              return rtl ? tabWidth : -tabWidth;
  1.3820 +            return 0;
  1.3821 +          }
  1.3822 +        ]]></body>
  1.3823 +      </method>
  1.3824 +
  1.3825 +      <method name="_finishAnimateTabMove">
  1.3826 +        <body><![CDATA[
  1.3827 +          if (this.getAttribute("movingtab") != "true")
  1.3828 +            return;
  1.3829 +
  1.3830 +          for (let tab of this.tabbrowser.visibleTabs)
  1.3831 +            tab.style.transform = "";
  1.3832 +
  1.3833 +          this.removeAttribute("movingtab");
  1.3834 +
  1.3835 +          this._handleTabSelect();
  1.3836 +        ]]></body>
  1.3837 +      </method>
  1.3838 +
  1.3839 +      <method name="handleEvent">
  1.3840 +        <parameter name="aEvent"/>
  1.3841 +        <body><![CDATA[
  1.3842 +          switch (aEvent.type) {
  1.3843 +            case "load":
  1.3844 +              this.updateVisibility();
  1.3845 +              break;
  1.3846 +            case "resize":
  1.3847 +              if (aEvent.target != window)
  1.3848 +                break;
  1.3849 +
  1.3850 +              TabsInTitlebar.updateAppearance();
  1.3851 +
  1.3852 +              if (this.tabbrowser.visibleTabs.length > 1) {
  1.3853 +                var width = this.mTabstrip.boxObject.width;
  1.3854 +                if (width != this.mTabstripWidth) {
  1.3855 +                  this.adjustTabstrip();
  1.3856 +                  this._fillTrailingGap();
  1.3857 +                  this._handleTabSelect();
  1.3858 +                  this.mTabstripWidth = width;
  1.3859 +                }
  1.3860 +              }
  1.3861 +
  1.3862 +              this.tabbrowser.updateWindowResizers();
  1.3863 +              break;
  1.3864 +            case "mouseout":
  1.3865 +              // If the "related target" (the node to which the pointer went) is not
  1.3866 +              // a child of the current document, the mouse just left the window.
  1.3867 +              let relatedTarget = aEvent.relatedTarget;
  1.3868 +              if (relatedTarget && relatedTarget.ownerDocument == document)
  1.3869 +                break;
  1.3870 +            case "mousemove":
  1.3871 +              if (document.getElementById("tabContextMenu").state != "open")
  1.3872 +                this._unlockTabSizing();
  1.3873 +              break;
  1.3874 +          }
  1.3875 +        ]]></body>
  1.3876 +      </method>
  1.3877 +
  1.3878 +      <field name="_animateElement">
  1.3879 +        this.mTabstrip._scrollButtonDown;
  1.3880 +      </field>
  1.3881 +
  1.3882 +      <method name="_notifyBackgroundTab">
  1.3883 +        <parameter name="aTab"/>
  1.3884 +        <body><![CDATA[
  1.3885 +          if (aTab.pinned)
  1.3886 +            return;
  1.3887 +
  1.3888 +          var scrollRect = this.mTabstrip.scrollClientRect;
  1.3889 +          var tab = aTab.getBoundingClientRect();
  1.3890 +          this.mTabstrip._calcTabMargins(aTab);
  1.3891 +
  1.3892 +          // DOMRect left/right properties are immutable.
  1.3893 +          tab = {left: tab.left, right: tab.right};
  1.3894 +
  1.3895 +          // Is the new tab already completely visible?
  1.3896 +          if (scrollRect.left <= tab.left && tab.right <= scrollRect.right)
  1.3897 +            return;
  1.3898 +
  1.3899 +          if (this.mTabstrip.smoothScroll) {
  1.3900 +            let selected = !this.selectedItem.pinned &&
  1.3901 +                           this.selectedItem.getBoundingClientRect();
  1.3902 +            if (selected) {
  1.3903 +              selected = {left: selected.left, right: selected.right};
  1.3904 +              // Need to take in to account the width of the left/right margins on tabs.
  1.3905 +              selected.left = selected.left + this.mTabstrip._tabMarginLeft;
  1.3906 +              selected.right = selected.right - this.mTabstrip._tabMarginRight;
  1.3907 +            }
  1.3908 +
  1.3909 +            tab.left += this.mTabstrip._tabMarginLeft;
  1.3910 +            tab.right -= this.mTabstrip._tabMarginRight;
  1.3911 +
  1.3912 +            // Can we make both the new tab and the selected tab completely visible?
  1.3913 +            if (!selected ||
  1.3914 +                Math.max(tab.right - selected.left, selected.right - tab.left) <=
  1.3915 +                  scrollRect.width) {
  1.3916 +              this.mTabstrip.ensureElementIsVisible(aTab);
  1.3917 +              return;
  1.3918 +            }
  1.3919 +
  1.3920 +            this.mTabstrip._smoothScrollByPixels(this.mTabstrip._isRTLScrollbox ?
  1.3921 +                                                 selected.right - scrollRect.right :
  1.3922 +                                                 selected.left - scrollRect.left);
  1.3923 +          }
  1.3924 +
  1.3925 +          if (!this._animateElement.hasAttribute("notifybgtab")) {
  1.3926 +            this._animateElement.setAttribute("notifybgtab", "true");
  1.3927 +            setTimeout(function (ele) {
  1.3928 +              ele.removeAttribute("notifybgtab");
  1.3929 +            }, 150, this._animateElement);
  1.3930 +          }
  1.3931 +        ]]></body>
  1.3932 +      </method>
  1.3933 +
  1.3934 +      <method name="_getDragTargetTab">
  1.3935 +        <parameter name="event"/>
  1.3936 +        <body><![CDATA[
  1.3937 +          let tab = event.target.localName == "tab" ? event.target : null;
  1.3938 +          if (tab &&
  1.3939 +              (event.type == "drop" || event.type == "dragover") &&
  1.3940 +              event.dataTransfer.dropEffect == "link") {
  1.3941 +            let boxObject = tab.boxObject;
  1.3942 +            if (event.screenX < boxObject.screenX + boxObject.width * .25 ||
  1.3943 +                event.screenX > boxObject.screenX + boxObject.width * .75)
  1.3944 +              return null;
  1.3945 +          }
  1.3946 +          return tab;
  1.3947 +        ]]></body>
  1.3948 +      </method>
  1.3949 +
  1.3950 +      <method name="_getDropIndex">
  1.3951 +        <parameter name="event"/>
  1.3952 +        <body><![CDATA[
  1.3953 +          var tabs = this.childNodes;
  1.3954 +          var tab = this._getDragTargetTab(event);
  1.3955 +          if (window.getComputedStyle(this, null).direction == "ltr") {
  1.3956 +            for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
  1.3957 +              if (event.screenX < tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
  1.3958 +                return i;
  1.3959 +          } else {
  1.3960 +            for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
  1.3961 +              if (event.screenX > tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
  1.3962 +                return i;
  1.3963 +          }
  1.3964 +          return tabs.length;
  1.3965 +        ]]></body>
  1.3966 +      </method>
  1.3967 +
  1.3968 +      <method name="_setEffectAllowedForDataTransfer">
  1.3969 +        <parameter name="event"/>
  1.3970 +        <body><![CDATA[
  1.3971 +          var dt = event.dataTransfer;
  1.3972 +          // Disallow dropping multiple items
  1.3973 +          if (dt.mozItemCount > 1)
  1.3974 +            return dt.effectAllowed = "none";
  1.3975 +
  1.3976 +          var types = dt.mozTypesAt(0);
  1.3977 +          var sourceNode = null;
  1.3978 +          // tabs are always added as the first type
  1.3979 +          if (types[0] == TAB_DROP_TYPE) {
  1.3980 +            var sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
  1.3981 +            if (sourceNode instanceof XULElement &&
  1.3982 +                sourceNode.localName == "tab" &&
  1.3983 +                sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
  1.3984 +                sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" &&
  1.3985 +                sourceNode.ownerDocument.defaultView.gBrowser.tabContainer == sourceNode.parentNode) {
  1.3986 +              // Do not allow transfering a private tab to a non-private window
  1.3987 +              // and vice versa.
  1.3988 +              if (PrivateBrowsingUtils.isWindowPrivate(window) !=
  1.3989 +                  PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerDocument.defaultView))
  1.3990 +                return dt.effectAllowed = "none";
  1.3991 +
  1.3992 +              if (window.gMultiProcessBrowser !=
  1.3993 +                  sourceNode.ownerDocument.defaultView.gMultiProcessBrowser)
  1.3994 +                return dt.effectAllowed = "none";
  1.3995 +
  1.3996 +#ifdef XP_MACOSX
  1.3997 +              return dt.effectAllowed = event.altKey ? "copy" : "move";
  1.3998 +#else
  1.3999 +              return dt.effectAllowed = event.ctrlKey ? "copy" : "move";
  1.4000 +#endif
  1.4001 +            }
  1.4002 +          }
  1.4003 +
  1.4004 +          if (browserDragAndDrop.canDropLink(event)) {
  1.4005 +            // Here we need to do this manually
  1.4006 +            return dt.effectAllowed = dt.dropEffect = "link";
  1.4007 +          }
  1.4008 +          return dt.effectAllowed = "none";
  1.4009 +        ]]></body>
  1.4010 +      </method>
  1.4011 +
  1.4012 +      <method name="_handleNewTab">
  1.4013 +        <parameter name="tab"/>
  1.4014 +        <body><![CDATA[
  1.4015 +          if (tab.parentNode != this)
  1.4016 +            return;
  1.4017 +          tab._fullyOpen = true;
  1.4018 +
  1.4019 +          this.adjustTabstrip();
  1.4020 +
  1.4021 +          if (tab.getAttribute("selected") == "true") {
  1.4022 +            this._fillTrailingGap();
  1.4023 +            this._handleTabSelect();
  1.4024 +          } else {
  1.4025 +            this._notifyBackgroundTab(tab);
  1.4026 +          }
  1.4027 +
  1.4028 +          // XXXmano: this is a temporary workaround for bug 345399
  1.4029 +          // We need to manually update the scroll buttons disabled state
  1.4030 +          // if a tab was inserted to the overflow area or removed from it
  1.4031 +          // without any scrolling and when the tabbar has already
  1.4032 +          // overflowed.
  1.4033 +          this.mTabstrip._updateScrollButtonsDisabledState();
  1.4034 +        ]]></body>
  1.4035 +      </method>
  1.4036 +
  1.4037 +      <method name="_canAdvanceToTab">
  1.4038 +        <parameter name="aTab"/>
  1.4039 +        <body>
  1.4040 +        <![CDATA[
  1.4041 +          return !aTab.closing;
  1.4042 +        ]]>
  1.4043 +        </body>
  1.4044 +      </method>
  1.4045 +
  1.4046 +      <method name="_handleTabTelemetryStart">
  1.4047 +        <parameter name="aTab"/>
  1.4048 +        <parameter name="aURI"/>
  1.4049 +        <body>
  1.4050 +        <![CDATA[
  1.4051 +          // Animation-smoothness telemetry/logging
  1.4052 +          if (Services.telemetry.canRecord || this._tabAnimationLoggingEnabled) {
  1.4053 +            if (aURI == "about:newtab" && (aTab._tPos == 1 || aTab._tPos == 2)) {
  1.4054 +              // Indicate newtab page animation where other tabs are unaffected
  1.4055 +              // (for which case, the 2nd or 3rd tabs are good representatives, even if not absolute)
  1.4056 +              aTab._recordingTabOpenPlain = true;
  1.4057 +            }
  1.4058 +            aTab._recordingHandle = window.QueryInterface(Ci.nsIInterfaceRequestor)
  1.4059 +                                          .getInterface(Ci.nsIDOMWindowUtils)
  1.4060 +                                          .startFrameTimeRecording();
  1.4061 +          }
  1.4062 +
  1.4063 +          // Overall animation duration
  1.4064 +          aTab._animStartTime = Date.now();
  1.4065 +        ]]>
  1.4066 +        </body>
  1.4067 +      </method>
  1.4068 +
  1.4069 +      <method name="_handleTabTelemetryEnd">
  1.4070 +        <parameter name="aTab"/>
  1.4071 +        <body>
  1.4072 +        <![CDATA[
  1.4073 +          if (!aTab._animStartTime) {
  1.4074 +            return;
  1.4075 +          }
  1.4076 +
  1.4077 +          Services.telemetry.getHistogramById(aTab.closing ?
  1.4078 +                                              "FX_TAB_ANIM_CLOSE_MS" :
  1.4079 +                                              "FX_TAB_ANIM_OPEN_MS")
  1.4080 +                            .add(Date.now() - aTab._animStartTime);
  1.4081 +          aTab._animStartTime = 0;
  1.4082 +
  1.4083 +          // Handle tab animation smoothness telemetry/logging of frame intervals and paint times
  1.4084 +          if (!("_recordingHandle" in aTab)) {
  1.4085 +            return;
  1.4086 +          }
  1.4087 +
  1.4088 +          let intervals = window.QueryInterface(Ci.nsIInterfaceRequestor)
  1.4089 +                                .getInterface(Ci.nsIDOMWindowUtils)
  1.4090 +                                .stopFrameTimeRecording(aTab._recordingHandle);
  1.4091 +          delete aTab._recordingHandle;
  1.4092 +          let frameCount = intervals.length;
  1.4093 +
  1.4094 +          if (this._tabAnimationLoggingEnabled) {
  1.4095 +            let msg = "Tab " + (aTab.closing ? "close" : "open") + " (Frame-interval):\n";
  1.4096 +            for (let i = 0; i < frameCount; i++) {
  1.4097 +              msg += Math.round(intervals[i]) + "\n";
  1.4098 +            }
  1.4099 +            Services.console.logStringMessage(msg);
  1.4100 +          }
  1.4101 +
  1.4102 +          // For telemetry, the first frame interval is not useful since it may represent an interval
  1.4103 +          // to a relatively old frame (prior to recording start). So we'll ignore it for the average.
  1.4104 +          if (frameCount > 1) {
  1.4105 +            let averageInterval = 0;
  1.4106 +            for (let i = 1; i < frameCount; i++) {
  1.4107 +              averageInterval += intervals[i];
  1.4108 +            };
  1.4109 +            averageInterval = averageInterval / (frameCount - 1);
  1.4110 +
  1.4111 +            Services.telemetry.getHistogramById("FX_TAB_ANIM_ANY_FRAME_INTERVAL_MS").add(averageInterval);
  1.4112 +
  1.4113 +            if (aTab._recordingTabOpenPlain) {
  1.4114 +              delete aTab._recordingTabOpenPlain;
  1.4115 +              // While we do have a telemetry probe NEWTAB_PAGE_ENABLED to monitor newtab preview, it'll be
  1.4116 +              // easier to overview the data without slicing by it. Hence the additional histograms with _PREVIEW.
  1.4117 +              let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : "";
  1.4118 +              Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval);
  1.4119 +            }
  1.4120 +          }
  1.4121 +        ]]>
  1.4122 +        </body>
  1.4123 +      </method>
  1.4124 +
  1.4125 +      <!-- Deprecated stuff, implemented for backwards compatibility. -->
  1.4126 +      <property name="mTabstripClosebutton" readonly="true"
  1.4127 +                onget="return document.getElementById('tabs-closebutton');"/>
  1.4128 +      <property name="mAllTabsPopup" readonly="true"
  1.4129 +                onget="return document.getElementById('alltabs-popup');"/>
  1.4130 +    </implementation>
  1.4131 +
  1.4132 +    <handlers>
  1.4133 +      <handler event="TabSelect" action="this._handleTabSelect();"/>
  1.4134 +
  1.4135 +      <handler event="transitionend"><![CDATA[
  1.4136 +        if (event.propertyName != "max-width")
  1.4137 +          return;
  1.4138 +
  1.4139 +        var tab = event.target;
  1.4140 +
  1.4141 +        this._handleTabTelemetryEnd(tab);
  1.4142 +
  1.4143 +        if (tab.getAttribute("fadein") == "true") {
  1.4144 +          if (tab._fullyOpen)
  1.4145 +            this.adjustTabstrip();
  1.4146 +          else
  1.4147 +            this._handleNewTab(tab);
  1.4148 +        } else if (tab.closing) {
  1.4149 +          this.tabbrowser._endRemoveTab(tab);
  1.4150 +        }
  1.4151 +      ]]></handler>
  1.4152 +
  1.4153 +      <handler event="dblclick"><![CDATA[
  1.4154 +#ifndef XP_MACOSX
  1.4155 +        // When the tabbar has an unified appearance with the titlebar
  1.4156 +        // and menubar, a double-click in it should have the same behavior
  1.4157 +        // as double-clicking the titlebar
  1.4158 +        if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
  1.4159 +          return;
  1.4160 +#endif
  1.4161 +
  1.4162 +        if (event.button != 0 ||
  1.4163 +            event.originalTarget.localName != "box")
  1.4164 +          return;
  1.4165 +
  1.4166 +        // See hack note in the tabbrowser-close-tab-button binding
  1.4167 +        if (!this._blockDblClick)
  1.4168 +          BrowserOpenTab();
  1.4169 +
  1.4170 +        event.preventDefault();
  1.4171 +      ]]></handler>
  1.4172 +
  1.4173 +      <handler event="click" button="0" phase="capturing"><![CDATA[
  1.4174 +        /* Catches extra clicks meant for the in-tab close button.
  1.4175 +         * Placed here to avoid leaking (a temporary handler added from the
  1.4176 +         * in-tab close button binding would close over the tab and leak it
  1.4177 +         * until the handler itself was removed). (bug 897751)
  1.4178 +         *
  1.4179 +         * The only sequence in which a second click event (i.e. dblclik)
  1.4180 +         * can be dispatched on an in-tab close button is when it is shown
  1.4181 +         * after the first click (i.e. the first click event was dispatched
  1.4182 +         * on the tab). This happens when we show the close button only on
  1.4183 +         * the active tab. (bug 352021)
  1.4184 +         * The only sequence in which a third click event can be dispatched
  1.4185 +         * on an in-tab close button is when the tab was opened with a
  1.4186 +         * double click on the tabbar. (bug 378344)
  1.4187 +         * In both cases, it is most likely that the close button area has
  1.4188 +         * been accidentally clicked, therefore we do not close the tab.
  1.4189 +         *
  1.4190 +         * We don't want to ignore processing of more than one click event,
  1.4191 +         * though, since the user might actually be repeatedly clicking to
  1.4192 +         * close many tabs at once.
  1.4193 +         */
  1.4194 +        let target = event.originalTarget;
  1.4195 +        if (target.classList.contains('tab-close-button')) {
  1.4196 +          // We preemptively set this to allow the closing-multiple-tabs-
  1.4197 +          // in-a-row case.
  1.4198 +          if (this._blockDblClick) {
  1.4199 +            target._ignoredCloseButtonClicks = true;
  1.4200 +          } else if (event.detail > 1 && !target._ignoredCloseButtonClicks) {
  1.4201 +            target._ignoredCloseButtonClicks = true;
  1.4202 +            event.stopPropagation();
  1.4203 +            return;
  1.4204 +          } else {
  1.4205 +            // Reset the "ignored click" flag
  1.4206 +            target._ignoredCloseButtonClicks = false;
  1.4207 +          }
  1.4208 +        }
  1.4209 +
  1.4210 +        /* Protects from close-tab-button errant doubleclick:
  1.4211 +         * Since we're removing the event target, if the user
  1.4212 +         * double-clicks the button, the dblclick event will be dispatched
  1.4213 +         * with the tabbar as its event target (and explicit/originalTarget),
  1.4214 +         * which treats that as a mouse gesture for opening a new tab.
  1.4215 +         * In this context, we're manually blocking the dblclick event
  1.4216 +         * (see tabbrowser-close-tab-button dblclick handler).
  1.4217 +         */
  1.4218 +        if (this._blockDblClick) {
  1.4219 +          if (!("_clickedTabBarOnce" in this)) {
  1.4220 +            this._clickedTabBarOnce = true;
  1.4221 +            return;
  1.4222 +          }
  1.4223 +          delete this._clickedTabBarOnce;
  1.4224 +          this._blockDblClick = false;
  1.4225 +        }
  1.4226 +      ]]></handler>
  1.4227 +
  1.4228 +      <handler event="click"><![CDATA[
  1.4229 +        if (event.button != 1)
  1.4230 +          return;
  1.4231 +
  1.4232 +        if (event.target.localName == "tab") {
  1.4233 +          this.tabbrowser.removeTab(event.target, {animate: true, byMouse: true});
  1.4234 +        } else if (event.originalTarget.localName == "box") {
  1.4235 +          BrowserOpenTab();
  1.4236 +        } else {
  1.4237 +          return;
  1.4238 +        }
  1.4239 +
  1.4240 +        event.stopPropagation();
  1.4241 +      ]]></handler>
  1.4242 +
  1.4243 +      <handler event="keypress"><![CDATA[
  1.4244 +        if (event.altKey || event.shiftKey ||
  1.4245 +#ifdef XP_MACOSX
  1.4246 +            !event.metaKey)
  1.4247 +#else
  1.4248 +            !event.ctrlKey || event.metaKey)
  1.4249 +#endif
  1.4250 +          return;
  1.4251 +
  1.4252 +        switch (event.keyCode) {
  1.4253 +          case KeyEvent.DOM_VK_UP:
  1.4254 +            this.tabbrowser.moveTabBackward();
  1.4255 +            break;
  1.4256 +          case KeyEvent.DOM_VK_DOWN:
  1.4257 +            this.tabbrowser.moveTabForward();
  1.4258 +            break;
  1.4259 +          case KeyEvent.DOM_VK_RIGHT:
  1.4260 +          case KeyEvent.DOM_VK_LEFT:
  1.4261 +            this.tabbrowser.moveTabOver(event);
  1.4262 +            break;
  1.4263 +          case KeyEvent.DOM_VK_HOME:
  1.4264 +            this.tabbrowser.moveTabToStart();
  1.4265 +            break;
  1.4266 +          case KeyEvent.DOM_VK_END:
  1.4267 +            this.tabbrowser.moveTabToEnd();
  1.4268 +            break;
  1.4269 +          default:
  1.4270 +            // Stop the keypress event for the above keyboard
  1.4271 +            // shortcuts only.
  1.4272 +            return;
  1.4273 +        }
  1.4274 +        event.stopPropagation();
  1.4275 +        event.preventDefault();
  1.4276 +      ]]></handler>
  1.4277 +
  1.4278 +      <handler event="dragstart"><![CDATA[
  1.4279 +        var tab = this._getDragTargetTab(event);
  1.4280 +        if (!tab || this._isCustomizing)
  1.4281 +          return;
  1.4282 +
  1.4283 +        let dt = event.dataTransfer;
  1.4284 +        dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0);
  1.4285 +        let browser = tab.linkedBrowser;
  1.4286 +
  1.4287 +        // We must not set text/x-moz-url or text/plain data here,
  1.4288 +        // otherwise trying to deatch the tab by dropping it on the desktop
  1.4289 +        // may result in an "internet shortcut"
  1.4290 +        dt.mozSetDataAt("text/x-moz-text-internal", browser.currentURI.spec, 0);
  1.4291 +
  1.4292 +        // Set the cursor to an arrow during tab drags.
  1.4293 +        dt.mozCursor = "default";
  1.4294 +
  1.4295 +        // Create a canvas to which we capture the current tab.
  1.4296 +        // Until canvas is HiDPI-aware (bug 780362), we need to scale the desired
  1.4297 +        // canvas size (in CSS pixels) to the window's backing resolution in order
  1.4298 +        // to get a full-resolution drag image for use on HiDPI displays.
  1.4299 +        let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
  1.4300 +        let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
  1.4301 +        let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
  1.4302 +        canvas.mozOpaque = true;
  1.4303 +        canvas.width = 160 * scale;
  1.4304 +        canvas.height = 90 * scale;
  1.4305 +        PageThumbs.captureToCanvas(browser.contentWindow, canvas);
  1.4306 +        dt.setDragImage(canvas, -16 * scale, -16 * scale);
  1.4307 +
  1.4308 +        // _dragData.offsetX/Y give the coordinates that the mouse should be
  1.4309 +        // positioned relative to the corner of the new window created upon
  1.4310 +        // dragend such that the mouse appears to have the same position
  1.4311 +        // relative to the corner of the dragged tab.
  1.4312 +        function clientX(ele) ele.getBoundingClientRect().left;
  1.4313 +        let tabOffsetX = clientX(tab) - clientX(this);
  1.4314 +        tab._dragData = {
  1.4315 +          offsetX: event.screenX - window.screenX - tabOffsetX,
  1.4316 +          offsetY: event.screenY - window.screenY,
  1.4317 +          scrollX: this.mTabstrip.scrollPosition,
  1.4318 +          screenX: event.screenX
  1.4319 +        };
  1.4320 +
  1.4321 +        event.stopPropagation();
  1.4322 +      ]]></handler>
  1.4323 +
  1.4324 +      <handler event="dragover"><![CDATA[
  1.4325 +        var effects = this._setEffectAllowedForDataTransfer(event);
  1.4326 +
  1.4327 +        var ind = this._tabDropIndicator;
  1.4328 +        if (effects == "" || effects == "none") {
  1.4329 +          ind.collapsed = true;
  1.4330 +          return;
  1.4331 +        }
  1.4332 +        event.preventDefault();
  1.4333 +        event.stopPropagation();
  1.4334 +
  1.4335 +        var tabStrip = this.mTabstrip;
  1.4336 +        var ltr = (window.getComputedStyle(this, null).direction == "ltr");
  1.4337 +
  1.4338 +        // autoscroll the tab strip if we drag over the scroll
  1.4339 +        // buttons, even if we aren't dragging a tab, but then
  1.4340 +        // return to avoid drawing the drop indicator
  1.4341 +        var pixelsToScroll = 0;
  1.4342 +        if (this.getAttribute("overflow") == "true") {
  1.4343 +          var targetAnonid = event.originalTarget.getAttribute("anonid");
  1.4344 +          switch (targetAnonid) {
  1.4345 +            case "scrollbutton-up":
  1.4346 +              pixelsToScroll = tabStrip.scrollIncrement * -1;
  1.4347 +              break;
  1.4348 +            case "scrollbutton-down":
  1.4349 +              pixelsToScroll = tabStrip.scrollIncrement;
  1.4350 +              break;
  1.4351 +          }
  1.4352 +          if (pixelsToScroll)
  1.4353 +            tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
  1.4354 +        }
  1.4355 +
  1.4356 +        if (effects == "move" &&
  1.4357 +            this == event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0).parentNode) {
  1.4358 +          ind.collapsed = true;
  1.4359 +          this._animateTabMove(event);
  1.4360 +          return;
  1.4361 +        }
  1.4362 +
  1.4363 +        this._finishAnimateTabMove();
  1.4364 +
  1.4365 +        if (effects == "link") {
  1.4366 +          let tab = this._getDragTargetTab(event);
  1.4367 +          if (tab) {
  1.4368 +            if (!this._dragTime)
  1.4369 +              this._dragTime = Date.now();
  1.4370 +            if (Date.now() >= this._dragTime + this._dragOverDelay)
  1.4371 +              this.selectedItem = tab;
  1.4372 +            ind.collapsed = true;
  1.4373 +            return;
  1.4374 +          }
  1.4375 +        }
  1.4376 +
  1.4377 +        var rect = tabStrip.getBoundingClientRect();
  1.4378 +        var newMargin;
  1.4379 +        if (pixelsToScroll) {
  1.4380 +          // if we are scrolling, put the drop indicator at the edge
  1.4381 +          // so that it doesn't jump while scrolling
  1.4382 +          let scrollRect = tabStrip.scrollClientRect;
  1.4383 +          let minMargin = scrollRect.left - rect.left;
  1.4384 +          let maxMargin = Math.min(minMargin + scrollRect.width,
  1.4385 +                                   scrollRect.right);
  1.4386 +          if (!ltr)
  1.4387 +            [minMargin, maxMargin] = [this.clientWidth - maxMargin,
  1.4388 +                                      this.clientWidth - minMargin];
  1.4389 +          newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
  1.4390 +        }
  1.4391 +        else {
  1.4392 +          let newIndex = this._getDropIndex(event);
  1.4393 +          if (newIndex == this.childNodes.length) {
  1.4394 +            let tabRect = this.childNodes[newIndex-1].getBoundingClientRect();
  1.4395 +            if (ltr)
  1.4396 +              newMargin = tabRect.right - rect.left;
  1.4397 +            else
  1.4398 +              newMargin = rect.right - tabRect.left;
  1.4399 +          }
  1.4400 +          else {
  1.4401 +            let tabRect = this.childNodes[newIndex].getBoundingClientRect();
  1.4402 +            if (ltr)
  1.4403 +              newMargin = tabRect.left - rect.left;
  1.4404 +            else
  1.4405 +              newMargin = rect.right - tabRect.right;
  1.4406 +          }
  1.4407 +        }
  1.4408 +
  1.4409 +        ind.collapsed = false;
  1.4410 +
  1.4411 +        newMargin += ind.clientWidth / 2;
  1.4412 +        if (!ltr)
  1.4413 +          newMargin *= -1;
  1.4414 +
  1.4415 +        ind.style.transform = "translate(" + Math.round(newMargin) + "px)";
  1.4416 +        ind.style.MozMarginStart = (-ind.clientWidth) + "px";
  1.4417 +      ]]></handler>
  1.4418 +
  1.4419 +      <handler event="drop"><![CDATA[
  1.4420 +        var dt = event.dataTransfer;
  1.4421 +        var dropEffect = dt.dropEffect;
  1.4422 +        var draggedTab;
  1.4423 +        if (dropEffect != "link") { // copy or move
  1.4424 +          draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
  1.4425 +          // not our drop then
  1.4426 +          if (!draggedTab)
  1.4427 +            return;
  1.4428 +        }
  1.4429 +
  1.4430 +        this._tabDropIndicator.collapsed = true;
  1.4431 +        event.stopPropagation();
  1.4432 +        if (draggedTab && dropEffect == "copy") {
  1.4433 +          // copy the dropped tab (wherever it's from)
  1.4434 +          let newIndex = this._getDropIndex(event);
  1.4435 +          let newTab = this.tabbrowser.duplicateTab(draggedTab);
  1.4436 +          this.tabbrowser.moveTabTo(newTab, newIndex);
  1.4437 +          if (draggedTab.parentNode != this || event.shiftKey)
  1.4438 +            this.selectedItem = newTab;
  1.4439 +        } else if (draggedTab && draggedTab.parentNode == this) {
  1.4440 +          this._finishAnimateTabMove();
  1.4441 +
  1.4442 +          // actually move the dragged tab
  1.4443 +          if ("animDropIndex" in draggedTab._dragData) {
  1.4444 +            let newIndex = draggedTab._dragData.animDropIndex;
  1.4445 +            if (newIndex > draggedTab._tPos)
  1.4446 +              newIndex--;
  1.4447 +            this.tabbrowser.moveTabTo(draggedTab, newIndex);
  1.4448 +          }
  1.4449 +        } else if (draggedTab) {
  1.4450 +          // swap the dropped tab with a new one we create and then close
  1.4451 +          // it in the other window (making it seem to have moved between
  1.4452 +          // windows)
  1.4453 +          let newIndex = this._getDropIndex(event);
  1.4454 +          let newTab = this.tabbrowser.addTab("about:blank");
  1.4455 +          let newBrowser = this.tabbrowser.getBrowserForTab(newTab);
  1.4456 +          // Stop the about:blank load
  1.4457 +          newBrowser.stop();
  1.4458 +          // make sure it has a docshell
  1.4459 +          newBrowser.docShell;
  1.4460 +
  1.4461 +          let numPinned = this.tabbrowser._numPinnedTabs;
  1.4462 +          if (newIndex < numPinned || draggedTab.pinned && newIndex == numPinned)
  1.4463 +            this.tabbrowser.pinTab(newTab);
  1.4464 +          this.tabbrowser.moveTabTo(newTab, newIndex);
  1.4465 +
  1.4466 +          // We need to select the tab before calling swapBrowsersAndCloseOther
  1.4467 +          // so that window.content in chrome windows points to the right tab
  1.4468 +          // when pagehide/show events are fired.
  1.4469 +          this.tabbrowser.selectedTab = newTab;
  1.4470 +
  1.4471 +          draggedTab.parentNode._finishAnimateTabMove();
  1.4472 +          this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab);
  1.4473 +
  1.4474 +          // Call updateCurrentBrowser to make sure the URL bar is up to date
  1.4475 +          // for our new tab after we've done swapBrowsersAndCloseOther.
  1.4476 +          this.tabbrowser.updateCurrentBrowser(true);
  1.4477 +        } else {
  1.4478 +          // Pass true to disallow dropping javascript: or data: urls
  1.4479 +          let url;
  1.4480 +          try {
  1.4481 +            url = browserDragAndDrop.drop(event, { }, true);
  1.4482 +          } catch (ex) {}
  1.4483 +
  1.4484 +          if (!url)
  1.4485 +            return;
  1.4486 +
  1.4487 +          let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
  1.4488 +
  1.4489 +          if (event.shiftKey)
  1.4490 +            bgLoad = !bgLoad;
  1.4491 +
  1.4492 +          let tab = this._getDragTargetTab(event);
  1.4493 +          if (!tab || dropEffect == "copy") {
  1.4494 +            // We're adding a new tab.
  1.4495 +            let newIndex = this._getDropIndex(event);
  1.4496 +            let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad, allowThirdPartyFixup: true});
  1.4497 +            this.tabbrowser.moveTabTo(newTab, newIndex);
  1.4498 +          } else {
  1.4499 +            // Load in an existing tab.
  1.4500 +            try {
  1.4501 +              let webNav = Ci.nsIWebNavigation;
  1.4502 +              let flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
  1.4503 +                          webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
  1.4504 +              this.tabbrowser.getBrowserForTab(tab).loadURIWithFlags(url, flags);
  1.4505 +              if (!bgLoad)
  1.4506 +                this.selectedItem = tab;
  1.4507 +            } catch(ex) {
  1.4508 +              // Just ignore invalid urls
  1.4509 +            }
  1.4510 +          }
  1.4511 +        }
  1.4512 +
  1.4513 +        if (draggedTab) {
  1.4514 +          delete draggedTab._dragData;
  1.4515 +        }
  1.4516 +      ]]></handler>
  1.4517 +
  1.4518 +      <handler event="dragend"><![CDATA[
  1.4519 +        // Note: while this case is correctly handled here, this event
  1.4520 +        // isn't dispatched when the tab is moved within the tabstrip,
  1.4521 +        // see bug 460801.
  1.4522 +
  1.4523 +        this._finishAnimateTabMove();
  1.4524 +
  1.4525 +        var dt = event.dataTransfer;
  1.4526 +        var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
  1.4527 +        if (dt.mozUserCancelled || dt.dropEffect != "none" || this._isCustomizing) {
  1.4528 +          delete draggedTab._dragData;
  1.4529 +          return;
  1.4530 +        }
  1.4531 +
  1.4532 +        // Disable detach within the browser toolbox
  1.4533 +        var eX = event.screenX;
  1.4534 +        var eY = event.screenY;
  1.4535 +        var wX = window.screenX;
  1.4536 +        // check if the drop point is horizontally within the window
  1.4537 +        if (eX > wX && eX < (wX + window.outerWidth)) {
  1.4538 +          let bo = this.mTabstrip.boxObject;
  1.4539 +          // also avoid detaching if the the tab was dropped too close to
  1.4540 +          // the tabbar (half a tab)
  1.4541 +          let endScreenY = bo.screenY + 1.5 * bo.height;
  1.4542 +          if (eY < endScreenY && eY > window.screenY)
  1.4543 +            return;
  1.4544 +        }
  1.4545 +
  1.4546 +        // screen.availLeft et. al. only check the screen that this window is on,
  1.4547 +        // but we want to look at the screen the tab is being dropped onto.
  1.4548 +        var sX = {}, sY = {}, sWidth = {}, sHeight = {};
  1.4549 +        Cc["@mozilla.org/gfx/screenmanager;1"]
  1.4550 +          .getService(Ci.nsIScreenManager)
  1.4551 +          .screenForRect(eX, eY, 1, 1)
  1.4552 +          .GetAvailRect(sX, sY, sWidth, sHeight);
  1.4553 +        // ensure new window entirely within screen
  1.4554 +        var winWidth = Math.min(window.outerWidth, sWidth.value);
  1.4555 +        var winHeight = Math.min(window.outerHeight, sHeight.value);
  1.4556 +        var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value),
  1.4557 +                            sX.value + sWidth.value - winWidth);
  1.4558 +        var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value),
  1.4559 +                           sY.value + sHeight.value - winHeight);
  1.4560 +
  1.4561 +        delete draggedTab._dragData;
  1.4562 +
  1.4563 +        if (this.tabbrowser.tabs.length == 1) {
  1.4564 +          // resize _before_ move to ensure the window fits the new screen.  if
  1.4565 +          // the window is too large for its screen, the window manager may do
  1.4566 +          // automatic repositioning.
  1.4567 +          window.resizeTo(winWidth, winHeight);
  1.4568 +          window.moveTo(left, top);
  1.4569 +          window.focus();
  1.4570 +        } else {
  1.4571 +          this.tabbrowser.replaceTabWithWindow(draggedTab, { screenX: left,
  1.4572 +                                                             screenY: top,
  1.4573 +#ifndef XP_WIN
  1.4574 +                                                             outerWidth: winWidth,
  1.4575 +                                                             outerHeight: winHeight
  1.4576 +#endif
  1.4577 +                                                             });
  1.4578 +        }
  1.4579 +        event.stopPropagation();
  1.4580 +      ]]></handler>
  1.4581 +
  1.4582 +      <handler event="dragexit"><![CDATA[
  1.4583 +        this._dragTime = 0;
  1.4584 +
  1.4585 +        // This does not work at all (see bug 458613)
  1.4586 +        var target = event.relatedTarget;
  1.4587 +        while (target && target != this)
  1.4588 +          target = target.parentNode;
  1.4589 +        if (target)
  1.4590 +          return;
  1.4591 +
  1.4592 +        this._tabDropIndicator.collapsed = true;
  1.4593 +        event.stopPropagation();
  1.4594 +      ]]></handler>
  1.4595 +    </handlers>
  1.4596 +  </binding>
  1.4597 +
  1.4598 +  <!-- close-tab-button binding
  1.4599 +       This binding relies on the structure of the tabbrowser binding.
  1.4600 +       Therefore it should only be used as a child of the tab or the tabs
  1.4601 +       element (in both cases, when they are anonymous nodes of <tabbrowser>).
  1.4602 +  -->
  1.4603 +  <binding id="tabbrowser-close-tab-button"
  1.4604 +           extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image">
  1.4605 +    <handlers>
  1.4606 +      <handler event="click" button="0"><![CDATA[
  1.4607 +        var bindingParent = document.getBindingParent(this);
  1.4608 +        var tabContainer = bindingParent.parentNode;
  1.4609 +        tabContainer.tabbrowser.removeTab(bindingParent, {animate: true, byMouse: true});
  1.4610 +        // This enables double-click protection for the tab container
  1.4611 +        // (see tabbrowser-tabs 'click' handler).
  1.4612 +        tabContainer._blockDblClick = true;
  1.4613 +      ]]></handler>
  1.4614 +
  1.4615 +      <handler event="dblclick" button="0" phase="capturing">
  1.4616 +        // for the one-close-button case
  1.4617 +        event.stopPropagation();
  1.4618 +      </handler>
  1.4619 +
  1.4620 +      <handler event="dragstart">
  1.4621 +        event.stopPropagation();
  1.4622 +      </handler>
  1.4623 +    </handlers>
  1.4624 +  </binding>
  1.4625 +
  1.4626 +  <binding id="tabbrowser-tab" display="xul:hbox"
  1.4627 +           extends="chrome://global/content/bindings/tabbox.xml#tab">
  1.4628 +    <resources>
  1.4629 +      <stylesheet src="chrome://browser/content/tabbrowser.css"/>
  1.4630 +    </resources>
  1.4631 +
  1.4632 +    <content context="tabContextMenu" closetabtext="&closeTab.label;">
  1.4633 +      <xul:stack class="tab-stack" flex="1">
  1.4634 +        <xul:hbox xbl:inherits="pinned,selected,titlechanged,fadein"
  1.4635 +                  class="tab-background">
  1.4636 +          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
  1.4637 +                    class="tab-background-start"/>
  1.4638 +          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
  1.4639 +                    class="tab-background-middle"/>
  1.4640 +          <xul:hbox xbl:inherits="pinned,selected,titlechanged"
  1.4641 +                    class="tab-background-end"/>
  1.4642 +        </xul:hbox>
  1.4643 +        <xul:hbox xbl:inherits="pinned,selected,titlechanged"
  1.4644 +                  class="tab-content" align="center">
  1.4645 +          <xul:image xbl:inherits="fadein,pinned,busy,progress,selected"
  1.4646 +                     class="tab-throbber"
  1.4647 +                     role="presentation"
  1.4648 +                     layer="true" />
  1.4649 +          <xul:image xbl:inherits="src=image,fadein,pinned,selected"
  1.4650 +                     anonid="tab-icon-image"
  1.4651 +                     class="tab-icon-image"
  1.4652 +                     validate="never"
  1.4653 +                     role="presentation"/>
  1.4654 +          <xul:label flex="1"
  1.4655 +                     anonid="tab-label"
  1.4656 +                     xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected"
  1.4657 +                     class="tab-text tab-label"
  1.4658 +                     role="presentation"/>
  1.4659 +          <xul:toolbarbutton anonid="close-button"
  1.4660 +                             xbl:inherits="fadein,pinned,selected"
  1.4661 +                             class="tab-close-button close-icon"/>
  1.4662 +        </xul:hbox>
  1.4663 +      </xul:stack>
  1.4664 +    </content>
  1.4665 +
  1.4666 +    <implementation>
  1.4667 +      <property name="label">
  1.4668 +        <getter>
  1.4669 +          return this.getAttribute("label");
  1.4670 +        </getter>
  1.4671 +        <setter>
  1.4672 +          this.setAttribute("label", val);
  1.4673 +          let event = new CustomEvent("TabLabelModified", {
  1.4674 +            bubbles: true,
  1.4675 +            cancelable: true
  1.4676 +          });
  1.4677 +          this.dispatchEvent(event);
  1.4678 +
  1.4679 +          // Let listeners prevent synchronizing the actual label to the
  1.4680 +          // visible label (allowing them to override the visible label).
  1.4681 +          if (!event.defaultPrevented)
  1.4682 +            this.visibleLabel = val;
  1.4683 +        </setter>
  1.4684 +      </property>
  1.4685 +      <property name="visibleLabel">
  1.4686 +        <getter>
  1.4687 +          return this.getAttribute("visibleLabel");
  1.4688 +        </getter>
  1.4689 +        <setter>
  1.4690 +          this.setAttribute("visibleLabel", val);
  1.4691 +        </setter>
  1.4692 +      </property>
  1.4693 +      <property name="pinned" readonly="true">
  1.4694 +        <getter>
  1.4695 +          return this.getAttribute("pinned") == "true";
  1.4696 +        </getter>
  1.4697 +      </property>
  1.4698 +      <property name="hidden" readonly="true">
  1.4699 +        <getter>
  1.4700 +          return this.getAttribute("hidden") == "true";
  1.4701 +        </getter>
  1.4702 +      </property>
  1.4703 +
  1.4704 +      <property name="lastAccessed">
  1.4705 +        <getter>
  1.4706 +          return this.selected ? Date.now() : this._lastAccessed;
  1.4707 +        </getter>
  1.4708 +        <setter>
  1.4709 +          this._lastAccessed = val;
  1.4710 +        </setter>
  1.4711 +      </property>
  1.4712 +      <field name="_lastAccessed">0</field>
  1.4713 +
  1.4714 +      <field name="mOverCloseButton">false</field>
  1.4715 +      <field name="mCorrespondingMenuitem">null</field>
  1.4716 +      <field name="closing">false</field>
  1.4717 +
  1.4718 +      <method name="_mouseenter">
  1.4719 +        <body><![CDATA[
  1.4720 +          if (this.hidden || this.closing)
  1.4721 +            return;
  1.4722 +
  1.4723 +          let tabContainer = this.parentNode;
  1.4724 +          let visibleTabs = tabContainer.tabbrowser.visibleTabs;
  1.4725 +          let tabIndex = visibleTabs.indexOf(this);
  1.4726 +          if (tabIndex == 0) {
  1.4727 +            tabContainer._beforeHoveredTab = null;
  1.4728 +          } else {
  1.4729 +            let candidate = visibleTabs[tabIndex - 1];
  1.4730 +            if (!candidate.selected) {
  1.4731 +              tabContainer._beforeHoveredTab = candidate;
  1.4732 +              candidate.setAttribute("beforehovered", "true");
  1.4733 +            }
  1.4734 +          }
  1.4735 +
  1.4736 +          if (tabIndex == visibleTabs.length - 1) {
  1.4737 +            tabContainer._afterHoveredTab = null;
  1.4738 +          } else {
  1.4739 +            let candidate = visibleTabs[tabIndex + 1];
  1.4740 +            if (!candidate.selected) {
  1.4741 +              tabContainer._afterHoveredTab = candidate;
  1.4742 +              candidate.setAttribute("afterhovered", "true");
  1.4743 +            }
  1.4744 +          }
  1.4745 +
  1.4746 +          tabContainer._hoveredTab = this;
  1.4747 +        ]]></body>
  1.4748 +      </method>
  1.4749 +
  1.4750 +      <method name="_mouseleave">
  1.4751 +        <body><![CDATA[
  1.4752 +          let tabContainer = this.parentNode;
  1.4753 +          if (tabContainer._beforeHoveredTab) {
  1.4754 +            tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
  1.4755 +            tabContainer._beforeHoveredTab = null;
  1.4756 +          }
  1.4757 +          if (tabContainer._afterHoveredTab) {
  1.4758 +            tabContainer._afterHoveredTab.removeAttribute("afterhovered");
  1.4759 +            tabContainer._afterHoveredTab = null;
  1.4760 +          }
  1.4761 +
  1.4762 +          tabContainer._hoveredTab = null;
  1.4763 +        ]]></body>
  1.4764 +      </method>
  1.4765 +    </implementation>
  1.4766 +
  1.4767 +    <handlers>
  1.4768 +      <handler event="mouseover"><![CDATA[
  1.4769 +        let anonid = event.originalTarget.getAttribute("anonid");
  1.4770 +        if (anonid == "close-button")
  1.4771 +          this.mOverCloseButton = true;
  1.4772 +
  1.4773 +        this._mouseenter();
  1.4774 +      ]]></handler>
  1.4775 +      <handler event="mouseout"><![CDATA[
  1.4776 +        let anonid = event.originalTarget.getAttribute("anonid");
  1.4777 +        if (anonid == "close-button")
  1.4778 +          this.mOverCloseButton = false;
  1.4779 +
  1.4780 +        this._mouseleave();
  1.4781 +      ]]></handler>
  1.4782 +      <handler event="dragstart" phase="capturing">
  1.4783 +        this.style.MozUserFocus = '';
  1.4784 +      </handler>
  1.4785 +      <handler event="mousedown" phase="capturing">
  1.4786 +      <![CDATA[
  1.4787 +        if (this.selected) {
  1.4788 +          this.style.MozUserFocus = 'ignore';
  1.4789 +          this.clientTop; // just using this to flush style updates
  1.4790 +        } else if (this.mOverCloseButton) {
  1.4791 +          // Prevent tabbox.xml from selecting the tab.
  1.4792 +          event.stopPropagation();
  1.4793 +        }
  1.4794 +      ]]>
  1.4795 +      </handler>
  1.4796 +      <handler event="mouseup">
  1.4797 +        this.style.MozUserFocus = '';
  1.4798 +      </handler>
  1.4799 +    </handlers>
  1.4800 +  </binding>
  1.4801 +
  1.4802 +  <binding id="tabbrowser-alltabs-popup"
  1.4803 +           extends="chrome://global/content/bindings/popup.xml#popup">
  1.4804 +    <implementation implements="nsIDOMEventListener">
  1.4805 +      <method name="_tabOnAttrModified">
  1.4806 +        <parameter name="aEvent"/>
  1.4807 +        <body><![CDATA[
  1.4808 +          var tab = aEvent.target;
  1.4809 +          if (tab.mCorrespondingMenuitem)
  1.4810 +            this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
  1.4811 +        ]]></body>
  1.4812 +      </method>
  1.4813 +
  1.4814 +      <method name="_tabOnTabClose">
  1.4815 +        <parameter name="aEvent"/>
  1.4816 +        <body><![CDATA[
  1.4817 +          var tab = aEvent.target;
  1.4818 +          if (tab.mCorrespondingMenuitem)
  1.4819 +            this.removeChild(tab.mCorrespondingMenuitem);
  1.4820 +        ]]></body>
  1.4821 +      </method>
  1.4822 +
  1.4823 +      <method name="handleEvent">
  1.4824 +        <parameter name="aEvent"/>
  1.4825 +        <body><![CDATA[
  1.4826 +          switch (aEvent.type) {
  1.4827 +            case "TabAttrModified":
  1.4828 +              this._tabOnAttrModified(aEvent);
  1.4829 +              break;
  1.4830 +            case "TabClose":
  1.4831 +              this._tabOnTabClose(aEvent);
  1.4832 +              break;
  1.4833 +            case "scroll":
  1.4834 +              this._updateTabsVisibilityStatus();
  1.4835 +              break;
  1.4836 +          }
  1.4837 +        ]]></body>
  1.4838 +      </method>
  1.4839 +
  1.4840 +      <method name="_updateTabsVisibilityStatus">
  1.4841 +        <body><![CDATA[
  1.4842 +          var tabContainer = gBrowser.tabContainer;
  1.4843 +          // We don't want menu item decoration unless there is overflow.
  1.4844 +          if (tabContainer.getAttribute("overflow") != "true")
  1.4845 +            return;
  1.4846 +
  1.4847 +          var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
  1.4848 +          for (var i = 0; i < this.childNodes.length; i++) {
  1.4849 +            let curTab = this.childNodes[i].tab;
  1.4850 +            if (!curTab) // "Tab Groups" menuitem and its menuseparator
  1.4851 +              continue;
  1.4852 +            let curTabBO = curTab.boxObject;
  1.4853 +            if (curTabBO.screenX >= tabstripBO.screenX &&
  1.4854 +                curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
  1.4855 +              this.childNodes[i].setAttribute("tabIsVisible", "true");
  1.4856 +            else
  1.4857 +              this.childNodes[i].removeAttribute("tabIsVisible");
  1.4858 +          }
  1.4859 +        ]]></body>
  1.4860 +      </method>
  1.4861 +
  1.4862 +      <method name="_createTabMenuItem">
  1.4863 +        <parameter name="aTab"/>
  1.4864 +        <body><![CDATA[
  1.4865 +          var menuItem = document.createElementNS(
  1.4866 +            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  1.4867 +            "menuitem");
  1.4868 +
  1.4869 +          menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon");
  1.4870 +
  1.4871 +          this._setMenuitemAttributes(menuItem, aTab);
  1.4872 +
  1.4873 +          aTab.mCorrespondingMenuitem = menuItem;
  1.4874 +          menuItem.tab = aTab;
  1.4875 +
  1.4876 +          this.appendChild(menuItem);
  1.4877 +        ]]></body>
  1.4878 +      </method>
  1.4879 +
  1.4880 +      <method name="_setMenuitemAttributes">
  1.4881 +        <parameter name="aMenuitem"/>
  1.4882 +        <parameter name="aTab"/>
  1.4883 +        <body><![CDATA[
  1.4884 +          aMenuitem.setAttribute("label", aTab.label);
  1.4885 +          aMenuitem.setAttribute("crop", aTab.getAttribute("crop"));
  1.4886 +
  1.4887 +          if (aTab.hasAttribute("busy")) {
  1.4888 +            aMenuitem.setAttribute("busy", aTab.getAttribute("busy"));
  1.4889 +            aMenuitem.removeAttribute("image");
  1.4890 +          } else {
  1.4891 +            aMenuitem.setAttribute("image", aTab.getAttribute("image"));
  1.4892 +            aMenuitem.removeAttribute("busy");
  1.4893 +          }
  1.4894 +
  1.4895 +          if (aTab.hasAttribute("pending"))
  1.4896 +            aMenuitem.setAttribute("pending", aTab.getAttribute("pending"));
  1.4897 +          else
  1.4898 +            aMenuitem.removeAttribute("pending");
  1.4899 +
  1.4900 +          if (aTab.selected)
  1.4901 +            aMenuitem.setAttribute("selected", "true");
  1.4902 +          else
  1.4903 +            aMenuitem.removeAttribute("selected");
  1.4904 +        ]]></body>
  1.4905 +      </method>
  1.4906 +    </implementation>
  1.4907 +
  1.4908 +    <handlers>
  1.4909 +      <handler event="popupshowing">
  1.4910 +      <![CDATA[
  1.4911 +        var tabcontainer = gBrowser.tabContainer;
  1.4912 +
  1.4913 +        // Listen for changes in the tab bar.
  1.4914 +        tabcontainer.addEventListener("TabAttrModified", this, false);
  1.4915 +        tabcontainer.addEventListener("TabClose", this, false);
  1.4916 +        tabcontainer.mTabstrip.addEventListener("scroll", this, false);
  1.4917 +
  1.4918 +        let tabs = gBrowser.visibleTabs;
  1.4919 +        for (var i = 0; i < tabs.length; i++) {
  1.4920 +          if (!tabs[i].pinned)
  1.4921 +            this._createTabMenuItem(tabs[i]);
  1.4922 +        }
  1.4923 +        this._updateTabsVisibilityStatus();
  1.4924 +      ]]></handler>
  1.4925 +
  1.4926 +      <handler event="popuphidden">
  1.4927 +      <![CDATA[
  1.4928 +        // clear out the menu popup and remove the listeners
  1.4929 +        for (let i = this.childNodes.length - 1; i > 0; i--) {
  1.4930 +          let menuItem = this.childNodes[i];
  1.4931 +          if (menuItem.tab) {
  1.4932 +            menuItem.tab.mCorrespondingMenuitem = null;
  1.4933 +            this.removeChild(menuItem);
  1.4934 +          }
  1.4935 +        }
  1.4936 +        var tabcontainer = gBrowser.tabContainer;
  1.4937 +        tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
  1.4938 +        tabcontainer.removeEventListener("TabAttrModified", this, false);
  1.4939 +        tabcontainer.removeEventListener("TabClose", this, false);
  1.4940 +      ]]></handler>
  1.4941 +
  1.4942 +      <handler event="DOMMenuItemActive">
  1.4943 +      <![CDATA[
  1.4944 +        var tab = event.target.tab;
  1.4945 +        if (tab) {
  1.4946 +          let overLink = tab.linkedBrowser.currentURI.spec;
  1.4947 +          if (overLink == "about:blank")
  1.4948 +            overLink = "";
  1.4949 +          XULBrowserWindow.setOverLink(overLink, null);
  1.4950 +        }
  1.4951 +      ]]></handler>
  1.4952 +
  1.4953 +      <handler event="DOMMenuItemInactive">
  1.4954 +      <![CDATA[
  1.4955 +        XULBrowserWindow.setOverLink("", null);
  1.4956 +      ]]></handler>
  1.4957 +
  1.4958 +      <handler event="command"><![CDATA[
  1.4959 +        if (event.target.tab)
  1.4960 +          gBrowser.selectedTab = event.target.tab;
  1.4961 +      ]]></handler>
  1.4962 +
  1.4963 +    </handlers>
  1.4964 +  </binding>
  1.4965 +
  1.4966 +  <binding id="statuspanel" display="xul:hbox">
  1.4967 +    <content>
  1.4968 +      <xul:hbox class="statuspanel-inner">
  1.4969 +        <xul:label class="statuspanel-label"
  1.4970 +                   role="status"
  1.4971 +                   aria-live="off"
  1.4972 +                   xbl:inherits="value=label,crop,mirror"
  1.4973 +                   flex="1"
  1.4974 +                   crop="end"/>
  1.4975 +      </xul:hbox>
  1.4976 +    </content>
  1.4977 +
  1.4978 +    <implementation implements="nsIDOMEventListener">
  1.4979 +      <constructor><![CDATA[
  1.4980 +        window.addEventListener("resize", this, false);
  1.4981 +      ]]></constructor>
  1.4982 +
  1.4983 +      <destructor><![CDATA[
  1.4984 +        window.removeEventListener("resize", this, false);
  1.4985 +        MousePosTracker.removeListener(this);
  1.4986 +      ]]></destructor>
  1.4987 +
  1.4988 +      <property name="label">
  1.4989 +        <setter><![CDATA[
  1.4990 +          if (!this.label) {
  1.4991 +            this.removeAttribute("mirror");
  1.4992 +            this.removeAttribute("sizelimit");
  1.4993 +          }
  1.4994 +
  1.4995 +          this.style.minWidth = this.getAttribute("type") == "status" &&
  1.4996 +                                this.getAttribute("previoustype") == "status"
  1.4997 +                                  ? getComputedStyle(this).width : "";
  1.4998 +
  1.4999 +          if (val) {
  1.5000 +            this.setAttribute("label", val);
  1.5001 +            this.removeAttribute("inactive");
  1.5002 +            this._calcMouseTargetRect();
  1.5003 +            MousePosTracker.addListener(this);
  1.5004 +          } else {
  1.5005 +            this.setAttribute("inactive", "true");
  1.5006 +            MousePosTracker.removeListener(this);
  1.5007 +          }
  1.5008 +
  1.5009 +          return val;
  1.5010 +        ]]></setter>
  1.5011 +        <getter>
  1.5012 +          return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
  1.5013 +        </getter>
  1.5014 +      </property>
  1.5015 +
  1.5016 +      <method name="getMouseTargetRect">
  1.5017 +        <body><![CDATA[
  1.5018 +          return this._mouseTargetRect;
  1.5019 +        ]]></body>
  1.5020 +      </method>
  1.5021 +
  1.5022 +      <method name="onMouseEnter">
  1.5023 +        <body>
  1.5024 +          this._mirror();
  1.5025 +        </body>
  1.5026 +      </method>
  1.5027 +
  1.5028 +      <method name="onMouseLeave">
  1.5029 +        <body>
  1.5030 +          this._mirror();
  1.5031 +        </body>
  1.5032 +      </method>
  1.5033 +
  1.5034 +      <method name="handleEvent">
  1.5035 +        <parameter name="event"/>
  1.5036 +        <body><![CDATA[
  1.5037 +          if (!this.label)
  1.5038 +            return;
  1.5039 +
  1.5040 +          switch (event.type) {
  1.5041 +            case "resize":
  1.5042 +              this._calcMouseTargetRect();
  1.5043 +              break;
  1.5044 +          }
  1.5045 +        ]]></body>
  1.5046 +      </method>
  1.5047 +
  1.5048 +      <method name="_calcMouseTargetRect">
  1.5049 +        <body><![CDATA[
  1.5050 +          let container = this.parentNode;
  1.5051 +          let alignRight = (getComputedStyle(container).direction == "rtl");
  1.5052 +          let panelRect = this.getBoundingClientRect();
  1.5053 +          let containerRect = container.getBoundingClientRect();
  1.5054 +
  1.5055 +          this._mouseTargetRect = {
  1.5056 +            top:    panelRect.top,
  1.5057 +            bottom: panelRect.bottom,
  1.5058 +            left:   alignRight ? containerRect.right - panelRect.width : containerRect.left,
  1.5059 +            right:  alignRight ? containerRect.right : containerRect.left + panelRect.width
  1.5060 +          };
  1.5061 +        ]]></body>
  1.5062 +      </method>
  1.5063 +
  1.5064 +      <method name="_mirror">
  1.5065 +        <body>
  1.5066 +          if (this.hasAttribute("mirror"))
  1.5067 +            this.removeAttribute("mirror");
  1.5068 +          else
  1.5069 +            this.setAttribute("mirror", "true");
  1.5070 +
  1.5071 +          if (!this.hasAttribute("sizelimit")) {
  1.5072 +            this.setAttribute("sizelimit", "true");
  1.5073 +            this._calcMouseTargetRect();
  1.5074 +          }
  1.5075 +        </body>
  1.5076 +      </method>
  1.5077 +    </implementation>
  1.5078 +  </binding>
  1.5079 +
  1.5080 +</bindings>

mercurial