gecko/browser/base/content/tabbrowser.xml

1423 lines
52 KiB
XML
Raw Normal View History

<?xml version="1.0"?>
<!--
- The contents of this file are subject to the Mozilla Public
- License Version 1.1 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of
- the License at http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS
- IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- implied. See the License for the specific language governing
- rights and limitations under the License.
-
- The Original Code is this file as it was released on
- March 28, 2001.
-
2007-08-21 21:59:00 -07:00
- The Initial Developer of the Original Code is David Hyatt
- Portions created by David Hyatt are Copyright (C) 2001
- David Hyatt. All Rights Reserved.
-
- Contributor(s):
- David Hyatt <hyatt@netscape.com> (Original Author of <tabbrowser>)
-
- Alternatively, the contents of this file may be used under the
- terms of the GNU General Public License Version 2 or later (the
- "GPL"), in which case the provisions of the GPL are applicable
- instead of those above. If you wish to allow use of your
- version of this file only under the terms of the GPL and not to
- allow others to use your version of this file under the MPL,
- indicate your decision by deleting the provisions above and
- replace them with the notice and other provisions required by
- the GPL. If you do not delete the provisions above, a recipient
- may use your version of this file under either the MPL or the
- GPL.
-->
<!DOCTYPE bindings [
<!ENTITY % tabBrowserDTD SYSTEM "chrome://global/locale/tabbrowser.dtd" >
%tabBrowserDTD;
]>
<bindings id="tabBrowserBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="tabbrowser">
<resources>
<stylesheet src="chrome://global/skin/browser.css"/>
</resources>
<content>
<xul:stringbundle src="chrome://global/locale/tabbrowser.properties"/>
<xul:tabbox flex="1" xbl:inherits="handleCtrlPageUpDown" eventnode="document"
onselect="if (!('updateCurrentBrowser' in this.parentNode) || event.target.localName != 'tabpanels') return; this.parentNode.updateCurrentBrowser();">
<xul:hbox class="tabbrowser-strip chromeclass-toolbar" collapsed="true" tooltip="_child" context="_child">
<xul:tooltip onpopupshowing="event.preventBubble(); if (document.tooltipNode.hasAttribute('label')) { this.setAttribute('label', document.tooltipNode.getAttribute('label')); return true; } return false;"/>
<xul:menupopup onpopupshowing="this.parentNode.parentNode.parentNode.updatePopupMenu(this);">
<xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;"
xbl:inherits="oncommand=onnewtab"/>
<xul:menuseparator/>
<xul:menuitem label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.reloadTab(tabbrowser.mContextTab);"/>
<xul:menuitem label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.reloadAllTabs(tabbrowser.mContextTab);"/>
<xul:menuitem label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/>
<xul:menuseparator/>
<xul:menuitem label="&closeTab.label;" accesskey="&closeTab.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode;
tabbrowser.removeTab(tabbrowser.mContextTab);"/>
</xul:menupopup>
<xul:tabs class="tabbrowser-tabs" closebutton="true" flex="1"
setfocus="false"
onclick="this.parentNode.parentNode.parentNode.onTabClick(event);"
onmousedown="this.parentNode.parentNode.parentNode.updateContextTab(event);"
ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode.parentNode);
event.stopPropagation();"
ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode.parentNode);
event.stopPropagation();"
xbl:inherits="onnewtab"
ondblclick="if (event.originalTarget.localName != 'tab') this.parentNode.parentNode.parentNode.selectedTab = this.parentNode.parentNode.parentNode.addTab();"
onclosetab="var node = this.parentNode;
while (node.localName != 'tabbrowser')
node = node.parentNode;
node.removeCurrentTab();">
<xul:tab validate="never"
onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image'));
this.removeAttribute('image');"
maxwidth="250" width="0" minwidth="30" flex="100"
class="tabbrowser-tab" label="&untitledTab;" crop="end"/>
</xul:tabs>
</xul:hbox>
<xul:tabpanels flex="1" class="plain">
2007-08-21 21:59:04 -07:00
<xul:browser type="content-primary" disablehistory="true" xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/>
</xul:tabpanels>
</xul:tabbox>
<children/>
</content>
<implementation>
<field name="mPrefs" readonly="true">
Components.classes['@mozilla.org/preferences-service;1']
.getService(Components.interfaces.nsIPrefService)
.getBranch(null);
</field>
<field name="mTabBox">
document.getAnonymousNodes(this)[1]
</field>
<field name="mStrip">
this.mTabBox.firstChild
</field>
<field name="mTabContainer">
this.mStrip.childNodes[2]
</field>
<field name="mPanelContainer">
this.mTabBox.childNodes[1]
</field>
<field name="mStringBundle">
document.getAnonymousNodes(this)[0]
</field>
<field name="mCurrentTab">
null
</field>
<field name="mCurrentBrowser">
null
</field>
<field name="mProgressListeners">
null
</field>
<field name="mTabListeners">
new Array()
</field>
<field name="mTabFilters">
new Array()
</field>
<field name="mTabbedMode">
false
</field>
<field name="mIsBusy">
false
</field>
<field name="mMissedIconCache">
null
</field>
<field name="mContextTab">
null
</field>
<!-- A web progress listener object definition for a given tab. -->
<method name="mTabProgressListener">
<parameter name="aTab"/>
<parameter name="aBrowser"/>
<parameter name="aStartsBlank"/>
<body>
<![CDATA[
return ({
mTabBrowser: this,
mTab: aTab,
mBrowser: aBrowser,
mBlank: aStartsBlank,
mIcon: "",
onProgressChange : function (aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress)
{
if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
var p = this.mTabBrowser.mProgressListeners[i];
if (p)
p.onProgressChange(aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress);
}
}
},
onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
{
if (!aRequest)
return;
var oldBlank = this.mBlank;
const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
const nsIChannel = Components.interfaces.nsIChannel;
if (aStateFlags & nsIWebProgressListener.STATE_START &&
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
// It's okay to clear what the user typed when we start
// loading a document. If the user types, this flag gets
// set to false, if the document load ends without an
// onLocationChange, this flag also gets set to false
// (so we keep it while switching tabs after failed load
if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
this.mBrowser.userTypedClear = true;
if (!this.mBlank) {
this.mTab.setAttribute("busy", "true");
this.mTab.label = this.mTabBrowser.mStringBundle.getString("tabs.loading");
this.mTab.removeAttribute("image");
this.mIcon = "";
if (this.mTabBrowser.mCurrentTab == this.mTab)
this.mTabBrowser.mIsBusy = true;
}
}
else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
// The document is done loading, it's okay to clear
// the value again.
if (aWebProgress.DOMWindow == this.mBrowser.contentWindow)
this.mBrowser.userTypedClear = false;
if (this.mBlank)
this.mBlank = false;
this.mTab.removeAttribute("busy");
var location = aRequest.QueryInterface(nsIChannel).URI;
if (this.mIcon) {
this.mTab.setAttribute("image", this.mIcon);
mIcon = "";
}
else if (this.mTabBrowser.shouldLoadFavIcon(location))
this.mTabBrowser.loadFavIcon(location, "image", this.mTab);
if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
this.mTabBrowser.setTabTitle(this.mTab);
if (this.mTabBrowser.mCurrentTab == this.mTab)
this.mTabBrowser.mIsBusy = false;
}
if (!oldBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
var p = this.mTabBrowser.mProgressListeners[i];
if (p)
p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
}
}
}
,
onLocationChange : function(aWebProgress, aRequest, aLocation) {
// The document loaded correctly, clear the value if we should
if (this.mBrowser.userTypedClear)
this.mBrowser.userTypedValue = null;
if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) {
for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
var p = this.mTabBrowser.mProgressListeners[i];
if (p)
p.onLocationChange(aWebProgress, aRequest, aLocation);
}
}
},
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {
if (this.mBlank)
return;
if (this.mTabBrowser.mCurrentTab == this.mTab) {
for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
var p = this.mTabBrowser.mProgressListeners[i];
if (p)
p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage);
}
}
},
onSecurityChange : function(aWebProgress, aRequest, aState)
{
if (this.mTabBrowser.mCurrentTab == this.mTab) {
for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
var p = this.mTabBrowser.mProgressListeners[i];
if (p)
p.onSecurityChange(aWebProgress, aRequest, aState);
}
}
},
QueryInterface : function(aIID)
{
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
}
});
]]>
</body>
</method>
<method name="buildFavIconString">
<parameter name="aURI"/>
<body>
<![CDATA[
var end = (aURI.port == -1) ? "/favicon.ico" : (":" + aURI.port + "/favicon.ico");
return aURI.scheme + "://" + aURI.host + end;
]]>
</body>
</method>
<method name="shouldLoadFavIcon">
<parameter name="aURI"/>
<body>
<![CDATA[
return (aURI && this.mPrefs.getBoolPref("browser.chrome.site_icons") &&
this.mPrefs.getBoolPref("browser.chrome.favicons") &&
("schemeIs" in aURI) && (aURI.schemeIs("http") || aURI.schemeIs("https")));
]]>
</body>
</method>
<method name="loadFavIcon">
<parameter name="aURI"/>
<parameter name="aAttr"/>
<parameter name="aElt"/>
<body>
<![CDATA[
var iconURL = this.buildFavIconString(aURI);
var entry = this.openCacheEntry(iconURL, Components.interfaces.nsICache.ACCESS_READ);
if (!entry)
aElt.setAttribute(aAttr, iconURL);
else {
entry.close();
entry = null;
}
]]>
</body>
</method>
<method name="addToMissedIconCache">
<parameter name="aURI"/>
<body>
<![CDATA[
var entry = this.openCacheEntry(aURI, Components.interfaces.nsICache.ACCESS_READ_WRITE);
if (!entry)
return;
if (entry.accessGranted == Components.interfaces.nsICache.ACCESS_WRITE)
// It's a new entry. Just write a bit of metadata in to the entry.
entry.setMetaDataElement("Icon", "Missed");
entry.markValid();
entry.close();
]]>
</body>
</method>
<method name="openCacheEntry">
<parameter name="key"/>
<parameter name="access"/>
<body>
<![CDATA[
try {
if (!this.mMissedIconCache) {
var cacheService = Components.classes['@mozilla.org/network/cache-service;1'].getService(Components.interfaces.nsICacheService);
this.mMissedIconCache = cacheService.createSession("MissedIconCache", Components.interfaces.nsICache.STORE_ANYWHERE, true);
if (!this.mMissedIconCache)
return null;
}
return this.mMissedIconCache.openCacheEntry(key, access, true);
}
catch (e) {
return null;
}
]]>
</body>
</method>
<method name="updateTitlebar">
<body>
<![CDATA[
var newTitle = "";
var docTitle;
if (this.docShell.contentViewer)
2007-08-21 21:59:00 -07:00
docTitle = this.contentDocument.title;
if (!docTitle)
docTitle = this.ownerDocument.documentElement.getAttribute("titledefault");
var modifier = this.ownerDocument.documentElement.getAttribute("titlemodifier");
if (docTitle) {
newTitle += this.ownerDocument.documentElement.getAttribute("titlepreface");
newTitle += docTitle;
var sep = this.ownerDocument.documentElement.getAttribute("titlemenuseparator");
if (modifier)
newTitle += sep;
}
newTitle += modifier;
window.title = newTitle;
]]>
</body>
</method>
<method name="updateContextTab">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (aEvent.originalTarget.localName == "tab")
this.mContextTab = aEvent.originalTarget;
else
this.mContextTab = document.popupNode;
]]>
</body>
</method>
<method name="updatePopupMenu">
<parameter name="aPopupMenu"/>
<body>
<![CDATA[
var disabled = this.mPanelContainer.childNodes.length == 1;
var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
for (var i = 0; i < menuItems.length; i++)
menuItems[i].setAttribute("disabled", disabled);
]]>
</body>
</method>
<method name="updateCurrentBrowser">
<body>
<![CDATA[
var newBrowser = this.mPanelContainer.childNodes[this.mPanelContainer.selectedIndex];
2007-08-21 21:58:59 -07:00
if (this.mCurrentBrowser == newBrowser)
return;
if (this.mCurrentBrowser) {
this.mCurrentBrowser.focusedWindow = document.commandDispatcher.focusedWindow;
this.mCurrentBrowser.focusedElement = document.commandDispatcher.focusedElement;
if (this.mCurrentBrowser.focusedElement) {
// Clear focus outline before we draw on top of it
this.mCurrentBrowser.focusedElement.blur();
}
this.mCurrentBrowser.setAttribute("type", "content");
}
2007-08-21 21:59:00 -07:00
var updatePageReport = false;
if ((this.mCurrentBrowser.pageReport && !newBrowser.pageReport) ||
(!this.mCurrentBrowser.pageReport && newBrowser.pageReport))
updatePageReport = true;
newBrowser.setAttribute("type", "content-primary");
this.mCurrentBrowser = newBrowser;
this.mCurrentTab = this.selectedTab;
2007-08-21 21:59:00 -07:00
if (updatePageReport)
this.mCurrentBrowser.updatePageReport();
// Update the URL bar.
var loc = this.mCurrentBrowser.currentURI;
2007-08-21 21:59:00 -07:00
if (!loc)
loc = ({ spec: "" });
var webProgress = this.mCurrentBrowser.webProgress;
var securityUI = this.mCurrentBrowser.securityUI;
// Remember the current clear state, then set it to false
// so we don't clear the userTypedValue when just switching
// tabs. Set it back to its old state after we're done.
var userTypedClear = this.mCurrentBrowser.userTypedClear;
this.mCurrentBrowser.userTypedClear = false;
var i, p;
for (i = 0; i < this.mProgressListeners.length; i++) {
p = this.mProgressListeners[i];
if (p) {
p.onLocationChange(webProgress, null, loc);
if (securityUI)
p.onSecurityChange(webProgress, null, securityUI.state);
var listener = this.mTabListeners[this.mPanelContainer.selectedIndex];
if (listener.mIcon)
p.onLinkIconAvailable(newBrowser, listener.mIcon);
}
}
this.mCurrentBrowser.userTypedClear = userTypedClear;
// Update the window title.
this.updateTitlebar();
// If the new tab is busy, and our current state is not busy, then
// we need to fire a start to all progress listeners.
const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
if (this.mCurrentTab.hasAttribute("busy") && !this.mIsBusy) {
this.mIsBusy = true;
webProgress = this.mCurrentBrowser.webProgress;
for (i = 0; i < this.mProgressListeners.length; i++) {
p = this.mProgressListeners[i];
if (p)
p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_START | nsIWebProgressListener.STATE_IS_NETWORK, 0);
}
}
// If the new tab is not busy, and our current state is busy, then
// we need to fire a stop to all progress listeners.
if (!this.mCurrentTab.hasAttribute("busy") && this.mIsBusy) {
this.mIsBusy = false;
webProgress = this.mCurrentBrowser.webProgress;
for (i = 0; i < this.mProgressListeners.length; i++) {
p = this.mProgressListeners[i];
if (p)
p.onStateChange(webProgress, null, nsIWebProgressListener.STATE_STOP | nsIWebProgressListener.STATE_IS_NETWORK, 0);
}
}
if (document.commandDispatcher.focusedElement &&
document.commandDispatcher.focusedElement.parentNode ==
this.mCurrentTab.parentNode) {
// The focus is on a tab in the same tab panel
return; // If focus was on a tab, switching tabs focuses the new tab
}
var whatToFocus = window.content;
// Focus the previously focused element or window
if (newBrowser.focusedElement) {
if (newBrowser.focusedElement.parentNode !=
this.mCurrentTab.parentNode) {
// Focus the remembered element unless it's in the current tab panel
whatToFocus = newBrowser.focusedElement;
}
}
else if (newBrowser.focusedWindow) {
whatToFocus = newBrowser.focusedWindow;
}
function setFocus(element) {
document.commandDispatcher.suppressFocusScroll = true;
Components.lookupMethod(element, "focus").call(element);
document.commandDispatcher.suppressFocusScroll = false;
}
// Use setTimeout to avoid focus outline ghosting.
setTimeout(setFocus, 0, whatToFocus);
]]>
</body>
</method>
<method name="onTabClick">
<parameter name="event"/>
<body>
<![CDATA[
if (event.button != 1 || event.target.localName != 'tab' ||
this.mPrefs.getBoolPref("middlemouse.contentLoadURL"))
return;
this.removeTab(event.target);
event.stopPropagation();
]]>
</body>
</method>
<method name="onLinkAdded">
<parameter name="event"/>
<body>
<![CDATA[
// mechanism for reading properties of the underlying XPCOM object
// (ignoring potential getters/setters added by malicious content)
var safeGetProperty = function(obj, propname) {
return Components.lookupMethod(obj, propname).call(obj);
};
var tabBrowser = this.parentNode.parentNode;
if (!tabBrowser.mPrefs.getBoolPref("browser.chrome.site_icons"))
return;
if (!event.target.rel.match((/(?:^|\s)icon(?:\s|$)/i)))
return;
// We have an icon.
var href = event.target.href;
if (!href)
return;
const nsIContentPolicy = Components.interfaces.nsIContentPolicy;
try {
var contentPolicy =
Components.classes['@mozilla.org/layout/content-policy;1']
.getService(nsIContentPolicy);
} catch(e) {
return; // Refuse to load if we can't do a security check.
}
// Verify that the load of this icon is legal.
// We check first with the security manager
const secMan =
Components.classes["@mozilla.org/scriptsecuritymanager;1"]
.getService(Components.interfaces.nsIScriptSecurityManager);
// Get the IOService so we can make URIs
const ioService =
Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
const targetDoc = safeGetProperty(event.target, "ownerDocument");
// Make a URI out of our href.
var docCharset = safeGetProperty(targetDoc, "characterSet");
var uri = ioService.newURI(href, docCharset, null);
var origURIStr = safeGetProperty(targetDoc, "documentURI");
var origURI = ioService.newURI(origURIStr, docCharset, null);
const nsIScriptSecMan =
Components.interfaces.nsIScriptSecurityManager;
try {
secMan.checkLoadURI(origURI, uri, nsIScriptSecMan.STANDARD);
} catch(e) {
return;
}
// Security says okay, now ask content policy
if (contentPolicy.shouldLoad(nsIContentPolicy.TYPE_IMAGE,
uri, origURI, event.target,
safeGetProperty(event.target, "type"),
null) != nsIContentPolicy.ACCEPT)
return;
// var browserIndex = tabBrowser.getBrowserIndexForDocument(targetDoc);
var browserIndex = -1;
if (tabBrowser.mTabbedMode) {
for (var i = 0; i < tabBrowser.mPanelContainer.childNodes.length; i++) {
if (tabBrowser.mPanelContainer.childNodes[i].contentDocument == targetDoc) {
browserIndex = i;
break;
}
}
} else {
if (tabBrowser.mCurrentBrowser.contentDocument == targetDoc)
browserIndex = 0;
}
// no browser? no favicon.
if (browserIndex == -1)
return;
var listener = tabBrowser.mTabListeners[browserIndex];
// there's no tab listener for non-tabbed mode browser 0
if (listener)
listener.mIcon = href;
if (tabBrowser.mProgressListeners) {
var targetBrowser = tabBrowser.mTabbedMode ? tabBrowser.mPanelContainer.childNodes[i] : tabBrowser.mCurrentBrowser;
for (i = 0; i < tabBrowser.mProgressListeners.length; i++) {
var p = tabBrowser.mProgressListeners[i];
if (p)
p.onLinkIconAvailable(targetBrowser, href);
}
}
]]>
</body>
</method>
<method name="onTitleChanged">
<parameter name="evt"/>
<body>
<![CDATA[
if (evt.target != this.contentDocument)
return;
var i = 0;
for ( ; i < this.parentNode.childNodes.length; i++) {
if (this.parentNode.childNodes[i] == this)
break;
}
var tabBrowser = this.parentNode.parentNode.parentNode;
var tab = tabBrowser.mTabContainer.childNodes[i];
tabBrowser.setTabTitle(tab);
if (tab == tabBrowser.mCurrentTab)
tabBrowser.updateTitlebar();
]]>
</body>
</method>
<method name="setTabTitle">
<parameter name="aTab"/>
<body>
<![CDATA[
var browser = this.getBrowserForTab(aTab);
var crop = "end";
2007-08-21 21:59:00 -07:00
var titleViaGetter = browser.contentDocument.__proto__.__lookupGetter__('title').call(browser.contentDocument);
var title;
if (titleViaGetter)
title = titleViaGetter
else if (browser.currentURI.spec && browser.currentURI.spec != "about:blank") {
title = browser.currentURI.spec;
crop = "center";
}
2007-08-21 21:59:00 -07:00
else
title = this.mStringBundle.getString("tabs.untitled");
aTab.label = title;
aTab.setAttribute("crop", crop);
]]>
</body>
</method>
<method name="setStripVisibilityTo">
<parameter name="aShow"/>
<body>
<![CDATA[
this.mStrip.collapsed = !aShow;
if (aShow) {
// XXXdwh temporary unclean dependency on specific menu items in navigator.xul
document.getElementById("menu_closeWindow").hidden = false;
document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.closeTab"));
if (!this.mTabbedMode)
this.enterTabbedMode();
}
else {
// XXXdwh temporary unclean dependency on specific menu items in navigator.xul
document.getElementById("menu_closeWindow").hidden = true;
document.getElementById("menu_close").setAttribute("label", this.mStringBundle.getString("tabs.close"));
}
]]>
</body>
</method>
<method name="getStripVisibility">
<body>
return !this.mStrip.collapsed;
</body>
</method>
<method name="enterTabbedMode">
<body>
<![CDATA[
this.mTabbedMode = true; // Welcome to multi-tabbed mode.
2007-08-21 21:59:00 -07:00
// Get the first tab all hooked up with a title listener and popup blocking listener.
this.mCurrentBrowser.addEventListener("DOMTitleChanged", this.onTitleChanged, false);
this.setTabTitle(this.mCurrentTab);
// Hook up our favicon.
var uri = this.mCurrentBrowser.currentURI;
if (this.shouldLoadFavIcon(uri))
this.loadFavIcon(uri, "image", this.mCurrentTab);
var filter;
if (this.mTabFilters.length > 0) {
// Use the filter hooked up in our addProgressListener
filter = this.mTabFilters[0];
} else {
// create a filter and hook it up to our first browser
filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Components.interfaces.nsIWebProgress);
this.mTabFilters[0] = filter;
this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
}
// Remove all our progress listeners from the active browser's filter.
if (this.mProgressListeners) {
for (var i = 0; i < this.mProgressListeners.length; i++) {
var p = this.mProgressListeners[i];
if (p)
filter.removeProgressListener(p);
}
}
// Wire up a progress listener to our filter.
const listener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, false);
filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
this.mTabListeners[0] = listener;
]]>
</body>
</method>
<method name="addTab">
<parameter name="aURI"/>
<parameter name="aReferrerURI"/>
<parameter name="aCharset"/>
<parameter name="aPostData"/>
<body>
<![CDATA[
var blank = (aURI == "about:blank");
if (!this.mTabbedMode)
this.enterTabbedMode();
var b = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"browser");
var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"tab");
if (blank)
t.setAttribute("label", this.mStringBundle.getString("tabs.untitled"));
else
t.setAttribute("label", aURI);
t.setAttribute("crop", "end");
t.maxWidth = 250;
t.minWidth = 30;
t.width = 0;
t.setAttribute("flex", "100");
t.setAttribute("validate", "never");
t.setAttribute("onerror", "this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); this.removeAttribute('image');");
this.mTabContainer.appendChild(t);
b.setAttribute("type", "content");
b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
this.mPanelContainer.appendChild(b);
b.addEventListener("DOMTitleChanged", this.onTitleChanged, false);
if (this.mStrip.collapsed)
this.setStripVisibilityTo(true);
this.mPrefs.setBoolPref("browser.tabs.forceHide", false);
// wire up a progress listener for the new browser object.
var position = this.mTabContainer.childNodes.length-1;
var tabListener = this.mTabProgressListener(t, b, blank);
const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Components.interfaces.nsIWebProgress);
filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
this.mTabListeners[position] = tabListener;
this.mTabFilters[position] = filter;
if (!blank) {
// pretend the user typed this so it'll be available till
// the document successfully loads
b.userTypedValue = aURI;
if (aPostData === undefined)
aPostData = null;
b.loadURIWithFlags(aURI, nsIWebNavigation.LOAD_FLAGS_NONE,
aReferrerURI, aPostData, null);
}
return t;
]]>
</body>
</method>
<method name="removeAllTabsBut">
<parameter name="aTab"/>
<body>
<![CDATA[
if (aTab.localName != "tab")
aTab = this.mCurrentTab;
else
this.mTabContainer.selectedItem = aTab;
var childNodes = this.mTabContainer.childNodes;
for (var i = childNodes.length - 1; i >= 0; --i) {
if (childNodes[i] != aTab)
this.removeTab(childNodes[i]);
}
]]>
</body>
</method>
<method name="removeCurrentTab">
<body>
<![CDATA[
return this.removeTab(this.mCurrentTab);
]]>
</body>
</method>
<method name="removeTab">
<parameter name="aTab"/>
<body>
<![CDATA[
if (aTab.localName != "tab")
aTab = this.mCurrentTab;
var l = this.mTabContainer.childNodes.length;
if (l == 1) {
// hide the tab bar
this.mPrefs.setBoolPref("browser.tabs.forceHide", true);
this.setStripVisibilityTo(false);
return;
}
var ds = this.getBrowserForTab(aTab).docShell;
if (ds.contentViewer && !ds.contentViewer.permitUnload())
return;
if (l == 2) {
var autohide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
if (autohide)
this.setStripVisibilityTo(false);
}
var index = -1;
if (this.mCurrentTab == aTab)
index = this.mPanelContainer.selectedIndex;
else {
// Find and locate the tab in our list.
for (var i = 0; i < l; i++)
if (this.mTabContainer.childNodes[i] == aTab)
index = i;
}
// Remove the tab's filter and progress listener.
const filter = this.mTabFilters[index];
var oldBrowser = this.mPanelContainer.childNodes[index];
oldBrowser.webProgress.removeProgressListener(filter);
filter.removeProgressListener(this.mTabListeners[index]);
this.mTabFilters.splice(index, 1);
this.mTabListeners.splice(index, 1);
2007-08-21 21:59:00 -07:00
// Remove our title change and blocking listeners
oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, false);
// We are no longer the primary content area.
oldBrowser.setAttribute("type", "content");
// Now select the new tab before nuking the old one.
var currentIndex = this.mPanelContainer.selectedIndex;
var newIndex = -1;
if (currentIndex > index)
newIndex = currentIndex-1;
else if (currentIndex < index)
newIndex = currentIndex;
2007-08-21 21:58:52 -07:00
else if (index == l - 1)
newIndex = index-1;
else
newIndex = index;
var oldTab = aTab;
// clean up the before/afterselected attributes before removing the tab
oldTab.selected = false;
2007-08-21 21:59:00 -07:00
// XXX browser's destructor isn't always called, so we force a cleanup ourselves
oldBrowser.destroy();
this.mTabContainer.removeChild(oldTab);
this.mPanelContainer.removeChild(oldBrowser);
this.selectedTab = this.mTabContainer.childNodes[newIndex];
2007-08-21 21:58:59 -07:00
this.mPanelContainer.selectedIndex = newIndex;
2007-08-21 21:59:00 -07:00
2007-08-21 21:58:59 -07:00
this.updateCurrentBrowser();
2007-08-21 21:59:00 -07:00
}
]]>
</body>
</method>
<method name="reloadAllTabs">
<body>
<![CDATA[
var l = this.mPanelContainer.childNodes.length;
for (var i = 0; i < l; i++) {
try {
this.mPanelContainer.childNodes[i].reload();
} catch (e) {
// ignore failure to reload so others will be reloaded
}
}
]]>
</body>
</method>
<method name="reloadTab">
<parameter name="aTab"/>
<body>
<![CDATA[
if (aTab.localName != "tab")
aTab = this.mCurrentTab;
this.getBrowserForTab(aTab).reload();
]]>
</body>
</method>
<method name="addProgressListener">
<parameter name="aListener"/>
<parameter name="aMask"/>
<body>
<![CDATA[
if (!this.mProgressListeners) {
this.mProgressListeners = [];
const autoHide = this.mPrefs.getBoolPref("browser.tabs.autoHide");
const forceHide = this.mPrefs.getBoolPref("browser.tabs.forceHide");
if (!autoHide && !forceHide)
this.setStripVisibilityTo(true);
// Hook up a listener for <link>s.
this.mPanelContainer.addEventListener("DOMLinkAdded", this.onLinkAdded, false);
}
if (!this.mTabbedMode && this.mProgressListeners.length == 1) {
// If we are adding a 2nd progress listener, we need to enter tabbed mode
// because the browser status filter can only handle one progress listener.
// In tabbed mode, mTabProgressListener is used which will iterate over all listeners.
this.enterTabbedMode();
}
this.mProgressListeners.push(aListener);
if (!this.mTabbedMode) {
// If someone does this:
// addProgressListener, removeProgressListener, addProgressListener
// don't create a new filter; reuse the existing filter.
if (this.mTabFilters.length == 0) {
// hook a filter up to our first browser
const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Components.interfaces.nsIWebProgress);
this.mTabFilters[0] = filter;
this.mCurrentBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
}
// Directly hook the listener up to the filter for better performance
this.mTabFilters[0].addProgressListener(aListener, aMask);
}
]]>
</body>
</method>
<method name="removeProgressListener">
<parameter name="aListener"/>
<body>
<![CDATA[
if (!this.mProgressListeners) return;
for (var i = 0; i < this.mProgressListeners.length; i++) {
if (this.mProgressListeners[i] == aListener) {
this.mProgressListeners[i] = null;
break;
}
}
if (!this.mTabbedMode)
// Don't forget to remove it from the filter we hooked it up to
this.mTabFilters[0].removeProgressListener(aListener);
]]>
</body>
</method>
<method name="getBrowserForTab">
<parameter name="aTab"/>
<body>
<![CDATA[
if (this.mCurrentTab == aTab)
return this.mCurrentBrowser;
for (var i = 0; i < this.mTabContainer.childNodes.length; i++) {
if (this.mTabContainer.childNodes[i] == aTab) {
return this.mPanelContainer.childNodes[i];
}
}
return null;
]]>
</body>
</method>
<property name="tabContainer">
<getter>
return this.mTabContainer;
</getter>
</property>
<property name="selectedTab">
<getter>
return this.mTabBox.selectedTab;
</getter>
<setter>
<![CDATA[
// Update the tab
this.mTabBox.selectedTab = val;
return val;
]]>
</setter>
</property>
<property name="selectedBrowser"
onget="return this.mCurrentBrowser;"
readonly="true"/>
<property name="browsers"
onget="return this.mPanelContainer.childNodes;"
readonly="true"/>
<!-- Drag and drop observer API -->
<!--<method name="onDragStart">
<parameter name="aEvent"/>
<parameter name="aXferData"/>
<parameter name="aDragAction"/>
<body/>
</method>-->
<method name="onDragOver">
<parameter name="aEvent"/>
<parameter name="aFlavour"/>
<parameter name="aDragSession"/>
<body>
<![CDATA[
return; // Just having this makes our feedback correct.
]]>
</body>
</method>
<method name="onDrop">
<parameter name="aEvent"/>
<parameter name="aXferData"/>
<parameter name="aDragSession"/>
<body>
<![CDATA[
var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
// valid urls don't contain spaces ' '; if we have a space it isn't a valid url so bail out
if (!url || !url.length || url.indexOf(" ", 0) != -1)
return;
var bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground");
var tab = null;
if (aEvent.originalTarget.localName != "tab") {
// We're adding a new tab.
tab = this.addTab(getShortcutOrURI(url));
}
else {
// Load in an existing tab.
tab = aEvent.originalTarget;
this.getBrowserForTab(tab).loadURI(getShortcutOrURI(url));
}
if (this.mCurrentTab != tab && !bgLoad)
this.selectedTab = tab;
]]>
</body>
</method>
<method name="getSupportedFlavours">
<body>
<![CDATA[
var flavourSet = new FlavourSet();
flavourSet.appendFlavour("text/x-moz-url");
flavourSet.appendFlavour("text/unicode");
flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
return flavourSet;
]]>
</body>
</method>
<!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
MAKE SURE TO ADD IT HERE AS WELL. -->
<property name="canGoBack"
onget="return this.mCurrentBrowser.canGoBack;"
readonly="true"/>
<property name="canGoForward"
onget="return this.mCurrentBrowser.canGoForward;"
readonly="true"/>
<method name="goBack">
<body>
<![CDATA[
return this.mCurrentBrowser.goBack();
]]>
</body>
</method>
<method name="goForward">
<body>
<![CDATA[
return this.mCurrentBrowser.goForward();
]]>
</body>
</method>
<method name="reload">
<body>
<![CDATA[
return this.mCurrentBrowser.reload();
]]>
</body>
</method>
<method name="reloadWithFlags">
<parameter name="aFlags"/>
<body>
<![CDATA[
return this.mCurrentBrowser.reloadWithFlags(aFlags);
]]>
</body>
</method>
<method name="stop">
<body>
<![CDATA[
return this.mCurrentBrowser.stop();
]]>
</body>
</method>
<!-- throws exception for unknown schemes -->
<method name="loadURI">
<parameter name="aURI"/>
<parameter name="aReferrerURI"/>
<parameter name="aCharset"/>
<body>
<![CDATA[
return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
]]>
</body>
</method>
<!-- throws exception for unknown schemes -->
<method name="loadURIWithFlags">
<parameter name="aURI"/>
<parameter name="aFlags"/>
<parameter name="aReferrerURI"/>
<parameter name="aCharset"/>
<body>
<![CDATA[
return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset);
]]>
</body>
</method>
<method name="goHome">
<body>
<![CDATA[
return this.mCurrentBrowser.goHome();
]]>
</body>
</method>
<property name="homePage">
<getter>
<![CDATA[
return this.mCurrentBrowser.homePage;
]]>
</getter>
<setter>
<![CDATA[
this.mCurrentBrowser.homePage = val;
return val;
]]>
</setter>
</property>
<method name="gotoIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
return this.mCurrentBrowser.gotoIndex(aIndex);
]]>
</body>
</method>
<method name="attachFormFill">
<body><![CDATA[
var browsers = this.mPanelContainer.childNodes;
for (var i = 0; i < browsers.length; ++i) {
var cb = browsers[i];
cb.attachFormFill();
}
]]></body>
</method>
<method name="detachFormFill">
<body><![CDATA[
var browsers = this.mPanelContainer.childNodes;
for (var i = 0; i < browsers.length; ++i) {
var cb = browsers[i];
cb.detachFormFill();
}
]]></body>
</method>
2007-08-21 21:59:00 -07:00
<property name="pageReport"
onget="return this.mCurrentBrowser.pageReport;"
readonly="true"/>
<property name="currentURI"
onget="return this.mCurrentBrowser.currentURI;"
readonly="true"/>
<property name="docShell"
onget="return this.mCurrentBrowser.docShell"
readonly="true"/>
<property name="webNavigation"
onget="return this.mCurrentBrowser.webNavigation"
readonly="true"/>
<property name="webBrowserFind"
readonly="true"
onget="return this.mCurrentBrowser.webBrowserFind"/>
<property name="webProgress"
readonly="true"
onget="return this.mCurrentBrowser.webProgress"/>
<property name="contentWindow"
readonly="true"
onget="return this.mCurrentBrowser.contentWindow"/>
<property name="sessionHistory"
onget="return this.mCurrentBrowser.sessionHistory;"
readonly="true"/>
<property name="markupDocumentViewer"
onget="return this.mCurrentBrowser.markupDocumentViewer;"
readonly="true"/>
<property name="contentViewerEdit"
onget="return this.mCurrentBrowser.contentViewerEdit;"
readonly="true"/>
<property name="contentViewerFile"
onget="return this.mCurrentBrowser.contentViewerFile;"
readonly="true"/>
<property name="documentCharsetInfo"
onget="return this.mCurrentBrowser.documentCharsetInfo;"
readonly="true"/>
<property name="contentDocument"
onget="return this.mCurrentBrowser.contentDocument;"
readonly="true"/>
<property name="securityUI"
onget="return this.mCurrentBrowser.securityUI;"
readonly="true"/>
<method name="find">
<body>
<![CDATA[
return this.mCurrentBrowser.find();
]]>
</body>
</method>
<method name="findAgain">
<body>
<![CDATA[
return this.mCurrentBrowser.findAgain();
]]>
</body>
</method>
<method name="findPrevious">
<body>
<![CDATA[
return this.mCurrentBrowser.findPrevious();
]]>
</body>
</method>
<property name="canFindAgain"
onget="return this.mCurrentBrowser.canFindAgain;"
readonly="true"/>
<property name="userTypedClear"
onget="return this.mCurrentBrowser.userTypedClear;"
onset="return this.mCurrentBrowser.userTypedClear = val;"/>
<property name="userTypedValue"
onget="return this.mCurrentBrowser.userTypedValue;"
onset="return this.mCurrentBrowser.userTypedValue = val;"/>
<constructor>
<![CDATA[
this.mCurrentBrowser = this.mPanelContainer.firstChild;
this.mCurrentTab = this.mTabContainer.firstChild;
this.mTabBox.handleCtrlTab = !/Mac/.test(navigator.platform);
]]>
</constructor>
<destructor>
<![CDATA[
for (var i = 0; i < this.mTabListeners.length; ++i) {
this.mPanelContainer.childNodes[i].webProgress.removeProgressListener(this.mTabFilters[i]);
this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
this.mTabFilters[i] = null;
this.mTabListeners[i] = null;
this.mPanelContainer.childNodes[i].removeEventListener("DOMTitleChanged", this.onTitleChanged, false);
}
this.mPanelContainer.removeEventListener("DOMLinkAdded", this.onLinkAdded, false);
]]>
</destructor>
</implementation>
<handlers>
<handler event="keypress" modifiers="control" keycode="VK_F4">
<![CDATA[
if (this.mTabBox.handleCtrlPageUpDown)
this.removeCurrentTab();
]]>
</handler>
<handler event="DOMWindowClose">
<![CDATA[
const browsers = this.browsers;
if (browsers.length == 1)
return;
var i = 0;
for (; i < browsers.length; ++i) {
if (browsers[i].contentWindow == event.target)
break;
}
this.removeTab(this.mTabContainer.childNodes[i]);
event.preventDefault();
]]>
</handler>
</handlers>
</binding>
</bindings>