%tabBrowserDTD; ]> ({ ALL: 0, OTHER: 1, TO_END: 2 }); Cc["@mozilla.org/suite/sessionstore;1"].getService(Ci.nsISessionStore); document.getAnonymousElementByAttribute(this, "anonid", "tabbox"); document.getAnonymousElementByAttribute(this, "anonid", "strip"); document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer"); document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer"); this.tabContainer.childNodes document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle"); null null null [] [] new Array() new Array() 0 window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsILoadContext) .usePrivateBrowsing; null null 0 null new Array() null 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; ]]> 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 & Ci.nsIWebProgressListener.STATE_START && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) { if (aWebProgress.isTopLevel) { this.mFeeds = []; // 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 & Ci.nsIWebProgressListener.STATE_RESTORING) && aWebProgress && aWebProgress.isTopLevel) { this.mTab.setAttribute("busy", "true"); // Do the following only for the top frame not any subframes. // Remove favicon. This shows busy and progress indicators even during a reload. this.mTab.removeAttribute("image"); if (!(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_RELOAD)) this.mTabBrowser.setTabTitleLoading(this.mTab); } if (this.mTab.selected) this.mTabBrowser.mIsBusy = true; } } else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && aStateFlags & Ci.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 && 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 & (Ci.nsIWebProgressListener.STATE_START | Ci.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; // 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) { Cu.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) { Cu.reportError(e); } } } , this); return allowRefresh; }, addFeed: function(aLink) { this.mFeeds.push(aLink); }, QueryInterface: function(aIID) { if (aIID.equals(Ci.nsIWebProgressListener) || aIID.equals(Ci.nsIWebProgressListener2) || aIID.equals(Ci.nsISupportsWeakReference) || aIID.equals(Ci.nsISupports)) return this; throw Cr.NS_NOINTERFACE; } }); ]]> 1; var isAtEnd = this.getTabsToTheEndFrom(this.mContextTab).length == 0; var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "*"); for (let menuitem of menuItems) { let tbattr = menuitem.getAttribute("tbattr"); if (tbattr.includes("tabbrowser-undoclosetab")) { menuitem.disabled = (this.usePrivateBrowsing ? this.savedBrowsers.length : this.mSessionStore.getClosedTabCount(window)) == 0; menuitem.hidden = (this.usePrivateBrowsing || Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo") <= 0) && Services.prefs.getIntPref("browser.tabs.max_tabs_undo") <= 0; } else menuitem.disabled = (tbattr.includes("tabbrowser-totheend") && isAtEnd) || (tbattr.includes("tabbrowser-multiple") && !isMultiple) || (tbattr.includes("tabbrowser-tab") && !isTab); } ]]> false this.mCurrentBrowser.finder.removeResultListener(l)); } let oldTab = this.mCurrentTab; // Preview mode should not reset the owner. if (!this._previewMode && !oldTab.selected) oldTab.owner = null; let lastRelatedTab = this.mLastRelatedIndex ? this.tabs[this.mLastRelatedIndex] : null; if (lastRelatedTab && !lastRelatedTab.selected) { lastRelatedTab.owner = null; } newBrowser.setAttribute("primary", "true"); 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. 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 & (Ci.nsIWebProgressListener.STATE_START | Ci.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); // Also send the onUpdateCurrentBrowser event for compatibility this._callProgressListeners(null, "onUpdateCurrentBrowser", [tabListener.mStateFlags, tabListener.mStatus, tabListener.mMessage, tabListener.mTotalProgress], 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(); } ]]> return !this.mStrip.collapsed; 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 = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; if (aAllowThirdPartyFixup) { flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS; } try { browser.loadURIWithFlags(aURIs[0], { flags, postData: aPostDatas[0], triggeringPrincipal : aTriggeringPrincipal, }); } 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, triggeringPrincipal: aTriggeringPrincipal, }); } let tabNum = targetTabIndex; for (let i = 1; i < aURIs.length; ++i) { let tab = this.addTab(aURIs[i], { allowThirdPartyFixup: aAllowThirdPartyFixup, postData: aPostDatas[i], userContextId: aUserContextId, triggeringPrincipal: aTriggeringPrincipal, }); if (targetTabIndex !== -1) this.moveTabTo(tab, ++tabNum); } if (!aLoadInBackground) { if (firstTabAdded) { // .selectedTab setter focuses the content area this.selectedTab = firstTabAdded; } else this.selectedBrowser.focus(); } ]]> = 0; --i) { tabsToEnd.push(tabs[i]); } return tabsToEnd.reverse(); ]]> = 0; --i) { this.removeTab(tabs[i]); } } ]]> = 0; --i) { if (this.tabs[i] != aTab) this.removeTab(this.tabs[i]); } } ]]> = 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; // add back the filters, security first (bug 313335) var secFlags = Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION | Ci.nsIWebProgress.NOTIFY_SECURITY; b.webProgress.addProgressListener(b.securityUI, secFlags); var position = this.tabs.length - 1; var tabListener = this.mTabProgressListener(t, b, false); const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"] .createInstance(Ci.nsIWebProgress); filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL); b.webProgress.addProgressListener(filter, Ci.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(t, 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; ]]> 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; let owner = ("owner" in aTab) ? aTab.owner : null; // Clean up before/after selected 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 (owner && !owner.hidden && !owner.closing && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")) { this.selectedTab = owner; } else 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 = Services.prefs.getIntPref("browser.tabs.max_tabs_undo"); var oldSH = oldBrowser.webNavigation.sessionHistory; var inOnLoad = oldBrowser.docShell.isExecutingOnLoadHandler; var isPopup = oldBrowser.contentWindow.opener && !Services.prefs.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. Also fix up the selected panel in the case // the removed browser was to the left of the current browser. this.removeBrowser(oldBrowser); 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 = Cc["@mozilla.org/browser/shistory;1"] .createInstance(Ci.nsISHistory); oldBrowser.webNavigation.sessionHistory = newSH; var entry = oldSH.getEntryAtIndex(oldSH.index) .QueryInterface(Ci.nsISHEntry) .clone(); // The bfcache entry is tightly coupled to the original shistory it // belongs to, better to drop it. entry.abandonBFCacheEntry(); // 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; this.removeBrowser(deadBrowser); } ]]> = this.savedBrowsers.length || aIndex < 0) return false; var tabData = this.savedBrowsers.splice(aIndex, 1)[0]; var deadBrowser = tabData.browserData.browser; delete tabData.browserData; this.removeBrowser(deadBrowser); return true; ]]> = 0 && aIndex < this.tabs.length && aIndex != this.tabContainer.selectedIndex) this.selectedTab = this.tabs[aIndex]; if (aEvent) { aEvent.preventDefault(); aEvent.stopPropagation(); } ]]> return this.mTabBox.selectedTab; tab.linkedBrowser)); ]]> ' + title + '', 0); } aEvent.stopPropagation(); ]]> 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; ]]> this.getTabIndex(draggedTab)) newIndex--; this.moveTabTo(draggedTab, newIndex); return; } var url; try { // Pass true to disallow dropping javascript: or data: urls. url = Services.droppedLinkHandler.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.includes(" ")) return; getShortcutOrURIAndPostData(url).then(data => { var bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground"); if (aEvent.shiftKey) bgLoad = !bgLoad; let triggeringPrincipal = browserDragAndDrop.getTriggeringPrincipal(aEvent); var tab = null; tabIndex = this.getDropOnIndex(aEvent); if (tabIndex != -1) { // Load in an existing tab. tab = this.tabs[tabIndex]; tab.linkedBrowser.loadURI(data.url, { allowThirdPartyFixup: true, triggeringPrincipal, }); 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, allowThirdPartyFixup: true, triggeringPrincipal, }); this.moveTabTo(tab, newIndex); } else { // We're adding a new tab, but do not want parent-tab tracking. tab = this.addTab(data.url, { allowThirdPartyFixup: true, triggeringPrincipal, }); this.moveTabTo(tab, newIndex); if (this.mCurrentTab != tab && !bgLoad) this.selectedTab = tab; } }); ]]> = oldPosition) ++aIndex; this.mCurrentTab._selected = false; // invalidate cache this._browsers = null; // Use .item() instead of [] because dragging to the end of the // strip goes out of bounds: .item() returns null (so it acts like // appendChild), but [] throws. var tab = this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex)); this.mCurrentTab._selected = true; if (wasFocused) this.mCurrentTab.focus(); this.tabContainer._handleTabSelect(false); tab.dispatchEvent(new UIEvent("TabMove", { bubbles: true, cancelable: false, view: window, detail: oldPosition })); ]]> coord) return i; } } return this.tabs.length; ]]> tabBoxObject.screenX + tabBoxObject.width * .25 && aEvent.screenX < tabBoxObject.screenX + tabBoxObject.width * .75) return i; } return -1; ]]> 0) { this.moveTabTo(this.mCurrentTab, tabPos - 1); } else if (this.arrowKeysShouldWrap) this.moveTabToEnd(); ]]> 0) { this.moveTabTo(this.mCurrentTab, 0); } ]]> maxUndoDepth) { var tabData = this.savedBrowsers.pop(); var deadBrowser = tabData.browserData.browser; delete tabData.browserData; this.removeBrowser(deadBrowser); } ]]> null null false 0 document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox"); document.getAnonymousElementByAttribute(this, "anonid", "alltabs-popup"); this.arrowScrollbox._scrollButtonDown; = tabstripBO.screenX && curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width) this.childNodes[i].removeAttribute("tabIsScrolled"); else this.childNodes[i].setAttribute("tabIsScrolled", "true"); } ]]>