browser/base/content/tabbrowser.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 <?xml version="1.0"?>
michael@0 2
michael@0 3 <!-- This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 - License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
michael@0 6
michael@0 7 <!DOCTYPE bindings [
michael@0 8 <!ENTITY % tabBrowserDTD SYSTEM "chrome://browser/locale/tabbrowser.dtd" >
michael@0 9 %tabBrowserDTD;
michael@0 10 ]>
michael@0 11
michael@0 12 # MAKE_E10S_WORK surrounds code needed to have the front-end try to be smart
michael@0 13 # about using non-remote browsers for loading certain URIs when remote tabs
michael@0 14 # (browser.tabs.remote) are enabled.
michael@0 15 #define MAKE_E10S_WORK 1
michael@0 16
michael@0 17 <bindings id="tabBrowserBindings"
michael@0 18 xmlns="http://www.mozilla.org/xbl"
michael@0 19 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
michael@0 20 xmlns:xbl="http://www.mozilla.org/xbl">
michael@0 21
michael@0 22 <binding id="tabbrowser">
michael@0 23 <resources>
michael@0 24 <stylesheet src="chrome://browser/content/tabbrowser.css"/>
michael@0 25 </resources>
michael@0 26
michael@0 27 <content>
michael@0 28 <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
michael@0 29 <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
michael@0 30 flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
michael@0 31 onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
michael@0 32 <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
michael@0 33 <xul:notificationbox flex="1">
michael@0 34 <xul:hbox flex="1" class="browserSidebarContainer">
michael@0 35 <xul:vbox flex="1" class="browserContainer">
michael@0 36 <xul:stack flex="1" class="browserStack" anonid="browserStack">
michael@0 37 <xul:browser anonid="initialBrowser" type="content-primary" message="true"
michael@0 38 xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectpopup"/>
michael@0 39 </xul:stack>
michael@0 40 </xul:vbox>
michael@0 41 </xul:hbox>
michael@0 42 </xul:notificationbox>
michael@0 43 </xul:tabpanels>
michael@0 44 </xul:tabbox>
michael@0 45 <children/>
michael@0 46 </content>
michael@0 47 <implementation implements="nsIDOMEventListener, nsIMessageListener">
michael@0 48
michael@0 49 <property name="tabContextMenu" readonly="true"
michael@0 50 onget="return this.tabContainer.contextMenu;"/>
michael@0 51
michael@0 52 <field name="tabContainer" readonly="true">
michael@0 53 document.getElementById(this.getAttribute("tabcontainer"));
michael@0 54 </field>
michael@0 55 <field name="tabs" readonly="true">
michael@0 56 this.tabContainer.childNodes;
michael@0 57 </field>
michael@0 58
michael@0 59 <property name="visibleTabs" readonly="true">
michael@0 60 <getter><![CDATA[
michael@0 61 if (!this._visibleTabs)
michael@0 62 this._visibleTabs = Array.filter(this.tabs,
michael@0 63 function (tab) !tab.hidden && !tab.closing);
michael@0 64 return this._visibleTabs;
michael@0 65 ]]></getter>
michael@0 66 </property>
michael@0 67
michael@0 68 <field name="closingTabsEnum" readonly="true">({ ALL: 0, OTHER: 1, TO_END: 2 });</field>
michael@0 69
michael@0 70 <field name="_visibleTabs">null</field>
michael@0 71
michael@0 72 <field name="mURIFixup" readonly="true">
michael@0 73 Components.classes["@mozilla.org/docshell/urifixup;1"]
michael@0 74 .getService(Components.interfaces.nsIURIFixup);
michael@0 75 </field>
michael@0 76 <field name="mFaviconService" readonly="true">
michael@0 77 Components.classes["@mozilla.org/browser/favicon-service;1"]
michael@0 78 .getService(Components.interfaces.nsIFaviconService);
michael@0 79 </field>
michael@0 80 <field name="_placesAutocomplete" readonly="true">
michael@0 81 Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
michael@0 82 .getService(Components.interfaces.mozIPlacesAutoComplete);
michael@0 83 </field>
michael@0 84 <field name="_unifiedComplete" readonly="true">
michael@0 85 Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
michael@0 86 .getService(Components.interfaces.mozIPlacesAutoComplete);
michael@0 87 </field>
michael@0 88 <field name="mTabBox" readonly="true">
michael@0 89 document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
michael@0 90 </field>
michael@0 91 <field name="mPanelContainer" readonly="true">
michael@0 92 document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
michael@0 93 </field>
michael@0 94 <field name="mStringBundle">
michael@0 95 document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
michael@0 96 </field>
michael@0 97 <field name="mCurrentTab">
michael@0 98 null
michael@0 99 </field>
michael@0 100 <field name="_lastRelatedTab">
michael@0 101 null
michael@0 102 </field>
michael@0 103 <field name="mCurrentBrowser">
michael@0 104 null
michael@0 105 </field>
michael@0 106 <field name="mProgressListeners">
michael@0 107 []
michael@0 108 </field>
michael@0 109 <field name="mTabsProgressListeners">
michael@0 110 []
michael@0 111 </field>
michael@0 112 <field name="mTabListeners">
michael@0 113 []
michael@0 114 </field>
michael@0 115 <field name="mTabFilters">
michael@0 116 []
michael@0 117 </field>
michael@0 118 <field name="mIsBusy">
michael@0 119 false
michael@0 120 </field>
michael@0 121 <field name="arrowKeysShouldWrap" readonly="true">
michael@0 122 #ifdef XP_MACOSX
michael@0 123 true
michael@0 124 #else
michael@0 125 false
michael@0 126 #endif
michael@0 127 </field>
michael@0 128
michael@0 129 <field name="_autoScrollPopup">
michael@0 130 null
michael@0 131 </field>
michael@0 132
michael@0 133 <field name="_previewMode">
michael@0 134 false
michael@0 135 </field>
michael@0 136
michael@0 137 <field name="_lastFindValue">
michael@0 138 ""
michael@0 139 </field>
michael@0 140
michael@0 141 <property name="_numPinnedTabs" readonly="true">
michael@0 142 <getter><![CDATA[
michael@0 143 for (var i = 0; i < this.tabs.length; i++) {
michael@0 144 if (!this.tabs[i].pinned)
michael@0 145 break;
michael@0 146 }
michael@0 147 return i;
michael@0 148 ]]></getter>
michael@0 149 </property>
michael@0 150
michael@0 151 <method name="isFindBarInitialized">
michael@0 152 <parameter name="aTab"/>
michael@0 153 <body><![CDATA[
michael@0 154 return (aTab || this.selectedTab)._findBar != undefined;
michael@0 155 ]]></body>
michael@0 156 </method>
michael@0 157
michael@0 158 <method name="getFindBar">
michael@0 159 <parameter name="aTab"/>
michael@0 160 <body><![CDATA[
michael@0 161 if (!aTab)
michael@0 162 aTab = this.selectedTab;
michael@0 163
michael@0 164 if (aTab._findBar)
michael@0 165 return aTab._findBar;
michael@0 166
michael@0 167 let findBar = document.createElementNS(this.namespaceURI, "findbar");
michael@0 168 let browser = this.getBrowserForTab(aTab);
michael@0 169 let browserContainer = this.getBrowserContainer(browser);
michael@0 170 browserContainer.appendChild(findBar);
michael@0 171
michael@0 172 // Force a style flush to ensure that our binding is attached.
michael@0 173 findBar.clientTop;
michael@0 174
michael@0 175 findBar.browser = browser;
michael@0 176 findBar._findField.value = this._lastFindValue;
michael@0 177
michael@0 178 aTab._findBar = findBar;
michael@0 179
michael@0 180 let event = document.createEvent("Events");
michael@0 181 event.initEvent("TabFindInitialized", true, false);
michael@0 182 aTab.dispatchEvent(event);
michael@0 183
michael@0 184 return findBar;
michael@0 185 ]]></body>
michael@0 186 </method>
michael@0 187
michael@0 188 <method name="getStatusPanel">
michael@0 189 <body><![CDATA[
michael@0 190 if (!this._statusPanel) {
michael@0 191 this._statusPanel = document.createElementNS(this.namespaceURI, "statuspanel");
michael@0 192 this._statusPanel.setAttribute("inactive", "true");
michael@0 193 this._statusPanel.setAttribute("layer", "true");
michael@0 194 this._appendStatusPanel();
michael@0 195 }
michael@0 196 return this._statusPanel;
michael@0 197 ]]></body>
michael@0 198 </method>
michael@0 199
michael@0 200 <method name="_appendStatusPanel">
michael@0 201 <body><![CDATA[
michael@0 202 if (this._statusPanel) {
michael@0 203 let browser = this.selectedBrowser;
michael@0 204 let browserContainer = this.getBrowserContainer(browser);
michael@0 205 browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling);
michael@0 206 }
michael@0 207 ]]></body>
michael@0 208 </method>
michael@0 209
michael@0 210 <method name="updateWindowResizers">
michael@0 211 <body><![CDATA[
michael@0 212 if (!window.gShowPageResizers)
michael@0 213 return;
michael@0 214
michael@0 215 var show = window.windowState == window.STATE_NORMAL;
michael@0 216 for (let i = 0; i < this.browsers.length; i++) {
michael@0 217 this.browsers[i].showWindowResizer = show;
michael@0 218 }
michael@0 219 ]]></body>
michael@0 220 </method>
michael@0 221
michael@0 222 <method name="_setCloseKeyState">
michael@0 223 <parameter name="aEnabled"/>
michael@0 224 <body><![CDATA[
michael@0 225 let keyClose = document.getElementById("key_close");
michael@0 226 let closeKeyEnabled = keyClose.getAttribute("disabled") != "true";
michael@0 227 if (closeKeyEnabled == aEnabled)
michael@0 228 return;
michael@0 229
michael@0 230 if (aEnabled)
michael@0 231 keyClose.removeAttribute("disabled");
michael@0 232 else
michael@0 233 keyClose.setAttribute("disabled", "true");
michael@0 234
michael@0 235 // We also want to remove the keyboard shortcut from the file menu
michael@0 236 // when the shortcut is disabled, and bring it back when it's
michael@0 237 // renabled.
michael@0 238 //
michael@0 239 // Fixing bug 630826 could make that happen automatically.
michael@0 240 // Fixing bug 630830 could avoid the ugly hack below.
michael@0 241
michael@0 242 let closeMenuItem = document.getElementById("menu_close");
michael@0 243 let parentPopup = closeMenuItem.parentNode;
michael@0 244 let nextItem = closeMenuItem.nextSibling;
michael@0 245 let clonedItem = closeMenuItem.cloneNode(true);
michael@0 246
michael@0 247 parentPopup.removeChild(closeMenuItem);
michael@0 248
michael@0 249 if (aEnabled)
michael@0 250 clonedItem.setAttribute("key", "key_close");
michael@0 251 else
michael@0 252 clonedItem.removeAttribute("key");
michael@0 253
michael@0 254 parentPopup.insertBefore(clonedItem, nextItem);
michael@0 255 ]]></body>
michael@0 256 </method>
michael@0 257
michael@0 258 <method name="pinTab">
michael@0 259 <parameter name="aTab"/>
michael@0 260 <body><![CDATA[
michael@0 261 if (aTab.pinned)
michael@0 262 return;
michael@0 263
michael@0 264 if (aTab.hidden)
michael@0 265 this.showTab(aTab);
michael@0 266
michael@0 267 this.moveTabTo(aTab, this._numPinnedTabs);
michael@0 268 aTab.setAttribute("pinned", "true");
michael@0 269 this.tabContainer._unlockTabSizing();
michael@0 270 this.tabContainer._positionPinnedTabs();
michael@0 271 this.tabContainer.adjustTabstrip();
michael@0 272
michael@0 273 // Bug 961867 - [e10s] Implement the logic for app tabs
michael@0 274 if (!gMultiProcessBrowser)
michael@0 275 this.getBrowserForTab(aTab).docShell.isAppTab = true;
michael@0 276
michael@0 277 if (aTab.selected)
michael@0 278 this._setCloseKeyState(false);
michael@0 279
michael@0 280 let event = document.createEvent("Events");
michael@0 281 event.initEvent("TabPinned", true, false);
michael@0 282 aTab.dispatchEvent(event);
michael@0 283 ]]></body>
michael@0 284 </method>
michael@0 285
michael@0 286 <method name="unpinTab">
michael@0 287 <parameter name="aTab"/>
michael@0 288 <body><![CDATA[
michael@0 289 if (!aTab.pinned)
michael@0 290 return;
michael@0 291
michael@0 292 this.moveTabTo(aTab, this._numPinnedTabs - 1);
michael@0 293 aTab.removeAttribute("pinned");
michael@0 294 aTab.style.MozMarginStart = "";
michael@0 295 this.tabContainer._unlockTabSizing();
michael@0 296 this.tabContainer._positionPinnedTabs();
michael@0 297 this.tabContainer.adjustTabstrip();
michael@0 298
michael@0 299 // Bug 961867 - [e10s] Implement the logic for app tabs
michael@0 300 if (!gMultiProcessBrowser)
michael@0 301 this.getBrowserForTab(aTab).docShell.isAppTab = false;
michael@0 302
michael@0 303 if (aTab.selected)
michael@0 304 this._setCloseKeyState(true);
michael@0 305
michael@0 306 let event = document.createEvent("Events");
michael@0 307 event.initEvent("TabUnpinned", true, false);
michael@0 308 aTab.dispatchEvent(event);
michael@0 309 ]]></body>
michael@0 310 </method>
michael@0 311
michael@0 312 <method name="previewTab">
michael@0 313 <parameter name="aTab"/>
michael@0 314 <parameter name="aCallback"/>
michael@0 315 <body>
michael@0 316 <![CDATA[
michael@0 317 let currentTab = this.selectedTab;
michael@0 318 try {
michael@0 319 // Suppress focus, ownership and selected tab changes
michael@0 320 this._previewMode = true;
michael@0 321 this.selectedTab = aTab;
michael@0 322 aCallback();
michael@0 323 } finally {
michael@0 324 this.selectedTab = currentTab;
michael@0 325 this._previewMode = false;
michael@0 326 }
michael@0 327 ]]>
michael@0 328 </body>
michael@0 329 </method>
michael@0 330
michael@0 331 <method name="getBrowserAtIndex">
michael@0 332 <parameter name="aIndex"/>
michael@0 333 <body>
michael@0 334 <![CDATA[
michael@0 335 return this.browsers[aIndex];
michael@0 336 ]]>
michael@0 337 </body>
michael@0 338 </method>
michael@0 339
michael@0 340 <method name="getBrowserIndexForDocument">
michael@0 341 <parameter name="aDocument"/>
michael@0 342 <body>
michael@0 343 <![CDATA[
michael@0 344 var tab = this._getTabForContentWindow(aDocument.defaultView);
michael@0 345 return tab ? tab._tPos : -1;
michael@0 346 ]]>
michael@0 347 </body>
michael@0 348 </method>
michael@0 349
michael@0 350 <method name="getBrowserForDocument">
michael@0 351 <parameter name="aDocument"/>
michael@0 352 <body>
michael@0 353 <![CDATA[
michael@0 354 var tab = this._getTabForContentWindow(aDocument.defaultView);
michael@0 355 return tab ? tab.linkedBrowser : null;
michael@0 356 ]]>
michael@0 357 </body>
michael@0 358 </method>
michael@0 359
michael@0 360 <method name="_getTabForContentWindow">
michael@0 361 <parameter name="aWindow"/>
michael@0 362 <body>
michael@0 363 <![CDATA[
michael@0 364 for (let i = 0; i < this.browsers.length; i++) {
michael@0 365 if (this.browsers[i].contentWindow == aWindow)
michael@0 366 return this.tabs[i];
michael@0 367 }
michael@0 368 return null;
michael@0 369 ]]>
michael@0 370 </body>
michael@0 371 </method>
michael@0 372
michael@0 373 <method name="_getTabForBrowser">
michael@0 374 <parameter name="aBrowser"/>
michael@0 375 <body>
michael@0 376 <![CDATA[
michael@0 377 for (let i = 0; i < this.tabs.length; i++) {
michael@0 378 if (this.tabs[i].linkedBrowser == aBrowser)
michael@0 379 return this.tabs[i];
michael@0 380 }
michael@0 381 return null;
michael@0 382 ]]>
michael@0 383 </body>
michael@0 384 </method>
michael@0 385
michael@0 386 <method name="getNotificationBox">
michael@0 387 <parameter name="aBrowser"/>
michael@0 388 <body>
michael@0 389 <![CDATA[
michael@0 390 return this.getSidebarContainer(aBrowser).parentNode;
michael@0 391 ]]>
michael@0 392 </body>
michael@0 393 </method>
michael@0 394
michael@0 395 <method name="getSidebarContainer">
michael@0 396 <parameter name="aBrowser"/>
michael@0 397 <body>
michael@0 398 <![CDATA[
michael@0 399 return this.getBrowserContainer(aBrowser).parentNode;
michael@0 400 ]]>
michael@0 401 </body>
michael@0 402 </method>
michael@0 403
michael@0 404 <method name="getBrowserContainer">
michael@0 405 <parameter name="aBrowser"/>
michael@0 406 <body>
michael@0 407 <![CDATA[
michael@0 408 return (aBrowser || this.mCurrentBrowser).parentNode.parentNode;
michael@0 409 ]]>
michael@0 410 </body>
michael@0 411 </method>
michael@0 412
michael@0 413 <method name="getTabModalPromptBox">
michael@0 414 <parameter name="aBrowser"/>
michael@0 415 <body>
michael@0 416 <![CDATA[
michael@0 417 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 418 let browser = (aBrowser || this.mCurrentBrowser);
michael@0 419 let stack = browser.parentNode;
michael@0 420 let self = this;
michael@0 421
michael@0 422 let promptBox = {
michael@0 423 appendPrompt : function(args, onCloseCallback) {
michael@0 424 let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt");
michael@0 425 stack.appendChild(newPrompt);
michael@0 426 browser.setAttribute("tabmodalPromptShowing", true);
michael@0 427
michael@0 428 newPrompt.clientTop; // style flush to assure binding is attached
michael@0 429
michael@0 430 let tab = self._getTabForBrowser(browser);
michael@0 431 newPrompt.init(args, tab, onCloseCallback);
michael@0 432 return newPrompt;
michael@0 433 },
michael@0 434
michael@0 435 removePrompt : function(aPrompt) {
michael@0 436 stack.removeChild(aPrompt);
michael@0 437
michael@0 438 let prompts = this.listPrompts();
michael@0 439 if (prompts.length) {
michael@0 440 let prompt = prompts[prompts.length - 1];
michael@0 441 prompt.Dialog.setDefaultFocus();
michael@0 442 } else {
michael@0 443 browser.removeAttribute("tabmodalPromptShowing");
michael@0 444 browser.focus();
michael@0 445 }
michael@0 446 },
michael@0 447
michael@0 448 listPrompts : function(aPrompt) {
michael@0 449 let els = stack.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
michael@0 450 // NodeList --> real JS array
michael@0 451 let prompts = Array.slice(els);
michael@0 452 return prompts;
michael@0 453 },
michael@0 454 };
michael@0 455
michael@0 456 return promptBox;
michael@0 457 ]]>
michael@0 458 </body>
michael@0 459 </method>
michael@0 460
michael@0 461 <method name="_callProgressListeners">
michael@0 462 <parameter name="aBrowser"/>
michael@0 463 <parameter name="aMethod"/>
michael@0 464 <parameter name="aArguments"/>
michael@0 465 <parameter name="aCallGlobalListeners"/>
michael@0 466 <parameter name="aCallTabsListeners"/>
michael@0 467 <body><![CDATA[
michael@0 468 var rv = true;
michael@0 469
michael@0 470 if (!aBrowser)
michael@0 471 aBrowser = this.mCurrentBrowser;
michael@0 472
michael@0 473 if (aCallGlobalListeners != false &&
michael@0 474 aBrowser == this.mCurrentBrowser) {
michael@0 475 this.mProgressListeners.forEach(function (p) {
michael@0 476 if (aMethod in p) {
michael@0 477 try {
michael@0 478 if (!p[aMethod].apply(p, aArguments))
michael@0 479 rv = false;
michael@0 480 } catch (e) {
michael@0 481 // don't inhibit other listeners
michael@0 482 Components.utils.reportError(e);
michael@0 483 }
michael@0 484 }
michael@0 485 });
michael@0 486 }
michael@0 487
michael@0 488 if (aCallTabsListeners != false) {
michael@0 489 aArguments.unshift(aBrowser);
michael@0 490
michael@0 491 this.mTabsProgressListeners.forEach(function (p) {
michael@0 492 if (aMethod in p) {
michael@0 493 try {
michael@0 494 if (!p[aMethod].apply(p, aArguments))
michael@0 495 rv = false;
michael@0 496 } catch (e) {
michael@0 497 // don't inhibit other listeners
michael@0 498 Components.utils.reportError(e);
michael@0 499 }
michael@0 500 }
michael@0 501 });
michael@0 502 }
michael@0 503
michael@0 504 return rv;
michael@0 505 ]]></body>
michael@0 506 </method>
michael@0 507
michael@0 508 <!-- A web progress listener object definition for a given tab. -->
michael@0 509 <method name="mTabProgressListener">
michael@0 510 <parameter name="aTab"/>
michael@0 511 <parameter name="aBrowser"/>
michael@0 512 <parameter name="aStartsBlank"/>
michael@0 513 <body>
michael@0 514 <![CDATA[
michael@0 515 return ({
michael@0 516 mTabBrowser: this,
michael@0 517 mTab: aTab,
michael@0 518 mBrowser: aBrowser,
michael@0 519 mBlank: aStartsBlank,
michael@0 520
michael@0 521 // cache flags for correct status UI update after tab switching
michael@0 522 mStateFlags: 0,
michael@0 523 mStatus: 0,
michael@0 524 mMessage: "",
michael@0 525 mTotalProgress: 0,
michael@0 526
michael@0 527 // count of open requests (should always be 0 or 1)
michael@0 528 mRequestCount: 0,
michael@0 529
michael@0 530 destroy: function () {
michael@0 531 delete this.mTab;
michael@0 532 delete this.mBrowser;
michael@0 533 delete this.mTabBrowser;
michael@0 534 },
michael@0 535
michael@0 536 _callProgressListeners: function () {
michael@0 537 Array.unshift(arguments, this.mBrowser);
michael@0 538 return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
michael@0 539 },
michael@0 540
michael@0 541 _shouldShowProgress: function (aRequest) {
michael@0 542 if (this.mBlank)
michael@0 543 return false;
michael@0 544
michael@0 545 if (gMultiProcessBrowser)
michael@0 546 return true;
michael@0 547
michael@0 548 // Don't show progress indicators in tabs for about: URIs
michael@0 549 // pointing to local resources.
michael@0 550 try {
michael@0 551 let channel = aRequest.QueryInterface(Ci.nsIChannel);
michael@0 552 if (channel.originalURI.schemeIs("about") &&
michael@0 553 (channel.URI.schemeIs("jar") || channel.URI.schemeIs("file")))
michael@0 554 return false;
michael@0 555 } catch (e) {}
michael@0 556
michael@0 557 return true;
michael@0 558 },
michael@0 559
michael@0 560 onProgressChange: function (aWebProgress, aRequest,
michael@0 561 aCurSelfProgress, aMaxSelfProgress,
michael@0 562 aCurTotalProgress, aMaxTotalProgress) {
michael@0 563 this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
michael@0 564
michael@0 565 if (!this._shouldShowProgress(aRequest))
michael@0 566 return;
michael@0 567
michael@0 568 if (this.mTotalProgress)
michael@0 569 this.mTab.setAttribute("progress", "true");
michael@0 570
michael@0 571 this._callProgressListeners("onProgressChange",
michael@0 572 [aWebProgress, aRequest,
michael@0 573 aCurSelfProgress, aMaxSelfProgress,
michael@0 574 aCurTotalProgress, aMaxTotalProgress]);
michael@0 575 },
michael@0 576
michael@0 577 onProgressChange64: function (aWebProgress, aRequest,
michael@0 578 aCurSelfProgress, aMaxSelfProgress,
michael@0 579 aCurTotalProgress, aMaxTotalProgress) {
michael@0 580 return this.onProgressChange(aWebProgress, aRequest,
michael@0 581 aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
michael@0 582 aMaxTotalProgress);
michael@0 583 },
michael@0 584
michael@0 585 onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
michael@0 586 if (!aRequest)
michael@0 587 return;
michael@0 588
michael@0 589 var oldBlank = this.mBlank;
michael@0 590
michael@0 591 const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
michael@0 592 const nsIChannel = Components.interfaces.nsIChannel;
michael@0 593
michael@0 594 if (aStateFlags & nsIWebProgressListener.STATE_START) {
michael@0 595 this.mRequestCount++;
michael@0 596 }
michael@0 597 else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
michael@0 598 const NS_ERROR_UNKNOWN_HOST = 2152398878;
michael@0 599 if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
michael@0 600 // to prevent bug 235825: wait for the request handled
michael@0 601 // by the automatic keyword resolver
michael@0 602 return;
michael@0 603 }
michael@0 604 // since we (try to) only handle STATE_STOP of the last request,
michael@0 605 // the count of open requests should now be 0
michael@0 606 this.mRequestCount = 0;
michael@0 607 }
michael@0 608
michael@0 609 if (aStateFlags & nsIWebProgressListener.STATE_START &&
michael@0 610 aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
michael@0 611 // It's okay to clear what the user typed when we start
michael@0 612 // loading a document. If the user types, this counter gets
michael@0 613 // set to zero, if the document load ends without an
michael@0 614 // onLocationChange, this counter gets decremented
michael@0 615 // (so we keep it while switching tabs after failed loads)
michael@0 616 // We need to add 2 because loadURIWithFlags may have
michael@0 617 // cancelled a pending load which would have cleared
michael@0 618 // its anchor scroll detection temporary increment.
michael@0 619 if (aWebProgress.isTopLevel)
michael@0 620 this.mBrowser.userTypedClear += 2;
michael@0 621
michael@0 622 if (this._shouldShowProgress(aRequest)) {
michael@0 623 if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
michael@0 624 this.mTab.setAttribute("busy", "true");
michael@0 625 if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD))
michael@0 626 this.mTabBrowser.setTabTitleLoading(this.mTab);
michael@0 627 }
michael@0 628
michael@0 629 if (this.mTab.selected)
michael@0 630 this.mTabBrowser.mIsBusy = true;
michael@0 631 }
michael@0 632 }
michael@0 633 else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
michael@0 634 aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
michael@0 635
michael@0 636 if (this.mTab.hasAttribute("busy")) {
michael@0 637 this.mTab.removeAttribute("busy");
michael@0 638 this.mTabBrowser._tabAttrModified(this.mTab);
michael@0 639 if (!this.mTab.selected)
michael@0 640 this.mTab.setAttribute("unread", "true");
michael@0 641 }
michael@0 642 this.mTab.removeAttribute("progress");
michael@0 643
michael@0 644 if (aWebProgress.isTopLevel) {
michael@0 645 if (!Components.isSuccessCode(aStatus) &&
michael@0 646 !isTabEmpty(this.mTab)) {
michael@0 647 // Restore the current document's location in case the
michael@0 648 // request was stopped (possibly from a content script)
michael@0 649 // before the location changed.
michael@0 650
michael@0 651 this.mBrowser.userTypedValue = null;
michael@0 652
michael@0 653 if (this.mTab.selected && gURLBar)
michael@0 654 URLBarSetURI();
michael@0 655 } else {
michael@0 656 // The document is done loading, we no longer want the
michael@0 657 // value cleared.
michael@0 658
michael@0 659 if (this.mBrowser.userTypedClear > 1)
michael@0 660 this.mBrowser.userTypedClear -= 2;
michael@0 661 else if (this.mBrowser.userTypedClear > 0)
michael@0 662 this.mBrowser.userTypedClear--;
michael@0 663 }
michael@0 664
michael@0 665 if (!this.mBrowser.mIconURL)
michael@0 666 this.mTabBrowser.useDefaultIcon(this.mTab);
michael@0 667 }
michael@0 668
michael@0 669 if (this.mBlank)
michael@0 670 this.mBlank = false;
michael@0 671
michael@0 672 var location = aRequest.QueryInterface(nsIChannel).URI;
michael@0 673
michael@0 674 // For keyword URIs clear the user typed value since they will be changed into real URIs
michael@0 675 if (location.scheme == "keyword")
michael@0 676 this.mBrowser.userTypedValue = null;
michael@0 677
michael@0 678 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.connecting"))
michael@0 679 this.mTabBrowser.setTabTitle(this.mTab);
michael@0 680
michael@0 681 if (this.mTab.selected)
michael@0 682 this.mTabBrowser.mIsBusy = false;
michael@0 683 }
michael@0 684
michael@0 685 if (oldBlank) {
michael@0 686 this._callProgressListeners("onUpdateCurrentBrowser",
michael@0 687 [aStateFlags, aStatus, "", 0],
michael@0 688 true, false);
michael@0 689 } else {
michael@0 690 this._callProgressListeners("onStateChange",
michael@0 691 [aWebProgress, aRequest, aStateFlags, aStatus],
michael@0 692 true, false);
michael@0 693 }
michael@0 694
michael@0 695 this._callProgressListeners("onStateChange",
michael@0 696 [aWebProgress, aRequest, aStateFlags, aStatus],
michael@0 697 false);
michael@0 698
michael@0 699 if (aStateFlags & (nsIWebProgressListener.STATE_START |
michael@0 700 nsIWebProgressListener.STATE_STOP)) {
michael@0 701 // reset cached temporary values at beginning and end
michael@0 702 this.mMessage = "";
michael@0 703 this.mTotalProgress = 0;
michael@0 704 }
michael@0 705 this.mStateFlags = aStateFlags;
michael@0 706 this.mStatus = aStatus;
michael@0 707 },
michael@0 708
michael@0 709 onLocationChange: function (aWebProgress, aRequest, aLocation,
michael@0 710 aFlags) {
michael@0 711 // OnLocationChange is called for both the top-level content
michael@0 712 // and the subframes.
michael@0 713 let topLevel = aWebProgress.isTopLevel;
michael@0 714
michael@0 715 if (topLevel) {
michael@0 716 // If userTypedClear > 0, the document loaded correctly and we should be
michael@0 717 // clearing the user typed value. We also need to clear the typed value
michael@0 718 // if the document failed to load, to make sure the urlbar reflects the
michael@0 719 // failed URI (particularly for SSL errors). However, don't clear the value
michael@0 720 // if the error page's URI is about:blank, because that causes complete
michael@0 721 // loss of urlbar contents for invalid URI errors (see bug 867957).
michael@0 722 if (this.mBrowser.userTypedClear > 0 ||
michael@0 723 ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
michael@0 724 aLocation.spec != "about:blank"))
michael@0 725 this.mBrowser.userTypedValue = null;
michael@0 726
michael@0 727 // Clear out the missing plugins list since it's related to the
michael@0 728 // previous location.
michael@0 729 this.mBrowser.missingPlugins = null;
michael@0 730
michael@0 731 if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
michael@0 732 let findBar = this.mTabBrowser.getFindBar(this.mTab);
michael@0 733
michael@0 734 // Close the Find toolbar if we're in old-style TAF mode
michael@0 735 if (findBar.findMode != findBar.FIND_NORMAL)
michael@0 736 findBar.close();
michael@0 737
michael@0 738 // fix bug 253793 - turn off highlight when page changes
michael@0 739 findBar.getElement("highlight").checked = false;
michael@0 740 }
michael@0 741
michael@0 742 // Don't clear the favicon if this onLocationChange was
michael@0 743 // triggered by a pushState or a replaceState. See bug 550565.
michael@0 744 if (aWebProgress.isLoadingDocument &&
michael@0 745 !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) {
michael@0 746 this.mBrowser.mIconURL = null;
michael@0 747 }
michael@0 748
michael@0 749 let autocomplete = this.mTabBrowser._placesAutocomplete;
michael@0 750 let unifiedComplete = this.mTabBrowser._unifiedComplete;
michael@0 751 if (this.mBrowser.registeredOpenURI) {
michael@0 752 autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
michael@0 753 unifiedComplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
michael@0 754 delete this.mBrowser.registeredOpenURI;
michael@0 755 }
michael@0 756 // Tabs in private windows aren't registered as "Open" so
michael@0 757 // that they don't appear as switch-to-tab candidates.
michael@0 758 if (!isBlankPageURL(aLocation.spec) &&
michael@0 759 (!PrivateBrowsingUtils.isWindowPrivate(window) ||
michael@0 760 PrivateBrowsingUtils.permanentPrivateBrowsing)) {
michael@0 761 autocomplete.registerOpenPage(aLocation);
michael@0 762 unifiedComplete.registerOpenPage(aLocation);
michael@0 763 this.mBrowser.registeredOpenURI = aLocation;
michael@0 764 }
michael@0 765 }
michael@0 766
michael@0 767 if (!this.mBlank) {
michael@0 768 this._callProgressListeners("onLocationChange",
michael@0 769 [aWebProgress, aRequest, aLocation,
michael@0 770 aFlags]);
michael@0 771 }
michael@0 772
michael@0 773 if (topLevel) {
michael@0 774 this.mBrowser.lastURI = aLocation;
michael@0 775 this.mBrowser.lastLocationChange = Date.now();
michael@0 776 }
michael@0 777 },
michael@0 778
michael@0 779 onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
michael@0 780 if (this.mBlank)
michael@0 781 return;
michael@0 782
michael@0 783 this._callProgressListeners("onStatusChange",
michael@0 784 [aWebProgress, aRequest, aStatus, aMessage]);
michael@0 785
michael@0 786 this.mMessage = aMessage;
michael@0 787 },
michael@0 788
michael@0 789 onSecurityChange: function (aWebProgress, aRequest, aState) {
michael@0 790 this._callProgressListeners("onSecurityChange",
michael@0 791 [aWebProgress, aRequest, aState]);
michael@0 792 },
michael@0 793
michael@0 794 onRefreshAttempted: function (aWebProgress, aURI, aDelay, aSameURI) {
michael@0 795 return this._callProgressListeners("onRefreshAttempted",
michael@0 796 [aWebProgress, aURI, aDelay, aSameURI]);
michael@0 797 },
michael@0 798
michael@0 799 QueryInterface: function (aIID) {
michael@0 800 if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
michael@0 801 aIID.equals(Components.interfaces.nsIWebProgressListener2) ||
michael@0 802 aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
michael@0 803 aIID.equals(Components.interfaces.nsISupports))
michael@0 804 return this;
michael@0 805 throw Components.results.NS_NOINTERFACE;
michael@0 806 }
michael@0 807 });
michael@0 808 ]]>
michael@0 809 </body>
michael@0 810 </method>
michael@0 811
michael@0 812 <method name="setIcon">
michael@0 813 <parameter name="aTab"/>
michael@0 814 <parameter name="aURI"/>
michael@0 815 <body>
michael@0 816 <![CDATA[
michael@0 817 var browser = this.getBrowserForTab(aTab);
michael@0 818 browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
michael@0 819
michael@0 820 if (aURI && this.mFaviconService) {
michael@0 821 if (!(aURI instanceof Ci.nsIURI))
michael@0 822 aURI = makeURI(aURI);
michael@0 823 this.mFaviconService.setAndFetchFaviconForPage(browser.currentURI,
michael@0 824 aURI, false,
michael@0 825 PrivateBrowsingUtils.isWindowPrivate(window) ?
michael@0 826 this.mFaviconService.FAVICON_LOAD_PRIVATE :
michael@0 827 this.mFaviconService.FAVICON_LOAD_NON_PRIVATE);
michael@0 828 }
michael@0 829
michael@0 830 let sizedIconUrl = browser.mIconURL || "";
michael@0 831 if (sizedIconUrl) {
michael@0 832 let size = Math.round(16 * window.devicePixelRatio);
michael@0 833 sizedIconUrl += (sizedIconUrl.contains("#") ? "&" : "#") +
michael@0 834 "-moz-resolution=" + size + "," + size;
michael@0 835 }
michael@0 836 if (sizedIconUrl != aTab.getAttribute("image")) {
michael@0 837 if (sizedIconUrl)
michael@0 838 aTab.setAttribute("image", sizedIconUrl);
michael@0 839 else
michael@0 840 aTab.removeAttribute("image");
michael@0 841 this._tabAttrModified(aTab);
michael@0 842 }
michael@0 843
michael@0 844 this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
michael@0 845 ]]>
michael@0 846 </body>
michael@0 847 </method>
michael@0 848
michael@0 849 <method name="getIcon">
michael@0 850 <parameter name="aTab"/>
michael@0 851 <body>
michael@0 852 <![CDATA[
michael@0 853 let browser = aTab ? this.getBrowserForTab(aTab) : this.selectedBrowser;
michael@0 854 return browser.mIconURL;
michael@0 855 ]]>
michael@0 856 </body>
michael@0 857 </method>
michael@0 858
michael@0 859 <method name="shouldLoadFavIcon">
michael@0 860 <parameter name="aURI"/>
michael@0 861 <body>
michael@0 862 <![CDATA[
michael@0 863 return (aURI &&
michael@0 864 Services.prefs.getBoolPref("browser.chrome.site_icons") &&
michael@0 865 Services.prefs.getBoolPref("browser.chrome.favicons") &&
michael@0 866 ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
michael@0 867 ]]>
michael@0 868 </body>
michael@0 869 </method>
michael@0 870
michael@0 871 <method name="useDefaultIcon">
michael@0 872 <parameter name="aTab"/>
michael@0 873 <body>
michael@0 874 <![CDATA[
michael@0 875 var browser = this.getBrowserForTab(aTab);
michael@0 876 var documentURI = browser.documentURI;
michael@0 877 var icon = null;
michael@0 878
michael@0 879 if (browser.imageDocument) {
michael@0 880 if (Services.prefs.getBoolPref("browser.chrome.site_icons")) {
michael@0 881 let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size");
michael@0 882 if (browser.imageDocument.width <= sz &&
michael@0 883 browser.imageDocument.height <= sz) {
michael@0 884 icon = browser.currentURI;
michael@0 885 }
michael@0 886 }
michael@0 887 }
michael@0 888 // Use documentURIObject in the check for shouldLoadFavIcon so that we
michael@0 889 // do the right thing with about:-style error pages. Bug 453442
michael@0 890 else if (this.shouldLoadFavIcon(documentURI)) {
michael@0 891 let url = documentURI.prePath + "/favicon.ico";
michael@0 892 if (!this.isFailedIcon(url))
michael@0 893 icon = url;
michael@0 894 }
michael@0 895 this.setIcon(aTab, icon);
michael@0 896 ]]>
michael@0 897 </body>
michael@0 898 </method>
michael@0 899
michael@0 900 <method name="isFailedIcon">
michael@0 901 <parameter name="aURI"/>
michael@0 902 <body>
michael@0 903 <![CDATA[
michael@0 904 if (this.mFaviconService) {
michael@0 905 if (!(aURI instanceof Ci.nsIURI))
michael@0 906 aURI = makeURI(aURI);
michael@0 907 return this.mFaviconService.isFailedFavicon(aURI);
michael@0 908 }
michael@0 909 return null;
michael@0 910 ]]>
michael@0 911 </body>
michael@0 912 </method>
michael@0 913
michael@0 914 <method name="getWindowTitleForBrowser">
michael@0 915 <parameter name="aBrowser"/>
michael@0 916 <body>
michael@0 917 <![CDATA[
michael@0 918 var newTitle = "";
michael@0 919 var docElement = this.ownerDocument.documentElement;
michael@0 920 var sep = docElement.getAttribute("titlemenuseparator");
michael@0 921
michael@0 922 // Strip out any null bytes in the content title, since the
michael@0 923 // underlying widget implementations of nsWindow::SetTitle pass
michael@0 924 // null-terminated strings to system APIs.
michael@0 925 var docTitle = aBrowser.contentTitle.replace("\0", "", "g");
michael@0 926
michael@0 927 if (!docTitle)
michael@0 928 docTitle = docElement.getAttribute("titledefault");
michael@0 929
michael@0 930 var modifier = docElement.getAttribute("titlemodifier");
michael@0 931 if (docTitle) {
michael@0 932 newTitle += docElement.getAttribute("titlepreface");
michael@0 933 newTitle += docTitle;
michael@0 934 if (modifier)
michael@0 935 newTitle += sep;
michael@0 936 }
michael@0 937 newTitle += modifier;
michael@0 938
michael@0 939 // If location bar is hidden and the URL type supports a host,
michael@0 940 // add the scheme and host to the title to prevent spoofing.
michael@0 941 // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
michael@0 942 try {
michael@0 943 if (docElement.getAttribute("chromehidden").contains("location")) {
michael@0 944 var uri = this.mURIFixup.createExposableURI(
michael@0 945 aBrowser.currentURI);
michael@0 946 if (uri.scheme == "about")
michael@0 947 newTitle = uri.spec + sep + newTitle;
michael@0 948 else
michael@0 949 newTitle = uri.prePath + sep + newTitle;
michael@0 950 }
michael@0 951 } catch (e) {}
michael@0 952
michael@0 953 return newTitle;
michael@0 954 ]]>
michael@0 955 </body>
michael@0 956 </method>
michael@0 957
michael@0 958 <method name="updateTitlebar">
michael@0 959 <body>
michael@0 960 <![CDATA[
michael@0 961 if ("TabView" in window && TabView.isVisible()) {
michael@0 962 // ToDo: this will be removed when we gain ability to draw to the menu bar.
michael@0 963 // Bug 586175
michael@0 964 this.ownerDocument.title = TabView.windowTitle;
michael@0 965 }
michael@0 966 else {
michael@0 967 this.ownerDocument.title = this.getWindowTitleForBrowser(this.mCurrentBrowser);
michael@0 968 }
michael@0 969 ]]>
michael@0 970 </body>
michael@0 971 </method>
michael@0 972
michael@0 973 <method name="updateCurrentBrowser">
michael@0 974 <parameter name="aForceUpdate"/>
michael@0 975 <body>
michael@0 976 <![CDATA[
michael@0 977 var newBrowser = this.getBrowserAtIndex(this.tabContainer.selectedIndex);
michael@0 978 if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
michael@0 979 return;
michael@0 980
michael@0 981 if (!aForceUpdate) {
michael@0 982 TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
michael@0 983 window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
michael@0 984 .beginTabSwitch();
michael@0 985 }
michael@0 986
michael@0 987 var oldTab = this.mCurrentTab;
michael@0 988
michael@0 989 // Preview mode should not reset the owner
michael@0 990 if (!this._previewMode && !oldTab.selected)
michael@0 991 oldTab.owner = null;
michael@0 992
michael@0 993 if (this._lastRelatedTab) {
michael@0 994 if (!this._lastRelatedTab.selected)
michael@0 995 this._lastRelatedTab.owner = null;
michael@0 996 this._lastRelatedTab = null;
michael@0 997 }
michael@0 998
michael@0 999 var oldBrowser = this.mCurrentBrowser;
michael@0 1000 oldBrowser.setAttribute("type", "content-targetable");
michael@0 1001 oldBrowser.docShellIsActive = false;
michael@0 1002
michael@0 1003 var updateBlockedPopups = false;
michael@0 1004 if ((oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
michael@0 1005 (!oldBrowser.blockedPopups && newBrowser.blockedPopups))
michael@0 1006 updateBlockedPopups = true;
michael@0 1007
michael@0 1008 newBrowser.setAttribute("type", "content-primary");
michael@0 1009 newBrowser.docShellIsActive =
michael@0 1010 (window.windowState != window.STATE_MINIMIZED);
michael@0 1011 this.mCurrentBrowser = newBrowser;
michael@0 1012 this.mCurrentTab = this.tabContainer.selectedItem;
michael@0 1013 this.showTab(this.mCurrentTab);
michael@0 1014
michael@0 1015 var forwardButtonContainer = document.getElementById("urlbar-wrapper");
michael@0 1016 if (forwardButtonContainer) {
michael@0 1017 forwardButtonContainer.setAttribute("switchingtabs", "true");
michael@0 1018 window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
michael@0 1019 window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
michael@0 1020 forwardButtonContainer.removeAttribute("switchingtabs");
michael@0 1021 });
michael@0 1022 }
michael@0 1023
michael@0 1024 this._appendStatusPanel();
michael@0 1025
michael@0 1026 if (updateBlockedPopups)
michael@0 1027 this.mCurrentBrowser.updateBlockedPopups(false);
michael@0 1028
michael@0 1029 // Update the URL bar.
michael@0 1030 var loc = this.mCurrentBrowser.currentURI;
michael@0 1031
michael@0 1032 // Bug 666809 - SecurityUI support for e10s
michael@0 1033 var webProgress = this.mCurrentBrowser.webProgress;
michael@0 1034 var securityUI = this.mCurrentBrowser.securityUI;
michael@0 1035
michael@0 1036 this._callProgressListeners(null, "onLocationChange",
michael@0 1037 [webProgress, null, loc, 0], true,
michael@0 1038 false);
michael@0 1039
michael@0 1040 if (securityUI) {
michael@0 1041 this._callProgressListeners(null, "onSecurityChange",
michael@0 1042 [webProgress, null, securityUI.state], true, false);
michael@0 1043 }
michael@0 1044
michael@0 1045 var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
michael@0 1046 if (listener && listener.mStateFlags) {
michael@0 1047 this._callProgressListeners(null, "onUpdateCurrentBrowser",
michael@0 1048 [listener.mStateFlags, listener.mStatus,
michael@0 1049 listener.mMessage, listener.mTotalProgress],
michael@0 1050 true, false);
michael@0 1051 }
michael@0 1052
michael@0 1053 if (!this._previewMode) {
michael@0 1054 this.mCurrentTab.removeAttribute("unread");
michael@0 1055 oldTab.lastAccessed = Date.now();
michael@0 1056
michael@0 1057 let oldFindBar = oldTab._findBar;
michael@0 1058 if (oldFindBar &&
michael@0 1059 oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
michael@0 1060 !oldFindBar.hidden)
michael@0 1061 this._lastFindValue = oldFindBar._findField.value;
michael@0 1062
michael@0 1063 this.updateTitlebar();
michael@0 1064
michael@0 1065 this.mCurrentTab.removeAttribute("titlechanged");
michael@0 1066 }
michael@0 1067
michael@0 1068 // If the new tab is busy, and our current state is not busy, then
michael@0 1069 // we need to fire a start to all progress listeners.
michael@0 1070 const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
michael@0 1071 if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
michael@0 1072 this.mIsBusy = true;
michael@0 1073 this._callProgressListeners(null, "onStateChange",
michael@0 1074 [webProgress, null,
michael@0 1075 nsIWebProgressListener.STATE_START |
michael@0 1076 nsIWebProgressListener.STATE_IS_NETWORK, 0],
michael@0 1077 true, false);
michael@0 1078 }
michael@0 1079
michael@0 1080 // If the new tab is not busy, and our current state is busy, then
michael@0 1081 // we need to fire a stop to all progress listeners.
michael@0 1082 if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
michael@0 1083 this.mIsBusy = false;
michael@0 1084 this._callProgressListeners(null, "onStateChange",
michael@0 1085 [webProgress, null,
michael@0 1086 nsIWebProgressListener.STATE_STOP |
michael@0 1087 nsIWebProgressListener.STATE_IS_NETWORK, 0],
michael@0 1088 true, false);
michael@0 1089 }
michael@0 1090
michael@0 1091 this._setCloseKeyState(!this.mCurrentTab.pinned);
michael@0 1092
michael@0 1093 // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
michael@0 1094 // that might rely upon the other changes suppressed.
michael@0 1095 // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
michael@0 1096 if (!this._previewMode) {
michael@0 1097 // We've selected the new tab, so go ahead and notify listeners.
michael@0 1098 let event = new CustomEvent("TabSelect", {
michael@0 1099 bubbles: true,
michael@0 1100 cancelable: false,
michael@0 1101 detail: {
michael@0 1102 previousTab: oldTab
michael@0 1103 }
michael@0 1104 });
michael@0 1105 this.mCurrentTab.dispatchEvent(event);
michael@0 1106
michael@0 1107 this._tabAttrModified(oldTab);
michael@0 1108 this._tabAttrModified(this.mCurrentTab);
michael@0 1109
michael@0 1110 if (oldBrowser != newBrowser &&
michael@0 1111 oldBrowser.docShell &&
michael@0 1112 oldBrowser.docShell.contentViewer.inPermitUnload) {
michael@0 1113 // Since the user is switching away from a tab that has
michael@0 1114 // a beforeunload prompt active, we remove the prompt.
michael@0 1115 // This prevents confusing user flows like the following:
michael@0 1116 // 1. User attempts to close Firefox
michael@0 1117 // 2. User switches tabs (ingoring a beforeunload prompt)
michael@0 1118 // 3. User returns to tab, presses "Leave page"
michael@0 1119 let promptBox = this.getTabModalPromptBox(oldBrowser);
michael@0 1120 let prompts = promptBox.listPrompts();
michael@0 1121 // NB: This code assumes that the beforeunload prompt
michael@0 1122 // is the top-most prompt on the tab.
michael@0 1123 promptBox.removePrompt(prompts[prompts.length - 1]);
michael@0 1124 }
michael@0 1125
michael@0 1126 // Adjust focus
michael@0 1127 oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
michael@0 1128 if (this.isFindBarInitialized(oldTab)) {
michael@0 1129 let findBar = this.getFindBar(oldTab);
michael@0 1130 oldTab._findBarFocused = (!findBar.hidden &&
michael@0 1131 findBar._findField.getAttribute("focused") == "true");
michael@0 1132 }
michael@0 1133 do {
michael@0 1134 // When focus is in the tab bar, retain it there.
michael@0 1135 if (document.activeElement == oldTab) {
michael@0 1136 // We need to explicitly focus the new tab, because
michael@0 1137 // tabbox.xml does this only in some cases.
michael@0 1138 this.mCurrentTab.focus();
michael@0 1139 break;
michael@0 1140 }
michael@0 1141
michael@0 1142 // If there's a tabmodal prompt showing, focus it.
michael@0 1143 if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
michael@0 1144 let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 1145 let prompts = newBrowser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt");
michael@0 1146 let prompt = prompts[prompts.length - 1];
michael@0 1147 prompt.Dialog.setDefaultFocus();
michael@0 1148 break;
michael@0 1149 }
michael@0 1150
michael@0 1151 // Focus the location bar if it was previously focused for that tab.
michael@0 1152 // In full screen mode, only bother making the location bar visible
michael@0 1153 // if the tab is a blank one.
michael@0 1154 if (newBrowser._urlbarFocused && gURLBar) {
michael@0 1155
michael@0 1156 // Explicitly close the popup if the URL bar retains focus
michael@0 1157 gURLBar.closePopup();
michael@0 1158
michael@0 1159 if (!window.fullScreen) {
michael@0 1160 gURLBar.focus();
michael@0 1161 break;
michael@0 1162 } else if (isTabEmpty(this.mCurrentTab)) {
michael@0 1163 focusAndSelectUrlBar();
michael@0 1164 break;
michael@0 1165 }
michael@0 1166 }
michael@0 1167
michael@0 1168 // Focus the find bar if it was previously focused for that tab.
michael@0 1169 if (gFindBarInitialized && !gFindBar.hidden &&
michael@0 1170 this.selectedTab._findBarFocused) {
michael@0 1171 gFindBar._findField.focus();
michael@0 1172 break;
michael@0 1173 }
michael@0 1174
michael@0 1175 // Otherwise, focus the content area.
michael@0 1176 let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
michael@0 1177 let focusFlags = fm.FLAG_NOSCROLL;
michael@0 1178
michael@0 1179 if (!gMultiProcessBrowser) {
michael@0 1180 let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {});
michael@0 1181
michael@0 1182 // for anchors, use FLAG_SHOWRING so that it is clear what link was
michael@0 1183 // last clicked when switching back to that tab
michael@0 1184 if (newFocusedElement &&
michael@0 1185 (newFocusedElement instanceof HTMLAnchorElement ||
michael@0 1186 newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
michael@0 1187 focusFlags |= fm.FLAG_SHOWRING;
michael@0 1188 }
michael@0 1189 fm.setFocus(newBrowser, focusFlags);
michael@0 1190 } while (false);
michael@0 1191 }
michael@0 1192
michael@0 1193 this.tabContainer._setPositionalAttributes();
michael@0 1194
michael@0 1195 if (!aForceUpdate)
michael@0 1196 TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
michael@0 1197 ]]>
michael@0 1198 </body>
michael@0 1199 </method>
michael@0 1200
michael@0 1201 <method name="_tabAttrModified">
michael@0 1202 <parameter name="aTab"/>
michael@0 1203 <body><![CDATA[
michael@0 1204 if (aTab.closing)
michael@0 1205 return;
michael@0 1206
michael@0 1207 // This event should be dispatched when any of these attributes change:
michael@0 1208 // label, crop, busy, image, selected
michael@0 1209 var event = document.createEvent("Events");
michael@0 1210 event.initEvent("TabAttrModified", true, false);
michael@0 1211 aTab.dispatchEvent(event);
michael@0 1212 ]]></body>
michael@0 1213 </method>
michael@0 1214
michael@0 1215 <method name="setTabTitleLoading">
michael@0 1216 <parameter name="aTab"/>
michael@0 1217 <body>
michael@0 1218 <![CDATA[
michael@0 1219 aTab.label = this.mStringBundle.getString("tabs.connecting");
michael@0 1220 aTab.crop = "end";
michael@0 1221 this._tabAttrModified(aTab);
michael@0 1222 ]]>
michael@0 1223 </body>
michael@0 1224 </method>
michael@0 1225
michael@0 1226 <method name="setTabTitle">
michael@0 1227 <parameter name="aTab"/>
michael@0 1228 <body>
michael@0 1229 <![CDATA[
michael@0 1230 var browser = this.getBrowserForTab(aTab);
michael@0 1231 var crop = "end";
michael@0 1232 var title = browser.contentTitle;
michael@0 1233
michael@0 1234 if (!title) {
michael@0 1235 if (browser.currentURI.spec) {
michael@0 1236 try {
michael@0 1237 title = this.mURIFixup.createExposableURI(browser.currentURI).spec;
michael@0 1238 } catch(ex) {
michael@0 1239 title = browser.currentURI.spec;
michael@0 1240 }
michael@0 1241 }
michael@0 1242
michael@0 1243 if (title && !isBlankPageURL(title)) {
michael@0 1244 // At this point, we now have a URI.
michael@0 1245 // Let's try to unescape it using a character set
michael@0 1246 // in case the URI is not ASCII.
michael@0 1247 try {
michael@0 1248 var characterSet = browser.characterSet;
michael@0 1249 const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
michael@0 1250 .getService(Components.interfaces.nsITextToSubURI);
michael@0 1251 title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
michael@0 1252 } catch(ex) { /* Do nothing. */ }
michael@0 1253
michael@0 1254 crop = "center";
michael@0 1255
michael@0 1256 } else // Still no title? Fall back to our untitled string.
michael@0 1257 title = this.mStringBundle.getString("tabs.emptyTabTitle");
michael@0 1258 }
michael@0 1259
michael@0 1260 if (aTab.label == title &&
michael@0 1261 aTab.crop == crop)
michael@0 1262 return false;
michael@0 1263
michael@0 1264 aTab.label = title;
michael@0 1265 aTab.crop = crop;
michael@0 1266 this._tabAttrModified(aTab);
michael@0 1267
michael@0 1268 if (aTab.selected)
michael@0 1269 this.updateTitlebar();
michael@0 1270
michael@0 1271 return true;
michael@0 1272 ]]>
michael@0 1273 </body>
michael@0 1274 </method>
michael@0 1275
michael@0 1276 <method name="loadOneTab">
michael@0 1277 <parameter name="aURI"/>
michael@0 1278 <parameter name="aReferrerURI"/>
michael@0 1279 <parameter name="aCharset"/>
michael@0 1280 <parameter name="aPostData"/>
michael@0 1281 <parameter name="aLoadInBackground"/>
michael@0 1282 <parameter name="aAllowThirdPartyFixup"/>
michael@0 1283 <body>
michael@0 1284 <![CDATA[
michael@0 1285 var aFromExternal;
michael@0 1286 var aRelatedToCurrent;
michael@0 1287 var aDisableMCB;
michael@0 1288 var aSkipAnimation;
michael@0 1289 if (arguments.length == 2 &&
michael@0 1290 typeof arguments[1] == "object" &&
michael@0 1291 !(arguments[1] instanceof Ci.nsIURI)) {
michael@0 1292 let params = arguments[1];
michael@0 1293 aReferrerURI = params.referrerURI;
michael@0 1294 aCharset = params.charset;
michael@0 1295 aPostData = params.postData;
michael@0 1296 aLoadInBackground = params.inBackground;
michael@0 1297 aAllowThirdPartyFixup = params.allowThirdPartyFixup;
michael@0 1298 aFromExternal = params.fromExternal;
michael@0 1299 aRelatedToCurrent = params.relatedToCurrent;
michael@0 1300 aDisableMCB = params.disableMCB;
michael@0 1301 aSkipAnimation = params.skipAnimation;
michael@0 1302 }
michael@0 1303
michael@0 1304 var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
michael@0 1305 Services.prefs.getBoolPref("browser.tabs.loadInBackground");
michael@0 1306 var owner = bgLoad ? null : this.selectedTab;
michael@0 1307 var tab = this.addTab(aURI, {
michael@0 1308 referrerURI: aReferrerURI,
michael@0 1309 charset: aCharset,
michael@0 1310 postData: aPostData,
michael@0 1311 ownerTab: owner,
michael@0 1312 allowThirdPartyFixup: aAllowThirdPartyFixup,
michael@0 1313 fromExternal: aFromExternal,
michael@0 1314 relatedToCurrent: aRelatedToCurrent,
michael@0 1315 skipAnimation: aSkipAnimation,
michael@0 1316 disableMCB: aDisableMCB});
michael@0 1317 if (!bgLoad)
michael@0 1318 this.selectedTab = tab;
michael@0 1319
michael@0 1320 return tab;
michael@0 1321 ]]>
michael@0 1322 </body>
michael@0 1323 </method>
michael@0 1324
michael@0 1325 <method name="loadTabs">
michael@0 1326 <parameter name="aURIs"/>
michael@0 1327 <parameter name="aLoadInBackground"/>
michael@0 1328 <parameter name="aReplace"/>
michael@0 1329 <body><![CDATA[
michael@0 1330 if (!aURIs.length)
michael@0 1331 return;
michael@0 1332
michael@0 1333 // The tab selected after this new tab is closed (i.e. the new tab's
michael@0 1334 // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
michael@0 1335 // when several urls are opened here (i.e. closing the first should select
michael@0 1336 // the next of many URLs opened) or if the pref to have UI links opened in
michael@0 1337 // the background is set (i.e. the link is not being opened modally)
michael@0 1338 //
michael@0 1339 // i.e.
michael@0 1340 // Number of URLs Load UI Links in BG Focus Last Viewed?
michael@0 1341 // == 1 false YES
michael@0 1342 // == 1 true NO
michael@0 1343 // > 1 false/true NO
michael@0 1344 var multiple = aURIs.length > 1;
michael@0 1345 var owner = multiple || aLoadInBackground ? null : this.selectedTab;
michael@0 1346 var firstTabAdded = null;
michael@0 1347
michael@0 1348 if (aReplace) {
michael@0 1349 try {
michael@0 1350 this.loadURI(aURIs[0], null, null);
michael@0 1351 } catch (e) {
michael@0 1352 // Ignore failure in case a URI is wrong, so we can continue
michael@0 1353 // opening the next ones.
michael@0 1354 }
michael@0 1355 }
michael@0 1356 else
michael@0 1357 firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: multiple});
michael@0 1358
michael@0 1359 var tabNum = this.tabContainer.selectedIndex;
michael@0 1360 for (let i = 1; i < aURIs.length; ++i) {
michael@0 1361 let tab = this.addTab(aURIs[i], {skipAnimation: true});
michael@0 1362 if (aReplace)
michael@0 1363 this.moveTabTo(tab, ++tabNum);
michael@0 1364 }
michael@0 1365
michael@0 1366 if (!aLoadInBackground) {
michael@0 1367 if (firstTabAdded) {
michael@0 1368 // .selectedTab setter focuses the content area
michael@0 1369 this.selectedTab = firstTabAdded;
michael@0 1370 }
michael@0 1371 else
michael@0 1372 this.selectedBrowser.focus();
michael@0 1373 }
michael@0 1374 ]]></body>
michael@0 1375 </method>
michael@0 1376
michael@0 1377 #ifdef MAKE_E10S_WORK
michael@0 1378 <method name="updateBrowserRemoteness">
michael@0 1379 <parameter name="aBrowser"/>
michael@0 1380 <parameter name="aURL"/>
michael@0 1381 <body>
michael@0 1382 <![CDATA[
michael@0 1383 let shouldBeRemote = this._shouldBrowserBeRemote(aURL);
michael@0 1384
michael@0 1385 let isRemote = aBrowser.getAttribute("remote") == "true";
michael@0 1386 if (isRemote == shouldBeRemote)
michael@0 1387 return false;
michael@0 1388
michael@0 1389 let wasActive = document.activeElement == aBrowser;
michael@0 1390
michael@0 1391 // Unhook our progress listener.
michael@0 1392 let tab = this._getTabForBrowser(aBrowser);
michael@0 1393 let index = tab._tPos;
michael@0 1394 let filter = this.mTabFilters[index];
michael@0 1395 aBrowser.webProgress.removeProgressListener(filter);
michael@0 1396
michael@0 1397 // Change the "remote" attribute.
michael@0 1398 let parent = aBrowser.parentNode;
michael@0 1399 parent.removeChild(aBrowser);
michael@0 1400 aBrowser.setAttribute("remote", shouldBeRemote ? "true" : "false");
michael@0 1401 parent.appendChild(aBrowser);
michael@0 1402
michael@0 1403 // Restore the progress listener.
michael@0 1404 aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
michael@0 1405
michael@0 1406 if (shouldBeRemote)
michael@0 1407 tab.setAttribute("remote", "true");
michael@0 1408 else
michael@0 1409 tab.removeAttribute("remote");
michael@0 1410
michael@0 1411 if (wasActive)
michael@0 1412 aBrowser.focus();
michael@0 1413
michael@0 1414 return true;
michael@0 1415 ]]>
michael@0 1416 </body>
michael@0 1417 </method>
michael@0 1418
michael@0 1419 <!--
michael@0 1420 Returns true if we want to load the content for this URL in a
michael@0 1421 remote process. Eventually this should just check whether aURL
michael@0 1422 is unprivileged. Right now, though, we would like to load
michael@0 1423 some unprivileged URLs (like about:neterror) in the main
michael@0 1424 process since they interact with chrome code through
michael@0 1425 BrowserOnClick.
michael@0 1426 -->
michael@0 1427 <method name="_shouldBrowserBeRemote">
michael@0 1428 <parameter name="aURL"/>
michael@0 1429 <body>
michael@0 1430 <![CDATA[
michael@0 1431 if (!gMultiProcessBrowser)
michael@0 1432 return false;
michael@0 1433
michael@0 1434 // loadURI in browser.xml treats null as about:blank
michael@0 1435 if (!aURL)
michael@0 1436 aURL = "about:blank";
michael@0 1437
michael@0 1438 if (aURL.startsWith("about:") &&
michael@0 1439 aURL.toLowerCase() != "about:home" &&
michael@0 1440 aURL.toLowerCase() != "about:blank") {
michael@0 1441 return false;
michael@0 1442 }
michael@0 1443
michael@0 1444 if (aURL.startsWith("chrome:"))
michael@0 1445 return false;
michael@0 1446
michael@0 1447 return true;
michael@0 1448 ]]>
michael@0 1449 </body>
michael@0 1450 </method>
michael@0 1451 #endif
michael@0 1452
michael@0 1453 <method name="addTab">
michael@0 1454 <parameter name="aURI"/>
michael@0 1455 <parameter name="aReferrerURI"/>
michael@0 1456 <parameter name="aCharset"/>
michael@0 1457 <parameter name="aPostData"/>
michael@0 1458 <parameter name="aOwner"/>
michael@0 1459 <parameter name="aAllowThirdPartyFixup"/>
michael@0 1460 <body>
michael@0 1461 <![CDATA[
michael@0 1462 const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 1463 var aFromExternal;
michael@0 1464 var aRelatedToCurrent;
michael@0 1465 var aSkipAnimation;
michael@0 1466 var aDisableMCB;
michael@0 1467 if (arguments.length == 2 &&
michael@0 1468 typeof arguments[1] == "object" &&
michael@0 1469 !(arguments[1] instanceof Ci.nsIURI)) {
michael@0 1470 let params = arguments[1];
michael@0 1471 aReferrerURI = params.referrerURI;
michael@0 1472 aCharset = params.charset;
michael@0 1473 aPostData = params.postData;
michael@0 1474 aOwner = params.ownerTab;
michael@0 1475 aAllowThirdPartyFixup = params.allowThirdPartyFixup;
michael@0 1476 aFromExternal = params.fromExternal;
michael@0 1477 aRelatedToCurrent = params.relatedToCurrent;
michael@0 1478 aSkipAnimation = params.skipAnimation;
michael@0 1479 aDisableMCB = params.disableMCB;
michael@0 1480 }
michael@0 1481
michael@0 1482 // if we're adding tabs, we're past interrupt mode, ditch the owner
michael@0 1483 if (this.mCurrentTab.owner)
michael@0 1484 this.mCurrentTab.owner = null;
michael@0 1485
michael@0 1486 var t = document.createElementNS(NS_XUL, "tab");
michael@0 1487
michael@0 1488 var uriIsAboutBlank = !aURI || aURI == "about:blank";
michael@0 1489
michael@0 1490 t.setAttribute("crop", "end");
michael@0 1491 t.setAttribute("onerror", "this.removeAttribute('image');");
michael@0 1492 t.className = "tabbrowser-tab";
michael@0 1493
michael@0 1494 #ifdef MAKE_E10S_WORK
michael@0 1495 let remote = this._shouldBrowserBeRemote(aURI);
michael@0 1496 #else
michael@0 1497 let remote = gMultiProcessBrowser;
michael@0 1498 #endif
michael@0 1499 if (remote)
michael@0 1500 t.setAttribute("remote", "true");
michael@0 1501
michael@0 1502 this.tabContainer._unlockTabSizing();
michael@0 1503
michael@0 1504 // When overflowing, new tabs are scrolled into view smoothly, which
michael@0 1505 // doesn't go well together with the width transition. So we skip the
michael@0 1506 // transition in that case.
michael@0 1507 let animate = !aSkipAnimation &&
michael@0 1508 this.tabContainer.getAttribute("overflow") != "true" &&
michael@0 1509 Services.prefs.getBoolPref("browser.tabs.animate");
michael@0 1510 if (!animate) {
michael@0 1511 t.setAttribute("fadein", "true");
michael@0 1512 setTimeout(function (tabContainer) {
michael@0 1513 tabContainer._handleNewTab(t);
michael@0 1514 }, 0, this.tabContainer);
michael@0 1515 }
michael@0 1516
michael@0 1517 // invalidate caches
michael@0 1518 this._browsers = null;
michael@0 1519 this._visibleTabs = null;
michael@0 1520
michael@0 1521 this.tabContainer.appendChild(t);
michael@0 1522
michael@0 1523 // If this new tab is owned by another, assert that relationship
michael@0 1524 if (aOwner)
michael@0 1525 t.owner = aOwner;
michael@0 1526
michael@0 1527 var b = document.createElementNS(
michael@0 1528 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
michael@0 1529 "browser");
michael@0 1530 b.setAttribute("type", "content-targetable");
michael@0 1531 b.setAttribute("message", "true");
michael@0 1532 b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
michael@0 1533 b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
michael@0 1534
michael@0 1535 if (remote)
michael@0 1536 b.setAttribute("remote", "true");
michael@0 1537
michael@0 1538 if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
michael@0 1539 b.setAttribute("showresizer", "true");
michael@0 1540 }
michael@0 1541
michael@0 1542 if (this.hasAttribute("autocompletepopup"))
michael@0 1543 b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
michael@0 1544
michael@0 1545 if (this.hasAttribute("selectpopup"))
michael@0 1546 b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
michael@0 1547
michael@0 1548 b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
michael@0 1549
michael@0 1550 // Create the browserStack container
michael@0 1551 var stack = document.createElementNS(NS_XUL, "stack");
michael@0 1552 stack.className = "browserStack";
michael@0 1553 stack.appendChild(b);
michael@0 1554 stack.setAttribute("flex", "1");
michael@0 1555
michael@0 1556 // Create the browserContainer
michael@0 1557 var browserContainer = document.createElementNS(NS_XUL, "vbox");
michael@0 1558 browserContainer.className = "browserContainer";
michael@0 1559 browserContainer.appendChild(stack);
michael@0 1560 browserContainer.setAttribute("flex", "1");
michael@0 1561
michael@0 1562 // Create the sidebar container
michael@0 1563 var browserSidebarContainer = document.createElementNS(NS_XUL,
michael@0 1564 "hbox");
michael@0 1565 browserSidebarContainer.className = "browserSidebarContainer";
michael@0 1566 browserSidebarContainer.appendChild(browserContainer);
michael@0 1567 browserSidebarContainer.setAttribute("flex", "1");
michael@0 1568
michael@0 1569 // Add the Message and the Browser to the box
michael@0 1570 var notificationbox = document.createElementNS(NS_XUL,
michael@0 1571 "notificationbox");
michael@0 1572 notificationbox.setAttribute("flex", "1");
michael@0 1573 notificationbox.appendChild(browserSidebarContainer);
michael@0 1574
michael@0 1575 var position = this.tabs.length - 1;
michael@0 1576 var uniqueId = this._generateUniquePanelID();
michael@0 1577 notificationbox.id = uniqueId;
michael@0 1578 t.linkedPanel = uniqueId;
michael@0 1579 t.linkedBrowser = b;
michael@0 1580 t._tPos = position;
michael@0 1581 this.tabContainer._setPositionalAttributes();
michael@0 1582
michael@0 1583 // Prevent the superfluous initial load of a blank document
michael@0 1584 // if we're going to load something other than about:blank.
michael@0 1585 if (!uriIsAboutBlank) {
michael@0 1586 b.setAttribute("nodefaultsrc", "true");
michael@0 1587 }
michael@0 1588
michael@0 1589 // NB: this appendChild call causes us to run constructors for the
michael@0 1590 // browser element, which fires off a bunch of notifications. Some
michael@0 1591 // of those notifications can cause code to run that inspects our
michael@0 1592 // state, so it is important that the tab element is fully
michael@0 1593 // initialized by this point.
michael@0 1594 this.mPanelContainer.appendChild(notificationbox);
michael@0 1595
michael@0 1596 // We've waited until the tab is in the DOM to set the label. This
michael@0 1597 // allows the TabLabelModified event to be properly dispatched.
michael@0 1598 if (!aURI || isBlankPageURL(aURI)) {
michael@0 1599 t.label = this.mStringBundle.getString("tabs.emptyTabTitle");
michael@0 1600 } else {
michael@0 1601 t.label = aURI;
michael@0 1602 }
michael@0 1603
michael@0 1604 this.tabContainer.updateVisibility();
michael@0 1605
michael@0 1606 // wire up a progress listener for the new browser object.
michael@0 1607 var tabListener = this.mTabProgressListener(t, b, uriIsAboutBlank);
michael@0 1608 const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
michael@0 1609 .createInstance(Components.interfaces.nsIWebProgress);
michael@0 1610 filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
michael@0 1611 b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
michael@0 1612 this.mTabListeners[position] = tabListener;
michael@0 1613 this.mTabFilters[position] = filter;
michael@0 1614
michael@0 1615 b.droppedLinkHandler = handleDroppedLink;
michael@0 1616
michael@0 1617 // If we just created a new tab that loads the default
michael@0 1618 // newtab url, swap in a preloaded page if possible.
michael@0 1619 // Do nothing if we're a private window.
michael@0 1620 let docShellsSwapped = false;
michael@0 1621 if (aURI == BROWSER_NEW_TAB_URL &&
michael@0 1622 !PrivateBrowsingUtils.isWindowPrivate(window) &&
michael@0 1623 !gMultiProcessBrowser) {
michael@0 1624 docShellsSwapped = gBrowserNewTabPreloader.newTab(t);
michael@0 1625 } else if (aURI == "about:customizing") {
michael@0 1626 docShellsSwapped = gCustomizationTabPreloader.newTab(t);
michael@0 1627 }
michael@0 1628
michael@0 1629 // Dispatch a new tab notification. We do this once we're
michael@0 1630 // entirely done, so that things are in a consistent state
michael@0 1631 // even if the event listener opens or closes tabs.
michael@0 1632 var evt = document.createEvent("Events");
michael@0 1633 evt.initEvent("TabOpen", true, false);
michael@0 1634 t.dispatchEvent(evt);
michael@0 1635
michael@0 1636 // If we didn't swap docShells with a preloaded browser
michael@0 1637 // then let's just continue loading the page normally.
michael@0 1638 if (!docShellsSwapped && !uriIsAboutBlank) {
michael@0 1639 // pretend the user typed this so it'll be available till
michael@0 1640 // the document successfully loads
michael@0 1641 if (aURI && gInitialPages.indexOf(aURI) == -1)
michael@0 1642 b.userTypedValue = aURI;
michael@0 1643
michael@0 1644 let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
michael@0 1645 if (aAllowThirdPartyFixup) {
michael@0 1646 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
michael@0 1647 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
michael@0 1648 }
michael@0 1649 if (aFromExternal)
michael@0 1650 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
michael@0 1651 if (aDisableMCB)
michael@0 1652 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
michael@0 1653 try {
michael@0 1654 b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
michael@0 1655 } catch (ex) {
michael@0 1656 Cu.reportError(ex);
michael@0 1657 }
michael@0 1658 }
michael@0 1659
michael@0 1660 // We start our browsers out as inactive, and then maintain
michael@0 1661 // activeness in the tab switcher.
michael@0 1662 b.docShellIsActive = false;
michael@0 1663
michael@0 1664 // Check if we're opening a tab related to the current tab and
michael@0 1665 // move it to after the current tab.
michael@0 1666 // aReferrerURI is null or undefined if the tab is opened from
michael@0 1667 // an external application or bookmark, i.e. somewhere other
michael@0 1668 // than the current tab.
michael@0 1669 if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
michael@0 1670 Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
michael@0 1671 let newTabPos = (this._lastRelatedTab ||
michael@0 1672 this.selectedTab)._tPos + 1;
michael@0 1673 if (this._lastRelatedTab)
michael@0 1674 this._lastRelatedTab.owner = null;
michael@0 1675 else
michael@0 1676 t.owner = this.selectedTab;
michael@0 1677 this.moveTabTo(t, newTabPos);
michael@0 1678 this._lastRelatedTab = t;
michael@0 1679 }
michael@0 1680
michael@0 1681 if (animate) {
michael@0 1682 mozRequestAnimationFrame(function () {
michael@0 1683 this.tabContainer._handleTabTelemetryStart(t, aURI);
michael@0 1684
michael@0 1685 // kick the animation off
michael@0 1686 t.setAttribute("fadein", "true");
michael@0 1687 }.bind(this));
michael@0 1688 }
michael@0 1689
michael@0 1690 return t;
michael@0 1691 ]]>
michael@0 1692 </body>
michael@0 1693 </method>
michael@0 1694
michael@0 1695 <method name="warnAboutClosingTabs">
michael@0 1696 <parameter name="aCloseTabs"/>
michael@0 1697 <parameter name="aTab"/>
michael@0 1698 <body>
michael@0 1699 <![CDATA[
michael@0 1700 var tabsToClose;
michael@0 1701 switch (aCloseTabs) {
michael@0 1702 case this.closingTabsEnum.ALL:
michael@0 1703 tabsToClose = this.tabs.length - this._removingTabs.length -
michael@0 1704 gBrowser._numPinnedTabs;
michael@0 1705 break;
michael@0 1706 case this.closingTabsEnum.OTHER:
michael@0 1707 tabsToClose = this.visibleTabs.length - 1 - gBrowser._numPinnedTabs;
michael@0 1708 break;
michael@0 1709 case this.closingTabsEnum.TO_END:
michael@0 1710 if (!aTab)
michael@0 1711 throw new Error("Required argument missing: aTab");
michael@0 1712
michael@0 1713 tabsToClose = this.getTabsToTheEndFrom(aTab).length;
michael@0 1714 break;
michael@0 1715 default:
michael@0 1716 throw new Error("Invalid argument: " + aCloseTabs);
michael@0 1717 }
michael@0 1718
michael@0 1719 if (tabsToClose <= 1)
michael@0 1720 return true;
michael@0 1721
michael@0 1722 const pref = aCloseTabs == this.closingTabsEnum.ALL ?
michael@0 1723 "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
michael@0 1724 var shouldPrompt = Services.prefs.getBoolPref(pref);
michael@0 1725 if (!shouldPrompt)
michael@0 1726 return true;
michael@0 1727
michael@0 1728 var ps = Services.prompt;
michael@0 1729
michael@0 1730 // default to true: if it were false, we wouldn't get this far
michael@0 1731 var warnOnClose = { value: true };
michael@0 1732 var bundle = this.mStringBundle;
michael@0 1733
michael@0 1734 // focus the window before prompting.
michael@0 1735 // this will raise any minimized window, which will
michael@0 1736 // make it obvious which window the prompt is for and will
michael@0 1737 // solve the problem of windows "obscuring" the prompt.
michael@0 1738 // see bug #350299 for more details
michael@0 1739 window.focus();
michael@0 1740 var warningMessage =
michael@0 1741 PluralForm.get(tabsToClose, bundle.getString("tabs.closeWarningMultiple"))
michael@0 1742 .replace("#1", tabsToClose);
michael@0 1743 var buttonPressed =
michael@0 1744 ps.confirmEx(window,
michael@0 1745 bundle.getString("tabs.closeWarningTitle"),
michael@0 1746 warningMessage,
michael@0 1747 (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0)
michael@0 1748 + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
michael@0 1749 bundle.getString("tabs.closeButtonMultiple"),
michael@0 1750 null, null,
michael@0 1751 aCloseTabs == this.closingTabsEnum.ALL ?
michael@0 1752 bundle.getString("tabs.closeWarningPromptMe") : null,
michael@0 1753 warnOnClose);
michael@0 1754 var reallyClose = (buttonPressed == 0);
michael@0 1755
michael@0 1756 // don't set the pref unless they press OK and it's false
michael@0 1757 if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
michael@0 1758 Services.prefs.setBoolPref(pref, false);
michael@0 1759
michael@0 1760 return reallyClose;
michael@0 1761 ]]>
michael@0 1762 </body>
michael@0 1763 </method>
michael@0 1764
michael@0 1765 <method name="getTabsToTheEndFrom">
michael@0 1766 <parameter name="aTab"/>
michael@0 1767 <body>
michael@0 1768 <![CDATA[
michael@0 1769 var tabsToEnd = [];
michael@0 1770 let tabs = this.visibleTabs;
michael@0 1771 for (let i = tabs.length - 1; tabs[i] != aTab && i >= 0; --i) {
michael@0 1772 tabsToEnd.push(tabs[i]);
michael@0 1773 }
michael@0 1774 return tabsToEnd.reverse();
michael@0 1775 ]]>
michael@0 1776 </body>
michael@0 1777 </method>
michael@0 1778
michael@0 1779 <method name="removeTabsToTheEndFrom">
michael@0 1780 <parameter name="aTab"/>
michael@0 1781 <body>
michael@0 1782 <![CDATA[
michael@0 1783 if (this.warnAboutClosingTabs(this.closingTabsEnum.TO_END, aTab)) {
michael@0 1784 let tabs = this.getTabsToTheEndFrom(aTab);
michael@0 1785 for (let i = tabs.length - 1; i >= 0; --i) {
michael@0 1786 this.removeTab(tabs[i], {animate: true});
michael@0 1787 }
michael@0 1788 }
michael@0 1789 ]]>
michael@0 1790 </body>
michael@0 1791 </method>
michael@0 1792
michael@0 1793 <method name="removeAllTabsBut">
michael@0 1794 <parameter name="aTab"/>
michael@0 1795 <body>
michael@0 1796 <![CDATA[
michael@0 1797 if (aTab.pinned)
michael@0 1798 return;
michael@0 1799
michael@0 1800 if (this.warnAboutClosingTabs(this.closingTabsEnum.OTHER)) {
michael@0 1801 let tabs = this.visibleTabs;
michael@0 1802 this.selectedTab = aTab;
michael@0 1803
michael@0 1804 for (let i = tabs.length - 1; i >= 0; --i) {
michael@0 1805 if (tabs[i] != aTab && !tabs[i].pinned)
michael@0 1806 this.removeTab(tabs[i], {animate: true});
michael@0 1807 }
michael@0 1808 }
michael@0 1809 ]]>
michael@0 1810 </body>
michael@0 1811 </method>
michael@0 1812
michael@0 1813 <method name="removeCurrentTab">
michael@0 1814 <parameter name="aParams"/>
michael@0 1815 <body>
michael@0 1816 <![CDATA[
michael@0 1817 this.removeTab(this.mCurrentTab, aParams);
michael@0 1818 ]]>
michael@0 1819 </body>
michael@0 1820 </method>
michael@0 1821
michael@0 1822 <field name="_removingTabs">
michael@0 1823 []
michael@0 1824 </field>
michael@0 1825
michael@0 1826 <method name="removeTab">
michael@0 1827 <parameter name="aTab"/>
michael@0 1828 <parameter name="aParams"/>
michael@0 1829 <body>
michael@0 1830 <![CDATA[
michael@0 1831 if (aParams) {
michael@0 1832 var animate = aParams.animate;
michael@0 1833 var byMouse = aParams.byMouse;
michael@0 1834 }
michael@0 1835
michael@0 1836 // Handle requests for synchronously removing an already
michael@0 1837 // asynchronously closing tab.
michael@0 1838 if (!animate &&
michael@0 1839 aTab.closing) {
michael@0 1840 this._endRemoveTab(aTab);
michael@0 1841 return;
michael@0 1842 }
michael@0 1843
michael@0 1844 var isLastTab = (this.tabs.length - this._removingTabs.length == 1);
michael@0 1845
michael@0 1846 if (!this._beginRemoveTab(aTab, false, null, true))
michael@0 1847 return;
michael@0 1848
michael@0 1849 if (!aTab.pinned && !aTab.hidden && aTab._fullyOpen && byMouse)
michael@0 1850 this.tabContainer._lockTabSizing(aTab);
michael@0 1851 else
michael@0 1852 this.tabContainer._unlockTabSizing();
michael@0 1853
michael@0 1854 if (!animate /* the caller didn't opt in */ ||
michael@0 1855 isLastTab ||
michael@0 1856 aTab.pinned ||
michael@0 1857 aTab.hidden ||
michael@0 1858 this._removingTabs.length > 3 /* don't want lots of concurrent animations */ ||
michael@0 1859 aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
michael@0 1860 window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
michael@0 1861 !Services.prefs.getBoolPref("browser.tabs.animate")) {
michael@0 1862 this._endRemoveTab(aTab);
michael@0 1863 return;
michael@0 1864 }
michael@0 1865
michael@0 1866 this.tabContainer._handleTabTelemetryStart(aTab);
michael@0 1867
michael@0 1868 this._blurTab(aTab);
michael@0 1869 aTab.style.maxWidth = ""; // ensure that fade-out transition happens
michael@0 1870 aTab.removeAttribute("fadein");
michael@0 1871
michael@0 1872 setTimeout(function (tab, tabbrowser) {
michael@0 1873 if (tab.parentNode &&
michael@0 1874 window.getComputedStyle(tab).maxWidth == "0.1px") {
michael@0 1875 NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
michael@0 1876 tabbrowser._endRemoveTab(tab);
michael@0 1877 }
michael@0 1878 }, 3000, aTab, this);
michael@0 1879 ]]>
michael@0 1880 </body>
michael@0 1881 </method>
michael@0 1882
michael@0 1883 <!-- Tab close requests are ignored if the window is closing anyway,
michael@0 1884 e.g. when holding Ctrl+W. -->
michael@0 1885 <field name="_windowIsClosing">
michael@0 1886 false
michael@0 1887 </field>
michael@0 1888
michael@0 1889 <method name="_beginRemoveTab">
michael@0 1890 <parameter name="aTab"/>
michael@0 1891 <parameter name="aTabWillBeMoved"/>
michael@0 1892 <parameter name="aCloseWindowWithLastTab"/>
michael@0 1893 <parameter name="aCloseWindowFastpath"/>
michael@0 1894 <body>
michael@0 1895 <![CDATA[
michael@0 1896 if (aTab.closing ||
michael@0 1897 this._windowIsClosing)
michael@0 1898 return false;
michael@0 1899
michael@0 1900 var browser = this.getBrowserForTab(aTab);
michael@0 1901
michael@0 1902 if (!aTabWillBeMoved) {
michael@0 1903 let ds = browser.docShell;
michael@0 1904 if (ds &&
michael@0 1905 ds.contentViewer &&
michael@0 1906 !ds.contentViewer.permitUnload()) {
michael@0 1907 return false;
michael@0 1908 }
michael@0 1909 }
michael@0 1910
michael@0 1911 var closeWindow = false;
michael@0 1912 var newTab = false;
michael@0 1913 if (this.tabs.length - this._removingTabs.length == 1) {
michael@0 1914 closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
michael@0 1915 !window.toolbar.visible ||
michael@0 1916 Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
michael@0 1917
michael@0 1918 // Closing the tab and replacing it with a blank one is notably slower
michael@0 1919 // than closing the window right away. If the caller opts in, take
michael@0 1920 // the fast path.
michael@0 1921 if (closeWindow &&
michael@0 1922 aCloseWindowFastpath &&
michael@0 1923 this._removingTabs.length == 0) {
michael@0 1924 // This call actually closes the window, unless the user
michael@0 1925 // cancels the operation. We are finished here in both cases.
michael@0 1926 this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
michael@0 1927 return null;
michael@0 1928 }
michael@0 1929
michael@0 1930 newTab = true;
michael@0 1931 }
michael@0 1932
michael@0 1933 aTab.closing = true;
michael@0 1934 this._removingTabs.push(aTab);
michael@0 1935 this._visibleTabs = null; // invalidate cache
michael@0 1936
michael@0 1937 // Invalidate hovered tab state tracking for this closing tab.
michael@0 1938 if (this.tabContainer._hoveredTab == aTab)
michael@0 1939 aTab._mouseleave();
michael@0 1940
michael@0 1941 if (newTab)
michael@0 1942 this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
michael@0 1943 else
michael@0 1944 this.tabContainer.updateVisibility();
michael@0 1945
michael@0 1946 // We're committed to closing the tab now.
michael@0 1947 // Dispatch a notification.
michael@0 1948 // We dispatch it before any teardown so that event listeners can
michael@0 1949 // inspect the tab that's about to close.
michael@0 1950 var evt = document.createEvent("UIEvent");
michael@0 1951 evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
michael@0 1952 aTab.dispatchEvent(evt);
michael@0 1953
michael@0 1954 if (!aTabWillBeMoved && !gMultiProcessBrowser) {
michael@0 1955 // Prevent this tab from showing further dialogs, since we're closing it
michael@0 1956 var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
michael@0 1957 getInterface(Ci.nsIDOMWindowUtils);
michael@0 1958 windowUtils.disableDialogs();
michael@0 1959 }
michael@0 1960
michael@0 1961 // Remove the tab's filter and progress listener.
michael@0 1962 const filter = this.mTabFilters[aTab._tPos];
michael@0 1963
michael@0 1964 browser.webProgress.removeProgressListener(filter);
michael@0 1965
michael@0 1966 filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
michael@0 1967 this.mTabListeners[aTab._tPos].destroy();
michael@0 1968
michael@0 1969 if (browser.registeredOpenURI && !aTabWillBeMoved) {
michael@0 1970 this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
michael@0 1971 this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
michael@0 1972 delete browser.registeredOpenURI;
michael@0 1973 }
michael@0 1974
michael@0 1975 // We are no longer the primary content area.
michael@0 1976 browser.setAttribute("type", "content-targetable");
michael@0 1977
michael@0 1978 // Remove this tab as the owner of any other tabs, since it's going away.
michael@0 1979 Array.forEach(this.tabs, function (tab) {
michael@0 1980 if ("owner" in tab && tab.owner == aTab)
michael@0 1981 // |tab| is a child of the tab we're removing, make it an orphan
michael@0 1982 tab.owner = null;
michael@0 1983 });
michael@0 1984
michael@0 1985 aTab._endRemoveArgs = [closeWindow, newTab];
michael@0 1986 return true;
michael@0 1987 ]]>
michael@0 1988 </body>
michael@0 1989 </method>
michael@0 1990
michael@0 1991 <method name="_endRemoveTab">
michael@0 1992 <parameter name="aTab"/>
michael@0 1993 <body>
michael@0 1994 <![CDATA[
michael@0 1995 if (!aTab || !aTab._endRemoveArgs)
michael@0 1996 return;
michael@0 1997
michael@0 1998 var [aCloseWindow, aNewTab] = aTab._endRemoveArgs;
michael@0 1999 aTab._endRemoveArgs = null;
michael@0 2000
michael@0 2001 if (this._windowIsClosing) {
michael@0 2002 aCloseWindow = false;
michael@0 2003 aNewTab = false;
michael@0 2004 }
michael@0 2005
michael@0 2006 this._lastRelatedTab = null;
michael@0 2007
michael@0 2008 // update the UI early for responsiveness
michael@0 2009 aTab.collapsed = true;
michael@0 2010 this.tabContainer._fillTrailingGap();
michael@0 2011 this._blurTab(aTab);
michael@0 2012
michael@0 2013 this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1);
michael@0 2014
michael@0 2015 if (aCloseWindow) {
michael@0 2016 this._windowIsClosing = true;
michael@0 2017 while (this._removingTabs.length)
michael@0 2018 this._endRemoveTab(this._removingTabs[0]);
michael@0 2019 } else if (!this._windowIsClosing) {
michael@0 2020 if (aNewTab)
michael@0 2021 focusAndSelectUrlBar();
michael@0 2022
michael@0 2023 // workaround for bug 345399
michael@0 2024 this.tabContainer.mTabstrip._updateScrollButtonsDisabledState();
michael@0 2025 }
michael@0 2026
michael@0 2027 // We're going to remove the tab and the browser now.
michael@0 2028 // Clean up mTabFilters and mTabListeners now rather than in
michael@0 2029 // _beginRemoveTab, so that their size is always in sync with the
michael@0 2030 // number of tabs and browsers (the xbl destructor depends on this).
michael@0 2031 this.mTabFilters.splice(aTab._tPos, 1);
michael@0 2032 this.mTabListeners.splice(aTab._tPos, 1);
michael@0 2033
michael@0 2034 var browser = this.getBrowserForTab(aTab);
michael@0 2035
michael@0 2036 // Because of the way XBL works (fields just set JS
michael@0 2037 // properties on the element) and the code we have in place
michael@0 2038 // to preserve the JS objects for any elements that have
michael@0 2039 // JS properties set on them, the browser element won't be
michael@0 2040 // destroyed until the document goes away. So we force a
michael@0 2041 // cleanup ourselves.
michael@0 2042 // This has to happen before we remove the child so that the
michael@0 2043 // XBL implementation of nsIObserver still works.
michael@0 2044 browser.destroy();
michael@0 2045
michael@0 2046 var wasPinned = aTab.pinned;
michael@0 2047
michael@0 2048 // Invalidate browsers cache, as the tab is removed from the
michael@0 2049 // tab container.
michael@0 2050 this._browsers = null;
michael@0 2051
michael@0 2052 // Remove the tab ...
michael@0 2053 this.tabContainer.removeChild(aTab);
michael@0 2054
michael@0 2055 // ... and fix up the _tPos properties immediately.
michael@0 2056 for (let i = aTab._tPos; i < this.tabs.length; i++)
michael@0 2057 this.tabs[i]._tPos = i;
michael@0 2058
michael@0 2059 if (!this._windowIsClosing) {
michael@0 2060 if (wasPinned)
michael@0 2061 this.tabContainer._positionPinnedTabs();
michael@0 2062
michael@0 2063 // update tab close buttons state
michael@0 2064 this.tabContainer.adjustTabstrip();
michael@0 2065
michael@0 2066 setTimeout(function(tabs) {
michael@0 2067 tabs._lastTabClosedByMouse = false;
michael@0 2068 }, 0, this.tabContainer);
michael@0 2069 }
michael@0 2070
michael@0 2071 // update tab positional properties and attributes
michael@0 2072 this.selectedTab._selected = true;
michael@0 2073 this.tabContainer._setPositionalAttributes();
michael@0 2074
michael@0 2075 // Removing the panel requires fixing up selectedPanel immediately
michael@0 2076 // (see below), which would be hindered by the potentially expensive
michael@0 2077 // browser removal. So we remove the browser and the panel in two
michael@0 2078 // steps.
michael@0 2079
michael@0 2080 var panel = this.getNotificationBox(browser);
michael@0 2081
michael@0 2082 // This will unload the document. An unload handler could remove
michael@0 2083 // dependant tabs, so it's important that the tabbrowser is now in
michael@0 2084 // a consistent state (tab removed, tab positions updated, etc.).
michael@0 2085 browser.parentNode.removeChild(browser);
michael@0 2086
michael@0 2087 // Release the browser in case something is erroneously holding a
michael@0 2088 // reference to the tab after its removal.
michael@0 2089 aTab.linkedBrowser = null;
michael@0 2090
michael@0 2091 // As the browser is removed, the removal of a dependent document can
michael@0 2092 // cause the whole window to close. So at this point, it's possible
michael@0 2093 // that the binding is destructed.
michael@0 2094 if (this.mTabBox) {
michael@0 2095 let selectedPanel = this.mTabBox.selectedPanel;
michael@0 2096
michael@0 2097 this.mPanelContainer.removeChild(panel);
michael@0 2098
michael@0 2099 // Under the hood, a selectedIndex attribute controls which panel
michael@0 2100 // is displayed. Removing a panel A which precedes the selected
michael@0 2101 // panel B makes selectedIndex point to the panel next to B. We
michael@0 2102 // need to explicitly preserve B as the selected panel.
michael@0 2103 this.mTabBox.selectedPanel = selectedPanel;
michael@0 2104 }
michael@0 2105
michael@0 2106 if (aCloseWindow)
michael@0 2107 this._windowIsClosing = closeWindow(true, window.warnAboutClosingWindow);
michael@0 2108 ]]>
michael@0 2109 </body>
michael@0 2110 </method>
michael@0 2111
michael@0 2112 <method name="_blurTab">
michael@0 2113 <parameter name="aTab"/>
michael@0 2114 <body>
michael@0 2115 <![CDATA[
michael@0 2116 if (!aTab.selected)
michael@0 2117 return;
michael@0 2118
michael@0 2119 if (aTab.owner &&
michael@0 2120 !aTab.owner.hidden &&
michael@0 2121 !aTab.owner.closing &&
michael@0 2122 Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
michael@0 2123 this.selectedTab = aTab.owner;
michael@0 2124 return;
michael@0 2125 }
michael@0 2126
michael@0 2127 // Switch to a visible tab unless there aren't any others remaining
michael@0 2128 let remainingTabs = this.visibleTabs;
michael@0 2129 let numTabs = remainingTabs.length;
michael@0 2130 if (numTabs == 0 || numTabs == 1 && remainingTabs[0] == aTab) {
michael@0 2131 remainingTabs = Array.filter(this.tabs, function(tab) {
michael@0 2132 return !tab.closing;
michael@0 2133 }, this);
michael@0 2134 }
michael@0 2135
michael@0 2136 // Try to find a remaining tab that comes after the given tab
michael@0 2137 var tab = aTab;
michael@0 2138 do {
michael@0 2139 tab = tab.nextSibling;
michael@0 2140 } while (tab && remainingTabs.indexOf(tab) == -1);
michael@0 2141
michael@0 2142 if (!tab) {
michael@0 2143 tab = aTab;
michael@0 2144
michael@0 2145 do {
michael@0 2146 tab = tab.previousSibling;
michael@0 2147 } while (tab && remainingTabs.indexOf(tab) == -1);
michael@0 2148 }
michael@0 2149
michael@0 2150 this.selectedTab = tab;
michael@0 2151 ]]>
michael@0 2152 </body>
michael@0 2153 </method>
michael@0 2154
michael@0 2155 <method name="swapNewTabWithBrowser">
michael@0 2156 <parameter name="aNewTab"/>
michael@0 2157 <parameter name="aBrowser"/>
michael@0 2158 <body>
michael@0 2159 <![CDATA[
michael@0 2160 // The browser must be standalone.
michael@0 2161 if (aBrowser.getTabBrowser())
michael@0 2162 throw Cr.NS_ERROR_INVALID_ARG;
michael@0 2163
michael@0 2164 // The tab is definitely not loading.
michael@0 2165 aNewTab.removeAttribute("busy");
michael@0 2166 if (aNewTab.selected) {
michael@0 2167 this.mIsBusy = false;
michael@0 2168 }
michael@0 2169
michael@0 2170 this._swapBrowserDocShells(aNewTab, aBrowser);
michael@0 2171
michael@0 2172 // Update the new tab's title.
michael@0 2173 this.setTabTitle(aNewTab);
michael@0 2174
michael@0 2175 if (aNewTab.selected) {
michael@0 2176 this.updateCurrentBrowser(true);
michael@0 2177 }
michael@0 2178 ]]>
michael@0 2179 </body>
michael@0 2180 </method>
michael@0 2181
michael@0 2182 <method name="swapBrowsersAndCloseOther">
michael@0 2183 <parameter name="aOurTab"/>
michael@0 2184 <parameter name="aOtherTab"/>
michael@0 2185 <body>
michael@0 2186 <![CDATA[
michael@0 2187 // Do not allow transfering a private tab to a non-private window
michael@0 2188 // and vice versa.
michael@0 2189 if (PrivateBrowsingUtils.isWindowPrivate(window) !=
michael@0 2190 PrivateBrowsingUtils.isWindowPrivate(aOtherTab.ownerDocument.defaultView))
michael@0 2191 return;
michael@0 2192
michael@0 2193 // That's gBrowser for the other window, not the tab's browser!
michael@0 2194 var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser;
michael@0 2195 var isPending = aOtherTab.hasAttribute("pending");
michael@0 2196
michael@0 2197 // First, start teardown of the other browser. Make sure to not
michael@0 2198 // fire the beforeunload event in the process. Close the other
michael@0 2199 // window if this was its last tab.
michael@0 2200 if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true))
michael@0 2201 return;
michael@0 2202
michael@0 2203 let ourBrowser = this.getBrowserForTab(aOurTab);
michael@0 2204 let otherBrowser = aOtherTab.linkedBrowser;
michael@0 2205
michael@0 2206 // If the other tab is pending (i.e. has not been restored, yet)
michael@0 2207 // then do not switch docShells but retrieve the other tab's state
michael@0 2208 // and apply it to our tab.
michael@0 2209 if (isPending) {
michael@0 2210 SessionStore.setTabState(aOurTab, SessionStore.getTabState(aOtherTab));
michael@0 2211
michael@0 2212 // Make sure to unregister any open URIs.
michael@0 2213 this._swapRegisteredOpenURIs(ourBrowser, otherBrowser);
michael@0 2214 } else {
michael@0 2215 // Workarounds for bug 458697
michael@0 2216 // Icon might have been set on DOMLinkAdded, don't override that.
michael@0 2217 if (!ourBrowser.mIconURL && otherBrowser.mIconURL)
michael@0 2218 this.setIcon(aOurTab, otherBrowser.mIconURL);
michael@0 2219 var isBusy = aOtherTab.hasAttribute("busy");
michael@0 2220 if (isBusy) {
michael@0 2221 aOurTab.setAttribute("busy", "true");
michael@0 2222 this._tabAttrModified(aOurTab);
michael@0 2223 if (aOurTab.selected)
michael@0 2224 this.mIsBusy = true;
michael@0 2225 }
michael@0 2226
michael@0 2227 this._swapBrowserDocShells(aOurTab, otherBrowser);
michael@0 2228 }
michael@0 2229
michael@0 2230 // Handle findbar data (if any)
michael@0 2231 let otherFindBar = aOtherTab._findBar;
michael@0 2232 if (otherFindBar &&
michael@0 2233 otherFindBar.findMode == otherFindBar.FIND_NORMAL) {
michael@0 2234 let ourFindBar = this.getFindBar(aOurTab);
michael@0 2235 ourFindBar._findField.value = otherFindBar._findField.value;
michael@0 2236 if (!otherFindBar.hidden)
michael@0 2237 ourFindBar.onFindCommand();
michael@0 2238 }
michael@0 2239
michael@0 2240 // Finish tearing down the tab that's going away.
michael@0 2241 remoteBrowser._endRemoveTab(aOtherTab);
michael@0 2242
michael@0 2243 if (isBusy)
michael@0 2244 this.setTabTitleLoading(aOurTab);
michael@0 2245 else
michael@0 2246 this.setTabTitle(aOurTab);
michael@0 2247
michael@0 2248 // If the tab was already selected (this happpens in the scenario
michael@0 2249 // of replaceTabWithWindow), notify onLocationChange, etc.
michael@0 2250 if (aOurTab.selected)
michael@0 2251 this.updateCurrentBrowser(true);
michael@0 2252 ]]>
michael@0 2253 </body>
michael@0 2254 </method>
michael@0 2255
michael@0 2256 <method name="_swapBrowserDocShells">
michael@0 2257 <parameter name="aOurTab"/>
michael@0 2258 <parameter name="aOtherBrowser"/>
michael@0 2259 <body>
michael@0 2260 <![CDATA[
michael@0 2261 // Unhook our progress listener
michael@0 2262 let index = aOurTab._tPos;
michael@0 2263 const filter = this.mTabFilters[index];
michael@0 2264 let tabListener = this.mTabListeners[index];
michael@0 2265 let ourBrowser = this.getBrowserForTab(aOurTab);
michael@0 2266 ourBrowser.webProgress.removeProgressListener(filter);
michael@0 2267 filter.removeProgressListener(tabListener);
michael@0 2268
michael@0 2269 // Make sure to unregister any open URIs.
michael@0 2270 this._swapRegisteredOpenURIs(ourBrowser, aOtherBrowser);
michael@0 2271
michael@0 2272 // Give others a chance to swap state.
michael@0 2273 let event = new CustomEvent("SwapDocShells", {"detail": aOtherBrowser});
michael@0 2274 ourBrowser.dispatchEvent(event);
michael@0 2275
michael@0 2276 // Swap the docshells
michael@0 2277 ourBrowser.swapDocShells(aOtherBrowser);
michael@0 2278
michael@0 2279 // Restore the progress listener
michael@0 2280 this.mTabListeners[index] = tabListener =
michael@0 2281 this.mTabProgressListener(aOurTab, ourBrowser, false);
michael@0 2282
michael@0 2283 const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
michael@0 2284 filter.addProgressListener(tabListener, notifyAll);
michael@0 2285 ourBrowser.webProgress.addProgressListener(filter, notifyAll);
michael@0 2286 ]]>
michael@0 2287 </body>
michael@0 2288 </method>
michael@0 2289
michael@0 2290 <method name="_swapRegisteredOpenURIs">
michael@0 2291 <parameter name="aOurBrowser"/>
michael@0 2292 <parameter name="aOtherBrowser"/>
michael@0 2293 <body>
michael@0 2294 <![CDATA[
michael@0 2295 // If the current URI is registered as open remove it from the list.
michael@0 2296 if (aOurBrowser.registeredOpenURI) {
michael@0 2297 this._placesAutocomplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
michael@0 2298 this._unifiedComplete.unregisterOpenPage(aOurBrowser.registeredOpenURI);
michael@0 2299 delete aOurBrowser.registeredOpenURI;
michael@0 2300 }
michael@0 2301
michael@0 2302 // If the other/new URI is registered as open then copy it over.
michael@0 2303 if (aOtherBrowser.registeredOpenURI) {
michael@0 2304 aOurBrowser.registeredOpenURI = aOtherBrowser.registeredOpenURI;
michael@0 2305 delete aOtherBrowser.registeredOpenURI;
michael@0 2306 }
michael@0 2307 ]]>
michael@0 2308 </body>
michael@0 2309 </method>
michael@0 2310
michael@0 2311 <method name="reloadAllTabs">
michael@0 2312 <body>
michael@0 2313 <![CDATA[
michael@0 2314 let tabs = this.visibleTabs;
michael@0 2315 let l = tabs.length;
michael@0 2316 for (var i = 0; i < l; i++) {
michael@0 2317 try {
michael@0 2318 this.getBrowserForTab(tabs[i]).reload();
michael@0 2319 } catch (e) {
michael@0 2320 // ignore failure to reload so others will be reloaded
michael@0 2321 }
michael@0 2322 }
michael@0 2323 ]]>
michael@0 2324 </body>
michael@0 2325 </method>
michael@0 2326
michael@0 2327 <method name="reloadTab">
michael@0 2328 <parameter name="aTab"/>
michael@0 2329 <body>
michael@0 2330 <![CDATA[
michael@0 2331 this.getBrowserForTab(aTab).reload();
michael@0 2332 ]]>
michael@0 2333 </body>
michael@0 2334 </method>
michael@0 2335
michael@0 2336 <method name="addProgressListener">
michael@0 2337 <parameter name="aListener"/>
michael@0 2338 <body>
michael@0 2339 <![CDATA[
michael@0 2340 if (arguments.length != 1) {
michael@0 2341 Components.utils.reportError("gBrowser.addProgressListener was " +
michael@0 2342 "called with a second argument, " +
michael@0 2343 "which is not supported. See bug " +
michael@0 2344 "608628. Call stack: " + new Error().stack);
michael@0 2345 }
michael@0 2346
michael@0 2347 this.mProgressListeners.push(aListener);
michael@0 2348 ]]>
michael@0 2349 </body>
michael@0 2350 </method>
michael@0 2351
michael@0 2352 <method name="removeProgressListener">
michael@0 2353 <parameter name="aListener"/>
michael@0 2354 <body>
michael@0 2355 <![CDATA[
michael@0 2356 this.mProgressListeners =
michael@0 2357 this.mProgressListeners.filter(function (l) l != aListener);
michael@0 2358 ]]>
michael@0 2359 </body>
michael@0 2360 </method>
michael@0 2361
michael@0 2362 <method name="addTabsProgressListener">
michael@0 2363 <parameter name="aListener"/>
michael@0 2364 <body>
michael@0 2365 this.mTabsProgressListeners.push(aListener);
michael@0 2366 </body>
michael@0 2367 </method>
michael@0 2368
michael@0 2369 <method name="removeTabsProgressListener">
michael@0 2370 <parameter name="aListener"/>
michael@0 2371 <body>
michael@0 2372 <![CDATA[
michael@0 2373 this.mTabsProgressListeners =
michael@0 2374 this.mTabsProgressListeners.filter(function (l) l != aListener);
michael@0 2375 ]]>
michael@0 2376 </body>
michael@0 2377 </method>
michael@0 2378
michael@0 2379 <method name="getBrowserForTab">
michael@0 2380 <parameter name="aTab"/>
michael@0 2381 <body>
michael@0 2382 <![CDATA[
michael@0 2383 return aTab.linkedBrowser;
michael@0 2384 ]]>
michael@0 2385 </body>
michael@0 2386 </method>
michael@0 2387
michael@0 2388 <method name="showOnlyTheseTabs">
michael@0 2389 <parameter name="aTabs"/>
michael@0 2390 <body>
michael@0 2391 <![CDATA[
michael@0 2392 Array.forEach(this.tabs, function(tab) {
michael@0 2393 if (aTabs.indexOf(tab) == -1)
michael@0 2394 this.hideTab(tab);
michael@0 2395 else
michael@0 2396 this.showTab(tab);
michael@0 2397 }, this);
michael@0 2398
michael@0 2399 this.tabContainer._handleTabSelect(false);
michael@0 2400 ]]>
michael@0 2401 </body>
michael@0 2402 </method>
michael@0 2403
michael@0 2404 <method name="showTab">
michael@0 2405 <parameter name="aTab"/>
michael@0 2406 <body>
michael@0 2407 <![CDATA[
michael@0 2408 if (aTab.hidden) {
michael@0 2409 aTab.removeAttribute("hidden");
michael@0 2410 this._visibleTabs = null; // invalidate cache
michael@0 2411
michael@0 2412 this.tabContainer.adjustTabstrip();
michael@0 2413
michael@0 2414 this.tabContainer._setPositionalAttributes();
michael@0 2415
michael@0 2416 let event = document.createEvent("Events");
michael@0 2417 event.initEvent("TabShow", true, false);
michael@0 2418 aTab.dispatchEvent(event);
michael@0 2419 }
michael@0 2420 ]]>
michael@0 2421 </body>
michael@0 2422 </method>
michael@0 2423
michael@0 2424 <method name="hideTab">
michael@0 2425 <parameter name="aTab"/>
michael@0 2426 <body>
michael@0 2427 <![CDATA[
michael@0 2428 if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
michael@0 2429 !aTab.closing) {
michael@0 2430 aTab.setAttribute("hidden", "true");
michael@0 2431 this._visibleTabs = null; // invalidate cache
michael@0 2432
michael@0 2433 this.tabContainer.adjustTabstrip();
michael@0 2434
michael@0 2435 this.tabContainer._setPositionalAttributes();
michael@0 2436
michael@0 2437 let event = document.createEvent("Events");
michael@0 2438 event.initEvent("TabHide", true, false);
michael@0 2439 aTab.dispatchEvent(event);
michael@0 2440 }
michael@0 2441 ]]>
michael@0 2442 </body>
michael@0 2443 </method>
michael@0 2444
michael@0 2445 <method name="selectTabAtIndex">
michael@0 2446 <parameter name="aIndex"/>
michael@0 2447 <parameter name="aEvent"/>
michael@0 2448 <body>
michael@0 2449 <![CDATA[
michael@0 2450 let tabs = this.visibleTabs;
michael@0 2451
michael@0 2452 // count backwards for aIndex < 0
michael@0 2453 if (aIndex < 0)
michael@0 2454 aIndex += tabs.length;
michael@0 2455
michael@0 2456 if (aIndex >= 0 && aIndex < tabs.length)
michael@0 2457 this.selectedTab = tabs[aIndex];
michael@0 2458
michael@0 2459 if (aEvent) {
michael@0 2460 aEvent.preventDefault();
michael@0 2461 aEvent.stopPropagation();
michael@0 2462 }
michael@0 2463 ]]>
michael@0 2464 </body>
michael@0 2465 </method>
michael@0 2466
michael@0 2467 <property name="selectedTab">
michael@0 2468 <getter>
michael@0 2469 return this.mCurrentTab;
michael@0 2470 </getter>
michael@0 2471 <setter>
michael@0 2472 <![CDATA[
michael@0 2473 // Update the tab
michael@0 2474 this.mTabBox.selectedTab = val;
michael@0 2475 return val;
michael@0 2476 ]]>
michael@0 2477 </setter>
michael@0 2478 </property>
michael@0 2479
michael@0 2480 <property name="selectedBrowser"
michael@0 2481 onget="return this.mCurrentBrowser;"
michael@0 2482 readonly="true"/>
michael@0 2483
michael@0 2484 <property name="browsers" readonly="true">
michael@0 2485 <getter>
michael@0 2486 <![CDATA[
michael@0 2487 return this._browsers ||
michael@0 2488 (this._browsers = Array.map(this.tabs, function (tab) tab.linkedBrowser));
michael@0 2489 ]]>
michael@0 2490 </getter>
michael@0 2491 </property>
michael@0 2492 <field name="_browsers">null</field>
michael@0 2493
michael@0 2494 <!-- Moves a tab to a new browser window, unless it's already the only tab
michael@0 2495 in the current window, in which case this will do nothing. -->
michael@0 2496 <method name="replaceTabWithWindow">
michael@0 2497 <parameter name="aTab"/>
michael@0 2498 <parameter name="aOptions"/>
michael@0 2499 <body>
michael@0 2500 <![CDATA[
michael@0 2501 if (this.tabs.length == 1)
michael@0 2502 return null;
michael@0 2503
michael@0 2504 let event = new CustomEvent("TabBecomingWindow", {
michael@0 2505 bubbles: true,
michael@0 2506 cancelable: true
michael@0 2507 });
michael@0 2508 aTab.dispatchEvent(event);
michael@0 2509 if (event.defaultPrevented) {
michael@0 2510 return null;
michael@0 2511 }
michael@0 2512
michael@0 2513 var options = "chrome,dialog=no,all";
michael@0 2514 for (var name in aOptions)
michael@0 2515 options += "," + name + "=" + aOptions[name];
michael@0 2516
michael@0 2517 // tell a new window to take the "dropped" tab
michael@0 2518 return window.openDialog(getBrowserURL(), "_blank", options, aTab);
michael@0 2519 ]]>
michael@0 2520 </body>
michael@0 2521 </method>
michael@0 2522
michael@0 2523 <method name="moveTabTo">
michael@0 2524 <parameter name="aTab"/>
michael@0 2525 <parameter name="aIndex"/>
michael@0 2526 <body>
michael@0 2527 <![CDATA[
michael@0 2528 var oldPosition = aTab._tPos;
michael@0 2529 if (oldPosition == aIndex)
michael@0 2530 return;
michael@0 2531
michael@0 2532 // Don't allow mixing pinned and unpinned tabs.
michael@0 2533 if (aTab.pinned)
michael@0 2534 aIndex = Math.min(aIndex, this._numPinnedTabs - 1);
michael@0 2535 else
michael@0 2536 aIndex = Math.max(aIndex, this._numPinnedTabs);
michael@0 2537 if (oldPosition == aIndex)
michael@0 2538 return;
michael@0 2539
michael@0 2540 this._lastRelatedTab = null;
michael@0 2541
michael@0 2542 this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
michael@0 2543 this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
michael@0 2544
michael@0 2545 let wasFocused = (document.activeElement == this.mCurrentTab);
michael@0 2546
michael@0 2547 aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
michael@0 2548 this.mCurrentTab._selected = false;
michael@0 2549
michael@0 2550 // invalidate caches
michael@0 2551 this._browsers = null;
michael@0 2552 this._visibleTabs = null;
michael@0 2553
michael@0 2554 // use .item() instead of [] because dragging to the end of the strip goes out of
michael@0 2555 // bounds: .item() returns null (so it acts like appendChild), but [] throws
michael@0 2556 this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
michael@0 2557
michael@0 2558 for (let i = 0; i < this.tabs.length; i++) {
michael@0 2559 this.tabs[i]._tPos = i;
michael@0 2560 this.tabs[i]._selected = false;
michael@0 2561 }
michael@0 2562 this.mCurrentTab._selected = true;
michael@0 2563
michael@0 2564 if (wasFocused)
michael@0 2565 this.mCurrentTab.focus();
michael@0 2566
michael@0 2567 this.tabContainer._handleTabSelect(false);
michael@0 2568
michael@0 2569 if (aTab.pinned)
michael@0 2570 this.tabContainer._positionPinnedTabs();
michael@0 2571
michael@0 2572 this.tabContainer._setPositionalAttributes();
michael@0 2573
michael@0 2574 var evt = document.createEvent("UIEvents");
michael@0 2575 evt.initUIEvent("TabMove", true, false, window, oldPosition);
michael@0 2576 aTab.dispatchEvent(evt);
michael@0 2577 ]]>
michael@0 2578 </body>
michael@0 2579 </method>
michael@0 2580
michael@0 2581 <method name="moveTabForward">
michael@0 2582 <body>
michael@0 2583 <![CDATA[
michael@0 2584 let nextTab = this.mCurrentTab.nextSibling;
michael@0 2585 while (nextTab && nextTab.hidden)
michael@0 2586 nextTab = nextTab.nextSibling;
michael@0 2587
michael@0 2588 if (nextTab)
michael@0 2589 this.moveTabTo(this.mCurrentTab, nextTab._tPos);
michael@0 2590 else if (this.arrowKeysShouldWrap)
michael@0 2591 this.moveTabToStart();
michael@0 2592 ]]>
michael@0 2593 </body>
michael@0 2594 </method>
michael@0 2595
michael@0 2596 <method name="moveTabBackward">
michael@0 2597 <body>
michael@0 2598 <![CDATA[
michael@0 2599 let previousTab = this.mCurrentTab.previousSibling;
michael@0 2600 while (previousTab && previousTab.hidden)
michael@0 2601 previousTab = previousTab.previousSibling;
michael@0 2602
michael@0 2603 if (previousTab)
michael@0 2604 this.moveTabTo(this.mCurrentTab, previousTab._tPos);
michael@0 2605 else if (this.arrowKeysShouldWrap)
michael@0 2606 this.moveTabToEnd();
michael@0 2607 ]]>
michael@0 2608 </body>
michael@0 2609 </method>
michael@0 2610
michael@0 2611 <method name="moveTabToStart">
michael@0 2612 <body>
michael@0 2613 <![CDATA[
michael@0 2614 var tabPos = this.mCurrentTab._tPos;
michael@0 2615 if (tabPos > 0)
michael@0 2616 this.moveTabTo(this.mCurrentTab, 0);
michael@0 2617 ]]>
michael@0 2618 </body>
michael@0 2619 </method>
michael@0 2620
michael@0 2621 <method name="moveTabToEnd">
michael@0 2622 <body>
michael@0 2623 <![CDATA[
michael@0 2624 var tabPos = this.mCurrentTab._tPos;
michael@0 2625 if (tabPos < this.browsers.length - 1)
michael@0 2626 this.moveTabTo(this.mCurrentTab, this.browsers.length - 1);
michael@0 2627 ]]>
michael@0 2628 </body>
michael@0 2629 </method>
michael@0 2630
michael@0 2631 <method name="moveTabOver">
michael@0 2632 <parameter name="aEvent"/>
michael@0 2633 <body>
michael@0 2634 <![CDATA[
michael@0 2635 var direction = window.getComputedStyle(this.parentNode, null).direction;
michael@0 2636 if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
michael@0 2637 (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT))
michael@0 2638 this.moveTabForward();
michael@0 2639 else
michael@0 2640 this.moveTabBackward();
michael@0 2641 ]]>
michael@0 2642 </body>
michael@0 2643 </method>
michael@0 2644
michael@0 2645 <method name="duplicateTab">
michael@0 2646 <parameter name="aTab"/><!-- can be from a different window as well -->
michael@0 2647 <body>
michael@0 2648 <![CDATA[
michael@0 2649 return SessionStore.duplicateTab(window, aTab);
michael@0 2650 ]]>
michael@0 2651 </body>
michael@0 2652 </method>
michael@0 2653
michael@0 2654 <!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
michael@0 2655 MAKE SURE TO ADD IT HERE AS WELL. -->
michael@0 2656 <property name="canGoBack"
michael@0 2657 onget="return this.mCurrentBrowser.canGoBack;"
michael@0 2658 readonly="true"/>
michael@0 2659
michael@0 2660 <property name="canGoForward"
michael@0 2661 onget="return this.mCurrentBrowser.canGoForward;"
michael@0 2662 readonly="true"/>
michael@0 2663
michael@0 2664 <method name="goBack">
michael@0 2665 <body>
michael@0 2666 <![CDATA[
michael@0 2667 return this.mCurrentBrowser.goBack();
michael@0 2668 ]]>
michael@0 2669 </body>
michael@0 2670 </method>
michael@0 2671
michael@0 2672 <method name="goForward">
michael@0 2673 <body>
michael@0 2674 <![CDATA[
michael@0 2675 return this.mCurrentBrowser.goForward();
michael@0 2676 ]]>
michael@0 2677 </body>
michael@0 2678 </method>
michael@0 2679
michael@0 2680 <method name="reload">
michael@0 2681 <body>
michael@0 2682 <![CDATA[
michael@0 2683 return this.mCurrentBrowser.reload();
michael@0 2684 ]]>
michael@0 2685 </body>
michael@0 2686 </method>
michael@0 2687
michael@0 2688 <method name="reloadWithFlags">
michael@0 2689 <parameter name="aFlags"/>
michael@0 2690 <body>
michael@0 2691 <![CDATA[
michael@0 2692 return this.mCurrentBrowser.reloadWithFlags(aFlags);
michael@0 2693 ]]>
michael@0 2694 </body>
michael@0 2695 </method>
michael@0 2696
michael@0 2697 <method name="stop">
michael@0 2698 <body>
michael@0 2699 <![CDATA[
michael@0 2700 return this.mCurrentBrowser.stop();
michael@0 2701 ]]>
michael@0 2702 </body>
michael@0 2703 </method>
michael@0 2704
michael@0 2705 <!-- throws exception for unknown schemes -->
michael@0 2706 <method name="loadURI">
michael@0 2707 <parameter name="aURI"/>
michael@0 2708 <parameter name="aReferrerURI"/>
michael@0 2709 <parameter name="aCharset"/>
michael@0 2710 <body>
michael@0 2711 <![CDATA[
michael@0 2712 #ifdef MAKE_E10S_WORK
michael@0 2713 this.updateBrowserRemoteness(this.mCurrentBrowser, aURI);
michael@0 2714 try {
michael@0 2715 #endif
michael@0 2716 return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
michael@0 2717 #ifdef MAKE_E10S_WORK
michael@0 2718 } catch (e) {
michael@0 2719 let url = this.mCurrentBrowser.currentURI.spec;
michael@0 2720 this.updateBrowserRemoteness(this.mCurrentBrowser, url);
michael@0 2721 throw e;
michael@0 2722 }
michael@0 2723 #endif
michael@0 2724 ]]>
michael@0 2725 </body>
michael@0 2726 </method>
michael@0 2727
michael@0 2728 <!-- throws exception for unknown schemes -->
michael@0 2729 <method name="loadURIWithFlags">
michael@0 2730 <parameter name="aURI"/>
michael@0 2731 <parameter name="aFlags"/>
michael@0 2732 <parameter name="aReferrerURI"/>
michael@0 2733 <parameter name="aCharset"/>
michael@0 2734 <parameter name="aPostData"/>
michael@0 2735 <body>
michael@0 2736 <![CDATA[
michael@0 2737 #ifdef MAKE_E10S_WORK
michael@0 2738 this.updateBrowserRemoteness(this.mCurrentBrowser, aURI);
michael@0 2739 try {
michael@0 2740 #endif
michael@0 2741 return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
michael@0 2742 #ifdef MAKE_E10S_WORK
michael@0 2743 } catch (e) {
michael@0 2744 let url = this.mCurrentBrowser.currentURI.spec;
michael@0 2745 this.updateBrowserRemoteness(this.mCurrentBrowser, url);
michael@0 2746 throw e;
michael@0 2747 }
michael@0 2748 #endif
michael@0 2749 ]]>
michael@0 2750 </body>
michael@0 2751 </method>
michael@0 2752
michael@0 2753 <method name="goHome">
michael@0 2754 <body>
michael@0 2755 <![CDATA[
michael@0 2756 return this.mCurrentBrowser.goHome();
michael@0 2757 ]]>
michael@0 2758 </body>
michael@0 2759 </method>
michael@0 2760
michael@0 2761 <property name="homePage">
michael@0 2762 <getter>
michael@0 2763 <![CDATA[
michael@0 2764 return this.mCurrentBrowser.homePage;
michael@0 2765 ]]>
michael@0 2766 </getter>
michael@0 2767 <setter>
michael@0 2768 <![CDATA[
michael@0 2769 this.mCurrentBrowser.homePage = val;
michael@0 2770 return val;
michael@0 2771 ]]>
michael@0 2772 </setter>
michael@0 2773 </property>
michael@0 2774
michael@0 2775 <method name="gotoIndex">
michael@0 2776 <parameter name="aIndex"/>
michael@0 2777 <body>
michael@0 2778 <![CDATA[
michael@0 2779 return this.mCurrentBrowser.gotoIndex(aIndex);
michael@0 2780 ]]>
michael@0 2781 </body>
michael@0 2782 </method>
michael@0 2783
michael@0 2784 <method name="attachFormFill">
michael@0 2785 <body><![CDATA[
michael@0 2786 for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
michael@0 2787 var cb = this.getBrowserAtIndex(i);
michael@0 2788 cb.attachFormFill();
michael@0 2789 }
michael@0 2790 ]]></body>
michael@0 2791 </method>
michael@0 2792
michael@0 2793 <method name="detachFormFill">
michael@0 2794 <body><![CDATA[
michael@0 2795 for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
michael@0 2796 var cb = this.getBrowserAtIndex(i);
michael@0 2797 cb.detachFormFill();
michael@0 2798 }
michael@0 2799 ]]></body>
michael@0 2800 </method>
michael@0 2801
michael@0 2802 <property name="currentURI"
michael@0 2803 onget="return this.mCurrentBrowser.currentURI;"
michael@0 2804 readonly="true"/>
michael@0 2805
michael@0 2806 <property name="finder"
michael@0 2807 onget="return this.mCurrentBrowser.finder"
michael@0 2808 readonly="true"/>
michael@0 2809
michael@0 2810 <property name="docShell"
michael@0 2811 onget="return this.mCurrentBrowser.docShell"
michael@0 2812 readonly="true"/>
michael@0 2813
michael@0 2814 <property name="webNavigation"
michael@0 2815 onget="return this.mCurrentBrowser.webNavigation"
michael@0 2816 readonly="true"/>
michael@0 2817
michael@0 2818 <property name="webBrowserFind"
michael@0 2819 readonly="true"
michael@0 2820 onget="return this.mCurrentBrowser.webBrowserFind"/>
michael@0 2821
michael@0 2822 <property name="webProgress"
michael@0 2823 readonly="true"
michael@0 2824 onget="return this.mCurrentBrowser.webProgress"/>
michael@0 2825
michael@0 2826 <property name="contentWindow"
michael@0 2827 readonly="true"
michael@0 2828 onget="return this.mCurrentBrowser.contentWindow"/>
michael@0 2829
michael@0 2830 <property name="sessionHistory"
michael@0 2831 onget="return this.mCurrentBrowser.sessionHistory;"
michael@0 2832 readonly="true"/>
michael@0 2833
michael@0 2834 <property name="markupDocumentViewer"
michael@0 2835 onget="return this.mCurrentBrowser.markupDocumentViewer;"
michael@0 2836 readonly="true"/>
michael@0 2837
michael@0 2838 <property name="contentViewerEdit"
michael@0 2839 onget="return this.mCurrentBrowser.contentViewerEdit;"
michael@0 2840 readonly="true"/>
michael@0 2841
michael@0 2842 <property name="contentViewerFile"
michael@0 2843 onget="return this.mCurrentBrowser.contentViewerFile;"
michael@0 2844 readonly="true"/>
michael@0 2845
michael@0 2846 <property name="contentDocument"
michael@0 2847 onget="return this.mCurrentBrowser.contentDocument;"
michael@0 2848 readonly="true"/>
michael@0 2849
michael@0 2850 <property name="contentTitle"
michael@0 2851 onget="return this.mCurrentBrowser.contentTitle;"
michael@0 2852 readonly="true"/>
michael@0 2853
michael@0 2854 <property name="contentPrincipal"
michael@0 2855 onget="return this.mCurrentBrowser.contentPrincipal;"
michael@0 2856 readonly="true"/>
michael@0 2857
michael@0 2858 <property name="securityUI"
michael@0 2859 onget="return this.mCurrentBrowser.securityUI;"
michael@0 2860 readonly="true"/>
michael@0 2861
michael@0 2862 <property name="fullZoom"
michael@0 2863 onget="return this.mCurrentBrowser.fullZoom;"
michael@0 2864 onset="this.mCurrentBrowser.fullZoom = val;"/>
michael@0 2865
michael@0 2866 <property name="textZoom"
michael@0 2867 onget="return this.mCurrentBrowser.textZoom;"
michael@0 2868 onset="this.mCurrentBrowser.textZoom = val;"/>
michael@0 2869
michael@0 2870 <property name="isSyntheticDocument"
michael@0 2871 onget="return this.mCurrentBrowser.isSyntheticDocument;"
michael@0 2872 readonly="true"/>
michael@0 2873
michael@0 2874 <method name="_handleKeyEvent">
michael@0 2875 <parameter name="aEvent"/>
michael@0 2876 <body><![CDATA[
michael@0 2877 if (!aEvent.isTrusted) {
michael@0 2878 // Don't let untrusted events mess with tabs.
michael@0 2879 return;
michael@0 2880 }
michael@0 2881
michael@0 2882 if (aEvent.altKey)
michael@0 2883 return;
michael@0 2884
michael@0 2885 if (aEvent.ctrlKey && aEvent.shiftKey && !aEvent.metaKey) {
michael@0 2886 switch (aEvent.keyCode) {
michael@0 2887 case aEvent.DOM_VK_PAGE_UP:
michael@0 2888 this.moveTabBackward();
michael@0 2889 aEvent.stopPropagation();
michael@0 2890 aEvent.preventDefault();
michael@0 2891 return;
michael@0 2892 case aEvent.DOM_VK_PAGE_DOWN:
michael@0 2893 this.moveTabForward();
michael@0 2894 aEvent.stopPropagation();
michael@0 2895 aEvent.preventDefault();
michael@0 2896 return;
michael@0 2897 }
michael@0 2898 }
michael@0 2899
michael@0 2900 // We need to take care of FAYT-watching as long as the findbar
michael@0 2901 // isn't initialized. The checks on aEvent are copied from
michael@0 2902 // _shouldFastFind (see findbar.xml).
michael@0 2903 if (!gFindBarInitialized &&
michael@0 2904 !(aEvent.ctrlKey || aEvent.metaKey) &&
michael@0 2905 !aEvent.defaultPrevented) {
michael@0 2906 let charCode = aEvent.charCode;
michael@0 2907 if (charCode) {
michael@0 2908 let char = String.fromCharCode(charCode);
michael@0 2909 if (char == "'" || char == "/" ||
michael@0 2910 Services.prefs.getBoolPref("accessibility.typeaheadfind")) {
michael@0 2911 gFindBar._onBrowserKeypress(aEvent);
michael@0 2912 return;
michael@0 2913 }
michael@0 2914 }
michael@0 2915 }
michael@0 2916
michael@0 2917 #ifdef XP_MACOSX
michael@0 2918 if (!aEvent.metaKey)
michael@0 2919 return;
michael@0 2920
michael@0 2921 var offset = 1;
michael@0 2922 switch (aEvent.charCode) {
michael@0 2923 case '}'.charCodeAt(0):
michael@0 2924 offset = -1;
michael@0 2925 case '{'.charCodeAt(0):
michael@0 2926 if (window.getComputedStyle(this, null).direction == "ltr")
michael@0 2927 offset *= -1;
michael@0 2928 this.tabContainer.advanceSelectedTab(offset, true);
michael@0 2929 aEvent.stopPropagation();
michael@0 2930 aEvent.preventDefault();
michael@0 2931 }
michael@0 2932 #else
michael@0 2933 if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
michael@0 2934 aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
michael@0 2935 !this.mCurrentTab.pinned) {
michael@0 2936 this.removeCurrentTab({animate: true});
michael@0 2937 aEvent.stopPropagation();
michael@0 2938 aEvent.preventDefault();
michael@0 2939 }
michael@0 2940 #endif
michael@0 2941 ]]></body>
michael@0 2942 </method>
michael@0 2943
michael@0 2944 <property name="userTypedClear"
michael@0 2945 onget="return this.mCurrentBrowser.userTypedClear;"
michael@0 2946 onset="return this.mCurrentBrowser.userTypedClear = val;"/>
michael@0 2947
michael@0 2948 <property name="userTypedValue"
michael@0 2949 onget="return this.mCurrentBrowser.userTypedValue;"
michael@0 2950 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
michael@0 2951
michael@0 2952 <method name="createTooltip">
michael@0 2953 <parameter name="event"/>
michael@0 2954 <body><![CDATA[
michael@0 2955 event.stopPropagation();
michael@0 2956 var tab = document.tooltipNode;
michael@0 2957 if (tab.localName != "tab") {
michael@0 2958 event.preventDefault();
michael@0 2959 return;
michael@0 2960 }
michael@0 2961 event.target.setAttribute("label", tab.mOverCloseButton ?
michael@0 2962 tab.getAttribute("closetabtext") :
michael@0 2963 tab.getAttribute("label"));
michael@0 2964 ]]></body>
michael@0 2965 </method>
michael@0 2966
michael@0 2967 <method name="handleEvent">
michael@0 2968 <parameter name="aEvent"/>
michael@0 2969 <body><![CDATA[
michael@0 2970 switch (aEvent.type) {
michael@0 2971 case "keypress":
michael@0 2972 this._handleKeyEvent(aEvent);
michael@0 2973 break;
michael@0 2974 case "sizemodechange":
michael@0 2975 if (aEvent.target == window) {
michael@0 2976 this.mCurrentBrowser.docShellIsActive =
michael@0 2977 (window.windowState != window.STATE_MINIMIZED);
michael@0 2978 }
michael@0 2979 break;
michael@0 2980 }
michael@0 2981 ]]></body>
michael@0 2982 </method>
michael@0 2983
michael@0 2984 <method name="receiveMessage">
michael@0 2985 <parameter name="aMessage"/>
michael@0 2986 <body><![CDATA[
michael@0 2987 let json = aMessage.json;
michael@0 2988 let browser = aMessage.target;
michael@0 2989
michael@0 2990 switch (aMessage.name) {
michael@0 2991 case "DOMTitleChanged": {
michael@0 2992 let tab = this._getTabForBrowser(browser);
michael@0 2993 if (!tab || tab.hasAttribute("pending"))
michael@0 2994 return;
michael@0 2995 let titleChanged = this.setTabTitle(tab);
michael@0 2996 if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
michael@0 2997 tab.setAttribute("titlechanged", "true");
michael@0 2998 break;
michael@0 2999 }
michael@0 3000 case "DOMWindowClose": {
michael@0 3001 if (this.tabs.length == 1) {
michael@0 3002 window.close();
michael@0 3003 return;
michael@0 3004 }
michael@0 3005
michael@0 3006 let tab = this._getTabForBrowser(browser);
michael@0 3007 if (tab) {
michael@0 3008 this.removeTab(tab);
michael@0 3009 }
michael@0 3010 break;
michael@0 3011 }
michael@0 3012 case "contextmenu": {
michael@0 3013 gContextMenuContentData = { event: aMessage.objects.event,
michael@0 3014 browser: browser };
michael@0 3015 let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
michael@0 3016 let event = gContextMenuContentData.event;
michael@0 3017 let pos = browser.mapScreenCoordinatesFromContent(event.screenX, event.screenY);
michael@0 3018 popup.openPopupAtScreen(pos.x, pos.y, true);
michael@0 3019 break;
michael@0 3020 }
michael@0 3021 case "DOMWebNotificationClicked": {
michael@0 3022 let tab = this._getTabForBrowser(browser);
michael@0 3023 if (!tab)
michael@0 3024 return;
michael@0 3025 this.selectedTab = tab;
michael@0 3026 window.focus();
michael@0 3027 break;
michael@0 3028 }
michael@0 3029 }
michael@0 3030 ]]></body>
michael@0 3031 </method>
michael@0 3032
michael@0 3033 <constructor>
michael@0 3034 <![CDATA[
michael@0 3035 let browserStack = document.getAnonymousElementByAttribute(this, "anonid", "browserStack");
michael@0 3036 this.mCurrentBrowser = document.getAnonymousElementByAttribute(this, "anonid", "initialBrowser");
michael@0 3037
michael@0 3038 this.mCurrentTab = this.tabContainer.firstChild;
michael@0 3039 document.addEventListener("keypress", this, false);
michael@0 3040 window.addEventListener("sizemodechange", this, false);
michael@0 3041
michael@0 3042 var uniqueId = this._generateUniquePanelID();
michael@0 3043 this.mPanelContainer.childNodes[0].id = uniqueId;
michael@0 3044 this.mCurrentTab.linkedPanel = uniqueId;
michael@0 3045 this.mCurrentTab._tPos = 0;
michael@0 3046 this.mCurrentTab._fullyOpen = true;
michael@0 3047 this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
michael@0 3048
michael@0 3049 // set up the shared autoscroll popup
michael@0 3050 this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
michael@0 3051 this._autoScrollPopup.id = "autoscroller";
michael@0 3052 this.appendChild(this._autoScrollPopup);
michael@0 3053 this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
michael@0 3054 this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
michael@0 3055 this.updateWindowResizers();
michael@0 3056
michael@0 3057 // Hook up the event listeners to the first browser
michael@0 3058 var tabListener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true);
michael@0 3059 const nsIWebProgress = Components.interfaces.nsIWebProgress;
michael@0 3060 const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
michael@0 3061 .createInstance(nsIWebProgress);
michael@0 3062 filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL);
michael@0 3063 this.mTabListeners[0] = tabListener;
michael@0 3064 this.mTabFilters[0] = filter;
michael@0 3065 this.webProgress.addProgressListener(filter, nsIWebProgress.NOTIFY_ALL);
michael@0 3066
michael@0 3067 this.style.backgroundColor =
michael@0 3068 Services.prefs.getBoolPref("browser.display.use_system_colors") ?
michael@0 3069 "-moz-default-background-color" :
michael@0 3070 Services.prefs.getCharPref("browser.display.background_color");
michael@0 3071
michael@0 3072 let remote = window.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 3073 .getInterface(Ci.nsIWebNavigation)
michael@0 3074 .QueryInterface(Ci.nsILoadContext)
michael@0 3075 .useRemoteTabs;
michael@0 3076 if (remote) {
michael@0 3077 messageManager.addMessageListener("DOMTitleChanged", this);
michael@0 3078 messageManager.addMessageListener("DOMWindowClose", this);
michael@0 3079 messageManager.addMessageListener("contextmenu", this);
michael@0 3080 }
michael@0 3081 messageManager.addMessageListener("DOMWebNotificationClicked", this);
michael@0 3082 ]]>
michael@0 3083 </constructor>
michael@0 3084
michael@0 3085 <method name="_generateUniquePanelID">
michael@0 3086 <body><![CDATA[
michael@0 3087 if (!this._uniquePanelIDCounter) {
michael@0 3088 this._uniquePanelIDCounter = 0;
michael@0 3089 }
michael@0 3090
michael@0 3091 let outerID = window.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 3092 .getInterface(Ci.nsIDOMWindowUtils)
michael@0 3093 .outerWindowID;
michael@0 3094
michael@0 3095 // We want panel IDs to be globally unique, that's why we include the
michael@0 3096 // window ID. We switched to a monotonic counter as Date.now() lead
michael@0 3097 // to random failures because of colliding IDs.
michael@0 3098 return "panel-" + outerID + "-" + (++this._uniquePanelIDCounter);
michael@0 3099 ]]></body>
michael@0 3100 </method>
michael@0 3101
michael@0 3102 <destructor>
michael@0 3103 <![CDATA[
michael@0 3104 for (var i = 0; i < this.mTabListeners.length; ++i) {
michael@0 3105 let browser = this.getBrowserAtIndex(i);
michael@0 3106 if (browser.registeredOpenURI) {
michael@0 3107 this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
michael@0 3108 this._unifiedComplete.unregisterOpenPage(browser.registeredOpenURI);
michael@0 3109 delete browser.registeredOpenURI;
michael@0 3110 }
michael@0 3111 browser.webProgress.removeProgressListener(this.mTabFilters[i]);
michael@0 3112 this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
michael@0 3113 this.mTabFilters[i] = null;
michael@0 3114 this.mTabListeners[i].destroy();
michael@0 3115 this.mTabListeners[i] = null;
michael@0 3116 }
michael@0 3117 document.removeEventListener("keypress", this, false);
michael@0 3118 window.removeEventListener("sizemodechange", this, false);
michael@0 3119
michael@0 3120 if (gMultiProcessBrowser) {
michael@0 3121 messageManager.removeMessageListener("DOMTitleChanged", this);
michael@0 3122 messageManager.removeMessageListener("contextmenu", this);
michael@0 3123 }
michael@0 3124 ]]>
michael@0 3125 </destructor>
michael@0 3126
michael@0 3127 <!-- Deprecated stuff, implemented for backwards compatibility. -->
michael@0 3128 <method name="enterTabbedMode">
michael@0 3129 <body>
michael@0 3130 Application.console.log("enterTabbedMode is an obsolete method and " +
michael@0 3131 "will be removed in a future release.");
michael@0 3132 </body>
michael@0 3133 </method>
michael@0 3134 <field name="mTabbedMode" readonly="true">true</field>
michael@0 3135 <method name="setStripVisibilityTo">
michael@0 3136 <parameter name="aShow"/>
michael@0 3137 <body>
michael@0 3138 this.tabContainer.visible = aShow;
michael@0 3139 </body>
michael@0 3140 </method>
michael@0 3141 <method name="getStripVisibility">
michael@0 3142 <body>
michael@0 3143 return this.tabContainer.visible;
michael@0 3144 </body>
michael@0 3145 </method>
michael@0 3146 <property name="mContextTab" readonly="true"
michael@0 3147 onget="return TabContextMenu.contextTab;"/>
michael@0 3148 <property name="mPrefs" readonly="true"
michael@0 3149 onget="return Services.prefs;"/>
michael@0 3150 <property name="mTabContainer" readonly="true"
michael@0 3151 onget="return this.tabContainer;"/>
michael@0 3152 <property name="mTabs" readonly="true"
michael@0 3153 onget="return this.tabs;"/>
michael@0 3154 <!--
michael@0 3155 - Compatibility hack: several extensions depend on this property to
michael@0 3156 - access the tab context menu or tab container, so keep that working for
michael@0 3157 - now. Ideally we can remove this once extensions are using
michael@0 3158 - tabbrowser.tabContextMenu and tabbrowser.tabContainer directly.
michael@0 3159 -->
michael@0 3160 <property name="mStrip" readonly="true">
michael@0 3161 <getter>
michael@0 3162 <![CDATA[
michael@0 3163 return ({
michael@0 3164 self: this,
michael@0 3165 childNodes: [null, this.tabContextMenu, this.tabContainer],
michael@0 3166 firstChild: { nextSibling: this.tabContextMenu },
michael@0 3167 getElementsByAttribute: function (attr, attrValue) {
michael@0 3168 if (attr == "anonid" && attrValue == "tabContextMenu")
michael@0 3169 return [this.self.tabContextMenu];
michael@0 3170 return [];
michael@0 3171 },
michael@0 3172 // Also support adding event listeners (forward to the tab container)
michael@0 3173 addEventListener: function (a,b,c) { this.self.tabContainer.addEventListener(a,b,c); },
michael@0 3174 removeEventListener: function (a,b,c) { this.self.tabContainer.removeEventListener(a,b,c); }
michael@0 3175 });
michael@0 3176 ]]>
michael@0 3177 </getter>
michael@0 3178 </property>
michael@0 3179 </implementation>
michael@0 3180
michael@0 3181 <handlers>
michael@0 3182 <handler event="DOMWindowClose" phase="capturing">
michael@0 3183 <![CDATA[
michael@0 3184 if (!event.isTrusted)
michael@0 3185 return;
michael@0 3186
michael@0 3187 if (this.tabs.length == 1)
michael@0 3188 return;
michael@0 3189
michael@0 3190 var tab = this._getTabForContentWindow(event.target);
michael@0 3191 if (tab) {
michael@0 3192 this.removeTab(tab);
michael@0 3193 event.preventDefault();
michael@0 3194 }
michael@0 3195 ]]>
michael@0 3196 </handler>
michael@0 3197 <handler event="DOMWillOpenModalDialog" phase="capturing">
michael@0 3198 <![CDATA[
michael@0 3199 if (!event.isTrusted)
michael@0 3200 return;
michael@0 3201
michael@0 3202 // We're about to open a modal dialog, make sure the opening
michael@0 3203 // tab is brought to the front.
michael@0 3204 // If this is a same-process modal dialog, then we're given its DOM
michael@0 3205 // window as the event's target. For remote dialogs, we're given the
michael@0 3206 // browser, but that's in the originalTarget.
michael@0 3207 // XXX Why originalTarget for the browser?
michael@0 3208 this.selectedTab = (event.target instanceof Window) ?
michael@0 3209 this._getTabForContentWindow(event.target.top) :
michael@0 3210 this._getTabForBrowser(event.originalTarget);
michael@0 3211 ]]>
michael@0 3212 </handler>
michael@0 3213 <handler event="DOMTitleChanged">
michael@0 3214 <![CDATA[
michael@0 3215 if (!event.isTrusted)
michael@0 3216 return;
michael@0 3217
michael@0 3218 var contentWin = event.target.defaultView;
michael@0 3219 if (contentWin != contentWin.top)
michael@0 3220 return;
michael@0 3221
michael@0 3222 var tab = this._getTabForContentWindow(contentWin);
michael@0 3223 if (tab.hasAttribute("pending"))
michael@0 3224 return;
michael@0 3225
michael@0 3226 var titleChanged = this.setTabTitle(tab);
michael@0 3227 if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
michael@0 3228 tab.setAttribute("titlechanged", "true");
michael@0 3229 ]]>
michael@0 3230 </handler>
michael@0 3231 <handler event="oop-browser-crashed">
michael@0 3232 <![CDATA[
michael@0 3233 if (!event.isTrusted)
michael@0 3234 return;
michael@0 3235
michael@0 3236 let browser = event.originalTarget;
michael@0 3237 let title = browser.contentTitle;
michael@0 3238 let uri = browser.currentURI;
michael@0 3239 let icon = browser.mIconURL;
michael@0 3240
michael@0 3241 this.updateBrowserRemoteness(browser, "about:tabcrashed");
michael@0 3242
michael@0 3243 browser.setAttribute("crashedPageTitle", title);
michael@0 3244 browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
michael@0 3245 browser.removeAttribute("crashedPageTitle");
michael@0 3246 let tab = this._getTabForBrowser(browser);
michael@0 3247 this.setIcon(tab, icon);
michael@0 3248 ]]>
michael@0 3249 </handler>
michael@0 3250 </handlers>
michael@0 3251 </binding>
michael@0 3252
michael@0 3253 <binding id="tabbrowser-tabbox"
michael@0 3254 extends="chrome://global/content/bindings/tabbox.xml#tabbox">
michael@0 3255 <implementation>
michael@0 3256 <property name="tabs" readonly="true"
michael@0 3257 onget="return document.getBindingParent(this).tabContainer;"/>
michael@0 3258 </implementation>
michael@0 3259 </binding>
michael@0 3260
michael@0 3261 <binding id="tabbrowser-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
michael@0 3262 <implementation>
michael@0 3263 <!-- Override scrollbox.xml method, since our scrollbox's children are
michael@0 3264 inherited from the binding parent -->
michael@0 3265 <method name="_getScrollableElements">
michael@0 3266 <body><![CDATA[
michael@0 3267 return Array.filter(document.getBindingParent(this).childNodes,
michael@0 3268 this._canScrollToElement, this);
michael@0 3269 ]]></body>
michael@0 3270 </method>
michael@0 3271 <method name="_canScrollToElement">
michael@0 3272 <parameter name="tab"/>
michael@0 3273 <body><![CDATA[
michael@0 3274 return !tab.pinned && !tab.hidden;
michael@0 3275 ]]></body>
michael@0 3276 </method>
michael@0 3277 <field name="_tabMarginLeft">null</field>
michael@0 3278 <field name="_tabMarginRight">null</field>
michael@0 3279 <method name="_calcTabMargins">
michael@0 3280 <parameter name="aTab"/>
michael@0 3281 <body><![CDATA[
michael@0 3282 if (this._tabMarginLeft === null || this._tabMarginRight === null) {
michael@0 3283 let tabMiddle = document.getAnonymousElementByAttribute(aTab, "class", "tab-background-middle");
michael@0 3284 let tabMiddleStyle = window.getComputedStyle(tabMiddle, null);
michael@0 3285 this._tabMarginLeft = parseFloat(tabMiddleStyle.marginLeft);
michael@0 3286 this._tabMarginRight = parseFloat(tabMiddleStyle.marginRight);
michael@0 3287 }
michael@0 3288 ]]></body>
michael@0 3289 </method>
michael@0 3290 <method name="_adjustElementStartAndEnd">
michael@0 3291 <parameter name="aTab"/>
michael@0 3292 <parameter name="tabStart"/>
michael@0 3293 <parameter name="tabEnd"/>
michael@0 3294 <body><![CDATA[
michael@0 3295 this._calcTabMargins(aTab);
michael@0 3296 if (this._tabMarginLeft < 0) {
michael@0 3297 tabStart = tabStart + this._tabMarginLeft;
michael@0 3298 }
michael@0 3299 if (this._tabMarginRight < 0) {
michael@0 3300 tabEnd = tabEnd - this._tabMarginRight;
michael@0 3301 }
michael@0 3302 return [tabStart, tabEnd];
michael@0 3303 ]]></body>
michael@0 3304 </method>
michael@0 3305 </implementation>
michael@0 3306
michael@0 3307 <handlers>
michael@0 3308 <handler event="underflow" phase="capturing"><![CDATA[
michael@0 3309 if (event.detail == 0)
michael@0 3310 return; // Ignore vertical events
michael@0 3311
michael@0 3312 var tabs = document.getBindingParent(this);
michael@0 3313 tabs.removeAttribute("overflow");
michael@0 3314
michael@0 3315 if (tabs._lastTabClosedByMouse)
michael@0 3316 tabs._expandSpacerBy(this._scrollButtonDown.clientWidth);
michael@0 3317
michael@0 3318 tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab,
michael@0 3319 tabs.tabbrowser);
michael@0 3320
michael@0 3321 tabs._positionPinnedTabs();
michael@0 3322 ]]></handler>
michael@0 3323 <handler event="overflow"><![CDATA[
michael@0 3324 if (event.detail == 0)
michael@0 3325 return; // Ignore vertical events
michael@0 3326 var tabs = document.getBindingParent(this);
michael@0 3327 var numberOfTabs = tabs.tabbrowser.visibleTabs.length;
michael@0 3328 if (numberOfTabs == 1)
michael@0 3329 return;
michael@0 3330
michael@0 3331 tabs.setAttribute("overflow", "true");
michael@0 3332 tabs._positionPinnedTabs();
michael@0 3333 tabs._handleTabSelect(false);
michael@0 3334 ]]></handler>
michael@0 3335 </handlers>
michael@0 3336 </binding>
michael@0 3337
michael@0 3338 <binding id="tabbrowser-tabs"
michael@0 3339 extends="chrome://global/content/bindings/tabbox.xml#tabs">
michael@0 3340 <resources>
michael@0 3341 <stylesheet src="chrome://browser/content/tabbrowser.css"/>
michael@0 3342 </resources>
michael@0 3343
michael@0 3344 <content>
michael@0 3345 <xul:hbox align="end">
michael@0 3346 <xul:image class="tab-drop-indicator" anonid="tab-drop-indicator" collapsed="true"/>
michael@0 3347 </xul:hbox>
michael@0 3348 <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
michael@0 3349 style="min-width: 1px;"
michael@0 3350 #ifndef XP_MACOSX
michael@0 3351 clicktoscroll="true"
michael@0 3352 #endif
michael@0 3353 class="tabbrowser-arrowscrollbox">
michael@0 3354 # This is a hack to circumvent bug 472020, otherwise the tabs show up on the
michael@0 3355 # right of the newtab button.
michael@0 3356 <children includes="tab"/>
michael@0 3357 # This is to ensure anything extensions put here will go before the newtab
michael@0 3358 # button, necessary due to the previous hack.
michael@0 3359 <children/>
michael@0 3360 <xul:toolbarbutton class="tabs-newtab-button"
michael@0 3361 command="cmd_newNavigatorTab"
michael@0 3362 onclick="checkForMiddleClick(this, event);"
michael@0 3363 onmouseover="document.getBindingParent(this)._enterNewTab();"
michael@0 3364 onmouseout="document.getBindingParent(this)._leaveNewTab();"
michael@0 3365 tooltiptext="&newTabButton.tooltip;"/>
michael@0 3366 <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
michael@0 3367 style="width: 0;"/>
michael@0 3368 </xul:arrowscrollbox>
michael@0 3369 </content>
michael@0 3370
michael@0 3371 <implementation implements="nsIDOMEventListener">
michael@0 3372 <constructor>
michael@0 3373 <![CDATA[
michael@0 3374 this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
michael@0 3375
michael@0 3376 var tab = this.firstChild;
michael@0 3377 tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
michael@0 3378 tab.setAttribute("crop", "end");
michael@0 3379 tab.setAttribute("onerror", "this.removeAttribute('image');");
michael@0 3380
michael@0 3381 window.addEventListener("resize", this, false);
michael@0 3382 window.addEventListener("load", this, false);
michael@0 3383
michael@0 3384 try {
michael@0 3385 this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled");
michael@0 3386 } catch (ex) {
michael@0 3387 this._tabAnimationLoggingEnabled = false;
michael@0 3388 }
michael@0 3389 this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled");
michael@0 3390 ]]>
michael@0 3391 </constructor>
michael@0 3392
michael@0 3393 <field name="tabbrowser" readonly="true">
michael@0 3394 document.getElementById(this.getAttribute("tabbrowser"));
michael@0 3395 </field>
michael@0 3396
michael@0 3397 <field name="tabbox" readonly="true">
michael@0 3398 this.tabbrowser.mTabBox;
michael@0 3399 </field>
michael@0 3400
michael@0 3401 <field name="contextMenu" readonly="true">
michael@0 3402 document.getElementById("tabContextMenu");
michael@0 3403 </field>
michael@0 3404
michael@0 3405 <field name="mTabstripWidth">0</field>
michael@0 3406
michael@0 3407 <field name="mTabstrip">
michael@0 3408 document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
michael@0 3409 </field>
michael@0 3410
michael@0 3411 <field name="_firstTab">null</field>
michael@0 3412 <field name="_lastTab">null</field>
michael@0 3413 <field name="_afterSelectedTab">null</field>
michael@0 3414 <field name="_beforeHoveredTab">null</field>
michael@0 3415 <field name="_afterHoveredTab">null</field>
michael@0 3416 <field name="_hoveredTab">null</field>
michael@0 3417
michael@0 3418 <property name="_isCustomizing" readonly="true">
michael@0 3419 <getter>
michael@0 3420 let root = document.documentElement;
michael@0 3421 return root.getAttribute("customizing") == "true" ||
michael@0 3422 root.getAttribute("customize-exiting") == "true";
michael@0 3423 </getter>
michael@0 3424 </property>
michael@0 3425
michael@0 3426 <method name="_setPositionalAttributes">
michael@0 3427 <body><![CDATA[
michael@0 3428 let visibleTabs = this.tabbrowser.visibleTabs;
michael@0 3429
michael@0 3430 if (!visibleTabs.length)
michael@0 3431 return;
michael@0 3432
michael@0 3433 let selectedIndex = visibleTabs.indexOf(this.selectedItem);
michael@0 3434
michael@0 3435 let lastVisible = visibleTabs.length - 1;
michael@0 3436
michael@0 3437 if (this._afterSelectedTab)
michael@0 3438 this._afterSelectedTab.removeAttribute("afterselected-visible");
michael@0 3439 if (this.selectedItem.closing || selectedIndex == lastVisible) {
michael@0 3440 this._afterSelectedTab = null;
michael@0 3441 } else {
michael@0 3442 this._afterSelectedTab = visibleTabs[selectedIndex + 1];
michael@0 3443 this._afterSelectedTab.setAttribute("afterselected-visible",
michael@0 3444 "true");
michael@0 3445 }
michael@0 3446
michael@0 3447 if (this._firstTab)
michael@0 3448 this._firstTab.removeAttribute("first-visible-tab");
michael@0 3449 this._firstTab = visibleTabs[0];
michael@0 3450 this._firstTab.setAttribute("first-visible-tab", "true");
michael@0 3451 if (this._lastTab)
michael@0 3452 this._lastTab.removeAttribute("last-visible-tab");
michael@0 3453 this._lastTab = visibleTabs[lastVisible];
michael@0 3454 this._lastTab.setAttribute("last-visible-tab", "true");
michael@0 3455
michael@0 3456 let hoveredTab = this._hoveredTab;
michael@0 3457 if (hoveredTab) {
michael@0 3458 hoveredTab._mouseleave();
michael@0 3459 hoveredTab._mouseenter();
michael@0 3460 }
michael@0 3461 ]]></body>
michael@0 3462 </method>
michael@0 3463
michael@0 3464 <field name="_blockDblClick">false</field>
michael@0 3465
michael@0 3466 <field name="_tabDropIndicator">
michael@0 3467 document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
michael@0 3468 </field>
michael@0 3469
michael@0 3470 <field name="_dragOverDelay">350</field>
michael@0 3471 <field name="_dragTime">0</field>
michael@0 3472
michael@0 3473 <field name="_container" readonly="true"><![CDATA[
michael@0 3474 this.parentNode && this.parentNode.localName == "toolbar" ? this.parentNode : this;
michael@0 3475 ]]></field>
michael@0 3476
michael@0 3477 <field name="_propagatedVisibilityOnce">false</field>
michael@0 3478
michael@0 3479 <property name="visible"
michael@0 3480 onget="return !this._container.collapsed;">
michael@0 3481 <setter><![CDATA[
michael@0 3482 if (val == this.visible &&
michael@0 3483 this._propagatedVisibilityOnce)
michael@0 3484 return val;
michael@0 3485
michael@0 3486 this._container.collapsed = !val;
michael@0 3487
michael@0 3488 this._propagateVisibility();
michael@0 3489 this._propagatedVisibilityOnce = true;
michael@0 3490
michael@0 3491 return val;
michael@0 3492 ]]></setter>
michael@0 3493 </property>
michael@0 3494
michael@0 3495 <method name="_enterNewTab">
michael@0 3496 <body><![CDATA[
michael@0 3497 let visibleTabs = this.tabbrowser.visibleTabs;
michael@0 3498 let candidate = visibleTabs[visibleTabs.length - 1];
michael@0 3499 if (!candidate.selected) {
michael@0 3500 this._beforeHoveredTab = candidate;
michael@0 3501 candidate.setAttribute("beforehovered", "true");
michael@0 3502 }
michael@0 3503 ]]></body>
michael@0 3504 </method>
michael@0 3505
michael@0 3506 <method name="_leaveNewTab">
michael@0 3507 <body><![CDATA[
michael@0 3508 if (this._beforeHoveredTab) {
michael@0 3509 this._beforeHoveredTab.removeAttribute("beforehovered");
michael@0 3510 this._beforeHoveredTab = null;
michael@0 3511 }
michael@0 3512 ]]></body>
michael@0 3513 </method>
michael@0 3514
michael@0 3515 <method name="_propagateVisibility">
michael@0 3516 <body><![CDATA[
michael@0 3517 let visible = this.visible;
michael@0 3518
michael@0 3519 document.getElementById("menu_closeWindow").hidden = !visible;
michael@0 3520 document.getElementById("menu_close").setAttribute("label",
michael@0 3521 this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
michael@0 3522
michael@0 3523 TabsInTitlebar.allowedBy("tabs-visible", visible);
michael@0 3524 ]]></body>
michael@0 3525 </method>
michael@0 3526
michael@0 3527 <method name="updateVisibility">
michael@0 3528 <body><![CDATA[
michael@0 3529 if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
michael@0 3530 this.visible = window.toolbar.visible;
michael@0 3531 else
michael@0 3532 this.visible = true;
michael@0 3533 ]]></body>
michael@0 3534 </method>
michael@0 3535
michael@0 3536 <method name="adjustTabstrip">
michael@0 3537 <body><![CDATA[
michael@0 3538 let numTabs = this.childNodes.length -
michael@0 3539 this.tabbrowser._removingTabs.length;
michael@0 3540 if (numTabs > 2) {
michael@0 3541 // This is an optimization to avoid layout flushes by calling
michael@0 3542 // getBoundingClientRect() when we just opened a second tab. In
michael@0 3543 // this case it's highly unlikely that the tab width is smaller
michael@0 3544 // than mTabClipWidth and the tab close button obscures too much
michael@0 3545 // of the tab's label. In the edge case of the window being too
michael@0 3546 // narrow (or if tabClipWidth has been set to a way higher value),
michael@0 3547 // we'll correct the 'closebuttons' attribute after the tabopen
michael@0 3548 // animation has finished.
michael@0 3549
michael@0 3550 let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
michael@0 3551 if (tab && tab.getBoundingClientRect().width <= this.mTabClipWidth) {
michael@0 3552 this.setAttribute("closebuttons", "activetab");
michael@0 3553 return;
michael@0 3554 }
michael@0 3555 }
michael@0 3556 this.removeAttribute("closebuttons");
michael@0 3557 ]]></body>
michael@0 3558 </method>
michael@0 3559
michael@0 3560 <method name="_handleTabSelect">
michael@0 3561 <parameter name="aSmoothScroll"/>
michael@0 3562 <body><![CDATA[
michael@0 3563 if (this.getAttribute("overflow") == "true")
michael@0 3564 this.mTabstrip.ensureElementIsVisible(this.selectedItem, aSmoothScroll);
michael@0 3565 ]]></body>
michael@0 3566 </method>
michael@0 3567
michael@0 3568 <method name="_fillTrailingGap">
michael@0 3569 <body><![CDATA[
michael@0 3570 try {
michael@0 3571 // if we're at the right side (and not the logical end,
michael@0 3572 // which is why this works for both LTR and RTL)
michael@0 3573 // of the tabstrip, we need to ensure that we stay
michael@0 3574 // completely scrolled to the right side
michael@0 3575 var tabStrip = this.mTabstrip;
michael@0 3576 if (tabStrip.scrollPosition + tabStrip.scrollClientSize >
michael@0 3577 tabStrip.scrollSize)
michael@0 3578 tabStrip.scrollByPixels(-1);
michael@0 3579 } catch (e) {}
michael@0 3580 ]]></body>
michael@0 3581 </method>
michael@0 3582
michael@0 3583 <field name="_closingTabsSpacer">
michael@0 3584 document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer");
michael@0 3585 </field>
michael@0 3586
michael@0 3587 <field name="_tabDefaultMaxWidth">NaN</field>
michael@0 3588 <field name="_lastTabClosedByMouse">false</field>
michael@0 3589 <field name="_hasTabTempMaxWidth">false</field>
michael@0 3590
michael@0 3591 <!-- Try to keep the active tab's close button under the mouse cursor -->
michael@0 3592 <method name="_lockTabSizing">
michael@0 3593 <parameter name="aTab"/>
michael@0 3594 <body><![CDATA[
michael@0 3595 var tabs = this.tabbrowser.visibleTabs;
michael@0 3596 if (!tabs.length)
michael@0 3597 return;
michael@0 3598
michael@0 3599 var isEndTab = (aTab._tPos > tabs[tabs.length-1]._tPos);
michael@0 3600 var tabWidth = aTab.getBoundingClientRect().width;
michael@0 3601
michael@0 3602 if (!this._tabDefaultMaxWidth)
michael@0 3603 this._tabDefaultMaxWidth =
michael@0 3604 parseFloat(window.getComputedStyle(aTab).maxWidth);
michael@0 3605 this._lastTabClosedByMouse = true;
michael@0 3606
michael@0 3607 if (this.getAttribute("overflow") == "true") {
michael@0 3608 // Don't need to do anything if we're in overflow mode and aren't scrolled
michael@0 3609 // all the way to the right, or if we're closing the last tab.
michael@0 3610 if (isEndTab || !this.mTabstrip._scrollButtonDown.disabled)
michael@0 3611 return;
michael@0 3612
michael@0 3613 // If the tab has an owner that will become the active tab, the owner will
michael@0 3614 // be to the left of it, so we actually want the left tab to slide over.
michael@0 3615 // This can't be done as easily in non-overflow mode, so we don't bother.
michael@0 3616 if (aTab.owner)
michael@0 3617 return;
michael@0 3618
michael@0 3619 this._expandSpacerBy(tabWidth);
michael@0 3620 } else { // non-overflow mode
michael@0 3621 // Locking is neither in effect nor needed, so let tabs expand normally.
michael@0 3622 if (isEndTab && !this._hasTabTempMaxWidth)
michael@0 3623 return;
michael@0 3624
michael@0 3625 let numPinned = this.tabbrowser._numPinnedTabs;
michael@0 3626 // Force tabs to stay the same width, unless we're closing the last tab,
michael@0 3627 // which case we need to let them expand just enough so that the overall
michael@0 3628 // tabbar width is the same.
michael@0 3629 if (isEndTab) {
michael@0 3630 let numNormalTabs = tabs.length - numPinned;
michael@0 3631 tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs;
michael@0 3632 if (tabWidth > this._tabDefaultMaxWidth)
michael@0 3633 tabWidth = this._tabDefaultMaxWidth;
michael@0 3634 }
michael@0 3635 tabWidth += "px";
michael@0 3636 for (let i = numPinned; i < tabs.length; i++) {
michael@0 3637 let tab = tabs[i];
michael@0 3638 tab.style.setProperty("max-width", tabWidth, "important");
michael@0 3639 if (!isEndTab) { // keep tabs the same width
michael@0 3640 tab.style.transition = "none";
michael@0 3641 tab.clientTop; // flush styles to skip animation; see bug 649247
michael@0 3642 tab.style.transition = "";
michael@0 3643 }
michael@0 3644 }
michael@0 3645 this._hasTabTempMaxWidth = true;
michael@0 3646 this.tabbrowser.addEventListener("mousemove", this, false);
michael@0 3647 window.addEventListener("mouseout", this, false);
michael@0 3648 }
michael@0 3649 ]]></body>
michael@0 3650 </method>
michael@0 3651
michael@0 3652 <method name="_expandSpacerBy">
michael@0 3653 <parameter name="pixels"/>
michael@0 3654 <body><![CDATA[
michael@0 3655 let spacer = this._closingTabsSpacer;
michael@0 3656 spacer.style.width = parseFloat(spacer.style.width) + pixels + "px";
michael@0 3657 this.setAttribute("using-closing-tabs-spacer", "true");
michael@0 3658 this.tabbrowser.addEventListener("mousemove", this, false);
michael@0 3659 window.addEventListener("mouseout", this, false);
michael@0 3660 ]]></body>
michael@0 3661 </method>
michael@0 3662
michael@0 3663 <method name="_unlockTabSizing">
michael@0 3664 <body><![CDATA[
michael@0 3665 this.tabbrowser.removeEventListener("mousemove", this, false);
michael@0 3666 window.removeEventListener("mouseout", this, false);
michael@0 3667
michael@0 3668 if (this._hasTabTempMaxWidth) {
michael@0 3669 this._hasTabTempMaxWidth = false;
michael@0 3670 let tabs = this.tabbrowser.visibleTabs;
michael@0 3671 for (let i = 0; i < tabs.length; i++)
michael@0 3672 tabs[i].style.maxWidth = "";
michael@0 3673 }
michael@0 3674
michael@0 3675 if (this.hasAttribute("using-closing-tabs-spacer")) {
michael@0 3676 this.removeAttribute("using-closing-tabs-spacer");
michael@0 3677 this._closingTabsSpacer.style.width = 0;
michael@0 3678 }
michael@0 3679 ]]></body>
michael@0 3680 </method>
michael@0 3681
michael@0 3682 <field name="_lastNumPinned">0</field>
michael@0 3683 <method name="_positionPinnedTabs">
michael@0 3684 <body><![CDATA[
michael@0 3685 var numPinned = this.tabbrowser._numPinnedTabs;
michael@0 3686 var doPosition = this.getAttribute("overflow") == "true" &&
michael@0 3687 numPinned > 0;
michael@0 3688
michael@0 3689 if (doPosition) {
michael@0 3690 this.setAttribute("positionpinnedtabs", "true");
michael@0 3691
michael@0 3692 let scrollButtonWidth = this.mTabstrip._scrollButtonDown.getBoundingClientRect().width;
michael@0 3693 let paddingStart = this.mTabstrip.scrollboxPaddingStart;
michael@0 3694 let width = 0;
michael@0 3695
michael@0 3696 for (let i = numPinned - 1; i >= 0; i--) {
michael@0 3697 let tab = this.childNodes[i];
michael@0 3698 width += tab.getBoundingClientRect().width;
michael@0 3699 tab.style.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px";
michael@0 3700 }
michael@0 3701
michael@0 3702 this.style.MozPaddingStart = width + paddingStart + "px";
michael@0 3703
michael@0 3704 } else {
michael@0 3705 this.removeAttribute("positionpinnedtabs");
michael@0 3706
michael@0 3707 for (let i = 0; i < numPinned; i++) {
michael@0 3708 let tab = this.childNodes[i];
michael@0 3709 tab.style.MozMarginStart = "";
michael@0 3710 }
michael@0 3711
michael@0 3712 this.style.MozPaddingStart = "";
michael@0 3713 }
michael@0 3714
michael@0 3715 if (this._lastNumPinned != numPinned) {
michael@0 3716 this._lastNumPinned = numPinned;
michael@0 3717 this._handleTabSelect(false);
michael@0 3718 }
michael@0 3719 ]]></body>
michael@0 3720 </method>
michael@0 3721
michael@0 3722 <method name="_animateTabMove">
michael@0 3723 <parameter name="event"/>
michael@0 3724 <body><![CDATA[
michael@0 3725 let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
michael@0 3726
michael@0 3727 if (this.getAttribute("movingtab") != "true") {
michael@0 3728 this.setAttribute("movingtab", "true");
michael@0 3729 this.selectedItem = draggedTab;
michael@0 3730 }
michael@0 3731
michael@0 3732 if (!("animLastScreenX" in draggedTab._dragData))
michael@0 3733 draggedTab._dragData.animLastScreenX = draggedTab._dragData.screenX;
michael@0 3734
michael@0 3735 let screenX = event.screenX;
michael@0 3736 if (screenX == draggedTab._dragData.animLastScreenX)
michael@0 3737 return;
michael@0 3738
michael@0 3739 let draggingRight = screenX > draggedTab._dragData.animLastScreenX;
michael@0 3740 draggedTab._dragData.animLastScreenX = screenX;
michael@0 3741
michael@0 3742 let rtl = (window.getComputedStyle(this).direction == "rtl");
michael@0 3743 let pinned = draggedTab.pinned;
michael@0 3744 let numPinned = this.tabbrowser._numPinnedTabs;
michael@0 3745 let tabs = this.tabbrowser.visibleTabs
michael@0 3746 .slice(pinned ? 0 : numPinned,
michael@0 3747 pinned ? numPinned : undefined);
michael@0 3748 if (rtl)
michael@0 3749 tabs.reverse();
michael@0 3750 let tabWidth = draggedTab.getBoundingClientRect().width;
michael@0 3751
michael@0 3752 // Move the dragged tab based on the mouse position.
michael@0 3753
michael@0 3754 let leftTab = tabs[0];
michael@0 3755 let rightTab = tabs[tabs.length - 1];
michael@0 3756 let tabScreenX = draggedTab.boxObject.screenX;
michael@0 3757 let translateX = screenX - draggedTab._dragData.screenX;
michael@0 3758 if (!pinned)
michael@0 3759 translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX;
michael@0 3760 let leftBound = leftTab.boxObject.screenX - tabScreenX;
michael@0 3761 let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
michael@0 3762 (tabScreenX + tabWidth);
michael@0 3763 translateX = Math.max(translateX, leftBound);
michael@0 3764 translateX = Math.min(translateX, rightBound);
michael@0 3765 draggedTab.style.transform = "translateX(" + translateX + "px)";
michael@0 3766
michael@0 3767 // Determine what tab we're dragging over.
michael@0 3768 // * Point of reference is the center of the dragged tab. If that
michael@0 3769 // point touches a background tab, the dragged tab would take that
michael@0 3770 // tab's position when dropped.
michael@0 3771 // * We're doing a binary search in order to reduce the amount of
michael@0 3772 // tabs we need to check.
michael@0 3773
michael@0 3774 let tabCenter = tabScreenX + translateX + tabWidth / 2;
michael@0 3775 let newIndex = -1;
michael@0 3776 let oldIndex = "animDropIndex" in draggedTab._dragData ?
michael@0 3777 draggedTab._dragData.animDropIndex : draggedTab._tPos;
michael@0 3778 let low = 0;
michael@0 3779 let high = tabs.length - 1;
michael@0 3780 while (low <= high) {
michael@0 3781 let mid = Math.floor((low + high) / 2);
michael@0 3782 if (tabs[mid] == draggedTab &&
michael@0 3783 ++mid > high)
michael@0 3784 break;
michael@0 3785 let boxObject = tabs[mid].boxObject;
michael@0 3786 let screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
michael@0 3787 if (screenX > tabCenter) {
michael@0 3788 high = mid - 1;
michael@0 3789 } else if (screenX + boxObject.width < tabCenter) {
michael@0 3790 low = mid + 1;
michael@0 3791 } else {
michael@0 3792 newIndex = tabs[mid]._tPos;
michael@0 3793 break;
michael@0 3794 }
michael@0 3795 }
michael@0 3796 if (newIndex >= oldIndex)
michael@0 3797 newIndex++;
michael@0 3798 if (newIndex < 0 || newIndex == oldIndex)
michael@0 3799 return;
michael@0 3800 draggedTab._dragData.animDropIndex = newIndex;
michael@0 3801
michael@0 3802 // Shift background tabs to leave a gap where the dragged tab
michael@0 3803 // would currently be dropped.
michael@0 3804
michael@0 3805 for (let tab of tabs) {
michael@0 3806 if (tab != draggedTab) {
michael@0 3807 let shift = getTabShift(tab, newIndex);
michael@0 3808 tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
michael@0 3809 }
michael@0 3810 }
michael@0 3811
michael@0 3812 function getTabShift(tab, dropIndex) {
michael@0 3813 if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
michael@0 3814 return rtl ? -tabWidth : tabWidth;
michael@0 3815 if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
michael@0 3816 return rtl ? tabWidth : -tabWidth;
michael@0 3817 return 0;
michael@0 3818 }
michael@0 3819 ]]></body>
michael@0 3820 </method>
michael@0 3821
michael@0 3822 <method name="_finishAnimateTabMove">
michael@0 3823 <body><![CDATA[
michael@0 3824 if (this.getAttribute("movingtab") != "true")
michael@0 3825 return;
michael@0 3826
michael@0 3827 for (let tab of this.tabbrowser.visibleTabs)
michael@0 3828 tab.style.transform = "";
michael@0 3829
michael@0 3830 this.removeAttribute("movingtab");
michael@0 3831
michael@0 3832 this._handleTabSelect();
michael@0 3833 ]]></body>
michael@0 3834 </method>
michael@0 3835
michael@0 3836 <method name="handleEvent">
michael@0 3837 <parameter name="aEvent"/>
michael@0 3838 <body><![CDATA[
michael@0 3839 switch (aEvent.type) {
michael@0 3840 case "load":
michael@0 3841 this.updateVisibility();
michael@0 3842 break;
michael@0 3843 case "resize":
michael@0 3844 if (aEvent.target != window)
michael@0 3845 break;
michael@0 3846
michael@0 3847 TabsInTitlebar.updateAppearance();
michael@0 3848
michael@0 3849 if (this.tabbrowser.visibleTabs.length > 1) {
michael@0 3850 var width = this.mTabstrip.boxObject.width;
michael@0 3851 if (width != this.mTabstripWidth) {
michael@0 3852 this.adjustTabstrip();
michael@0 3853 this._fillTrailingGap();
michael@0 3854 this._handleTabSelect();
michael@0 3855 this.mTabstripWidth = width;
michael@0 3856 }
michael@0 3857 }
michael@0 3858
michael@0 3859 this.tabbrowser.updateWindowResizers();
michael@0 3860 break;
michael@0 3861 case "mouseout":
michael@0 3862 // If the "related target" (the node to which the pointer went) is not
michael@0 3863 // a child of the current document, the mouse just left the window.
michael@0 3864 let relatedTarget = aEvent.relatedTarget;
michael@0 3865 if (relatedTarget && relatedTarget.ownerDocument == document)
michael@0 3866 break;
michael@0 3867 case "mousemove":
michael@0 3868 if (document.getElementById("tabContextMenu").state != "open")
michael@0 3869 this._unlockTabSizing();
michael@0 3870 break;
michael@0 3871 }
michael@0 3872 ]]></body>
michael@0 3873 </method>
michael@0 3874
michael@0 3875 <field name="_animateElement">
michael@0 3876 this.mTabstrip._scrollButtonDown;
michael@0 3877 </field>
michael@0 3878
michael@0 3879 <method name="_notifyBackgroundTab">
michael@0 3880 <parameter name="aTab"/>
michael@0 3881 <body><![CDATA[
michael@0 3882 if (aTab.pinned)
michael@0 3883 return;
michael@0 3884
michael@0 3885 var scrollRect = this.mTabstrip.scrollClientRect;
michael@0 3886 var tab = aTab.getBoundingClientRect();
michael@0 3887 this.mTabstrip._calcTabMargins(aTab);
michael@0 3888
michael@0 3889 // DOMRect left/right properties are immutable.
michael@0 3890 tab = {left: tab.left, right: tab.right};
michael@0 3891
michael@0 3892 // Is the new tab already completely visible?
michael@0 3893 if (scrollRect.left <= tab.left && tab.right <= scrollRect.right)
michael@0 3894 return;
michael@0 3895
michael@0 3896 if (this.mTabstrip.smoothScroll) {
michael@0 3897 let selected = !this.selectedItem.pinned &&
michael@0 3898 this.selectedItem.getBoundingClientRect();
michael@0 3899 if (selected) {
michael@0 3900 selected = {left: selected.left, right: selected.right};
michael@0 3901 // Need to take in to account the width of the left/right margins on tabs.
michael@0 3902 selected.left = selected.left + this.mTabstrip._tabMarginLeft;
michael@0 3903 selected.right = selected.right - this.mTabstrip._tabMarginRight;
michael@0 3904 }
michael@0 3905
michael@0 3906 tab.left += this.mTabstrip._tabMarginLeft;
michael@0 3907 tab.right -= this.mTabstrip._tabMarginRight;
michael@0 3908
michael@0 3909 // Can we make both the new tab and the selected tab completely visible?
michael@0 3910 if (!selected ||
michael@0 3911 Math.max(tab.right - selected.left, selected.right - tab.left) <=
michael@0 3912 scrollRect.width) {
michael@0 3913 this.mTabstrip.ensureElementIsVisible(aTab);
michael@0 3914 return;
michael@0 3915 }
michael@0 3916
michael@0 3917 this.mTabstrip._smoothScrollByPixels(this.mTabstrip._isRTLScrollbox ?
michael@0 3918 selected.right - scrollRect.right :
michael@0 3919 selected.left - scrollRect.left);
michael@0 3920 }
michael@0 3921
michael@0 3922 if (!this._animateElement.hasAttribute("notifybgtab")) {
michael@0 3923 this._animateElement.setAttribute("notifybgtab", "true");
michael@0 3924 setTimeout(function (ele) {
michael@0 3925 ele.removeAttribute("notifybgtab");
michael@0 3926 }, 150, this._animateElement);
michael@0 3927 }
michael@0 3928 ]]></body>
michael@0 3929 </method>
michael@0 3930
michael@0 3931 <method name="_getDragTargetTab">
michael@0 3932 <parameter name="event"/>
michael@0 3933 <body><![CDATA[
michael@0 3934 let tab = event.target.localName == "tab" ? event.target : null;
michael@0 3935 if (tab &&
michael@0 3936 (event.type == "drop" || event.type == "dragover") &&
michael@0 3937 event.dataTransfer.dropEffect == "link") {
michael@0 3938 let boxObject = tab.boxObject;
michael@0 3939 if (event.screenX < boxObject.screenX + boxObject.width * .25 ||
michael@0 3940 event.screenX > boxObject.screenX + boxObject.width * .75)
michael@0 3941 return null;
michael@0 3942 }
michael@0 3943 return tab;
michael@0 3944 ]]></body>
michael@0 3945 </method>
michael@0 3946
michael@0 3947 <method name="_getDropIndex">
michael@0 3948 <parameter name="event"/>
michael@0 3949 <body><![CDATA[
michael@0 3950 var tabs = this.childNodes;
michael@0 3951 var tab = this._getDragTargetTab(event);
michael@0 3952 if (window.getComputedStyle(this, null).direction == "ltr") {
michael@0 3953 for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
michael@0 3954 if (event.screenX < tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
michael@0 3955 return i;
michael@0 3956 } else {
michael@0 3957 for (let i = tab ? tab._tPos : 0; i < tabs.length; i++)
michael@0 3958 if (event.screenX > tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
michael@0 3959 return i;
michael@0 3960 }
michael@0 3961 return tabs.length;
michael@0 3962 ]]></body>
michael@0 3963 </method>
michael@0 3964
michael@0 3965 <method name="_setEffectAllowedForDataTransfer">
michael@0 3966 <parameter name="event"/>
michael@0 3967 <body><![CDATA[
michael@0 3968 var dt = event.dataTransfer;
michael@0 3969 // Disallow dropping multiple items
michael@0 3970 if (dt.mozItemCount > 1)
michael@0 3971 return dt.effectAllowed = "none";
michael@0 3972
michael@0 3973 var types = dt.mozTypesAt(0);
michael@0 3974 var sourceNode = null;
michael@0 3975 // tabs are always added as the first type
michael@0 3976 if (types[0] == TAB_DROP_TYPE) {
michael@0 3977 var sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
michael@0 3978 if (sourceNode instanceof XULElement &&
michael@0 3979 sourceNode.localName == "tab" &&
michael@0 3980 sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
michael@0 3981 sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" &&
michael@0 3982 sourceNode.ownerDocument.defaultView.gBrowser.tabContainer == sourceNode.parentNode) {
michael@0 3983 // Do not allow transfering a private tab to a non-private window
michael@0 3984 // and vice versa.
michael@0 3985 if (PrivateBrowsingUtils.isWindowPrivate(window) !=
michael@0 3986 PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerDocument.defaultView))
michael@0 3987 return dt.effectAllowed = "none";
michael@0 3988
michael@0 3989 if (window.gMultiProcessBrowser !=
michael@0 3990 sourceNode.ownerDocument.defaultView.gMultiProcessBrowser)
michael@0 3991 return dt.effectAllowed = "none";
michael@0 3992
michael@0 3993 #ifdef XP_MACOSX
michael@0 3994 return dt.effectAllowed = event.altKey ? "copy" : "move";
michael@0 3995 #else
michael@0 3996 return dt.effectAllowed = event.ctrlKey ? "copy" : "move";
michael@0 3997 #endif
michael@0 3998 }
michael@0 3999 }
michael@0 4000
michael@0 4001 if (browserDragAndDrop.canDropLink(event)) {
michael@0 4002 // Here we need to do this manually
michael@0 4003 return dt.effectAllowed = dt.dropEffect = "link";
michael@0 4004 }
michael@0 4005 return dt.effectAllowed = "none";
michael@0 4006 ]]></body>
michael@0 4007 </method>
michael@0 4008
michael@0 4009 <method name="_handleNewTab">
michael@0 4010 <parameter name="tab"/>
michael@0 4011 <body><![CDATA[
michael@0 4012 if (tab.parentNode != this)
michael@0 4013 return;
michael@0 4014 tab._fullyOpen = true;
michael@0 4015
michael@0 4016 this.adjustTabstrip();
michael@0 4017
michael@0 4018 if (tab.getAttribute("selected") == "true") {
michael@0 4019 this._fillTrailingGap();
michael@0 4020 this._handleTabSelect();
michael@0 4021 } else {
michael@0 4022 this._notifyBackgroundTab(tab);
michael@0 4023 }
michael@0 4024
michael@0 4025 // XXXmano: this is a temporary workaround for bug 345399
michael@0 4026 // We need to manually update the scroll buttons disabled state
michael@0 4027 // if a tab was inserted to the overflow area or removed from it
michael@0 4028 // without any scrolling and when the tabbar has already
michael@0 4029 // overflowed.
michael@0 4030 this.mTabstrip._updateScrollButtonsDisabledState();
michael@0 4031 ]]></body>
michael@0 4032 </method>
michael@0 4033
michael@0 4034 <method name="_canAdvanceToTab">
michael@0 4035 <parameter name="aTab"/>
michael@0 4036 <body>
michael@0 4037 <![CDATA[
michael@0 4038 return !aTab.closing;
michael@0 4039 ]]>
michael@0 4040 </body>
michael@0 4041 </method>
michael@0 4042
michael@0 4043 <method name="_handleTabTelemetryStart">
michael@0 4044 <parameter name="aTab"/>
michael@0 4045 <parameter name="aURI"/>
michael@0 4046 <body>
michael@0 4047 <![CDATA[
michael@0 4048 // Animation-smoothness telemetry/logging
michael@0 4049 if (Services.telemetry.canRecord || this._tabAnimationLoggingEnabled) {
michael@0 4050 if (aURI == "about:newtab" && (aTab._tPos == 1 || aTab._tPos == 2)) {
michael@0 4051 // Indicate newtab page animation where other tabs are unaffected
michael@0 4052 // (for which case, the 2nd or 3rd tabs are good representatives, even if not absolute)
michael@0 4053 aTab._recordingTabOpenPlain = true;
michael@0 4054 }
michael@0 4055 aTab._recordingHandle = window.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 4056 .getInterface(Ci.nsIDOMWindowUtils)
michael@0 4057 .startFrameTimeRecording();
michael@0 4058 }
michael@0 4059
michael@0 4060 // Overall animation duration
michael@0 4061 aTab._animStartTime = Date.now();
michael@0 4062 ]]>
michael@0 4063 </body>
michael@0 4064 </method>
michael@0 4065
michael@0 4066 <method name="_handleTabTelemetryEnd">
michael@0 4067 <parameter name="aTab"/>
michael@0 4068 <body>
michael@0 4069 <![CDATA[
michael@0 4070 if (!aTab._animStartTime) {
michael@0 4071 return;
michael@0 4072 }
michael@0 4073
michael@0 4074 Services.telemetry.getHistogramById(aTab.closing ?
michael@0 4075 "FX_TAB_ANIM_CLOSE_MS" :
michael@0 4076 "FX_TAB_ANIM_OPEN_MS")
michael@0 4077 .add(Date.now() - aTab._animStartTime);
michael@0 4078 aTab._animStartTime = 0;
michael@0 4079
michael@0 4080 // Handle tab animation smoothness telemetry/logging of frame intervals and paint times
michael@0 4081 if (!("_recordingHandle" in aTab)) {
michael@0 4082 return;
michael@0 4083 }
michael@0 4084
michael@0 4085 let intervals = window.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 4086 .getInterface(Ci.nsIDOMWindowUtils)
michael@0 4087 .stopFrameTimeRecording(aTab._recordingHandle);
michael@0 4088 delete aTab._recordingHandle;
michael@0 4089 let frameCount = intervals.length;
michael@0 4090
michael@0 4091 if (this._tabAnimationLoggingEnabled) {
michael@0 4092 let msg = "Tab " + (aTab.closing ? "close" : "open") + " (Frame-interval):\n";
michael@0 4093 for (let i = 0; i < frameCount; i++) {
michael@0 4094 msg += Math.round(intervals[i]) + "\n";
michael@0 4095 }
michael@0 4096 Services.console.logStringMessage(msg);
michael@0 4097 }
michael@0 4098
michael@0 4099 // For telemetry, the first frame interval is not useful since it may represent an interval
michael@0 4100 // to a relatively old frame (prior to recording start). So we'll ignore it for the average.
michael@0 4101 if (frameCount > 1) {
michael@0 4102 let averageInterval = 0;
michael@0 4103 for (let i = 1; i < frameCount; i++) {
michael@0 4104 averageInterval += intervals[i];
michael@0 4105 };
michael@0 4106 averageInterval = averageInterval / (frameCount - 1);
michael@0 4107
michael@0 4108 Services.telemetry.getHistogramById("FX_TAB_ANIM_ANY_FRAME_INTERVAL_MS").add(averageInterval);
michael@0 4109
michael@0 4110 if (aTab._recordingTabOpenPlain) {
michael@0 4111 delete aTab._recordingTabOpenPlain;
michael@0 4112 // While we do have a telemetry probe NEWTAB_PAGE_ENABLED to monitor newtab preview, it'll be
michael@0 4113 // easier to overview the data without slicing by it. Hence the additional histograms with _PREVIEW.
michael@0 4114 let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : "";
michael@0 4115 Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval);
michael@0 4116 }
michael@0 4117 }
michael@0 4118 ]]>
michael@0 4119 </body>
michael@0 4120 </method>
michael@0 4121
michael@0 4122 <!-- Deprecated stuff, implemented for backwards compatibility. -->
michael@0 4123 <property name="mTabstripClosebutton" readonly="true"
michael@0 4124 onget="return document.getElementById('tabs-closebutton');"/>
michael@0 4125 <property name="mAllTabsPopup" readonly="true"
michael@0 4126 onget="return document.getElementById('alltabs-popup');"/>
michael@0 4127 </implementation>
michael@0 4128
michael@0 4129 <handlers>
michael@0 4130 <handler event="TabSelect" action="this._handleTabSelect();"/>
michael@0 4131
michael@0 4132 <handler event="transitionend"><![CDATA[
michael@0 4133 if (event.propertyName != "max-width")
michael@0 4134 return;
michael@0 4135
michael@0 4136 var tab = event.target;
michael@0 4137
michael@0 4138 this._handleTabTelemetryEnd(tab);
michael@0 4139
michael@0 4140 if (tab.getAttribute("fadein") == "true") {
michael@0 4141 if (tab._fullyOpen)
michael@0 4142 this.adjustTabstrip();
michael@0 4143 else
michael@0 4144 this._handleNewTab(tab);
michael@0 4145 } else if (tab.closing) {
michael@0 4146 this.tabbrowser._endRemoveTab(tab);
michael@0 4147 }
michael@0 4148 ]]></handler>
michael@0 4149
michael@0 4150 <handler event="dblclick"><![CDATA[
michael@0 4151 #ifndef XP_MACOSX
michael@0 4152 // When the tabbar has an unified appearance with the titlebar
michael@0 4153 // and menubar, a double-click in it should have the same behavior
michael@0 4154 // as double-clicking the titlebar
michael@0 4155 if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
michael@0 4156 return;
michael@0 4157 #endif
michael@0 4158
michael@0 4159 if (event.button != 0 ||
michael@0 4160 event.originalTarget.localName != "box")
michael@0 4161 return;
michael@0 4162
michael@0 4163 // See hack note in the tabbrowser-close-tab-button binding
michael@0 4164 if (!this._blockDblClick)
michael@0 4165 BrowserOpenTab();
michael@0 4166
michael@0 4167 event.preventDefault();
michael@0 4168 ]]></handler>
michael@0 4169
michael@0 4170 <handler event="click" button="0" phase="capturing"><![CDATA[
michael@0 4171 /* Catches extra clicks meant for the in-tab close button.
michael@0 4172 * Placed here to avoid leaking (a temporary handler added from the
michael@0 4173 * in-tab close button binding would close over the tab and leak it
michael@0 4174 * until the handler itself was removed). (bug 897751)
michael@0 4175 *
michael@0 4176 * The only sequence in which a second click event (i.e. dblclik)
michael@0 4177 * can be dispatched on an in-tab close button is when it is shown
michael@0 4178 * after the first click (i.e. the first click event was dispatched
michael@0 4179 * on the tab). This happens when we show the close button only on
michael@0 4180 * the active tab. (bug 352021)
michael@0 4181 * The only sequence in which a third click event can be dispatched
michael@0 4182 * on an in-tab close button is when the tab was opened with a
michael@0 4183 * double click on the tabbar. (bug 378344)
michael@0 4184 * In both cases, it is most likely that the close button area has
michael@0 4185 * been accidentally clicked, therefore we do not close the tab.
michael@0 4186 *
michael@0 4187 * We don't want to ignore processing of more than one click event,
michael@0 4188 * though, since the user might actually be repeatedly clicking to
michael@0 4189 * close many tabs at once.
michael@0 4190 */
michael@0 4191 let target = event.originalTarget;
michael@0 4192 if (target.classList.contains('tab-close-button')) {
michael@0 4193 // We preemptively set this to allow the closing-multiple-tabs-
michael@0 4194 // in-a-row case.
michael@0 4195 if (this._blockDblClick) {
michael@0 4196 target._ignoredCloseButtonClicks = true;
michael@0 4197 } else if (event.detail > 1 && !target._ignoredCloseButtonClicks) {
michael@0 4198 target._ignoredCloseButtonClicks = true;
michael@0 4199 event.stopPropagation();
michael@0 4200 return;
michael@0 4201 } else {
michael@0 4202 // Reset the "ignored click" flag
michael@0 4203 target._ignoredCloseButtonClicks = false;
michael@0 4204 }
michael@0 4205 }
michael@0 4206
michael@0 4207 /* Protects from close-tab-button errant doubleclick:
michael@0 4208 * Since we're removing the event target, if the user
michael@0 4209 * double-clicks the button, the dblclick event will be dispatched
michael@0 4210 * with the tabbar as its event target (and explicit/originalTarget),
michael@0 4211 * which treats that as a mouse gesture for opening a new tab.
michael@0 4212 * In this context, we're manually blocking the dblclick event
michael@0 4213 * (see tabbrowser-close-tab-button dblclick handler).
michael@0 4214 */
michael@0 4215 if (this._blockDblClick) {
michael@0 4216 if (!("_clickedTabBarOnce" in this)) {
michael@0 4217 this._clickedTabBarOnce = true;
michael@0 4218 return;
michael@0 4219 }
michael@0 4220 delete this._clickedTabBarOnce;
michael@0 4221 this._blockDblClick = false;
michael@0 4222 }
michael@0 4223 ]]></handler>
michael@0 4224
michael@0 4225 <handler event="click"><![CDATA[
michael@0 4226 if (event.button != 1)
michael@0 4227 return;
michael@0 4228
michael@0 4229 if (event.target.localName == "tab") {
michael@0 4230 this.tabbrowser.removeTab(event.target, {animate: true, byMouse: true});
michael@0 4231 } else if (event.originalTarget.localName == "box") {
michael@0 4232 BrowserOpenTab();
michael@0 4233 } else {
michael@0 4234 return;
michael@0 4235 }
michael@0 4236
michael@0 4237 event.stopPropagation();
michael@0 4238 ]]></handler>
michael@0 4239
michael@0 4240 <handler event="keypress"><![CDATA[
michael@0 4241 if (event.altKey || event.shiftKey ||
michael@0 4242 #ifdef XP_MACOSX
michael@0 4243 !event.metaKey)
michael@0 4244 #else
michael@0 4245 !event.ctrlKey || event.metaKey)
michael@0 4246 #endif
michael@0 4247 return;
michael@0 4248
michael@0 4249 switch (event.keyCode) {
michael@0 4250 case KeyEvent.DOM_VK_UP:
michael@0 4251 this.tabbrowser.moveTabBackward();
michael@0 4252 break;
michael@0 4253 case KeyEvent.DOM_VK_DOWN:
michael@0 4254 this.tabbrowser.moveTabForward();
michael@0 4255 break;
michael@0 4256 case KeyEvent.DOM_VK_RIGHT:
michael@0 4257 case KeyEvent.DOM_VK_LEFT:
michael@0 4258 this.tabbrowser.moveTabOver(event);
michael@0 4259 break;
michael@0 4260 case KeyEvent.DOM_VK_HOME:
michael@0 4261 this.tabbrowser.moveTabToStart();
michael@0 4262 break;
michael@0 4263 case KeyEvent.DOM_VK_END:
michael@0 4264 this.tabbrowser.moveTabToEnd();
michael@0 4265 break;
michael@0 4266 default:
michael@0 4267 // Stop the keypress event for the above keyboard
michael@0 4268 // shortcuts only.
michael@0 4269 return;
michael@0 4270 }
michael@0 4271 event.stopPropagation();
michael@0 4272 event.preventDefault();
michael@0 4273 ]]></handler>
michael@0 4274
michael@0 4275 <handler event="dragstart"><![CDATA[
michael@0 4276 var tab = this._getDragTargetTab(event);
michael@0 4277 if (!tab || this._isCustomizing)
michael@0 4278 return;
michael@0 4279
michael@0 4280 let dt = event.dataTransfer;
michael@0 4281 dt.mozSetDataAt(TAB_DROP_TYPE, tab, 0);
michael@0 4282 let browser = tab.linkedBrowser;
michael@0 4283
michael@0 4284 // We must not set text/x-moz-url or text/plain data here,
michael@0 4285 // otherwise trying to deatch the tab by dropping it on the desktop
michael@0 4286 // may result in an "internet shortcut"
michael@0 4287 dt.mozSetDataAt("text/x-moz-text-internal", browser.currentURI.spec, 0);
michael@0 4288
michael@0 4289 // Set the cursor to an arrow during tab drags.
michael@0 4290 dt.mozCursor = "default";
michael@0 4291
michael@0 4292 // Create a canvas to which we capture the current tab.
michael@0 4293 // Until canvas is HiDPI-aware (bug 780362), we need to scale the desired
michael@0 4294 // canvas size (in CSS pixels) to the window's backing resolution in order
michael@0 4295 // to get a full-resolution drag image for use on HiDPI displays.
michael@0 4296 let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
michael@0 4297 let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
michael@0 4298 let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
michael@0 4299 canvas.mozOpaque = true;
michael@0 4300 canvas.width = 160 * scale;
michael@0 4301 canvas.height = 90 * scale;
michael@0 4302 PageThumbs.captureToCanvas(browser.contentWindow, canvas);
michael@0 4303 dt.setDragImage(canvas, -16 * scale, -16 * scale);
michael@0 4304
michael@0 4305 // _dragData.offsetX/Y give the coordinates that the mouse should be
michael@0 4306 // positioned relative to the corner of the new window created upon
michael@0 4307 // dragend such that the mouse appears to have the same position
michael@0 4308 // relative to the corner of the dragged tab.
michael@0 4309 function clientX(ele) ele.getBoundingClientRect().left;
michael@0 4310 let tabOffsetX = clientX(tab) - clientX(this);
michael@0 4311 tab._dragData = {
michael@0 4312 offsetX: event.screenX - window.screenX - tabOffsetX,
michael@0 4313 offsetY: event.screenY - window.screenY,
michael@0 4314 scrollX: this.mTabstrip.scrollPosition,
michael@0 4315 screenX: event.screenX
michael@0 4316 };
michael@0 4317
michael@0 4318 event.stopPropagation();
michael@0 4319 ]]></handler>
michael@0 4320
michael@0 4321 <handler event="dragover"><![CDATA[
michael@0 4322 var effects = this._setEffectAllowedForDataTransfer(event);
michael@0 4323
michael@0 4324 var ind = this._tabDropIndicator;
michael@0 4325 if (effects == "" || effects == "none") {
michael@0 4326 ind.collapsed = true;
michael@0 4327 return;
michael@0 4328 }
michael@0 4329 event.preventDefault();
michael@0 4330 event.stopPropagation();
michael@0 4331
michael@0 4332 var tabStrip = this.mTabstrip;
michael@0 4333 var ltr = (window.getComputedStyle(this, null).direction == "ltr");
michael@0 4334
michael@0 4335 // autoscroll the tab strip if we drag over the scroll
michael@0 4336 // buttons, even if we aren't dragging a tab, but then
michael@0 4337 // return to avoid drawing the drop indicator
michael@0 4338 var pixelsToScroll = 0;
michael@0 4339 if (this.getAttribute("overflow") == "true") {
michael@0 4340 var targetAnonid = event.originalTarget.getAttribute("anonid");
michael@0 4341 switch (targetAnonid) {
michael@0 4342 case "scrollbutton-up":
michael@0 4343 pixelsToScroll = tabStrip.scrollIncrement * -1;
michael@0 4344 break;
michael@0 4345 case "scrollbutton-down":
michael@0 4346 pixelsToScroll = tabStrip.scrollIncrement;
michael@0 4347 break;
michael@0 4348 }
michael@0 4349 if (pixelsToScroll)
michael@0 4350 tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll);
michael@0 4351 }
michael@0 4352
michael@0 4353 if (effects == "move" &&
michael@0 4354 this == event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0).parentNode) {
michael@0 4355 ind.collapsed = true;
michael@0 4356 this._animateTabMove(event);
michael@0 4357 return;
michael@0 4358 }
michael@0 4359
michael@0 4360 this._finishAnimateTabMove();
michael@0 4361
michael@0 4362 if (effects == "link") {
michael@0 4363 let tab = this._getDragTargetTab(event);
michael@0 4364 if (tab) {
michael@0 4365 if (!this._dragTime)
michael@0 4366 this._dragTime = Date.now();
michael@0 4367 if (Date.now() >= this._dragTime + this._dragOverDelay)
michael@0 4368 this.selectedItem = tab;
michael@0 4369 ind.collapsed = true;
michael@0 4370 return;
michael@0 4371 }
michael@0 4372 }
michael@0 4373
michael@0 4374 var rect = tabStrip.getBoundingClientRect();
michael@0 4375 var newMargin;
michael@0 4376 if (pixelsToScroll) {
michael@0 4377 // if we are scrolling, put the drop indicator at the edge
michael@0 4378 // so that it doesn't jump while scrolling
michael@0 4379 let scrollRect = tabStrip.scrollClientRect;
michael@0 4380 let minMargin = scrollRect.left - rect.left;
michael@0 4381 let maxMargin = Math.min(minMargin + scrollRect.width,
michael@0 4382 scrollRect.right);
michael@0 4383 if (!ltr)
michael@0 4384 [minMargin, maxMargin] = [this.clientWidth - maxMargin,
michael@0 4385 this.clientWidth - minMargin];
michael@0 4386 newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
michael@0 4387 }
michael@0 4388 else {
michael@0 4389 let newIndex = this._getDropIndex(event);
michael@0 4390 if (newIndex == this.childNodes.length) {
michael@0 4391 let tabRect = this.childNodes[newIndex-1].getBoundingClientRect();
michael@0 4392 if (ltr)
michael@0 4393 newMargin = tabRect.right - rect.left;
michael@0 4394 else
michael@0 4395 newMargin = rect.right - tabRect.left;
michael@0 4396 }
michael@0 4397 else {
michael@0 4398 let tabRect = this.childNodes[newIndex].getBoundingClientRect();
michael@0 4399 if (ltr)
michael@0 4400 newMargin = tabRect.left - rect.left;
michael@0 4401 else
michael@0 4402 newMargin = rect.right - tabRect.right;
michael@0 4403 }
michael@0 4404 }
michael@0 4405
michael@0 4406 ind.collapsed = false;
michael@0 4407
michael@0 4408 newMargin += ind.clientWidth / 2;
michael@0 4409 if (!ltr)
michael@0 4410 newMargin *= -1;
michael@0 4411
michael@0 4412 ind.style.transform = "translate(" + Math.round(newMargin) + "px)";
michael@0 4413 ind.style.MozMarginStart = (-ind.clientWidth) + "px";
michael@0 4414 ]]></handler>
michael@0 4415
michael@0 4416 <handler event="drop"><![CDATA[
michael@0 4417 var dt = event.dataTransfer;
michael@0 4418 var dropEffect = dt.dropEffect;
michael@0 4419 var draggedTab;
michael@0 4420 if (dropEffect != "link") { // copy or move
michael@0 4421 draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
michael@0 4422 // not our drop then
michael@0 4423 if (!draggedTab)
michael@0 4424 return;
michael@0 4425 }
michael@0 4426
michael@0 4427 this._tabDropIndicator.collapsed = true;
michael@0 4428 event.stopPropagation();
michael@0 4429 if (draggedTab && dropEffect == "copy") {
michael@0 4430 // copy the dropped tab (wherever it's from)
michael@0 4431 let newIndex = this._getDropIndex(event);
michael@0 4432 let newTab = this.tabbrowser.duplicateTab(draggedTab);
michael@0 4433 this.tabbrowser.moveTabTo(newTab, newIndex);
michael@0 4434 if (draggedTab.parentNode != this || event.shiftKey)
michael@0 4435 this.selectedItem = newTab;
michael@0 4436 } else if (draggedTab && draggedTab.parentNode == this) {
michael@0 4437 this._finishAnimateTabMove();
michael@0 4438
michael@0 4439 // actually move the dragged tab
michael@0 4440 if ("animDropIndex" in draggedTab._dragData) {
michael@0 4441 let newIndex = draggedTab._dragData.animDropIndex;
michael@0 4442 if (newIndex > draggedTab._tPos)
michael@0 4443 newIndex--;
michael@0 4444 this.tabbrowser.moveTabTo(draggedTab, newIndex);
michael@0 4445 }
michael@0 4446 } else if (draggedTab) {
michael@0 4447 // swap the dropped tab with a new one we create and then close
michael@0 4448 // it in the other window (making it seem to have moved between
michael@0 4449 // windows)
michael@0 4450 let newIndex = this._getDropIndex(event);
michael@0 4451 let newTab = this.tabbrowser.addTab("about:blank");
michael@0 4452 let newBrowser = this.tabbrowser.getBrowserForTab(newTab);
michael@0 4453 // Stop the about:blank load
michael@0 4454 newBrowser.stop();
michael@0 4455 // make sure it has a docshell
michael@0 4456 newBrowser.docShell;
michael@0 4457
michael@0 4458 let numPinned = this.tabbrowser._numPinnedTabs;
michael@0 4459 if (newIndex < numPinned || draggedTab.pinned && newIndex == numPinned)
michael@0 4460 this.tabbrowser.pinTab(newTab);
michael@0 4461 this.tabbrowser.moveTabTo(newTab, newIndex);
michael@0 4462
michael@0 4463 // We need to select the tab before calling swapBrowsersAndCloseOther
michael@0 4464 // so that window.content in chrome windows points to the right tab
michael@0 4465 // when pagehide/show events are fired.
michael@0 4466 this.tabbrowser.selectedTab = newTab;
michael@0 4467
michael@0 4468 draggedTab.parentNode._finishAnimateTabMove();
michael@0 4469 this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab);
michael@0 4470
michael@0 4471 // Call updateCurrentBrowser to make sure the URL bar is up to date
michael@0 4472 // for our new tab after we've done swapBrowsersAndCloseOther.
michael@0 4473 this.tabbrowser.updateCurrentBrowser(true);
michael@0 4474 } else {
michael@0 4475 // Pass true to disallow dropping javascript: or data: urls
michael@0 4476 let url;
michael@0 4477 try {
michael@0 4478 url = browserDragAndDrop.drop(event, { }, true);
michael@0 4479 } catch (ex) {}
michael@0 4480
michael@0 4481 if (!url)
michael@0 4482 return;
michael@0 4483
michael@0 4484 let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
michael@0 4485
michael@0 4486 if (event.shiftKey)
michael@0 4487 bgLoad = !bgLoad;
michael@0 4488
michael@0 4489 let tab = this._getDragTargetTab(event);
michael@0 4490 if (!tab || dropEffect == "copy") {
michael@0 4491 // We're adding a new tab.
michael@0 4492 let newIndex = this._getDropIndex(event);
michael@0 4493 let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad, allowThirdPartyFixup: true});
michael@0 4494 this.tabbrowser.moveTabTo(newTab, newIndex);
michael@0 4495 } else {
michael@0 4496 // Load in an existing tab.
michael@0 4497 try {
michael@0 4498 let webNav = Ci.nsIWebNavigation;
michael@0 4499 let flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
michael@0 4500 webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
michael@0 4501 this.tabbrowser.getBrowserForTab(tab).loadURIWithFlags(url, flags);
michael@0 4502 if (!bgLoad)
michael@0 4503 this.selectedItem = tab;
michael@0 4504 } catch(ex) {
michael@0 4505 // Just ignore invalid urls
michael@0 4506 }
michael@0 4507 }
michael@0 4508 }
michael@0 4509
michael@0 4510 if (draggedTab) {
michael@0 4511 delete draggedTab._dragData;
michael@0 4512 }
michael@0 4513 ]]></handler>
michael@0 4514
michael@0 4515 <handler event="dragend"><![CDATA[
michael@0 4516 // Note: while this case is correctly handled here, this event
michael@0 4517 // isn't dispatched when the tab is moved within the tabstrip,
michael@0 4518 // see bug 460801.
michael@0 4519
michael@0 4520 this._finishAnimateTabMove();
michael@0 4521
michael@0 4522 var dt = event.dataTransfer;
michael@0 4523 var draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
michael@0 4524 if (dt.mozUserCancelled || dt.dropEffect != "none" || this._isCustomizing) {
michael@0 4525 delete draggedTab._dragData;
michael@0 4526 return;
michael@0 4527 }
michael@0 4528
michael@0 4529 // Disable detach within the browser toolbox
michael@0 4530 var eX = event.screenX;
michael@0 4531 var eY = event.screenY;
michael@0 4532 var wX = window.screenX;
michael@0 4533 // check if the drop point is horizontally within the window
michael@0 4534 if (eX > wX && eX < (wX + window.outerWidth)) {
michael@0 4535 let bo = this.mTabstrip.boxObject;
michael@0 4536 // also avoid detaching if the the tab was dropped too close to
michael@0 4537 // the tabbar (half a tab)
michael@0 4538 let endScreenY = bo.screenY + 1.5 * bo.height;
michael@0 4539 if (eY < endScreenY && eY > window.screenY)
michael@0 4540 return;
michael@0 4541 }
michael@0 4542
michael@0 4543 // screen.availLeft et. al. only check the screen that this window is on,
michael@0 4544 // but we want to look at the screen the tab is being dropped onto.
michael@0 4545 var sX = {}, sY = {}, sWidth = {}, sHeight = {};
michael@0 4546 Cc["@mozilla.org/gfx/screenmanager;1"]
michael@0 4547 .getService(Ci.nsIScreenManager)
michael@0 4548 .screenForRect(eX, eY, 1, 1)
michael@0 4549 .GetAvailRect(sX, sY, sWidth, sHeight);
michael@0 4550 // ensure new window entirely within screen
michael@0 4551 var winWidth = Math.min(window.outerWidth, sWidth.value);
michael@0 4552 var winHeight = Math.min(window.outerHeight, sHeight.value);
michael@0 4553 var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value),
michael@0 4554 sX.value + sWidth.value - winWidth);
michael@0 4555 var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value),
michael@0 4556 sY.value + sHeight.value - winHeight);
michael@0 4557
michael@0 4558 delete draggedTab._dragData;
michael@0 4559
michael@0 4560 if (this.tabbrowser.tabs.length == 1) {
michael@0 4561 // resize _before_ move to ensure the window fits the new screen. if
michael@0 4562 // the window is too large for its screen, the window manager may do
michael@0 4563 // automatic repositioning.
michael@0 4564 window.resizeTo(winWidth, winHeight);
michael@0 4565 window.moveTo(left, top);
michael@0 4566 window.focus();
michael@0 4567 } else {
michael@0 4568 this.tabbrowser.replaceTabWithWindow(draggedTab, { screenX: left,
michael@0 4569 screenY: top,
michael@0 4570 #ifndef XP_WIN
michael@0 4571 outerWidth: winWidth,
michael@0 4572 outerHeight: winHeight
michael@0 4573 #endif
michael@0 4574 });
michael@0 4575 }
michael@0 4576 event.stopPropagation();
michael@0 4577 ]]></handler>
michael@0 4578
michael@0 4579 <handler event="dragexit"><![CDATA[
michael@0 4580 this._dragTime = 0;
michael@0 4581
michael@0 4582 // This does not work at all (see bug 458613)
michael@0 4583 var target = event.relatedTarget;
michael@0 4584 while (target && target != this)
michael@0 4585 target = target.parentNode;
michael@0 4586 if (target)
michael@0 4587 return;
michael@0 4588
michael@0 4589 this._tabDropIndicator.collapsed = true;
michael@0 4590 event.stopPropagation();
michael@0 4591 ]]></handler>
michael@0 4592 </handlers>
michael@0 4593 </binding>
michael@0 4594
michael@0 4595 <!-- close-tab-button binding
michael@0 4596 This binding relies on the structure of the tabbrowser binding.
michael@0 4597 Therefore it should only be used as a child of the tab or the tabs
michael@0 4598 element (in both cases, when they are anonymous nodes of <tabbrowser>).
michael@0 4599 -->
michael@0 4600 <binding id="tabbrowser-close-tab-button"
michael@0 4601 extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image">
michael@0 4602 <handlers>
michael@0 4603 <handler event="click" button="0"><![CDATA[
michael@0 4604 var bindingParent = document.getBindingParent(this);
michael@0 4605 var tabContainer = bindingParent.parentNode;
michael@0 4606 tabContainer.tabbrowser.removeTab(bindingParent, {animate: true, byMouse: true});
michael@0 4607 // This enables double-click protection for the tab container
michael@0 4608 // (see tabbrowser-tabs 'click' handler).
michael@0 4609 tabContainer._blockDblClick = true;
michael@0 4610 ]]></handler>
michael@0 4611
michael@0 4612 <handler event="dblclick" button="0" phase="capturing">
michael@0 4613 // for the one-close-button case
michael@0 4614 event.stopPropagation();
michael@0 4615 </handler>
michael@0 4616
michael@0 4617 <handler event="dragstart">
michael@0 4618 event.stopPropagation();
michael@0 4619 </handler>
michael@0 4620 </handlers>
michael@0 4621 </binding>
michael@0 4622
michael@0 4623 <binding id="tabbrowser-tab" display="xul:hbox"
michael@0 4624 extends="chrome://global/content/bindings/tabbox.xml#tab">
michael@0 4625 <resources>
michael@0 4626 <stylesheet src="chrome://browser/content/tabbrowser.css"/>
michael@0 4627 </resources>
michael@0 4628
michael@0 4629 <content context="tabContextMenu" closetabtext="&closeTab.label;">
michael@0 4630 <xul:stack class="tab-stack" flex="1">
michael@0 4631 <xul:hbox xbl:inherits="pinned,selected,titlechanged,fadein"
michael@0 4632 class="tab-background">
michael@0 4633 <xul:hbox xbl:inherits="pinned,selected,titlechanged"
michael@0 4634 class="tab-background-start"/>
michael@0 4635 <xul:hbox xbl:inherits="pinned,selected,titlechanged"
michael@0 4636 class="tab-background-middle"/>
michael@0 4637 <xul:hbox xbl:inherits="pinned,selected,titlechanged"
michael@0 4638 class="tab-background-end"/>
michael@0 4639 </xul:hbox>
michael@0 4640 <xul:hbox xbl:inherits="pinned,selected,titlechanged"
michael@0 4641 class="tab-content" align="center">
michael@0 4642 <xul:image xbl:inherits="fadein,pinned,busy,progress,selected"
michael@0 4643 class="tab-throbber"
michael@0 4644 role="presentation"
michael@0 4645 layer="true" />
michael@0 4646 <xul:image xbl:inherits="src=image,fadein,pinned,selected"
michael@0 4647 anonid="tab-icon-image"
michael@0 4648 class="tab-icon-image"
michael@0 4649 validate="never"
michael@0 4650 role="presentation"/>
michael@0 4651 <xul:label flex="1"
michael@0 4652 anonid="tab-label"
michael@0 4653 xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected"
michael@0 4654 class="tab-text tab-label"
michael@0 4655 role="presentation"/>
michael@0 4656 <xul:toolbarbutton anonid="close-button"
michael@0 4657 xbl:inherits="fadein,pinned,selected"
michael@0 4658 class="tab-close-button close-icon"/>
michael@0 4659 </xul:hbox>
michael@0 4660 </xul:stack>
michael@0 4661 </content>
michael@0 4662
michael@0 4663 <implementation>
michael@0 4664 <property name="label">
michael@0 4665 <getter>
michael@0 4666 return this.getAttribute("label");
michael@0 4667 </getter>
michael@0 4668 <setter>
michael@0 4669 this.setAttribute("label", val);
michael@0 4670 let event = new CustomEvent("TabLabelModified", {
michael@0 4671 bubbles: true,
michael@0 4672 cancelable: true
michael@0 4673 });
michael@0 4674 this.dispatchEvent(event);
michael@0 4675
michael@0 4676 // Let listeners prevent synchronizing the actual label to the
michael@0 4677 // visible label (allowing them to override the visible label).
michael@0 4678 if (!event.defaultPrevented)
michael@0 4679 this.visibleLabel = val;
michael@0 4680 </setter>
michael@0 4681 </property>
michael@0 4682 <property name="visibleLabel">
michael@0 4683 <getter>
michael@0 4684 return this.getAttribute("visibleLabel");
michael@0 4685 </getter>
michael@0 4686 <setter>
michael@0 4687 this.setAttribute("visibleLabel", val);
michael@0 4688 </setter>
michael@0 4689 </property>
michael@0 4690 <property name="pinned" readonly="true">
michael@0 4691 <getter>
michael@0 4692 return this.getAttribute("pinned") == "true";
michael@0 4693 </getter>
michael@0 4694 </property>
michael@0 4695 <property name="hidden" readonly="true">
michael@0 4696 <getter>
michael@0 4697 return this.getAttribute("hidden") == "true";
michael@0 4698 </getter>
michael@0 4699 </property>
michael@0 4700
michael@0 4701 <property name="lastAccessed">
michael@0 4702 <getter>
michael@0 4703 return this.selected ? Date.now() : this._lastAccessed;
michael@0 4704 </getter>
michael@0 4705 <setter>
michael@0 4706 this._lastAccessed = val;
michael@0 4707 </setter>
michael@0 4708 </property>
michael@0 4709 <field name="_lastAccessed">0</field>
michael@0 4710
michael@0 4711 <field name="mOverCloseButton">false</field>
michael@0 4712 <field name="mCorrespondingMenuitem">null</field>
michael@0 4713 <field name="closing">false</field>
michael@0 4714
michael@0 4715 <method name="_mouseenter">
michael@0 4716 <body><![CDATA[
michael@0 4717 if (this.hidden || this.closing)
michael@0 4718 return;
michael@0 4719
michael@0 4720 let tabContainer = this.parentNode;
michael@0 4721 let visibleTabs = tabContainer.tabbrowser.visibleTabs;
michael@0 4722 let tabIndex = visibleTabs.indexOf(this);
michael@0 4723 if (tabIndex == 0) {
michael@0 4724 tabContainer._beforeHoveredTab = null;
michael@0 4725 } else {
michael@0 4726 let candidate = visibleTabs[tabIndex - 1];
michael@0 4727 if (!candidate.selected) {
michael@0 4728 tabContainer._beforeHoveredTab = candidate;
michael@0 4729 candidate.setAttribute("beforehovered", "true");
michael@0 4730 }
michael@0 4731 }
michael@0 4732
michael@0 4733 if (tabIndex == visibleTabs.length - 1) {
michael@0 4734 tabContainer._afterHoveredTab = null;
michael@0 4735 } else {
michael@0 4736 let candidate = visibleTabs[tabIndex + 1];
michael@0 4737 if (!candidate.selected) {
michael@0 4738 tabContainer._afterHoveredTab = candidate;
michael@0 4739 candidate.setAttribute("afterhovered", "true");
michael@0 4740 }
michael@0 4741 }
michael@0 4742
michael@0 4743 tabContainer._hoveredTab = this;
michael@0 4744 ]]></body>
michael@0 4745 </method>
michael@0 4746
michael@0 4747 <method name="_mouseleave">
michael@0 4748 <body><![CDATA[
michael@0 4749 let tabContainer = this.parentNode;
michael@0 4750 if (tabContainer._beforeHoveredTab) {
michael@0 4751 tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
michael@0 4752 tabContainer._beforeHoveredTab = null;
michael@0 4753 }
michael@0 4754 if (tabContainer._afterHoveredTab) {
michael@0 4755 tabContainer._afterHoveredTab.removeAttribute("afterhovered");
michael@0 4756 tabContainer._afterHoveredTab = null;
michael@0 4757 }
michael@0 4758
michael@0 4759 tabContainer._hoveredTab = null;
michael@0 4760 ]]></body>
michael@0 4761 </method>
michael@0 4762 </implementation>
michael@0 4763
michael@0 4764 <handlers>
michael@0 4765 <handler event="mouseover"><![CDATA[
michael@0 4766 let anonid = event.originalTarget.getAttribute("anonid");
michael@0 4767 if (anonid == "close-button")
michael@0 4768 this.mOverCloseButton = true;
michael@0 4769
michael@0 4770 this._mouseenter();
michael@0 4771 ]]></handler>
michael@0 4772 <handler event="mouseout"><![CDATA[
michael@0 4773 let anonid = event.originalTarget.getAttribute("anonid");
michael@0 4774 if (anonid == "close-button")
michael@0 4775 this.mOverCloseButton = false;
michael@0 4776
michael@0 4777 this._mouseleave();
michael@0 4778 ]]></handler>
michael@0 4779 <handler event="dragstart" phase="capturing">
michael@0 4780 this.style.MozUserFocus = '';
michael@0 4781 </handler>
michael@0 4782 <handler event="mousedown" phase="capturing">
michael@0 4783 <![CDATA[
michael@0 4784 if (this.selected) {
michael@0 4785 this.style.MozUserFocus = 'ignore';
michael@0 4786 this.clientTop; // just using this to flush style updates
michael@0 4787 } else if (this.mOverCloseButton) {
michael@0 4788 // Prevent tabbox.xml from selecting the tab.
michael@0 4789 event.stopPropagation();
michael@0 4790 }
michael@0 4791 ]]>
michael@0 4792 </handler>
michael@0 4793 <handler event="mouseup">
michael@0 4794 this.style.MozUserFocus = '';
michael@0 4795 </handler>
michael@0 4796 </handlers>
michael@0 4797 </binding>
michael@0 4798
michael@0 4799 <binding id="tabbrowser-alltabs-popup"
michael@0 4800 extends="chrome://global/content/bindings/popup.xml#popup">
michael@0 4801 <implementation implements="nsIDOMEventListener">
michael@0 4802 <method name="_tabOnAttrModified">
michael@0 4803 <parameter name="aEvent"/>
michael@0 4804 <body><![CDATA[
michael@0 4805 var tab = aEvent.target;
michael@0 4806 if (tab.mCorrespondingMenuitem)
michael@0 4807 this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
michael@0 4808 ]]></body>
michael@0 4809 </method>
michael@0 4810
michael@0 4811 <method name="_tabOnTabClose">
michael@0 4812 <parameter name="aEvent"/>
michael@0 4813 <body><![CDATA[
michael@0 4814 var tab = aEvent.target;
michael@0 4815 if (tab.mCorrespondingMenuitem)
michael@0 4816 this.removeChild(tab.mCorrespondingMenuitem);
michael@0 4817 ]]></body>
michael@0 4818 </method>
michael@0 4819
michael@0 4820 <method name="handleEvent">
michael@0 4821 <parameter name="aEvent"/>
michael@0 4822 <body><![CDATA[
michael@0 4823 switch (aEvent.type) {
michael@0 4824 case "TabAttrModified":
michael@0 4825 this._tabOnAttrModified(aEvent);
michael@0 4826 break;
michael@0 4827 case "TabClose":
michael@0 4828 this._tabOnTabClose(aEvent);
michael@0 4829 break;
michael@0 4830 case "scroll":
michael@0 4831 this._updateTabsVisibilityStatus();
michael@0 4832 break;
michael@0 4833 }
michael@0 4834 ]]></body>
michael@0 4835 </method>
michael@0 4836
michael@0 4837 <method name="_updateTabsVisibilityStatus">
michael@0 4838 <body><![CDATA[
michael@0 4839 var tabContainer = gBrowser.tabContainer;
michael@0 4840 // We don't want menu item decoration unless there is overflow.
michael@0 4841 if (tabContainer.getAttribute("overflow") != "true")
michael@0 4842 return;
michael@0 4843
michael@0 4844 var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
michael@0 4845 for (var i = 0; i < this.childNodes.length; i++) {
michael@0 4846 let curTab = this.childNodes[i].tab;
michael@0 4847 if (!curTab) // "Tab Groups" menuitem and its menuseparator
michael@0 4848 continue;
michael@0 4849 let curTabBO = curTab.boxObject;
michael@0 4850 if (curTabBO.screenX >= tabstripBO.screenX &&
michael@0 4851 curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
michael@0 4852 this.childNodes[i].setAttribute("tabIsVisible", "true");
michael@0 4853 else
michael@0 4854 this.childNodes[i].removeAttribute("tabIsVisible");
michael@0 4855 }
michael@0 4856 ]]></body>
michael@0 4857 </method>
michael@0 4858
michael@0 4859 <method name="_createTabMenuItem">
michael@0 4860 <parameter name="aTab"/>
michael@0 4861 <body><![CDATA[
michael@0 4862 var menuItem = document.createElementNS(
michael@0 4863 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
michael@0 4864 "menuitem");
michael@0 4865
michael@0 4866 menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon");
michael@0 4867
michael@0 4868 this._setMenuitemAttributes(menuItem, aTab);
michael@0 4869
michael@0 4870 aTab.mCorrespondingMenuitem = menuItem;
michael@0 4871 menuItem.tab = aTab;
michael@0 4872
michael@0 4873 this.appendChild(menuItem);
michael@0 4874 ]]></body>
michael@0 4875 </method>
michael@0 4876
michael@0 4877 <method name="_setMenuitemAttributes">
michael@0 4878 <parameter name="aMenuitem"/>
michael@0 4879 <parameter name="aTab"/>
michael@0 4880 <body><![CDATA[
michael@0 4881 aMenuitem.setAttribute("label", aTab.label);
michael@0 4882 aMenuitem.setAttribute("crop", aTab.getAttribute("crop"));
michael@0 4883
michael@0 4884 if (aTab.hasAttribute("busy")) {
michael@0 4885 aMenuitem.setAttribute("busy", aTab.getAttribute("busy"));
michael@0 4886 aMenuitem.removeAttribute("image");
michael@0 4887 } else {
michael@0 4888 aMenuitem.setAttribute("image", aTab.getAttribute("image"));
michael@0 4889 aMenuitem.removeAttribute("busy");
michael@0 4890 }
michael@0 4891
michael@0 4892 if (aTab.hasAttribute("pending"))
michael@0 4893 aMenuitem.setAttribute("pending", aTab.getAttribute("pending"));
michael@0 4894 else
michael@0 4895 aMenuitem.removeAttribute("pending");
michael@0 4896
michael@0 4897 if (aTab.selected)
michael@0 4898 aMenuitem.setAttribute("selected", "true");
michael@0 4899 else
michael@0 4900 aMenuitem.removeAttribute("selected");
michael@0 4901 ]]></body>
michael@0 4902 </method>
michael@0 4903 </implementation>
michael@0 4904
michael@0 4905 <handlers>
michael@0 4906 <handler event="popupshowing">
michael@0 4907 <![CDATA[
michael@0 4908 var tabcontainer = gBrowser.tabContainer;
michael@0 4909
michael@0 4910 // Listen for changes in the tab bar.
michael@0 4911 tabcontainer.addEventListener("TabAttrModified", this, false);
michael@0 4912 tabcontainer.addEventListener("TabClose", this, false);
michael@0 4913 tabcontainer.mTabstrip.addEventListener("scroll", this, false);
michael@0 4914
michael@0 4915 let tabs = gBrowser.visibleTabs;
michael@0 4916 for (var i = 0; i < tabs.length; i++) {
michael@0 4917 if (!tabs[i].pinned)
michael@0 4918 this._createTabMenuItem(tabs[i]);
michael@0 4919 }
michael@0 4920 this._updateTabsVisibilityStatus();
michael@0 4921 ]]></handler>
michael@0 4922
michael@0 4923 <handler event="popuphidden">
michael@0 4924 <![CDATA[
michael@0 4925 // clear out the menu popup and remove the listeners
michael@0 4926 for (let i = this.childNodes.length - 1; i > 0; i--) {
michael@0 4927 let menuItem = this.childNodes[i];
michael@0 4928 if (menuItem.tab) {
michael@0 4929 menuItem.tab.mCorrespondingMenuitem = null;
michael@0 4930 this.removeChild(menuItem);
michael@0 4931 }
michael@0 4932 }
michael@0 4933 var tabcontainer = gBrowser.tabContainer;
michael@0 4934 tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
michael@0 4935 tabcontainer.removeEventListener("TabAttrModified", this, false);
michael@0 4936 tabcontainer.removeEventListener("TabClose", this, false);
michael@0 4937 ]]></handler>
michael@0 4938
michael@0 4939 <handler event="DOMMenuItemActive">
michael@0 4940 <![CDATA[
michael@0 4941 var tab = event.target.tab;
michael@0 4942 if (tab) {
michael@0 4943 let overLink = tab.linkedBrowser.currentURI.spec;
michael@0 4944 if (overLink == "about:blank")
michael@0 4945 overLink = "";
michael@0 4946 XULBrowserWindow.setOverLink(overLink, null);
michael@0 4947 }
michael@0 4948 ]]></handler>
michael@0 4949
michael@0 4950 <handler event="DOMMenuItemInactive">
michael@0 4951 <![CDATA[
michael@0 4952 XULBrowserWindow.setOverLink("", null);
michael@0 4953 ]]></handler>
michael@0 4954
michael@0 4955 <handler event="command"><![CDATA[
michael@0 4956 if (event.target.tab)
michael@0 4957 gBrowser.selectedTab = event.target.tab;
michael@0 4958 ]]></handler>
michael@0 4959
michael@0 4960 </handlers>
michael@0 4961 </binding>
michael@0 4962
michael@0 4963 <binding id="statuspanel" display="xul:hbox">
michael@0 4964 <content>
michael@0 4965 <xul:hbox class="statuspanel-inner">
michael@0 4966 <xul:label class="statuspanel-label"
michael@0 4967 role="status"
michael@0 4968 aria-live="off"
michael@0 4969 xbl:inherits="value=label,crop,mirror"
michael@0 4970 flex="1"
michael@0 4971 crop="end"/>
michael@0 4972 </xul:hbox>
michael@0 4973 </content>
michael@0 4974
michael@0 4975 <implementation implements="nsIDOMEventListener">
michael@0 4976 <constructor><![CDATA[
michael@0 4977 window.addEventListener("resize", this, false);
michael@0 4978 ]]></constructor>
michael@0 4979
michael@0 4980 <destructor><![CDATA[
michael@0 4981 window.removeEventListener("resize", this, false);
michael@0 4982 MousePosTracker.removeListener(this);
michael@0 4983 ]]></destructor>
michael@0 4984
michael@0 4985 <property name="label">
michael@0 4986 <setter><![CDATA[
michael@0 4987 if (!this.label) {
michael@0 4988 this.removeAttribute("mirror");
michael@0 4989 this.removeAttribute("sizelimit");
michael@0 4990 }
michael@0 4991
michael@0 4992 this.style.minWidth = this.getAttribute("type") == "status" &&
michael@0 4993 this.getAttribute("previoustype") == "status"
michael@0 4994 ? getComputedStyle(this).width : "";
michael@0 4995
michael@0 4996 if (val) {
michael@0 4997 this.setAttribute("label", val);
michael@0 4998 this.removeAttribute("inactive");
michael@0 4999 this._calcMouseTargetRect();
michael@0 5000 MousePosTracker.addListener(this);
michael@0 5001 } else {
michael@0 5002 this.setAttribute("inactive", "true");
michael@0 5003 MousePosTracker.removeListener(this);
michael@0 5004 }
michael@0 5005
michael@0 5006 return val;
michael@0 5007 ]]></setter>
michael@0 5008 <getter>
michael@0 5009 return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
michael@0 5010 </getter>
michael@0 5011 </property>
michael@0 5012
michael@0 5013 <method name="getMouseTargetRect">
michael@0 5014 <body><![CDATA[
michael@0 5015 return this._mouseTargetRect;
michael@0 5016 ]]></body>
michael@0 5017 </method>
michael@0 5018
michael@0 5019 <method name="onMouseEnter">
michael@0 5020 <body>
michael@0 5021 this._mirror();
michael@0 5022 </body>
michael@0 5023 </method>
michael@0 5024
michael@0 5025 <method name="onMouseLeave">
michael@0 5026 <body>
michael@0 5027 this._mirror();
michael@0 5028 </body>
michael@0 5029 </method>
michael@0 5030
michael@0 5031 <method name="handleEvent">
michael@0 5032 <parameter name="event"/>
michael@0 5033 <body><![CDATA[
michael@0 5034 if (!this.label)
michael@0 5035 return;
michael@0 5036
michael@0 5037 switch (event.type) {
michael@0 5038 case "resize":
michael@0 5039 this._calcMouseTargetRect();
michael@0 5040 break;
michael@0 5041 }
michael@0 5042 ]]></body>
michael@0 5043 </method>
michael@0 5044
michael@0 5045 <method name="_calcMouseTargetRect">
michael@0 5046 <body><![CDATA[
michael@0 5047 let container = this.parentNode;
michael@0 5048 let alignRight = (getComputedStyle(container).direction == "rtl");
michael@0 5049 let panelRect = this.getBoundingClientRect();
michael@0 5050 let containerRect = container.getBoundingClientRect();
michael@0 5051
michael@0 5052 this._mouseTargetRect = {
michael@0 5053 top: panelRect.top,
michael@0 5054 bottom: panelRect.bottom,
michael@0 5055 left: alignRight ? containerRect.right - panelRect.width : containerRect.left,
michael@0 5056 right: alignRight ? containerRect.right : containerRect.left + panelRect.width
michael@0 5057 };
michael@0 5058 ]]></body>
michael@0 5059 </method>
michael@0 5060
michael@0 5061 <method name="_mirror">
michael@0 5062 <body>
michael@0 5063 if (this.hasAttribute("mirror"))
michael@0 5064 this.removeAttribute("mirror");
michael@0 5065 else
michael@0 5066 this.setAttribute("mirror", "true");
michael@0 5067
michael@0 5068 if (!this.hasAttribute("sizelimit")) {
michael@0 5069 this.setAttribute("sizelimit", "true");
michael@0 5070 this._calcMouseTargetRect();
michael@0 5071 }
michael@0 5072 </body>
michael@0 5073 </method>
michael@0 5074 </implementation>
michael@0 5075 </binding>
michael@0 5076
michael@0 5077 </bindings>

mercurial