4 <!ENTITY % tabBrowserDTD SYSTEM "chrome://global/locale/tabbrowser.dtd" >
6 <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
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">
17 <binding id="tabbrowser">
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"/>
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);"/>
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);"/>
51 <xul:tabs class="tabbrowser-tabs" flex="1"
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');"
65 class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
68 <xul:splitter class="tabbrowser-splitter" collapse="before" resizeafter="closest" resizebefore="closest" ondblclick="this.previousSibling.width=102">
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>
81 <field name="mTabstrip">
82 document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
84 <field name="mPrefs" readonly="true">
85 Components.classes['@mozilla.org/preferences-service;1']
86 .getService(Components.interfaces.nsIPrefService)
89 <field name="mURIFixup" readonly="true">
90 Components.classes["@mozilla.org/docshell/urifixup;1"]
91 .getService(Components.interfaces.nsIURIFixup);
93 <field name="mTabBox" readonly="true">
94 document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
96 <field name="mTabDropIndicatorBar">
97 this.mTabBox.childNodes[0]
99 <field name="mStrip" readonly="true">
100 document.getAnonymousElementByAttribute(this, "anonid", "strip");
102 <field name="mTabContainer" readonly="true">
103 document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
105 <field name="mPanelContainer" readonly="true">
106 document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
108 <field name="mTabs" readonly="true">
109 this.mTabContainer.childNodes
111 <field name="mStringBundle">
112 document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
114 <field name="mCurrentTab">
117 <field name="mCurrentBrowser">
120 <field name="mProgressListeners">
123 <field name="mTabListeners">
126 <field name="mTabFilters">
129 <field name="mTabbedMode">
132 <field name="mIsBusy">
135 <field name="mMissedIconCache">
138 <field name="mContextTab">
141 <field name="mModalDialogShowing">
144 <field name="arrowKeysShouldWrap" readonly="true">
147 <field name="mAddProgressListenerWasCalled">
150 <field name="_browsers">
154 <field name="_blockDblClick">
158 <method name="getBrowserAtIndex">
159 <parameter name="aIndex"/>
162 return this.mTabContainer.childNodes[aIndex].linkedBrowser;
167 <method name="getBrowserIndexForDocument">
168 <parameter name="aDocument"/>
171 for (var i = 0; i < this.mPanelContainer.childNodes.length; i++) {
172 if (this.getBrowserAtIndex(i).contentDocument == aDocument) {
181 <method name="getBrowserForDocument">
182 <parameter name="aDocument"/>
185 var index = this.getBrowserIndexForDocument(aDocument);
188 return this.getBrowserAtIndex(index);
193 <method name="getNotificationBox">
194 <parameter name="aBrowser"/>
198 return aBrowser.parentNode;
199 else if (this.mCurrentBrowser)
200 return this.mCurrentBrowser.parentNode;
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"/>
217 mBlank: aStartsBlank,
220 // cache flags for correct status bar update after tab switching
226 // count of open requests (should always be 0 or 1)
229 onProgressChange : function (aWebProgress, aRequest,
230 aCurSelfProgress, aMaxSelfProgress,
231 aCurTotalProgress, aMaxTotalProgress)
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];
237 p.onProgressChange(aWebProgress, aRequest,
238 aCurSelfProgress, aMaxSelfProgress,
239 aCurTotalProgress, aMaxTotalProgress);
243 this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
246 onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
251 var oldBlank = this.mBlank;
253 const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
254 const nsIChannel = Components.interfaces.nsIChannel;
256 if (aStateFlags & nsIWebProgressListener.STATE_START) {
257 this.mRequestCount++;
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
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;
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++;
282 this.mTab.setAttribute("busy", "true");
283 this.mTabBrowser.updateIcon(this.mTab);
284 this.mTabBrowser.setTabTitleLoading(this.mTab);
286 if (this.mTabBrowser.mCurrentTab == this.mTab)
287 this.mTabBrowser.mIsBusy = true;
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
295 if (this.mBrowser.userTypedClear > 0)
296 this.mBrowser.userTypedClear--;
298 if (!this.mBrowser.mIconURL)
299 this.mTabBrowser.useDefaultIcon(this.mTab);
305 this.mTab.removeAttribute("busy");
306 this.mTabBrowser.updateIcon(this.mTab);
308 var location = aRequest.QueryInterface(nsIChannel).URI;
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;
314 if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
315 this.mTabBrowser.setTabTitle(this.mTab);
317 if (this.mTabBrowser.mCurrentTab == this.mTab)
318 this.mTabBrowser.mIsBusy = false;
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];
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);
332 if (aStateFlags & (nsIWebProgressListener.STATE_START |
333 nsIWebProgressListener.STATE_STOP)) {
334 // reset cached temporary values at beginning and end
336 this.mTotalProgress = 0;
338 this.mStateFlags = aStateFlags;
339 this.mStatus = aStatus;
342 onLocationChange : function(aWebProgress, aRequest, aLocation)
344 // The document loaded correctly, clear the value if we should
345 if (this.mBrowser.userTypedClear > 0 && aRequest)
346 this.mBrowser.userTypedValue = null;
348 if (aWebProgress.DOMWindow == this.mBrowser.contentWindow &&
349 aWebProgress.isLoadingDocument)
350 this.mTabBrowser.setIcon(this.mTab, null);
352 // changing location, clear out the missing plugins list
353 this.mTab.missingPlugins = null;
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];
359 p.onLocationChange(aWebProgress, aRequest, aLocation);
364 onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
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];
373 p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
377 this.mMessage = aMessage;
380 onSecurityChange : function(aWebProgress, aRequest, aState)
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];
386 p.onSecurityChange(aWebProgress, aRequest, aState);
391 QueryInterface : function(aIID)
393 if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
394 aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
395 aIID.equals(Components.interfaces.nsISupports))
397 throw Components.results.NS_NOINTERFACE;
404 <method name="setIcon">
405 <parameter name="aTab"/>
406 <parameter name="aURI"/>
409 var browser = this.getBrowserForTab(aTab);
410 browser.mIconURL = aURI;
412 this.updateIcon(aTab);
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);
423 <method name="updateIcon">
424 <parameter name="aTab"/>
427 var browser = this.getBrowserForTab(aTab);
428 if (!aTab.hasAttribute("busy") && browser.mIconURL)
429 aTab.setAttribute("image", browser.mIconURL);
431 aTab.removeAttribute("image");
436 <method name="shouldLoadFavIcon">
437 <parameter name="aURI"/>
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")));
447 <method name="useDefaultIcon">
448 <parameter name="aTab"/>
451 var browser = this.getBrowserForTab(aTab);
452 if (browser.contentDocument instanceof ImageDocument) {
453 if (this.mPrefs.getBoolPref("browser.chrome.site_icons")) {
455 var sz = this.mPrefs.getIntPref("browser.chrome.image_icons.max_size");
459 var req = browser.contentDocument.imageRequest;
460 if (!req || !req.image ||
461 req.image.width > sz ||
462 req.image.height > sz)
465 this.setIcon(aTab, browser.currentURI.spec);
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);
478 <method name="addToMissedIconCache">
479 <parameter name="aURI"/>
482 var entry = this.openCacheEntry(aURI, Components.interfaces.nsICache.ACCESS_READ_WRITE);
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");
495 <method name="openCacheEntry">
496 <parameter name="key"/>
497 <parameter name="access"/>
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)
507 return this.mMissedIconCache.openCacheEntry(key, access, true);
516 <method name="isIconKnownMissing">
517 <parameter name="key"/>
520 var e = this.openCacheEntry(key, Components.interfaces.nsICache.ACCESS_READ);
530 <method name="updateTitlebar">
535 var docElement = this.ownerDocument.documentElement;
536 var sep = docElement.getAttribute("titlemenuseparator");
538 if (this.docShell.contentViewer)
539 docTitle = this.contentTitle;
542 docTitle = docElement.getAttribute("titledefault");
544 var modifier = docElement.getAttribute("titlemodifier");
546 newTitle += docElement.getAttribute("titlepreface");
547 newTitle += docTitle;
551 newTitle += modifier;
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)
558 if (docElement.getAttribute("chromehidden").indexOf("location") != -1) {
559 var uri = this.mURIFixup.createExposableURI(
560 this.mCurrentBrowser.currentURI);
562 newTitle = uri.prePath + sep + newTitle;
566 this.ownerDocument.title = newTitle;
571 <method name="updatePopupMenu">
572 <parameter name="aPopupMenu"/>
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;
584 <method name="updateCurrentBrowser">
587 var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
588 if (this.mCurrentBrowser == newBrowser)
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;
597 if (focusedWindow && focusedWindow.top == window.content) {
600 var contentWindow = window;
602 while (contentWindow) {
603 if (contentWindow == focusedWindow) {
608 if (contentWindow.parent == contentWindow) {
612 contentWindow = contentWindow.parent;
617 // Preserve the currently-focused element or DOM window for
620 this.mCurrentBrowser.focusedWindow = focusedWindow;
621 this.mCurrentBrowser.focusedElement = document.commandDispatcher.focusedElement;
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();
630 // non-HTML/XUL elements have no blur method, see bug 323805
631 this.mCurrentBrowser.focusedElement = null;
633 this.mCurrentBrowser.setAttribute("type", "content-targetable");
636 var updatePageReport = false;
637 if ((this.mCurrentBrowser.pageReport && !newBrowser.pageReport) ||
638 (!this.mCurrentBrowser.pageReport && newBrowser.pageReport))
639 updatePageReport = true;
641 newBrowser.setAttribute("type", "content-primary");
642 this.mCurrentBrowser = newBrowser;
643 this.mCurrentTab = this.selectedTab;
645 if (updatePageReport)
646 this.mCurrentBrowser.updatePageReport();
648 // Update the URL bar.
649 var loc = this.mCurrentBrowser.currentURI;
651 loc = ({ spec: "" });
653 var webProgress = this.mCurrentBrowser.webProgress;
654 var securityUI = this.mCurrentBrowser.securityUI;
657 for (i = 0; i < this.mProgressListeners.length; i++) {
658 p = this.mProgressListeners[i];
660 p.onLocationChange(webProgress, null, loc);
662 p.onSecurityChange(webProgress, null, securityUI.state);
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);
674 this._fastFind.setDocShell(this.mCurrentBrowser.docShell);
676 // Update the window title.
677 this.updateTitlebar();
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) {
684 webProgress = this.mCurrentBrowser.webProgress;
685 for (i = 0; i < this.mProgressListeners.length; i++) {
686 p = this.mProgressListeners[i];
688 p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
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];
700 p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
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);
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
716 var whatToFocus = window.content;
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;
726 else if (newBrowser.focusedWindow) {
727 whatToFocus = newBrowser.focusedWindow;
730 function setFocus(element) {
731 document.commandDispatcher.suppressFocusScroll = true;
733 if (element instanceof Window ||
734 element instanceof NSHTMLElement ||
735 element instanceof XULElement) {
740 dump("XXXfocus() failed, see bug #348183: ex = " + ex + "\n");
743 document.commandDispatcher.suppressFocusScroll = false;
746 // Use setTimeout to avoid focus outline ghosting.
747 setTimeout(setFocus, 0, whatToFocus);
752 <method name="onTabClick">
753 <parameter name="event"/>
756 if (event.button != 1 || event.target.localName != 'tab')
759 this.removeTab(event.target);
760 event.stopPropagation();
765 <method name="onLinkAdded">
766 <parameter name="event"/>
769 if (!this.mPrefs.getBoolPref("browser.chrome.site_icons"))
772 if (!event.originalTarget.rel.match((/(?:^|\s)icon(?:\s|$)/i)))
776 var href = event.originalTarget.href;
780 const nsIContentPolicy = Components.interfaces.nsIContentPolicy;
783 Components.classes['@mozilla.org/layout/content-policy;1']
784 .getService(nsIContentPolicy);
786 return; // Refuse to load if we can't do a security check.
789 // Verify that the load of this icon is legal.
790 // We check first with the security manager
792 Components.classes["@mozilla.org/scriptsecuritymanager;1"]
793 .getService(Components.interfaces.nsIScriptSecurityManager);
795 // Get the IOService so we can make URIs
797 Components.classes["@mozilla.org/network/io-service;1"]
798 .getService(Components.interfaces.nsIIOService);
800 const targetDoc = event.target.ownerDocument;
801 // Make a URI out of our href.
802 var uri = ioService.newURI(href, targetDoc.characterSet, null);
804 var origURI = ioService.newURI(targetDoc.documentURI, targetDoc.characterSet, null);
806 const nsIScriptSecMan =
807 Components.interfaces.nsIScriptSecurityManager;
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);
821 // Security says okay, now ask content policy
822 if (contentPolicy.shouldLoad(nsIContentPolicy.TYPE_IMAGE,
823 uri, origURI, event.target,
825 null) != nsIContentPolicy.ACCEPT)
828 var browserIndex = this.getBrowserIndexForDocument(targetDoc);
829 // no browser? no favicon.
830 if (browserIndex == -1)
833 var tab = this.mTabContainer.childNodes[browserIndex];
834 this.setIcon(tab, href);
839 <method name="onTitleChanged">
840 <parameter name="evt"/>
843 if (evt.target != this.contentDocument)
847 for ( ; i < this.parentNode.parentNode.childNodes.length; i++) {
848 if (this.parentNode.parentNode.childNodes[i].firstChild == this)
852 var tabBrowser = this.parentNode.parentNode.parentNode.parentNode;
854 var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id);
855 tabBrowser.setTabTitle(tab);
857 if (tab == tabBrowser.mCurrentTab)
858 tabBrowser.updateTitlebar();
863 <method name="setTabTitleLoading">
864 <parameter name="aTab"/>
867 aTab.label = this.mStringBundle.getString("tabs.loading");
868 aTab.setAttribute("crop", "end");
873 <method name="setTabTitle">
874 <parameter name="aTab"/>
877 var browser = this.getBrowserForTab(aTab);
878 var title = browser.contentDocument.title;
881 if (browser.currentURI.spec) {
883 title = this.mURIFixup.createExposableURI(browser.currentURI).spec;
885 title = browser.currentURI.spec;
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.
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. */ }
901 } else // Still no title? Fall back to our untitled string.
910 <method name="setStripVisibilityTo">
911 <parameter name="aShow"/>
914 this.mStrip.collapsed = !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();
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"));
931 <method name="getStripVisibility">
933 return !this.mStrip.collapsed;
937 <method name="enterTabbedMode">
940 this.mTabbedMode = true; // Welcome to multi-tabbed mode.
942 // Get the first tab all hooked up with a title listener and popup blocking listener.
943 this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
945 var throbberElement = document.getElementById("navigator-throbber");
946 if (throbberElement && throbberElement.hasAttribute("busy")) {
947 this.mCurrentTab.setAttribute("busy", "true");
949 this.setTabTitleLoading(this.mCurrentTab);
950 this.updateIcon(this.mCurrentTab);
952 this.setTabTitle(this.mCurrentTab);
953 this.setIcon(this.mCurrentTab, this.mCurrentBrowser.mIconURL);
957 if (this.mTabFilters.length > 0) {
958 // Use the filter hooked up in our addProgressListener
959 filter = this.mTabFilters[0];
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);
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];
972 filter.removeProgressListener(p);
975 // Wire up a progress listener to our filter.
976 const listener = this.mTabProgressListener(this.mCurrentTab,
977 this.mCurrentBrowser,
979 filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
980 this.mTabListeners[0] = listener;
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"/>
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
1002 function selectNewForegroundTab(browser, tab) {
1003 browser.selectedTab = tab;
1005 setTimeout(selectNewForegroundTab, 0, getBrowser(), tab);
1008 this.selectedTab = tab;
1015 <method name="loadTabs">
1016 <parameter name="aURIs"/>
1017 <parameter name="aLoadInBackground"/>
1018 <parameter name="aReplace"/>
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)
1027 // Number of URLs Load UI Links in BG Focus Last Viewed?
1030 // > 1 false/true NO
1031 var owner = (aURIs.length > 1) || aLoadInBackground ? null : gBrowser.selectedTab;
1032 var firstTabAdded = null;
1034 this.loadURI(aURIs[0], null, null);
1036 firstTabAdded = gBrowser.addTab(aURIs[0], null, null, null, owner, false);
1038 var tabNum = this.mTabContainer.selectedIndex;
1039 for (var i = 1; i < aURIs.length; ++i) {
1040 var tab = gBrowser.addTab(aURIs[i]);
1042 this.moveTabTo(tab, ++tabNum);
1045 if (!aLoadInBackground) {
1046 if (firstTabAdded) {
1047 // .selectedTab setter focuses the content area
1048 this.selectedTab = firstTabAdded;
1051 window.content.focus();
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"/>
1065 this._browsers = null; // invalidate cache
1067 if (!this.mTabbedMode)
1068 this.enterTabbedMode();
1070 // if we're adding tabs, we're past interrupt mode, ditch the owner
1071 if (this.mCurrentTab.owner)
1072 this.mCurrentTab.owner = null;
1074 var t = document.createElementNS(
1075 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
1078 var blank = (aURI == "about:blank");
1081 t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
1083 t.setAttribute("label", aURI);
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";
1092 this.mTabContainer.appendChild(t);
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 */
1104 // invalidate cache, because mTabContainer is about to change
1105 this._browsers = null;
1107 // If this new tab is owned by another, assert that relationship
1108 if (aOwner !== undefined && aOwner !== null) {
1112 function attrChanged(event) {
1113 if (event.attrName == "selectedIndex" &&
1114 event.prevValue != event.newValue)
1115 self.resetOwner(parseInt(event.prevValue));
1117 if (!this.mTabChangedListenerAdded) {
1118 this.mTabBox.addEventListener("DOMAttrModified", attrChanged, false);
1119 this.mTabChangedListenerAdded = true;
1123 var b = document.createElementNS(
1124 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
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"));
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",
1137 notificationbox.setAttribute("flex", "1");
1138 notificationbox.appendChild(b);
1139 b.setAttribute("flex", "1");
1140 this.mPanelContainer.appendChild(notificationbox);
1142 b.addEventListener("DOMTitleChanged", this.onTitleChanged, true);
1144 if (this.mStrip.collapsed)
1145 this.setStripVisibilityTo(true);
1147 this.mPrefs.setBoolPref("browser.tabs.forceHide", false);
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;
1159 b._fastFind = this.fastFind;
1161 var uniqueId = "panel" + Date.now() + position;
1162 this.mPanelContainer.lastChild.id = uniqueId;
1163 t.linkedPanel = uniqueId;
1164 t.linkedBrowser = b;
1166 if (t.previousSibling.selected)
1167 t.setAttribute("afterselected", true);
1170 // pretend the user typed this so it'll be available till
1171 // the document successfully loads
1172 b.userTypedValue = aURI;
1174 if (aPostData === undefined)
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;
1181 b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData);
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);
1197 <method name="warnAboutClosingTabs">
1198 <parameter name="aAll"/>
1201 var numTabs = this.mTabContainer.childNodes.length;
1202 var reallyClose = true;
1206 const pref = "browser.tabs.warnOnClose";
1207 var shouldPrompt = this.mPrefs.getBoolPref(pref);
1210 var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
1211 .getService(Components.interfaces.nsIPromptService);
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
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
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),
1235 bundle.getString("tabs.closeWarningPromptMe"),
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);
1247 <method name="removeAllTabsBut">
1248 <parameter name="aTab"/>
1251 if (this.warnAboutClosingTabs(false)) {
1252 if (aTab.localName != "tab")
1253 aTab = this.mCurrentTab;
1255 this.mTabContainer.selectedItem = aTab;
1257 var childNodes = this.mTabContainer.childNodes;
1259 for (var i = childNodes.length - 1; i >= 0; --i) {
1260 if (childNodes[i] != aTab)
1261 this.removeTab(childNodes[i]);
1268 <method name="removeCurrentTab">
1271 return this.removeTab(this.mCurrentTab);
1276 <method name="resetOwner">
1277 <parameter name="oldIndex"/>
1280 // Reset the owner property, since we're leaving the modally opened
1282 if (oldIndex < this.mTabContainer.childNodes.length) {
1283 var tab = this.mTabContainer.childNodes[oldIndex];
1290 <method name="removeTab">
1291 <parameter name="aTab"/>
1294 this._browsers = null; // invalidate cache
1295 if (aTab.localName != "tab")
1296 aTab = this.mCurrentTab;
1298 var l = this.mTabContainer.childNodes.length;
1299 if (l == 1 && this.mPrefs.getBoolPref("browser.tabs.autoHide")) {
1301 this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
1302 this.setStripVisibilityTo(false);
1306 var ds = this.getBrowserForTab(aTab).docShell;
1307 if (ds.contentViewer && !ds.contentViewer.permitUnload())
1310 // see notes in addTab
1311 var _delayedUpdate = function(aTabContainer) {
1313 setTimeout(_delayedUpdate, 0, this.mTabContainer);
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");
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);
1330 if (this.mCurrentTab == aTab)
1331 index = this.mTabContainer.selectedIndex;
1333 // Find and locate the tab in our list.
1334 for (var i = 0; i < l; i++)
1335 if (this.mTabContainer.childNodes[i] == aTab)
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);
1347 // Remove our title change and blocking listeners
1348 oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true);
1350 // We are no longer the primary content area.
1351 oldBrowser.setAttribute("type", "content-targetable");
1353 // Get the index of the tab we're removing before unselecting it
1354 var currentIndex = this.mTabContainer.selectedIndex;
1358 // clean up the before/afterselected attributes before removing the tab
1359 oldTab.selected = false;
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
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();
1382 this.mTabContainer.removeChild(oldTab);
1383 // invalidate cache, because mTabContainer is about to change
1384 this._browsers = null;
1385 this.mPanelContainer.removeChild(oldBrowser.parentNode);
1387 // Find the tab to select
1389 if (currentIndex > index)
1390 newIndex = currentIndex-1;
1391 else if (currentIndex < index)
1392 newIndex = currentIndex;
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) {
1405 newIndex = (index == l - 1) ? index - 1 : index;
1408 // Select the new tab
1409 this.selectedTab = this.mTabContainer.childNodes[newIndex];
1411 for (i = oldTab._tPos; i < this.mTabContainer.childNodes.length; i++) {
1412 this.mTabContainer.childNodes[i]._tPos = i;
1414 this.mTabBox.selectedPanel = this.getBrowserForTab(this.mCurrentTab).parentNode;
1415 this.mCurrentTab.selected = true;
1417 this.updateCurrentBrowser();
1419 // see comment above destroy above
1420 oldBrowser.focusedWindow = null;
1421 oldBrowser.focusedElement = null;
1426 <method name="reloadAllTabs">
1429 var l = this.mPanelContainer.childNodes.length;
1430 for (var i = 0; i < l; i++) {
1432 this.getBrowserAtIndex(i).reload();
1434 // ignore failure to reload so others will be reloaded
1441 <method name="reloadTab">
1442 <parameter name="aTab"/>
1445 if (aTab.localName != "tab")
1446 aTab = this.mCurrentTab;
1448 this.getBrowserForTab(aTab).reload();
1453 <method name="onTabBarDblClick">
1454 <parameter name="aEvent"/>
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);
1469 <method name="addProgressListener">
1470 <parameter name="aListener"/>
1471 <parameter name="aMask"/>
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);
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();
1490 this.mProgressListeners.push(aListener);
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);
1504 // Directly hook the listener up to the filter for better performance
1505 this.mTabFilters[0].addProgressListener(aListener, aMask);
1511 <method name="removeProgressListener">
1512 <parameter name="aListener"/>
1515 for (var i = 0; i < this.mProgressListeners.length; i++) {
1516 if (this.mProgressListeners[i] == aListener) {
1517 this.mProgressListeners.splice(i, 1);
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);
1529 <method name="getBrowserForTab">
1530 <parameter name="aTab"/>
1533 return aTab.linkedBrowser;
1538 <property name="tabContainer">
1540 return this.mTabContainer;
1544 <property name="selectedTab">
1546 return this.mTabBox.selectedTab;
1551 this.mTabBox.selectedTab = val;
1557 <property name="selectedBrowser"
1558 onget="return this.mCurrentBrowser;"
1561 <property name="browsers" readonly="true">
1564 if (!this._browsers) {
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;
1572 return this._browsers;
1577 <!-- Drag and drop observer API -->
1578 <method name="onDragStart">
1579 <parameter name="aEvent"/>
1580 <parameter name="aXferData"/>
1581 <parameter name="aDragAction"/>
1584 if (aEvent.target.localName == "tab" &&
1585 aEvent.originalTarget.localName != "toolbarbutton") {
1586 aXferData.data = new TransferData();
1588 var URI = this.getBrowserForTab(aEvent.target).currentURI;
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>');
1594 aXferData.data.addDataForFlavour("text/unicode", "about:blank");
1601 <method name="canDrop">
1602 <parameter name="aEvent"/>
1603 <parameter name="aDragSession"/>
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)))
1617 <method name="onDragOver">
1618 <parameter name="aEvent"/>
1619 <parameter name="aFlavour"/>
1620 <parameter name="aDragSession"/>
1628 <method name="onDrop">
1629 <parameter name="aEvent"/>
1630 <parameter name="aXferData"/>
1631 <parameter name="aDragSession"/>
1634 if (aDragSession.sourceNode && aDragSession.sourceNode.parentNode == this.mTabContainer) {
1635 var newIndex = this.getNewIndex(aEvent);
1636 var oldIndex = aDragSession.sourceNode._tPos;
1638 /*if (newIndex > oldIndex)
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;
1646 var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
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))
1654 this.dragDropSecurityCheck(aEvent, aDragSession, url);
1658 bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
1662 if (aEvent.shiftKey)
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);
1670 // Load in an existing tab.
1671 var tab = aEvent.target;
1673 this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
1674 if (this.mCurrentTab != tab && !bgLoad)
1675 this.selectedTab = tab;
1677 // Just ignore invalid urls
1685 <method name="onDragExit">
1686 <parameter name="aEvent"/>
1687 <parameter name="aDragSession"/>
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;
1699 this.mTabDropIndicatorBar.setAttribute('dragging','false');
1704 <method name="getSupportedFlavours">
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");
1716 <method name="moveTabTo">
1717 <parameter name="aTab"/>
1718 <parameter name="aIndex"/>
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]);
1725 var oldPosition = aTab._tPos;
1727 aIndex = aIndex <= aTab._tPos ? aIndex: aIndex+1;
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;
1735 for (i = 0; i < this.mTabContainer.childNodes.length; i++) {
1736 this.mTabContainer.childNodes[i]._tPos = i;
1737 this.mTabContainer.childNodes[i].selected = false;
1739 this.mCurrentTab.selected = true;
1741 var evt = document.createEvent("UIEvents");
1742 evt.initUIEvent("TabMove", true, false, window, oldPosition);
1743 aTab.dispatchEvent(evt);
1750 <method name="getNewIndex">
1751 <parameter name="aEvent"/>
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)
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)
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)
1769 return this.mTabs.length;
1775 <method name="moveTabForward">
1778 var tabPos = this.mCurrentTab._tPos;
1779 if (tabPos < this.browsers.length - 1) {
1780 this.moveTabTo(this.mCurrentTab, tabPos + 1);
1781 this.mCurrentTab.focus();
1783 else if (this.arrowKeysShouldWrap)
1784 this.moveTabToStart();
1789 <method name="moveTabBackward">
1792 var tabPos = this.mCurrentTab._tPos;
1794 this.moveTabTo(this.mCurrentTab, tabPos - 1);
1795 this.mCurrentTab.focus();
1797 else if (this.arrowKeysShouldWrap)
1798 this.moveTabToEnd();
1803 <method name="moveTabToStart">
1806 var tabPos = this.mCurrentTab._tPos;
1808 this.moveTabTo(this.mCurrentTab, 0);
1809 this.mCurrentTab.focus();
1815 <method name="moveTabToEnd">
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();
1828 <method name="moveTabOver">
1829 <parameter name="aEvent"/>
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();
1837 this.moveTabBackward();
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;"
1848 <property name="canGoForward"
1849 onget="return this.mCurrentBrowser.canGoForward;"
1852 <method name="goBack">
1855 return this.mCurrentBrowser.goBack();
1860 <method name="goForward">
1863 return this.mCurrentBrowser.goForward();
1868 <method name="reload">
1871 return this.mCurrentBrowser.reload();
1876 <method name="reloadWithFlags">
1877 <parameter name="aFlags"/>
1880 return this.mCurrentBrowser.reloadWithFlags(aFlags);
1885 <method name="stop">
1888 return this.mCurrentBrowser.stop();
1893 <!-- throws exception for unknown schemes -->
1894 <method name="loadURI">
1895 <parameter name="aURI"/>
1896 <parameter name="aReferrerURI"/>
1897 <parameter name="aCharset"/>
1900 return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
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"/>
1913 return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset);
1918 <method name="goHome">
1921 return this.mCurrentBrowser.goHome();
1926 <property name="homePage">
1929 return this.mCurrentBrowser.homePage;
1934 this.mCurrentBrowser.homePage = val;
1940 <method name="gotoIndex">
1941 <parameter name="aIndex"/>
1944 return this.mCurrentBrowser.gotoIndex(aIndex);
1949 <method name="attachFormFill">
1951 for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
1952 var cb = this.getBrowserAtIndex(i);
1953 cb.attachFormFill();
1958 <method name="detachFormFill">
1960 for (var i = 0; i < this.mPanelContainer.childNodes.length; ++i) {
1961 var cb = this.getBrowserAtIndex(i);
1962 cb.detachFormFill();
1967 <property name="pageReport"
1968 onget="return this.mCurrentBrowser.pageReport;"
1971 <property name="currentURI"
1972 onget="return this.mCurrentBrowser.currentURI;"
1975 <field name="_fastFind">null</field>
1976 <property name="fastFind"
1980 if (!this._fastFind) {
1981 this._fastFind = Components.classes["@mozilla.org/typeaheadfind;1"]
1982 .createInstance(Components.interfaces.nsITypeAheadFind_MOZILLA_1_8_BRANCH);
1983 this._fastFind.init(this.docShell);
1985 return this._fastFind;
1990 <property name="findString"
1991 onget="return this.mCurrentBrowser.findString;"
1994 <property name="docShell"
1995 onget="return this.mCurrentBrowser.docShell"
1998 <property name="webNavigation"
1999 onget="return this.mCurrentBrowser.webNavigation"
2002 <property name="webBrowserFind"
2004 onget="return this.mCurrentBrowser.webBrowserFind"/>
2006 <property name="webProgress"
2008 onget="return this.mCurrentBrowser.webProgress"/>
2010 <property name="contentWindow"
2012 onget="return this.mCurrentBrowser.contentWindow"/>
2014 <property name="sessionHistory"
2015 onget="return this.mCurrentBrowser.sessionHistory;"
2018 <property name="markupDocumentViewer"
2019 onget="return this.mCurrentBrowser.markupDocumentViewer;"
2022 <property name="contentViewerEdit"
2023 onget="return this.mCurrentBrowser.contentViewerEdit;"
2026 <property name="contentViewerFile"
2027 onget="return this.mCurrentBrowser.contentViewerFile;"
2030 <property name="documentCharsetInfo"
2031 onget="return this.mCurrentBrowser.documentCharsetInfo;"
2034 <property name="contentDocument"
2035 onget="return this.mCurrentBrowser.contentDocument;"
2038 <property name="contentTitle"
2039 onget="return this.mCurrentBrowser.contentTitle;"
2042 <property name="securityUI"
2043 onget="return this.mCurrentBrowser.securityUI;"
2046 <method name="find">
2049 return this.mCurrentBrowser.find();
2054 <method name="findAgain">
2057 return this.mCurrentBrowser.findAgain();
2062 <method name="findPrevious">
2065 return this.mCurrentBrowser.findPrevious();
2070 <method name="dragDropSecurityCheck">
2071 <parameter name="aEvent"/>
2072 <parameter name="aDragSession"/>
2073 <parameter name="aUri"/>
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;
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
2090 var uriStr = aUri.replace(/^\s*|\s*$/g, '');
2094 uri = Components.classes["@mozilla.org/network/io-service;1"]
2095 .getService(Components.interfaces.nsIIOService)
2096 .newURI(uriStr, null, null);
2101 // aUri is a URI, do the security check.
2102 var sourceURI = sourceDoc.documentURI;
2104 const nsIScriptSecurityManager =
2105 Components.interfaces.nsIScriptSecurityManager;
2107 Components.classes["@mozilla.org/scriptsecuritymanager;1"]
2108 .getService(nsIScriptSecurityManager);
2111 secMan.checkLoadURIStr(sourceURI, uriStr,
2112 nsIScriptSecurityManager.STANDARD);
2114 // Stop event propagation right here.
2115 aEvent.stopPropagation();
2117 throw "Drop of " + aUri + " denied.";
2125 <field name="_keyEventHandler" readonly="true">
2128 handleEvent: function handleEvent(aEvent) {
2129 if (!aEvent.isTrusted) {
2130 // Don't let untrusted events mess with tabs.
2134 if (('shiftKey' in aEvent && aEvent.shiftKey) ||
2135 ('altKey' in aEvent && aEvent.altKey))
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();
2143 aEvent.stopPropagation();
2144 aEvent.preventDefault();
2147 if (aEvent.target.localName == "tabbrowser") {
2148 switch (aEvent.keyCode) {
2149 case KeyEvent.DOM_VK_UP:
2150 this.tabbrowser.moveTabBackward();
2152 case KeyEvent.DOM_VK_DOWN:
2153 this.tabbrowser.moveTabForward();
2155 case KeyEvent.DOM_VK_RIGHT:
2156 case KeyEvent.DOM_VK_LEFT:
2157 this.tabbrowser.moveTabOver(aEvent);
2159 case KeyEvent.DOM_VK_HOME:
2160 this.tabbrowser.moveTabToStart();
2162 case KeyEvent.DOM_VK_END:
2163 this.tabbrowser.moveTabToEnd();
2166 // Stop the keypress event for the above keyboard
2170 aEvent.stopPropagation();
2171 aEvent.preventDefault();
2178 <property name="canFindAgain"
2179 onget="return this.mCurrentBrowser.canFindAgain;"
2182 <property name="userTypedClear"
2183 onget="return this.mCurrentBrowser.userTypedClear;"
2184 onset="return this.mCurrentBrowser.userTypedClear = val;"/>
2186 <property name="userTypedValue"
2187 onget="return this.mCurrentBrowser.userTypedValue;"
2188 onset="return this.mCurrentBrowser.userTypedValue = val;"/>
2190 <property name="forceSyncURLBarUpdate"
2191 onget="return this.mModalDialogShowing;"/>
2193 <method name="createTooltip">
2194 <parameter name="event"/>
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"));
2205 if (tn.hasAttribute("label")) {
2206 event.target.setAttribute("label", tn.getAttribute("label"));
2216 try{this.mStrip.width= this.mPrefs.getIntPref("browser.tabs.width");}catch(ex){};
2218 this.mCurrentBrowser = this.mPanelContainer.childNodes[0].firstChild;
2219 this.mCurrentTab = this.mTabContainer.firstChild;
2220 document.addEventListener("keypress", this._keyEventHandler, false);
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;
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);
2239 document.removeEventListener("keypress", this._keyEventHandler, false);
2245 <handler event="DOMLinkAdded" phase="capturing" action="this.onLinkAdded(event);"/>
2247 <handler event="DOMWindowClose" phase="capturing">
2249 if (!event.isTrusted)
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
2259 if (this.getBrowserAtIndex(0).contentWindow != event.target)
2260 event.preventDefault();
2266 for (; i < browsers.length; ++i) {
2267 if (this.getBrowserAtIndex(i).contentWindow == event.target) {
2268 this.removeTab(this.mTabContainer.childNodes[i]);
2269 event.preventDefault();
2276 <handler event="DOMWillOpenModalDialog" phase="capturing">
2278 if (!event.isTrusted)
2281 // We're about to open a modal dialog, make sure the opening
2282 // tab is brought to the front.
2284 var targetTop = event.target.top;
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];
2296 <handler event="DOMModalDialogClosed" phase="capturing">
2298 if (!event.isTrusted)
2301 this.mModalDialogShowing = false;
2304 <handler event="resize">
2306 this.mPrefs.setIntPref("browser.tabs.width", this.mStrip.width);
2312 <binding id="tabbrowser-tabs"
2313 extends="chrome://global/content/bindings/tabbox.xml#tabs" >
2315 <xul:vbox style="overflow-y: auto; overflow-x: hidden;" anonid="arrowscrollbox" flex="1">
2316 <children includes="tab"/>
2320 <!--<handler event="dblclick">
2323 <handler event="click" button="1">
2324 if (event.target==this) undoCloseTab();
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>
2336 <!--<xul:label class="tab-text" xbl:inherits="value=label,accesskey,crop,disabled" flex="1"/>-->
2341 <field name="mCorrespondingMenuitem">null</field>
2345 <method name="textinit">
2347 document.getAnonymousElementByAttribute(this,'anonid','tabtext').textContent= this.label;
2353 <handler event="DOMAttrModified"><![CDATA[ if (event.attrName=='label') this.textinit(); ]]></handler>