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