document.getElementById(this.getAttribute("tabcontainer"));
this.tabContainer.childNodes;
!tab.hidden && !tab.closing);
return this._visibleTabs;
]]>
({ ALL: 0, OTHER: 1, TO_END: 2 });
null
Components.classes["@mozilla.org/docshell/urifixup;1"]
.getService(Components.interfaces.nsIURIFixup);
Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
.getService(Components.interfaces.mozIPlacesAutoComplete);
(Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
null
null
null
[]
null
[]
new Map()
new Map()
false
new Map();
this.AppConstants.platform == "macosx";
null
false
""
0
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:home and about:privatebrowsing arrive with nsIRequest
// pointing to their resolved jar: or file: URIs.
if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
originalLocation != "about:blank" &&
this.mBrowser.initialPageLoadedFromURLBar != originalLocation.spec &&
this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
// Indicating that we started a load will allow the location
// bar to be cleared when the load finishes.
// In order to not overwrite user-typed content, we avoid it
// (see if condition above) in a very specific case:
// If the load is of an 'initial' page (e.g. about:privatebrowsing,
// about:newtab, etc.), was not explicitly typed in the location
// bar by the user, is not about:blank (because about:blank can be
// loaded by websites under their principal), and the current
// page in the browser is about:blank (indicating it is a newly
// created or re-created browser, e.g. because it just switched
// remoteness or is a new tab/window).
this.mBrowser.urlbarChangeTracker.startedLoad();
}
delete this.mBrowser.initialPageLoadedFromURLBar;
// 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");
if (aWebProgress.isTopLevel &&
!(aWebProgress.loadType & Ci.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.connecting"))
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 & Ci.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 & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
aLocation.spec != "about:blank") ||
(isSameDocument && this.mBrowser.inLoadURI)) {
this.mBrowser.userTypedValue = null;
}
// If the browser was playing audio, we should remove the playing state.
if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
this.mTab.removeAttribute("soundplaying");
this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
}
// If the browser was previously muted, we should restore the muted state.
if (this.mTab.hasAttribute("muted")) {
this.mTab.linkedBrowser.mute();
}
if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
let findBar = this.mTabBrowser.getFindBar(this.mTab);
// Close the Find toolbar if we're in old-style TAF mode
if (findBar.findMode != findBar.FIND_NORMAL) {
findBar.close();
}
// fix bug 253793 - turn off highlight when page changes
findBar.getElement("highlight").checked = false;
}
// 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;
}
let unifiedComplete = this.mTabBrowser._unifiedComplete;
if (this.mBrowser.registeredOpenURI) {
unifiedComplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
delete this.mBrowser.registeredOpenURI;
}
// Tabs in private windows aren't registered as "Open" so
// that they don't appear as switch-to-tab candidates.
if (!isBlankPageURL(aLocation.spec) &&
(!PrivateBrowsingUtils.isWindowPrivate(window) ||
PrivateBrowsingUtils.permanentPrivateBrowsing)) {
unifiedComplete.registerOpenPage(aLocation);
this.mBrowser.registeredOpenURI = aLocation;
}
}
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._callProgressListeners("onStatusChange",
[aWebProgress, aRequest, aStatus, aMessage]);
this.mMessage = aMessage;
},
onSecurityChange: function (aWebProgress, aRequest, aState) {
this._callProgressListeners("onSecurityChange",
[aWebProgress, aRequest, aState]);
},
onRefreshAttempted: function (aWebProgress, aURI, aDelay, aSameURI) {
return this._callProgressListeners("onRefreshAttempted",
[aWebProgress, aURI, aDelay, aSameURI]);
},
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;
}
});
]]>
null
{
if (this._tabSwitchID === tabSwitchID) {
TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_MS");
this._tabSwitchID = null;
}
window.removeEventListener("MozAfterPaint", onMozAfterPaint);
}
window.addEventListener("MozAfterPaint", onMozAfterPaint);
}
}
var oldTab = this.mCurrentTab;
// Preview mode should not reset the owner
if (!this._previewMode && !oldTab.selected)
oldTab.owner = null;
if (this._lastRelatedTab) {
if (!this._lastRelatedTab.selected)
this._lastRelatedTab.owner = null;
this._lastRelatedTab = null;
}
var oldBrowser = this.mCurrentBrowser;
if (!gMultiProcessBrowser) {
oldBrowser.setAttribute("type", "content-targetable");
oldBrowser.docShellIsActive = false;
newBrowser.setAttribute("type", "content-primary");
newBrowser.docShellIsActive =
(window.windowState != window.STATE_MINIMIZED);
}
var updateBlockedPopups = false;
if ((oldBrowser.blockedPopups && !newBrowser.blockedPopups) ||
(!oldBrowser.blockedPopups && newBrowser.blockedPopups))
updateBlockedPopups = true;
this.mCurrentBrowser = newBrowser;
this.mCurrentTab = this.tabContainer.selectedItem;
this.showTab(this.mCurrentTab);
var forwardButtonContainer = document.getElementById("urlbar-wrapper");
if (forwardButtonContainer) {
forwardButtonContainer.setAttribute("switchingtabs", "true");
window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() {
window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr);
forwardButtonContainer.removeAttribute("switchingtabs");
});
}
this._appendStatusPanel();
if (updateBlockedPopups)
this.mCurrentBrowser.updateBlockedPopups();
// Update the URL bar.
var loc = this.mCurrentBrowser.currentURI;
var webProgress = this.mCurrentBrowser.webProgress;
var securityUI = this.mCurrentBrowser.securityUI;
this._callProgressListeners(null, "onLocationChange",
[webProgress, null, loc, 0], true,
false);
if (securityUI) {
// Include the true final argument to indicate that this event is
// simulated (instead of being observed by the webProgressListener).
this._callProgressListeners(null, "onSecurityChange",
[webProgress, null, securityUI.state, true],
true, false);
}
var listener = this._tabListeners.get(this.mCurrentTab);
if (listener && listener.mStateFlags) {
this._callProgressListeners(null, "onUpdateCurrentBrowser",
[listener.mStateFlags, listener.mStatus,
listener.mMessage, listener.mTotalProgress],
true, false);
}
if (!this._previewMode) {
this._recordTabAccess(this.mCurrentTab);
this.mCurrentTab.updateLastAccessed();
this.mCurrentTab.removeAttribute("unread");
oldTab.updateLastAccessed();
let oldFindBar = oldTab._findBar;
if (oldFindBar &&
oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
!oldFindBar.hidden)
this._lastFindValue = oldFindBar._findField.value;
this.updateTitlebar();
this.mCurrentTab.removeAttribute("titlechanged");
this.mCurrentTab.removeAttribute("attention");
}
// 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;
this._callProgressListeners(null, "onStateChange",
[webProgress, null,
nsIWebProgressListener.STATE_START |
nsIWebProgressListener.STATE_IS_NETWORK, 0],
true, false);
}
// 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;
this._callProgressListeners(null, "onStateChange",
[webProgress, null,
nsIWebProgressListener.STATE_STOP |
nsIWebProgressListener.STATE_IS_NETWORK, 0],
true, false);
}
this._setCloseKeyState(!this.mCurrentTab.pinned);
// TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
// that might rely upon the other changes suppressed.
// Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
if (!this._previewMode) {
// We've selected the new tab, so go ahead and notify listeners.
let event = new CustomEvent("TabSelect", {
bubbles: true,
cancelable: false,
detail: {
previousTab: oldTab
}
});
this.mCurrentTab.dispatchEvent(event);
this._tabAttrModified(oldTab, ["selected"]);
this._tabAttrModified(this.mCurrentTab, ["selected"]);
if (oldBrowser != newBrowser &&
oldBrowser.getInPermitUnload) {
oldBrowser.getInPermitUnload(inPermitUnload => {
if (!inPermitUnload) {
return;
}
// Since the user is switching away from a tab that has
// a beforeunload prompt active, we remove the prompt.
// This prevents confusing user flows like the following:
// 1. User attempts to close Firefox
// 2. User switches tabs (ingoring a beforeunload prompt)
// 3. User returns to tab, presses "Leave page"
let promptBox = this.getTabModalPromptBox(oldBrowser);
let prompts = promptBox.listPrompts();
// There might not be any prompts here if the tab was closed
// while in an onbeforeunload prompt, which will have
// destroyed aforementioned prompt already, so check there's
// something to remove, first:
if (prompts.length) {
// NB: This code assumes that the beforeunload prompt
// is the top-most prompt on the tab.
prompts[prompts.length - 1].abortPrompt();
}
});
}
oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
if (this.isFindBarInitialized(oldTab)) {
let findBar = this.getFindBar(oldTab);
oldTab._findBarFocused = (!findBar.hidden &&
findBar._findField.getAttribute("focused") == "true");
}
// If focus is in the tab bar, retain it there.
if (document.activeElement == oldTab) {
// We need to explicitly focus the new tab, because
// tabbox.xml does this only in some cases.
this.mCurrentTab.focus();
} else if (gMultiProcessBrowser && document.activeElement !== newBrowser) {
// Clear focus so that _adjustFocusAfterTabSwitch can detect if
// some element has been focused and respect that.
document.activeElement.blur();
}
if (!gMultiProcessBrowser)
this._adjustFocusAfterTabSwitch(this.mCurrentTab);
}
updateUserContextUIIndicator();
gIdentityHandler.updateSharingIndicator();
this.tabContainer._setPositionalAttributes();
if (!gMultiProcessBrowser) {
let event = new CustomEvent("TabSwitchDone", {
bubbles: true,
cancelable: true
});
this.dispatchEvent(event);
}
if (!aForceUpdate)
TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
]]>
1 false/true NO
var multiple = aURIs.length > 1;
var owner = multiple || aLoadInBackground ? null : this.selectedTab;
var firstTabAdded = null;
if (aReplace) {
try {
this.loadURI(aURIs[0], null, null);
} 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, skipAnimation: multiple});
var tabNum = this.tabContainer.selectedIndex;
for (let i = 1; i < aURIs.length; ++i) {
let tab = this.addTab(aURIs[i], {skipAnimation: true});
if (aReplace)
this.moveTabTo(tab, ++tabNum);
}
if (!aLoadInBackground) {
if (firstTabAdded) {
// .selectedTab setter focuses the content area
this.selectedTab = firstTabAdded;
}
else
this.selectedBrowser.focus();
}
]]>
null
[
"canGoBack", "canGoForward", "goBack", "goForward", "permitUnload",
"reload", "reloadWithFlags", "stop", "loadURI", "loadURIWithFlags",
"goHome", "homePage", "gotoIndex", "currentURI", "documentURI",
"preferences", "imageDocument", "isRemoteBrowser", "messageManager",
"getTabBrowser", "finder", "fastFind", "sessionHistory", "contentTitle",
"characterSet", "fullZoom", "textZoom", "webProgress",
"addProgressListener", "removeProgressListener",
"audioPlaybackStarted", "audioPlaybackStopped", "adjustPriority",
"pauseMedia", "stopMedia", "blockMedia", "resumeMedia",
"audioMuted", "mute", "unmute", "blockedPopups", "mIconURL", "lastURI",
"userTypedValue", "purgeSessionHistory", "stopScroll", "startScroll"
]
{
this._insertBrowser(aTab);
return browser[name];
};
setter = (value) => {
this._insertBrowser(aTab);
return browser[name] = value;
};
}
Object.defineProperty(browser, name, {
get: getter,
set: setter,
configurable: true,
enumerable: true
});
}
]]>
into the DOM if necessary.
if (!notificationbox.parentNode) {
// 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(notificationbox);
}
// wire up a progress listener for the new browser object.
let tabListener = this.mTabProgressListener(aTab, browser, uriIsAboutBlank, usingPreloadedContent);
const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Ci.nsIWebProgress);
filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
this._tabListeners.set(aTab, tabListener);
this._tabFilters.set(aTab, filter);
browser.droppedLinkHandler = handleDroppedLink;
// We start our browsers out as inactive, and then maintain
// activeness in the tab switcher.
browser.docShellIsActive = false;
// When addTab() is called with an URL that is not "about:blank" we
// set the "nodefaultsrc" attribute that prevents a frameLoader
// from being created as soon as the linked is inserted
// into the DOM. We thus have to register the new outerWindowID
// for non-remote browsers after we have called browser.loadURI().
if (!remote) {
this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser);
}
var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} });
aTab.dispatchEvent(evt);
]]>
= 0; --i) {
tabsToEnd.push(tabs[i]);
}
return tabsToEnd.reverse();
]]>
= 0; --i) {
this.removeTab(tabs[i], aParams);
}
}
]]>
= 0; --i) {
if (tabs[i] != aTab && !tabs[i].pinned)
this.removeTab(tabs[i], {animate: true});
}
}
]]>
[]
3 /* don't want lots of concurrent animations */ ||
aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
!Services.prefs.getBoolPref("browser.tabs.animate")) {
this._endRemoveTab(aTab);
return;
}
this.tabContainer._handleTabTelemetryStart(aTab);
this._blurTab(aTab);
aTab.style.maxWidth = ""; // ensure that fade-out transition happens
aTab.removeAttribute("fadein");
setTimeout(function (tab, tabbrowser) {
if (tab.parentNode &&
window.getComputedStyle(tab).maxWidth == "0.1px") {
NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
tabbrowser._endRemoveTab(tab);
}
}, 3000, aTab, this);
]]>
false
l != aListener);
]]>
this.mTabsProgressListeners.push(aListener);
l != aListener);
]]>
= tabs.length) {
// clamp at right-most tab if out of range.
aIndex = tabs.length - 1;
}
this.selectedTab = tabs[aIndex];
if (aEvent) {
aEvent.preventDefault();
aEvent.stopPropagation();
}
]]>
return this.mCurrentTab;
{
if (typeof name == "string" && Number.isInteger(parseInt(name))) {
return (name in this.tabs);
}
return false;
},
get: (target, name) => {
if (name == "length") {
return this.tabs.length;
}
if (typeof name == "string" && Number.isInteger(parseInt(name))) {
if (!(name in this.tabs)) {
return undefined;
}
return this.tabs[name].linkedBrowser;
}
return target[name];
}
});
]]>
0)
this.moveTabTo(this.mCurrentTab, 0);
]]>
null
this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
},
// This function runs before every event. It fixes up the state
// to account for closed tabs.
preActions: function() {
this.assert(this.tabbrowser._switcher);
this.assert(this.tabbrowser._switcher === this);
for (let [tab, state] of this.tabState) {
if (!tab.linkedBrowser) {
this.tabState.delete(tab);
}
}
if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
this.lastVisibleTab = null;
}
if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
this.spinnerHidden();
this.spinnerTab = null;
}
if (this.loadingTab && !this.loadingTab.linkedBrowser) {
this.loadingTab = null;
this.clearTimer(this.loadTimer);
this.loadTimer = null;
}
},
// This code runs after we've responded to an event or requested a new
// tab. It's expected that we've already updated all the principal
// state variables. This function takes care of updating any auxilliary
// state.
postActions: function() {
// Once we finish loading loadingTab, we null it out. So the state should
// always be LOADING.
this.assert(!this.loadingTab ||
this.getTabState(this.loadingTab) == this.STATE_LOADING);
// We guarantee that loadingTab is non-null iff loadTimer is non-null. So
// the timer is set only when we're loading something.
this.assert(!this.loadTimer || this.loadingTab);
this.assert(!this.loadingTab || this.loadTimer);
// If we're not loading anything, try loading the requested tab.
if (!this.loadTimer && this.getTabState(this.requestedTab) == this.STATE_UNLOADED) {
this.loadRequestedTab();
}
// See how many tabs still have work to do.
let numPending = 0;
for (let [tab, state] of this.tabState) {
if (state == this.STATE_LOADED && tab !== this.requestedTab) {
numPending++;
}
if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
numPending++;
}
}
this.updateDisplay();
// It's possible for updateDisplay to trigger one of our own event
// handlers, which might cause finish() to already have been called.
// Check for that before calling finish() again.
if (!this.tabbrowser._switcher) {
return;
}
if (numPending == 0) {
this.finish();
}
this.logState("done");
},
// Fires when we're ready to unload unused tabs.
onUnloadTimeout: function() {
this.logState("onUnloadTimeout");
this.unloadTimer = null;
this.preActions();
let numPending = 0;
// Unload any tabs that can be unloaded.
for (let [tab, state] of this.tabState) {
if (state == this.STATE_LOADED &&
!this.maybeVisibleTabs.has(tab) &&
tab !== this.lastVisibleTab &&
tab !== this.loadingTab &&
tab !== this.requestedTab)
{
this.setTabState(tab, this.STATE_UNLOADING);
}
if (state != this.STATE_UNLOADED && tab !== this.requestedTab) {
numPending++;
}
}
if (numPending) {
// Keep the timer going since there may be more tabs to unload.
this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
}
this.postActions();
},
// Fires when an ongoing load has taken too long.
onLoadTimeout: function() {
this.logState("onLoadTimeout");
this.preActions();
this.loadTimer = null;
this.loadingTab = null;
this.postActions();
},
// Fires when the layers become available for a tab.
onLayersReady: function(browser) {
this.logState("onLayersReady");
let tab = this.tabbrowser.getTabForBrowser(browser);
this.setTabState(tab, this.STATE_LOADED);
this.maybeFinishTabSwitch();
if (this.loadingTab === tab) {
this.clearTimer(this.loadTimer);
this.loadTimer = null;
this.loadingTab = null;
}
},
// Fires when we paint the screen. Any tab switches we initiated
// previously are done, so there's no need to keep the old layers
// around.
onPaint: function() {
this.maybeVisibleTabs.clear();
this.maybeFinishTabSwitch();
},
// Called when we're done clearing the layers for a tab.
onLayersCleared: function(browser) {
this.logState("onLayersCleared");
let tab = this.tabbrowser.getTabForBrowser(browser);
if (tab) {
this.setTabState(tab, this.STATE_UNLOADED);
}
},
// Called when a tab switches from remote to non-remote. In this case
// a MozLayerTreeReady notification that we requested may never fire,
// so we need to simulate it.
onRemotenessChange: function(tab) {
this.logState("onRemotenessChange");
if (!tab.linkedBrowser.isRemoteBrowser) {
if (this.getTabState(tab) == this.STATE_LOADING) {
this.onLayersReady(tab.linkedBrowser);
} else if (this.getTabState(tab) == this.STATE_UNLOADING) {
this.onLayersCleared(tab.linkedBrowser);
}
}
},
// Called when a tab has been removed, and the browser node is
// about to be removed from the DOM.
onTabRemoved: function(tab) {
if (this.lastVisibleTab == tab) {
// The browser that was being presented to the user is
// going to be removed during this tick of the event loop.
// This will cause us to show a tab spinner instead.
this.preActions();
this.lastVisibleTab = null;
this.postActions();
}
},
// Called when the user asks to switch to a given tab.
requestTab: function(tab) {
if (tab === this.requestedTab) {
return;
}
// Instrumentation to figure out bug 1166351 - if the binding
// on the browser we're switching to has gone away, try to find out
// why. We should remove this and the checkBrowserBindingAlive
// method once bug 1166351 has been closed.
if (this.tabbrowser.AppConstants.E10S_TESTING_ONLY &&
!this.checkBrowserBindingAlive(tab)) {
Cu.reportError("Please report the above errors in bug 1166351.");
return;
}
this.logState("requestTab " + this.tinfo(tab));
this.startTabSwitch();
this.requestedTab = tab;
let browser = this.requestedTab.linkedBrowser;
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
fl.tabParent.suppressDisplayport(true);
this.activeSuppressDisplayport.add(fl.tabParent);
}
this.preActions();
if (this.unloadTimer) {
this.clearTimer(this.unloadTimer);
}
this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), this.UNLOAD_DELAY);
this.postActions();
},
handleEvent: function(event, delayed = false) {
if (this._processing) {
this.setTimer(() => this.handleEvent(event, true), 0);
return;
}
if (delayed && this.tabbrowser._switcher != this) {
// if we delayed processing this event, we might be out of date, in which
// case we drop the delayed events
return;
}
this._processing = true;
this.preActions();
if (event.type == "MozLayerTreeReady") {
this.onLayersReady(event.originalTarget);
} if (event.type == "MozAfterPaint") {
this.onPaint();
} else if (event.type == "MozLayerTreeCleared") {
this.onLayersCleared(event.originalTarget);
} else if (event.type == "TabRemotenessChange") {
this.onRemotenessChange(event.target);
}
this.postActions();
this._processing = false;
},
/*
* Telemetry and Profiler related helpers for recording tab switch
* timing.
*/
startTabSwitch: function () {
TelemetryStopwatch.cancel("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
this.addMarker("AsyncTabSwitch:Start");
this.switchInProgress = true;
},
/**
* Something has occurred that might mean that we've completed
* the tab switch (layers are ready, paints are done, spinners
* are hidden). This checks to make sure all conditions are
* satisfied, and then records the tab switch as finished.
*/
maybeFinishTabSwitch: function () {
if (this.switchInProgress && this.requestedTab &&
this.getTabState(this.requestedTab) == this.STATE_LOADED) {
// After this point the tab has switched from the content thread's point of view.
// The changes will be visible after the next refresh driver tick + composite.
let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
if (time != -1) {
TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
this.log("DEBUG: tab switch time = " + time);
this.addMarker("AsyncTabSwitch:Finish");
}
this.switchInProgress = false;
}
},
spinnerDisplayed: function () {
this.assert(!this.spinnerTab);
TelemetryStopwatch.start("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window);
this.addMarker("AsyncTabSwitch:SpinnerShown");
},
spinnerHidden: function () {
this.assert(this.spinnerTab);
this.log("DEBUG: spinner time = " +
TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window));
TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_MS", window);
this.addMarker("AsyncTabSwitch:SpinnerHidden");
// we do not get a onPaint after displaying the spinner
this.maybeFinishTabSwitch();
},
addMarker: function(marker) {
if (Services.profiler) {
Services.profiler.AddMarker(marker);
}
},
/*
* Debug related logging for switcher.
*/
_useDumpForLogging: false,
_logInit: false,
logging: function () {
if (this._useDumpForLogging)
return true;
if (this._logInit)
return this._shouldLog;
let result = false;
try {
result = Services.prefs.getBoolPref("browser.tabs.remote.logSwitchTiming");
} catch (ex) {
}
this._shouldLog = result;
this._logInit = true;
return this._shouldLog;
},
// Instrumentation for bug 1166351
checkBrowserBindingAlive: function(tab) {
let err = Cu.reportError;
if (!tab.linkedBrowser) {
err("Attempting to switch to tab that has no linkedBrowser.");
return false;
}
let b = tab.linkedBrowser;
if (!b._alive) {
// The browser binding has been removed. Dump a bunch of
// diagnostic information to the browser console.
let utils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
let results = utils.getBindingURLs(b);
let urls = [];
for (let i = 0; i < results.length; ++i) {
urls.push(results.queryElementAt(i, Ci.nsIURI).spec);
}
err("The browser has the following bindings:");
err(urls);
err("MozBinding is currently: " +
window.getComputedStyle(b).MozBinding);
if (!b.parentNode) {
err("Browser was removed from the DOM.");
} else {
err("Parent is: " + b.parentNode.outerHTML);
}
return false;
}
return true;
},
tinfo: function(tab) {
if (tab) {
return tab._tPos + "(" + tab.linkedBrowser.currentURI.spec + ")";
}
return "null";
},
log: function(s) {
if (!this.logging())
return;
if (this._useDumpForLogging) {
dump(s + "\n");
} else {
Services.console.logStringMessage(s);
}
},
logState: function(prefix) {
if (!this.logging())
return;
let accum = prefix + " ";
for (let i = 0; i < this.tabbrowser.tabs.length; i++) {
let tab = this.tabbrowser.tabs[i];
let state = this.getTabState(tab);
accum += i + ":";
if (tab === this.lastVisibleTab) accum += "V";
if (tab === this.loadingTab) accum += "L";
if (tab === this.requestedTab) accum += "R";
if (state == this.STATE_LOADED) accum += "(+)";
if (state == this.STATE_LOADING) accum += "(+?)";
if (state == this.STATE_UNLOADED) accum += "(-)";
if (state == this.STATE_UNLOADING) accum += "(-?)";
accum += " ";
}
if (this._useDumpForLogging) {
dump(accum + "\n");
} else {
Services.console.logStringMessage(accum);
}
},
};
this._switcher = switcher;
switcher.init();
return switcher;
]]>
{
let keyElem = document.getElementById(keyElemId);
let shortcut = ShortcutUtils.prettifyShortcut(keyElem);
return this.mStringBundle.getFormattedString(stringId, [shortcut]);
};
var label;
if (tab.mOverCloseButton) {
label = tab.selected ?
stringWithShortcut("tabs.closeSelectedTab.tooltip", "key_close") :
this.mStringBundle.getString("tabs.closeTab.tooltip");
} else if (tab._overPlayingIcon) {
let stringID;
if (tab.selected) {
stringID = tab.linkedBrowser.audioMuted ?
"tabs.unmuteAudio.tooltip" :
"tabs.muteAudio.tooltip";
label = stringWithShortcut(stringID, "key_toggleMute");
} else {
stringID = tab.linkedBrowser.audioMuted ?
"tabs.unmuteAudio.background.tooltip" :
"tabs.muteAudio.background.tooltip";
label = this.mStringBundle.getString(stringID);
}
} else {
label = tab.getAttribute("label") +
(this.AppConstants.E10S_TESTING_ONLY && tab.linkedBrowser && tab.linkedBrowser.isRemoteBrowser ? " - e10s" : "");
}
event.target.setAttribute("label", label);
]]>
Services.console.logStringMessage("enterTabbedMode is an obsolete method and " +
"will be removed in a future release.");
true
this.tabContainer.visible = aShow;
return this.tabContainer.visible;
null
null
document.getElementById(this.getAttribute("tabbrowser"));
this.tabbrowser.mTabBox;
document.getElementById("tabContextMenu");
0
document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
null
null
null
null
null
null
let root = document.documentElement;
return root.getAttribute("customizing") == "true" ||
root.getAttribute("customize-exiting") == "true";
false
document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator");
350
0
false
2) {
// This is an optimization to avoid layout flushes by calling
// getBoundingClientRect() when we just opened a second tab. In
// this case it's highly unlikely that the tab width is smaller
// than mTabClipWidth and the tab close button obscures too much
// of the tab's label. In the edge case of the window being too
// narrow (or if tabClipWidth has been set to a way higher value),
// we'll correct the 'closebuttons' attribute after the tabopen
// animation has finished.
let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs];
if (tab && tab.getBoundingClientRect().width <= this.mTabClipWidth) {
this.setAttribute("closebuttons", "activetab");
return;
}
}
this.removeAttribute("closebuttons");
]]>
tabStrip.scrollSize)
tabStrip.scrollByPixels(-1);
} catch (e) {}
]]>
document.getAnonymousElementByAttribute(this, "anonid", "closing-tabs-spacer");
NaN
false
false
tabs[tabs.length-1]._tPos);
var tabWidth = aTab.getBoundingClientRect().width;
if (!this._tabDefaultMaxWidth)
this._tabDefaultMaxWidth =
parseFloat(window.getComputedStyle(aTab).maxWidth);
this._lastTabClosedByMouse = true;
if (this.getAttribute("overflow") == "true") {
// Don't need to do anything if we're in overflow mode and aren't scrolled
// all the way to the right, or if we're closing the last tab.
if (isEndTab || !this.mTabstrip._scrollButtonDown.disabled)
return;
// If the tab has an owner that will become the active tab, the owner will
// be to the left of it, so we actually want the left tab to slide over.
// This can't be done as easily in non-overflow mode, so we don't bother.
if (aTab.owner)
return;
this._expandSpacerBy(tabWidth);
} else { // non-overflow mode
// Locking is neither in effect nor needed, so let tabs expand normally.
if (isEndTab && !this._hasTabTempMaxWidth)
return;
let numPinned = this.tabbrowser._numPinnedTabs;
// Force tabs to stay the same width, unless we're closing the last tab,
// which case we need to let them expand just enough so that the overall
// tabbar width is the same.
if (isEndTab) {
let numNormalTabs = tabs.length - numPinned;
tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs;
if (tabWidth > this._tabDefaultMaxWidth)
tabWidth = this._tabDefaultMaxWidth;
}
tabWidth += "px";
for (let i = numPinned; i < tabs.length; i++) {
let tab = tabs[i];
tab.style.setProperty("max-width", tabWidth, "important");
if (!isEndTab) { // keep tabs the same width
tab.style.transition = "none";
tab.clientTop; // flush styles to skip animation; see bug 649247
tab.style.transition = "";
}
}
this._hasTabTempMaxWidth = true;
this.tabbrowser.addEventListener("mousemove", this, false);
window.addEventListener("mouseout", this, false);
}
]]>
0
0;
if (doPosition) {
this.setAttribute("positionpinnedtabs", "true");
let scrollButtonWidth = this.mTabstrip._scrollButtonDown.getBoundingClientRect().width;
let paddingStart = this.mTabstrip.scrollboxPaddingStart;
let width = 0;
for (let i = numPinned - 1; i >= 0; i--) {
let tab = this.childNodes[i];
width += tab.getBoundingClientRect().width;
tab.style.marginInlineStart = - (width + scrollButtonWidth + paddingStart) + "px";
}
this.style.paddingInlineStart = width + paddingStart + "px";
} else {
this.removeAttribute("positionpinnedtabs");
for (let i = 0; i < numPinned; i++) {
let tab = this.childNodes[i];
tab.style.marginInlineStart = "";
}
this.style.paddingInlineStart = "";
}
if (this._lastNumPinned != numPinned) {
this._lastNumPinned = numPinned;
this._handleTabSelect(false);
}
]]>
draggedTab._dragData.animLastScreenX;
draggedTab._dragData.animLastScreenX = screenX;
let rtl = (window.getComputedStyle(this).direction == "rtl");
let pinned = draggedTab.pinned;
let numPinned = this.tabbrowser._numPinnedTabs;
let tabs = this.tabbrowser.visibleTabs
.slice(pinned ? 0 : numPinned,
pinned ? numPinned : undefined);
if (rtl)
tabs.reverse();
let tabWidth = draggedTab.getBoundingClientRect().width;
// Move the dragged tab based on the mouse position.
let leftTab = tabs[0];
let rightTab = tabs[tabs.length - 1];
let tabScreenX = draggedTab.boxObject.screenX;
let translateX = screenX - draggedTab._dragData.screenX;
if (!pinned)
translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX;
let leftBound = leftTab.boxObject.screenX - tabScreenX;
let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
(tabScreenX + tabWidth);
translateX = Math.max(translateX, leftBound);
translateX = Math.min(translateX, rightBound);
draggedTab.style.transform = "translateX(" + translateX + "px)";
// Determine what tab we're dragging over.
// * Point of reference is the center of the dragged tab. If that
// point touches a background tab, the dragged tab would take that
// tab's position when dropped.
// * We're doing a binary search in order to reduce the amount of
// tabs we need to check.
let tabCenter = tabScreenX + translateX + tabWidth / 2;
let newIndex = -1;
let oldIndex = "animDropIndex" in draggedTab._dragData ?
draggedTab._dragData.animDropIndex : draggedTab._tPos;
let low = 0;
let high = tabs.length - 1;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (tabs[mid] == draggedTab &&
++mid > high)
break;
let boxObject = tabs[mid].boxObject;
let screenX = boxObject.screenX + getTabShift(tabs[mid], oldIndex);
if (screenX > tabCenter) {
high = mid - 1;
} else if (screenX + boxObject.width < tabCenter) {
low = mid + 1;
} else {
newIndex = tabs[mid]._tPos;
break;
}
}
if (newIndex >= oldIndex)
newIndex++;
if (newIndex < 0 || newIndex == oldIndex)
return;
draggedTab._dragData.animDropIndex = newIndex;
// Shift background tabs to leave a gap where the dragged tab
// would currently be dropped.
for (let tab of tabs) {
if (tab != draggedTab) {
let shift = getTabShift(tab, newIndex);
tab.style.transform = shift ? "translateX(" + shift + "px)" : "";
}
}
function getTabShift(tab, dropIndex) {
if (tab._tPos < draggedTab._tPos && tab._tPos >= dropIndex)
return rtl ? -tabWidth : tabWidth;
if (tab._tPos > draggedTab._tPos && tab._tPos < dropIndex)
return rtl ? tabWidth : -tabWidth;
return 0;
}
]]>
this.mTabstrip._scrollButtonDown;
boxObject.screenX + boxObject.width * .75)
return null;
}
return tab;
]]>
tabs[i].boxObject.screenX + tabs[i].boxObject.width / 2)
return i;
}
return tabs.length;
]]>
1)
return "none";
var types = dt.mozTypesAt(0);
// tabs are always added as the first type
if (types[0] == TAB_DROP_TYPE) {
let sourceNode = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (sourceNode instanceof XULElement &&
sourceNode.localName == "tab" &&
sourceNode.ownerDocument.defaultView instanceof ChromeWindow &&
sourceNode.ownerDocument.documentElement.getAttribute("windowtype") == "navigator:browser" &&
sourceNode.ownerDocument.defaultView.gBrowser.tabContainer == sourceNode.parentNode) {
// Do not allow transfering a private tab to a non-private window
// and vice versa.
if (PrivateBrowsingUtils.isWindowPrivate(window) !=
PrivateBrowsingUtils.isWindowPrivate(sourceNode.ownerDocument.defaultView))
return "none";
if (window.gMultiProcessBrowser !=
sourceNode.ownerDocument.defaultView.gMultiProcessBrowser)
return "none";
return dt.dropEffect == "copy" ? "copy" : "move";
}
}
if (browserDragAndDrop.canDropLink(event)) {
return "link";
}
return "none";
]]>
1) {
let averageInterval = 0;
for (let i = 1; i < frameCount; i++) {
averageInterval += intervals[i];
}
averageInterval = averageInterval / (frameCount - 1);
Services.telemetry.getHistogramById("FX_TAB_ANIM_ANY_FRAME_INTERVAL_MS").add(averageInterval);
if (aTab._recordingTabOpenPlain) {
delete aTab._recordingTabOpenPlain;
// While we do have a telemetry probe NEWTAB_PAGE_ENABLED to monitor newtab preview, it'll be
// easier to overview the data without slicing by it. Hence the additional histograms with _PREVIEW.
let preview = this._browserNewtabpageEnabled ? "_PREVIEW" : "";
Services.telemetry.getHistogramById("FX_TAB_ANIM_OPEN" + preview + "_FRAME_INTERVAL_MS").add(averageInterval);
}
}
]]>
1 && !target._ignoredCloseButtonClicks) {
target._ignoredCloseButtonClicks = true;
event.stopPropagation();
return;
} else {
// Reset the "ignored click" flag
target._ignoredCloseButtonClicks = false;
}
}
/* Protects from close-tab-button errant doubleclick:
* Since we're removing the event target, if the user
* double-clicks the button, the dblclick event will be dispatched
* with the tabbar as its event target (and explicit/originalTarget),
* which treats that as a mouse gesture for opening a new tab.
* In this context, we're manually blocking the dblclick event
* (see tabbrowser-close-tab-button dblclick handler).
*/
if (this._blockDblClick) {
if (!("_clickedTabBarOnce" in this)) {
this._clickedTabBarOnce = true;
return;
}
delete this._clickedTabBarOnce;
this._blockDblClick = false;
}
]]>
endOfTab) ||
(!ltr && event.clientX < endOfTab)) {
BrowserOpenTab();
}
} else {
BrowserOpenTab();
}
} else {
return;
}
event.stopPropagation();
]]>
= this._dragTime + this._dragOverDelay)
this.selectedItem = tab;
ind.collapsed = true;
return;
}
}
var rect = tabStrip.getBoundingClientRect();
var newMargin;
if (pixelsToScroll) {
// if we are scrolling, put the drop indicator at the edge
// so that it doesn't jump while scrolling
let scrollRect = tabStrip.scrollClientRect;
let minMargin = scrollRect.left - rect.left;
let maxMargin = Math.min(minMargin + scrollRect.width,
scrollRect.right);
if (!ltr)
[minMargin, maxMargin] = [this.clientWidth - maxMargin,
this.clientWidth - minMargin];
newMargin = (pixelsToScroll > 0) ? maxMargin : minMargin;
}
else {
let newIndex = this._getDropIndex(event, effects == "link");
if (newIndex == this.childNodes.length) {
let tabRect = this.childNodes[newIndex-1].getBoundingClientRect();
if (ltr)
newMargin = tabRect.right - rect.left;
else
newMargin = rect.right - tabRect.left;
}
else {
let tabRect = this.childNodes[newIndex].getBoundingClientRect();
if (ltr)
newMargin = tabRect.left - rect.left;
else
newMargin = rect.right - tabRect.right;
}
}
ind.collapsed = false;
newMargin += ind.clientWidth / 2;
if (!ltr)
newMargin *= -1;
ind.style.transform = "translate(" + Math.round(newMargin) + "px)";
ind.style.marginInlineStart = (-ind.clientWidth) + "px";
]]>
draggedTab._tPos)
newIndex--;
this.tabbrowser.moveTabTo(draggedTab, newIndex);
}
} else if (draggedTab) {
let newIndex = this._getDropIndex(event, false);
this.tabbrowser.adoptTab(draggedTab, newIndex, true);
} else {
// Pass true to disallow dropping javascript: or data: urls
let url;
try {
url = browserDragAndDrop.drop(event, { }, true);
} catch (ex) {}
if (!url)
return;
let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
if (event.shiftKey)
bgLoad = !bgLoad;
let tab = this._getDragTargetTab(event, true);
if (!tab) {
// We're adding a new tab.
let userContextId = this.selectedItem.getAttribute("usercontextid");
let newIndex = this._getDropIndex(event, true);
let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad,
allowThirdPartyFixup: true,
userContextId});
this.tabbrowser.moveTabTo(newTab, newIndex);
} else {
// Load in an existing tab.
try {
let webNav = Ci.nsIWebNavigation;
let flags = webNav.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
webNav.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
this.tabbrowser.getBrowserForTab(tab).loadURIWithFlags(url, flags);
if (!bgLoad)
this.selectedItem = tab;
} catch (ex) {
// Just ignore invalid urls
}
}
}
if (draggedTab) {
delete draggedTab._dragData;
}
]]>
wX && eX < (wX + window.outerWidth)) {
let bo = this.mTabstrip.boxObject;
// also avoid detaching if the the tab was dropped too close to
// the tabbar (half a tab)
let endScreenY = bo.screenY + 1.5 * bo.height;
if (eY < endScreenY && eY > window.screenY)
return;
}
// screen.availLeft et. al. only check the screen that this window is on,
// but we want to look at the screen the tab is being dropped onto.
var screen = Cc["@mozilla.org/gfx/screenmanager;1"]
.getService(Ci.nsIScreenManager)
.screenForRect(eX, eY, 1, 1);
var fullX = {}, fullY = {}, fullWidth = {}, fullHeight = {};
var availX = {}, availY = {}, availWidth = {}, availHeight = {};
// get full screen rect and available rect, both in desktop pix
screen.GetRectDisplayPix(fullX, fullY, fullWidth, fullHeight);
screen.GetAvailRectDisplayPix(availX, availY, availWidth, availHeight);
// scale factor to convert desktop pixels to CSS px
var scaleFactor =
screen.contentsScaleFactor / screen.defaultCSSScaleFactor;
// synchronize CSS-px top-left coordinates with the screen's desktop-px
// coordinates, to ensure uniqueness across multiple screens
// (compare the equivalent adjustments in nsGlobalWindow::GetScreenXY()
// and related methods)
availX.value = (availX.value - fullX.value) * scaleFactor + fullX.value;
availY.value = (availY.value - fullY.value) * scaleFactor + fullY.value;
availWidth.value *= scaleFactor;
availHeight.value *= scaleFactor;
// ensure new window entirely within screen
var winWidth = Math.min(window.outerWidth, availWidth.value);
var winHeight = Math.min(window.outerHeight, availHeight.value);
var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, availX.value),
availX.value + availWidth.value - winWidth);
var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, availY.value),
availY.value + availHeight.value - winHeight);
delete draggedTab._dragData;
if (this.tabbrowser.tabs.length == 1) {
// resize _before_ move to ensure the window fits the new screen. if
// the window is too large for its screen, the window manager may do
// automatic repositioning.
window.resizeTo(winWidth, winHeight);
window.moveTo(left, top);
window.focus();
} else {
let props = { screenX: left, screenY: top };
if (this.tabbrowser.AppConstants.platform != "win") {
props.outerWidth = winWidth;
props.outerHeight = winHeight;
}
this.tabbrowser.replaceTabWithWindow(draggedTab, props);
}
event.stopPropagation();
]]>
// for the one-close-button case
event.stopPropagation();
event.stopPropagation();
return this.getAttribute("pinned") == "true";
return this.getAttribute("hidden") == "true";
return this.getAttribute("muted") == "true";
undefined
return this.getAttribute("soundplaying") == "true";
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
Infinity
false
null
this.style.MozUserFocus = '';
this.style.MozUserFocus = '';
return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
this._mirror();
this._mirror();
if (this.hasAttribute("mirror"))
this.removeAttribute("mirror");
else
this.setAttribute("mirror", "true");
if (!this.hasAttribute("sizelimit")) {
this.setAttribute("sizelimit", "true");
this._calcMouseTargetRect();
}
0
= this.childNodes.length)
return val;
let toTab = this.getRelatedElement(this.childNodes[val]);
let fromTab = this._selectedPanel ? this.getRelatedElement(this._selectedPanel)
: null;
gBrowser._getSwitcher().requestTab(toTab);
var panel = this._selectedPanel;
var newPanel = this.childNodes[val];
this._selectedPanel = newPanel;
if (this._selectedPanel != panel) {
var event = document.createEvent("Events");
event.initEvent("select", true, true);
this.dispatchEvent(event);
this._selectedIndex = val;
}
return val;
]]>
null
null