Wed, 31 Dec 2014 06:09:35 +0100
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> |