mirror of https://github.com/roytam1/boc-uxp.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3623 lines
135 KiB
3623 lines
135 KiB
<?xml version="1.0"?> |
|
|
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public |
|
- License, v. 2.0. If a copy of the MPL was not distributed with this |
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
|
|
|
<!DOCTYPE bindings [ |
|
<!ENTITY % tabBrowserDTD SYSTEM "chrome://navigator/locale/tabbrowser.dtd" > |
|
%tabBrowserDTD; |
|
]> |
|
|
|
<bindings id="tabBrowserBindings" |
|
xmlns="http://www.mozilla.org/xbl" |
|
xmlns:html="http://www.w3.org/1999/xhtml" |
|
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://navigator/skin/tabbrowser.css"/> |
|
</resources> |
|
|
|
<content> |
|
<xul:stringbundle anonid="tbstringbundle" src="chrome://navigator/locale/tabbrowser.properties"/> |
|
<xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"> |
|
<xul:hbox class="tab-drop-indicator-bar" collapsed="true"> |
|
<xul:image class="tab-drop-indicator" mousethrough="always"/> |
|
</xul:hbox> |
|
<xul:hbox class="tabbrowser-strip" collapsed="true" tooltip="_child" context="_child" |
|
anonid="strip" |
|
ondragstart="this.parentNode.parentNode._onDragStart(event);" |
|
ondragover="this.parentNode.parentNode._onDragOver(event);" |
|
ondrop="this.parentNode.parentNode._onDrop(event);" |
|
ondragleave="this.parentNode.parentNode._onDragLeave(event);"> |
|
<xul:tooltip onpopupshowing="event.stopPropagation(); return this.parentNode.parentNode.parentNode.doPreview(this);" |
|
onpopuphiding="this.parentNode.parentNode.parentNode.resetPreview(this);" orient="vertical"> |
|
<xul:label class="tooltip-label" crop="right"/> |
|
<xul:label class="tooltip-label" hidden="true"><html:canvas class="tab-tooltip-canvas"/></xul:label> |
|
</xul:tooltip> |
|
<xul:menupopup anonid="tabContextMenu" onpopupshowing="return document.getBindingParent(this).updatePopupMenu(this);"> |
|
<xul:menuitem label="&newTab.label;" accesskey="&newTab.accesskey;" |
|
xbl:inherits="oncommand=onnewtab"/> |
|
<xul:menuseparator/> |
|
<xul:menuitem label="&reloadTab.label;" accesskey="&reloadTab.accesskey;" |
|
tbattr="tabbrowser-tab" |
|
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();"/> |
|
<xul:menuitem label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;" |
|
tbattr="tabbrowser-multiple tabbrowser-tab" |
|
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode; |
|
tabbrowser.removeAllTabsBut(tabbrowser.mContextTab);"/> |
|
<xul:menuseparator/> |
|
<xul:menuitem label="&bookmarkTab.label;" accesskey="&bookmarkTab.accesskey;" |
|
tbattr="tabbrowser-tab" |
|
xbl:inherits="oncommand=onbookmarktab"/> |
|
<xul:menuitem label="&bookmarkGroup.label;" |
|
tbattr="tabbrowser-multiple" |
|
xbl:inherits="oncommand=onbookmarkgroup"/> |
|
<xul:menuitem label="&undoCloseTab.label;" accesskey="&undoCloseTab.accesskey;" |
|
tbattr="tabbrowser-undoclosetab" |
|
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode; |
|
tabbrowser.undoCloseTab(0);"/> |
|
<xul:menuseparator/> |
|
<xul:menuitem label="&closeTab.label;" accesskey="&closeTab.accesskey;" |
|
tbattr="tabbrowser-tab" |
|
oncommand="var tabbrowser = this.parentNode.parentNode.parentNode.parentNode; |
|
tabbrowser.removeTab(tabbrowser.mContextTab);"/> |
|
</xul:menupopup> |
|
|
|
<xul:tabs class="tabbrowser-tabs" flex="1" |
|
anonid="tabcontainer" |
|
tooltiptextnew="&newTabButton.tooltip;" |
|
tooltiptextclose="&closeTabButton.tooltip;" |
|
tooltiptextalltabs="&listAllTabs.tooltip;" |
|
setfocus="false" |
|
onclick="this.parentNode.parentNode.parentNode.onTabClick(event);" |
|
xbl:inherits="onnewtab,onnewtabclick" |
|
onclosetab="var node = this.parentNode; |
|
while (node.localName != 'tabbrowser') |
|
node = node.parentNode; |
|
node.removeCurrentTab();"> |
|
<xul:tab selected="true" validate="never" |
|
onerror="this.parentNode.parentNode.parentNode.parentNode.addToMissedIconCache(this.getAttribute('image')); |
|
this.removeAttribute('image');" |
|
width="0" flex="100" |
|
class="tabbrowser-tab" label="&untitledTab;" crop="end"/> |
|
</xul:tabs> |
|
</xul:hbox> |
|
<xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer"> |
|
<xul:notificationbox class="browser-notificationbox" xbl:inherits="popupnotification"> |
|
<xul:browser flex="1" type="content-primary" xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup"/> |
|
</xul:notificationbox> |
|
</xul:tabpanels> |
|
</xul:tabbox> |
|
<children/> |
|
</content> |
|
<implementation implements="nsIObserver"> |
|
<field name="mSessionStore" readonly="true"> |
|
Components.classes["@binaryoutcast.com/navigator/sessionstore;1"] |
|
.getService(Components.interfaces.nsISessionStore); |
|
</field> |
|
<field name="mPrefs" readonly="true"> |
|
Components.classes['@mozilla.org/preferences-service;1'] |
|
.getService(Components.interfaces.nsIPrefBranch); |
|
</field> |
|
<field name="mURIFixup" readonly="true"> |
|
Components.classes["@mozilla.org/docshell/urifixup;1"] |
|
.getService(Components.interfaces.nsIURIFixup); |
|
</field> |
|
<field name="mFaviconService" readonly="true"> |
|
Components.classes["@mozilla.org/browser/favicon-service;1"] |
|
.getService(Components.interfaces.nsIFaviconService) |
|
.QueryInterface(Components.interfaces.mozIAsyncFavicons); |
|
</field> |
|
<field name="mIOService" readonly="true"> |
|
Components.classes["@mozilla.org/network/io-service;1"] |
|
.getService(Components.interfaces.nsIIOService); |
|
</field> |
|
<field name="mSecMan" readonly="true"> |
|
Components.classes["@mozilla.org/scriptsecuritymanager;1"] |
|
.getService(Components.interfaces.nsIScriptSecurityManager); |
|
</field> |
|
<field name="mTabBox" readonly="true"> |
|
document.getAnonymousElementByAttribute(this, "anonid", "tabbox"); |
|
</field> |
|
<field name="mStrip" readonly="true"> |
|
document.getAnonymousElementByAttribute(this, "anonid", "strip"); |
|
</field> |
|
<field name="tabContainer" readonly="true"> |
|
document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer"); |
|
</field> |
|
<field name="mPanelContainer" readonly="true"> |
|
document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer"); |
|
</field> |
|
<field name="tabs" readonly="true"> |
|
this.tabContainer.childNodes |
|
</field> |
|
<field name="mStringBundle"> |
|
document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle"); |
|
</field> |
|
<field name="mCurrentTab"> |
|
null |
|
</field> |
|
<field name="mPreviousTab"> |
|
null |
|
</field> |
|
<field name="mCurrentBrowser"> |
|
null |
|
</field> |
|
<field name="mProgressListeners"> |
|
[] |
|
</field> |
|
<field name="mTabsProgressListeners"> |
|
[] |
|
</field> |
|
<field name="mTabListeners"> |
|
new Array() |
|
</field> |
|
<field name="mTabFilters"> |
|
new Array() |
|
</field> |
|
<field name="mLastRelatedIndex"> |
|
0 |
|
</field> |
|
<field name="usePrivateBrowsing" readonly="true"> |
|
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
|
.getInterface(Components.interfaces.nsIWebNavigation) |
|
.QueryInterface(Components.interfaces.nsILoadContext) |
|
.usePrivateBrowsing |
|
</field> |
|
<field name="mContextTab"> |
|
null |
|
</field> |
|
<field name="_keyEventHandler" readonly="true"> |
|
<![CDATA[({ |
|
handleEvent: function handleEvent(aEvent) { |
|
if (aEvent.altKey) |
|
return; |
|
|
|
if (/Mac/.test(navigator.platform)) { |
|
if (!aEvent.metaKey) |
|
return; |
|
|
|
var offset = 1; |
|
switch (aEvent.charCode) { |
|
case '}'.charCodeAt(0): |
|
offset = -1; |
|
case '{'.charCodeAt(0): |
|
if (window.getComputedStyle(this, null).direction == "ltr") |
|
offset *= -1; |
|
this.tabContainer.advanceSelectedTab(offset, true); |
|
aEvent.stopPropagation(); |
|
aEvent.preventDefault(); |
|
} |
|
} |
|
else { |
|
if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey && |
|
aEvent.keyCode == KeyEvent.DOM_VK_F4 && |
|
this.getStripVisibility()) { |
|
this.removeCurrentTab(); |
|
aEvent.stopPropagation(); |
|
aEvent.preventDefault(); |
|
} |
|
} |
|
}.bind(this) |
|
})]]> |
|
</field> |
|
<field name="arrowKeysShouldWrap"> |
|
null |
|
</field> |
|
<field name="nextTabNumber"> |
|
0 |
|
</field> |
|
<field name="_browsers"> |
|
null |
|
</field> |
|
<field name="savedBrowsers"> |
|
new Array() |
|
</field> |
|
<field name="referenceTab"> |
|
null |
|
</field> |
|
|
|
<method name="doPreview"> |
|
<parameter name="aPopup"/> |
|
<body> |
|
<![CDATA[ |
|
var tab = document.tooltipNode; |
|
if (tab.localName != "tab") |
|
return false; |
|
var b = tab.linkedBrowser; |
|
if (!b) |
|
return false; |
|
|
|
var label = aPopup.firstChild; |
|
label.setAttribute("value", tab.getAttribute("label")); |
|
|
|
var canvas = aPopup.lastChild.firstChild; |
|
canvas.parentNode.hidden = true; |
|
|
|
var win = b.contentWindow; |
|
var w = win.innerWidth; |
|
var h = win.innerHeight; |
|
|
|
if (tab == this.mCurrentTab || h == 0 || |
|
!this.mPrefs.getBoolPref("browser.tabs.tooltippreview.enable")) { |
|
return true; |
|
} |
|
|
|
var ctx; |
|
try { |
|
ctx = canvas.getContext("2d"); |
|
} catch (e) { |
|
return true; |
|
} |
|
|
|
label.width = 0; |
|
aPopup.setAttribute("tabpreview", "true"); |
|
|
|
var canvasW = this.mPrefs.getIntPref("browser.tabs.tooltippreview.width"); |
|
var canvasH = Math.round(canvasW * h / w); |
|
canvas.width = canvasW; |
|
canvas.height = canvasH; |
|
canvas.parentNode.hidden = false; |
|
|
|
var bgColor = this.mPrefs.getBoolPref("browser.display.use_system_colors") ? |
|
"Window" : |
|
this.mPrefs.getCharPref("browser.display.background_color"); |
|
if (b.contentDocument instanceof ImageDocument && |
|
!(b.contentDocument.imageRequest.imageStatus & |
|
Components.interfaces.imgIRequest.STATUS_ERROR)) { |
|
ctx.fillStyle = bgColor; |
|
ctx.fillRect(0, 0, canvasW, canvasH); |
|
var img = b.contentDocument.body.firstChild; |
|
var ratio = img.naturalHeight / img.naturalWidth; |
|
if (img.naturalHeight <= canvasH && img.naturalWidth <= canvasW) { |
|
ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight); |
|
} |
|
else if (ratio * canvasW > canvasH) { |
|
ctx.drawImage(img, 0, 0, canvasH / ratio, canvasH); |
|
} |
|
else { |
|
ctx.drawImage(img, 0, 0, canvasW, ratio * canvasW); |
|
} |
|
} |
|
else { |
|
ctx.save(); |
|
ctx.scale(canvasW / w, canvasH / h); |
|
ctx.drawWindow(win, win.pageXOffset, win.pageYOffset, w, h, bgColor); |
|
ctx.restore(); |
|
} |
|
return true; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<!-- XXXcst This should not be needed, but it seems that the tooltip |
|
sizing is happening too early when we want to stop showing the |
|
preview. This clears the label's width early (i.e. when the |
|
previous preview disappears) so that when the next tooltip appears, |
|
it doesn't start with a bad size. For now, I blame Gecko. --> |
|
<method name="resetPreview"> |
|
<parameter name="aPopup"/> |
|
<body> |
|
<![CDATA[ |
|
var label = aPopup.firstChild; |
|
// if this function is removed, these two lines need to be restored |
|
// to the non-preview codepath above |
|
label.removeAttribute("width"); |
|
aPopup.removeAttribute("tabpreview"); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="previewTab"> |
|
<parameter name="aTab"/> |
|
<parameter name="aCallback"/> |
|
<body> |
|
<![CDATA[ |
|
let currentTab = this.selectedTab; |
|
try { |
|
// Suppress focus, ownership and selected tab changes. |
|
this._previewMode = true; |
|
this.selectedTab = aTab; |
|
aCallback(); |
|
} finally { |
|
this.selectedTab = currentTab; |
|
this._previewMode = false; |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getBrowserAtIndex"> |
|
<parameter name="aIndex"/> |
|
<body> |
|
<![CDATA[ |
|
return this.browsers[aIndex]; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getBrowserIndexForDocument"> |
|
<parameter name="aDocument"/> |
|
<body> |
|
<![CDATA[ |
|
var browsers = this.browsers; |
|
for (var i = 0; i < browsers.length; i++) |
|
if (browsers[i].contentDocument == aDocument) |
|
return i; |
|
return -1; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getBrowserForDocument"> |
|
<parameter name="aDocument"/> |
|
<body> |
|
<![CDATA[ |
|
var browsers = this.browsers; |
|
for (var i = 0; i < browsers.length; i++) |
|
if (browsers[i].contentDocument == aDocument) |
|
return browsers[i]; |
|
return null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getBrowserForContentWindow"> |
|
<parameter name="aWindow"/> |
|
<body> |
|
<![CDATA[ |
|
const browsers = this.browsers; |
|
for (let browser of browsers) { |
|
if (browser.contentWindow == aWindow) |
|
return browser; |
|
} |
|
return null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getNotificationBox"> |
|
<parameter name="aBrowser"/> |
|
<body> |
|
<![CDATA[ |
|
return aBrowser ? aBrowser.parentNode : this.mCurrentBrowser.parentNode; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="_callProgressListeners"> |
|
<parameter name="aBrowser"/> |
|
<parameter name="aMethod"/> |
|
<parameter name="aArguments"/> |
|
<parameter name="aCallGlobalListeners"/> |
|
<parameter name="aCallTabsListeners"/> |
|
<body><![CDATA[ |
|
if (!aBrowser) |
|
aBrowser = this.mCurrentBrowser; |
|
|
|
if (aCallGlobalListeners != false && |
|
aBrowser == this.mCurrentBrowser) { |
|
this.mProgressListeners.forEach(function (p) { |
|
if (aMethod in p) { |
|
try { |
|
p[aMethod].apply(p, aArguments); |
|
} catch (e) { |
|
// don't inhibit other listeners |
|
Components.utils.reportError(e); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
if (aCallTabsListeners != false) { |
|
aArguments.unshift(aBrowser); |
|
|
|
this.mTabsProgressListeners.forEach(function (p) { |
|
if (aMethod in p) { |
|
try { |
|
p[aMethod].apply(p, aArguments); |
|
} catch (e) { |
|
// don't inhibit other listeners |
|
Components.utils.reportError(e); |
|
} |
|
} |
|
}); |
|
} |
|
]]></body> |
|
</method> |
|
|
|
<!-- 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, |
|
mFeeds: [], |
|
mRequest: null, |
|
mStateFlags: 0, |
|
mStatus: 0, |
|
mMessage: "", |
|
mCurProgress: 0, |
|
mMaxProgress: 0, |
|
|
|
// cache flags for correct status UI update after tab switching |
|
mTotalProgress: 0, |
|
|
|
// count of open requests (should always be 0 or 1) |
|
mRequestCount: 0, |
|
|
|
_callProgressListeners: function () { |
|
Array.unshift(arguments, this.mBrowser); |
|
return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments); |
|
}, |
|
|
|
_shouldShowProgress: function (aRequest) { |
|
if (this.mBlank) |
|
return false; |
|
|
|
// Don't show progress indicators in tabs for about: URIs |
|
// pointing to local resources. |
|
if ((aRequest instanceof Components.interfaces.nsIChannel) && |
|
aRequest.originalURI.schemeIs("about") && |
|
(aRequest.URI.schemeIs("jar") || aRequest.URI.schemeIs("file"))) |
|
return false; |
|
|
|
return true; |
|
}, |
|
|
|
onProgressChange: function (aWebProgress, aRequest, |
|
aCurSelfProgress, aMaxSelfProgress, |
|
aCurTotalProgress, aMaxTotalProgress) { |
|
if (aMaxTotalProgress > 0) |
|
this.mTab.setAttribute("progress", Math.floor(aCurTotalProgress * 9.9 / aMaxTotalProgress)); |
|
|
|
if (this.mBlank) |
|
return; |
|
|
|
this.mCurProgress = aCurTotalProgress; |
|
this.mMaxProgress = aMaxTotalProgress; |
|
|
|
this.mTabBrowser._callProgressListeners(this.mBrowser, "onProgressChange", |
|
[aWebProgress, aRequest, |
|
aCurSelfProgress, aMaxSelfProgress, |
|
aCurTotalProgress, aMaxTotalProgress]); |
|
}, |
|
|
|
onProgressChange64: function (aWebProgress, aRequest, |
|
aCurSelfProgress, aMaxSelfProgress, |
|
aCurTotalProgress, aMaxTotalProgress) { |
|
return this.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; |
|
let location, originalLocation; |
|
try { |
|
aRequest.QueryInterface(nsIChannel) |
|
location = aRequest.URI; |
|
originalLocation = aRequest.originalURI; |
|
} catch (ex) {} |
|
|
|
if (aStateFlags & nsIWebProgressListener.STATE_START) { |
|
this.mRequestCount++; |
|
} |
|
else if (aStateFlags & nsIWebProgressListener.STATE_STOP) { |
|
const NS_ERROR_UNKNOWN_HOST = 2152398878; |
|
if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) { |
|
// to prevent bug 235825: wait for the request handled |
|
// by the automatic keyword resolver |
|
return; |
|
} |
|
// since we (try to) only handle STATE_STOP of the last request, |
|
// the count of open requests should now be 0 |
|
this.mRequestCount = 0; |
|
} |
|
|
|
if (aStateFlags & nsIWebProgressListener.STATE_START && |
|
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { |
|
if (aWebProgress.isTopLevel) { |
|
// Need to use originalLocation rather than location because things |
|
// like about:privatebrowsing arrive with nsIRequest pointing to |
|
// their resolved jar: or file: URIs. |
|
if (!(originalLocation && gInitialPages.has(originalLocation.spec) && |
|
originalLocation != "about:blank" && this.mBrowser.currentURI && |
|
this.mBrowser.currentURI.spec == "about:blank")) { |
|
// This will trigger clearing the location bar. Don't do it if |
|
// we loaded off a blank browser and this is an initial page load |
|
// (e.g. about:privatebrowsing, etc.) so we avoid clearing the |
|
// location bar in case the user is typing in it. |
|
// Loading about:blank shouldn't trigger this, either, because its |
|
// loads are "special". |
|
this.mBrowser.urlbarChangeTracker.startedLoad(); |
|
} |
|
// If the browser is loading it must not be crashed anymore. |
|
this.mTab.removeAttribute("crashed"); |
|
} |
|
|
|
if (this._shouldShowProgress(aRequest)) { |
|
if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) { |
|
this.mTab.setAttribute("busy", "true"); |
|
|
|
// Do the following only for the top frame not any subframes. |
|
if (aWebProgress.isTopLevel) { |
|
// Remove favicon. This shows busy and progress indicators even during a reload. |
|
this.mTab.removeAttribute("image"); |
|
|
|
if (!(aWebProgress.loadType & Components.interfaces.nsIDocShell.LOAD_CMD_RELOAD)) |
|
this.mTabBrowser.setTabTitleLoading(this.mTab); |
|
} |
|
} |
|
|
|
if (this.mTab.selected) |
|
this.mTabBrowser.mIsBusy = true; |
|
} |
|
} |
|
else if (aStateFlags & nsIWebProgressListener.STATE_STOP && |
|
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { |
|
|
|
if (this.mTab.hasAttribute("busy")) { |
|
this.mTab.removeAttribute("busy"); |
|
this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]); |
|
if (!this.mTab.selected) |
|
this.mTab.setAttribute("unread", "true"); |
|
} |
|
this.mTab.removeAttribute("progress"); |
|
|
|
if (aWebProgress.isTopLevel) { |
|
let isSuccessful = Components.isSuccessCode(aStatus); |
|
if (!isSuccessful && !isTabEmpty(this.mTab)) { |
|
// Restore the current document's location in case the |
|
// request was stopped (possibly from a content script) |
|
// before the location changed. |
|
this.mBrowser.userTypedValue = null; |
|
|
|
let inLoadURI = this.mBrowser.inLoadURI; |
|
if (this.mTab.selected && gURLBar && !inLoadURI) |
|
URLBarSetURI(); |
|
} else if (isSuccessful) { |
|
this.mBrowser.urlbarChangeTracker.finishedLoad(); |
|
} |
|
|
|
if (!this.mBrowser.mIconURL) |
|
this.mTabBrowser.useDefaultIcon(this.mTab); |
|
} |
|
|
|
if (this.mBlank) |
|
this.mBlank = false; |
|
|
|
// For keyword URIs clear the user typed value since they will be changed into real URIs. |
|
if (location.scheme == "keyword") |
|
this.mBrowser.userTypedValue = null; |
|
|
|
if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading")) |
|
this.mTabBrowser.setTabTitle(this.mTab); |
|
|
|
if (this.mTab.selected) |
|
this.mTabBrowser.mIsBusy = false; |
|
} |
|
|
|
if (oldBlank) { |
|
this._callProgressListeners("onUpdateCurrentBrowser", |
|
[aStateFlags, aStatus, "", 0], |
|
true, false); |
|
} else { |
|
this._callProgressListeners("onStateChange", |
|
[aWebProgress, aRequest, aStateFlags, aStatus], |
|
true, false); |
|
} |
|
|
|
this._callProgressListeners("onStateChange", |
|
[aWebProgress, aRequest, aStateFlags, aStatus], |
|
false); |
|
|
|
if (aStateFlags & (nsIWebProgressListener.STATE_START | |
|
nsIWebProgressListener.STATE_STOP)) { |
|
// reset cached temporary values at beginning and end |
|
this.mMessage = ""; |
|
this.mTotalProgress = 0; |
|
} |
|
this.mStateFlags = aStateFlags; |
|
this.mStatus = aStatus; |
|
}, |
|
|
|
onLocationChange: function (aWebProgress, aRequest, aLocation, aFlags) { |
|
// OnLocationChange is called for both the top-level content |
|
// and the subframes. |
|
let topLevel = aWebProgress.isTopLevel; |
|
|
|
if (topLevel) { |
|
let isSameDocument = |
|
!!(aFlags & Components.interfaces.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT); |
|
// We need to clear the typed value |
|
// if the document failed to load, to make sure the urlbar reflects the |
|
// failed URI (particularly for SSL errors). However, don't clear the value |
|
// if the error page's URI is about:blank, because that causes complete |
|
// loss of urlbar contents for invalid URI errors (see bug 867957). |
|
// Another reason to clear the userTypedValue is if this was an anchor |
|
// navigation initiated by the user. |
|
if (this.mBrowser.didStartLoadSinceLastUserTyping() || |
|
((aFlags & Components.interfaces.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) && |
|
aLocation.spec != "about:blank") || |
|
(isSameDocument && this.mBrowser.inLoadURI)) |
|
this.mBrowser.userTypedValue = null; |
|
|
|
// Don't clear the favicon if this onLocationChange was |
|
// triggered by a pushState or a replaceState (bug 550565) or |
|
// a hash change (bug 408415). |
|
if (aWebProgress.isLoadingDocument && !isSameDocument) |
|
this.mBrowser.mIconURL = null; |
|
} |
|
|
|
if (!this.mBlank) |
|
this._callProgressListeners("onLocationChange", |
|
[aWebProgress, aRequest, aLocation, |
|
aFlags]); |
|
|
|
if (topLevel) { |
|
this.mBrowser.lastURI = aLocation; |
|
this.mBrowser.lastLocationChange = Date.now(); |
|
} |
|
}, |
|
|
|
onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) { |
|
if (this.mBlank) |
|
return; |
|
|
|
this.mMessage = aMessage; |
|
|
|
this.mTabBrowser._callProgressListeners(this.mBrowser, "onStatusChange", |
|
[aWebProgress, aRequest, aStatus, aMessage]); |
|
}, |
|
|
|
onSecurityChange: function (aWebProgress, aRequest, aState) { |
|
this.mTabBrowser._callProgressListeners(this.mBrowser, "onSecurityChange", |
|
[aWebProgress, aRequest, aState]); |
|
}, |
|
|
|
onRefreshAttempted: function(aWebProgress, aURI, aDelay, aSameURI) |
|
{ |
|
var allowRefresh = true; |
|
if (this.mTabBrowser.mCurrentTab == this.mTab) { |
|
this.mTabBrowser.mProgressListeners.forEach( |
|
function notifyRefreshAttempted(element) { |
|
if (element && "onRefreshAttempted" in element) { |
|
try { |
|
if (!element.onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI)) |
|
allowRefresh = false; |
|
} catch (e) { |
|
Components.utils.reportError(e); |
|
} |
|
} |
|
} |
|
); |
|
} |
|
|
|
this.mTabBrowser.mTabsProgressListeners.forEach( |
|
function notifyRefreshAttempted(element) { |
|
if (element && "onRefreshAttempted" in element) { |
|
try { |
|
if (!element.onRefreshAttempted(this.mBrowser, aWebProgress, aURI, aDelay, aSameURI)) |
|
allowRefresh = false; |
|
} catch (e) { |
|
Components.utils.reportError(e); |
|
} |
|
} |
|
} |
|
, this); |
|
return allowRefresh; |
|
}, |
|
|
|
addFeed: function(aLink) |
|
{ |
|
this.mFeeds.push(aLink); |
|
}, |
|
|
|
QueryInterface: function(aIID) { |
|
if (aIID.equals(Components.interfaces.nsIWebProgressListener) || |
|
aIID.equals(Components.interfaces.nsIWebProgressListener2) || |
|
aIID.equals(Components.interfaces.nsISupportsWeakReference) || |
|
aIID.equals(Components.interfaces.nsISupports)) |
|
return this; |
|
throw Components.results.NS_NOINTERFACE; |
|
} |
|
}); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="mInstallSH"> |
|
<parameter name="aBrowser"/> |
|
<parameter name="aSH"/> |
|
<body> |
|
<![CDATA[ |
|
return ({ |
|
mBrowser: aBrowser, |
|
mSH: aSH, |
|
|
|
onProgressChange : function (aWebProgress, aRequest, |
|
aCurSelfProgress, aMaxSelfProgress, |
|
aCurTotalProgress, aMaxTotalProgress) |
|
{ |
|
}, |
|
|
|
onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) |
|
{ |
|
const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; |
|
if ((aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) && |
|
(aStateFlags & nsIWebProgressListener.STATE_STOP)) { |
|
function refresh(closure) { |
|
closure.mBrowser.webNavigation.sessionHistory = closure.mSH; |
|
closure.mBrowser.webProgress.removeProgressListener(closure); |
|
delete closure.mBrowser._SHListener; |
|
closure.mSH.QueryInterface(Components.interfaces.nsIWebNavigation) |
|
.gotoIndex(closure.mSH.index); |
|
} |
|
setTimeout(refresh, 0, this); |
|
} |
|
}, |
|
|
|
onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags) |
|
{ |
|
}, |
|
|
|
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) |
|
{ |
|
}, |
|
|
|
onSecurityChange : function(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="setIcon"> |
|
<parameter name="aTab"/> |
|
<parameter name="aURI"/> |
|
<parameter name="aLoadingPrincipal"/> |
|
<body> |
|
<![CDATA[ |
|
let browser = this.getBrowserForTab(aTab); |
|
browser.mIconURL = aURI instanceof Components.interfaces.nsIURI ? aURI.spec : aURI; |
|
|
|
if (aURI && this.mFaviconService) { |
|
if (!(aURI instanceof Components.interfaces.nsIURI)) |
|
aURI = makeURI(aURI); |
|
|
|
// We do not serialize the principal from within nsSessionStore.js, |
|
// hence if aLoadingPrincipal is null we default to the |
|
// systemPrincipal which will allow the favicon to load. |
|
let loadingPrincipal = aLoadingPrincipal |
|
? aLoadingPrincipal |
|
: Services.scriptSecurityManager.getSystemPrincipal(); |
|
let loadType = PrivateBrowsingUtils.isWindowPrivate(window) |
|
? this.mFaviconService.FAVICON_LOAD_PRIVATE |
|
: this.mFaviconService.FAVICON_LOAD_NON_PRIVATE; |
|
|
|
this.mFaviconService.setAndFetchFaviconForPage( |
|
browser.currentURI, aURI, false, loadType, null, loadingPrincipal); |
|
} |
|
|
|
let sizedIconUrl = browser.mIconURL || ""; |
|
if (sizedIconUrl != aTab.getAttribute("image")) { |
|
if (sizedIconUrl) |
|
aTab.setAttribute("image", sizedIconUrl); |
|
else |
|
aTab.removeAttribute("image"); |
|
this._tabAttrModified(aTab, ["image"]); |
|
} |
|
|
|
this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getIcon"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
let browser = aTab ? aTab.linkedBrowser : this.selectedBrowser; |
|
return browser.mIconURL; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="buildFavIconString"> |
|
<parameter name="aURI"/> |
|
<body> |
|
<![CDATA[ |
|
try { |
|
aURI = this.mURIFixup.createExposableURI(aURI); |
|
} catch (e) { |
|
} |
|
return aURI.resolve("/favicon.ico"); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="shouldLoadFavIcon"> |
|
<parameter name="aURI"/> |
|
<body> |
|
<![CDATA[ |
|
try { |
|
aURI = this.mURIFixup.createExposableURI(aURI); |
|
} catch (e) { |
|
} |
|
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="useDefaultIcon"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
var browser = this.getBrowserForTab(aTab); |
|
var documentURI = browser.documentURI; |
|
var icon = null; |
|
|
|
if (browser.imageDocument) { |
|
if (Services.prefs.getBoolPref("browser.chrome.site_icons")) { |
|
let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size"); |
|
if (browser.imageDocument.width <= sz && |
|
browser.imageDocument.height <= sz) { |
|
icon = browser.currentURI; |
|
} |
|
} |
|
} |
|
|
|
// Use documentURIObject in the check for shouldLoadFavIcon so that we |
|
// do the right thing with about:-style error pages. Bug 453442 |
|
if (!icon && this.shouldLoadFavIcon(documentURI)) { |
|
let url = documentURI.prePath + "/favicon.ico"; |
|
if (!this.isFailedIcon(url)) |
|
icon = url; |
|
} |
|
this.setIcon(aTab, icon, browser.contentPrincipal); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="isFailedIcon"> |
|
<parameter name="aURI"/> |
|
<body> |
|
<![CDATA[ |
|
if (this.mFaviconService) { |
|
if (!(aURI instanceof Components.interfaces.nsIURI)) |
|
aURI = makeURI(aURI); |
|
return this.mFaviconService.isFailedFavicon(aURI); |
|
} |
|
return null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="loadFavIcon"> |
|
<parameter name="aURI"/> |
|
<parameter name="aAttr"/> |
|
<parameter name="aElt"/> |
|
<parameter name="aLoadingPrincipal"/> |
|
<body> |
|
<![CDATA[ |
|
var iconURL = this.buildFavIconString(aURI); |
|
if (this.mFaviconService) { |
|
var iconURI = this.mIOService.newURI(iconURL, null, null); |
|
var faviconFlags = this.usePrivateBrowsing ? |
|
this.mFaviconService.FAVICON_LOAD_PRIVATE : |
|
this.mFaviconService.FAVICON_LOAD_NON_PRIVATE; |
|
var loadingPrincipal = aLoadingPrincipal || |
|
this.mSecMan.getSystemPrincipal(); |
|
|
|
this.mFaviconService |
|
.setAndFetchFaviconForPage(aURI, iconURI, false, |
|
faviconFlags, null, |
|
loadingPrincipal); |
|
if (this.mFaviconService.isFailedFavicon(aURI)) |
|
return; |
|
} |
|
aElt.setAttribute(aAttr, iconURL); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="addToMissedIconCache"> |
|
<parameter name="aURI"/> |
|
<body> |
|
<![CDATA[ |
|
if (this.mFaviconService) { |
|
let uri = this.mIOService.newURI(aURI, null, null); |
|
this.mFaviconService.addFailedFavicon(uri); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getTitleForURI"> |
|
<parameter name="aURI"/> |
|
<body> |
|
<![CDATA[ |
|
try { |
|
aURI = this.mURIFixup.createExposableURI(aURI).spec; |
|
} catch (e) { |
|
aURI = aURI.spec; |
|
} |
|
|
|
if (aURI == "about:blank") |
|
return "Blank Page"; |
|
|
|
// We have a URI. Let's try to unescape it using a character set |
|
// in case the URI is not ASCII. |
|
try { |
|
let characterSet = this.mCurrentBrowser.contentDocument.characterSet; |
|
let textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"] |
|
.getService(Components.interfaces.nsITextToSubURI); |
|
aURI = textToSubURI.unEscapeNonAsciiURI(characterSet, aURI); |
|
} catch (e) { |
|
// Do nothing. |
|
} |
|
return aURI; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="updateUrlBar"> |
|
<parameter name="aWebProgress"/> |
|
<parameter name="aRequest"/> |
|
<parameter name="aLocation"/> |
|
<parameter name="aFlags"/> |
|
<parameter name="aSecurityUI"/> |
|
<parameter name="aBrowser"/> |
|
<parameter name="aFeeds"/> |
|
<body> |
|
<![CDATA[ |
|
this.mProgressListeners.forEach( |
|
function notifyUrlBar(element) { |
|
try { |
|
if ("onLocationChange" in element) |
|
element.onLocationChange(aWebProgress, aRequest, aLocation, aFlags); |
|
// If switching tabs, the security may have changed. |
|
if (aSecurityUI && "onSecurityChange" in element) |
|
element.onSecurityChange(aWebProgress, null, aSecurityUI.state); |
|
// If the document already exists, just resend cached data. |
|
if (!aRequest && aWebProgress.isTopLevel) { |
|
if (aBrowser.mIconURL && "onLinkIconAvailable" in element) |
|
element.onLinkIconAvailable(aBrowser.mIconURL); |
|
if ("onFeedAvailable" in element) { |
|
aFeeds.forEach( |
|
function notifyFeedAvailable(feed) { |
|
element.onFeedAvailable(feed); |
|
} |
|
); |
|
} |
|
} |
|
} catch (e) { |
|
Components.utils.reportError(e); |
|
} |
|
} |
|
); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getWindowTitleForBrowser"> |
|
<parameter name="aBrowser"/> |
|
<body> |
|
<![CDATA[ |
|
var newTitle = ""; |
|
var docTitle; |
|
var docElement = this.ownerDocument.documentElement; |
|
var sep = docElement.getAttribute("titlemenuseparator"); |
|
var modifier = docElement.getAttribute("titlemodifier"); |
|
|
|
// Strip out any null bytes in the content title, since the |
|
// underlying widget implementations of nsWindow::SetTitle pass |
|
// null-terminated strings to system APIs. |
|
if (aBrowser.docShell.contentViewer) |
|
docTitle = aBrowser.contentTitle.replace(/\0/g, ""); |
|
|
|
if (!docTitle && !modifier) { |
|
docTitle = this.getTitleForURI(aBrowser.currentURI); |
|
if (!docTitle) { |
|
// Here we actually override contenttitlesetting, because we |
|
// don't want the titledefault value. |
|
docTitle = this.mStringBundle.getString("tabs.untitled"); |
|
} |
|
} |
|
|
|
if (docTitle) { |
|
newTitle += docElement.getAttribute("titlepreface") + docTitle; |
|
if (modifier) |
|
newTitle += sep; |
|
} |
|
newTitle += modifier; |
|
|
|
// If location bar is hidden and the URL type supports a host, |
|
// add the scheme and host to the title to prevent spoofing. |
|
// XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239 |
|
// (only for schemes that support a host) |
|
try { |
|
if (docElement.getAttribute("chromehidden").indexOf("location") != -1) { |
|
var uri = this.mURIFixup.createExposableURI(aBrowser.currentURI); |
|
if (uri.schemeIs("about")) |
|
newTitle = uri.spec + sep + newTitle; |
|
else if (uri.host) |
|
newTitle = uri.prePath + sep + newTitle; |
|
} |
|
} catch (e) { |
|
} |
|
|
|
return newTitle; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="updateTitlebar"> |
|
<body> |
|
<![CDATA[ |
|
var newTitle = this.getWindowTitleForBrowser(this.mCurrentBrowser); |
|
document.title = newTitle; |
|
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
|
.getInterface(Components.interfaces.nsIWebNavigation) |
|
.QueryInterface(Components.interfaces.nsIBaseWindow).title = newTitle; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="updatePopupMenu"> |
|
<parameter name="aPopupMenu"/> |
|
<body> |
|
<![CDATA[ |
|
this.mContextTab = aPopupMenu.triggerNode; |
|
// The user might right-click on a tab or an empty part of the tabbar. |
|
var isTab = this.mContextTab.localName == "tab"; |
|
var isMultiple = this.tabs.length > 1; |
|
var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "*"); |
|
for (let menuitem of menuItems) { |
|
let tbattr = menuitem.getAttribute("tbattr"); |
|
|
|
if (tbattr.includes("tabbrowser-undoclosetab")) { |
|
menuitem.setAttribute("disabled", (this.usePrivateBrowsing ? |
|
this.savedBrowsers.length : |
|
this.mSessionStore.getClosedTabCount(window)) == 0); |
|
menuitem.hidden = (this.usePrivateBrowsing || |
|
this.mPrefs.getIntPref("browser.sessionstore.max_tabs_undo") <= 0) && |
|
this.mPrefs.getIntPref("browser.tabs.max_tabs_undo") <= 0; |
|
} |
|
else |
|
menuitem.setAttribute("disabled", tbattr.includes("tabbrowser-multiple") && !isMultiple || |
|
tbattr.includes("tabbrowser-tab") && !isTab) |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<field name="mAeroPeek">false</field> |
|
|
|
<method name="updateCurrentBrowser"> |
|
<body> |
|
<![CDATA[ |
|
var newBrowser = this.mPanelContainer.selectedPanel.firstChild; |
|
var oldBrowser = this.mCurrentBrowser; |
|
|
|
// Transfer the dropped link handler to the new browser. |
|
// Note: closing the current tab sets mCurrentBrowser to null |
|
// so we use mCurrentTab.linkedBrowser instead. |
|
newBrowser.droppedLinkHandler = this.mCurrentTab.linkedBrowser.droppedLinkHandler; |
|
newBrowser.showWindowResizer = this.mCurrentTab.linkedBrowser.showWindowResizer; |
|
newBrowser.docShellIsActive = this.mCurrentTab.linkedBrowser.docShellIsActive; |
|
if (this.mCurrentBrowser) { |
|
this.mCurrentBrowser.droppedLinkHandler = null; |
|
this.mCurrentBrowser.docShellIsActive = false; |
|
this.mCurrentBrowser.setAttribute("type", "content-targetable"); |
|
this.finder.mListeners.forEach(l => this.mCurrentBrowser.finder.removeResultListener(l)); |
|
} |
|
|
|
newBrowser.setAttribute("type", "content-primary"); |
|
this.mCurrentBrowser = newBrowser; |
|
this.mCurrentTab = this.selectedTab; |
|
this.mCurrentTab.removeAttribute("unread"); |
|
this.finder.mListeners.forEach(l => this.mCurrentBrowser.finder.addResultListener(l)); |
|
|
|
var tabListener = this.mTabListeners[this.tabContainer.selectedIndex]; |
|
|
|
if (!oldBrowser || |
|
(!oldBrowser.blockedPopups != !newBrowser.blockedPopups)) |
|
this.mCurrentBrowser.updateBlockedPopups(); |
|
|
|
// Update the URL bar. |
|
const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; |
|
this.updateUrlBar(newBrowser.webProgress, |
|
null, |
|
newBrowser.currentURI, |
|
0, |
|
newBrowser.securityUI, |
|
newBrowser, |
|
tabListener.mFeeds); |
|
|
|
// Send the state, status and progress to all progress listeners. |
|
var flags = tabListener.mStateFlags & |
|
(nsIWebProgressListener.STATE_START | |
|
nsIWebProgressListener.STATE_STOP); |
|
this._callProgressListeners(null, "onStateChange", |
|
[this.mCurrentBrowser.webProgress, |
|
tabListener.mRequest, |
|
flags, |
|
tabListener.mStatus], |
|
true, false); |
|
|
|
this._callProgressListeners(null, "onStatusChange", |
|
[this.mCurrentBrowser.webProgress, |
|
tabListener.mRequest, |
|
tabListener.mStatus, |
|
tabListener.mMessage], |
|
true, false); |
|
|
|
this._callProgressListeners(null, "onProgressChange", |
|
[this.mCurrentBrowser.webProgress, |
|
tabListener.mRequest, |
|
tabListener.mCurProgress, |
|
tabListener.mMaxProgress, |
|
tabListener.mCurProgress, |
|
tabListener.mMaxProgress], |
|
true, false); |
|
|
|
// Also send the onUpdateCurrentBrowser event for compatibility |
|
var progress = tabListener.mMaxProgress > 0 ? |
|
tabListener.mCurProgress / tabListener.mMaxProgress : 0; |
|
this._callProgressListeners(null, "onUpdateCurrentBrowser", |
|
[tabListener.mStateFlags, |
|
tabListener.mStatus, |
|
tabListener.mMessage, |
|
progress], |
|
true, false); |
|
|
|
if (this.mAeroPeek) |
|
return; |
|
|
|
// we only want to return to the parent tab if no other |
|
// tabs have been opened and the user hasn't switched tabs |
|
this.mPreviousTab = null; |
|
this.mLastRelatedIndex = 0; |
|
|
|
// Update the window title. |
|
this.updateTitlebar(); |
|
|
|
// FAYT |
|
this.fastFind.setDocShell(this.mCurrentBrowser.docShell); |
|
|
|
// We've selected the new tab, so go ahead and notify listeners |
|
this.mCurrentTab.dispatchEvent(new Event("TabSelect", |
|
{ bubbles: true, cancelable: false })); |
|
|
|
if (!document.commandDispatcher.focusedElement || |
|
document.commandDispatcher.focusedElement.parentNode != |
|
this.mCurrentTab.parentNode) { |
|
// The focus was not on one of our tabs, so focus the new browser. |
|
newBrowser.focus(); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="onTabClick"> |
|
<parameter name="event"/> |
|
<body> |
|
<![CDATA[ |
|
if (event.button != 1 || event.target.localName != 'tab' || |
|
(this.mPrefs.getBoolPref("middlemouse.contentLoadURL") && |
|
!this.mPrefs.getBoolPref("general.autoScroll"))) |
|
return; |
|
|
|
this.removeTab(event.target); |
|
event.stopPropagation(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="onLinkEvent"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
var link = aEvent.originalTarget; |
|
var href = link.href; |
|
if (!href) |
|
return; |
|
|
|
var targetDoc = link.ownerDocument; |
|
var index = this.getBrowserIndexForDocument(targetDoc); |
|
if (index < 0) |
|
return; |
|
|
|
var rel = link.rel; |
|
var type = link.type; |
|
var isIcon = /(?:^|\s)icon(?:\s|$)/i.test(rel) && |
|
this.mPrefs.getBoolPref("browser.chrome.site_icons"); |
|
if (isIcon) { |
|
var iconUri = this.getLinkIconURI(link); |
|
if (iconUri) |
|
this.setIcon(this.tabs[index], iconUri, |
|
link.nodePrincipal); |
|
return; |
|
} |
|
|
|
if (aEvent.type == "DOMLinkChanged") |
|
return; |
|
|
|
var isFeed = /(?:^|\s)feed(?:\s|$)/i.test(rel) || |
|
(/(?:^|\s)alternate(?:\s|$)/i.test(rel) && |
|
!/(?:^|\s)stylesheet(?:\s|$)/i.test(rel) && |
|
/^\s*application\/(?:atom|rss)\+xml\s*$/i.test(type)); |
|
|
|
if (!isFeed) |
|
return; |
|
|
|
try { |
|
var feedUri = this.mIOService.newURI(href, targetDoc.characterSet, null); |
|
this.mSecMan.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, feedUri, |
|
this.mSecMan.DISALLOW_INHERIT_PRINCIPAL); |
|
} catch(e) { |
|
return; |
|
} |
|
|
|
this.mTabListeners[index].addFeed(link); |
|
if (this.browsers[index] == this.mCurrentBrowser) { |
|
this.mProgressListeners.forEach( |
|
function notifyFeedAvailable(element) { |
|
if ("onFeedAvailable" in element) { |
|
try { |
|
element.onFeedAvailable(link); |
|
} catch (e) { |
|
Components.utils.reportError(e); |
|
} |
|
} |
|
} |
|
); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getLinkIconURI"> |
|
<parameter name="aLink"/> |
|
<body><![CDATA[ |
|
var targetDoc = aLink.ownerDocument; |
|
// Make a URI out of our href. |
|
var uri = this.mIOService.newURI(aLink.href, targetDoc.characterSet, null); |
|
|
|
// Verify that the load of this icon is legal. |
|
// Some error or special pages can load their favicon. |
|
// To be on the safe side, only allow chrome:// favicons. |
|
const re = /^about:(neterror|certerror|blocked)\?/; |
|
var isAllowedPage = re.test(targetDoc.documentURI); |
|
|
|
if (!isAllowedPage || !uri.schemeIs("chrome")) { |
|
try { |
|
this.mSecMan.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri, |
|
this.mSecMan.DISALLOW_SCRIPT); |
|
} catch(e) { |
|
return null; |
|
} |
|
} |
|
|
|
// Security says okay, now ask content policy |
|
const nsIContentPolicy = Components.interfaces.nsIContentPolicy; |
|
try { |
|
var contentPolicy = |
|
Components.classes['@mozilla.org/layout/content-policy;1'] |
|
.getService(nsIContentPolicy); |
|
} catch (e) { |
|
return null; // Refuse to load if we can't do a security check. |
|
} |
|
|
|
if (contentPolicy.shouldLoad(nsIContentPolicy.TYPE_INTERNAL_IMAGE, |
|
uri, targetDoc.documentURIObject, |
|
aLink, aLink.type, |
|
null) != nsIContentPolicy.ACCEPT) { |
|
return null; |
|
} |
|
return uri; |
|
]]></body> |
|
</method> |
|
|
|
<method name="_tabAttrModified"> |
|
<parameter name="aTab"/> |
|
<parameter name="aChanged"/> |
|
<body><![CDATA[ |
|
if (aTab.closing) |
|
return; |
|
|
|
let event = new CustomEvent("TabAttrModified", { |
|
bubbles: true, |
|
cancelable: false, |
|
detail: { |
|
changed: aChanged, |
|
} |
|
}); |
|
aTab.dispatchEvent(event); |
|
]]></body> |
|
</method> |
|
|
|
<method name="setTabTitleLoading"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
aTab.label = this.mStringBundle.getString("tabs.loading"); |
|
aTab.crop = "end"; |
|
this._tabAttrModified(aTab, ["label", "crop"]); |
|
]]> |
|
</body> |
|
</method> |
|
<method name="onTitleChanged"> |
|
<parameter name="evt"/> |
|
<body> |
|
<![CDATA[ |
|
if (evt.target != this.contentDocument) |
|
return; |
|
|
|
var tabBrowser = this.parentNode.parentNode.parentNode.parentNode; |
|
var tab = document.getAnonymousElementByAttribute(tabBrowser, "linkedpanel", this.parentNode.id); |
|
|
|
tabBrowser.setTabTitle(tab); |
|
|
|
if (tab == tabBrowser.mCurrentTab) |
|
tabBrowser.updateTitlebar(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="setTabTitle"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
var browser = aTab.linkedBrowser; |
|
var title = browser.contentTitle; |
|
var crop = "end"; |
|
|
|
if (!title) { |
|
title = this.getTitleForURI(browser.currentURI); |
|
|
|
if (title) |
|
crop = "center"; |
|
else |
|
title = this.mStringBundle.getString("tabs.untitled"); |
|
} |
|
aTab.label = title; |
|
aTab.crop = crop; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="setStripVisibilityTo"> |
|
<parameter name="aShow"/> |
|
<body> |
|
<![CDATA[ |
|
this.mStrip.collapsed = !aShow; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getStripVisibility"> |
|
<body> |
|
return !this.mStrip.collapsed; |
|
</body> |
|
</method> |
|
|
|
<method name="loadOneTab"> |
|
<parameter name="aURI"/> |
|
<parameter name="aReferrerURI"/> |
|
<parameter name="aCharset"/> |
|
<parameter name="aPostData"/> |
|
<parameter name="aLoadInBackground"/> |
|
<parameter name="aAllowThirdPartyFixup"/> |
|
<body> |
|
<![CDATA[ |
|
var params = aReferrerURI; |
|
if (!params || params instanceof Components.interfaces.nsIURI) { |
|
params = { |
|
referrerURI: aReferrerURI, |
|
charset: aCharset, |
|
postData: aPostData, |
|
inBackground: aLoadInBackground, |
|
allowThirdPartyFixup: aAllowThirdPartyFixup, |
|
userContextId: null, |
|
opener: null, |
|
}; |
|
} |
|
|
|
params.focusNewTab = params.inBackground != null ? |
|
!params.inBackground : |
|
!this.mPrefs.getBoolPref("browser.tabs.loadInBackground"); |
|
|
|
return this.addTab(aURI, params); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="loadTabs"> |
|
<parameter name="aURIs"/> |
|
<parameter name="aLoadInBackground"/> |
|
<parameter name="aReplace"/> |
|
<body><![CDATA[ |
|
let aAllowThirdPartyFixup; |
|
let aPostDatas = []; |
|
let aUserContextId; |
|
|
|
// Additional parameters are in a params object. |
|
// Firefox uses additional parameters not supported here. |
|
if (arguments.length == 2 && |
|
typeof arguments[1] == "object") { |
|
let params = arguments[1]; |
|
aLoadInBackground = params.inBackground; |
|
aReplace = params.replace; |
|
aAllowThirdPartyFixup = params.allowThirdPartyFixup; |
|
aPostDatas = params.postDatas || aPostDatas; |
|
aUserContextId = params.userContextId; |
|
} |
|
|
|
if (!aURIs.length) |
|
return; |
|
|
|
// The tab selected after this new tab is closed (i.e. the new tab's |
|
// "owner") is the next adjacent tab (i.e. not the previously viewed tab) |
|
// when several urls are opened here (i.e. closing the first should select |
|
// the next of many URLs opened) or if the pref to have UI links opened in |
|
// the background is set (i.e. the link is not being opened modally) |
|
// |
|
// i.e. |
|
// Number of URLs Load UI Links in BG Focus Last Viewed? |
|
// == 1 false YES |
|
// == 1 true NO |
|
// > 1 false/true NO |
|
var multiple = aURIs.length > 1; |
|
var owner = multiple || aLoadInBackground ? null : this.selectedTab; |
|
var firstTabAdded = null; |
|
var targetTabIndex = -1; |
|
|
|
if (aReplace) { |
|
let browser; |
|
browser = this.mCurrentBrowser; |
|
targetTabIndex = this.tabContainer.selectedIndex; |
|
|
|
let flags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE; |
|
if (aAllowThirdPartyFixup) { |
|
flags |= Components.interfaces.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | |
|
Components.interfaces.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; |
|
} |
|
try { |
|
browser.loadURIWithFlags(aURIs[0], { |
|
flags, postData: aPostDatas[0] |
|
}); |
|
} catch (e) { |
|
// Ignore failure in case a URI is wrong, so we can continue |
|
// opening the next ones. |
|
} |
|
} else { |
|
firstTabAdded = this.addTab(aURIs[0], { |
|
ownerTab: owner, |
|
allowThirdPartyFixup: aAllowThirdPartyFixup, |
|
postData: aPostDatas[0], |
|
userContextId: aUserContextId |
|
}); |
|
} |
|
|
|
let tabNum = targetTabIndex; |
|
for (let i = 1; i < aURIs.length; ++i) { |
|
let tab = this.addTab(aURIs[i], { |
|
allowThirdPartyFixup: aAllowThirdPartyFixup, |
|
postData: aPostDatas[i], |
|
userContextId: aUserContextId |
|
}); |
|
if (targetTabIndex !== -1) |
|
this.moveTabTo(tab, ++tabNum); |
|
} |
|
|
|
if (!aLoadInBackground) { |
|
if (firstTabAdded) { |
|
// .selectedTab setter focuses the content area |
|
this.selectedTab = firstTabAdded; |
|
} else |
|
this.selectedBrowser.focus(); |
|
} |
|
]]></body> |
|
</method> |
|
|
|
<method name="addTab"> |
|
<parameter name="aURI"/> |
|
<parameter name="aReferrerURI"/> |
|
<parameter name="aCharset"/> |
|
<parameter name="aPostData"/> |
|
<parameter name="aFocusNewTab"/> |
|
<parameter name="aAllowThirdPartyFixup"/> |
|
<body> |
|
<![CDATA[ |
|
var aFromExternal; |
|
var aRelatedToCurrent; |
|
var aUserContextId; |
|
var aOpener; |
|
if (arguments.length == 2 && |
|
arguments[1] != null && |
|
typeof arguments[1] == "object" && |
|
!(arguments[1] instanceof Components.interfaces.nsIURI)) { |
|
let params = arguments[1]; |
|
aReferrerURI = params.referrerURI; |
|
aCharset = params.charset; |
|
aPostData = params.postData; |
|
aFocusNewTab = params.focusNewTab; |
|
aAllowThirdPartyFixup = params.allowThirdPartyFixup; |
|
aFromExternal = params.fromExternal; |
|
aRelatedToCurrent = params.relatedToCurrent; |
|
aUserContextId = params.userContextId; |
|
aOpener = params.opener; |
|
} |
|
|
|
this._browsers = null; // invalidate cache |
|
|
|
var t = this.referenceTab.cloneNode(true); |
|
|
|
var blank = !aURI || aURI == "about:blank"; |
|
|
|
if (!blank) |
|
t.setAttribute("label", aURI); |
|
|
|
this.tabContainer.appendChild(t); |
|
|
|
var b = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
"browser"); |
|
b.setAttribute("type", "content-targetable"); |
|
b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu")); |
|
b.setAttribute("tooltip", this.getAttribute("contenttooltip")); |
|
b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup")); |
|
b.setAttribute("flex", "1"); |
|
|
|
// Check if we have a "parent" window which we need to set as our opener |
|
if (aOpener) { |
|
b.presetOpenerWindow(aOpener); |
|
} |
|
|
|
// Add the Message and the Browser to the box |
|
var n = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", |
|
"notificationbox"); |
|
n.setAttribute("class", "browser-notificationbox"); |
|
n.setAttribute("popupnotification", this.getAttribute("popupnotification")); |
|
n.appendChild(b); |
|
|
|
var uniqueId = "panel" + this.nextTabNumber++; |
|
n.id = uniqueId; |
|
t.linkedPanel = uniqueId; |
|
t.linkedBrowser = b; |
|
if (t.previousSibling.selected) |
|
t.setAttribute("afterselected", true); |
|
|
|
// Prevent the superfluous initial load of a blank document |
|
// if we're going to load something other than about:blank. |
|
if (!blank) |
|
b.setAttribute("nodefaultsrc", "true"); |
|
|
|
// NB: this appendChild call causes us to run constructors for the |
|
// browser element, which fires off a bunch of notifications. Some |
|
// of those notifications can cause code to run that inspects our |
|
// state, so it is important that the tab element is fully |
|
// initialized by this point. |
|
this.mPanelContainer.appendChild(n); |
|
|
|
b.addEventListener("DOMTitleChanged", this.onTitleChanged, true); |
|
|
|
// We start our browsers out as inactive. |
|
b.docShellIsActive = false; |
|
|
|
this.mStrip.collapsed = false; |
|
|
|
this.mPrefs.setBoolPref("browser.tabs.forceHide", false); |
|
|
|
// wire up a progress listener for the new browser object. |
|
var position = this.tabs.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; |
|
|
|
let nsIWebNavigation = Components.interfaces.nsIWebNavigation; |
|
let flags = nsIWebNavigation.LOAD_FLAGS_NONE; |
|
if (aAllowThirdPartyFixup) |
|
flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | |
|
nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; |
|
if (aFromExternal) |
|
flags |= nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL; |
|
try { |
|
b.loadURIWithFlags(aURI, flags, aReferrerURI, aCharset, aPostData); |
|
} |
|
catch (ex) { } |
|
} |
|
|
|
t.dispatchEvent(new Event("TabOpen", |
|
{ bubbles: true, cancelable: false })); |
|
|
|
// Check if we're opening a tab related to the current tab and |
|
// move it to after the current tab. |
|
// aReferrerURI is null or undefined if the tab is opened from |
|
// an external application or bookmark, i.e. somewhere other |
|
// than the current tab. |
|
if ((aRelatedToCurrent || aReferrerURI) && |
|
this.mPrefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) { |
|
var lastRelatedIndex = this.mLastRelatedIndex || |
|
this.tabContainer.selectedIndex; |
|
this.moveTabTo(t, ++lastRelatedIndex); |
|
this.mLastRelatedIndex = lastRelatedIndex; |
|
} |
|
|
|
if (aFocusNewTab) { |
|
var parentTab = this.selectedTab; |
|
this.selectedTab = t; |
|
this.mPreviousTab = parentTab; |
|
} |
|
else { |
|
// The user opened a background tab, so updateCurrentBrowser |
|
// won't be called. Explicitly clear the previous tab. |
|
this.mPreviousTab = null; |
|
} |
|
this.tabContainer._handleNewTab(t); |
|
|
|
return t; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="removeAllTabsBut"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
var numTabs = this.tabs.length; |
|
|
|
if (numTabs > 1) { |
|
const closeOtherTabsPref = "browser.tabs.warnOnCloseOther"; |
|
var shouldPrompt = this.mPrefs.getBoolPref(closeOtherTabsPref); |
|
var reallyClose = true; |
|
|
|
if (shouldPrompt) { |
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] |
|
.getService(Components.interfaces.nsIPromptService); |
|
|
|
//default to true: if it were false, we wouldn't get this far |
|
var warnOnClose = { value:true }; |
|
var bundle = this.mStringBundle; |
|
var tabsToClose = numTabs - 1; //number of tabs to be removed |
|
|
|
var buttonPressed = promptService.confirmEx(window, |
|
bundle.getString('tabs.closeWarningTitle'), |
|
bundle.getFormattedString("tabs.closeWarning", [tabsToClose]), |
|
(promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0) |
|
+ (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1), |
|
bundle.getString('tabs.closeButton'), |
|
null, null, |
|
bundle.getString('tabs.closeWarningPromptMe'), |
|
warnOnClose); |
|
reallyClose = (buttonPressed == 0); |
|
//don't set the pref unless they press OK and it's false |
|
if (reallyClose && !warnOnClose.value) |
|
this.mPrefs.setBoolPref(closeOtherTabsPref, false); |
|
} |
|
|
|
if (reallyClose) { |
|
if (aTab.localName != "tab") |
|
aTab = this.mCurrentTab; |
|
else |
|
this.tabContainer.selectedItem = aTab; |
|
|
|
for (var i = this.tabs.length - 1; i >= 0; --i) { |
|
if (this.tabs[i] != aTab) |
|
this.removeTab(this.tabs[i]); |
|
} |
|
} |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="removeCurrentTab"> |
|
<parameter name="aParams"/> |
|
<body> |
|
<![CDATA[ |
|
return this.removeTab(this.mCurrentTab, aParams); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="isBrowserEmpty"> |
|
<parameter name="aBrowser"/> |
|
<body> |
|
<![CDATA[ |
|
return aBrowser.sessionHistory.count < 2 && |
|
aBrowser.currentURI.spec == "about:blank" && |
|
!aBrowser.contentDocument.body.hasChildNodes(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getUndoList"> |
|
<body> |
|
<![CDATA[ |
|
var tabData = this.usePrivateBrowsing ? this.savedBrowsers : |
|
JSON.parse(this.mSessionStore.getClosedTabData(window)); |
|
return tabData.map(function(aTabData) { return aTabData.title; }); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="undoCloseTab"> |
|
<parameter name="aIndex"/> |
|
<body> |
|
<![CDATA[ |
|
if (this.usePrivateBrowsing) |
|
return this.savedBrowsers.length ? this.restoreTab(aIndex) : null; |
|
|
|
return this.mSessionStore.getClosedTabCount(window) ? |
|
this.mSessionStore.undoCloseTab(window, aIndex) : null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="restoreTab"> |
|
<parameter name="aIndex"/> |
|
<body> |
|
<![CDATA[ |
|
if (aIndex >= this.savedBrowsers.length || aIndex < 0) |
|
return null; |
|
|
|
this._browsers = null; |
|
|
|
var savedData = this.savedBrowsers.splice(aIndex, 1)[0]; |
|
var t = savedData.browserData.tab; |
|
var b = savedData.browserData.browser; |
|
var hist = savedData.browserData.history; |
|
|
|
this.tabContainer.appendChild(t); |
|
if (t.previousSibling.selected) |
|
t.setAttribute("afterselected", true); |
|
|
|
// navigate back to the proper page from the light page |
|
b.stop(); |
|
b.webNavigation.gotoIndex(0); |
|
|
|
// reattach the old history |
|
b.webNavigation.sessionHistory = hist; |
|
|
|
// Hook up the title change listener again |
|
b.addEventListener("DOMTitleChanged", this.onTitleChanged, true); |
|
|
|
// add back the filters, security first (bug 313335) |
|
const nsIWebProgress = Components.interfaces.nsIWebProgress; |
|
var secFlags = nsIWebProgress.NOTIFY_STATE_ALL | nsIWebProgress.NOTIFY_LOCATION | nsIWebProgress.NOTIFY_SECURITY; |
|
b.webProgress.addProgressListener(b.securityUI, secFlags); |
|
|
|
var position = this.tabs.length - 1; |
|
var tabListener = this.mTabProgressListener(t, b, false); |
|
const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"] |
|
.createInstance(nsIWebProgress); |
|
filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL); |
|
b.webProgress.addProgressListener(filter, nsIWebProgress.NOTIFY_ALL); |
|
this.mTabListeners[position] = tabListener; |
|
this.mTabFilters[position] = filter; |
|
|
|
t.dispatchEvent(new Event("TabOpen", |
|
{ bubbles: true, cancelable: false })); |
|
|
|
if (savedData.pos < position) |
|
this.moveTabTo(position, savedData.pos); |
|
|
|
if (this.tabs.length == 2 && this.isBrowserEmpty(this)) |
|
this.removeCurrentTab({ disableUndo: true }); |
|
else { |
|
this.selectedTab = t; |
|
this.mStrip.collapsed = false; |
|
} |
|
this.tabContainer._handleNewTab(t); |
|
|
|
return t; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="removeTab"> |
|
<parameter name="aTab"/> |
|
<parameter name="aParams"/> |
|
<body> |
|
<![CDATA[ |
|
this.mLastRelatedIndex = 0; |
|
|
|
if (aTab.localName != "tab") |
|
aTab = this.mCurrentTab; |
|
|
|
if (!aParams) { |
|
aParams = { |
|
animate: false, |
|
disableUndo: false |
|
}; |
|
} |
|
|
|
var oldBrowser = aTab.linkedBrowser; |
|
|
|
var ds = oldBrowser.docShell; |
|
|
|
if (ds.contentViewer && !ds.contentViewer.permitUnload()) |
|
return; |
|
|
|
// We're committed to closing the tab now. |
|
var l = this.tabs.length; |
|
switch (l) { |
|
case 1: |
|
// add a new blank tab to replace the one we're about to close |
|
// (this ensures that the remaining tab is as good as new) |
|
this.addTab("about:blank"); |
|
l++; |
|
// fall through |
|
case 2: |
|
if (this.mPrefs.getBoolPref("browser.tabs.autoHide")) |
|
this.mStrip.collapsed = true; |
|
} |
|
|
|
// Dispatch a notification. |
|
// We dispatch it before any teardown so that event listeners can |
|
// inspect the tab that's about to close. |
|
aTab.dispatchEvent(new UIEvent("TabClose", |
|
{ bubbles: true, cancelable: false, view: window, |
|
detail: !!aParams.disableUndo })); |
|
var tabData = aTab.tabData || {}; |
|
tabData.pos = this.getTabIndex(aTab); |
|
tabData.panel = oldBrowser.parentNode.id; |
|
tabData.title = oldBrowser.contentDocument.title || |
|
this.getTitleForURI(oldBrowser.currentURI) || |
|
this.mStringBundle.getString("tabs.untitled"); |
|
|
|
var index = this.getTabIndex(aTab); |
|
|
|
// Remove SSL listener |
|
oldBrowser.webProgress.removeProgressListener(oldBrowser.securityUI); |
|
|
|
// Remove the tab's filter and progress listener. |
|
const filter = this.mTabFilters[index]; |
|
oldBrowser.webProgress.removeProgressListener(filter); |
|
filter.removeProgressListener(this.mTabListeners[index]); |
|
this.mTabFilters.splice(index, 1); |
|
this.mTabListeners.splice(index, 1); |
|
|
|
// Remove our title change listener |
|
oldBrowser.removeEventListener("DOMTitleChanged", this.onTitleChanged, true); |
|
|
|
// We are no longer a targetable content area |
|
oldBrowser.setAttribute("type", "content"); |
|
|
|
// Now select the new tab before nuking the old one. |
|
var currentIndex = this.tabContainer.selectedIndex; |
|
|
|
var newIndex = -1; |
|
if (currentIndex > index) |
|
newIndex = currentIndex - 1; |
|
else if (currentIndex < index) |
|
newIndex = currentIndex; |
|
else if (index == l - 1) |
|
newIndex = index - 1; |
|
else |
|
newIndex = index; |
|
|
|
if (oldBrowser == this.mCurrentBrowser) |
|
this.mCurrentBrowser = null; |
|
|
|
// Invalidate browsers cache, as the tab is removed from the |
|
// tab container. |
|
this._browsers = null; |
|
|
|
// Clean up before/afterselected attributes before removing the tab |
|
aTab._selected = false; |
|
aTab.remove(); |
|
|
|
// When the current tab is removed select a new tab |
|
// and fire select events on tabpanels and tabs |
|
if (this.mPreviousTab && (aTab == this.mCurrentTab)) |
|
this.selectedTab = this.mPreviousTab; |
|
else { |
|
this.tabContainer.selectedIndex = newIndex; |
|
|
|
// We need to explicitly clear this, because updateCurrentBrowser |
|
// doesn't get called for a background tab |
|
this.mPreviousTab = null; |
|
} |
|
|
|
// Save the tab for undo. |
|
// Even though we navigate to about:blank, it costs more RAM than |
|
// really closing the tab. The pref controls how far you can undo |
|
var maxUndoDepth = this.mPrefs.getIntPref("browser.tabs.max_tabs_undo"); |
|
var oldSH = oldBrowser.webNavigation.sessionHistory; |
|
var inOnLoad = oldBrowser.docShell.isExecutingOnLoadHandler; |
|
var isPopup = oldBrowser.contentWindow.opener && |
|
!this.mPrefs.getBoolPref("browser.tabs.cache_popups"); |
|
if (maxUndoDepth <= 0 || aParams.disableUndo || inOnLoad || isPopup || this.isBrowserEmpty(oldBrowser)) { |
|
// Undo is disabled/tab is blank. Kill the browser for real. |
|
// Because of the way XBL works (fields just set JS |
|
// properties on the element) and the code we have in place |
|
// to preserve the JS objects for any elements that have |
|
// JS properties set on them, the browser element won't be |
|
// destroyed until the document goes away. So we force a |
|
// cleanup ourselves. |
|
oldBrowser.parentNode.destroy(); |
|
oldBrowser.destroy(); |
|
oldBrowser.parentNode.remove(); |
|
|
|
// Fix up the selected panel in the case the removed |
|
// browser was to the left of the current browser |
|
this.mTabBox.selectedPanel = this.selectedTab.linkedBrowser.parentNode; |
|
return; |
|
} |
|
|
|
// preserve a pointer to the browser for undoing the close |
|
// 1. save a copy of the session history (oldSH) |
|
// 2. hook up a new history |
|
// 3. add the last history entry from the old history the new |
|
// history so we'll be able to go back from about:blank |
|
// 4. load a light URL in the browser, pushing the current page |
|
// into bfcache - allows for saving of JS modifications |
|
// and also saves RAM by allowing bfcache to evict the full page |
|
|
|
tabData.browserData = { |
|
tab: aTab, |
|
browser: oldBrowser, |
|
history: oldSH, |
|
toJSON: function() {} // hides this object from JSON.stringify |
|
}; |
|
this.savedBrowsers.unshift(tabData); |
|
|
|
var newSH = Components.classes["@mozilla.org/browser/shistory;1"] |
|
.createInstance(Components.interfaces.nsISHistoryInternal); |
|
oldBrowser.webNavigation.sessionHistory = newSH; |
|
var entry = oldSH.getEntryAtIndex(oldSH.index, false) |
|
.QueryInterface(Components.interfaces.nsISHEntry) |
|
.clone(); |
|
// don't try to repost data when restoring the tab |
|
entry.postData = null; |
|
newSH.addEntry(entry, true); |
|
|
|
// about:blank is light |
|
oldBrowser.loadURI("about:blank"); |
|
|
|
// remove overflow from the undo stack |
|
if (this.savedBrowsers.length > maxUndoDepth) { |
|
tabData = this.savedBrowsers.pop(); |
|
var deadBrowser = tabData.browserData.browser; |
|
delete tabData.browserData; |
|
deadBrowser.parentNode.destroy(); |
|
deadBrowser.destroy(); |
|
|
|
// The pagehide event that this removal triggers is safe |
|
// because the browser is no longer current at this point |
|
deadBrowser.parentNode.remove(); |
|
this.mTabBox.selectedPanel = this.selectedTab.linkedBrowser.parentNode; |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="forgetSavedBrowser"> |
|
<parameter name="aIndex"/> |
|
<body> |
|
<![CDATA[ |
|
if (aIndex >= this.savedBrowsers.length || aIndex < 0) |
|
return false; |
|
|
|
var tabData = this.savedBrowsers.splice(aIndex, 1)[0]; |
|
var deadBrowser = tabData.browserData.browser; |
|
delete tabData.browserData; |
|
deadBrowser.parentNode.destroy(); |
|
deadBrowser.destroy(); |
|
|
|
// The pagehide event that this removal triggers is safe |
|
// because the browser is no longer current at this point |
|
deadBrowser.parentNode.remove(); |
|
this.mTabBox.selectedPanel = this.selectedTab.linkedBrowser.parentNode; |
|
return true; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="reloadAllTabs"> |
|
<body> |
|
<![CDATA[ |
|
var l = this.tabs.length; |
|
for (var i = 0; i < l; i++) { |
|
try { |
|
this.tabs[i].linkedBrowser.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; |
|
|
|
aTab.linkedBrowser.reload(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="addProgressListener"> |
|
<parameter name="aListener"/> |
|
<body> |
|
<![CDATA[ |
|
if (!aListener) |
|
throw Components.results.NS_ERROR_INVALID_ARG; |
|
|
|
if (this.mProgressListeners.indexOf(aListener) != -1) |
|
throw Components.results.NS_ERROR_FAILURE; |
|
|
|
// push() does not disturb possibly ongoing iterations. |
|
this.mProgressListeners.push(aListener); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="removeProgressListener"> |
|
<parameter name="aListener"/> |
|
<body> |
|
<![CDATA[ |
|
if (this.mProgressListeners.indexOf(aListener) == -1) |
|
throw Components.results.NS_ERROR_FAILURE; |
|
|
|
// Create a new array, not to disturb possibly ongoing iterations. |
|
this.mProgressListeners = |
|
this.mProgressListeners.filter( |
|
function removeListener(element) { |
|
return element != aListener; |
|
} |
|
); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="addTabsProgressListener"> |
|
<parameter name="aListener"/> |
|
<body> |
|
<![CDATA[ |
|
if (!aListener) |
|
throw Components.results.NS_ERROR_INVALID_ARG; |
|
|
|
if (this.mTabsProgressListeners.indexOf(aListener) != -1) |
|
throw Components.results.NS_ERROR_FAILURE; |
|
|
|
// push() does not disturb possibly ongoing iterations. |
|
this.mTabsProgressListeners.push(aListener); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="removeTabsProgressListener"> |
|
<parameter name="aListener"/> |
|
<body> |
|
<![CDATA[ |
|
if (this.mTabsProgressListeners.indexOf(aListener) == -1) |
|
throw Components.results.NS_ERROR_FAILURE; |
|
|
|
// Create a new array, not to disturb possibly ongoing iterations. |
|
this.mTabsProgressListeners = |
|
this.mTabsProgressListeners.filter( |
|
function removeListener(element) { |
|
return element != aListener; |
|
} |
|
); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="_getTabForContentWindow"> |
|
<parameter name="aWindow"/> |
|
<body> |
|
<![CDATA[ |
|
const browsers = this.browsers; |
|
for (var i = 0; i < browsers.length; ++i) |
|
if (browsers[i].contentWindow == aWindow) |
|
return this.tabs[i]; |
|
|
|
return null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getTabForBrowser"> |
|
<parameter name="aBrowser"/> |
|
<body> |
|
<![CDATA[ |
|
for (var tab of this.tabs) |
|
if (tab.linkedBrowser == aBrowser) |
|
return tab; |
|
|
|
return null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getBrowserForTab"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
if (aTab.localName != "tab") |
|
return null; |
|
return aTab.linkedBrowser; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getBrowserForOuterWindowID"> |
|
<parameter name="aID"/> |
|
<body> |
|
<![CDATA[ |
|
for (var browser of this.browsers) |
|
if (browser.outerWindowID == aID) |
|
return browser; |
|
|
|
return null; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getTabIndex"> |
|
<parameter name="aTab"/> |
|
<body> |
|
<![CDATA[ |
|
for (var i = 0; i < this.tabs.length; ++i) |
|
if (this.tabs[i] == aTab) |
|
return i; |
|
|
|
throw Components.results.NS_ERROR_ILLEGAL_VALUE; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="selectTabAtIndex"> |
|
<parameter name="aIndex"/> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
// count backwards for aIndex < 0 |
|
if (aIndex < 0) |
|
aIndex += this.tabs.length; |
|
|
|
if (aIndex >= 0 && |
|
aIndex < this.tabs.length && |
|
aIndex != this.tabContainer.selectedIndex) |
|
this.selectedTab = this.tabs[aIndex]; |
|
|
|
if (aEvent) { |
|
aEvent.preventDefault(); |
|
aEvent.stopPropagation(); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<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" readonly="true"> |
|
<getter> |
|
<![CDATA[ |
|
return this._browsers || |
|
(this._browsers = Array.map(this.tabs, tab => tab.linkedBrowser)); |
|
]]> |
|
</getter> |
|
</property> |
|
|
|
<!-- Drag and drop observer API --> |
|
<method name="_onDragStart"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
var target = aEvent.target; |
|
if (target.localName == "tab") { |
|
var URI = target.linkedBrowser.currentURI; |
|
var spec = URI.spec; |
|
var title = target.linkedBrowser.contentTitle || spec; |
|
var dt = aEvent.dataTransfer; |
|
dt.mozSetDataAt("text/x-moz-url", spec + "\n" + title, 0); |
|
dt.mozSetDataAt("text/uri-list", spec, 0); |
|
dt.mozSetDataAt("text/plain", spec, 0); |
|
dt.mozSetDataAt("text/html", '<a href="' + spec + '">' + title + '</a>', 0); |
|
} |
|
aEvent.stopPropagation(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="_onDragOver"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
aEvent.preventDefault(); |
|
aEvent.stopPropagation(); |
|
|
|
var ib = document.getAnonymousElementByAttribute(this, "class", "tab-drop-indicator-bar"); |
|
|
|
// autoscroll the tab strip if we drag over the scroll buttons, |
|
// even if we aren't dragging a tab |
|
var pixelsToScroll = 0; |
|
var tabStrip = this.mTabContainer.mTabstrip; |
|
var ltr = window.getComputedStyle(this, null).direction == "ltr"; |
|
if (this.mTabContainer.getAttribute("overflow") == "true") { |
|
var targetAnonid = aEvent.originalTarget.getAttribute("anonid"); |
|
switch (targetAnonid) { |
|
case "scrollbutton-up": |
|
pixelsToScroll = -tabStrip.scrollIncrement; |
|
break; |
|
case "scrollbutton-down": |
|
case "alltabs-button": |
|
pixelsToScroll = tabStrip.scrollIncrement; |
|
break; |
|
} |
|
if (pixelsToScroll) |
|
tabStrip.scrollByPixels((ltr ? 1 : -1) * pixelsToScroll); |
|
} |
|
|
|
var ind = document.getAnonymousElementByAttribute(this, "class", "tab-drop-indicator"); |
|
|
|
var draggedTab = aEvent.dataTransfer.mozSourceNode; |
|
var within = draggedTab && |
|
draggedTab.parentNode == this.tabContainer; |
|
var newIndexOn = within ? -1 : this.getDropOnIndex(aEvent); |
|
|
|
var ltr = window.getComputedStyle(this, null).direction == "ltr"; |
|
var arrowX, tabBoxObject; |
|
if (newIndexOn != -1) { |
|
tabBoxObject = this.tabs[newIndexOn].boxObject; |
|
arrowX = tabBoxObject.screenX + tabBoxObject.width / 2; |
|
} |
|
else { |
|
var newIndexBetween = this.getDropIndex(aEvent); |
|
if (within) { |
|
var tabIndex = this.getTabIndex(draggedTab); |
|
if (newIndexBetween == tabIndex || |
|
newIndexBetween == tabIndex + 1) { |
|
ib.collapsed = true; |
|
return; |
|
} |
|
} |
|
|
|
if (newIndexBetween == this.tabs.length) { |
|
tabBoxObject = this.tabs[this.tabs.length - 1].boxObject; |
|
arrowX = tabBoxObject.x; |
|
arrowX = tabBoxObject.screenX; |
|
if (ltr) // for LTR "after" is on the right-hand side of the tab |
|
arrowX += tabBoxObject.width; |
|
} |
|
else { |
|
tabBoxObject = this.tabs[newIndexBetween].boxObject; |
|
arrowX = tabBoxObject.screenX; |
|
if (!ltr) // for RTL "before" is on the right-hand side of the tab |
|
arrowX += tabBoxObject.width; |
|
} |
|
} |
|
|
|
var boxObject = tabStrip.scrollBoxObject; |
|
// Check pixelsToScroll as well to prevent noticable judder. |
|
if (pixelsToScroll > 0 || arrowX >= boxObject.screenX + boxObject.width) |
|
arrowX = boxObject.screenX + boxObject.width; |
|
else if (pixelsToScroll < 0 || arrowX < boxObject.screenX) |
|
arrowX = boxObject.screenX; |
|
|
|
if (ltr) |
|
ind.style.marginLeft = (arrowX - this.boxObject.screenX) + "px"; |
|
else |
|
ind.style.marginRight = (this.boxObject.screenX + this.boxObject.width - arrowX) + "px"; |
|
|
|
ib.collapsed = false; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="_onDrop"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
document.getAnonymousElementByAttribute(this, "class", |
|
"tab-drop-indicator-bar") |
|
.collapsed = true; |
|
aEvent.stopPropagation(); |
|
|
|
var newIndex = this.getDropIndex(aEvent); |
|
var tabIndex; |
|
var dt = aEvent.dataTransfer; |
|
var draggedTab = dt.mozSourceNode; |
|
if (draggedTab && draggedTab.parentNode == this.tabContainer) { |
|
tabIndex = this.getTabIndex(draggedTab); |
|
if (newIndex > tabIndex) |
|
newIndex--; |
|
this.moveTabTo(tabIndex, newIndex); |
|
return; |
|
} |
|
|
|
var url; |
|
var linkHandler = Components.classes["@mozilla.org/content/dropped-link-handler;1"] |
|
.getService(Components.interfaces.nsIDroppedLinkHandler); |
|
try { |
|
// Pass true to disallow dropping javascript: or data: urls. |
|
url = linkHandler.dropLink(aEvent, {}, true); |
|
} catch (ex) {} |
|
|
|
// Valid urls don't contain spaces ' '; if we have a space |
|
// it isn't a valid url. |
|
if (!url || url.indexOf(" ") != -1) |
|
return; |
|
|
|
getShortcutOrURIAndPostData(url).then(data => { |
|
var bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground"); |
|
if (aEvent.shiftKey) |
|
bgLoad = !bgLoad; |
|
|
|
var tab = null; |
|
tabIndex = this.getDropOnIndex(aEvent); |
|
if (tabIndex != -1) { |
|
// Load in an existing tab. |
|
tab = this.tabs[tabIndex]; |
|
tab.linkedBrowser.loadURI(data.url); |
|
if (this.mCurrentTab != tab && !bgLoad) |
|
this.selectedTab = tab; |
|
} |
|
else if (dt.mozSourceDocument && |
|
dt.mozSourceDocument.defaultView.top == content) { |
|
// We're adding a new tab, and we may want parent-tab tracking. |
|
tab = this.loadOneTab(data.url, {inBackground: bgLoad}); |
|
if (newIndex != this.tabs.length - 1) |
|
this.moveTabTo(this.tabs.length - 1, newIndex); |
|
} |
|
else { |
|
// We're adding a new tab, but do not want parent-tab tracking. |
|
tab = this.addTab(data.url); |
|
if (newIndex != this.tabs.length - 1) |
|
this.moveTabTo(this.tabs.length - 1, newIndex); |
|
if (this.mCurrentTab != tab && !bgLoad) |
|
this.selectedTab = tab; |
|
} |
|
}); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="_onDragLeave"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
var target = aEvent.relatedTarget; |
|
while (target && target != this.mStrip) |
|
target = target.parentNode; |
|
|
|
if (target) |
|
return; |
|
|
|
document.getAnonymousElementByAttribute(this, "class", |
|
"tab-drop-indicator-bar") |
|
.collapsed = true; |
|
aEvent.stopPropagation(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabTo"> |
|
<parameter name="aSrcIndex"/> |
|
<parameter name="aDestIndex"/> |
|
<body> |
|
<![CDATA[ |
|
// for compatibility with extensions |
|
if (typeof(aSrcIndex) != "number") |
|
aSrcIndex = this.getTabIndex(aSrcIndex); |
|
|
|
if (aSrcIndex == aDestIndex) |
|
return; |
|
|
|
this._browsers = null; // invalidate cache |
|
this.mLastRelatedIndex = 0; |
|
|
|
this.mTabFilters.splice(aDestIndex, 0, this.mTabFilters.splice(aSrcIndex, 1)[0]); |
|
this.mTabListeners.splice(aDestIndex, 0, this.mTabListeners.splice(aSrcIndex, 1)[0]); |
|
|
|
this.mCurrentTab._selected = false; |
|
|
|
if (aDestIndex >= aSrcIndex) |
|
++aDestIndex; |
|
var tab = this.tabContainer.insertBefore(this.tabs[aSrcIndex], this.tabs.item(aDestIndex)); |
|
|
|
this.mCurrentTab._selected = true; |
|
this.tabContainer.mTabstrip.ensureElementIsVisible(this.mCurrentTab, false); |
|
|
|
tab.dispatchEvent(new UIEvent("TabMove", |
|
{ bubbles: true, cancelable: false, view: window, |
|
detail: aSrcIndex })); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getDropIndex"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
for (var i = 0; i < this.tabs.length; ++i) { |
|
var coord = this.tabs[i].boxObject.screenX + |
|
this.tabs[i].boxObject.width / 2; |
|
if (window.getComputedStyle(this, null).direction == "ltr") { |
|
if (aEvent.screenX < coord) |
|
return i; |
|
} else { |
|
if (aEvent.screenX > coord) |
|
return i; |
|
} |
|
} |
|
|
|
return this.tabs.length; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="getDropOnIndex"> |
|
<parameter name="aEvent"/> |
|
<body> |
|
<![CDATA[ |
|
for (var i = 0; i < this.tabs.length; ++i) { |
|
var tabBoxObject = this.tabs[i].boxObject; |
|
if (aEvent.screenX > tabBoxObject.screenX + tabBoxObject.width * .25 && |
|
aEvent.screenX < tabBoxObject.screenX + tabBoxObject.width * .75) |
|
return i; |
|
} |
|
|
|
return -1; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabLeft"> |
|
<body> |
|
<![CDATA[ |
|
if (window.getComputedStyle(this, null).direction == "ltr") |
|
this.moveTabBackward(); |
|
else |
|
this.moveTabForward(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabRight"> |
|
<body> |
|
<![CDATA[ |
|
if (window.getComputedStyle(this, null).direction == "ltr") |
|
this.moveTabForward(); |
|
else |
|
this.moveTabBackward(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabForward"> |
|
<body> |
|
<![CDATA[ |
|
var tabPos = this.tabContainer.selectedIndex; |
|
if (tabPos < this.browsers.length - 1) { |
|
this.moveTabTo(tabPos, tabPos + 1); |
|
this.mCurrentTab.focus(); |
|
} |
|
else if (this.arrowKeysShouldWrap) |
|
this.moveTabToStart(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabBackward"> |
|
<body> |
|
<![CDATA[ |
|
var tabPos = this.tabContainer.selectedIndex; |
|
if (tabPos > 0) { |
|
this.moveTabTo(tabPos, tabPos - 1); |
|
this.mCurrentTab.focus(); |
|
} |
|
else if (this.arrowKeysShouldWrap) |
|
this.moveTabToEnd(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabToStart"> |
|
<body> |
|
<![CDATA[ |
|
var tabPos = this.tabContainer.selectedIndex; |
|
if (tabPos > 0) { |
|
this.moveTabTo(tabPos, 0); |
|
this.mCurrentTab.focus(); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="moveTabToEnd"> |
|
<body> |
|
<![CDATA[ |
|
var tabPos = this.tabContainer.selectedIndex; |
|
if (tabPos < this.browsers.length - 1) { |
|
this.moveTabTo(tabPos, this.browsers.length - 1); |
|
this.mCurrentTab.focus(); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<field name="backBrowserGroup"> |
|
[] |
|
</field> |
|
|
|
<field name="forwardBrowserGroup"> |
|
[] |
|
</field> |
|
|
|
<method name="replaceGroup"> |
|
<parameter name="aGroup"/> |
|
<body> |
|
<![CDATA[ |
|
var oldBrowserGroup = []; |
|
var oldCount = this.tabs.length; |
|
var newCount = aGroup.length; |
|
var n = Math.max(oldCount, newCount); |
|
for (var i = 0; i < n; ++i) { |
|
if (i < newCount) { |
|
var data = aGroup[i]; |
|
if ("sessionHistory" in data) { |
|
this.addTab("about:blank", null); |
|
var browser = this.tabContainer.lastChild.linkedBrowser; |
|
// need to hold on to the listener so it won't go away |
|
// addProgressListener only has a weak pointer to it |
|
browser._SHListener = |
|
this.mInstallSH(browser, aGroup[i].sessionHistory); |
|
browser.webProgress.addProgressListener(browser._SHListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL); |
|
} else { |
|
var referrerURI = "referrerURI" in data ? data.referrerURI : null; |
|
this.addTab(data.URI, referrerURI); |
|
} |
|
} |
|
if (i < oldCount) { |
|
var firstTab = this.tabContainer.firstChild; |
|
var browserData = { |
|
sessionHistory : firstTab.linkedBrowser.sessionHistory |
|
} |
|
oldBrowserGroup.push(browserData); |
|
this.removeTab(firstTab, { disableUndo: true }); |
|
} |
|
} |
|
return oldBrowserGroup; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="appendGroup"> |
|
<parameter name="aGroup"/> |
|
<body> |
|
<![CDATA[ |
|
for (var i in aGroup) { |
|
var page = aGroup[i]; |
|
var referrerURI = "referrerURI" in page ? page.referrerURI : null; |
|
this.addTab(page.URI, referrerURI); |
|
} |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="loadGroup"> |
|
<parameter name="aGroup"/> |
|
<body> |
|
<![CDATA[ |
|
if (aGroup.length == 0) |
|
return null; |
|
|
|
var tab; |
|
if (this.mPrefs.getIntPref("browser.tabs.loadGroup") == 0) { |
|
var oldCount = this.tabs.length; |
|
this.appendGroup(aGroup); |
|
tab = this.tabs[oldCount]; |
|
} else { |
|
this.backBrowserGroup = this.replaceGroup(aGroup); |
|
this.forwardBrowserGroup = []; |
|
tab = this.tabContainer.firstChild; |
|
} |
|
return tab; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="goBackGroup"> |
|
<body> |
|
<![CDATA[ |
|
this.forwardBrowserGroup = this.replaceGroup(this.backBrowserGroup); |
|
this.backBrowserGroup = []; |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="goForwardGroup"> |
|
<body> |
|
<![CDATA[ |
|
this.backBrowserGroup = this.replaceGroup(this.forwardBrowserGroup); |
|
this.forwardBrowserGroup = []; |
|
]]> |
|
</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.backBrowserGroup.length != 0 || this.mCurrentBrowser.canGoBack;" |
|
readonly="true"/> |
|
|
|
<property name="canGoForward" |
|
onget="return this.forwardBrowserGroup.length != 0 || this.mCurrentBrowser.canGoForward;" |
|
readonly="true"/> |
|
|
|
<method name="goBack"> |
|
<body> |
|
<![CDATA[ |
|
if (this.backBrowserGroup.length != 0) |
|
return this.goBackGroup(); |
|
|
|
return this.mCurrentBrowser.goBack(); |
|
]]> |
|
</body> |
|
</method> |
|
|
|
<method name="goForward"> |
|
<body> |
|
<![CDATA[ |
|
if (this.forwardBrowserGroup.length != 0) |
|
return this.goForwardGroup(); |
|
|
|
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"/> |
|
<parameter name="aPostData"/> |
|
<body> |
|
<![CDATA[ |
|
return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData); |
|
]]> |
|
</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> |
|
|
|
<property name="currentURI" |
|
onget="return this.mCurrentBrowser.currentURI;" |
|
readonly="true"/> |
|
|
|
<field name="finder"><![CDATA[ |
|
({ |
|
mTabBrowser: this, |
|
mListeners: new Set(), |
|
get finder() { |
|
return this.mTabBrowser.mCurrentBrowser.finder; |
|
}, |
|
addResultListener: function(aListener) { |
|
this.mListeners.add(aListener); |
|
this.finder.addResultListener(aListener); |
|
}, |
|
removeResultListener: function(aListener) { |
|
this.mListeners.delete(aListener); |
|
this.finder.removeResultListener(aListener); |
|
}, |
|
get searchString() { |
|
return this.finder.searchString; |
|
}, |
|
get clipboardSearchString() { |
|
return this.finder.clipboardSearchString; |
|
}, |
|
set clipboardSearchString(val) { |
|
return this.finder.clipboardSearchString = val; |
|
}, |
|
set caseSensitive(val) { |
|
return this.finder.caseSensitive = val; |
|
}, |
|
set entireWord(val) { |
|
return this.finder.entireWord = val; |
|
}, |
|
get highlighter() { |
|
return this.finder.highlighter; |
|
}, |
|
get matchesCountLimit() { |
|
return this.finder.matchesCountLimit; |
|
}, |
|
fastFind: function(aSearchString, aLinksOnly, aDrawOutline) { |
|
this.finder.fastFind(aSearchString, aLinksOnly, aDrawOutline); |
|
}, |
|
findAgain: function(aFindBackwards, aLinksOnly, aDrawOutline) { |
|
this.finder.findAgain(aFindBackwards, aLinksOnly, aDrawOutline); |
|
}, |
|
setSearchStringToSelection: function() { |
|
return this.finder.setSearchStringToSelection(); |
|
}, |
|
highlight: function(...args) { |
|
this.finder.highlight(...args); |
|
}, |
|
getInitialSelection: function() { |
|
this.finder.getInitialSelection(); |
|
}, |
|
getActiveSelectionText: function() { |
|
return this.finder.getActiveSelectionText(); |
|
}, |
|
enableSelection: function() { |
|
this.finder.enableSelection(); |
|
}, |
|
removeSelection: function() { |
|
this.finder.removeSelection(); |
|
}, |
|
focusContent: function() { |
|
this.finder.focusContent(); |
|
}, |
|
onFindbarClose: function() { |
|
this.finder.onFindbarClose(); |
|
}, |
|
onFindbarOpen: function() { |
|
this.finder.onFindbarOpen(); |
|
}, |
|
onModalHighlightChange: function(...args) { |
|
return this.finder.onModalHighlightChange(...args); |
|
}, |
|
onHighlightAllChange: function(...args) { |
|
return this.finder.onHighlightAllChange(...args); |
|
}, |
|
keyPress: function(aEvent) { |
|
this.finder.keyPress(aEvent); |
|
}, |
|
requestMatchesCount: function(...args) { |
|
this.finder.requestMatchesCount(...args); |
|
} |
|
}) |
|
]]></field> |
|
|
|
<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="contentWindowAsCPOW" |
|
readonly="true" |
|
onget="return this.mCurrentBrowser.contentWindowAsCPOW"/> |
|
|
|
<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="contentDocument" |
|
onget="return this.mCurrentBrowser.contentDocument;" |
|
readonly="true"/> |
|
|
|
<property name="contentTitle" |
|
onget="return this.mCurrentBrowser.contentTitle;" |
|
readonly="true"/> |
|
|
|
<property name="securityUI" |
|
onget="return this.mCurrentBrowser.securityUI;" |
|
readonly="true"/> |
|
|
|
<property name="userTypedValue" |
|
onget="return this.mCurrentBrowser.userTypedValue;" |
|
onset="return this.mCurrentBrowser.userTypedValue = val;"/> |
|
|
|
<property name="droppedLinkHandler" |
|
onget="return this.mCurrentBrowser.droppedLinkHandler;" |
|
onset="return this.mCurrentBrowser.droppedLinkHandler = val;"/> |
|
|
|
<property name="showWindowResizer" |
|
onget="return this.mCurrentBrowser.showWindowResizer;" |
|
onset="return this.mCurrentBrowser.showWindowResizer = val;"/> |
|
|
|
<property name="docShellIsActive" |
|
onget="return this.mCurrentBrowser.docShellIsActive;" |
|
onset="return this.mCurrentBrowser.docShellIsActive = val;"/> |
|
|
|
<property name="fullZoom" |
|
onget="return this.mCurrentBrowser.fullZoom;" |
|
onset="return this.mCurrentBrowser.fullZoom = val;"/> |
|
|
|
<property name="textZoom" |
|
onget="return this.mCurrentBrowser.textZoom;" |
|
onset="return this.mCurrentBrowser.textZoom = val;"/> |
|
|
|
<property name="isSyntheticDocument" |
|
onget="return this.mCurrentBrowser.isSyntheticDocument;" |
|
readonly="true"/> |
|
|
|
<property name="messageManager" |
|
onget="return window.messageManager;" |
|
readonly="true"/> |
|
|
|
<method name="observe"> |
|
<parameter name="aSubject"/> |
|
<parameter name="aTopic"/> |
|
<parameter name="aData"/> |
|
<body> |
|
<![CDATA[ |
|
var maxUndoDepth = 0; |
|
switch (aTopic) { |
|
case "browser:purge-session-history": |
|
break; |
|
|
|
case "nsPref:changed": |
|
if (aData == "browser.tabs.max_tabs_undo") { |
|
maxUndoDepth = Math.max(0, this.mPrefs.getIntPref(aData)); |
|
break; |
|