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>