]> git.donarmstrong.com Git - x_full.git/blob - .mozilla/firefox/default/extensions/vertical-tabbar@dark-demon.nm.ru/chrome/vertical-tabbar/content/browser.xbl
74ff543931a34f8184bea60276b2c53251fe7747
[x_full.git] / .mozilla / firefox / default / extensions / vertical-tabbar@dark-demon.nm.ru / chrome / vertical-tabbar / content / browser.xbl
1 <?xml version="1.0"?>
2
3 <!DOCTYPE bindings [
4 <!ENTITY % tabBrowserDTD SYSTEM "chrome://global/locale/tabbrowser.dtd" >
5 %tabBrowserDTD;
6 <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
7 %globalDTD;
8 ]>
9
10 <bindings id="vertical-tabbar-bindings"
11           xmlns="http://www.mozilla.org/xbl"
12           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
13           xmlns:xbl="http://www.mozilla.org/xbl">
14
15
16
17   <binding id="tabbrowser">
18     <content>
19       <xul:stringbundle anonid="tbstringbundle" src="chrome://global/locale/tabbrowser.properties"/>
20       <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
21                   onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
22         <xul:vbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child"
23                   anonid="strip" width="102" orient="vertical"
24                   ondraggesture="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
25                   ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
26                   ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
27                   ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
28           <xul:tooltip onpopupshowing="return this.parentNode.parentNode.parentNode.createTooltip(event);"/>
29           <xul:menupopup anonid="tabContextMenu" onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
30             <xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;"
31                           xbl:inherits="oncommand=onnewtab"/>
32             <xul:menuseparator/>
33             <xul:menuitem label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
34                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
35                                      tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
36             <xul:menuitem label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
37                           tbattr="tabbrowser-multiple"
38                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
39                                      tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
40             <xul:menuitem label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
41                           tbattr="tabbrowser-multiple"
42                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
43                                      tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
44             <xul:menuseparator/>
45             <xul:menuitem label="&closeTab.label;" accesskey="&closeTab.accesskey;"
46                           tbattr="tabbrowser-multiple"
47                           oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
48                                      tabbrowser.removeTab(tabbrowser.mContextTab);"/>
49           </xul:menupopup>
50
51           <xul:tabs class="tabbrowser-tabs" flex="1"
52                     anonid="tabcontainer"
53                     setfocus="false"
54                     onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
55                     xbl:inherits="onnewtab"
56                     ondblclick="this.parentNode.parentNode.parentNode.onTabBarDblClick(event);"
57                     onclosetab="var node = this.parentNode;
58                                 while (node.localName != 'tabbrowser')
59                                   node = node.parentNode;
60                                 node.removeCurrentTab();">
61             <xul:tab selected="true" validate="never"
62                      onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image'));
63                               this.removeAttribute('image');"
64                       flex="100"
65                      class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
66           </xul:tabs>
67         </xul:vbox>
68         <xul:splitter class="tabbrowser-splitter" collapse="before" resizeafter="closest" resizebefore="closest" ondblclick="this.previousSibling.width=102">
69                 <xul:grippy />
70         </xul:splitter>
71         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
72           <xul:notificationbox flex="1">
73             <xul:browser flex="1" type="content-primary" message="true" disablehistory="true"
74                          xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
75           </xul:notificationbox>
76         </xul:tabpanels>
77       </xul:tabbox>
78       <children/>
79     </content>
80     <implementation>
81       <field name="mTabstrip">
82         document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
83       </field>
84       <field name="mPrefs" readonly="true">
85         Components.classes['@mozilla.org/preferences-service;1']
86                   .getService(Components.interfaces.nsIPrefService)
87                   .getBranch(null);
88       </field>
89       <field name="mURIFixup" readonly="true">
90         Components.classes["@mozilla.org/docshell/urifixup;1"]
91                   .getService(Components.interfaces.nsIURIFixup);
92       </field>
93       <field name="mTabBox" readonly="true">
94         document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
95       </field>
96       <field name="mTabDropIndicatorBar">
97         this.mTabBox.childNodes[0]
98       </field>
99       <field name="mStrip" readonly="true">
100         document.getAnonymousElementByAttribute(this, "anonid", "strip");
101       </field>
102       <field name="mTabContainer" readonly="true">
103         document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
104       </field>
105       <field name="mPanelContainer" readonly="true">
106         document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
107       </field>
108       <field name="mTabs" readonly="true">
109         this.mTabContainer.childNodes
110       </field>
111       <field name="mStringBundle">
112         document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
113       </field>
114       <field name="mCurrentTab">
115         null
116       </field>
117       <field name="mCurrentBrowser">
118         null
119       </field>
120       <field name="mProgressListeners">
121         []
122       </field>
123       <field name="mTabListeners">
124         new Array()
125       </field>
126       <field name="mTabFilters">
127         new Array()
128       </field>
129       <field name="mTabbedMode">
130         false
131       </field>
132       <field name="mIsBusy">
133         false
134       </field>
135       <field name="mMissedIconCache">
136         null
137       </field>
138       <field name="mContextTab">
139         null
140       </field>
141       <field name="mModalDialogShowing">
142         false
143       </field>
144       <field name="arrowKeysShouldWrap" readonly="true">
145         false
146       </field>
147       <field name="mAddProgressListenerWasCalled">
148         false
149       </field>
150       <field name="_browsers">
151         null
152       </field>
153
154       <field name="_blockDblClick">
155         false
156       </field>
157
158       <method name="getBrowserAtIndex">
159         <parameter name="aIndex"/>
160         <body>
161           <![CDATA[
162             return this.mTabContainer.childNodes[aIndex].linkedBrowser;
163           ]]>
164         </body>
165       </method>
166
167       <method name="getBrowserIndexForDocument">
168         <parameter name="aDocument"/>
169         <body>
170           <![CDATA[
171             for (var i = 0; i < this.mPanelContainer.childNodes.length; i++) {
172               if (this.getBrowserAtIndex(i).contentDocument == aDocument) {
173                 return i;
174               }
175             }
176             return -1;
177           ]]>
178         </body>
179       </method>
180
181       <method name="getBrowserForDocument">
182         <parameter name="aDocument"/>
183         <body>
184           <![CDATA[
185             var index = this.getBrowserIndexForDocument(aDocument);
186             if (index < 0)
187               return null;
188             return this.getBrowserAtIndex(index);
189           ]]>
190         </body>
191       </method>
192
193       <method name="getNotificationBox">
194         <parameter name="aBrowser"/>
195         <body>
196           <![CDATA[
197             if (aBrowser)
198               return aBrowser.parentNode;
199             else if (this.mCurrentBrowser)
200               return this.mCurrentBrowser.parentNode;
201             return null;
202           ]]>
203         </body>
204       </method>
205
206       <!-- A web progress listener object definition for a given tab. -->
207       <method name="mTabProgressListener">
208         <parameter name="aTab"/>
209         <parameter name="aBrowser"/>
210         <parameter name="aStartsBlank"/>
211         <body>
212         <![CDATA[
213           return ({
214             mTabBrowser: this,
215             mTab: aTab,
216             mBrowser: aBrowser,
217             mBlank: aStartsBlank,
218             mLastURI: null,
219
220             // cache flags for correct status bar update after tab switching
221             mStateFlags: 0,
222             mStatus: 0,
223             mMessage: "",
224             mTotalProgress: 0,
225
226             // count of open requests (should always be 0 or 1)
227             mRequestCount: 0,
228
229             onProgressChange : function (aWebProgress, aRequest,
230                                          aCurSelfProgress, aMaxSelfProgress,
231                                          aCurTotalProgress, aMaxTotalProgress)
232             {
233               if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
234                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
235                   var p = this.mTabBrowser.mProgressListeners[i];
236                   if (p)
237                     p.onProgressChange(aWebProgress, aRequest,
238                                        aCurSelfProgress, aMaxSelfProgress,
239                                        aCurTotalProgress, aMaxTotalProgress);
240                 }
241               }
242
243               this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
244             },
245
246             onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
247             {
248               if (!aRequest)
249                 return;
250
251               var oldBlank = this.mBlank;
252
253               const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
254               const nsIChannel = Components.interfaces.nsIChannel;
255
256               if (aStateFlags & nsIWebProgressListener.STATE_START) {
257                 this.mRequestCount++;
258               }
259               else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
260                 const NS_ERROR_UNKNOWN_HOST = 2152398878;
261                 if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
262                   // to prevent bug 235825: wait for the request handled
263                   // by the automatic keyword resolver
264                   return;
265                 }
266                 // since we (try to) only handle STATE_STOP of the last request,
267                 // the count of open requests should now be 0
268                 this.mRequestCount = 0;
269               }
270
271               if (aStateFlags & nsIWebProgressListener.STATE_START &&
272                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
273                 // It's okay to clear what the user typed when we start
274                 // loading a document. If the user types, this counter gets
275                 // set to zero, if the document load ends without an
276                 // onLocationChange, this counter gets decremented
277                 // (so we keep it while switching tabs after failed loads)
278                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
279                   this.mBrowser.userTypedClear++;
280
281                 if (!this.mBlank) {
282                   this.mTab.setAttribute("busy", "true");
283                   this.mTabBrowser.updateIcon(this.mTab);
284                   this.mTabBrowser.setTabTitleLoading(this.mTab);
285
286                   if (this.mTabBrowser.mCurrentTab == this.mTab)
287                     this.mTabBrowser.mIsBusy = true;
288                 }
289               }
290               else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
291                        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
292                 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow) {
293                   // The document is done loading, we no longer want the
294                   // value cleared.
295                   if (this.mBrowser.userTypedClear > 0)
296                     this.mBrowser.userTypedClear--;
297
298                   if (!this.mBrowser.mIconURL)
299                     this.mTabBrowser.useDefaultIcon(this.mTab);
300                 }
301
302                 if (this.mBlank)
303                   this.mBlank = false;
304
305                 this.mTab.removeAttribute("busy");
306                 this.mTabBrowser.updateIcon(this.mTab);
307
308                 var location = aRequest.QueryInterface(nsIChannel).URI;
309
310                 // For keyword URIs clear the user typed value since they will be changed into real URIs
311                 if (location.scheme == "keyword")
312                   this.mBrowser.userTypedValue = null;
313
314                 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
315                   this.mTabBrowser.setTabTitle(this.mTab);
316
317                 if (this.mTabBrowser.mCurrentTab == this.mTab)
318                   this.mTabBrowser.mIsBusy = false;
319               }
320
321               if (this.mTabBrowser.mCurrentTab == this.mTab) {
322                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
323                   var p = this.mTabBrowser.mProgressListeners[i];
324                   if (p && !oldBlank)
325                     p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
326                   // make sure that the visible status of new blank tabs is correctly set
327                   else if (p && "onUpdateCurrentBrowser" in p)
328                     p.onUpdateCurrentBrowser(aStateFlags, aStatus, "", 0);
329                 }
330               }
331
332               if (aStateFlags & (nsIWebProgressListener.STATE_START |
333                                  nsIWebProgressListener.STATE_STOP)) {
334                 // reset cached temporary values at beginning and end
335                 this.mMessage = "";
336                 this.mTotalProgress = 0;
337               }
338               this.mStateFlags = aStateFlags;
339               this.mStatus = aStatus;
340             },
341
342             onLocationChange : function(aWebProgress, aRequest, aLocation)
343             {
344               // The document loaded correctly, clear the value if we should
345               if (this.mBrowser.userTypedClear > 0 && aRequest)
346                 this.mBrowser.userTypedValue = null;
347
348               if (aWebProgress.DOMWindow == this.mBrowser.contentWindow &&
349                   aWebProgress.isLoadingDocument)
350                 this.mTabBrowser.setIcon(this.mTab, null);
351
352               // changing location, clear out the missing plugins list
353               this.mTab.missingPlugins = null;
354
355               if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
356                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
357                   var p = this.mTabBrowser.mProgressListeners[i];
358                   if (p)
359                     p.onLocationChange(aWebProgress, aRequest, aLocation);
360                 }
361               }
362             },
363
364             onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
365             {
366               if (this.mBlank)
367                 return;
368
369               if (this.mTabBrowser.mCurrentTab == this.mTab) {
370                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
371                   var p = this.mTabBrowser.mProgressListeners[i];
372                   if (p)
373                     p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
374                 }
375               }
376
377               this.mMessage = aMessage;
378             },
379
380             onSecurityChange : function(aWebProgress, aRequest, aState)
381             {
382               if (this.mTabBrowser.mCurrentTab == this.mTab) {
383                 for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
384                   var p = this.mTabBrowser.mProgressListeners[i];
385                   if (p)
386                     p.onSecurityChange(aWebProgress, aRequest, aState);
387                 }
388               }
389             },
390
391             QueryInterface : function(aIID)
392             {
393               if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
394                   aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
395                   aIID.equals(Components.interfaces.nsISupports))
396                 return this;
397               throw Components.results.NS_NOINTERFACE;
398             }
399           });
400         ]]>
401         </body>
402       </method>
403
404       <method name="setIcon">
405         <parameter name="aTab"/>
406         <parameter name="aURI"/>
407         <body>
408           <![CDATA[
409             var browser = this.getBrowserForTab(aTab);
410             browser.mIconURL = aURI;
411
412             this.updateIcon(aTab);
413
414             for (var i = 0; i < this.mProgressListeners.length; i++) {
415               var p = this.mProgressListeners[i];
416               if ('onLinkIconAvailable' in p)
417                 p.onLinkIconAvailable(browser);
418             }
419           ]]>
420         </body>
421       </method>
422
423       <method name="updateIcon">
424         <parameter name="aTab"/>
425         <body>
426           <![CDATA[
427             var browser = this.getBrowserForTab(aTab);
428             if (!aTab.hasAttribute("busy") && browser.mIconURL)
429               aTab.setAttribute("image", browser.mIconURL);
430             else
431               aTab.removeAttribute("image");
432           ]]>
433         </body>
434       </method>
435
436       <method name="shouldLoadFavIcon">
437         <parameter name="aURI"/>
438         <body>
439           <![CDATA[
440             return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
441                     this.mPrefs.getBoolPref("browser.chrome.favicons") &&
442                     ("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
443           ]]>
444         </body>
445       </method>
446
447       <method name="useDefaultIcon">
448         <parameter name="aTab"/>
449         <body>
450           <![CDATA[
451             var browser = this.getBrowserForTab(aTab);
452             if (browser.contentDocument instanceof ImageDocument) {
453               if (this.mPrefs.getBoolPref("browser.chrome.site_icons")) {
454                 try {
455                   var sz = this.mPrefs.getIntPref("browser.chrome.image_icons.max_size");
456                   if (!sz)
457                     return;
458
459                   var req = browser.contentDocument.imageRequest;
460                   if (!req || !req.image ||
461                       req.image.width > sz ||
462                       req.image.height > sz)
463                     return;
464
465                   this.setIcon(aTab, browser.currentURI.spec);
466                 } catch (e) { }
467               }
468             }
469             else if (this.shouldLoadFavIcon(browser.currentURI)) {
470               var url = browser.currentURI.prePath + "/favicon.ico";
471               if (!this.isIconKnownMissing(url))
472                 this.setIcon(aTab, url);
473             }
474           ]]>
475         </body>
476       </method>
477
478       <method name="addToMissedIconCache">
479         <parameter name="aURI"/>
480         <body>
481           <![CDATA[
482             var entry = this.openCacheEntry(aURI, Components.interfaces.nsICache.ACCESS_READ_WRITE);
483             if (!entry)
484               return;
485
486             if (entry.accessGranted == Components.interfaces.nsICache.ACCESS_WRITE)
487               // It's a new entry.  Just write a bit of metadata in to the entry.
488               entry.setMetaDataElement("Icon", "Missed");
489             entry.markValid();
490             entry.close();
491           ]]>
492         </body>
493       </method>
494
495       <method name="openCacheEntry">
496         <parameter name="key"/>
497         <parameter name="access"/>
498         <body>
499           <![CDATA[
500             try {
501               if (!this.mMissedIconCache) {
502                 var cacheService = Components.classes['@mozilla.org/network/cache-service;1'].getService(Components.interfaces.nsICacheService);
503                 this.mMissedIconCache = cacheService.createSession("MissedIconCache", Components.interfaces.nsICache.STORE_ANYWHERE, true);
504                 if (!this.mMissedIconCache)
505                   return null;
506               }
507               return this.mMissedIconCache.openCacheEntry(key, access, true);
508             }
509             catch (e) {
510               return null;
511             }
512           ]]>
513         </body>
514       </method>
515
516       <method name="isIconKnownMissing">
517         <parameter name="key"/>
518         <body>
519           <![CDATA[
520             var e = this.openCacheEntry(key, Components.interfaces.nsICache.ACCESS_READ);
521             if (e) {
522                 e.close();
523                 return true;
524             }
525             return false;
526           ]]>
527         </body>
528       </method>
529
530       <method name="updateTitlebar">
531         <body>
532           <![CDATA[
533             var newTitle = "";
534             var docTitle;
535             var docElement = this.ownerDocument.documentElement;
536             var sep = docElement.getAttribute("titlemenuseparator");
537
538             if (this.docShell.contentViewer)
539               docTitle = this.contentTitle;
540
541             if (!docTitle)
542               docTitle = docElement.getAttribute("titledefault");
543
544             var modifier = docElement.getAttribute("titlemodifier");
545             if (docTitle) {
546               newTitle += docElement.getAttribute("titlepreface");
547               newTitle += docTitle;
548               if (modifier)
549                 newTitle += sep;
550             }
551             newTitle += modifier;
552
553             // If location bar is hidden and the URL type supports a host,
554             // add the scheme and host to the title to prevent spoofing.
555             // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
556             // (only for schemes that support a host)
557             try {
558               if (docElement.getAttribute("chromehidden").indexOf("location") != -1) {
559                 var uri = this.mURIFixup.createExposableURI(
560                             this.mCurrentBrowser.currentURI);
561                 if (uri.host)
562                   newTitle = uri.prePath + sep + newTitle;
563               }
564             } catch (e) {}
565
566             this.ownerDocument.title = newTitle;
567           ]]>
568         </body>
569       </method>
570
571       <method name="updatePopupMenu">
572         <parameter name="aPopupMenu"/>
573         <body>
574           <![CDATA[
575             this.mContextTab = document.popupNode;
576             var disabled = this.mPanelContainer.childNodes.length == 1;
577             var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
578             for (var i = 0; i < menuItems.length; i++)
579               menuItems[i].disabled = disabled;
580           ]]>
581         </body>
582       </method>
583
584       <method name="updateCurrentBrowser">
585         <body>
586           <![CDATA[
587             var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
588             if (this.mCurrentBrowser == newBrowser)
589               return;
590
591             if (this.mCurrentBrowser) {
592               // Only save the focused element if it is in our content window
593               // or in an ancestor window.
594               var focusedWindow = document.commandDispatcher.focusedWindow;
595               var saveFocus = false;
596
597               if (focusedWindow && focusedWindow.top == window.content) {
598                 saveFocus = true;
599               } else {
600                 var contentWindow = window;
601
602                 while (contentWindow) {
603                   if (contentWindow == focusedWindow) {
604                     saveFocus = true;
605                     break;
606                   }
607
608                   if (contentWindow.parent == contentWindow) {
609                     break;
610                   }
611
612                   contentWindow = contentWindow.parent;
613                 }
614               }
615
616               if (saveFocus) {
617                 // Preserve the currently-focused element or DOM window for
618                 // this tab.
619
620                 this.mCurrentBrowser.focusedWindow = focusedWindow;
621                 this.mCurrentBrowser.focusedElement = document.commandDispatcher.focusedElement;
622               }
623
624               if (this.mCurrentBrowser.focusedElement instanceof NSHTMLElement || 
625                   this.mCurrentBrowser.focusedElement instanceof XULElement) {
626                 // Clear focus outline before we draw on top of it
627                 this.mCurrentBrowser.focusedElement.blur();
628               }
629               else {
630                 // non-HTML/XUL elements have no blur method, see bug 323805
631                 this.mCurrentBrowser.focusedElement = null;
632               }
633               this.mCurrentBrowser.setAttribute("type", "content-targetable");
634             }
635
636             var updatePageReport = false;
637             if ((this.mCurrentBrowser.pageReport && !newBrowser.pageReport) ||
638                 (!this.mCurrentBrowser.pageReport && newBrowser.pageReport))
639               updatePageReport = true;
640
641             newBrowser.setAttribute("type", "content-primary");
642             this.mCurrentBrowser = newBrowser;
643             this.mCurrentTab = this.selectedTab;
644
645             if (updatePageReport)
646               this.mCurrentBrowser.updatePageReport();
647
648             // Update the URL bar.
649             var loc = this.mCurrentBrowser.currentURI;
650             if (!loc)
651               loc = ({ spec: "" });
652
653             var webProgress = this.mCurrentBrowser.webProgress;
654             var securityUI = this.mCurrentBrowser.securityUI;
655
656             var i, p;
657             for (i = 0; i < this.mProgressListeners.length; i++) {
658               p = this.mProgressListeners[i];
659               if (p) {
660                 p.onLocationChange(webProgress, null, loc);
661                 if (securityUI)
662                   p.onSecurityChange(webProgress, null, securityUI.state);
663
664                 // make sure that all status indicators are properly updated
665                 if ("onUpdateCurrentBrowser" in p) {
666                   var listener = this.mTabListeners[this.mTabContainer.selectedIndex] || null;
667                   if (listener && listener.mStateFlags)
668                     p.onUpdateCurrentBrowser(listener.mStateFlags, listener.mStatus,
669                                              listener.mMessage, listener.mTotalProgress);
670                 }
671               }
672             }
673
674             this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
675
676             // Update the window title.
677             this.updateTitlebar();
678
679             // If the new tab is busy, and our current state is not busy, then
680             // we need to fire a start to all progress listeners.
681             const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
682             if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
683               this.mIsBusy = true;
684               webProgress = this.mCurrentBrowser.webProgress;
685               for (i = 0; i < this.mProgressListeners.length; i++) {
686                 p = this.mProgressListeners[i];
687                 if (p)
688                   p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
689               }
690             }
691
692             // If the new tab is not busy, and our current state is busy, then
693             // we need to fire a stop to all progress listeners.
694             if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
695               this.mIsBusy = false;
696               webProgress = this.mCurrentBrowser.webProgress;
697               for (i = 0; i < this.mProgressListeners.length; i++) {
698                 p = this.mProgressListeners[i];
699                 if (p)
700                   p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
701               }
702             }
703
704             // We've selected the new tab, so go ahead and notify listeners.
705             var event = document.createEvent("Events");
706             event.initEvent("TabSelect", true, false);
707             this.mCurrentTab.dispatchEvent(event);
708
709             if (document.commandDispatcher.focusedElement &&
710                 document.commandDispatcher.focusedElement.parentNode ==
711                 this.mCurrentTab.parentNode) {
712               // The focus is on a tab in the same tab panel
713               return;  // If focus was on a tab, switching tabs focuses the new tab
714             }
715
716             var whatToFocus = window.content;
717
718             // Focus the previously focused element or window
719             if (newBrowser.focusedElement) {
720               if (newBrowser.focusedElement.parentNode !=
721                   this.mCurrentTab.parentNode) {
722                 // Focus the remembered element unless it's in the current tab panel
723                 whatToFocus = newBrowser.focusedElement;
724               }
725             }
726             else if (newBrowser.focusedWindow) {
727               whatToFocus = newBrowser.focusedWindow;
728             }
729
730             function setFocus(element) {
731               document.commandDispatcher.suppressFocusScroll = true;
732
733               if (element instanceof Window ||
734                   element instanceof NSHTMLElement || 
735                   element instanceof XULElement) {
736                 try {
737                   element.focus();
738                 }
739                 catch(ex) {
740                   dump("XXXfocus() failed, see bug #348183: ex = " + ex + "\n");
741                 }
742               }
743               document.commandDispatcher.suppressFocusScroll = false;
744             }
745
746             // Use setTimeout to avoid focus outline ghosting.
747             setTimeout(setFocus, 0, whatToFocus);
748           ]]>
749         </body>
750       </method>
751
752       <method name="onTabClick">
753         <parameter name="event"/>
754         <body>
755           <![CDATA[
756             if (event.button != 1 || event.target.localName != 'tab')
757               return;
758
759             this.removeTab(event.target);
760             event.stopPropagation();
761           ]]>
762         </body>
763       </method>
764
765       <method name="onLinkAdded">
766         <parameter name="event"/>
767         <body>
768           <![CDATA[
769             if (!this.mPrefs.getBoolPref("browser.chrome.site_icons"))
770               return;
771
772             if (!event.originalTarget.rel.match((/(?:^|\s)icon(?:\s|$)/i)))
773               return;
774
775             // We have an icon.
776             var href = event.originalTarget.href;
777             if (!href)
778               return;
779
780             const nsIContentPolicy = Components.interfaces.nsIContentPolicy;
781             try {
782               var contentPolicy =
783                 Components.classes['@mozilla.org/layout/content-policy;1']
784                           .getService(nsIContentPolicy);
785             } catch(e) {
786               return; // Refuse to load if we can't do a security check.
787             }
788
789             // Verify that the load of this icon is legal.
790             // We check first with the security manager
791             const secMan =
792               Components.classes["@mozilla.org/scriptsecuritymanager;1"]
793                         .getService(Components.interfaces.nsIScriptSecurityManager);
794
795             // Get the IOService so we can make URIs
796             const ioService =
797               Components.classes["@mozilla.org/network/io-service;1"]
798                         .getService(Components.interfaces.nsIIOService);
799
800             const targetDoc = event.target.ownerDocument;
801             // Make a URI out of our href.
802             var uri = ioService.newURI(href, targetDoc.characterSet, null);
803
804             var origURI = ioService.newURI(targetDoc.documentURI, targetDoc.characterSet, null);
805
806             const nsIScriptSecMan =
807               Components.interfaces.nsIScriptSecurityManager;
808
809             try {
810               // error pages can load their favicon
811               // to be on the safe side, only allow chrome:// favicons
812               const aboutNeterr = "about:neterror?";
813               if (origURI.spec.substr(0, aboutNeterr.length) != aboutNeterr ||
814                   !uri.schemeIs("chrome"))
815                 secMan.checkLoadURI(origURI, uri,
816                                     nsIScriptSecMan.DISALLOW_SCRIPT);
817             } catch(e) {
818               return;
819             }
820
821             // Security says okay, now ask content policy
822             if (contentPolicy.shouldLoad(nsIContentPolicy.TYPE_IMAGE,
823                                          uri, origURI, event.target,
824                                          event.target.type,
825                                          null) != nsIContentPolicy.ACCEPT)
826               return;
827
828             var browserIndex = this.getBrowserIndexForDocument(targetDoc);
829             // no browser? no favicon.
830             if (browserIndex == -1)
831               return;
832
833             var tab = this.mTabContainer.childNodes[browserIndex];
834             this.setIcon(tab, href);
835           ]]>
836         </body>
837       </method>
838
839       <method name="onTitleChanged">
840         <parameter name="evt"/>
841         <body>
842           <![CDATA[
843             if (evt.target != this.contentDocument)
844               return;
845
846             var i = 0;
847             for ( ; i < this.parentNode.parentNode.childNodes.length; i++) {
848               if (this.parentNode.parentNode.childNodes[i].firstChild == this)
849                 break;
850             }
851
852             var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;
853
854             var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id);
855             tabBrowser.setTabTitle(tab);
856
857             if (tab == tabBrowser.mCurrentTab)
858               tabBrowser.updateTitlebar();
859           ]]>
860         </body>
861       </method>
862
863       <method name="setTabTitleLoading">
864         <parameter name="aTab"/>
865         <body>
866           <![CDATA[
867             aTab.label = this.mStringBundle.getString("tabs.loading");
868             aTab.setAttribute("crop", "end");
869           ]]>
870         </body>
871       </method>
872
873       <method name="setTabTitle">
874         <parameter name="aTab"/>
875         <body>
876           <![CDATA[
877             var browser = this.getBrowserForTab(aTab);
878             var title = browser.contentDocument.title;
879
880             if (!title) {
881               if (browser.currentURI.spec) {
882                 try {
883                   title = this.mURIFixup.createExposableURI(browser.currentURI).spec;
884                 } catch(ex) {
885                   title = browser.currentURI.spec;
886                 }
887               }
888
889               if (title && title != "about:blank") {
890                 // At this point, we now have a URI.
891                 // Let's try to unescape it using a character set
892                 // in case the URI is not ASCII.
893                 try {
894                   var characterSet = browser.contentDocument.characterSet;
895                   const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
896                                                  .getService(Components.interfaces.nsITextToSubURI);
897                   title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
898                 } catch(ex) { /* Do nothing. */ }
899
900
901               } else // Still no title?  Fall back to our untitled string.
902                 title = '\\{0_0}/';
903             }
904
905             aTab.label = title;
906           ]]>
907         </body>
908       </method>
909
910       <method name="setStripVisibilityTo">
911         <parameter name="aShow"/>
912         <body>
913         <![CDATA[
914           this.mStrip.collapsed = !aShow;
915           if (aShow) {
916             // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
917             document.getElementById("menu_closeWindow").hidden = false;
918             document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
919             if (!this.mTabbedMode)
920               this.enterTabbedMode();
921           }
922           else {
923             // XXXdwh temporary unclean dependency on specific menu items in navigator.xul
924             document.getElementById("menu_closeWindow").hidden = true;
925             document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
926           }
927         ]]>
928         </body>
929       </method>
930
931       <method name="getStripVisibility">
932         <body>
933           return !this.mStrip.collapsed;
934         </body>
935       </method>
936
937       <method name="enterTabbedMode">
938         <body>
939           <![CDATA[
940             this.mTabbedMode = true; // Welcome to multi-tabbed mode.
941
942             // Get the first tab all hooked up with a title listener and popup blocking listener.
943             this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
944
945             var throbberElement = document.getElementById("navigator-throbber");
946             if (throbberElement && throbberElement.hasAttribute("busy")) {
947               this.mCurrentTab.setAttribute("busy", "true");
948               this.mIsBusy = true;
949               this.setTabTitleLoading(this.mCurrentTab);
950               this.updateIcon(this.mCurrentTab);
951             } else {
952               this.setTabTitle(this.mCurrentTab);
953               this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
954             }
955
956             var filter;
957             if (this.mTabFilters.length > 0) {
958               // Use the filter hooked up in our addProgressListener
959               filter = this.mTabFilters[0];
960             } else {
961               // create a filter and hook it up to our first browser
962               filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
963                                  .createInstance(Components.interfaces.nsIWebProgress);
964               this.mTabFilters[0] = filter;
965               this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
966             }
967
968             // Remove all our progress listeners from the active browser's filter.
969             for (var i = 0; i < this.mProgressListeners.length; i++) {
970               var p = this.mProgressListeners[i];
971               if (p)
972                 filter.removeProgressListener(p);
973             }
974
975             // Wire up a progress listener to our filter.
976             const listener = this.mTabProgressListener(this.mCurrentTab,
977                                                        this.mCurrentBrowser,
978                                                        false);
979             filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
980             this.mTabListeners[0] = listener;
981           ]]>
982         </body>
983       </method>
984
985       <method name="loadOneTab">
986         <parameter name="aURI"/>
987         <parameter name="aReferrerURI"/>
988         <parameter name="aCharset"/>
989         <parameter name="aPostData"/>
990         <parameter name="aLoadInBackground"/>
991         <parameter name="aAllowThirdPartyFixup"/>
992         <body>
993           <![CDATA[
994             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
995                          this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
996             var owner = bgLoad ? null : this.selectedTab;
997             var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner,
998                                   aAllowThirdPartyFixup);
999             // Set newly selected tab after quick timeout, otherwise hideous focus problems
1000             // can occur when "browser.tabs.loadInBackground" is false and presshell is not ready
1001             if (!bgLoad) {
1002               function selectNewForegroundTab(browser, tab) {
1003                 browser.selectedTab = tab;
1004               }
1005               setTimeout(selectNewForegroundTab, 0, getBrowser(), tab);
1006             }
1007             if (!bgLoad)
1008               this.selectedTab = tab;
1009             
1010             return tab;
1011          ]]>
1012         </body>
1013       </method>
1014
1015       <method name="loadTabs">
1016         <parameter name="aURIs"/>
1017         <parameter name="aLoadInBackground"/>
1018         <parameter name="aReplace"/>
1019         <body><![CDATA[
1020           // The tab selected after this new tab is closed (i.e. the new tab's
1021           // "owner") is the next adjacent tab (i.e. not the previously viewed tab)
1022           // when several urls are opened here (i.e. closing the first should select
1023           // the next of many URLs opened) or if the pref to have UI links opened in
1024           // the background is set (i.e. the link is not being opened modally)
1025           //
1026           // i.e.
1027           //    Number of URLs    Load UI Links in BG       Focus Last Viewed?
1028           //    == 1              false                     YES
1029           //    == 1              true                      NO
1030           //    > 1               false/true                NO
1031           var owner = (aURIs.length > 1) || aLoadInBackground ? null : gBrowser.selectedTab;
1032           var firstTabAdded = null;
1033           if (aReplace)
1034             this.loadURI(aURIs[0], null, null);
1035           else
1036             firstTabAdded = gBrowser.addTab(aURIs[0], null, null, null, owner, false);
1037
1038           var tabNum = this.mTabContainer.selectedIndex;
1039           for (var i = 1; i < aURIs.length; ++i) {
1040             var tab = gBrowser.addTab(aURIs[i]);
1041             if (aReplace)
1042               this.moveTabTo(tab, ++tabNum);
1043           }
1044
1045           if (!aLoadInBackground) {
1046             if (firstTabAdded) {
1047               // .selectedTab setter focuses the content area
1048               this.selectedTab = firstTabAdded;
1049             }
1050             else
1051               window.content.focus();
1052           }
1053         ]]></body>
1054       </method>
1055
1056       <method name="addTab">
1057         <parameter name="aURI"/>
1058         <parameter name="aReferrerURI"/>
1059         <parameter name="aCharset"/>
1060         <parameter name="aPostData"/>
1061         <parameter name="aOwner"/>
1062         <parameter name="aAllowThirdPartyFixup"/>
1063         <body>
1064           <![CDATA[
1065             this._browsers = null; // invalidate cache
1066
1067             if (!this.mTabbedMode)
1068               this.enterTabbedMode();
1069
1070             // if we're adding tabs, we're past interrupt mode, ditch the owner
1071             if (this.mCurrentTab.owner)
1072               this.mCurrentTab.owner = null;
1073
1074             var t = document.createElementNS(
1075               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
1076                                              "tab");
1077
1078             var blank = (aURI == "about:blank");
1079
1080             if (blank)
1081               t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
1082             else
1083               t.setAttribute("label", aURI);
1084
1085             t.setAttribute("crop", "end");
1086             t.minWidth = this.mTabContainer.mTabMinWidth;
1087             t.setAttribute("flex", "100");
1088             t.setAttribute("validate", "never");
1089             t.setAttribute("onerror", "this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); this.removeAttribute('image');");
1090             t.className = "tabbrowser-tab";
1091
1092             this.mTabContainer.appendChild(t);
1093
1094             if (document.defaultView
1095                         .getComputedStyle(this.mTabContainer, "")
1096                         .direction == "rtl") {
1097               /* In RTL UI, the tab is visually added to the left side of the
1098                * tabstrip. This means the tabstip has to be scrolled back in
1099                * order to make sure the same set of tabs is visible before and
1100                * after the new tab is added */
1101
1102             }
1103
1104             // invalidate cache, because mTabContainer is about to change
1105             this._browsers = null; 
1106
1107             // If this new tab is owned by another, assert that relationship
1108             if (aOwner !== undefined && aOwner !== null) {
1109               t.owner = aOwner;
1110
1111               var self = this;
1112               function attrChanged(event) {
1113                 if (event.attrName == "selectedIndex" &&
1114                     event.prevValue != event.newValue)
1115                   self.resetOwner(parseInt(event.prevValue));
1116               }
1117               if (!this.mTabChangedListenerAdded) {
1118                 this.mTabBox.addEventListener("DOMAttrModified", attrChanged, false);
1119                 this.mTabChangedListenerAdded = true;
1120               }
1121             }
1122
1123             var b = document.createElementNS(
1124               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
1125                                              "browser");
1126             b.setAttribute("type", "content-targetable");
1127             b.setAttribute("message", "true");
1128             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
1129             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
1130             if (this.hasAttribute("autocompletepopup"))
1131               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
1132
1133             // Add the Message and the Browser to the box
1134             var notificationbox = document.createElementNS(
1135                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
1136                                     "notificationbox");
1137             notificationbox.setAttribute("flex", "1");
1138             notificationbox.appendChild(b);
1139             b.setAttribute("flex", "1");
1140             this.mPanelContainer.appendChild(notificationbox);
1141
1142             b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
1143
1144             if (this.mStrip.collapsed)
1145               this.setStripVisibilityTo(true);
1146
1147             this.mPrefs.setBoolPref("browser.tabs.forceHide", false);
1148
1149             // wire up a progress listener for the new browser object.
1150             var position = this.mTabContainer.childNodes.length-1;
1151             var tabListener = this.mTabProgressListener(t, b, blank);
1152             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
1153                                      .createInstance(Components.interfaces.nsIWebProgress);
1154             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
1155             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
1156             this.mTabListeners[position] = tabListener;
1157             this.mTabFilters[position] = filter;
1158
1159             b._fastFind = this.fastFind;
1160
1161             var uniqueId = "panel" + Date.now() + position;
1162             this.mPanelContainer.lastChild.id = uniqueId;
1163             t.linkedPanel = uniqueId;
1164             t.linkedBrowser = b;
1165             t._tPos = position;
1166             if (t.previousSibling.selected)
1167               t.setAttribute("afterselected", true);
1168
1169             if (!blank) {
1170               // pretend the user typed this so it'll be available till
1171               // the document successfully loads
1172               b.userTypedValue = aURI;
1173
1174               if (aPostData === undefined)
1175                 aPostData = null;
1176               const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
1177               var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
1178               if (aAllowThirdPartyFixup) {
1179                 flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
1180               }
1181               b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
1182             }
1183
1184
1185             // Dispatch a new tab notification.  We do this once we're
1186             // entirely done, so that things are in a consistent state
1187             // even if the event listener opens or closes tabs.
1188             var evt = document.createEvent("Events");
1189             evt.initEvent("TabOpen", true, false);
1190             t.dispatchEvent(evt);
1191
1192             return t;
1193           ]]>
1194         </body>
1195       </method>
1196
1197       <method name="warnAboutClosingTabs">
1198       <parameter name="aAll"/>
1199       <body>
1200         <![CDATA[
1201           var numTabs = this.mTabContainer.childNodes.length;
1202           var reallyClose = true;
1203           if (numTabs <= 1)
1204             return reallyClose;
1205
1206           const pref = "browser.tabs.warnOnClose";
1207           var shouldPrompt = this.mPrefs.getBoolPref(pref);
1208
1209           if (shouldPrompt) {
1210             var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
1211                                           .getService(Components.interfaces.nsIPromptService);
1212
1213             //default to true: if it were false, we wouldn't get this far
1214             var warnOnClose = { value:true };
1215             var bundle = this.mStringBundle;
1216             var tabsToClose = numTabs;  //number of tabs to be removed
1217             if (!aAll)
1218               --tabsToClose;
1219
1220             var messageKey = (tabsToClose == 1) ? "tabs.closeWarningOneTab" : "tabs.closeWarningMultipleTabs";
1221             var closeKey = (tabsToClose == 1) ? "tabs.closeButtonOne" : "tabs.closeButtonMultiple";
1222             // focus the window before prompting.
1223             // this will raise any minimized window, which will 
1224             // make it obvious which window the prompt is for and will
1225             // solve the problem of windows "obscuring" the prompt.
1226             // see bug #350299 for more details
1227             window.focus();
1228             var buttonPressed = promptService.confirmEx(window,
1229                                                         bundle.getString("tabs.closeWarningTitle"),
1230                                                         bundle.getFormattedString(messageKey, [tabsToClose]),
1231                                                         (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0)
1232                                                         + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
1233                                                         bundle.getString(closeKey),
1234                                                         null, null,
1235                                                         bundle.getString("tabs.closeWarningPromptMe"),
1236                                                         warnOnClose);
1237             reallyClose = (buttonPressed == 0);
1238             // don't set the pref unless they press OK and it's false
1239             if (reallyClose && !warnOnClose.value)
1240               this.mPrefs.setBoolPref(pref, false);
1241           }
1242           return reallyClose;
1243         ]]>
1244       </body>
1245       </method>
1246
1247       <method name="removeAllTabsBut">
1248         <parameter name="aTab"/>
1249         <body>
1250           <![CDATA[
1251             if (this.warnAboutClosingTabs(false)) {
1252               if (aTab.localName != "tab")
1253                 aTab = this.mCurrentTab;
1254               else
1255                 this.mTabContainer.selectedItem = aTab;
1256
1257               var childNodes = this.mTabContainer.childNodes;
1258
1259               for (var i = childNodes.length - 1; i >= 0; --i) {
1260                 if (childNodes[i] != aTab)
1261                   this.removeTab(childNodes[i]);
1262               }
1263             }
1264           ]]>
1265         </body>
1266       </method>
1267
1268       <method name="removeCurrentTab">
1269         <body>
1270           <![CDATA[
1271             return this.removeTab(this.mCurrentTab);
1272           ]]>
1273         </body>
1274       </method>
1275
1276       <method name="resetOwner">
1277         <parameter name="oldIndex"/>
1278         <body>
1279           <![CDATA[
1280             // Reset the owner property, since we're leaving the modally opened
1281             // tab for another.
1282             if (oldIndex < this.mTabContainer.childNodes.length) {
1283               var tab = this.mTabContainer.childNodes[oldIndex];
1284               tab.owner = null;
1285             }
1286           ]]>
1287         </body>
1288       </method>
1289
1290       <method name="removeTab">
1291         <parameter name="aTab"/>
1292         <body>
1293           <![CDATA[
1294             this._browsers = null; // invalidate cache
1295             if (aTab.localName != "tab")
1296               aTab = this.mCurrentTab;
1297
1298             var l = this.mTabContainer.childNodes.length;
1299             if (l == 1 && this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
1300               // hide the tab bar
1301               this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
1302               this.setStripVisibilityTo(false);
1303               return;
1304             }
1305
1306             var ds = this.getBrowserForTab(aTab).docShell;
1307             if (ds.contentViewer && !ds.contentViewer.permitUnload())
1308               return;
1309
1310             // see notes in addTab
1311             var _delayedUpdate = function(aTabContainer) {
1312             }
1313             setTimeout(_delayedUpdate, 0, this.mTabContainer);
1314
1315             if (l == 1) {
1316               // add a new blank tab to replace the one we're about to close
1317               // (this ensures that the remaining tab is as good as new)
1318               this.addTab("about:blank");
1319               l++;
1320             };
1321             // We're committed to closing the tab now.  
1322             // Dispatch a notification.
1323             // We dispatch it before any teardown so that event listeners can
1324             // inspect the tab that's about to close.
1325             var evt = document.createEvent("Events");
1326             evt.initEvent("TabClose", true, false);
1327             aTab.dispatchEvent(evt);
1328
1329             var index = -1;
1330             if (this.mCurrentTab == aTab)
1331               index = this.mTabContainer.selectedIndex;
1332             else {
1333               // Find and locate the tab in our list.
1334               for (var i = 0; i < l; i++)
1335                 if (this.mTabContainer.childNodes[i] == aTab)
1336                   index = i;
1337             }
1338
1339             // Remove the tab's filter and progress listener.
1340             const filter = this.mTabFilters[index];
1341             var oldBrowser = this.getBrowserAtIndex(index);
1342             oldBrowser.webProgress.removeProgressListener(filter);
1343             filter.removeProgressListener(this.mTabListeners[index]);
1344             this.mTabFilters.splice(index, 1);
1345             this.mTabListeners.splice(index, 1);
1346
1347             // Remove our title change and blocking listeners
1348             oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
1349
1350             // We are no longer the primary content area.
1351             oldBrowser.setAttribute("type", "content-targetable");
1352
1353             // Get the index of the tab we're removing before unselecting it
1354             var currentIndex = this.mTabContainer.selectedIndex;
1355
1356             var oldTab = aTab;
1357
1358             // clean up the before/afterselected attributes before removing the tab
1359             oldTab.selected = false;
1360
1361             // Remove this tab as the owner of any other tabs, since it's going away.
1362             for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
1363               var tab = this.mTabContainer.childNodes[i];
1364               if ("owner" in tab && tab.owner == oldTab)
1365                 // |tab| is a child of the tab we're removing, make it an orphan
1366                 tab.owner = null;
1367             }
1368
1369             // Because of the way XBL works (fields just set JS
1370             // properties on the element) and the code we have in place
1371             // to preserve the JS objects for any elements that have
1372             // JS properties set on them, the browser element won't be
1373             // destroyed until the document goes away.  So we force a
1374             // cleanup ourselves.
1375             // This has to happen before we remove the child so that the
1376             // XBL implementation of nsIObserver still works.  But
1377             // clearing focusedWindow happens below because it gets
1378             // reset by updateCurrentBrowser.
1379             oldBrowser.destroy();
1380
1381             // Remove the tab
1382             this.mTabContainer.removeChild(oldTab);
1383             // invalidate cache, because mTabContainer is about to change
1384             this._browsers = null; 
1385             this.mPanelContainer.removeChild(oldBrowser.parentNode);
1386
1387             // Find the tab to select
1388             var newIndex = -1;
1389             if (currentIndex > index)
1390               newIndex = currentIndex-1;
1391             else if (currentIndex < index)
1392               newIndex = currentIndex;
1393             else {
1394               if ("owner" in oldTab && oldTab.owner &&
1395                   this.mPrefs.getBoolPref("browser.tabs.selectOwnerOnClose")) {
1396                 for (i = 0; i < this.mTabContainer.childNodes.length; ++i) {
1397                   tab = this.mTabContainer.childNodes[i];
1398                   if (tab == oldTab.owner) {
1399                     newIndex = i;
1400                     break;
1401                   }
1402                 }
1403               }
1404               if (newIndex == -1)
1405                 newIndex = (index == l - 1) ? index - 1 : index;
1406             }
1407
1408             // Select the new tab
1409             this.selectedTab = this.mTabContainer.childNodes[newIndex];
1410
1411             for (i = oldTab._tPos; i < this.mTabContainer.childNodes.length; i++) {
1412               this.mTabContainer.childNodes[i]._tPos = i;
1413             }
1414             this.mTabBox.selectedPanel = this.getBrowserForTab(this.mCurrentTab).parentNode;
1415             this.mCurrentTab.selected = true;
1416
1417             this.updateCurrentBrowser();
1418
1419             // see comment above destroy above
1420             oldBrowser.focusedWindow = null;
1421             oldBrowser.focusedElement = null;
1422           ]]>
1423         </body>
1424       </method>
1425
1426       <method name="reloadAllTabs">
1427         <body>
1428           <![CDATA[
1429             var l = this.mPanelContainer.childNodes.length;
1430             for (var i = 0; i < l; i++) {
1431               try {
1432                 this.getBrowserAtIndex(i).reload();
1433               } catch (e) {
1434                 // ignore failure to reload so others will be reloaded
1435               }
1436             }
1437           ]]>
1438         </body>
1439       </method>
1440
1441       <method name="reloadTab">
1442         <parameter name="aTab"/>
1443         <body>
1444           <![CDATA[
1445             if (aTab.localName != "tab")
1446               aTab = this.mCurrentTab;
1447
1448             this.getBrowserForTab(aTab).reload();
1449           ]]>
1450         </body>
1451       </method>
1452
1453       <method name="onTabBarDblClick">
1454         <parameter name="aEvent"/>
1455         <body>
1456           <![CDATA[
1457             // See hack note in the tabbrowser-close-button binding
1458             if (!this._blockDblClick && aEvent.button == 0 &&
1459                 aEvent.originalTarget.localName == "box") {
1460               // xxx this needs to check that we're in the empty area of the tabstrip
1461               var e = document.createEvent("Events");
1462               e.initEvent("NewTab", true, true);
1463               this.dispatchEvent(e);
1464             }
1465           ]]>
1466         </body>
1467       </method>
1468
1469       <method name="addProgressListener">
1470         <parameter name="aListener"/>
1471         <parameter name="aMask"/>
1472         <body>
1473           <![CDATA[
1474             if (!this.mAddProgressListenerWasCalled) {
1475               this.mAddProgressListenerWasCalled = true;
1476               var autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
1477               var forceHide = this.mPrefs.getBoolPref("browser.tabs.forceHide");
1478               var tabStripHide = !window.toolbar.visible;
1479               if (!autoHide && !forceHide && !tabStripHide)
1480                 this.setStripVisibilityTo(true);
1481             }
1482
1483             if (!this.mTabbedMode && this.mProgressListeners.length == 1) {
1484               // If we are adding a 2nd progress listener, we need to enter tabbed mode
1485               // because the browser status filter can only handle one progress listener.
1486               // In tabbed mode, mTabProgressListener is used which will iterate over all listeners.
1487               this.enterTabbedMode();
1488             }
1489
1490             this.mProgressListeners.push(aListener);
1491
1492             if (!this.mTabbedMode) {
1493               // If someone does this:
1494               // addProgressListener, removeProgressListener, addProgressListener
1495               // don't create a new filter; reuse the existing filter.
1496               if (this.mTabFilters.length == 0) {
1497                 // hook a filter up to our first browser
1498                 const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
1499                                          .createInstance(Components.interfaces.nsIWebProgress);
1500                 this.mTabFilters[0] = filter;
1501                 this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
1502               }
1503
1504               // Directly hook the listener up to the filter for better performance
1505               this.mTabFilters[0].addProgressListener(aListener, aMask);
1506             }
1507           ]]>
1508         </body>
1509       </method>
1510
1511       <method name="removeProgressListener">
1512         <parameter name="aListener"/>
1513         <body>
1514           <![CDATA[
1515             for (var i = 0; i < this.mProgressListeners.length; i++) {
1516               if (this.mProgressListeners[i] == aListener) {
1517                 this.mProgressListeners.splice(i, 1);
1518                 break;
1519               }
1520             }
1521
1522             if (!this.mTabbedMode)
1523               // Don't forget to remove it from the filter we hooked it up to
1524               this.mTabFilters[0].removeProgressListener(aListener);
1525          ]]>
1526         </body>
1527       </method>
1528
1529       <method name="getBrowserForTab">
1530         <parameter name="aTab"/>
1531         <body>
1532         <![CDATA[
1533           return aTab.linkedBrowser;
1534         ]]>
1535         </body>
1536       </method>
1537
1538       <property name="tabContainer">
1539         <getter>
1540           return this.mTabContainer;
1541         </getter>
1542       </property>
1543
1544       <property name="selectedTab">
1545         <getter>
1546           return this.mTabBox.selectedTab;
1547         </getter>
1548         <setter>
1549           <![CDATA[
1550           // Update the tab
1551           this.mTabBox.selectedTab = val;
1552           return val;
1553           ]]>
1554         </setter>
1555       </property>
1556
1557       <property name="selectedBrowser"
1558                 onget="return this.mCurrentBrowser;"
1559                 readonly="true"/>
1560
1561       <property name="browsers" readonly="true">
1562        <getter>
1563           <![CDATA[
1564             if (!this._browsers) {
1565               var browsers = [];
1566               var i;
1567               browsers.item = function(i) {return this[i];}
1568               for (i = 0; i < this.mTabContainer.childNodes.length; i++)
1569                 browsers.push(this.mTabContainer.childNodes[i].linkedBrowser);
1570               this._browsers = browsers;
1571             }
1572             return this._browsers;
1573           ]]>
1574         </getter>
1575       </property>
1576
1577       <!-- Drag and drop observer API -->
1578       <method name="onDragStart">
1579         <parameter name="aEvent"/>
1580         <parameter name="aXferData"/>
1581         <parameter name="aDragAction"/>
1582         <body>
1583         <![CDATA[
1584           if (aEvent.target.localName == "tab" &&
1585               aEvent.originalTarget.localName != "toolbarbutton") {
1586             aXferData.data = new TransferData();
1587
1588             var URI = this.getBrowserForTab(aEvent.target).currentURI;
1589             if (URI) {
1590               aXferData.data.addDataForFlavour("text/x-moz-url", URI.spec + "\n" + aEvent.target.label);
1591               aXferData.data.addDataForFlavour("text/unicode", URI.spec);
1592               aXferData.data.addDataForFlavour("text/html", '<a href="' + URI.spec + '">' + aEvent.target.label + '</a>');
1593             } else {
1594               aXferData.data.addDataForFlavour("text/unicode", "about:blank");
1595             }
1596           }
1597         ]]>
1598         </body>
1599       </method>
1600
1601       <method name="canDrop">
1602         <parameter name="aEvent"/>
1603         <parameter name="aDragSession"/>
1604         <body>
1605           <![CDATA[
1606             /*if (aDragSession.sourceNode &&
1607                 aDragSession.sourceNode.parentNode == this.mTabContainer &&
1608                 (aEvent.screenX >= aDragSession.sourceNode.boxObject.screenX &&
1609                  aEvent.screenX <= (aDragSession.sourceNode.boxObject.screenX +
1610                                     aDragSession.sourceNode.boxObject.width)))
1611                 return false;*/
1612             return true;
1613           ]]>
1614         </body>
1615       </method>
1616
1617       <method name="onDragOver">
1618         <parameter name="aEvent"/>
1619         <parameter name="aFlavour"/>
1620         <parameter name="aDragSession"/>
1621         <body>
1622           <![CDATA[
1623
1624           ]]>
1625         </body>
1626       </method>
1627
1628       <method name="onDrop">
1629         <parameter name="aEvent"/>
1630         <parameter name="aXferData"/>
1631         <parameter name="aDragSession"/>
1632         <body>
1633           <![CDATA[
1634             if (aDragSession.sourceNode && aDragSession.sourceNode.parentNode == this.mTabContainer) {
1635               var newIndex = this.getNewIndex(aEvent);
1636               var oldIndex = aDragSession.sourceNode._tPos;
1637
1638               /*if (newIndex > oldIndex)
1639                 newIndex--;*/
1640               if (newIndex != oldIndex)
1641                 this.moveTabTo(this.mTabs[oldIndex], newIndex);
1642                 var tn=this.mTabs[newIndex].label;
1643                 this.mTabs[newIndex].label= '';
1644                 this.mTabs[newIndex].label= tn;
1645             } else {
1646               var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
1647
1648               // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
1649               // Also disallow dropping javascript: or data: urls--bail out
1650               if (!url || !url.length || url.indexOf(" ", 0) != -1 ||
1651                   /^\s*(javascript|data):/.test(url))
1652                 return;
1653
1654               this.dragDropSecurityCheck(aEvent, aDragSession, url);
1655
1656               var bgLoad = true;
1657               try {
1658                 bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
1659               }
1660               catch (e) { }
1661
1662               if (aEvent.shiftKey)
1663                 bgLoad = !bgLoad;
1664
1665               if (document.getBindingParent(aEvent.originalTarget).localName != "tab") {
1666                 // We're adding a new tab.
1667                 this.loadOneTab(getShortcutOrURI(url), null, null, null, bgLoad, false);
1668               }
1669               else {
1670                 // Load in an existing tab.
1671                 var tab = aEvent.target;
1672                 try {
1673                   this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
1674                   if (this.mCurrentTab != tab && !bgLoad)
1675                     this.selectedTab = tab;
1676                 } catch(ex) {
1677                   // Just ignore invalid urls
1678                 }
1679               }
1680             }
1681           ]]>
1682         </body>
1683       </method>
1684
1685       <method name="onDragExit">
1686         <parameter name="aEvent"/>
1687         <parameter name="aDragSession"/>
1688         <body>
1689           <![CDATA[
1690             if (aDragSession.sourceNode &&
1691                 aDragSession.sourceNode.parentNode == this.mTabContainer &&
1692                 aDragSession.canDrop) {
1693               var target = aEvent.relatedTarget;
1694               while (target && target != this.mStrip)
1695                 target = target.parentNode;
1696               if (target)
1697                 return;
1698             }
1699             this.mTabDropIndicatorBar.setAttribute('dragging','false');
1700           ]]>
1701         </body>
1702       </method>
1703
1704       <method name="getSupportedFlavours">
1705         <body>
1706         <![CDATA[
1707           var flavourSet = new FlavourSet();
1708           flavourSet.appendFlavour("text/x-moz-url");
1709           flavourSet.appendFlavour("text/unicode");
1710           flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
1711           return flavourSet;
1712         ]]>
1713         </body>
1714       </method>
1715
1716       <method name="moveTabTo">
1717         <parameter name="aTab"/>
1718         <parameter name="aIndex"/>
1719         <body>
1720         <![CDATA[
1721           this._browsers = null; // invalidate cache
1722           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
1723           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
1724
1725           var oldPosition = aTab._tPos;
1726
1727           aIndex = aIndex <= aTab._tPos ? aIndex: aIndex+1;
1728
1729           this.mCurrentTab.selected = false;
1730           this.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes[aIndex]);
1731           // invalidate cache, because mTabContainer is about to change
1732           this._browsers = null;
1733
1734           var i;
1735           for (i = 0; i < this.mTabContainer.childNodes.length; i++) {
1736             this.mTabContainer.childNodes[i]._tPos = i;
1737             this.mTabContainer.childNodes[i].selected = false;
1738           }
1739           this.mCurrentTab.selected = true;
1740
1741           var evt = document.createEvent("UIEvents");
1742           evt.initUIEvent("TabMove", true, false, window, oldPosition);
1743           aTab.dispatchEvent(evt);
1744
1745           return aTab;
1746         ]]>
1747         </body>
1748       </method>
1749
1750       <method name="getNewIndex">
1751         <parameter name="aEvent"/>
1752         <body>
1753           <![CDATA[
1754             var i;
1755               for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
1756                 if (aEvent.screenY < this.mTabs[i].boxObject.screenY + this.mTabs[i].boxObject.height) 
1757                   return i;
1758 /*            var i;
1759             if (window.getComputedStyle(this.parentNode, null).direction == "ltr") {
1760               for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
1761                 if (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2) 
1762                   return i;
1763             } else {
1764                for (i = aEvent.target.localName == "tab" ? aEvent.target._tPos : 0; i < this.mTabs.length; i++)
1765                 if (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2)
1766                   return i;
1767             }*/
1768
1769             return this.mTabs.length;
1770           ]]>
1771         </body>
1772       </method>
1773
1774
1775       <method name="moveTabForward">
1776         <body>
1777           <![CDATA[
1778             var tabPos = this.mCurrentTab._tPos;
1779             if (tabPos < this.browsers.length - 1) {
1780               this.moveTabTo(this.mCurrentTab, tabPos + 1);
1781               this.mCurrentTab.focus();
1782             }
1783             else if (this.arrowKeysShouldWrap)
1784               this.moveTabToStart();
1785           ]]>
1786         </body>
1787       </method>
1788
1789       <method name="moveTabBackward">
1790         <body>
1791           <![CDATA[
1792             var tabPos = this.mCurrentTab._tPos;
1793             if (tabPos > 0) {
1794               this.moveTabTo(this.mCurrentTab, tabPos - 1);
1795               this.mCurrentTab.focus();
1796             }
1797             else if (this.arrowKeysShouldWrap)
1798               this.moveTabToEnd();
1799           ]]>
1800         </body>
1801       </method>
1802
1803       <method name="moveTabToStart">
1804         <body>
1805           <![CDATA[
1806             var tabPos = this.mCurrentTab._tPos;
1807             if (tabPos > 0) {
1808               this.moveTabTo(this.mCurrentTab, 0);
1809               this.mCurrentTab.focus();
1810             }
1811           ]]>
1812         </body>
1813       </method>
1814
1815       <method name="moveTabToEnd">
1816         <body>
1817           <![CDATA[
1818             var tabPos = this.mCurrentTab._tPos;
1819             if (tabPos < this.browsers.length - 1) {
1820               this.moveTabTo(this.mCurrentTab,
1821                                         this.browsers.length - 1);
1822               this.mCurrentTab.focus();
1823             }
1824           ]]>
1825         </body>
1826       </method>
1827
1828       <method name="moveTabOver">
1829         <parameter name="aEvent"/>
1830         <body>
1831           <![CDATA[
1832             var direction = window.getComputedStyle(this.parentNode, null).direction;
1833             if ((direction == "ltr" && aEvent.keyCode == KeyEvent.DOM_VK_RIGHT) ||
1834                 (direction == "rtl" && aEvent.keyCode == KeyEvent.DOM_VK_LEFT))
1835               this.moveTabForward();
1836             else
1837               this.moveTabBackward();
1838           ]]>
1839         </body>
1840       </method>
1841
1842       <!-- BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
1843            MAKE SURE TO ADD IT HERE AS WELL. -->
1844       <property name="canGoBack"
1845                 onget="return this.mCurrentBrowser.canGoBack;"
1846                 readonly="true"/>
1847
1848       <property name="canGoForward"
1849                 onget="return this.mCurrentBrowser.canGoForward;"
1850                 readonly="true"/>
1851
1852       <method name="goBack">
1853         <body>
1854           <![CDATA[
1855             return this.mCurrentBrowser.goBack();
1856           ]]>
1857         </body>
1858       </method>
1859
1860       <method name="goForward">
1861         <body>
1862           <![CDATA[
1863             return this.mCurrentBrowser.goForward();
1864           ]]>
1865         </body>
1866       </method>
1867
1868       <method name="reload">
1869         <body>
1870           <![CDATA[
1871             return this.mCurrentBrowser.reload();
1872           ]]>
1873         </body>
1874       </method>
1875
1876       <method name="reloadWithFlags">
1877         <parameter name="aFlags"/>
1878         <body>
1879           <![CDATA[
1880             return this.mCurrentBrowser.reloadWithFlags(aFlags);
1881           ]]>
1882         </body>
1883       </method>
1884
1885       <method name="stop">
1886         <body>
1887           <![CDATA[
1888             return this.mCurrentBrowser.stop();
1889           ]]>
1890         </body>
1891       </method>
1892
1893       <!-- throws exception for unknown schemes -->
1894       <method name="loadURI">
1895         <parameter name="aURI"/>
1896         <parameter name="aReferrerURI"/>
1897         <parameter name="aCharset"/>
1898         <body>
1899           <![CDATA[
1900             return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
1901           ]]>
1902         </body>
1903       </method>
1904
1905       <!-- throws exception for unknown schemes -->
1906       <method name="loadURIWithFlags">
1907         <parameter name="aURI"/>
1908         <parameter name="aFlags"/>
1909         <parameter name="aReferrerURI"/>
1910         <parameter name="aCharset"/>
1911         <body>
1912           <![CDATA[
1913             return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset);
1914           ]]>
1915         </body>
1916       </method>
1917
1918       <method name="goHome">
1919         <body>
1920           <![CDATA[
1921             return this.mCurrentBrowser.goHome();
1922           ]]>
1923         </body>
1924       </method>
1925
1926       <property name="homePage">
1927         <getter>
1928           <![CDATA[
1929             return this.mCurrentBrowser.homePage;
1930           ]]>
1931         </getter>
1932         <setter>
1933           <![CDATA[
1934             this.mCurrentBrowser.homePage = val;
1935             return val;
1936           ]]>
1937         </setter>
1938       </property>
1939
1940       <method name="gotoIndex">
1941         <parameter name="aIndex"/>
1942         <body>
1943           <![CDATA[
1944             return this.mCurrentBrowser.gotoIndex(aIndex);
1945           ]]>
1946         </body>
1947       </method>
1948
1949       <method name="attachFormFill">
1950         <body><![CDATA[
1951           for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
1952             var cb = this.getBrowserAtIndex(i);
1953             cb.attachFormFill();
1954           }
1955         ]]></body>
1956       </method>
1957
1958       <method name="detachFormFill">
1959         <body><![CDATA[
1960           for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
1961             var cb = this.getBrowserAtIndex(i);
1962             cb.detachFormFill();
1963           }
1964         ]]></body>
1965       </method>
1966
1967       <property name="pageReport"
1968                 onget="return this.mCurrentBrowser.pageReport;"
1969                 readonly="true"/>
1970
1971       <property name="currentURI"
1972                 onget="return this.mCurrentBrowser.currentURI;"
1973                 readonly="true"/>
1974
1975       <field name="_fastFind">null</field>
1976       <property name="fastFind"
1977                 readonly="true">
1978         <getter>
1979         <![CDATA[
1980           if (!this._fastFind) {
1981             this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
1982                                        .createInstance(Components.interfaces.nsITypeAheadFind);
1983             this._fastFind.init(this.docShell);
1984           }
1985           return this._fastFind;
1986         ]]>
1987         </getter>
1988       </property>
1989
1990       <property name="findString"
1991                 onget="return this.mCurrentBrowser.findString;"
1992                 readonly="true"/>
1993
1994       <property name="docShell"
1995                 onget="return this.mCurrentBrowser.docShell"
1996                 readonly="true"/>
1997
1998       <property name="webNavigation"
1999                 onget="return this.mCurrentBrowser.webNavigation"
2000                 readonly="true"/>
2001
2002       <property name="webBrowserFind"
2003                 readonly="true"
2004                 onget="return this.mCurrentBrowser.webBrowserFind"/>
2005
2006       <property name="webProgress"
2007                 readonly="true"
2008                 onget="return this.mCurrentBrowser.webProgress"/>
2009
2010       <property name="contentWindow"
2011                 readonly="true"
2012                 onget="return this.mCurrentBrowser.contentWindow"/>
2013
2014       <property name="sessionHistory"
2015                 onget="return this.mCurrentBrowser.sessionHistory;"
2016                 readonly="true"/>
2017
2018       <property name="markupDocumentViewer"
2019                 onget="return this.mCurrentBrowser.markupDocumentViewer;"
2020                 readonly="true"/>
2021
2022       <property name="contentViewerEdit"
2023                 onget="return this.mCurrentBrowser.contentViewerEdit;"
2024                 readonly="true"/>
2025
2026       <property name="contentViewerFile"
2027                 onget="return this.mCurrentBrowser.contentViewerFile;"
2028                 readonly="true"/>
2029
2030       <property name="documentCharsetInfo"
2031                 onget="return this.mCurrentBrowser.documentCharsetInfo;"
2032                 readonly="true"/>
2033
2034       <property name="contentDocument"
2035                 onget="return this.mCurrentBrowser.contentDocument;"
2036                 readonly="true"/>
2037
2038       <property name="contentTitle"
2039                 onget="return this.mCurrentBrowser.contentTitle;"
2040                 readonly="true"/>
2041
2042       <property name="securityUI"
2043                 onget="return this.mCurrentBrowser.securityUI;"
2044                 readonly="true"/>
2045
2046       <method name="find">
2047         <body>
2048           <![CDATA[
2049             return this.mCurrentBrowser.find();
2050           ]]>
2051         </body>
2052       </method>
2053
2054       <method name="findAgain">
2055         <body>
2056           <![CDATA[
2057             return this.mCurrentBrowser.findAgain();
2058           ]]>
2059         </body>
2060       </method>
2061
2062       <method name="findPrevious">
2063         <body>
2064           <![CDATA[
2065             return this.mCurrentBrowser.findPrevious();
2066           ]]>
2067         </body>
2068       </method>
2069
2070       <method name="dragDropSecurityCheck">
2071         <parameter name="aEvent"/>
2072         <parameter name="aDragSession"/>
2073         <parameter name="aUri"/>
2074         <body>
2075           <![CDATA[
2076             // Do a security check for drag n' drop. Make sure the
2077             // source document can load the dragged link.
2078             var sourceDoc = aDragSession.sourceDocument;
2079
2080             if (sourceDoc) {
2081               // Strip leading and trailing whitespace, then try to
2082               // create a URI from the dropped string. If that
2083               // succeeds, we're dropping a URI and we need to do a
2084               // security check to make sure the source document can
2085               // load the dropped URI. We don't so much care about
2086               // creating the real URI here (i.e. encoding differences
2087               // etc don't matter), we just want to know if aUri
2088               // really is a URI.
2089
2090               var uriStr = aUri.replace(/^\s*|\s*$/g, '');
2091               var uri = null;
2092
2093               try {
2094                 uri = Components.classes["@mozilla.org/network/io-service;1"]
2095                   .getService(Components.interfaces.nsIIOService)
2096                   .newURI(uriStr, null, null);
2097               } catch (e) {
2098               }
2099
2100               if (uri) {
2101                 // aUri is a URI, do the security check.
2102                 var sourceURI = sourceDoc.documentURI;
2103
2104                 const nsIScriptSecurityManager =
2105                   Components.interfaces.nsIScriptSecurityManager;
2106                 var secMan =
2107                   Components.classes["@mozilla.org/scriptsecuritymanager;1"]
2108                   .getService(nsIScriptSecurityManager);
2109
2110                 try {
2111                   secMan.checkLoadURIStr(sourceURI, uriStr,
2112                                          nsIScriptSecurityManager.STANDARD);
2113                 } catch (e) {
2114                   // Stop event propagation right here.
2115                   aEvent.stopPropagation();
2116
2117                   throw "Drop of " + aUri + " denied.";
2118                 }
2119               }
2120             }
2121           ]]>
2122         </body>
2123       </method>
2124
2125       <field name="_keyEventHandler" readonly="true">
2126       <![CDATA[({
2127         tabbrowser: this,
2128         handleEvent: function handleEvent(aEvent) {
2129           if (!aEvent.isTrusted) {
2130             // Don't let untrusted events mess with tabs.
2131             return;
2132           }
2133
2134           if (('shiftKey' in aEvent && aEvent.shiftKey) ||
2135               ('altKey' in aEvent && aEvent.altKey))
2136             return;
2137           if (('ctrlKey' in aEvent && aEvent.ctrlKey) &&
2138               !('metaKey' in aEvent && aEvent.metaKey)) {
2139             if (aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
2140                 this.tabbrowser.mTabBox.handleCtrlPageUpDown) {
2141               this.tabbrowser.removeCurrentTab();
2142
2143               aEvent.stopPropagation();
2144               aEvent.preventDefault();
2145               return;
2146             }
2147             if (aEvent.target.localName == "tabbrowser") {
2148               switch (aEvent.keyCode) {
2149                 case KeyEvent.DOM_VK_UP:
2150                   this.tabbrowser.moveTabBackward();
2151                   break;
2152                 case KeyEvent.DOM_VK_DOWN:
2153                   this.tabbrowser.moveTabForward();
2154                   break;
2155                 case KeyEvent.DOM_VK_RIGHT:
2156                 case KeyEvent.DOM_VK_LEFT:
2157                   this.tabbrowser.moveTabOver(aEvent);
2158                   break;
2159                 case KeyEvent.DOM_VK_HOME:
2160                   this.tabbrowser.moveTabToStart();
2161                   break;
2162                 case KeyEvent.DOM_VK_END:
2163                   this.tabbrowser.moveTabToEnd();
2164                   break;
2165                 default:
2166                   // Stop the keypress event for the above keyboard
2167                   // shortcuts only.
2168                   return;
2169               }
2170               aEvent.stopPropagation();
2171               aEvent.preventDefault();
2172             }
2173           }
2174         }
2175       })]]>
2176       </field>
2177
2178       <property name="canFindAgain"
2179                 onget="return this.mCurrentBrowser.canFindAgain;"
2180                 readonly="true"/>
2181
2182       <property name="userTypedClear"
2183                 onget="return this.mCurrentBrowser.userTypedClear;"
2184                 onset="return this.mCurrentBrowser.userTypedClear = val;"/>
2185
2186       <property name="userTypedValue"
2187                 onget="return this.mCurrentBrowser.userTypedValue;"
2188                 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
2189
2190       <property name="forceSyncURLBarUpdate"
2191                 onget="return this.mModalDialogShowing;"/>
2192
2193       <method name="createTooltip">
2194         <parameter name="event"/>
2195         <body>
2196           <![CDATA[
2197             event.stopPropagation();
2198             var tn = document.tooltipNode;
2199             if (tn.localName != "tab")
2200               return false; // Not a tab, so cancel the tooltip
2201             if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
2202               event.target.setAttribute("label", tn.getAttribute("closetabtext"));
2203               return true;
2204             }
2205             if (tn.hasAttribute("label")) {
2206               event.target.setAttribute("label", tn.getAttribute("label"));
2207               return true;
2208             }
2209             return false;
2210           ]]>
2211         </body>
2212       </method>
2213
2214       <constructor>
2215         <![CDATA[
2216                 try{this.mStrip.width= this.mPrefs.getIntPref("browser.tabs.width");}catch(ex){};
2217
2218           this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
2219           this.mCurrentTab = this.mTabContainer.firstChild;
2220           document.addEventListener("keypress", this._keyEventHandler, false);
2221
2222           var uniqueId = "panel" + Date.now();
2223           this.mPanelContainer.childNodes[0].id = uniqueId;
2224           this.mTabContainer.childNodes[0].linkedPanel = uniqueId;
2225           this.mTabContainer.childNodes[0]._tPos = 0;
2226           this.mTabContainer.childNodes[0].linkedBrowser = this.mPanelContainer.childNodes[0].firstChild;
2227         ]]>
2228       </constructor>
2229
2230       <destructor>
2231         <![CDATA[
2232           for (var i = 0; i < this.mTabListeners.length; ++i) {
2233             this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
2234             this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
2235             this.mTabFilters[i] = null;
2236             this.mTabListeners[i] = null;
2237             this.getBrowserAtIndex(i).removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
2238           }
2239           document.removeEventListener("keypress", this._keyEventHandler, false);
2240         ]]>
2241       </destructor>
2242     </implementation>
2243
2244     <handlers>
2245       <handler event="DOMLinkAdded" phase="capturing" action="this.onLinkAdded(event);"/>
2246
2247       <handler event="DOMWindowClose" phase="capturing">
2248         <![CDATA[
2249           if (!event.isTrusted)
2250             return;
2251
2252           const browsers = this.mPanelContainer.childNodes;
2253           if (browsers.length == 1) {
2254             // There's only one browser left. If a window is being
2255             // closed and the window is *not* the window in the
2256             // browser that's still around, prevent the event's default
2257             // action to prevent closing a window that's being closed
2258             // already.
2259             if (this.getBrowserAtIndex(0).contentWindow != event.target)
2260               event.preventDefault();
2261
2262             return;
2263           }
2264
2265           var i = 0;
2266           for (; i < browsers.length; ++i) {
2267             if (this.getBrowserAtIndex(i).contentWindow == event.target) {
2268               this.removeTab(this.mTabContainer.childNodes[i]);
2269               event.preventDefault();
2270
2271               break;
2272             }
2273           }
2274         ]]>
2275       </handler>
2276       <handler event="DOMWillOpenModalDialog" phase="capturing">
2277         <![CDATA[
2278           if (!event.isTrusted)
2279             return;
2280
2281           // We're about to open a modal dialog, make sure the opening
2282           // tab is brought to the front.
2283
2284           var targetTop = event.target.top;
2285
2286           for (var i = 0; i < browsers.length; ++i) {
2287             if (this.getBrowserAtIndex(i).contentWindow == targetTop) {
2288               this.mModalDialogShowing = true;
2289               this.selectedTab = this.mTabContainer.childNodes[i];
2290
2291               break;
2292             }
2293           }
2294         ]]>
2295       </handler>
2296       <handler event="DOMModalDialogClosed" phase="capturing">
2297         <![CDATA[
2298           if (!event.isTrusted)
2299             return;
2300
2301           this.mModalDialogShowing = false;
2302         ]]>
2303       </handler>
2304       <handler event="resize">
2305         <![CDATA[
2306                 this.mPrefs.setIntPref("browser.tabs.width", this.mStrip.width);
2307       ]]>
2308       </handler>
2309     </handlers>
2310   </binding>
2311
2312   <binding id="tabbrowser-tabs"
2313            extends="chrome://global/content/bindings/tabbox.xml#tabs" >
2314     <content>
2315         <xul:vbox style="overflow-y: auto; overflow-x: hidden;" anonid="arrowscrollbox" flex="1">
2316                 <children includes="tab"/>
2317         </xul:vbox>
2318     </content>
2319     <handlers>
2320         <!--<handler event="dblclick">
2321                 BrowserOpenTab();
2322         </handler>
2323         <handler event="click" button="1">
2324                 if (event.target==this) undoCloseTab();
2325         </handler>-->
2326     </handlers>
2327   </binding>
2328
2329   <binding id="tabbrowser-tab" display="xul:box" extends="chrome://global/content/bindings/tabbox.xml#tab">
2330         <content chromedir="&locale.dir;">
2331                 <xul:hbox class="tab-mid box-inherit" xbl:inherits="align,dir,pack,orient,selected" flex="1">
2332                         <xul:stack flex="1">
2333                                 <xul:hbox><xul:vbox><xul:image class="tab-icon" xbl:inherits="validate,src=image" /></xul:vbox></xul:hbox>
2334                                 <xul:description class="tab-text" anonid="tabtext" xbl:inherits="accesskey,crop,disabled" flex="1" >\(0_0)/</xul:description>
2335                         </xul:stack>
2336                         <!--<xul:label class="tab-text" xbl:inherits="value=label,accesskey,crop,disabled" flex="1"/>-->
2337                 </xul:hbox>
2338         </content>
2339
2340     <implementation>
2341       <field name="mCorrespondingMenuitem">null</field>
2342       <constructor>
2343         
2344       </constructor>
2345       <method name="textinit">
2346         <body>
2347                 document.getAnonymousElementByAttribute(this,'anonid','tabtext').textContent= this.label;
2348         </body>
2349       </method>
2350     </implementation>
2351
2352     <handlers>
2353         <handler event="DOMAttrModified"><![CDATA[              if (event.attrName=='label') this.textinit();           ]]></handler>
2354     </handlers>
2355
2356   </binding>
2357
2358 </bindings>