%tabBrowserDTD; ]> document.getElementById(this.getAttribute("tabcontainer")); this.tabContainer.childNodes; ({ ALL: 0, OTHER: 1, TO_END: 2 }); null Components.classes["@mozilla.org/docshell/urifixup;1"] .getService(Components.interfaces.nsIURIFixup); Components.classes["@mozilla.org/browser/favicon-service;1"] .getService(Components.interfaces.nsIFaviconService); Components.classes["@mozilla.org/autocomplete/search;1?name=history"] .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 [] [] [] [] false new Map(); this.AppConstants.platform == "macosx"; null false "" real JS array let prompts = Array.slice(els); return prompts; }, }; return promptBox; ]]> 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) { // It's okay to clear what the user typed when we start // loading a document. If the user types, this counter gets // set to zero, if the document load ends without an // onLocationChange, this counter gets decremented // (so we keep it while switching tabs after failed loads) // 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.isTopLevel) this.mBrowser.userTypedClear += 2; if (this._shouldShowProgress(aRequest)) { if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) { this.mTab.setAttribute("busy", "true"); if (aWebProgress.isTopLevel && !(this.mBrowser.docShell.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); if (!this.mTab.selected) this.mTab.setAttribute("unread", "true"); } this.mTab.removeAttribute("progress"); if (aWebProgress.isTopLevel) { if (!Components.isSuccessCode(aStatus) && !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; if (this.mTab.selected && gURLBar) URLBarSetURI(); } else { // The document is done loading, we no longer want the // value cleared. if (this.mBrowser.userTypedClear > 1) this.mBrowser.userTypedClear -= 2; else if (this.mBrowser.userTypedClear > 0) this.mBrowser.userTypedClear--; } if (!this.mBrowser.mIconURL) this.mTabBrowser.useDefaultIcon(this.mTab); } if (this.mBlank) this.mBlank = false; var location = aRequest.QueryInterface(nsIChannel).URI; // 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) { // If userTypedClear > 0, the document loaded correctly and we should be // clearing the user typed value. We also 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). if (this.mBrowser.userTypedClear > 0 || ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) && aLocation.spec != "about:blank")) this.mBrowser.userTypedValue = null; // Don't clear the favicon if this onLocationChange was // triggered by a pushState or a replaceState. See bug 550565. if (aWebProgress.isLoadingDocument && !(aWebProgress.loadType & Ci.nsIDocShell.LOAD_CMD_PUSHSTATE)) { this.mBrowser.mIconURL = null; } let autocomplete = this.mTabBrowser._placesAutocomplete; if (this.mBrowser.registeredOpenURI) { autocomplete.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)) { autocomplete.registerOpenPage(aLocation); this.mBrowser.registeredOpenURI = aLocation; } } if (!this.mBlank) { this._callProgressListeners("onLocationChange", [aWebProgress, aRequest, aLocation, aFlags]); } if (topLevel) this.mBrowser.lastURI = aLocation; }, 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; } }); ]]> let req = browser.contentDocument.imageRequest; let sz = Services.prefs.getIntPref("browser.chrome.image_icons.max_size"); if (browser.contentDocument instanceof ImageDocument && req && req.image) { if (Services.prefs.getBoolPref("browser.chrome.site_icons") && sz) { try { var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); var tabImg = document.getAnonymousElementByAttribute(aTab, "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(browser.contentDocument.body.firstChild, 0, 0, w, h); icon = canvas.toDataURL(); } catch (e) { try { if (req && req.image && req.image.width <= sz && req.image.height <= sz) icon = browser.currentURI; } catch (e) { } } } } // Use documentURIObject in the check for shouldLoadFavIcon so that we // do the right thing with about:-style error pages. Bug 453442 else if (this.shouldLoadFavIcon(docURIObject)) { let url = docURIObject.prePath + "/favicon.ico"; if (!this.isFailedIcon(url)) icon = url; } this.setIcon(aTab, icon); ]]> oldBrowser.finder.removeResultListener(l)); } var updateBlockedPopups = false; if (!oldBrowser || (oldBrowser.blockedPopups && !newBrowser.blockedPopups) || (!oldBrowser.blockedPopups && newBrowser.blockedPopups)) updateBlockedPopups = true; newBrowser.setAttribute("type", "content-primary"); newBrowser.docShellIsActive = (window.windowState != window.STATE_MINIMIZED); this.mCurrentBrowser = newBrowser; this.mCurrentTab = this.tabContainer.selectedItem; this.finder.mListeners.forEach(l => this.mCurrentBrowser.finder.addResultListener(l)); this.showTab(this.mCurrentTab); var backForwardContainer = document.getElementById("unified-back-forward-button"); if (backForwardContainer) { backForwardContainer.setAttribute("switchingtabs", "true"); window.addEventListener("MozAfterPaint", function removeSwitchingtabsAttr() { window.removeEventListener("MozAfterPaint", removeSwitchingtabsAttr); backForwardContainer.removeAttribute("switchingtabs"); }); } if (updateBlockedPopups) this.mCurrentBrowser.updateBlockedPopups(); // Update the URL bar. var loc = this.mCurrentBrowser.currentURI; // Bug 666809 - SecurityUI support for e10s var webProgress = this.mCurrentBrowser.webProgress; var securityUI = this.mCurrentBrowser.securityUI; this._callProgressListeners(null, "onLocationChange", [webProgress, null, loc, 0], true, false); if (securityUI) { this._callProgressListeners(null, "onSecurityChange", [webProgress, null, securityUI.state], true, false); } var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null; if (listener && listener.mStateFlags) { this._callProgressListeners(null, "onUpdateCurrentBrowser", [listener.mStateFlags, listener.mStatus, listener.mMessage, listener.mTotalProgress], true, false); } if (!this._previewMode) { this.mCurrentTab.removeAttribute("unread"); this.selectedTab.lastAccessed = Date.now(); let oldFindBar = oldTab._findBar; if (oldFindBar && oldFindBar.findMode == oldFindBar.FIND_NORMAL && !oldFindBar.hidden) this._lastFindValue = oldFindBar._findField.value; this.updateTitlebar(); this.mCurrentTab.removeAttribute("titlechanged"); } // 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); this._tabAttrModified(this.mCurrentTab); // Adjust focus oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused); do { // When 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(); break; } // If there's a tabmodal prompt showing, focus it. if (newBrowser.hasAttribute("tabmodalPromptShowing")) { let XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; let prompts = newBrowser.parentNode.getElementsByTagNameNS(XUL_NS, "tabmodalprompt"); let prompt = prompts[prompts.length - 1]; prompt.Dialog.setDefaultFocus(); break; } // Focus the location bar if it was previously focused for that tab. // In full screen mode, only bother making the location bar visible // if the tab is a blank one. if (newBrowser._urlbarFocused && gURLBar) { // Explicitly close the popup if the URL bar retains focus gURLBar.closePopup(); if (!window.fullScreen) { gURLBar.focus(); break; } else if (isTabEmpty(this.mCurrentTab)) { focusAndSelectUrlBar(); break; } } // If the find bar is focused, keep it focused. if (gFindBarInitialized && !gFindBar.hidden && gFindBar.getElement("findbar-textbox").getAttribute("focused") == "true") break; // Otherwise, focus the content area. let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); let focusFlags = fm.FLAG_NOSCROLL; if (!gMultiProcessBrowser) { let newFocusedElement = fm.getFocusedElementForWindow(window.content, true, {}); // for anchors, use FLAG_SHOWRING so that it is clear what link was // last clicked when switching back to that tab if (newFocusedElement && (newFocusedElement instanceof HTMLAnchorElement || newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) focusFlags |= fm.FLAG_SHOWRING; } fm.setFocus(newBrowser, focusFlags); } while (false); } this.tabContainer._setPositionalAttributes(); ]]> 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(); } ]]> is inserted // into the DOM. We thus have to register the new outerWindowID // for non-remote browsers after we have called browser.loadURI(). // // Note: Only do this of we still have a docShell. The TabOpen // event was dispatched above and a gBrowser.removeTab() call from // one of its listeners could cause us to fail here. if (Services.prefs.getPrefType("browser.tabs.remote") == Services.prefs.PREF_BOOL && !Services.prefs.getBoolPref("browser.tabs.remote") && b.docShell) { this._outerWindowIDBrowserMap.set(b.outerWindowID, b); } // 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 == null ? aReferrerURI : aRelatedToCurrent) && Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) { let newTabPos = (this._lastRelatedTab || this.selectedTab)._tPos + 1; if (this._lastRelatedTab) this._lastRelatedTab.owner = null; else t.owner = this.selectedTab; this.moveTabTo(t, newTabPos); this._lastRelatedTab = t; } if (animate) { mozRequestAnimationFrame(function () { this.tabContainer._handleTabTelemetryStart(t, aURI); // kick the animation off t.setAttribute("fadein", "true"); // This call to adjustTabstrip is redundant but needed so that // when opening a second tab, the first tab's close buttons // appears immediately rather than when the transition ends. if (this.tabs.length - this._removingTabs.length == 2) this.tabContainer.adjustTabstrip(); }.bind(this)); } return t; ]]> = 0; --i) { tabsToEnd.push(tabs[i]); } return tabsToEnd.reverse(); ]]> = 0; --i) { this.removeTab(tabs[i], {animate: true}); } } ]]> = 0; --i) { if (tabs[i] != aTab && !tabs[i].pinned) this.removeTab(tabs[i]); } } ]]> [] 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"); if (this.tabs.length - this._removingTabs.length == 1) { // The second tab just got closed and we will end up with a single // one. Remove the first tab's close button immediately (if needed) // rather than after the tabclose animation ends. this.tabContainer.adjustTabstrip(); } 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 1) { // let newPos = this.selectedTab._tPos - 1; // this.selectedTab = this.tabs[newPos]; // } //} // update tab positional properties and attributes this.selectedTab._selected = true; this.tabContainer._setPositionalAttributes(); // Removing the panel requires fixing up selectedPanel immediately // (see below), which would be hindered by the potentially expensive // browser removal. So we remove the browser and the panel in two // steps. var panel = this.getNotificationBox(browser); // This will unload the document. An unload handler could remove // dependant tabs, so it's important that the tabbrowser is now in // a consistent state (tab removed, tab positions updated, etc.). browser.parentNode.removeChild(browser); // Release the browser in case something is erroneously holding a // reference to the tab after its removal. this._tabForBrowser.delete(aTab.linkedBrowser); aTab.linkedBrowser = null; // As the browser is removed, the removal of a dependent document can // cause the whole window to close. So at this point, it's possible // that the binding is destructed. if (this.mTabBox) { let selectedPanel = this.mTabBox.selectedPanel; this.mPanelContainer.removeChild(panel); // Under the hood, a selectedIndex attribute controls which panel // is displayed. Removing a panel A which precedes the selected // panel B makes selectedIndex point to the panel next to B. We // need to explicitly preserve B as the selected panel. this.mTabBox.selectedPanel = selectedPanel; } if (aCloseWindow) this._windowIsClosing = closeWindow(true, window.warnAboutClosingWindow); ]]> this.mTabsProgressListeners.push(aListener); = 0 && aIndex < tabs.length) this.selectedTab = tabs[aIndex]; if (aEvent) { aEvent.preventDefault(); aEvent.stopPropagation(); } ]]> return this.mCurrentTab; null 0) this.moveTabTo(this.mCurrentTab, 0); ]]> null false { if (this.browsers[0].contentWindow == subject) { Services.obs.removeObserver(obs, topic); this._addProgressListenerForInitialTab(); } }; // We use content-document-global-created as an approximation for // "docShell is initialized". We can do this because in the // mTabProgressListener we care most about the STATE_STOP notification // that will reset mBlank. That means it's important to at least add // the progress listener before the initial about:blank load stops // if we can't do it before the load starts. Services.obs.addObserver(obs, "content-document-global-created", false); ]]> Application.console.log("enterTabbedMode is an obsolete method and " + "will be removed in a future release."); true this.tabContainer.visible = aShow; return this.tabContainer.visible; document.getElementById(this.getAttribute("tabbrowser")); this.tabbrowser.mTabBox; document.getElementById("tabContextMenu"); 0 document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox"); null null null null null false document.getAnonymousElementByAttribute(this, "anonid", "tab-drop-indicator"); 350 0 false this.mTabClipWidth) this.setAttribute("closebuttons", "alltabs"); else this.setAttribute("closebuttons", "activetab"); } break; case 2: case 3: this.setAttribute("closebuttons", "never"); break; } var tabstripClosebutton = document.getElementById("tabs-closebutton"); if (tabstripClosebutton && tabstripClosebutton.parentNode == this._container) tabstripClosebutton.collapsed = this.mCloseButtons != 3; ]]> 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") { if (this.AppConstants.platform == "win") { // Don't need to do anything if we're in overflow mode and we're closing // the last tab. if (isEndTab) return; } else { // 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; // Resize immediately if preffed // XXX: we may want to make this a three-state pref to disable this early // exit if people prefer a mix of behavior (don't resize in overflow, // but resize if not overflowing) if (Services.prefs.getBoolPref("browser.tabs.resize_immediately")) 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; // Resize immediately if preffed // XXX: we may want to make this a three-state pref to disable this early // exit if people prefer a mix of behavior (don't resize in overflow, // but resize if not overflowing) if (Services.prefs.getBoolPref("browser.tabs.resize_immediately")) 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.MozMarginStart = - (width + scrollButtonWidth + paddingStart) + "px"; } this.style.MozPaddingStart = width + paddingStart + "px"; } else { this.removeAttribute("positionpinnedtabs"); for (let i = 0; i < numPinned; i++) { let tab = this.childNodes[i]; tab.style.MozMarginStart = ""; } this.style.MozPaddingStart = ""; } 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 dt.effectAllowed = "none"; var types = dt.mozTypesAt(0); var sourceNode = null; // tabs are always added as the first type if (types[0] == TAB_DROP_TYPE) { var 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 dt.effectAllowed = "none"; let copyModifier = this.tabbrowser.AppConstants.platform == "macosx" ? event.altKey : event.ctrlKey; return dt.effectAllowed = copyModifier ? "copy" : "move"; } } if (browserDragAndDrop.canDropLink(event)) { // Here we need to do this manually return dt.effectAllowed = dt.dropEffect = "link"; } return dt.effectAllowed = "none"; ]]> 0) { let averageInterval = 0; let averagePaint = paints[0]; for (let i = 1; i < frameCount; i++) { averageInterval += intervals[i]; averagePaint += paints[i]; }; averagePaint /= frameCount; averageInterval = (frameCount == 1) ? averagePaint : averageInterval / (frameCount - 1); if (aTab._recordingTabOpenPlain) { delete aTab._recordingTabOpenPlain; } } ]]> 1 || !this._closeWindowWithLastTab) this.tabbrowser.removeTab(event.target, {animate: true, byMouse: true}); } else if (event.originalTarget.localName == "box") { 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); 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.MozMarginStart = (-ind.clientWidth) + "px"; ]]> draggedTab._tPos) newIndex--; this.tabbrowser.moveTabTo(draggedTab, newIndex); } } else if (draggedTab) { // swap the dropped tab with a new one we create and then close // it in the other window (making it seem to have moved between // windows) let newIndex = this._getDropIndex(event); let newTab = this.tabbrowser.addTab("about:blank"); let newBrowser = this.tabbrowser.getBrowserForTab(newTab); // Stop the about:blank load newBrowser.stop(); // make sure it has a docshell newBrowser.docShell; let numPinned = this.tabbrowser._numPinnedTabs; if (newIndex < numPinned || draggedTab.pinned && newIndex == numPinned) this.tabbrowser.pinTab(newTab); this.tabbrowser.moveTabTo(newTab, newIndex); // We need to select the tab before calling swapBrowsersAndCloseOther // so that window.content in chrome windows points to the right tab // when pagehide/show events are fired. this.tabbrowser.selectedTab = newTab; draggedTab.parentNode._finishAnimateTabMove(); this.tabbrowser.swapBrowsersAndCloseOther(newTab, draggedTab); // Call updateCurrentBrowser to make sure the URL bar is up to date // for our new tab after we've done swapBrowsersAndCloseOther. this.tabbrowser.updateCurrentBrowser(true); } else { // Pass true to disallow dropping javascript: or data: urls let url; try { url = browserDragAndDrop.drop(event, { }, true); } catch (ex) {} // // valid urls don't contain spaces ' '; if we have a space it isn't a valid url. // if (!url || url.includes(" ")) //PMed if (!url) //FF return; let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground"); if (event.shiftKey) bgLoad = !bgLoad; let tab = this._getDragTargetTab(event); if (!tab || dropEffect == "copy") { // We're adding a new tab. let newIndex = this._getDropIndex(event); let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad, allowThirdPartyFixup: true}); 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 sX = {}, sY = {}, sWidth = {}, sHeight = {}; Cc["@mozilla.org/gfx/screenmanager;1"] .getService(Ci.nsIScreenManager) .screenForRect(eX, eY, 1, 1) .GetAvailRect(sX, sY, sWidth, sHeight); // ensure new window entirely within screen var winWidth = Math.min(window.outerWidth, sWidth.value); var winHeight = Math.min(window.outerHeight, sHeight.value); var left = Math.min(Math.max(eX - draggedTab._dragData.offsetX, sX.value), sX.value + sWidth.value - winWidth); var top = Math.min(Math.max(eY - draggedTab._dragData.offsetY, sY.value), sY.value + sHeight.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(); ]]> 1 && !this._ignoredClick) { this._ignoredClick = true; return; } // Reset the "ignored click" flag this._ignoredClick = false; tabContainer.tabbrowser.removeTab(bindingParent, {animate: true, byMouse: true}); tabContainer._blockDblClick = true; /* XXXmano hack (see bug 343628): * Since we're removing the event target, if the user * double-clicks this 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 dblclick handler). */ var clickedOnce = false; function enableDblClick(event) { var target = event.originalTarget; if (target.className == 'tab-close-button') target._ignoredClick = true; if (!clickedOnce) { clickedOnce = true; return; } tabContainer._blockDblClick = false; tabContainer.removeEventListener("click", enableDblClick, true); } tabContainer.addEventListener("click", enableDblClick, true); ]]> // for the one-close-button case event.stopPropagation(); event.stopPropagation(); return this.getAttribute("pinned") == "true"; return this.getAttribute("hidden") == "true"; false null false 0 this.style.MozUserFocus = ''; this.style.MozUserFocus = ''; = tabstripBO.screenX && curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width) this.childNodes[i].setAttribute("tabIsVisible", "true"); else this.childNodes[i].removeAttribute("tabIsVisible"); } ]]> = 0; i--) { let menuItem = this.childNodes[i]; if (menuItem.tab) { menuItem.tab.mCorrespondingMenuitem = null; this.removeChild(menuItem); } } var tabcontainer = gBrowser.tabContainer; tabcontainer.mTabstrip.removeEventListener("scroll", this, false); tabcontainer.removeEventListener("TabAttrModified", this, false); tabcontainer.removeEventListener("TabClose", this, false); ]]> 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(); }