/browser/base/content/browser.js
JavaScript | 8709 lines | 6273 code | 1198 blank | 1238 comment | 1302 complexity | 41fa898ee7e534e7a4216d876f5859bf MD5 | raw file
Possible License(s): AGPL-1.0, MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, 0BSD, LGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-2.0, JSON
Large files files are truncated, but you can click here to view the full file
- # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- # ***** BEGIN LICENSE BLOCK *****
- # Version: MPL 1.1/GPL 2.0/LGPL 2.1
- #
- # The contents of this file are subject to the Mozilla Public License Version
- # 1.1 (the "License"); you may not use this file except in compliance with
- # the License. You may obtain a copy of the License at
- # http://www.mozilla.org/MPL/
- #
- # Software distributed under the License is distributed on an "AS IS" basis,
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- # for the specific language governing rights and limitations under the
- # License.
- #
- # The Original Code is mozilla.org code.
- #
- # The Initial Developer of the Original Code is
- # Netscape Communications Corporation.
- # Portions created by the Initial Developer are Copyright (C) 1998
- # the Initial Developer. All Rights Reserved.
- #
- # Contributor(s):
- # Blake Ross <blake@cs.stanford.edu>
- # David Hyatt <hyatt@mozilla.org>
- # Peter Annema <disttsc@bart.nl>
- # Dean Tessman <dean_tessman@hotmail.com>
- # Kevin Puetz <puetzk@iastate.edu>
- # Ben Goodger <ben@netscape.com>
- # Pierre Chanial <chanial@noos.fr>
- # Jason Eager <jce2@po.cwru.edu>
- # Joe Hewitt <hewitt@netscape.com>
- # Alec Flett <alecf@netscape.com>
- # Asaf Romano <mozilla.mano@sent.com>
- # Jason Barnabe <jason_barnabe@fastmail.fm>
- # Peter Parente <parente@cs.unc.edu>
- # Giorgio Maone <g.maone@informaction.com>
- # Tom Germeau <tom.germeau@epigoon.com>
- # Jesse Ruderman <jruderman@gmail.com>
- # Joe Hughes <joe@retrovirus.com>
- # Pamela Greene <pamg.bugs@gmail.com>
- # Michael Ventnor <m.ventnor@gmail.com>
- # Simon Bünzli <zeniko@gmail.com>
- # Johnathan Nightingale <johnath@mozilla.com>
- # Ehsan Akhgari <ehsan.akhgari@gmail.com>
- # Dão Gottwald <dao@mozilla.com>
- # Thomas K. Dyas <tdyas@zecador.org>
- # Edward Lee <edward.lee@engineering.uiuc.edu>
- # Paul O’Shannessy <paul@oshannessy.com>
- # Nils Maier <maierman@web.de>
- # Rob Arnold <robarnold@cmu.edu>
- # Dietrich Ayala <dietrich@mozilla.com>
- # Gavin Sharp <gavin@gavinsharp.com>
- # Justin Dolske <dolske@mozilla.com>
- # Rob Campbell <rcampbell@mozilla.com>
- # David Dahl <ddahl@mozilla.com>
- # Patrick Walton <pcwalton@mozilla.com>
- # Mihai Sucan <mihai.sucan@gmail.com>
- #
- # Alternatively, the contents of this file may be used under the terms of
- # either the GNU General Public License Version 2 or later (the "GPL"), or
- # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- # in which case the provisions of the GPL or the LGPL are applicable instead
- # of those above. If you wish to allow use of your version of this file only
- # under the terms of either the GPL or the LGPL, and not to allow others to
- # use your version of this file under the terms of the MPL, indicate your
- # decision by deleting the provisions above and replace them with the notice
- # and other provisions required by the GPL or the LGPL. If you do not delete
- # the provisions above, a recipient may use your version of this file under
- # the terms of any one of the MPL, the GPL or the LGPL.
- #
- # ***** END LICENSE BLOCK *****
- let Ci = Components.interfaces;
- let Cu = Components.utils;
- Cu.import("resource://gre/modules/XPCOMUtils.jsm");
- const nsIWebNavigation = Ci.nsIWebNavigation;
- var gCharsetMenu = null;
- var gLastBrowserCharset = null;
- var gPrevCharset = null;
- var gProxyFavIcon = null;
- var gLastValidURLStr = "";
- var gInPrintPreviewMode = false;
- var gDownloadMgr = null;
- var gContextMenu = null; // nsContextMenu instance
- var gDelayedStartupTimeoutId;
- var gStartupRan = false;
- #ifndef XP_MACOSX
- var gEditUIVisible = true;
- #endif
- [
- ["gBrowser", "content"],
- ["gNavToolbox", "navigator-toolbox"],
- ["gURLBar", "urlbar"],
- ["gNavigatorBundle", "bundle_browser"]
- ].forEach(function (elementGlobal) {
- var [name, id] = elementGlobal;
- window.__defineGetter__(name, function () {
- var element = document.getElementById(id);
- if (!element)
- return null;
- delete window[name];
- return window[name] = element;
- });
- window.__defineSetter__(name, function (val) {
- delete window[name];
- return window[name] = val;
- });
- });
- // Smart getter for the findbar. If you don't wish to force the creation of
- // the findbar, check gFindBarInitialized first.
- var gFindBarInitialized = false;
- XPCOMUtils.defineLazyGetter(window, "gFindBar", function() {
- let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
- let findbar = document.createElementNS(XULNS, "findbar");
- findbar.id = "FindToolbar";
- let browserBottomBox = document.getElementById("browser-bottombox");
- browserBottomBox.insertBefore(findbar, browserBottomBox.firstChild);
- // Force a style flush to ensure that our binding is attached.
- findbar.clientTop;
- findbar.browser = gBrowser;
- window.gFindBarInitialized = true;
- return findbar;
- });
- __defineGetter__("gPrefService", function() {
- delete this.gPrefService;
- return this.gPrefService = Services.prefs;
- });
- __defineGetter__("AddonManager", function() {
- Cu.import("resource://gre/modules/AddonManager.jsm");
- return this.AddonManager;
- });
- __defineSetter__("AddonManager", function (val) {
- delete this.AddonManager;
- return this.AddonManager = val;
- });
- __defineGetter__("PluralForm", function() {
- Cu.import("resource://gre/modules/PluralForm.jsm");
- return this.PluralForm;
- });
- __defineSetter__("PluralForm", function (val) {
- delete this.PluralForm;
- return this.PluralForm = val;
- });
- #ifdef MOZ_SERVICES_SYNC
- XPCOMUtils.defineLazyGetter(this, "Weave", function() {
- let tmp = {};
- Cu.import("resource://services-sync/main.js", tmp);
- return tmp.Weave;
- });
- #endif
- XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
- let tmp = {};
- Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
- try {
- return new tmp.PopupNotifications(gBrowser,
- document.getElementById("notification-popup"),
- document.getElementById("notification-popup-box"));
- } catch (ex) {
- Cu.reportError(ex);
- }
- });
- let gInitialPages = [
- "about:blank",
- "about:privatebrowsing",
- "about:sessionrestore"
- ];
- #include browser-fullZoom.js
- #include inspector.js
- #include browser-places.js
- #include browser-tabPreviews.js
- #include browser-tabview.js
- #ifdef MOZ_SERVICES_SYNC
- #include browser-syncui.js
- #endif
- XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
- #ifdef XP_WIN
- const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
- if (WINTASKBAR_CONTRACTID in Cc &&
- Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
- let temp = {};
- Cu.import("resource://gre/modules/WindowsPreviewPerTab.jsm", temp);
- let AeroPeek = temp.AeroPeek;
- return {
- onOpenWindow: function () {
- AeroPeek.onOpenWindow(window);
- },
- onCloseWindow: function () {
- AeroPeek.onCloseWindow(window);
- }
- };
- }
- #endif
- return null;
- });
- #ifdef MOZ_CRASHREPORTER
- XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
- "@mozilla.org/xre/app-info;1",
- "nsICrashReporter");
- #endif
- XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
- let tmp = {};
- Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
- return new tmp.PageMenu();
- });
- /**
- * We can avoid adding multiple load event listeners and save some time by adding
- * one listener that calls all real handlers.
- */
- function pageShowEventHandlers(event) {
- // Filter out events that are not about the document load we are interested in
- if (event.originalTarget == content.document) {
- charsetLoadListener(event);
- XULBrowserWindow.asyncUpdateUI();
- }
- }
- function UpdateBackForwardCommands(aWebNavigation) {
- var backBroadcaster = document.getElementById("Browser:Back");
- var forwardBroadcaster = document.getElementById("Browser:Forward");
- // Avoid setting attributes on broadcasters if the value hasn't changed!
- // Remember, guys, setting attributes on elements is expensive! They
- // get inherited into anonymous content, broadcast to other widgets, etc.!
- // Don't do it if the value hasn't changed! - dwh
- var backDisabled = backBroadcaster.hasAttribute("disabled");
- var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
- if (backDisabled == aWebNavigation.canGoBack) {
- if (backDisabled)
- backBroadcaster.removeAttribute("disabled");
- else
- backBroadcaster.setAttribute("disabled", true);
- }
- if (forwardDisabled == aWebNavigation.canGoForward) {
- if (forwardDisabled)
- forwardBroadcaster.removeAttribute("disabled");
- else
- forwardBroadcaster.setAttribute("disabled", true);
- }
- }
- /**
- * Click-and-Hold implementation for the Back and Forward buttons
- * XXXmano: should this live in toolbarbutton.xml?
- */
- function SetClickAndHoldHandlers() {
- var timer;
- function openMenu(aButton) {
- cancelHold(aButton);
- aButton.firstChild.hidden = false;
- aButton.open = true;
- }
- function mousedownHandler(aEvent) {
- if (aEvent.button != 0 ||
- aEvent.currentTarget.open ||
- aEvent.currentTarget.disabled)
- return;
- // Prevent the menupopup from opening immediately
- aEvent.currentTarget.firstChild.hidden = true;
- aEvent.currentTarget.addEventListener("mouseout", mouseoutHandler, false);
- aEvent.currentTarget.addEventListener("mouseup", mouseupHandler, false);
- timer = setTimeout(openMenu, 500, aEvent.currentTarget);
- }
- function mouseoutHandler(aEvent) {
- let buttonRect = aEvent.currentTarget.getBoundingClientRect();
- if (aEvent.clientX >= buttonRect.left &&
- aEvent.clientX <= buttonRect.right &&
- aEvent.clientY >= buttonRect.bottom)
- openMenu(aEvent.currentTarget);
- else
- cancelHold(aEvent.currentTarget);
- }
- function mouseupHandler(aEvent) {
- cancelHold(aEvent.currentTarget);
- }
- function cancelHold(aButton) {
- clearTimeout(timer);
- aButton.removeEventListener("mouseout", mouseoutHandler, false);
- aButton.removeEventListener("mouseup", mouseupHandler, false);
- }
- function clickHandler(aEvent) {
- if (aEvent.button == 0 &&
- aEvent.target == aEvent.currentTarget &&
- !aEvent.currentTarget.open &&
- !aEvent.currentTarget.disabled) {
- let cmdEvent = document.createEvent("xulcommandevent");
- cmdEvent.initCommandEvent("command", true, true, window, 0,
- aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
- aEvent.metaKey, null);
- aEvent.currentTarget.dispatchEvent(cmdEvent);
- }
- }
- function _addClickAndHoldListenersOnElement(aElm) {
- aElm.addEventListener("mousedown", mousedownHandler, true);
- aElm.addEventListener("click", clickHandler, true);
- }
- // Bug 414797: Clone unified-back-forward-button's context menu into both the
- // back and the forward buttons.
- var unifiedButton = document.getElementById("unified-back-forward-button");
- if (unifiedButton && !unifiedButton._clickHandlersAttached) {
- unifiedButton._clickHandlersAttached = true;
- let popup = document.getElementById("backForwardMenu").cloneNode(true);
- popup.removeAttribute("id");
- // Prevent the context attribute on unified-back-forward-button from being
- // inherited.
- popup.setAttribute("context", "");
- let backButton = document.getElementById("back-button");
- backButton.setAttribute("type", "menu");
- backButton.appendChild(popup);
- _addClickAndHoldListenersOnElement(backButton);
- let forwardButton = document.getElementById("forward-button");
- popup = popup.cloneNode(true);
- forwardButton.setAttribute("type", "menu");
- forwardButton.appendChild(popup);
- _addClickAndHoldListenersOnElement(forwardButton);
- }
- }
- const gSessionHistoryObserver = {
- observe: function(subject, topic, data)
- {
- if (topic != "browser:purge-session-history")
- return;
- var backCommand = document.getElementById("Browser:Back");
- backCommand.setAttribute("disabled", "true");
- var fwdCommand = document.getElementById("Browser:Forward");
- fwdCommand.setAttribute("disabled", "true");
- // Hide session restore button on about:home
- window.messageManager.sendAsyncMessage("Browser:HideSessionRestoreButton");
- if (gURLBar) {
- // Clear undo history of the URL bar
- gURLBar.editor.transactionManager.clear()
- }
- }
- };
- /**
- * Given a starting docshell and a URI to look up, find the docshell the URI
- * is loaded in.
- * @param aDocument
- * A document to find instead of using just a URI - this is more specific.
- * @param aDocShell
- * The doc shell to start at
- * @param aSoughtURI
- * The URI that we're looking for
- * @returns The doc shell that the sought URI is loaded in. Can be in
- * subframes.
- */
- function findChildShell(aDocument, aDocShell, aSoughtURI) {
- aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation);
- aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
- var doc = aDocShell.getInterface(Components.interfaces.nsIDOMDocument);
- if ((aDocument && doc == aDocument) ||
- (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
- return aDocShell;
- var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
- for (var i = 0; i < node.childCount; ++i) {
- var docShell = node.getChildAt(i);
- docShell = findChildShell(aDocument, docShell, aSoughtURI);
- if (docShell)
- return docShell;
- }
- return null;
- }
- var gPopupBlockerObserver = {
- _reportButton: null,
-
- onReportButtonClick: function (aEvent)
- {
- if (aEvent.button != 0 || aEvent.target != this._reportButton)
- return;
- document.getElementById("blockedPopupOptions")
- .openPopup(this._reportButton, "after_end", 0, 2, false, false, aEvent);
- },
- handleEvent: function (aEvent)
- {
- if (aEvent.originalTarget != gBrowser.selectedBrowser)
- return;
- if (!this._reportButton && gURLBar)
- this._reportButton = document.getElementById("page-report-button");
- if (!gBrowser.pageReport) {
- // Hide the icon in the location bar (if the location bar exists)
- if (gURLBar)
- this._reportButton.hidden = true;
- return;
- }
- if (gURLBar)
- this._reportButton.hidden = false;
- // Only show the notification again if we've not already shown it. Since
- // notifications are per-browser, we don't need to worry about re-adding
- // it.
- if (!gBrowser.pageReport.reported) {
- if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
- var brandBundle = document.getElementById("bundle_brand");
- var brandShortName = brandBundle.getString("brandShortName");
- var message;
- var popupCount = gBrowser.pageReport.length;
- #ifdef XP_WIN
- var popupButtonText = gNavigatorBundle.getString("popupWarningButton");
- var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButton.accesskey");
- #else
- var popupButtonText = gNavigatorBundle.getString("popupWarningButtonUnix");
- var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButtonUnix.accesskey");
- #endif
- if (popupCount > 1)
- message = gNavigatorBundle.getFormattedString("popupWarningMultiple", [brandShortName, popupCount]);
- else
- message = gNavigatorBundle.getFormattedString("popupWarning", [brandShortName]);
- var notificationBox = gBrowser.getNotificationBox();
- var notification = notificationBox.getNotificationWithValue("popup-blocked");
- if (notification) {
- notification.label = message;
- }
- else {
- var buttons = [{
- label: popupButtonText,
- accessKey: popupButtonAccesskey,
- popup: "blockedPopupOptions",
- callback: null
- }];
- const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
- notificationBox.appendNotification(message, "popup-blocked",
- "chrome://browser/skin/Info.png",
- priority, buttons);
- }
- }
- // Record the fact that we've reported this blocked popup, so we don't
- // show it again.
- gBrowser.pageReport.reported = true;
- }
- },
- toggleAllowPopupsForSite: function (aEvent)
- {
- var pm = Services.perms;
- var shouldBlock = aEvent.target.getAttribute("block") == "true";
- var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
- pm.add(gBrowser.currentURI, "popup", perm);
- gBrowser.getNotificationBox().removeCurrentNotification();
- },
- fillPopupList: function (aEvent)
- {
- // XXXben - rather than using |currentURI| here, which breaks down on multi-framed sites
- // we should really walk the pageReport and create a list of "allow for <host>"
- // menuitems for the common subset of hosts present in the report, this will
- // make us frame-safe.
- //
- // XXXjst - Note that when this is fixed to work with multi-framed sites,
- // also back out the fix for bug 343772 where
- // nsGlobalWindow::CheckOpenAllow() was changed to also
- // check if the top window's location is whitelisted.
- var uri = gBrowser.currentURI;
- var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
- try {
- blockedPopupAllowSite.removeAttribute("hidden");
- var pm = Services.perms;
- if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
- // Offer an item to block popups for this site, if a whitelist entry exists
- // already for it.
- let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host]);
- blockedPopupAllowSite.setAttribute("label", blockString);
- blockedPopupAllowSite.setAttribute("block", "true");
- }
- else {
- // Offer an item to allow popups for this site
- let allowString = gNavigatorBundle.getFormattedString("popupAllow", [uri.host]);
- blockedPopupAllowSite.setAttribute("label", allowString);
- blockedPopupAllowSite.removeAttribute("block");
- }
- }
- catch (e) {
- blockedPopupAllowSite.setAttribute("hidden", "true");
- }
- if (gPrivateBrowsingUI.privateBrowsingEnabled)
- blockedPopupAllowSite.setAttribute("disabled", "true");
- else
- blockedPopupAllowSite.removeAttribute("disabled");
- var foundUsablePopupURI = false;
- var pageReport = gBrowser.pageReport;
- if (pageReport) {
- for (var i = 0; i < pageReport.length; ++i) {
- // popupWindowURI will be null if the file picker popup is blocked.
- // xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
- if (!pageReport[i].popupWindowURI)
- continue;
- var popupURIspec = pageReport[i].popupWindowURI.spec;
- // Sometimes the popup URI that we get back from the pageReport
- // isn't useful (for instance, netscape.com's popup URI ends up
- // being "http://www.netscape.com", which isn't really the URI of
- // the popup they're trying to show). This isn't going to be
- // useful to the user, so we won't create a menu item for it.
- if (popupURIspec == "" || popupURIspec == "about:blank" ||
- popupURIspec == uri.spec)
- continue;
- // Because of the short-circuit above, we may end up in a situation
- // in which we don't have any usable popup addresses to show in
- // the menu, and therefore we shouldn't show the separator. However,
- // since we got past the short-circuit, we must've found at least
- // one usable popup URI and thus we'll turn on the separator later.
- foundUsablePopupURI = true;
- var menuitem = document.createElement("menuitem");
- var label = gNavigatorBundle.getFormattedString("popupShowPopupPrefix",
- [popupURIspec]);
- menuitem.setAttribute("label", label);
- menuitem.setAttribute("popupWindowURI", popupURIspec);
- menuitem.setAttribute("popupWindowFeatures", pageReport[i].popupWindowFeatures);
- menuitem.setAttribute("popupWindowName", pageReport[i].popupWindowName);
- menuitem.setAttribute("oncommand", "gPopupBlockerObserver.showBlockedPopup(event);");
- menuitem.requestingWindow = pageReport[i].requestingWindow;
- menuitem.requestingDocument = pageReport[i].requestingDocument;
- aEvent.target.appendChild(menuitem);
- }
- }
- // Show or hide the separator, depending on whether we added any
- // showable popup addresses to the menu.
- var blockedPopupsSeparator =
- document.getElementById("blockedPopupsSeparator");
- if (foundUsablePopupURI)
- blockedPopupsSeparator.removeAttribute("hidden");
- else
- blockedPopupsSeparator.setAttribute("hidden", true);
- var blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
- var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
- blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
- if (aEvent.target.anchorNode.id == "page-report-button") {
- aEvent.target.anchorNode.setAttribute("open", "true");
- blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromLocationbar"));
- } else
- blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
- },
- onPopupHiding: function (aEvent) {
- if (aEvent.target.anchorNode.id == "page-report-button")
- aEvent.target.anchorNode.removeAttribute("open");
- let item = aEvent.target.lastChild;
- while (item && item.getAttribute("observes") != "blockedPopupsSeparator") {
- let next = item.previousSibling;
- item.parentNode.removeChild(item);
- item = next;
- }
- },
- showBlockedPopup: function (aEvent)
- {
- var target = aEvent.target;
- var popupWindowURI = target.getAttribute("popupWindowURI");
- var features = target.getAttribute("popupWindowFeatures");
- var name = target.getAttribute("popupWindowName");
- var dwi = target.requestingWindow;
- // If we have a requesting window and the requesting document is
- // still the current document, open the popup.
- if (dwi && dwi.document == target.requestingDocument) {
- dwi.open(popupWindowURI, name, features);
- }
- },
- editPopupSettings: function ()
- {
- var host = "";
- try {
- host = gBrowser.currentURI.host;
- }
- catch (e) { }
- var bundlePreferences = document.getElementById("bundle_preferences");
- var params = { blockVisible : false,
- sessionVisible : false,
- allowVisible : true,
- prefilledHost : host,
- permissionType : "popup",
- windowTitle : bundlePreferences.getString("popuppermissionstitle"),
- introText : bundlePreferences.getString("popuppermissionstext") };
- var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
- if (existingWindow) {
- existingWindow.initWithParams(params);
- existingWindow.focus();
- }
- else
- window.openDialog("chrome://browser/content/preferences/permissions.xul",
- "_blank", "resizable,dialog=no,centerscreen", params);
- },
- dontShowMessage: function ()
- {
- var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
- gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
- gBrowser.getNotificationBox().removeCurrentNotification();
- }
- };
- const gXPInstallObserver = {
- _findChildShell: function (aDocShell, aSoughtShell)
- {
- if (aDocShell == aSoughtShell)
- return aDocShell;
- var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
- for (var i = 0; i < node.childCount; ++i) {
- var docShell = node.getChildAt(i);
- docShell = this._findChildShell(docShell, aSoughtShell);
- if (docShell == aSoughtShell)
- return docShell;
- }
- return null;
- },
- _getBrowser: function (aDocShell)
- {
- for (var i = 0; i < gBrowser.browsers.length; ++i) {
- var browser = gBrowser.getBrowserAtIndex(i);
- if (this._findChildShell(browser.docShell, aDocShell))
- return browser;
- }
- return null;
- },
- observe: function (aSubject, aTopic, aData)
- {
- var brandBundle = document.getElementById("bundle_brand");
- var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
- var win = installInfo.originatingWindow;
- var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
- .getInterface(Components.interfaces.nsIWebNavigation)
- .QueryInterface(Components.interfaces.nsIDocShell);
- var browser = this._getBrowser(shell);
- if (!browser)
- return;
- const anchorID = "addons-notification-icon";
- var messageString, action;
- var brandShortName = brandBundle.getString("brandShortName");
- var notificationID = aTopic;
- // Make notifications persist a minimum of 30 seconds
- var options = {
- timeout: Date.now() + 30000
- };
- switch (aTopic) {
- case "addon-install-disabled":
- notificationID = "xpinstall-disabled"
- if (gPrefService.prefIsLocked("xpinstall.enabled")) {
- messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
- buttons = [];
- }
- else {
- messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
- action = {
- label: gNavigatorBundle.getString("xpinstallDisabledButton"),
- accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
- callback: function editPrefs() {
- gPrefService.setBoolPref("xpinstall.enabled", true);
- }
- };
- }
- PopupNotifications.show(browser, notificationID, messageString, anchorID,
- action, null, options);
- break;
- case "addon-install-blocked":
- messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
- [brandShortName, installInfo.originatingURI.host]);
- action = {
- label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
- accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
- callback: function() {
- installInfo.install();
- }
- };
- PopupNotifications.show(browser, notificationID, messageString, anchorID,
- action, null, options);
- break;
- case "addon-install-started":
- function needsDownload(aInstall) {
- return aInstall.state != AddonManager.STATE_DOWNLOADED;
- }
- // If all installs have already been downloaded then there is no need to
- // show the download progress
- if (!installInfo.installs.some(needsDownload))
- return;
- notificationID = "addon-progress";
- messageString = gNavigatorBundle.getString("addonDownloading");
- messageString = PluralForm.get(installInfo.installs.length, messageString);
- options.installs = installInfo.installs;
- options.contentWindow = browser.contentWindow;
- options.sourceURI = browser.currentURI;
- options.eventCallback = function(aEvent) {
- if (aEvent != "removed")
- return;
- options.contentWindow = null;
- options.sourceURI = null;
- };
- PopupNotifications.show(browser, notificationID, messageString, anchorID,
- null, null, options);
- break;
- case "addon-install-failed":
- // TODO This isn't terribly ideal for the multiple failure case
- installInfo.installs.forEach(function(aInstall) {
- var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
- installInfo.originatingURI.host;
- if (!host)
- host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
- aInstall.sourceURI.host;
- var error = (host || aInstall.error == 0) ? "addonError" : "addonLocalError";
- if (aInstall.error != 0)
- error += aInstall.error;
- else if (aInstall.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
- error += "Blocklisted";
- else
- error += "Incompatible";
- messageString = gNavigatorBundle.getString(error);
- messageString = messageString.replace("#1", aInstall.name);
- if (host)
- messageString = messageString.replace("#2", host);
- messageString = messageString.replace("#3", brandShortName);
- messageString = messageString.replace("#4", Services.appinfo.version);
- PopupNotifications.show(browser, notificationID, messageString, anchorID,
- action, null, options);
- });
- break;
- case "addon-install-complete":
- var needsRestart = installInfo.installs.some(function(i) {
- return i.addon.pendingOperations != AddonManager.PENDING_NONE;
- });
- if (needsRestart) {
- messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
- action = {
- label: gNavigatorBundle.getString("addonInstallRestartButton"),
- accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
- callback: function() {
- Application.restart();
- }
- };
- }
- else {
- messageString = gNavigatorBundle.getString("addonsInstalled");
- action = {
- label: gNavigatorBundle.getString("addonInstallManage"),
- accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
- callback: function() {
- // Calculate the add-on type that is most popular in the list of
- // installs
- var types = {};
- var bestType = null;
- installInfo.installs.forEach(function(aInstall) {
- if (aInstall.type in types)
- types[aInstall.type]++;
- else
- types[aInstall.type] = 1;
- if (!bestType || types[aInstall.type] > types[bestType])
- bestType = aInstall.type;
- });
- BrowserOpenAddonsMgr("addons://list/" + bestType);
- }
- };
- }
- messageString = PluralForm.get(installInfo.installs.length, messageString);
- messageString = messageString.replace("#1", installInfo.installs[0].name);
- messageString = messageString.replace("#2", installInfo.installs.length);
- messageString = messageString.replace("#3", brandShortName);
- // Remove notificaion on dismissal, since it's possible to cancel the
- // install through the addons manager UI, making the "restart" prompt
- // irrelevant.
- options.removeOnDismissal = true;
- PopupNotifications.show(browser, notificationID, messageString, anchorID,
- action, null, options);
- break;
- }
- }
- };
- const gFormSubmitObserver = {
- QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
- panel: null,
- init: function()
- {
- this.panel = document.getElementById('invalid-form-popup');
- },
- panelIsOpen: function()
- {
- return this.panel && this.panel.state != "hiding" &&
- this.panel.state != "closed";
- },
- notifyInvalidSubmit : function (aFormElement, aInvalidElements)
- {
- // We are going to handle invalid form submission attempt by focusing the
- // first invalid element and show the corresponding validation message in a
- // panel attached to the element.
- if (!aInvalidElements.length) {
- return;
- }
- // Don't show the popup if the current tab doesn't contain the invalid form.
- if (gBrowser.contentDocument !=
- aFormElement.ownerDocument.defaultView.top.document) {
- return;
- }
- let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
- if (!(element instanceof HTMLInputElement ||
- element instanceof HTMLTextAreaElement ||
- element instanceof HTMLSelectElement ||
- element instanceof HTMLButtonElement)) {
- return;
- }
- this.panel.firstChild.textContent = element.validationMessage;
- element.focus();
- // If the user interacts with the element and makes it valid or leaves it,
- // we want to remove the popup.
- // We could check for clicks but a click is already removing the popup.
- function blurHandler() {
- gFormSubmitObserver.panel.hidePopup();
- };
- function inputHandler(e) {
- if (e.originalTarget.validity.valid) {
- gFormSubmitObserver.panel.hidePopup();
- } else {
- // If the element is now invalid for a new reason, we should update the
- // error message.
- if (gFormSubmitObserver.panel.firstChild.textContent !=
- e.originalTarget.validationMessage) {
- gFormSubmitObserver.panel.firstChild.textContent =
- e.originalTarget.validationMessage;
- }
- }
- };
- element.addEventListener("input", inputHandler, false);
- element.addEventListener("blur", blurHandler, false);
- // One event to bring them all and in the darkness bind them.
- this.panel.addEventListener("popuphiding", function(aEvent) {
- aEvent.target.removeEventListener("popuphiding", arguments.callee, false);
- element.removeEventListener("input", inputHandler, false);
- element.removeEventListener("blur", blurHandler, false);
- }, false);
- this.panel.hidden = false;
- // We want to show the popup at the middle of checkbox and radio buttons
- // and where the content begin for the other elements.
- let offset = 0;
- let position = "";
- if (element.tagName == 'INPUT' &&
- (element.type == 'radio' || element.type == 'checkbox')) {
- position = "bottomcenter topleft";
- } else {
- let win = element.ownerDocument.defaultView;
- let style = win.getComputedStyle(element, null);
- let utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
- .getInterface(Components.interfaces.nsIDOMWindowUtils);
- if (style.direction == 'rtl') {
- offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
- } else {
- offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
- }
- offset = Math.round(offset * utils.screenPixelsPerCSSPixel);
- position = "after_start";
- }
- this.panel.openPopup(element, position, offset, 0);
- }
- };
- // Simple gestures support
- //
- // As per bug #412486, web content must not be allowed to receive any
- // simple gesture events. Multi-touch gesture APIs are in their
- // infancy and we do NOT want to be forced into supporting an API that
- // will probably have to change in the future. (The current Mac OS X
- // API is undocumented and was reverse-engineered.) Until support is
- // implemented in the event dispatcher to keep these events as
- // chrome-only, we must listen for the simple gesture events during
- // the capturing phase and call stopPropagation on every event.
- let gGestureSupport = {
- /**
- * Add or remove mouse gesture event listeners
- *
- * @param aAddListener
- * True to add/init listeners and false to remove/uninit
- */
- init: function GS_init(aAddListener) {
- const gestureEvents = ["SwipeGesture",
- "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
- "RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
- "TapGesture", "PressTapGesture"];
- let addRemove = aAddListener ? window.addEventListener :
- window.removeEventListener;
- gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true),
- this);
- },
- /**
- * Dispatch events based on the type of mouse gesture event. For now, make
- * sure to stop propagation of every gesture event so that web content cannot
- * receive gesture events.
- *
- * @param aEvent
- * The gesture event to handle
- */
- handleEvent: function GS_handleEvent(aEvent) {
- aEvent.stopPropagation();
- // Create a preference object with some defaults
- let def = function(aThreshold, aLatched)
- ({ threshold: aThreshold, latched: !!aLatched });
- switch (aEvent.type) {
- case "MozSwipeGesture":
- aEvent.preventDefault();
- return this.onSwipe(aEvent);
- case "MozMagnifyGestureStart":
- aEvent.preventDefault();
- #ifdef XP_WIN
- return this._setupGesture(aEvent, "pinch", def(25, 0), "out", "in");
- #else
- return this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
- #endif
- case "MozRotateGestureStart":
- aEvent.preventDefault();
- return this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
- case "MozMagnifyGestureUpdate":
- case "MozRotateGestureUpdate":
- aEvent.preventDefault();
- return this._doUpdate(aEvent);
- case "MozTapGesture":
- aEvent.preventDefault();
- return this._doAction(aEvent, ["tap"]);
- case "MozPressTapGesture":
- // Fall through to default behavior
- return;
- }
- },
- /**
- * Called at the start of "pinch" and "twist" gestures to setup all of the
- * information needed to process the gesture
- *
- * @param aEvent
- * The continual motion start event to handle
- * @param aGesture
- * Name of the gesture to handle
- * @param aPref
- * Preference object with the names of preferences and defaults
- * @param aInc
- * Command to trigger for increasing motion (without gesture name)
- * @param aDec
- * Command to trigger for decreasing motion (without gesture name)
- */
- _setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
- // Try to load user-set values from preferences
- for (let [pref, def] in Iterator(aPref))
- aPref[pref] = this._getPref(aGesture + "." + pref, def);
- // Keep track of the total deltas and latching behavior
- let offset = 0;
- let latchDir = aEvent.delta > 0 ? 1 : -1;
- let isLatched = false;
- // Create the update function here to capture closure state
- this._doUpdate = function GS__doUpdate(aEvent) {
- // Update the offset with new event data
- offset += aEvent.delta;
- // Check if the cumulative deltas exceed the threshold
- if (Math.abs(offset) > aPref["threshold"]) {
- // Trigger the action if we don't care about latching; otherwise, make
- // sure either we're not latched and going the same direction of the
- // initial motion; or we're latched and going the opposite way
- let sameDir = (latchDir ^ offset) >= 0;
- if (!aPref["latched"] || (isLatched ^ sameDir)) {
- this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
- // We must be getting latched or leaving it, so just toggle
- isLatched = !isLatched;
- }
- // Reset motion counter to prepare for more of the same gesture
- offset = 0;
- }
- };
- // The start event also contains deltas, so handle an update right away
- this._doUpdate(aEvent);
- },
- /**
- * Generator producing the powerset of the input array where the first result
- * is the complete set and the last result (before StopIteration) is empty.
- *
- * @param aArray
- * Source array containing any number of elements
- * @yield Array that is a subset of the input array from full set to empty
- */
- _power: function GS__power(aArray) {
- // Create a bitmask based on the length of the array
- let num = 1 << aArray.length;
- while (--num >= 0) {
- // Only select array elements where the current bit is set
- yield aArray.reduce(function (aPrev, aCurr, aIndex) {
- if (num & 1 << aIndex)
- aPrev.push(aCurr);
- return aPrev;
- }, []);
- }
- },
- /**
- * Determine what action to do for the gesture based on which keys are
- * pressed and which commands are set
- *
- * @param aEvent
- * The original gesture event to convert into a fake click event
- * @param aGesture
- * Array of gesture name parts (to be joined by periods)
- * @return Name of the command found for the event's keys and gesture. If no
- * command is found, no value is returned (undefined).
- */
- _doAction: function GS__doAction(aEvent, aGesture) {
- // Create an array of pressed keys in a fixed order so that a command for
- // "meta" is preferred over "ctrl" when both buttons are pressed (and a
- // command for both don't exist)
- let keyCombos = [];
- ["shift", "alt", "ctrl", "meta"].forEach(function (key) {
- if (aEvent[key + "Key"])
- keyCombos.push(key);
- });
- // Try each combination of key presses in decreasing order for commands
- for each (let subCombo in this._power(keyCombos)) {
- // Convert a gesture and pressed keys into the corresponding command
- // action where the preference has the gesture before "shift" before
- // "alt" before "ctrl" before "meta" all separated by periods
- let command;
- try {
- command = this._getPref(aGesture.concat(subCombo).join("."));
- } catch (e) {}
- if (!command)
- continue;
- let node = document.getElementById(command);
- if (node) {
- if (node.getAttribute("disabled") != "true") {
- let cmdEvent = document.createEvent("xulcommandevent");
- cmdEvent.initCommandEvent("command", true, true, window, 0,
- aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
- aEvent.metaKey, null);
- node.dispatchEvent(cmdEvent);
- }
- } else {
- goDoCommand(command);
- }
- return command;
- }
- return null;
- },
- /**
- * Convert continual motion events into an action if it exceeds a threshold
- * in a given direction. This function will be set by _setupGesture to
- * capture state that needs to be shared across multiple gesture updates.
- *
- * @param aEvent
- * The continual motion update event to handle
- */
- _doUpdate: function(aEvent) {},
- /**
- * Convert the swipe gesture into a browser action based on the direction
- *
- * @param aEvent
- * The swipe event to handle
- */
- onSwipe: function GS_onSwipe(aEvent) {
- // Figure out which one (and only one) direction was triggered
- ["UP", "RIGHT", "DOWN", "LEFT"].forEach(function (dir) {
- if (aEvent.direction == aEvent["DIRECTION_" + dir])
- return this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
- }, this);
- },
- /**
- * Get a gesture preference or use a default if it doesn't exist
- *
- * @param aPref
- * Name of the preference to load under the gesture branch
- * @param aDef
- * Default value if the preference doesn't exist
- */
- _getPref: function GS__getPref(aPref, aDef) {
- // Preferences branch under which all gestures preferences are stored
- const branch = "browser.gesture.";
- try {
- // Determine what type of data to load based on default value's type
- let type = typeof aDef;
- let getFunc = "get" + (type == "boolean" ? "Bool" :
- type == "number" ? "Int" : "Char") + "Pref";
- return gPrefService[getFunc](branch + aPref);
- }
- catch (e) {
- return aDef;
- }
- },
- };
- function BrowserStartup() {
- var uriToLoad = null;
- // window.arguments[0]: URI to load (string), or an nsISupportsArray of
- // nsISupportsStrings to load, or a xul:tab of
- // a tabbrowser, which will be replaced by this
- // window (for this case, all other arguments are
- // ignored).
- // [1]: character set (string)
- // [2]: referrer (nsIURI)
- // [3]: postData (nsIInputStream)
- // [4]: allowThirdPartyFixup (bool)
- if ("arguments" in window && window.arguments[0])
- uriToLoad = window.arguments[0];
- var isLoadingBlank = uriToLoad == "about:blank";
- var mustLoadSidebar = false;
- prepareForStartup();
- if (uriToLoad && !isLoadingBlank) {
- if (uriToLoad instanceof Ci.nsISupportsArray) {
- let count = uriToLoad.Count();
- let specs = [];
- for (let i = 0; i < count; i++) {
- let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
- specs.push(urisstring.data);
- }
- // This function throws for certain malformed URIs, so use exception handling
- // so that we don't disrupt startup
- try {
- gBrowser.loadTabs(specs, false, true);
- } catch (e) {}
- }
- else if (uriToLoad instanceof XULElement) {
- // swap the given tab with the default about:blank tab and then close
- // the original tab in the other window.
- // Stop the about:blank load
- gBrowser.stop();
- // make sure it has a docshell
- gBrowser.docShell;
- gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
- }
- else if (window.arguments.length >= 3) {
- loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
- window.arguments[4] || false);
- content.focus();
- }
- // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
- // Such callers expect that window.arguments[0] is handled as a single URI.
- else
- loadOneOrMoreURIs(uriToLoad);
- }
- if (window.opener && !window.opener.closed) {
- let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
- // If the opener had a sidebar, open the same sidebar in our window.
- // The opener can be the hidden window too, if we're coming from the state
- // where no windows are open, and the hidden window has no sidebar box.
- if (openerSidebarBox && !openerSidebarBox.hidden) {
- let sidebarCmd = openerSidebarBox.getAttribute("sidebarcommand");
- let sidebarCmdElem = document.getElementById(sidebarCmd);
- // dynamically generated sidebars will fail this check.
- if (sidebarCmdElem) {
- let sidebarBox = document.getElementById("sidebar-box");
- let sidebarTitle = document.getElementById("sidebar-title");
- sidebarTitle.setAttribute(
- "value", window.opener.document.getElementById("sidebar-title").getAttribute("value"));
- sidebarBox.setAttribute("width", openerSidebarBox.boxObject.width);
- sidebarBox.setAttribute("sidebarcommand", sidebarCmd);
- // Note: we're setting 'src' on sidebarBox, which is a <vbox>, not on
- // the <browser id="sidebar">. This lets us delay the actual load until
- // delayedStartup().
- sidebarBox.setAttribute(
- "src", window.opener.document.getElementById("sidebar").getAttribute("src"));
- mustLoadSidebar = true;
- sidebarBox.hidden = false;
- document.getElementById("sidebar-splitter").hidden = false;
- sidebarCmdElem.setAttribute("checked", "true");
- }
- }
- }
- else {
- let box = document.getElementById("sidebar-box");
- if (box.hasAttribute("sidebarcommand")) {
- let commandID = box.getAttribute("sidebarcommand");
- if (commandID) {
- let command = document.getElementById(commandID);
- if (command) {
- mustLoadSidebar = true;
- box.hidden = false;
- document.getElementById("sidebar-splitter").hidden = false;
- command.setAttribute("checked", "true");
- }
- else {
- // Remove the |sidebarcommand| attribute, because the element it
- // refers to no longer exists, so we should assume this sidebar
- // panel has been uninstalled. (249883)
- box.removeAttribute("sidebarcommand");
- }
- }
- }
- }
- // Certain kinds of automigration rely on this notification to complete their
- // tasks BEFORE the browser window is shown.
- Services.obs.notifyObservers(null, "browser-window-before-show", "");
- // Set a sane starting width/height for all resolutions on new profiles.
- if (!document.documentElement.hasAttribute("width")) {
- let defaultWidth = 994;
- let defaultHeight;
- if (screen.availHeight <= 600) {
- document.documentElement.setAttribute("sizemode", "maximized");
- defaultWidth = 610;
- defaultHeight = 450;
- }
- else {
- // Create a narrower window for large or wide-aspect displays, to suggest
- // side-by-side page view.
- if (screen.availWidth >= 1600)
- defaultWidth = (screen.availWidth / 2) - 20;
- defaultHeight = screen.availHeight - 10;
- #ifdef MOZ_WIDGET_GTK2
- // On X, we're not currently able to account for the size of the window
- // border. Use 28px as a guess (titlebar + bottom window border)
- defaultHeight -= 28;
- #endif
- }
- document.documentElement.setAttribute("width", defaultWidth);
- document.documentElement.setAttribute("height", defaultHeight);
- }
- if (!gShowPageResizers)
- document.getElementById("status-bar").setAttribute("hideresizer", "true");
- if (!window.toolbar.visible) {
- // adjust browser UI for popups
- if (gURLBar) {
- gURLBar.setAttribute("readonly", "true");
- gURLBar.setAttribute("enablehistory", "false");
- }
- goSetCommandEnabled("Browser:OpenLocation", false);
- goSetCommandEnabled("cmd_newNavigatorTab", false);
- }
- #ifdef MENUBAR_CAN_AUTOHIDE
- updateAppButtonDisplay();
- #endif
- CombinedStopReload.init();
- allTabs.readPref();
- TabsOnTop.syncCommand();
- BookmarksMenuButton.init();
- TabsInTitlebar.init();
- gPrivateBrowsingUI.init();
- retrieveToolbarIconsizesFromTheme();
- gDelayedStartupTimeoutId = setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
- gStartupRan …
Large files files are truncated, but you can click here to view the full file