%tabBrowserDTD; ]> Components.classes['@mozilla.org/preferences-service;1'] .getService(Components.interfaces.nsIPrefService) .getBranch(null); Components.classes["@mozilla.org/docshell/urifixup;1"] .getService(Components.interfaces.nsIURIFixup); document.getAnonymousElementByAttribute(this, "anonid", "tabbox"); document.getAnonymousElementByAttribute(this, "anonid", "strip"); document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer"); document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer"); this.mTabContainer.childNodes document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle"); null null null [] new Array() new Array() false null 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) this.mTab.setAttribute("progress", Math.floor(aCurTotalProgress * 9.9 / aMaxTotalProgress)); if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) { for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) { var p = this.mTabBrowser.mProgressListeners[i]; if (p) { try { p.onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress); } catch (e) { Components.utils.reportError(e); } } } } }, onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) { if (!aRequest) return; var oldBlank = this.mBlank; const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener; const nsIChannel = Components.interfaces.nsIChannel; if (aStateFlags & nsIWebProgressListener.STATE_START && aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { // It's okay to clear what the user typed when we start // loading a document. If the user types, this flag gets // set to false, if the document load ends without an // onLocationChange, this flag also gets set to false // (so we keep it while switching tabs after a failed load). // We need to add 2 because loadURIWithFlags may have // cancelled a pending load which would have cleared // its anchor scroll detection temporary increment. if (aWebProgress.DOMWindow == this.mBrowser.contentWindow) this.mBrowser.userTypedClear += 2; if (!this.mBlank) { this.mTab.removeAttribute("progress"); this.mTab.setAttribute("busy", "true"); this.mTab.label = this.mTabBrowser.mStringBundle.getString("tabs.loading"); this.mTab.removeAttribute("image"); if (this.mTabBrowser.mCurrentTab == this.mTab) this.mTabBrowser.mIsBusy = true; } } else if (aStateFlags & nsIWebProgressListener.STATE_STOP && aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) { // The document is done loading, it's okay to clear // the value again. if (aWebProgress.DOMWindow == this.mBrowser.contentWindow) if (this.mBrowser.userTypedClear > 1) this.mBrowser.userTypedClear -= 2; else if (this.mBrowser.userTypedClear > 0) this.mBrowser.userTypedClear--; if (this.mBlank) this.mBlank = false; this.mTab.removeAttribute("busy"); var location = this.mBrowser.currentURI; if (this.mIcon) { this.mTab.setAttribute("image", this.mIcon); } else if (this.mBrowser.contentDocument instanceof ImageDocument && this.mTabBrowser.mPrefs.getBoolPref("browser.chrome.site_icons")) { var req = this.mBrowser.contentDocument.imageRequest; if (req && !(req.imageStatus & Components.interfaces.imgIRequest.STATUS_ERROR)) { try { var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); var tabImg = document.getAnonymousElementByAttribute(this.mTab, "anonid", "tab-icon"); var w = tabImg.boxObject.width; var h = tabImg.boxObject.height; canvas.width = w; canvas.height = h; var ctx = canvas.getContext('2d'); ctx.drawImage(this.mBrowser.contentDocument.body.firstChild, 0, 0, w, h); this.mTab.setAttribute("image", canvas.toDataURL()); } catch (e) { // non-canvas build, fall back to the old method var sz = this.mTabBrowser.mPrefs.getIntPref("browser.chrome.image_icons.max_size"); if (req.image.width <= sz && req.image.height <= sz) this.mTab.setAttribute("image", this.mBrowser.currentURI.spec); } } } else if (this.mTabBrowser.shouldLoadFavIcon(location)) this.mTabBrowser.loadFavIcon(location, "image", this.mTab); if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading")) this.mTabBrowser.setTabTitle(this.mTab); if (this.mTabBrowser.mCurrentTab == this.mTab) this.mTabBrowser.mIsBusy = false; } if (!oldBlank && this.mTabBrowser.mCurrentTab == this.mTab) { for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) { var p = this.mTabBrowser.mProgressListeners[i]; if (p) { try { p.onStateChange(aWebProgress, aRequest, aStateFlags, aStatus); } catch (e) { Components.utils.reportError(e); } } } } }, // The first location change is gotoIndex called from mInstallSH, // the second one is considered a user action. mLocationChangeCount : 0, onLocationChange : function(aWebProgress, aRequest, aLocation) { if (aWebProgress.DOMWindow == this.mBrowser.contentWindow) { this.mIcon = ""; this.mFeeds = []; if (this.mLocationChangeCount > 0 || aLocation.spec != "about:blank") ++this.mLocationChangeCount; if (this.mLocationChangeCount == 2) { this.mTabBrowser.backBrowserGroup = []; this.mTabBrowser.forwardBrowserGroup = []; } // The document loaded correctly, clear the value if we should if (this.mBrowser.userTypedClear > 0) this.mBrowser.userTypedValue = null; } if (!this.mBlank && this.mTabBrowser.mCurrentTab == this.mTab) { for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) { var p = this.mTabBrowser.mProgressListeners[i]; if (p) { try { p.onLocationChange(aWebProgress, aRequest, aLocation); } catch (e) { Components.utils.reportError(e); } } } } }, onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) { if (this.mBlank) return; if (this.mTabBrowser.mCurrentTab == this.mTab) { for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) { var p = this.mTabBrowser.mProgressListeners[i]; if (p) { try { p.onStatusChange(aWebProgress, aRequest, aStatus, aMessage); } catch (e) { Components.utils.reportError(e); } } } } }, onSecurityChange : function(aWebProgress, aRequest, aState) { if (this.mTabBrowser.mCurrentTab == this.mTab) { for (var i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) { var p = this.mTabBrowser.mProgressListeners[i]; if (p) { try { p.onSecurityChange(aWebProgress, aRequest, aState); } catch (e) { Components.utils.reportError(e); } } } } }, setIcon : function(aURI) { this.mIcon = aURI; if (!this.mTab.hasAttribute("busy")) this.mTab.setAttribute("image", aURI); }, addFeed : function(aLink) { this.mFeeds.push(aLink); }, 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; } }); ]]> return !this.mStrip.collapsed; 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.mTabContainer.selectedItem = aTab; for (var i = this.mTabs.length - 1; i >= 0; --i) { if (this.mTabs[i] != aTab) this.removeTab(this.mTabs[i]); } } } ]]> = this.savedBrowsers.length || aIndex < 0) return; this._browsers = null; var t = this.referenceTab.cloneNode(true); var savedData = this.savedBrowsers.splice(aIndex, 1)[0]; var b = savedData.browser; var hist = savedData.history; this.mTabContainer.appendChild(t); if (t.previousSibling.selected) t.setAttribute("afterselected", true); // navigate back to the proper page from the light page b.webNavigation.goBack(); // reattach the old history b.webNavigation.sessionHistory = hist; var uniqueID = b.parentNode.id; t.linkedPanel = uniqueID; t.linkedBrowser = b; // 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.mTabs.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; if (this.mTabs.length == 2 && !this.mTabs[0].linkedBrowser.webNavigation.sessionHistory.count) this.removeTab(this.mTabs[0]); // only one tab => selected anyway else { this.selectedTab = t; this.mStrip.collapsed = false; } ]]> 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; // Clean up before/afterselected attributes before removing the tab aTab._selected = false; this.mTabContainer.removeChild(aTab); // 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.mTabContainer.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.undoclose.depth"); var oldSH = oldBrowser.webNavigation.sessionHistory; var inOnLoad = oldBrowser.docShell.isExecutingOnLoadHandler; if (maxUndoDepth <= 0 || oldSH.count == 0 || aDisableUndo || inOnLoad) { // 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(); this.mPanelContainer.removeChild(oldBrowser.parentNode); // 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 this.savedBrowsers.unshift({browser: oldBrowser, history: oldSH}); var newSH = Components.classes["@mozilla.org/browser/shistory;1"] .createInstance(Components.interfaces.nsISHistoryInternal); oldBrowser.webNavigation.sessionHistory = newSH; var entry = oldSH.getEntryAtIndex(oldSH.index, false) newSH.addEntry(entry, true); // about:blank is light oldBrowser.loadURI("about:blank"); // remove overflow from the undo stack if (this.savedBrowsers.length > maxUndoDepth) { var deadBrowser = this.savedBrowsers.pop().browser; deadBrowser.destroy(); // The pagehide event that this removal triggers is safe // because the browser is no longer current at this point this.mPanelContainer.removeChild(deadBrowser.parentNode); this.mTabBox.selectedPanel = this.selectedTab.linkedBrowser.parentNode; } ]]> return this.mTabContainer; return this.mTabBox.selectedTab; ' + title + ''); } ]]> tabIndex) newIndex--; this.moveTabTo(tabIndex, newIndex); } else { var url = transferUtils.retrieveURLFromData(aXferData.data, aXferData.flavour.contentType); // valid urls don't contain spaces ' '; if we have a space it isn't a valid url. // Also disallow dropping javascript: or data: urls--bail out if (!url || !url.length || url.indexOf(" ", 0) != -1 || /^\s*(javascript|data):/.test(url)) return; // Perform a security check before loading the URI nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, url); var bgLoad = this.mPrefs.getBoolPref("browser.tabs.loadInBackground"); var tab = null; tabIndex = this.getDropOnIndex(aEvent); if (tabIndex != -1) { // Load in an existing tab tab = this.mTabs[tabIndex]; tab.linkedBrowser.loadURI(getShortcutOrURI(url)); if (this.mCurrentTab != tab && !bgLoad) this.selectedTab = tab; } else if (aDragSession.sourceDocument && aDragSession.sourceDocument.defaultView.top == content) { // We're adding a new tab, and we may want parent-tab tracking tab = this.addTab(getShortcutOrURI(url), null, null, !bgLoad); if (newIndex != this.mTabs.length - 1) this.moveTabTo(this.mTabs.length - 1, newIndex); } else { // We're adding a new tab, but do not want parent-tab tracking tab = this.addTab(getShortcutOrURI(url)); if (newIndex != this.mTabs.length - 1) this.moveTabTo(this.mTabs.length - 1, newIndex); if (this.mCurrentTab != tab && !bgLoad) this.selectedTab = tab; } } ]]> = aSrcIndex) ++aDestIndex; var tab = this.mTabContainer.insertBefore(this.mTabs[aSrcIndex], this.mTabs.item(aDestIndex)); this.mCurrentTab._selected = true; var evt = document.createEvent("UIEvents"); evt.initUIEvent("TabMove", true, false, window, aSrcIndex); tab.dispatchEvent(evt); return tab; ]]> coord) return i; } } return this.mTabs.length; ]]> tabBoxObject.x + tabBoxObject.width * .25 && aEvent.clientX < tabBoxObject.x + tabBoxObject.width * .75) return i; } return -1; ]]> 0) { this.moveTabTo(tabPos, tabPos - 1); this.mCurrentTab.focus(); } else if (this.arrowKeysShouldWrap) this.moveTabToEnd(); ]]> 0) { this.moveTabTo(tabPos, 0); this.mCurrentTab.focus(); } ]]> [] []