browser/base/content/browser.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2 # This Source Code Form is subject to the terms of the Mozilla Public
     3 # License, v. 2.0. If a copy of the MPL was not distributed with this
     4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6 let Ci = Components.interfaces;
     7 let Cu = Components.utils;
     9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    10 Cu.import("resource://gre/modules/NotificationDB.jsm");
    11 Cu.import("resource:///modules/RecentWindow.jsm");
    12 Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
    14 XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
    15                                   "resource://gre/modules/BrowserUtils.jsm");
    16 XPCOMUtils.defineLazyModuleGetter(this, "Task",
    17                                   "resource://gre/modules/Task.jsm");
    18 XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
    19                                   "resource://gre/modules/CharsetMenu.jsm");
    20 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
    21                                   "resource://gre/modules/ShortcutUtils.jsm");
    23 const nsIWebNavigation = Ci.nsIWebNavigation;
    25 var gLastBrowserCharset = null;
    26 var gPrevCharset = null;
    27 var gProxyFavIcon = null;
    28 var gLastValidURLStr = "";
    29 var gInPrintPreviewMode = false;
    30 var gContextMenu = null; // nsContextMenu instance
    31 var gMultiProcessBrowser = false;
    33 #ifndef XP_MACOSX
    34 var gEditUIVisible = true;
    35 #endif
    37 [
    38   ["gBrowser",            "content"],
    39   ["gNavToolbox",         "navigator-toolbox"],
    40   ["gURLBar",             "urlbar"],
    41   ["gNavigatorBundle",    "bundle_browser"]
    42 ].forEach(function (elementGlobal) {
    43   var [name, id] = elementGlobal;
    44   window.__defineGetter__(name, function () {
    45     var element = document.getElementById(id);
    46     if (!element)
    47       return null;
    48     delete window[name];
    49     return window[name] = element;
    50   });
    51   window.__defineSetter__(name, function (val) {
    52     delete window[name];
    53     return window[name] = val;
    54   });
    55 });
    57 // Smart getter for the findbar.  If you don't wish to force the creation of
    58 // the findbar, check gFindBarInitialized first.
    60 this.__defineGetter__("gFindBar", function() {
    61   return window.gBrowser.getFindBar();
    62 });
    64 this.__defineGetter__("gFindBarInitialized", function() {
    65   return window.gBrowser.isFindBarInitialized();
    66 });
    68 XPCOMUtils.defineLazyGetter(this, "gPrefService", function() {
    69   return Services.prefs;
    70 });
    72 this.__defineGetter__("AddonManager", function() {
    73   let tmp = {};
    74   Cu.import("resource://gre/modules/AddonManager.jsm", tmp);
    75   return this.AddonManager = tmp.AddonManager;
    76 });
    77 this.__defineSetter__("AddonManager", function (val) {
    78   delete this.AddonManager;
    79   return this.AddonManager = val;
    80 });
    82 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
    83   "resource://gre/modules/PluralForm.jsm");
    85 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
    86   "resource://gre/modules/TelemetryStopwatch.jsm");
    88 XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
    89   let scope = {};
    90   Cu.import("resource:///modules/CustomizeMode.jsm", scope);
    91   return new scope.CustomizeMode(window);
    92 });
    94 #ifdef MOZ_SERVICES_SYNC
    95 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
    96   "resource://services-sync/main.js");
    97 #endif
    99 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
   100   let tmp = {};
   101   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
   102   try {
   103     return new tmp.PopupNotifications(gBrowser,
   104                                       document.getElementById("notification-popup"),
   105                                       document.getElementById("notification-popup-box"));
   106   } catch (ex) {
   107     Cu.reportError(ex);
   108     return null;
   109   }
   110 });
   112 XPCOMUtils.defineLazyGetter(this, "DeveloperToolbar", function() {
   113   let tmp = {};
   114   Cu.import("resource:///modules/devtools/DeveloperToolbar.jsm", tmp);
   115   return new tmp.DeveloperToolbar(window, document.getElementById("developer-toolbar"));
   116 });
   118 XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function() {
   119   let tmp = {};
   120   Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", tmp);
   121   return tmp.BrowserToolboxProcess;
   122 });
   124 XPCOMUtils.defineLazyModuleGetter(this, "Social",
   125   "resource:///modules/Social.jsm");
   127 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   128   "resource://gre/modules/PageThumbs.jsm");
   130 #ifdef MOZ_SAFE_BROWSING
   131 XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
   132   "resource://gre/modules/SafeBrowsing.jsm");
   133 #endif
   135 XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
   136   "resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
   138 XPCOMUtils.defineLazyModuleGetter(this, "gCustomizationTabPreloader",
   139   "resource:///modules/CustomizationTabPreloader.jsm", "CustomizationTabPreloader");
   141 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   142   "resource://gre/modules/PrivateBrowsingUtils.jsm");
   144 XPCOMUtils.defineLazyModuleGetter(this, "Translation",
   145   "resource:///modules/translation/Translation.jsm");
   147 XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions",
   148   "resource:///modules/SitePermissions.jsm");
   150 XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
   151   "resource:///modules/sessionstore/SessionStore.jsm");
   153 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
   154   "resource://gre/modules/FxAccounts.jsm");
   156 #ifdef MOZ_CRASHREPORTER
   157 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
   158   "resource:///modules/TabCrashReporter.jsm");
   159 #endif
   161 let gInitialPages = [
   162   "about:blank",
   163   "about:newtab",
   164   "about:home",
   165   "about:privatebrowsing",
   166   "about:welcomeback",
   167   "about:sessionrestore"
   168 ];
   170 #include browser-addons.js
   171 #include browser-customization.js
   172 #include browser-feeds.js
   173 #include browser-fullScreen.js
   174 #include browser-fullZoom.js
   175 #include browser-places.js
   176 #include browser-plugins.js
   177 #include browser-safebrowsing.js
   178 #include browser-social.js
   179 #include browser-tabPreviews.js
   180 #include browser-tabview.js
   181 #include browser-thumbnails.js
   182 #include browser-webrtcUI.js
   183 #include browser-gestureSupport.js
   185 #ifdef MOZ_DATA_REPORTING
   186 #include browser-data-submission-info-bar.js
   187 #endif
   189 #ifdef MOZ_SERVICES_SYNC
   190 #include browser-syncui.js
   191 #endif
   193 #include browser-fxaccounts.js
   195 XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
   196 #ifdef XP_WIN
   197   // Bug 666808 - AeroPeek support for e10s
   198   if (gMultiProcessBrowser)
   199     return null;
   201   const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
   202   if (WINTASKBAR_CONTRACTID in Cc &&
   203       Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
   204     let AeroPeek = Cu.import("resource:///modules/WindowsPreviewPerTab.jsm", {}).AeroPeek;
   205     return {
   206       onOpenWindow: function () {
   207         AeroPeek.onOpenWindow(window);
   208       },
   209       onCloseWindow: function () {
   210         AeroPeek.onCloseWindow(window);
   211       }
   212     };
   213   }
   214 #endif
   215   return null;
   216 });
   218 #ifdef MOZ_CRASHREPORTER
   219 XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
   220                                    "@mozilla.org/xre/app-info;1",
   221                                    "nsICrashReporter");
   222 #endif
   224 XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
   225   let tmp = {};
   226   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   227   return new tmp.PageMenu();
   228 });
   230 /**
   231 * We can avoid adding multiple load event listeners and save some time by adding
   232 * one listener that calls all real handlers.
   233 */
   234 function pageShowEventHandlers(persisted) {
   235   charsetLoadListener();
   236   XULBrowserWindow.asyncUpdateUI();
   238   // The PluginClickToPlay events are not fired when navigating using the
   239   // BF cache. |persisted| is true when the page is loaded from the
   240   // BF cache, so this code reshows the notification if necessary.
   241   if (persisted)
   242     gPluginHandler.reshowClickToPlayNotification();
   243 }
   245 function UpdateBackForwardCommands(aWebNavigation) {
   246   var backBroadcaster = document.getElementById("Browser:Back");
   247   var forwardBroadcaster = document.getElementById("Browser:Forward");
   249   // Avoid setting attributes on broadcasters if the value hasn't changed!
   250   // Remember, guys, setting attributes on elements is expensive!  They
   251   // get inherited into anonymous content, broadcast to other widgets, etc.!
   252   // Don't do it if the value hasn't changed! - dwh
   254   var backDisabled = backBroadcaster.hasAttribute("disabled");
   255   var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
   256   if (backDisabled == aWebNavigation.canGoBack) {
   257     if (backDisabled)
   258       backBroadcaster.removeAttribute("disabled");
   259     else
   260       backBroadcaster.setAttribute("disabled", true);
   261   }
   263   if (forwardDisabled == aWebNavigation.canGoForward) {
   264     if (forwardDisabled)
   265       forwardBroadcaster.removeAttribute("disabled");
   266     else
   267       forwardBroadcaster.setAttribute("disabled", true);
   268   }
   269 }
   271 /**
   272  * Click-and-Hold implementation for the Back and Forward buttons
   273  * XXXmano: should this live in toolbarbutton.xml?
   274  */
   275 function SetClickAndHoldHandlers() {
   276   var timer;
   278   function openMenu(aButton) {
   279     cancelHold(aButton);
   280     aButton.firstChild.hidden = false;
   281     aButton.open = true;
   282   }
   284   function mousedownHandler(aEvent) {
   285     if (aEvent.button != 0 ||
   286         aEvent.currentTarget.open ||
   287         aEvent.currentTarget.disabled)
   288       return;
   290     // Prevent the menupopup from opening immediately
   291     aEvent.currentTarget.firstChild.hidden = true;
   293     aEvent.currentTarget.addEventListener("mouseout", mouseoutHandler, false);
   294     aEvent.currentTarget.addEventListener("mouseup", mouseupHandler, false);
   295     timer = setTimeout(openMenu, 500, aEvent.currentTarget);
   296   }
   298   function mouseoutHandler(aEvent) {
   299     let buttonRect = aEvent.currentTarget.getBoundingClientRect();
   300     if (aEvent.clientX >= buttonRect.left &&
   301         aEvent.clientX <= buttonRect.right &&
   302         aEvent.clientY >= buttonRect.bottom)
   303       openMenu(aEvent.currentTarget);
   304     else
   305       cancelHold(aEvent.currentTarget);
   306   }
   308   function mouseupHandler(aEvent) {
   309     cancelHold(aEvent.currentTarget);
   310   }
   312   function cancelHold(aButton) {
   313     clearTimeout(timer);
   314     aButton.removeEventListener("mouseout", mouseoutHandler, false);
   315     aButton.removeEventListener("mouseup", mouseupHandler, false);
   316   }
   318   function clickHandler(aEvent) {
   319     if (aEvent.button == 0 &&
   320         aEvent.target == aEvent.currentTarget &&
   321         !aEvent.currentTarget.open &&
   322         !aEvent.currentTarget.disabled) {
   323       let cmdEvent = document.createEvent("xulcommandevent");
   324       cmdEvent.initCommandEvent("command", true, true, window, 0,
   325                                 aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
   326                                 aEvent.metaKey, null);
   327       aEvent.currentTarget.dispatchEvent(cmdEvent);
   328     }
   329   }
   331   function _addClickAndHoldListenersOnElement(aElm) {
   332     aElm.addEventListener("mousedown", mousedownHandler, true);
   333     aElm.addEventListener("click", clickHandler, true);
   334   }
   336   // Bug 414797: Clone the back/forward buttons' context menu into both buttons.
   337   let popup = document.getElementById("backForwardMenu").cloneNode(true);
   338   popup.removeAttribute("id");
   339   // Prevent the back/forward buttons' context attributes from being inherited.
   340   popup.setAttribute("context", "");
   342   let backButton = document.getElementById("back-button");
   343   backButton.setAttribute("type", "menu");
   344   backButton.appendChild(popup);
   345   _addClickAndHoldListenersOnElement(backButton);
   347   let forwardButton = document.getElementById("forward-button");
   348   popup = popup.cloneNode(true);
   349   forwardButton.setAttribute("type", "menu");
   350   forwardButton.appendChild(popup);
   351   _addClickAndHoldListenersOnElement(forwardButton);
   352 }
   354 const gSessionHistoryObserver = {
   355   observe: function(subject, topic, data)
   356   {
   357     if (topic != "browser:purge-session-history")
   358       return;
   360     var backCommand = document.getElementById("Browser:Back");
   361     backCommand.setAttribute("disabled", "true");
   362     var fwdCommand = document.getElementById("Browser:Forward");
   363     fwdCommand.setAttribute("disabled", "true");
   365     // Hide session restore button on about:home
   366     window.messageManager.broadcastAsyncMessage("Browser:HideSessionRestoreButton");
   368     if (gURLBar) {
   369       // Clear undo history of the URL bar
   370       gURLBar.editor.transactionManager.clear()
   371     }
   372   }
   373 };
   375 /**
   376  * Given a starting docshell and a URI to look up, find the docshell the URI
   377  * is loaded in.
   378  * @param   aDocument
   379  *          A document to find instead of using just a URI - this is more specific.
   380  * @param   aDocShell
   381  *          The doc shell to start at
   382  * @param   aSoughtURI
   383  *          The URI that we're looking for
   384  * @returns The doc shell that the sought URI is loaded in. Can be in
   385  *          subframes.
   386  */
   387 function findChildShell(aDocument, aDocShell, aSoughtURI) {
   388   aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation);
   389   aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
   390   var doc = aDocShell.getInterface(Components.interfaces.nsIDOMDocument);
   391   if ((aDocument && doc == aDocument) ||
   392       (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
   393     return aDocShell;
   395   var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeItem);
   396   for (var i = 0; i < node.childCount; ++i) {
   397     var docShell = node.getChildAt(i);
   398     docShell = findChildShell(aDocument, docShell, aSoughtURI);
   399     if (docShell)
   400       return docShell;
   401   }
   402   return null;
   403 }
   405 var gPopupBlockerObserver = {
   406   _reportButton: null,
   408   onReportButtonClick: function (aEvent)
   409   {
   410     if (aEvent.button != 0 || aEvent.target != this._reportButton)
   411       return;
   413     document.getElementById("blockedPopupOptions")
   414             .openPopup(this._reportButton, "after_end", 0, 2, false, false, aEvent);
   415   },
   417   handleEvent: function (aEvent)
   418   {
   419     if (aEvent.originalTarget != gBrowser.selectedBrowser)
   420       return;
   422     if (!this._reportButton && gURLBar)
   423       this._reportButton = document.getElementById("page-report-button");
   425     if (!gBrowser.selectedBrowser.blockedPopups) {
   426       // Hide the icon in the location bar (if the location bar exists)
   427       if (gURLBar)
   428         this._reportButton.hidden = true;
   429       return;
   430     }
   432     if (gURLBar)
   433       this._reportButton.hidden = false;
   435     // Only show the notification again if we've not already shown it. Since
   436     // notifications are per-browser, we don't need to worry about re-adding
   437     // it.
   438     if (!gBrowser.selectedBrowser.blockedPopups.reported) {
   439       if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
   440         var brandBundle = document.getElementById("bundle_brand");
   441         var brandShortName = brandBundle.getString("brandShortName");
   442         var popupCount = gBrowser.selectedBrowser.blockedPopups.length;
   443 #ifdef XP_WIN
   444         var popupButtonText = gNavigatorBundle.getString("popupWarningButton");
   445         var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButton.accesskey");
   446 #else
   447         var popupButtonText = gNavigatorBundle.getString("popupWarningButtonUnix");
   448         var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButtonUnix.accesskey");
   449 #endif
   450         var messageBase = gNavigatorBundle.getString("popupWarning.message");
   451         var message = PluralForm.get(popupCount, messageBase)
   452                                 .replace("#1", brandShortName)
   453                                 .replace("#2", popupCount);
   455         var notificationBox = gBrowser.getNotificationBox();
   456         var notification = notificationBox.getNotificationWithValue("popup-blocked");
   457         if (notification) {
   458           notification.label = message;
   459         }
   460         else {
   461           var buttons = [{
   462             label: popupButtonText,
   463             accessKey: popupButtonAccesskey,
   464             popup: "blockedPopupOptions",
   465             callback: null
   466           }];
   468           const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
   469           notificationBox.appendNotification(message, "popup-blocked",
   470                                              "chrome://browser/skin/Info.png",
   471                                              priority, buttons);
   472         }
   473       }
   475       // Record the fact that we've reported this blocked popup, so we don't
   476       // show it again.
   477       gBrowser.selectedBrowser.blockedPopups.reported = true;
   478     }
   479   },
   481   toggleAllowPopupsForSite: function (aEvent)
   482   {
   483     var pm = Services.perms;
   484     var shouldBlock = aEvent.target.getAttribute("block") == "true";
   485     var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
   486     pm.add(gBrowser.currentURI, "popup", perm);
   488     gBrowser.getNotificationBox().removeCurrentNotification();
   489   },
   491   fillPopupList: function (aEvent)
   492   {
   493     // XXXben - rather than using |currentURI| here, which breaks down on multi-framed sites
   494     //          we should really walk the blockedPopups and create a list of "allow for <host>"
   495     //          menuitems for the common subset of hosts present in the report, this will
   496     //          make us frame-safe.
   497     //
   498     // XXXjst - Note that when this is fixed to work with multi-framed sites,
   499     //          also back out the fix for bug 343772 where
   500     //          nsGlobalWindow::CheckOpenAllow() was changed to also
   501     //          check if the top window's location is whitelisted.
   502     let browser = gBrowser.selectedBrowser;
   503     var uri = browser.currentURI;
   504     var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
   505     try {
   506       blockedPopupAllowSite.removeAttribute("hidden");
   508       var pm = Services.perms;
   509       if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
   510         // Offer an item to block popups for this site, if a whitelist entry exists
   511         // already for it.
   512         let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host || uri.spec]);
   513         blockedPopupAllowSite.setAttribute("label", blockString);
   514         blockedPopupAllowSite.setAttribute("block", "true");
   515       }
   516       else {
   517         // Offer an item to allow popups for this site
   518         let allowString = gNavigatorBundle.getFormattedString("popupAllow", [uri.host || uri.spec]);
   519         blockedPopupAllowSite.setAttribute("label", allowString);
   520         blockedPopupAllowSite.removeAttribute("block");
   521       }
   522     }
   523     catch (e) {
   524       blockedPopupAllowSite.setAttribute("hidden", "true");
   525     }
   527     if (PrivateBrowsingUtils.isWindowPrivate(window))
   528       blockedPopupAllowSite.setAttribute("disabled", "true");
   529     else
   530       blockedPopupAllowSite.removeAttribute("disabled");
   532     var foundUsablePopupURI = false;
   533     var blockedPopups = browser.blockedPopups;
   534     if (blockedPopups) {
   535       for (let i = 0; i < blockedPopups.length; i++) {
   536         let blockedPopup = blockedPopups[i];
   538         // popupWindowURI will be null if the file picker popup is blocked.
   539         // xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
   540         if (!blockedPopup.popupWindowURI)
   541           continue;
   542         var popupURIspec = blockedPopup.popupWindowURI;
   544         // Sometimes the popup URI that we get back from the blockedPopup
   545         // isn't useful (for instance, netscape.com's popup URI ends up
   546         // being "http://www.netscape.com", which isn't really the URI of
   547         // the popup they're trying to show).  This isn't going to be
   548         // useful to the user, so we won't create a menu item for it.
   549         if (popupURIspec == "" || popupURIspec == "about:blank" ||
   550             popupURIspec == uri.spec)
   551           continue;
   553         // Because of the short-circuit above, we may end up in a situation
   554         // in which we don't have any usable popup addresses to show in
   555         // the menu, and therefore we shouldn't show the separator.  However,
   556         // since we got past the short-circuit, we must've found at least
   557         // one usable popup URI and thus we'll turn on the separator later.
   558         foundUsablePopupURI = true;
   560         var menuitem = document.createElement("menuitem");
   561         var label = gNavigatorBundle.getFormattedString("popupShowPopupPrefix",
   562                                                         [popupURIspec]);
   563         menuitem.setAttribute("label", label);
   564         menuitem.setAttribute("popupWindowURI", popupURIspec);
   565         menuitem.setAttribute("popupWindowFeatures", blockedPopup.popupWindowFeatures);
   566         menuitem.setAttribute("popupWindowName", blockedPopup.popupWindowName);
   567         menuitem.setAttribute("oncommand", "gPopupBlockerObserver.showBlockedPopup(event);");
   568         menuitem.setAttribute("popupReportIndex", i);
   569         menuitem.popupReportBrowser = browser;
   570         aEvent.target.appendChild(menuitem);
   571       }
   572     }
   574     // Show or hide the separator, depending on whether we added any
   575     // showable popup addresses to the menu.
   576     var blockedPopupsSeparator =
   577       document.getElementById("blockedPopupsSeparator");
   578     if (foundUsablePopupURI)
   579       blockedPopupsSeparator.removeAttribute("hidden");
   580     else
   581       blockedPopupsSeparator.setAttribute("hidden", true);
   583     var blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
   584     var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
   585     blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
   586     if (aEvent.target.anchorNode.id == "page-report-button") {
   587       aEvent.target.anchorNode.setAttribute("open", "true");
   588       blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromLocationbar"));
   589     } else
   590       blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
   591   },
   593   onPopupHiding: function (aEvent) {
   594     if (aEvent.target.anchorNode.id == "page-report-button")
   595       aEvent.target.anchorNode.removeAttribute("open");
   597     let item = aEvent.target.lastChild;
   598     while (item && item.getAttribute("observes") != "blockedPopupsSeparator") {
   599       let next = item.previousSibling;
   600       item.parentNode.removeChild(item);
   601       item = next;
   602     }
   603   },
   605   showBlockedPopup: function (aEvent)
   606   {
   607     var target = aEvent.target;
   608     var popupReportIndex = target.getAttribute("popupReportIndex");
   609     let browser = target.popupReportBrowser;
   610     browser.unblockPopup(popupReportIndex);
   611   },
   613   editPopupSettings: function ()
   614   {
   615     var host = "";
   616     try {
   617       host = gBrowser.currentURI.host;
   618     }
   619     catch (e) { }
   621     var bundlePreferences = document.getElementById("bundle_preferences");
   622     var params = { blockVisible   : false,
   623                    sessionVisible : false,
   624                    allowVisible   : true,
   625                    prefilledHost  : host,
   626                    permissionType : "popup",
   627                    windowTitle    : bundlePreferences.getString("popuppermissionstitle"),
   628                    introText      : bundlePreferences.getString("popuppermissionstext") };
   629     var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
   630     if (existingWindow) {
   631       existingWindow.initWithParams(params);
   632       existingWindow.focus();
   633     }
   634     else
   635       window.openDialog("chrome://browser/content/preferences/permissions.xul",
   636                         "_blank", "resizable,dialog=no,centerscreen", params);
   637   },
   639   dontShowMessage: function ()
   640   {
   641     var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
   642     gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
   643     gBrowser.getNotificationBox().removeCurrentNotification();
   644   }
   645 };
   647 const gFormSubmitObserver = {
   648   QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
   650   panel: null,
   652   init: function()
   653   {
   654     this.panel = document.getElementById('invalid-form-popup');
   655   },
   657   notifyInvalidSubmit : function (aFormElement, aInvalidElements)
   658   {
   659     // We are going to handle invalid form submission attempt by focusing the
   660     // first invalid element and show the corresponding validation message in a
   661     // panel attached to the element.
   662     if (!aInvalidElements.length) {
   663       return;
   664     }
   666     // Don't show the popup if the current tab doesn't contain the invalid form.
   667     if (gBrowser.contentDocument !=
   668         aFormElement.ownerDocument.defaultView.top.document) {
   669       return;
   670     }
   672     let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
   674     if (!(element instanceof HTMLInputElement ||
   675           element instanceof HTMLTextAreaElement ||
   676           element instanceof HTMLSelectElement ||
   677           element instanceof HTMLButtonElement)) {
   678       return;
   679     }
   681     this.panel.firstChild.textContent = element.validationMessage;
   683     element.focus();
   685     // If the user interacts with the element and makes it valid or leaves it,
   686     // we want to remove the popup.
   687     // We could check for clicks but a click is already removing the popup.
   688     function blurHandler() {
   689       gFormSubmitObserver.panel.hidePopup();
   690     };
   691     function inputHandler(e) {
   692       if (e.originalTarget.validity.valid) {
   693         gFormSubmitObserver.panel.hidePopup();
   694       } else {
   695         // If the element is now invalid for a new reason, we should update the
   696         // error message.
   697         if (gFormSubmitObserver.panel.firstChild.textContent !=
   698             e.originalTarget.validationMessage) {
   699           gFormSubmitObserver.panel.firstChild.textContent =
   700             e.originalTarget.validationMessage;
   701         }
   702       }
   703     };
   704     element.addEventListener("input", inputHandler, false);
   705     element.addEventListener("blur", blurHandler, false);
   707     // One event to bring them all and in the darkness bind them.
   708     this.panel.addEventListener("popuphiding", function onPopupHiding(aEvent) {
   709       aEvent.target.removeEventListener("popuphiding", onPopupHiding, false);
   710       element.removeEventListener("input", inputHandler, false);
   711       element.removeEventListener("blur", blurHandler, false);
   712     }, false);
   714     this.panel.hidden = false;
   716     // We want to show the popup at the middle of checkbox and radio buttons
   717     // and where the content begin for the other elements.
   718     let offset = 0;
   719     let position = "";
   721     if (element.tagName == 'INPUT' &&
   722         (element.type == 'radio' || element.type == 'checkbox')) {
   723       position = "bottomcenter topleft";
   724     } else {
   725       let win = element.ownerDocument.defaultView;
   726       let style = win.getComputedStyle(element, null);
   727       let utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
   728                      .getInterface(Components.interfaces.nsIDOMWindowUtils);
   730       if (style.direction == 'rtl') {
   731         offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
   732       } else {
   733         offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
   734       }
   736       offset = Math.round(offset * utils.fullZoom);
   738       position = "after_start";
   739     }
   741     this.panel.openPopup(element, position, offset, 0);
   742   }
   743 };
   745 var gBrowserInit = {
   746   delayedStartupFinished: false,
   748   onLoad: function() {
   749     gMultiProcessBrowser =
   750       window.QueryInterface(Ci.nsIInterfaceRequestor)
   751       .getInterface(Ci.nsIWebNavigation)
   752       .QueryInterface(Ci.nsILoadContext)
   753       .useRemoteTabs;
   755     var mustLoadSidebar = false;
   757     if (!gMultiProcessBrowser) {
   758       // There is a Content:Click message manually sent from content.
   759       Cc["@mozilla.org/eventlistenerservice;1"]
   760         .getService(Ci.nsIEventListenerService)
   761         .addSystemEventListener(gBrowser, "click", contentAreaClick, true);
   762     }
   764     gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
   766     // Note that the XBL binding is untrusted
   767     gBrowser.addEventListener("PluginBindingAttached", gPluginHandler, true, true);
   768     gBrowser.addEventListener("PluginCrashed",         gPluginHandler, true);
   769     gBrowser.addEventListener("PluginOutdated",        gPluginHandler, true);
   770     gBrowser.addEventListener("PluginInstantiated",    gPluginHandler, true);
   771     gBrowser.addEventListener("PluginRemoved",         gPluginHandler, true);
   773     gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
   775     Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);
   777     window.addEventListener("AppCommand", HandleAppCommandEvent, true);
   779     // These routines add message listeners. They must run before
   780     // loading the frame script to ensure that we don't miss any
   781     // message sent between when the frame script is loaded and when
   782     // the listener is registered.
   783     DOMLinkHandler.init();
   784     gPageStyleMenu.init();
   785     LanguageDetectionListener.init();
   787     messageManager.loadFrameScript("chrome://browser/content/content.js", true);
   789     // initialize observers and listeners
   790     // and give C++ access to gBrowser
   791     XULBrowserWindow.init();
   792     window.QueryInterface(Ci.nsIInterfaceRequestor)
   793           .getInterface(nsIWebNavigation)
   794           .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
   795           .QueryInterface(Ci.nsIInterfaceRequestor)
   796           .getInterface(Ci.nsIXULWindow)
   797           .XULBrowserWindow = window.XULBrowserWindow;
   798     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
   799       new nsBrowserAccess();
   801     // hook up UI through progress listener
   802     gBrowser.addProgressListener(window.XULBrowserWindow);
   803     gBrowser.addTabsProgressListener(window.TabsProgressListener);
   805     // setup simple gestures support
   806     gGestureSupport.init(true);
   808     // setup history swipe animation
   809     gHistorySwipeAnimation.init();
   811     if (window.opener && !window.opener.closed &&
   812         PrivateBrowsingUtils.isWindowPrivate(window) == PrivateBrowsingUtils.isWindowPrivate(window.opener)) {
   813       let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
   814       // If the opener had a sidebar, open the same sidebar in our window.
   815       // The opener can be the hidden window too, if we're coming from the state
   816       // where no windows are open, and the hidden window has no sidebar box.
   817       if (openerSidebarBox && !openerSidebarBox.hidden) {
   818         let sidebarCmd = openerSidebarBox.getAttribute("sidebarcommand");
   819         let sidebarCmdElem = document.getElementById(sidebarCmd);
   821         // dynamically generated sidebars will fail this check.
   822         if (sidebarCmdElem) {
   823           let sidebarBox = document.getElementById("sidebar-box");
   824           let sidebarTitle = document.getElementById("sidebar-title");
   826           sidebarTitle.setAttribute(
   827             "value", window.opener.document.getElementById("sidebar-title").getAttribute("value"));
   828           sidebarBox.setAttribute("width", openerSidebarBox.boxObject.width);
   830           sidebarBox.setAttribute("sidebarcommand", sidebarCmd);
   831           // Note: we're setting 'src' on sidebarBox, which is a <vbox>, not on
   832           // the <browser id="sidebar">. This lets us delay the actual load until
   833           // delayedStartup().
   834           sidebarBox.setAttribute(
   835             "src", window.opener.document.getElementById("sidebar").getAttribute("src"));
   836           mustLoadSidebar = true;
   838           sidebarBox.hidden = false;
   839           document.getElementById("sidebar-splitter").hidden = false;
   840           sidebarCmdElem.setAttribute("checked", "true");
   841         }
   842       }
   843     }
   844     else {
   845       let box = document.getElementById("sidebar-box");
   846       if (box.hasAttribute("sidebarcommand")) {
   847         let commandID = box.getAttribute("sidebarcommand");
   848         if (commandID) {
   849           let command = document.getElementById(commandID);
   850           if (command) {
   851             mustLoadSidebar = true;
   852             box.hidden = false;
   853             document.getElementById("sidebar-splitter").hidden = false;
   854             command.setAttribute("checked", "true");
   855           }
   856           else {
   857             // Remove the |sidebarcommand| attribute, because the element it
   858             // refers to no longer exists, so we should assume this sidebar
   859             // panel has been uninstalled. (249883)
   860             box.removeAttribute("sidebarcommand");
   861           }
   862         }
   863       }
   864     }
   866     // Certain kinds of automigration rely on this notification to complete their
   867     // tasks BEFORE the browser window is shown.
   868     Services.obs.notifyObservers(null, "browser-window-before-show", "");
   870     // Set a sane starting width/height for all resolutions on new profiles.
   871     if (!document.documentElement.hasAttribute("width")) {
   872       let defaultWidth;
   873       let defaultHeight;
   875       // Very small: maximize the window
   876       // Portrait  : use about full width and 3/4 height, to view entire pages
   877       //             at once (without being obnoxiously tall)
   878       // Widescreen: use about half width, to suggest side-by-side page view
   879       // Otherwise : use 3/4 height and width
   880       if (screen.availHeight <= 600) {
   881         document.documentElement.setAttribute("sizemode", "maximized");
   882         defaultWidth = 610;
   883         defaultHeight = 450;
   884       }
   885       else {
   886         if (screen.availWidth <= screen.availHeight) {
   887           defaultWidth = screen.availWidth * .9;
   888           defaultHeight = screen.availHeight * .75;
   889         }
   890         else if (screen.availWidth >= 2048) {
   891           defaultWidth = (screen.availWidth / 2) - 20;
   892           defaultHeight = screen.availHeight - 10;
   893         }
   894         else {
   895           defaultWidth = screen.availWidth * .75;
   896           defaultHeight = screen.availHeight * .75;
   897         }
   899 #if MOZ_WIDGET_GTK == 2
   900         // On X, we're not currently able to account for the size of the window
   901         // border.  Use 28px as a guess (titlebar + bottom window border)
   902         defaultHeight -= 28;
   903 #endif
   904       }
   905       document.documentElement.setAttribute("width", defaultWidth);
   906       document.documentElement.setAttribute("height", defaultHeight);
   907     }
   909     if (!window.toolbar.visible) {
   910       // adjust browser UI for popups
   911       if (gURLBar) {
   912         gURLBar.setAttribute("readonly", "true");
   913         gURLBar.setAttribute("enablehistory", "false");
   914       }
   915       goSetCommandEnabled("cmd_newNavigatorTab", false);
   916     }
   918     // Misc. inits.
   919     CombinedStopReload.init();
   920     gPrivateBrowsingUI.init();
   921     TabsInTitlebar.init();
   923 #ifdef XP_WIN
   924     if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
   925         window.matchMedia("(-moz-windows-default-theme)").matches) {
   926       let windowFrameColor = Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
   927                                .Windows8WindowFrameColor.get();
   929       // Formula from W3C's WCAG 2.0 spec's color ratio and relative luminance,
   930       // section 1.3.4, http://www.w3.org/TR/WCAG20/ .
   931       windowFrameColor = windowFrameColor.map((color) => {
   932         if (color <= 10) {
   933           return color / 255 / 12.92;
   934         }
   935         return Math.pow(((color / 255) + 0.055) / 1.055, 2.4);
   936       });
   937       let backgroundLuminance = windowFrameColor[0] * 0.2126 +
   938                                 windowFrameColor[1] * 0.7152 +
   939                                 windowFrameColor[2] * 0.0722;
   940       let foregroundLuminance = 0; // Default to black for foreground text.
   941       let contrastRatio = (backgroundLuminance + 0.05) / (foregroundLuminance + 0.05);
   942       if (contrastRatio < 3) {
   943         document.documentElement.setAttribute("darkwindowframe", "true");
   944       }
   945     }
   946 #endif
   948     ToolbarIconColor.init();
   950     // Wait until chrome is painted before executing code not critical to making the window visible
   951     this._boundDelayedStartup = this._delayedStartup.bind(this, mustLoadSidebar);
   952     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
   954     this._loadHandled = true;
   955   },
   957   _cancelDelayedStartup: function () {
   958     window.removeEventListener("MozAfterPaint", this._boundDelayedStartup);
   959     this._boundDelayedStartup = null;
   960   },
   962   _delayedStartup: function(mustLoadSidebar) {
   963     let tmp = {};
   964     Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", tmp);
   965     let TelemetryTimestamps = tmp.TelemetryTimestamps;
   966     TelemetryTimestamps.add("delayedStartupStarted");
   968     this._cancelDelayedStartup();
   970     // We need to set the MozApplicationManifest event listeners up
   971     // before we start loading the home pages in case a document has
   972     // a "manifest" attribute, in which the MozApplicationManifest event
   973     // will be fired.
   974     gBrowser.addEventListener("MozApplicationManifest",
   975                               OfflineApps, false);
   976     // listen for offline apps on social
   977     let socialBrowser = document.getElementById("social-sidebar-browser");
   978     socialBrowser.addEventListener("MozApplicationManifest",
   979                               OfflineApps, false);
   981     let uriToLoad = this._getUriToLoad();
   982     var isLoadingBlank = isBlankPageURL(uriToLoad);
   984     // This pageshow listener needs to be registered before we may call
   985     // swapBrowsersAndCloseOther() to receive pageshow events fired by that.
   986     gBrowser.addEventListener("pageshow", function(event) {
   987       // Filter out events that are not about the document load we are interested in
   988       if (content && event.target == content.document)
   989         setTimeout(pageShowEventHandlers, 0, event.persisted);
   990     }, true);
   992     if (uriToLoad && uriToLoad != "about:blank") {
   993       if (uriToLoad instanceof Ci.nsISupportsArray) {
   994         let count = uriToLoad.Count();
   995         let specs = [];
   996         for (let i = 0; i < count; i++) {
   997           let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
   998           specs.push(urisstring.data);
   999         }
  1001         // This function throws for certain malformed URIs, so use exception handling
  1002         // so that we don't disrupt startup
  1003         try {
  1004           gBrowser.loadTabs(specs, false, true);
  1005         } catch (e) {}
  1007       else if (uriToLoad instanceof XULElement) {
  1008         // swap the given tab with the default about:blank tab and then close
  1009         // the original tab in the other window.
  1011         // Stop the about:blank load
  1012         gBrowser.stop();
  1013         // make sure it has a docshell
  1014         gBrowser.docShell;
  1016         gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
  1018       // window.arguments[2]: referrer (nsIURI)
  1019       //                 [3]: postData (nsIInputStream)
  1020       //                 [4]: allowThirdPartyFixup (bool)
  1021       else if (window.arguments.length >= 3) {
  1022         loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
  1023                 window.arguments[4] || false);
  1024         window.focus();
  1026       // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
  1027       // Such callers expect that window.arguments[0] is handled as a single URI.
  1028       else {
  1029         if (uriToLoad == "about:newtab" &&
  1030             Services.prefs.getBoolPref("browser.newtabpage.enabled")) {
  1031           Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true);
  1033         loadOneOrMoreURIs(uriToLoad);
  1037 #ifdef MOZ_SAFE_BROWSING
  1038     // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
  1039     setTimeout(function() { SafeBrowsing.init(); }, 2000);
  1040 #endif
  1042     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
  1043     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
  1044     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
  1045     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
  1046     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
  1047     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
  1048     Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
  1050     BrowserOffline.init();
  1051     OfflineApps.init();
  1052     IndexedDBPromptHelper.init();
  1053     CanvasPermissionPromptHelper.init();
  1054     gFormSubmitObserver.init();
  1055     gRemoteTabsUI.init();
  1057     // Initialize the full zoom setting.
  1058     // We do this before the session restore service gets initialized so we can
  1059     // apply full zoom settings to tabs restored by the session restore service.
  1060     FullZoom.init();
  1061     PanelUI.init();
  1062     LightweightThemeListener.init();
  1063     WebrtcIndicator.init();
  1065     // Ensure login manager is up and running.
  1066     Services.logins;
  1068 #ifdef MOZ_CRASHREPORTER
  1069     if (gMultiProcessBrowser)
  1070       TabCrashReporter.init();
  1071 #endif
  1073     if (mustLoadSidebar) {
  1074       let sidebar = document.getElementById("sidebar");
  1075       let sidebarBox = document.getElementById("sidebar-box");
  1076       sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
  1079     UpdateUrlbarSearchSplitterState();
  1081     if (!isLoadingBlank || !focusAndSelectUrlBar())
  1082       gBrowser.selectedBrowser.focus();
  1084     // Set up Sanitize Item
  1085     this._initializeSanitizer();
  1087     // Enable/Disable auto-hide tabbar
  1088     gBrowser.tabContainer.updateVisibility();
  1090     BookmarkingUI.init();
  1092     gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
  1094     var homeButton = document.getElementById("home-button");
  1095     gHomeButton.updateTooltip(homeButton);
  1096     gHomeButton.updatePersonalToolbarStyle(homeButton);
  1098     // BiDi UI
  1099     gBidiUI = isBidiEnabled();
  1100     if (gBidiUI) {
  1101       document.getElementById("documentDirection-separator").hidden = false;
  1102       document.getElementById("documentDirection-swap").hidden = false;
  1103       document.getElementById("textfieldDirection-separator").hidden = false;
  1104       document.getElementById("textfieldDirection-swap").hidden = false;
  1107     // Setup click-and-hold gestures access to the session history
  1108     // menus if global click-and-hold isn't turned on
  1109     if (!getBoolPref("ui.click_hold_context_menus", false))
  1110       SetClickAndHoldHandlers();
  1112     let NP = {};
  1113     Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
  1114     NP.trackBrowserWindow(window);
  1116     PlacesToolbarHelper.init();
  1118     ctrlTab.readPref();
  1119     gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
  1121     // Initialize the download manager some time after the app starts so that
  1122     // auto-resume downloads begin (such as after crashing or quitting with
  1123     // active downloads) and speeds up the first-load of the download manager UI.
  1124     // If the user manually opens the download manager before the timeout, the
  1125     // downloads will start right away, and initializing again won't hurt.
  1126     setTimeout(function() {
  1127       try {
  1128         Cu.import("resource:///modules/DownloadsCommon.jsm", {})
  1129           .DownloadsCommon.initializeAllDataLinks();
  1130         Cu.import("resource:///modules/DownloadsTaskbar.jsm", {})
  1131           .DownloadsTaskbar.registerIndicator(window);
  1132       } catch (ex) {
  1133         Cu.reportError(ex);
  1135     }, 10000);
  1137     // The object handling the downloads indicator is also initialized here in the
  1138     // delayed startup function, but the actual indicator element is not loaded
  1139     // unless there are downloads to be displayed.
  1140     DownloadsButton.initializeIndicator();
  1142 #ifndef XP_MACOSX
  1143     updateEditUIVisibility();
  1144     let placesContext = document.getElementById("placesContext");
  1145     placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
  1146     placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
  1147 #endif
  1149     gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
  1150     gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
  1151     gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
  1153     if (Win7Features)
  1154       Win7Features.onOpenWindow();
  1156    // called when we go into full screen, even if initiated by a web page script
  1157     window.addEventListener("fullscreen", onFullScreen, true);
  1159     // Called when we enter DOM full-screen mode. Note we can already be in browser
  1160     // full-screen mode when we enter DOM full-screen mode.
  1161     window.addEventListener("MozEnteredDomFullscreen", onMozEnteredDomFullscreen, true);
  1163     if (window.fullScreen)
  1164       onFullScreen();
  1165     if (document.mozFullScreen)
  1166       onMozEnteredDomFullscreen();
  1168 #ifdef MOZ_SERVICES_SYNC
  1169     // initialize the sync UI
  1170     gSyncUI.init();
  1171     gFxAccounts.init();
  1172 #endif
  1174 #ifdef MOZ_DATA_REPORTING
  1175     gDataNotificationInfoBar.init();
  1176 #endif
  1178     gBrowserThumbnails.init();
  1180     // Add Devtools menuitems and listeners
  1181     gDevToolsBrowser.registerBrowserWindow(window);
  1183     window.addEventListener("mousemove", MousePosTracker, false);
  1184     window.addEventListener("dragover", MousePosTracker, false);
  1186     gNavToolbox.addEventListener("customizationstarting", CustomizationHandler);
  1187     gNavToolbox.addEventListener("customizationchange", CustomizationHandler);
  1188     gNavToolbox.addEventListener("customizationending", CustomizationHandler);
  1190     // End startup crash tracking after a delay to catch crashes while restoring
  1191     // tabs and to postpone saving the pref to disk.
  1192     try {
  1193       const startupCrashEndDelay = 30 * 1000;
  1194       setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
  1195     } catch (ex) {
  1196       Cu.reportError("Could not end startup crash tracking: " + ex);
  1199     if (typeof WindowsPrefSync !== 'undefined') {
  1200       // Pulls in Metro controlled prefs and pushes out Desktop controlled prefs
  1201       WindowsPrefSync.init();
  1204     SessionStore.promiseInitialized.then(() => {
  1205       // Bail out if the window has been closed in the meantime.
  1206       if (window.closed) {
  1207         return;
  1210       // Enable the Restore Last Session command if needed
  1211       RestoreLastSessionObserver.init();
  1213       SocialUI.init();
  1214       TabView.init();
  1216       setTimeout(function () { BrowserChromeTest.markAsReady(); }, 0);
  1217     });
  1218     this.delayedStartupFinished = true;
  1220     Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
  1221     TelemetryTimestamps.add("delayedStartupFinished");
  1222   },
  1224   // Returns the URI(s) to load at startup.
  1225   _getUriToLoad: function () {
  1226     // window.arguments[0]: URI to load (string), or an nsISupportsArray of
  1227     //                      nsISupportsStrings to load, or a xul:tab of
  1228     //                      a tabbrowser, which will be replaced by this
  1229     //                      window (for this case, all other arguments are
  1230     //                      ignored).
  1231     if (!window.arguments || !window.arguments[0])
  1232       return null;
  1234     let uri = window.arguments[0];
  1235     let sessionStartup = Cc["@mozilla.org/browser/sessionstartup;1"]
  1236                            .getService(Ci.nsISessionStartup);
  1237     let defaultArgs = Cc["@mozilla.org/browser/clh;1"]
  1238                         .getService(Ci.nsIBrowserHandler)
  1239                         .defaultArgs;
  1241     // If the given URI matches defaultArgs (the default homepage) we want
  1242     // to block its load if we're going to restore a session anyway.
  1243     if (uri == defaultArgs && sessionStartup.willOverrideHomepage)
  1244       return null;
  1246     return uri;
  1247   },
  1249   onUnload: function() {
  1250     // In certain scenarios it's possible for unload to be fired before onload,
  1251     // (e.g. if the window is being closed after browser.js loads but before the
  1252     // load completes). In that case, there's nothing to do here.
  1253     if (!this._loadHandled)
  1254       return;
  1256     gDevToolsBrowser.forgetBrowserWindow(window);
  1258     let desc = Object.getOwnPropertyDescriptor(window, "DeveloperToolbar");
  1259     if (desc && !desc.get) {
  1260       DeveloperToolbar.destroy();
  1263     // First clean up services initialized in gBrowserInit.onLoad (or those whose
  1264     // uninit methods don't depend on the services having been initialized).
  1266     CombinedStopReload.uninit();
  1268     gGestureSupport.init(false);
  1270     gHistorySwipeAnimation.uninit();
  1272     FullScreen.cleanup();
  1274 #ifdef MOZ_SERVICES_SYNC
  1275     gFxAccounts.uninit();
  1276 #endif
  1278     Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
  1280     try {
  1281       gBrowser.removeProgressListener(window.XULBrowserWindow);
  1282       gBrowser.removeTabsProgressListener(window.TabsProgressListener);
  1283     } catch (ex) {
  1286     PlacesToolbarHelper.uninit();
  1288     BookmarkingUI.uninit();
  1290     TabsInTitlebar.uninit();
  1292     ToolbarIconColor.uninit();
  1294     var enumerator = Services.wm.getEnumerator(null);
  1295     enumerator.getNext();
  1296     if (!enumerator.hasMoreElements()) {
  1297       document.persist("sidebar-box", "sidebarcommand");
  1298       document.persist("sidebar-box", "width");
  1299       document.persist("sidebar-box", "src");
  1300       document.persist("sidebar-title", "value");
  1303     // Now either cancel delayedStartup, or clean up the services initialized from
  1304     // it.
  1305     if (this._boundDelayedStartup) {
  1306       this._cancelDelayedStartup();
  1307     } else {
  1308       if (Win7Features)
  1309         Win7Features.onCloseWindow();
  1311       gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
  1312       ctrlTab.uninit();
  1313       TabView.uninit();
  1314       SocialUI.uninit();
  1315       gBrowserThumbnails.uninit();
  1316       FullZoom.destroy();
  1318       Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
  1319       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
  1320       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
  1321       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
  1322       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
  1323       Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
  1324       Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
  1326       try {
  1327         gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
  1328       } catch (ex) {
  1329         Cu.reportError(ex);
  1332       if (typeof WindowsPrefSync !== 'undefined') {
  1333         WindowsPrefSync.uninit();
  1336       BrowserOffline.uninit();
  1337       OfflineApps.uninit();
  1338       IndexedDBPromptHelper.uninit();
  1339       CanvasPermissionPromptHelper.uninit();
  1340       LightweightThemeListener.uninit();
  1341       PanelUI.uninit();
  1344     // Final window teardown, do this last.
  1345     window.XULBrowserWindow = null;
  1346     window.QueryInterface(Ci.nsIInterfaceRequestor)
  1347           .getInterface(Ci.nsIWebNavigation)
  1348           .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
  1349           .QueryInterface(Ci.nsIInterfaceRequestor)
  1350           .getInterface(Ci.nsIXULWindow)
  1351           .XULBrowserWindow = null;
  1352     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
  1353   },
  1355 #ifdef XP_MACOSX
  1356   // nonBrowserWindowStartup(), nonBrowserWindowDelayedStartup(), and
  1357   // nonBrowserWindowShutdown() are used for non-browser windows in
  1358   // macBrowserOverlay
  1359   nonBrowserWindowStartup: function() {
  1360     // Disable inappropriate commands / submenus
  1361     var disabledItems = ['Browser:SavePage',
  1362                          'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
  1363                          'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
  1364                          'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
  1365                          'viewHistorySidebar', 'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
  1366                          'View:PageInfo', 'Browser:ToggleTabView'];
  1367     var element;
  1369     for (let disabledItem of disabledItems) {
  1370       element = document.getElementById(disabledItem);
  1371       if (element)
  1372         element.setAttribute("disabled", "true");
  1375     // If no windows are active (i.e. we're the hidden window), disable the close, minimize
  1376     // and zoom menu commands as well
  1377     if (window.location.href == "chrome://browser/content/hiddenWindow.xul") {
  1378       var hiddenWindowDisabledItems = ['cmd_close', 'minimizeWindow', 'zoomWindow'];
  1379       for (let hiddenWindowDisabledItem of hiddenWindowDisabledItems) {
  1380         element = document.getElementById(hiddenWindowDisabledItem);
  1381         if (element)
  1382           element.setAttribute("disabled", "true");
  1385       // also hide the window-list separator
  1386       element = document.getElementById("sep-window-list");
  1387       element.setAttribute("hidden", "true");
  1389       // Setup the dock menu.
  1390       let dockMenuElement = document.getElementById("menu_mac_dockmenu");
  1391       if (dockMenuElement != null) {
  1392         let nativeMenu = Cc["@mozilla.org/widget/standalonenativemenu;1"]
  1393                          .createInstance(Ci.nsIStandaloneNativeMenu);
  1395         try {
  1396           nativeMenu.init(dockMenuElement);
  1398           let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
  1399                             .getService(Ci.nsIMacDockSupport);
  1400           dockSupport.dockMenu = nativeMenu;
  1402         catch (e) {
  1407     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
  1408       document.getElementById("macDockMenuNewWindow").hidden = true;
  1411     this._delayedStartupTimeoutId = setTimeout(this.nonBrowserWindowDelayedStartup.bind(this), 0);
  1412   },
  1414   nonBrowserWindowDelayedStartup: function() {
  1415     this._delayedStartupTimeoutId = null;
  1417     // initialise the offline listener
  1418     BrowserOffline.init();
  1420     // Set up Sanitize Item
  1421     this._initializeSanitizer();
  1423     // initialize the private browsing UI
  1424     gPrivateBrowsingUI.init();
  1426 #ifdef MOZ_SERVICES_SYNC
  1427     // initialize the sync UI
  1428     gSyncUI.init();
  1429 #endif
  1430   },
  1432   nonBrowserWindowShutdown: function() {
  1433     // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
  1434     // just cancel the pending timeout and return;
  1435     if (this._delayedStartupTimeoutId) {
  1436       clearTimeout(this._delayedStartupTimeoutId);
  1437       return;
  1440     BrowserOffline.uninit();
  1441   },
  1442 #endif
  1444   _initializeSanitizer: function() {
  1445     const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize";
  1446     if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) {
  1447       gPrefService.clearUserPref(kDidSanitizeDomain);
  1448       // We need to persist this preference change, since we want to
  1449       // check it at next app start even if the browser exits abruptly
  1450       gPrefService.savePrefFile(null);
  1453     /**
  1454      * Migrate Firefox 3.0 privacy.item prefs under one of these conditions:
  1456      * a) User has customized any privacy.item prefs
  1457      * b) privacy.sanitize.sanitizeOnShutdown is set
  1458      */
  1459     if (!gPrefService.getBoolPref("privacy.sanitize.migrateFx3Prefs")) {
  1460       let itemBranch = gPrefService.getBranch("privacy.item.");
  1461       let itemArray = itemBranch.getChildList("");
  1463       // See if any privacy.item prefs are set
  1464       let doMigrate = itemArray.some(function (name) itemBranch.prefHasUserValue(name));
  1465       // Or if sanitizeOnShutdown is set
  1466       if (!doMigrate)
  1467         doMigrate = gPrefService.getBoolPref("privacy.sanitize.sanitizeOnShutdown");
  1469       if (doMigrate) {
  1470         let cpdBranch = gPrefService.getBranch("privacy.cpd.");
  1471         let clearOnShutdownBranch = gPrefService.getBranch("privacy.clearOnShutdown.");
  1472         for (let name of itemArray) {
  1473           try {
  1474             // don't migrate password or offlineApps clearing in the CRH dialog since
  1475             // there's no UI for those anymore. They default to false. bug 497656
  1476             if (name != "passwords" && name != "offlineApps")
  1477               cpdBranch.setBoolPref(name, itemBranch.getBoolPref(name));
  1478             clearOnShutdownBranch.setBoolPref(name, itemBranch.getBoolPref(name));
  1480           catch(e) {
  1481             Cu.reportError("Exception thrown during privacy pref migration: " + e);
  1486       gPrefService.setBoolPref("privacy.sanitize.migrateFx3Prefs", true);
  1488   },
  1492 /* Legacy global init functions */
  1493 var BrowserStartup        = gBrowserInit.onLoad.bind(gBrowserInit);
  1494 var BrowserShutdown       = gBrowserInit.onUnload.bind(gBrowserInit);
  1495 #ifdef XP_MACOSX
  1496 var nonBrowserWindowStartup        = gBrowserInit.nonBrowserWindowStartup.bind(gBrowserInit);
  1497 var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup.bind(gBrowserInit);
  1498 var nonBrowserWindowShutdown       = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
  1499 #endif
  1501 function HandleAppCommandEvent(evt) {
  1502   switch (evt.command) {
  1503   case "Back":
  1504     BrowserBack();
  1505     break;
  1506   case "Forward":
  1507     BrowserForward();
  1508     break;
  1509   case "Reload":
  1510     BrowserReloadSkipCache();
  1511     break;
  1512   case "Stop":
  1513     if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
  1514       BrowserStop();
  1515     break;
  1516   case "Search":
  1517     BrowserSearch.webSearch();
  1518     break;
  1519   case "Bookmarks":
  1520     toggleSidebar('viewBookmarksSidebar');
  1521     break;
  1522   case "Home":
  1523     BrowserHome();
  1524     break;
  1525   case "New":
  1526     BrowserOpenTab();
  1527     break;
  1528   case "Close":
  1529     BrowserCloseTabOrWindow();
  1530     break;
  1531   case "Find":
  1532     gFindBar.onFindCommand();
  1533     break;
  1534   case "Help":
  1535     openHelpLink('firefox-help');
  1536     break;
  1537   case "Open":
  1538     BrowserOpenFileWindow();
  1539     break;
  1540   case "Print":
  1541     PrintUtils.print();
  1542     break;
  1543   case "Save":
  1544     saveDocument(window.content.document);
  1545     break;
  1546   case "SendMail":
  1547     MailIntegration.sendLinkForWindow(window.content);
  1548     break;
  1549   default:
  1550     return;
  1552   evt.stopPropagation();
  1553   evt.preventDefault();
  1556 function gotoHistoryIndex(aEvent) {
  1557   let index = aEvent.target.getAttribute("index");
  1558   if (!index)
  1559     return false;
  1561   let where = whereToOpenLink(aEvent);
  1563   if (where == "current") {
  1564     // Normal click. Go there in the current tab and update session history.
  1566     try {
  1567       gBrowser.gotoIndex(index);
  1569     catch(ex) {
  1570       return false;
  1572     return true;
  1574   // Modified click. Go there in a new tab/window.
  1576   duplicateTabIn(gBrowser.selectedTab, where, index - gBrowser.sessionHistory.index);
  1577   return true;
  1580 function BrowserForward(aEvent) {
  1581   let where = whereToOpenLink(aEvent, false, true);
  1583   if (where == "current") {
  1584     try {
  1585       gBrowser.goForward();
  1587     catch(ex) {
  1590   else {
  1591     duplicateTabIn(gBrowser.selectedTab, where, 1);
  1595 function BrowserBack(aEvent) {
  1596   let where = whereToOpenLink(aEvent, false, true);
  1598   if (where == "current") {
  1599     try {
  1600       gBrowser.goBack();
  1602     catch(ex) {
  1605   else {
  1606     duplicateTabIn(gBrowser.selectedTab, where, -1);
  1610 function BrowserHandleBackspace()
  1612   switch (gPrefService.getIntPref("browser.backspace_action")) {
  1613   case 0:
  1614     BrowserBack();
  1615     break;
  1616   case 1:
  1617     goDoCommand("cmd_scrollPageUp");
  1618     break;
  1622 function BrowserHandleShiftBackspace()
  1624   switch (gPrefService.getIntPref("browser.backspace_action")) {
  1625   case 0:
  1626     BrowserForward();
  1627     break;
  1628   case 1:
  1629     goDoCommand("cmd_scrollPageDown");
  1630     break;
  1634 function BrowserStop() {
  1635   const stopFlags = nsIWebNavigation.STOP_ALL;
  1636   gBrowser.webNavigation.stop(stopFlags);
  1639 function BrowserReloadOrDuplicate(aEvent) {
  1640   var backgroundTabModifier = aEvent.button == 1 ||
  1641 #ifdef XP_MACOSX
  1642     aEvent.metaKey;
  1643 #else
  1644     aEvent.ctrlKey;
  1645 #endif
  1646   if (aEvent.shiftKey && !backgroundTabModifier) {
  1647     BrowserReloadSkipCache();
  1648     return;
  1651   let where = whereToOpenLink(aEvent, false, true);
  1652   if (where == "current")
  1653     BrowserReload();
  1654   else
  1655     duplicateTabIn(gBrowser.selectedTab, where);
  1658 function BrowserReload() {
  1659   const reloadFlags = nsIWebNavigation.LOAD_FLAGS_NONE;
  1660   BrowserReloadWithFlags(reloadFlags);
  1663 function BrowserReloadSkipCache() {
  1664   // Bypass proxy and cache.
  1665   const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
  1666   BrowserReloadWithFlags(reloadFlags);
  1669 var BrowserHome = BrowserGoHome;
  1670 function BrowserGoHome(aEvent) {
  1671   if (aEvent && "button" in aEvent &&
  1672       aEvent.button == 2) // right-click: do nothing
  1673     return;
  1675   var homePage = gHomeButton.getHomePage();
  1676   var where = whereToOpenLink(aEvent, false, true);
  1677   var urls;
  1679   // Home page should open in a new tab when current tab is an app tab
  1680   if (where == "current" &&
  1681       gBrowser &&
  1682       gBrowser.selectedTab.pinned)
  1683     where = "tab";
  1685   // openUILinkIn in utilityOverlay.js doesn't handle loading multiple pages
  1686   switch (where) {
  1687   case "current":
  1688     loadOneOrMoreURIs(homePage);
  1689     break;
  1690   case "tabshifted":
  1691   case "tab":
  1692     urls = homePage.split("|");
  1693     var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
  1694     gBrowser.loadTabs(urls, loadInBackground);
  1695     break;
  1696   case "window":
  1697     OpenBrowserWindow();
  1698     break;
  1702 function loadOneOrMoreURIs(aURIString)
  1704 #ifdef XP_MACOSX
  1705   // we're not a browser window, pass the URI string to a new browser window
  1706   if (window.location.href != getBrowserURL())
  1708     window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
  1709     return;
  1711 #endif
  1712   // This function throws for certain malformed URIs, so use exception handling
  1713   // so that we don't disrupt startup
  1714   try {
  1715     gBrowser.loadTabs(aURIString.split("|"), false, true);
  1717   catch (e) {
  1721 function focusAndSelectUrlBar() {
  1722   if (gURLBar) {
  1723     if (window.fullScreen)
  1724       FullScreen.mouseoverToggle(true);
  1726     gURLBar.select();
  1727     if (document.activeElement == gURLBar.inputField)
  1728       return true;
  1730   return false;
  1733 function openLocation() {
  1734   if (focusAndSelectUrlBar())
  1735     return;
  1737 #ifdef XP_MACOSX
  1738   if (window.location.href != getBrowserURL()) {
  1739     var win = getTopWin();
  1740     if (win) {
  1741       // If there's an open browser window, it should handle this command
  1742       win.focus()
  1743       win.openLocation();
  1745     else {
  1746       // If there are no open browser windows, open a new one
  1747       window.openDialog("chrome://browser/content/", "_blank",
  1748                         "chrome,all,dialog=no", BROWSER_NEW_TAB_URL);
  1751 #endif
  1754 function BrowserOpenTab()
  1756   openUILinkIn(BROWSER_NEW_TAB_URL, "tab");
  1759 /* Called from the openLocation dialog. This allows that dialog to instruct
  1760    its opener to open a new window and then step completely out of the way.
  1761    Anything less byzantine is causing horrible crashes, rather believably,
  1762    though oddly only on Linux. */
  1763 function delayedOpenWindow(chrome, flags, href, postData)
  1765   // The other way to use setTimeout,
  1766   // setTimeout(openDialog, 10, chrome, "_blank", flags, url),
  1767   // doesn't work here.  The extra "magic" extra argument setTimeout adds to
  1768   // the callback function would confuse gBrowserInit.onLoad() by making
  1769   // window.arguments[1] be an integer instead of null.
  1770   setTimeout(function() { openDialog(chrome, "_blank", flags, href, null, null, postData); }, 10);
  1773 /* Required because the tab needs time to set up its content viewers and get the load of
  1774    the URI kicked off before becoming the active content area. */
  1775 function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFixup)
  1777   gBrowser.loadOneTab(aUrl, {
  1778                       referrerURI: aReferrer,
  1779                       charset: aCharset,
  1780                       postData: aPostData,
  1781                       inBackground: false,
  1782                       allowThirdPartyFixup: aAllowThirdPartyFixup});
  1785 var gLastOpenDirectory = {
  1786   _lastDir: null,
  1787   get path() {
  1788     if (!this._lastDir || !this._lastDir.exists()) {
  1789       try {
  1790         this._lastDir = gPrefService.getComplexValue("browser.open.lastDir",
  1791                                                      Ci.nsILocalFile);
  1792         if (!this._lastDir.exists())
  1793           this._lastDir = null;
  1795       catch(e) {}
  1797     return this._lastDir;
  1798   },
  1799   set path(val) {
  1800     try {
  1801       if (!val || !val.isDirectory())
  1802         return;
  1803     } catch(e) {
  1804       return;
  1806     this._lastDir = val.clone();
  1808     // Don't save the last open directory pref inside the Private Browsing mode
  1809     if (!PrivateBrowsingUtils.isWindowPrivate(window))
  1810       gPrefService.setComplexValue("browser.open.lastDir", Ci.nsILocalFile,
  1811                                    this._lastDir);
  1812   },
  1813   reset: function() {
  1814     this._lastDir = null;
  1816 };
  1818 function BrowserOpenFileWindow()
  1820   // Get filepicker component.
  1821   try {
  1822     const nsIFilePicker = Ci.nsIFilePicker;
  1823     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  1824     let fpCallback = function fpCallback_done(aResult) {
  1825       if (aResult == nsIFilePicker.returnOK) {
  1826         try {
  1827           if (fp.file) {
  1828             gLastOpenDirectory.path =
  1829               fp.file.parent.QueryInterface(Ci.nsILocalFile);
  1831         } catch (ex) {
  1833         openUILinkIn(fp.fileURL.spec, "current");
  1835     };
  1837     fp.init(window, gNavigatorBundle.getString("openFile"),
  1838             nsIFilePicker.modeOpen);
  1839     fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText |
  1840                      nsIFilePicker.filterImages | nsIFilePicker.filterXML |
  1841                      nsIFilePicker.filterHTML);
  1842     fp.displayDirectory = gLastOpenDirectory.path;
  1843     fp.open(fpCallback);
  1844   } catch (ex) {
  1848 function BrowserCloseTabOrWindow() {
  1849 #ifdef XP_MACOSX
  1850   // If we're not a browser window, just close the window
  1851   if (window.location.href != getBrowserURL()) {
  1852     closeWindow(true);
  1853     return;
  1855 #endif
  1857   // If the current tab is the last one, this will close the window.
  1858   gBrowser.removeCurrentTab({animate: true});
  1861 function BrowserTryToCloseWindow()
  1863   if (WindowIsClosing())
  1864     window.close();     // WindowIsClosing does all the necessary checks
  1867 function loadURI(uri, referrer, postData, allowThirdPartyFixup) {
  1868   if (postData === undefined)
  1869     postData = null;
  1871   var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
  1872   if (allowThirdPartyFixup) {
  1873     flags |= nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
  1874     flags |= nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
  1877   try {
  1878     gBrowser.loadURIWithFlags(uri, flags, referrer, null, postData);
  1879   } catch (e) {}
  1882 function getShortcutOrURIAndPostData(aURL, aCallback) {
  1883   let mayInheritPrincipal = false;
  1884   let postData = null;
  1885   let shortcutURL = null;
  1886   let keyword = aURL;
  1887   let param = "";
  1889   let offset = aURL.indexOf(" ");
  1890   if (offset > 0) {
  1891     keyword = aURL.substr(0, offset);
  1892     param = aURL.substr(offset + 1);
  1895   let engine = Services.search.getEngineByAlias(keyword);
  1896   if (engine) {
  1897     let submission = engine.getSubmission(param);
  1898     postData = submission.postData;
  1899     aCallback({ postData: submission.postData, url: submission.uri.spec,
  1900                 mayInheritPrincipal: mayInheritPrincipal });
  1901     return;
  1904   [shortcutURL, postData] =
  1905     PlacesUtils.getURLAndPostDataForKeyword(keyword);
  1907   if (!shortcutURL) {
  1908     aCallback({ postData: postData, url: aURL,
  1909                 mayInheritPrincipal: mayInheritPrincipal });
  1910     return;
  1913   let escapedPostData = "";
  1914   if (postData)
  1915     escapedPostData = unescape(postData);
  1917   if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
  1918     let charset = "";
  1919     const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
  1920     let matches = shortcutURL.match(re);
  1922     let continueOperation = function () {
  1923       // encodeURIComponent produces UTF-8, and cannot be used for other charsets.
  1924       // escape() works in those cases, but it doesn't uri-encode +, @, and /.
  1925       // Therefore we need to manually replace these ASCII characters by their
  1926       // encodeURIComponent result, to match the behavior of nsEscape() with
  1927       // url_XPAlphas
  1928       let encodedParam = "";
  1929       if (charset && charset != "UTF-8")
  1930         encodedParam = escape(convertFromUnicode(charset, param)).
  1931                        replace(/[+@\/]+/g, encodeURIComponent);
  1932       else // Default charset is UTF-8
  1933         encodedParam = encodeURIComponent(param);
  1935       shortcutURL = shortcutURL.replace(/%s/g, encodedParam).replace(/%S/g, param);
  1937       if (/%s/i.test(escapedPostData)) // POST keyword
  1938         postData = getPostDataStream(escapedPostData, param, encodedParam,
  1939                                                "application/x-www-form-urlencoded");
  1941       // This URL came from a bookmark, so it's safe to let it inherit the current
  1942       // document's principal.
  1943       mayInheritPrincipal = true;
  1945       aCallback({ postData: postData, url: shortcutURL,
  1946                   mayInheritPrincipal: mayInheritPrincipal });
  1949     if (matches) {
  1950       [, shortcutURL, charset] = matches;
  1951       continueOperation();
  1952     } else {
  1953       // Try to get the saved character-set.
  1954       // makeURI throws if URI is invalid.
  1955       // Will return an empty string if character-set is not found.
  1956       try {
  1957         PlacesUtils.getCharsetForURI(makeURI(shortcutURL))
  1958                    .then(c => { charset = c; continueOperation(); });
  1959       } catch (ex) {
  1960         continueOperation();
  1964   else if (param) {
  1965     // This keyword doesn't take a parameter, but one was provided. Just return
  1966     // the original URL.
  1967     postData = null;
  1969     aCallback({ postData: postData, url: aURL,
  1970                 mayInheritPrincipal: mayInheritPrincipal });
  1971   } else {
  1972     // This URL came from a bookmark, so it's safe to let it inherit the current
  1973     // document's principal.
  1974     mayInheritPrincipal = true;
  1976     aCallback({ postData: postData, url: shortcutURL,
  1977                 mayInheritPrincipal: mayInheritPrincipal });
  1981 function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
  1982   var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
  1983                    createInstance(Ci.nsIStringInputStream);
  1984   aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
  1985   dataStream.data = aStringData;
  1987   var mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"].
  1988                    createInstance(Ci.nsIMIMEInputStream);
  1989   mimeStream.addHeader("Content-Type", aType);
  1990   mimeStream.addContentLength = true;
  1991   mimeStream.setData(dataStream);
  1992   return mimeStream.QueryInterface(Ci.nsIInputStream);
  1995 function getLoadContext() {
  1996   return window.QueryInterface(Ci.nsIInterfaceRequestor)
  1997                .getInterface(Ci.nsIWebNavigation)
  1998                .QueryInterface(Ci.nsILoadContext);
  2001 function readFromClipboard()
  2003   var url;
  2005   try {
  2006     // Create transferable that will transfer the text.
  2007     var trans = Components.classes["@mozilla.org/widget/transferable;1"]
  2008                           .createInstance(Components.interfaces.nsITransferable);
  2009     trans.init(getLoadContext());
  2011     trans.addDataFlavor("text/unicode");
  2013     // If available, use selection clipboard, otherwise global one
  2014     if (Services.clipboard.supportsSelectionClipboard())
  2015       Services.clipboard.getData(trans, Services.clipboard.kSelectionClipboard);
  2016     else
  2017       Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard);
  2019     var data = {};
  2020     var dataLen = {};
  2021     trans.getTransferData("text/unicode", data, dataLen);
  2023     if (data) {
  2024       data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
  2025       url = data.data.substring(0, dataLen.value / 2);
  2027   } catch (ex) {
  2030   return url;
  2033 function BrowserViewSourceOfDocument(aDocument)
  2035   var pageCookie;
  2036   var webNav;
  2038   // Get the document charset
  2039   var docCharset = "charset=" + aDocument.characterSet;
  2041   // Get the nsIWebNavigation associated with the document
  2042   try {
  2043       var win;
  2044       var ifRequestor;
  2046       // Get the DOMWindow for the requested document.  If the DOMWindow
  2047       // cannot be found, then just use the content window...
  2048       //
  2049       // XXX:  This is a bit of a hack...
  2050       win = aDocument.defaultView;
  2051       if (win == window) {
  2052         win = content;
  2054       ifRequestor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  2056       webNav = ifRequestor.getInterface(nsIWebNavigation);
  2057   } catch(err) {
  2058       // If nsIWebNavigation cannot be found, just get the one for the whole
  2059       // window...
  2060       webNav = gBrowser.webNavigation;
  2062   //
  2063   // Get the 'PageDescriptor' for the current document. This allows the
  2064   // view-source to access the cached copy of the content rather than
  2065   // refetching it from the network...
  2066   //
  2067   try{
  2068     var PageLoader = webNav.QueryInterface(Components.interfaces.nsIWebPageDescriptor);
  2070     pageCookie = PageLoader.currentDescriptor;
  2071   } catch(err) {
  2072     // If no page descriptor is available, just use the view-source URL...
  2075   top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
  2078 // doc - document to use for source, or null for this window's document
  2079 // initialTab - name of the initial tab to display, or null for the first tab
  2080 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
  2081 function BrowserPageInfo(doc, initialTab, imageElement) {
  2082   var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
  2083   var windows = Services.wm.getEnumerator("Browser:page-info");
  2085   var documentURL = doc ? doc.location : window.content.document.location;
  2087   // Check for windows matching the url
  2088   while (windows.hasMoreElements()) {
  2089     var currentWindow = windows.getNext();
  2090     if (currentWindow.closed) {
  2091       continue;
  2093     if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
  2094       currentWindow.focus();
  2095       currentWindow.resetPageInfo(args);
  2096       return currentWindow;
  2100   // We didn't find a matching window, so open a new one.
  2101   return openDialog("chrome://browser/content/pageinfo/pageInfo.xul", "",
  2102                     "chrome,toolbar,dialog=no,resizable", args);
  2105 function URLBarSetURI(aURI) {
  2106   var value = gBrowser.userTypedValue;
  2107   var valid = false;
  2109   if (value == null) {
  2110     let uri = aURI || gBrowser.currentURI;
  2111     // Strip off "wyciwyg://" and passwords for the location bar
  2112     try {
  2113       uri = Services.uriFixup.createExposableURI(uri);
  2114     } catch (e) {}
  2116     // Replace initial page URIs with an empty string
  2117     // only if there's no opener (bug 370555).
  2118     // Bug 863515 - Make content.opener checks work in electrolysis.
  2119     if (gInitialPages.indexOf(uri.spec) != -1)
  2120       value = !gMultiProcessBrowser && content.opener ? uri.spec : "";
  2121     else
  2122       value = losslessDecodeURI(uri);
  2124     valid = !isBlankPageURL(uri.spec);
  2127   gURLBar.value = value;
  2128   gURLBar.valueIsTyped = !valid;
  2129   SetPageProxyState(valid ? "valid" : "invalid");
  2132 function losslessDecodeURI(aURI) {
  2133   var value = aURI.spec;
  2134   // Try to decode as UTF-8 if there's no encoding sequence that we would break.
  2135   if (!/%25(?:3B|2F|3F|3A|40|26|3D|2B|24|2C|23)/i.test(value))
  2136     try {
  2137       value = decodeURI(value)
  2138                 // 1. decodeURI decodes %25 to %, which creates unintended
  2139                 //    encoding sequences. Re-encode it, unless it's part of
  2140                 //    a sequence that survived decodeURI, i.e. one for:
  2141                 //    ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#'
  2142                 //    (RFC 3987 section 3.2)
  2143                 // 2. Re-encode whitespace so that it doesn't get eaten away
  2144                 //    by the location bar (bug 410726).
  2145                 .replace(/%(?!3B|2F|3F|3A|40|26|3D|2B|24|2C|23)|[\r\n\t]/ig,
  2146                          encodeURIComponent);
  2147     } catch (e) {}
  2149   // Encode invisible characters (C0/C1 control characters, U+007F [DEL],
  2150   // U+00A0 [no-break space], line and paragraph separator,
  2151   // object replacement character) (bug 452979, bug 909264)
  2152   value = value.replace(/[\u0000-\u001f\u007f-\u00a0\u2028\u2029\ufffc]/g,
  2153                         encodeURIComponent);
  2155   // Encode default ignorable characters (bug 546013)
  2156   // except ZWNJ (U+200C) and ZWJ (U+200D) (bug 582186).
  2157   // This includes all bidirectional formatting characters.
  2158   // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
  2159   value = value.replace(/[\u00ad\u034f\u061c\u115f-\u1160\u17b4-\u17b5\u180b-\u180d\u200b\u200e-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g,
  2160                         encodeURIComponent);
  2161   return value;
  2164 function UpdateUrlbarSearchSplitterState()
  2166   var splitter = document.getElementById("urlbar-search-splitter");
  2167   var urlbar = document.getElementById("urlbar-container");
  2168   var searchbar = document.getElementById("search-container");
  2170   if (document.documentElement.getAttribute("customizing") == "true") {
  2171     if (splitter) {
  2172       splitter.remove();
  2174     return;
  2177   // If the splitter is already in the right place, we don't need to do anything:
  2178   if (splitter &&
  2179       ((splitter.nextSibling == searchbar && splitter.previousSibling == urlbar) ||
  2180        (splitter.nextSibling == urlbar && splitter.previousSibling == searchbar))) {
  2181     return;
  2184   var ibefore = null;
  2185   if (urlbar && searchbar) {
  2186     if (urlbar.nextSibling == searchbar)
  2187       ibefore = searchbar;
  2188     else if (searchbar.nextSibling == urlbar)
  2189       ibefore = urlbar;
  2192   if (ibefore) {
  2193     if (!splitter) {
  2194       splitter = document.createElement("splitter");
  2195       splitter.id = "urlbar-search-splitter";
  2196       splitter.setAttribute("resizebefore", "flex");
  2197       splitter.setAttribute("resizeafter", "flex");
  2198       splitter.setAttribute("skipintoolbarset", "true");
  2199       splitter.setAttribute("overflows", "false");
  2200       splitter.className = "chromeclass-toolbar-additional";
  2202     urlbar.parentNode.insertBefore(splitter, ibefore);
  2203   } else if (splitter)
  2204     splitter.parentNode.removeChild(splitter);
  2207 function UpdatePageProxyState()
  2209   if (gURLBar && gURLBar.value != gLastValidURLStr)
  2210     SetPageProxyState("invalid");
  2213 function SetPageProxyState(aState)
  2215   BookmarkingUI.onPageProxyStateChanged(aState);
  2217   if (!gURLBar)
  2218     return;
  2220   if (!gProxyFavIcon)
  2221     gProxyFavIcon = document.getElementById("page-proxy-favicon");
  2223   gURLBar.setAttribute("pageproxystate", aState);
  2224   gProxyFavIcon.setAttribute("pageproxystate", aState);
  2226   // the page proxy state is set to valid via OnLocationChange, which
  2227   // gets called when we switch tabs.
  2228   if (aState == "valid") {
  2229     gLastValidURLStr = gURLBar.value;
  2230     gURLBar.addEventListener("input", UpdatePageProxyState, false);
  2231   } else if (aState == "invalid") {
  2232     gURLBar.removeEventListener("input", UpdatePageProxyState, false);
  2236 function PageProxyClickHandler(aEvent)
  2238   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
  2239     middleMousePaste(aEvent);
  2242 /**
  2243  * Handle command events bubbling up from error page content
  2244  * or from about:newtab
  2245  */
  2246 let BrowserOnClick = {
  2247   handleEvent: function BrowserOnClick_handleEvent(aEvent) {
  2248     if (!aEvent.isTrusted || // Don't trust synthetic events
  2249         aEvent.button == 2) {
  2250       return;
  2253     let originalTarget = aEvent.originalTarget;
  2254     let ownerDoc = originalTarget.ownerDocument;
  2256     // If the event came from an ssl error page, it is probably either the "Add
  2257     // Exception…" or "Get me out of here!" button
  2258     if (ownerDoc.documentURI.startsWith("about:certerror")) {
  2259       this.onAboutCertError(originalTarget, ownerDoc);
  2261     else if (ownerDoc.documentURI.startsWith("about:blocked")) {
  2262       this.onAboutBlocked(originalTarget, ownerDoc);
  2264     else if (ownerDoc.documentURI.startsWith("about:neterror")) {
  2265       this.onAboutNetError(originalTarget, ownerDoc);
  2267     else if (gMultiProcessBrowser &&
  2268              ownerDoc.documentURI.toLowerCase() == "about:newtab") {
  2269       this.onE10sAboutNewTab(aEvent, ownerDoc);
  2271     else if (ownerDoc.documentURI.startsWith("about:tabcrashed")) {
  2272       this.onAboutTabCrashed(aEvent, ownerDoc);
  2274   },
  2276   onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
  2277     let elmId = aTargetElm.getAttribute("id");
  2278     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
  2279     let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
  2281     switch (elmId) {
  2282       case "exceptionDialogButton":
  2283         if (isTopFrame) {
  2284           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
  2286         let params = { exceptionAdded : false };
  2288         try {
  2289           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
  2290             case 2 : // Pre-fetch & pre-populate
  2291               params.prefetchCert = true;
  2292             case 1 : // Pre-populate
  2293               params.location = aOwnerDoc.location.href;
  2295         } catch (e) {
  2296           Components.utils.reportError("Couldn't get ssl_override pref: " + e);
  2299         window.openDialog('chrome://pippki/content/exceptionDialog.xul',
  2300                           '','chrome,centerscreen,modal', params);
  2302         // If the user added the exception cert, attempt to reload the page
  2303         if (params.exceptionAdded) {
  2304           aOwnerDoc.location.reload();
  2306         break;
  2308       case "getMeOutOfHereButton":
  2309         if (isTopFrame) {
  2310           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_GET_ME_OUT_OF_HERE);
  2312         getMeOutOfHere();
  2313         break;
  2315       case "technicalContent":
  2316         if (isTopFrame) {
  2317           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_TECHNICAL_DETAILS);
  2319         break;
  2321       case "expertContent":
  2322         if (isTopFrame) {
  2323           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
  2325         break;
  2328   },
  2330   onAboutBlocked: function BrowserOnClick_onAboutBlocked(aTargetElm, aOwnerDoc) {
  2331     let elmId = aTargetElm.getAttribute("id");
  2332     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
  2334     // The event came from a button on a malware/phishing block page
  2335     // First check whether it's malware or phishing, so that we can
  2336     // use the right strings/links
  2337     let isMalware = /e=malwareBlocked/.test(aOwnerDoc.documentURI);
  2338     let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
  2339     let nsISecTel = Ci.nsISecurityUITelemetry;
  2340     let isIframe = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
  2341     bucketName += isIframe ? "TOP_" : "FRAME_";
  2343     switch (elmId) {
  2344       case "getMeOutButton":
  2345         secHistogram.add(nsISecTel[bucketName + "GET_ME_OUT_OF_HERE"]);
  2346         getMeOutOfHere();
  2347         break;
  2349       case "reportButton":
  2350         // This is the "Why is this site blocked" button.  For malware,
  2351         // we can fetch a site-specific report, for phishing, we redirect
  2352         // to the generic page describing phishing protection.
  2354         // We log even if malware/phishing info URL couldn't be found:
  2355         // the measurement is for how many users clicked the WHY BLOCKED button
  2356         secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
  2358         if (isMalware) {
  2359           // Get the stop badware "why is this blocked" report url,
  2360           // append the current url, and go there.
  2361           try {
  2362             let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
  2363             reportURL += aOwnerDoc.location.href;
  2364             content.location = reportURL;
  2365           } catch (e) {
  2366             Components.utils.reportError("Couldn't get malware report URL: " + e);
  2369         else { // It's a phishing site, not malware
  2370           openHelpLink("phishing-malware", false, "current");
  2372         break;
  2374       case "ignoreWarningButton":
  2375         secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
  2376         this.ignoreWarningButton(isMalware);
  2377         break;
  2379   },
  2381   /**
  2382    * This functions prevents navigation from happening directly through the <a>
  2383    * link in about:newtab (which is loaded in the parent and therefore would load
  2384    * the next page also in the parent) and instructs the browser to open the url
  2385    * in the current tab which will make it update the remoteness of the tab.
  2386    */
  2387   onE10sAboutNewTab: function(aEvent, aOwnerDoc) {
  2388     let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
  2389     if (!isTopFrame || aEvent.button != 0) {
  2390       return;
  2393     let anchorTarget = aEvent.originalTarget.parentNode;
  2395     if (anchorTarget instanceof HTMLAnchorElement &&
  2396         anchorTarget.classList.contains("newtab-link")) {
  2397       aEvent.preventDefault();
  2398       openUILinkIn(anchorTarget.href, "current");
  2400   },
  2402   /**
  2403    * The about:tabcrashed can't do window.reload() because that
  2404    * would reload the page but not use a remote browser.
  2405    */
  2406   onAboutTabCrashed: function(aEvent, aOwnerDoc) {
  2407     let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
  2408     if (!isTopFrame) {
  2409       return;
  2412     let button = aEvent.originalTarget;
  2413     if (button.id == "tryAgain") {
  2414 #ifdef MOZ_CRASHREPORTER
  2415       if (aOwnerDoc.getElementById("checkSendReport").checked) {
  2416         let browser = gBrowser.getBrowserForDocument(aOwnerDoc);
  2417         TabCrashReporter.submitCrashReport(browser);
  2419 #endif
  2420       openUILinkIn(button.getAttribute("url"), "current");
  2422   },
  2424   ignoreWarningButton: function BrowserOnClick_ignoreWarningButton(aIsMalware) {
  2425     // Allow users to override and continue through to the site,
  2426     // but add a notify bar as a reminder, so that they don't lose
  2427     // track after, e.g., tab switching.
  2428     gBrowser.loadURIWithFlags(content.location.href,
  2429                               nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
  2430                               null, null, null);
  2432     Services.perms.add(makeURI(content.location.href), "safe-browsing",
  2433                        Ci.nsIPermissionManager.ALLOW_ACTION,
  2434                        Ci.nsIPermissionManager.EXPIRE_SESSION);
  2436     let buttons = [{
  2437       label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
  2438       accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
  2439       callback: function() { getMeOutOfHere(); }
  2440     }];
  2442     let title;
  2443     if (aIsMalware) {
  2444       title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
  2445       buttons[1] = {
  2446         label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
  2447         accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
  2448         callback: function() {
  2449           openUILinkIn(gSafeBrowsing.getReportURL('MalwareError'), 'tab');
  2451       };
  2452     } else {
  2453       title = gNavigatorBundle.getString("safebrowsing.reportedWebForgery");
  2454       buttons[1] = {
  2455         label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
  2456         accessKey: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.accessKey"),
  2457         callback: function() {
  2458           openUILinkIn(gSafeBrowsing.getReportURL('Error'), 'tab');
  2460       };
  2463     let notificationBox = gBrowser.getNotificationBox();
  2464     let value = "blocked-badware-page";
  2466     let previousNotification = notificationBox.getNotificationWithValue(value);
  2467     if (previousNotification) {
  2468       notificationBox.removeNotification(previousNotification);
  2471     let notification = notificationBox.appendNotification(
  2472       title,
  2473       value,
  2474       "chrome://global/skin/icons/blacklist_favicon.png",
  2475       notificationBox.PRIORITY_CRITICAL_HIGH,
  2476       buttons
  2477     );
  2478     // Persist the notification until the user removes so it
  2479     // doesn't get removed on redirects.
  2480     notification.persistence = -1;
  2481   },
  2483   onAboutNetError: function BrowserOnClick_onAboutNetError(aTargetElm, aOwnerDoc) {
  2484     let elmId = aTargetElm.getAttribute("id");
  2485     if (elmId != "errorTryAgain" || !/e=netOffline/.test(aOwnerDoc.documentURI))
  2486       return;
  2487     Services.io.offline = false;
  2488   },
  2489 };
  2491 /**
  2492  * Re-direct the browser to a known-safe page.  This function is
  2493  * used when, for example, the user browses to a known malware page
  2494  * and is presented with about:blocked.  The "Get me out of here!"
  2495  * button should take the user to the default start page so that even
  2496  * when their own homepage is infected, we can get them somewhere safe.
  2497  */
  2498 function getMeOutOfHere() {
  2499   // Get the start page from the *default* pref branch, not the user's
  2500   var prefs = Services.prefs.getDefaultBranch(null);
  2501   var url = BROWSER_NEW_TAB_URL;
  2502   try {
  2503     url = prefs.getComplexValue("browser.startup.homepage",
  2504                                 Ci.nsIPrefLocalizedString).data;
  2505     // If url is a pipe-delimited set of pages, just take the first one.
  2506     if (url.contains("|"))
  2507       url = url.split("|")[0];
  2508   } catch(e) {
  2509     Components.utils.reportError("Couldn't get homepage pref: " + e);
  2511   content.location = url;
  2514 function BrowserFullScreen()
  2516   window.fullScreen = !window.fullScreen;
  2519 function _checkDefaultAndSwitchToMetro() {
  2520 #ifdef HAVE_SHELL_SERVICE
  2521 #ifdef XP_WIN
  2522 #ifdef MOZ_METRO
  2523   let shell = Components.classes["@mozilla.org/browser/shell-service;1"].
  2524     getService(Components.interfaces.nsIShellService);
  2525   let isDefault = shell.isDefaultBrowser(false, false);
  2527   if (isDefault) {
  2528     let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
  2529     getService(Components.interfaces.nsIAppStartup);
  2531     Services.prefs.setBoolPref('browser.sessionstore.resume_session_once', true);
  2533     let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
  2534                      .createInstance(Ci.nsISupportsPRBool);
  2535     Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
  2537     if (!cancelQuit.data) {
  2538       appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
  2539                       Components.interfaces.nsIAppStartup.eRestartTouchEnvironment);
  2541     return true;
  2543   return false;
  2544 #endif
  2545 #endif
  2546 #endif
  2549 function SwitchToMetro() {
  2550 #ifdef HAVE_SHELL_SERVICE
  2551 #ifdef XP_WIN
  2552 #ifdef MOZ_METRO
  2553   if (this._checkDefaultAndSwitchToMetro()) {
  2554     return;
  2557   let shell = Components.classes["@mozilla.org/browser/shell-service;1"].
  2558     getService(Components.interfaces.nsIShellService);
  2560   shell.setDefaultBrowser(false, false);
  2562   let intervalID = window.setInterval(this._checkDefaultAndSwitchToMetro, 1000);
  2563   window.setTimeout(function() { window.clearInterval(intervalID); }, 10000);
  2564 #endif
  2565 #endif
  2566 #endif
  2569 function onFullScreen(event) {
  2570   FullScreen.toggle(event);
  2573 function onMozEnteredDomFullscreen(event) {
  2574   FullScreen.enterDomFullscreen(event);
  2577 function getWebNavigation()
  2579   return gBrowser.webNavigation;
  2582 function BrowserReloadWithFlags(reloadFlags) {
  2583   let url = gBrowser.currentURI.spec;
  2584   if (gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, url)) {
  2585     // If the remoteness has changed, the new browser doesn't have any
  2586     // information of what was loaded before, so we need to load the previous
  2587     // URL again.
  2588     gBrowser.loadURIWithFlags(url, reloadFlags);
  2589     return;
  2592   /* First, we'll try to use the session history object to reload so
  2593    * that framesets are handled properly. If we're in a special
  2594    * window (such as view-source) that has no session history, fall
  2595    * back on using the web navigation's reload method.
  2596    */
  2598   var webNav = gBrowser.webNavigation;
  2599   try {
  2600     var sh = webNav.sessionHistory;
  2601     if (sh)
  2602       webNav = sh.QueryInterface(nsIWebNavigation);
  2603   } catch (e) {
  2606   try {
  2607     webNav.reload(reloadFlags);
  2608   } catch (e) {
  2612 var PrintPreviewListener = {
  2613   _printPreviewTab: null,
  2614   _tabBeforePrintPreview: null,
  2616   getPrintPreviewBrowser: function () {
  2617     if (!this._printPreviewTab) {
  2618       this._tabBeforePrintPreview = gBrowser.selectedTab;
  2619       this._printPreviewTab = gBrowser.loadOneTab("about:blank",
  2620                                                   { inBackground: false });
  2621       gBrowser.selectedTab = this._printPreviewTab;
  2623     return gBrowser.getBrowserForTab(this._printPreviewTab);
  2624   },
  2625   getSourceBrowser: function () {
  2626     return this._tabBeforePrintPreview ?
  2627       this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
  2628   },
  2629   getNavToolbox: function () {
  2630     return gNavToolbox;
  2631   },
  2632   onEnter: function () {
  2633     gInPrintPreviewMode = true;
  2634     this._toggleAffectedChrome();
  2635   },
  2636   onExit: function () {
  2637     gBrowser.selectedTab = this._tabBeforePrintPreview;
  2638     this._tabBeforePrintPreview = null;
  2639     gInPrintPreviewMode = false;
  2640     this._toggleAffectedChrome();
  2641     gBrowser.removeTab(this._printPreviewTab);
  2642     this._printPreviewTab = null;
  2643   },
  2644   _toggleAffectedChrome: function () {
  2645     gNavToolbox.collapsed = gInPrintPreviewMode;
  2647     if (gInPrintPreviewMode)
  2648       this._hideChrome();
  2649     else
  2650       this._showChrome();
  2652     if (this._chromeState.sidebarOpen)
  2653       toggleSidebar(this._sidebarCommand);
  2655     TabsInTitlebar.allowedBy("print-preview", !gInPrintPreviewMode);
  2656   },
  2657   _hideChrome: function () {
  2658     this._chromeState = {};
  2660     var sidebar = document.getElementById("sidebar-box");
  2661     this._chromeState.sidebarOpen = !sidebar.hidden;
  2662     this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
  2664     var notificationBox = gBrowser.getNotificationBox();
  2665     this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
  2666     notificationBox.notificationsHidden = true;
  2668     document.getElementById("sidebar").setAttribute("src", "about:blank");
  2669     gBrowser.updateWindowResizers();
  2671     this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
  2672     if (gFindBarInitialized)
  2673       gFindBar.close();
  2675     var globalNotificationBox = document.getElementById("global-notificationbox");
  2676     this._chromeState.globalNotificationsOpen = !globalNotificationBox.notificationsHidden;
  2677     globalNotificationBox.notificationsHidden = true;
  2679     this._chromeState.syncNotificationsOpen = false;
  2680     var syncNotifications = document.getElementById("sync-notifications");
  2681     if (syncNotifications) {
  2682       this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
  2683       syncNotifications.notificationsHidden = true;
  2685   },
  2686   _showChrome: function () {
  2687     if (this._chromeState.notificationsOpen)
  2688       gBrowser.getNotificationBox().notificationsHidden = false;
  2690     if (this._chromeState.findOpen)
  2691       gFindBar.open();
  2693     if (this._chromeState.globalNotificationsOpen)
  2694       document.getElementById("global-notificationbox").notificationsHidden = false;
  2696     if (this._chromeState.syncNotificationsOpen)
  2697       document.getElementById("sync-notifications").notificationsHidden = false;
  2701 function getMarkupDocumentViewer()
  2703   return gBrowser.markupDocumentViewer;
  2706 // This function is obsolete. Newer code should use <tooltip page="true"/> instead.
  2707 function FillInHTMLTooltip(tipElement)
  2709   document.getElementById("aHTMLTooltip").fillInPageTooltip(tipElement);
  2712 var browserDragAndDrop = {
  2713   canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
  2715   dragOver: function (aEvent)
  2717     if (this.canDropLink(aEvent)) {
  2718       aEvent.preventDefault();
  2720   },
  2722   drop: function (aEvent, aName, aDisallowInherit) {
  2723     return Services.droppedLinkHandler.dropLink(aEvent, aName, aDisallowInherit);
  2725 };
  2727 var homeButtonObserver = {
  2728   onDrop: function (aEvent)
  2730       // disallow setting home pages that inherit the principal
  2731       let url = browserDragAndDrop.drop(aEvent, {}, true);
  2732       setTimeout(openHomeDialog, 0, url);
  2733     },
  2735   onDragOver: function (aEvent)
  2737       browserDragAndDrop.dragOver(aEvent);
  2738       aEvent.dropEffect = "link";
  2739     },
  2740   onDragExit: function (aEvent)
  2745 function openHomeDialog(aURL)
  2747   var promptTitle = gNavigatorBundle.getString("droponhometitle");
  2748   var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
  2749   var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
  2750                           Services.prompt.STD_YES_NO_BUTTONS,
  2751                           null, null, null, null, {value:0});
  2753   if (pressedVal == 0) {
  2754     try {
  2755       var str = Components.classes["@mozilla.org/supports-string;1"]
  2756                           .createInstance(Components.interfaces.nsISupportsString);
  2757       str.data = aURL;
  2758       gPrefService.setComplexValue("browser.startup.homepage",
  2759                                    Components.interfaces.nsISupportsString, str);
  2760     } catch (ex) {
  2761       dump("Failed to set the home page.\n"+ex+"\n");
  2766 var newTabButtonObserver = {
  2767   onDragOver: function (aEvent)
  2769     browserDragAndDrop.dragOver(aEvent);
  2770   },
  2772   onDragExit: function (aEvent)
  2774   },
  2776   onDrop: function (aEvent)
  2778     let url = browserDragAndDrop.drop(aEvent, { });
  2779     getShortcutOrURIAndPostData(url, data => {
  2780       if (data.url) {
  2781         // allow third-party services to fixup this URL
  2782         openNewTabWith(data.url, null, data.postData, aEvent, true);
  2784     });
  2788 var newWindowButtonObserver = {
  2789   onDragOver: function (aEvent)
  2791     browserDragAndDrop.dragOver(aEvent);
  2792   },
  2793   onDragExit: function (aEvent)
  2795   },
  2796   onDrop: function (aEvent)
  2798     let url = browserDragAndDrop.drop(aEvent, { });
  2799     getShortcutOrURIAndPostData(url, data => {
  2800       if (data.url) {
  2801         // allow third-party services to fixup this URL
  2802         openNewWindowWith(data.url, null, data.postData, true);
  2804     });
  2808 const DOMLinkHandler = {
  2809   init: function() {
  2810     let mm = window.messageManager;
  2811     mm.addMessageListener("Link:AddFeed", this);
  2812     mm.addMessageListener("Link:AddIcon", this);
  2813     mm.addMessageListener("Link:AddSearch", this);
  2814   },
  2816   receiveMessage: function (aMsg) {
  2817     switch (aMsg.name) {
  2818       case "Link:AddFeed":
  2819         let link = {type: aMsg.data.type, href: aMsg.data.href, title: aMsg.data.title};
  2820         FeedHandler.addFeed(link, aMsg.target);
  2821         break;
  2823       case "Link:AddIcon":
  2824         return this.addIcon(aMsg.target, aMsg.data.url);
  2825         break;
  2827       case "Link:AddSearch":
  2828         this.addSearch(aMsg.target, aMsg.data.engine, aMsg.data.url);
  2829         break;
  2831   },
  2833   addIcon: function(aBrowser, aURL) {
  2834     if (gBrowser.isFailedIcon(aURL))
  2835       return false;
  2837     let tab = gBrowser._getTabForBrowser(aBrowser);
  2838     if (!tab)
  2839       return false;
  2841     gBrowser.setIcon(tab, aURL);
  2842     return true;
  2843   },
  2845   addSearch: function(aBrowser, aEngine, aURL) {
  2846     let tab = gBrowser._getTabForBrowser(aBrowser);
  2847     if (!tab)
  2848       return false;
  2850     BrowserSearch.addEngine(aBrowser, aEngine, makeURI(aURL));
  2851   },
  2854 const BrowserSearch = {
  2855   addEngine: function(browser, engine, uri) {
  2856     if (!this.searchBar)
  2857       return;
  2859     // Check to see whether we've already added an engine with this title
  2860     if (browser.engines) {
  2861       if (browser.engines.some(function (e) e.title == engine.title))
  2862         return;
  2865     // Append the URI and an appropriate title to the browser data.
  2866     // Use documentURIObject in the check for shouldLoadFavIcon so that we
  2867     // do the right thing with about:-style error pages.  Bug 453442
  2868     var iconURL = null;
  2869     if (gBrowser.shouldLoadFavIcon(uri))
  2870       iconURL = uri.prePath + "/favicon.ico";
  2872     var hidden = false;
  2873     // If this engine (identified by title) is already in the list, add it
  2874     // to the list of hidden engines rather than to the main list.
  2875     // XXX This will need to be changed when engines are identified by URL;
  2876     // see bug 335102.
  2877     if (Services.search.getEngineByName(engine.title))
  2878       hidden = true;
  2880     var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
  2882     engines.push({ uri: engine.href,
  2883                    title: engine.title,
  2884                    icon: iconURL });
  2886     if (hidden)
  2887       browser.hiddenEngines = engines;
  2888     else
  2889       browser.engines = engines;
  2890   },
  2892   /**
  2893    * Gives focus to the search bar, if it is present on the toolbar, or loads
  2894    * the default engine's search form otherwise. For Mac, opens a new window
  2895    * or focuses an existing window, if necessary.
  2896    */
  2897   webSearch: function BrowserSearch_webSearch() {
  2898 #ifdef XP_MACOSX
  2899     if (window.location.href != getBrowserURL()) {
  2900       var win = getTopWin();
  2901       if (win) {
  2902         // If there's an open browser window, it should handle this command
  2903         win.focus();
  2904         win.BrowserSearch.webSearch();
  2905       } else {
  2906         // If there are no open browser windows, open a new one
  2907         var observer = function observer(subject, topic, data) {
  2908           if (subject == win) {
  2909             BrowserSearch.webSearch();
  2910             Services.obs.removeObserver(observer, "browser-delayed-startup-finished");
  2913         win = window.openDialog(getBrowserURL(), "_blank",
  2914                                 "chrome,all,dialog=no", "about:blank");
  2915         Services.obs.addObserver(observer, "browser-delayed-startup-finished", false);
  2917       return;
  2919 #endif
  2920     let openSearchPageIfFieldIsNotActive = function(aSearchBar) {
  2921       if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField)
  2922         openUILinkIn("about:home", "current");
  2923     };
  2925     let searchBar = this.searchBar;
  2926     let placement = CustomizableUI.getPlacementOfWidget("search-container");
  2927     let focusSearchBar = () => {
  2928       searchBar = this.searchBar;
  2929       searchBar.select();
  2930       openSearchPageIfFieldIsNotActive(searchBar);
  2931     };
  2932     if (placement && placement.area == CustomizableUI.AREA_PANEL) {
  2933       // The panel is not constructed until the first time it is shown.
  2934       PanelUI.show().then(focusSearchBar);
  2935       return;
  2937     if (placement && placement.area == CustomizableUI.AREA_NAVBAR && searchBar &&
  2938         searchBar.parentNode.getAttribute("overflowedItem") == "true") {
  2939       let navBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
  2940       navBar.overflowable.show().then(() => {
  2941         focusSearchBar();
  2942       });
  2943       return;
  2945     if (searchBar) {
  2946       if (window.fullScreen)
  2947         FullScreen.mouseoverToggle(true);
  2948       searchBar.select();
  2950     openSearchPageIfFieldIsNotActive(searchBar);
  2951   },
  2953   /**
  2954    * Loads a search results page, given a set of search terms. Uses the current
  2955    * engine if the search bar is visible, or the default engine otherwise.
  2957    * @param searchText
  2958    *        The search terms to use for the search.
  2960    * @param useNewTab
  2961    *        Boolean indicating whether or not the search should load in a new
  2962    *        tab.
  2964    * @param purpose [optional]
  2965    *        A string meant to indicate the context of the search request. This
  2966    *        allows the search service to provide a different nsISearchSubmission
  2967    *        depending on e.g. where the search is triggered in the UI.
  2969    * @return engine The search engine used to perform a search, or null if no
  2970    *                search was performed.
  2971    */
  2972   _loadSearch: function (searchText, useNewTab, purpose) {
  2973     let engine;
  2975     // If the search bar is visible, use the current engine, otherwise, fall
  2976     // back to the default engine.
  2977     if (isElementVisible(this.searchBar))
  2978       engine = Services.search.currentEngine;
  2979     else
  2980       engine = Services.search.defaultEngine;
  2982     let submission = engine.getSubmission(searchText, null, purpose); // HTML response
  2984     // getSubmission can return null if the engine doesn't have a URL
  2985     // with a text/html response type.  This is unlikely (since
  2986     // SearchService._addEngineToStore() should fail for such an engine),
  2987     // but let's be on the safe side.
  2988     if (!submission) {
  2989       return null;
  2992     let inBackground = Services.prefs.getBoolPref("browser.search.context.loadInBackground");
  2993     openLinkIn(submission.uri.spec,
  2994                useNewTab ? "tab" : "current",
  2995                { postData: submission.postData,
  2996                  inBackground: inBackground,
  2997                  relatedToCurrent: true });
  2999     return engine;
  3000   },
  3002   /**
  3003    * Just like _loadSearch, but preserving an old API.
  3005    * @return string Name of the search engine used to perform a search or null
  3006    *         if a search was not performed.
  3007    */
  3008   loadSearch: function BrowserSearch_search(searchText, useNewTab, purpose) {
  3009     let engine = BrowserSearch._loadSearch(searchText, useNewTab, purpose);
  3010     if (!engine) {
  3011       return null;
  3013     return engine.name;
  3014   },
  3016   /**
  3017    * Perform a search initiated from the context menu.
  3019    * This should only be called from the context menu. See
  3020    * BrowserSearch.loadSearch for the preferred API.
  3021    */
  3022   loadSearchFromContext: function (terms) {
  3023     let engine = BrowserSearch._loadSearch(terms, true, "contextmenu");
  3024     if (engine) {
  3025       BrowserSearch.recordSearchInHealthReport(engine, "contextmenu");
  3027   },
  3029   /**
  3030    * Returns the search bar element if it is present in the toolbar, null otherwise.
  3031    */
  3032   get searchBar() {
  3033     return document.getElementById("searchbar");
  3034   },
  3036   loadAddEngines: function BrowserSearch_loadAddEngines() {
  3037     var newWindowPref = gPrefService.getIntPref("browser.link.open_newwindow");
  3038     var where = newWindowPref == 3 ? "tab" : "window";
  3039     var searchEnginesURL = formatURL("browser.search.searchEnginesURL", true);
  3040     openUILinkIn(searchEnginesURL, where);
  3041   },
  3043   /**
  3044    * Helper to record a search with Firefox Health Report.
  3046    * FHR records only search counts and nothing pertaining to the search itself.
  3048    * @param engine
  3049    *        (nsISearchEngine) The engine handling the search.
  3050    * @param source
  3051    *        (string) Where the search originated from. See the FHR
  3052    *        SearchesProvider for allowed values.
  3053    */
  3054   recordSearchInHealthReport: function (engine, source) {
  3055 #ifdef MOZ_SERVICES_HEALTHREPORT
  3056     let reporter = Cc["@mozilla.org/datareporting/service;1"]
  3057                      .getService()
  3058                      .wrappedJSObject
  3059                      .healthReporter;
  3061     // This can happen if the FHR component of the data reporting service is
  3062     // disabled. This is controlled by a pref that most will never use.
  3063     if (!reporter) {
  3064       return;
  3067     reporter.onInit().then(function record() {
  3068       try {
  3069         reporter.getProvider("org.mozilla.searches").recordSearch(engine, source);
  3070       } catch (ex) {
  3071         Cu.reportError(ex);
  3073     });
  3074 #endif
  3075   },
  3076 };
  3078 function FillHistoryMenu(aParent) {
  3079   // Lazily add the hover listeners on first showing and never remove them
  3080   if (!aParent.hasStatusListener) {
  3081     // Show history item's uri in the status bar when hovering, and clear on exit
  3082     aParent.addEventListener("DOMMenuItemActive", function(aEvent) {
  3083       // Only the current page should have the checked attribute, so skip it
  3084       if (!aEvent.target.hasAttribute("checked"))
  3085         XULBrowserWindow.setOverLink(aEvent.target.getAttribute("uri"));
  3086     }, false);
  3087     aParent.addEventListener("DOMMenuItemInactive", function() {
  3088       XULBrowserWindow.setOverLink("");
  3089     }, false);
  3091     aParent.hasStatusListener = true;
  3094   // Remove old entries if any
  3095   var children = aParent.childNodes;
  3096   for (var i = children.length - 1; i >= 0; --i) {
  3097     if (children[i].hasAttribute("index"))
  3098       aParent.removeChild(children[i]);
  3101   var webNav = gBrowser.webNavigation;
  3102   var sessionHistory = webNav.sessionHistory;
  3104   var count = sessionHistory.count;
  3105   if (count <= 1) // don't display the popup for a single item
  3106     return false;
  3108   const MAX_HISTORY_MENU_ITEMS = 15;
  3109   var index = sessionHistory.index;
  3110   var half_length = Math.floor(MAX_HISTORY_MENU_ITEMS / 2);
  3111   var start = Math.max(index - half_length, 0);
  3112   var end = Math.min(start == 0 ? MAX_HISTORY_MENU_ITEMS : index + half_length + 1, count);
  3113   if (end == count)
  3114     start = Math.max(count - MAX_HISTORY_MENU_ITEMS, 0);
  3116   var tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
  3117   var tooltipCurrent = gNavigatorBundle.getString("tabHistory.current");
  3118   var tooltipForward = gNavigatorBundle.getString("tabHistory.goForward");
  3120   for (var j = end - 1; j >= start; j--) {
  3121     let item = document.createElement("menuitem");
  3122     let entry = sessionHistory.getEntryAtIndex(j, false);
  3123     let uri = entry.URI.spec;
  3125     item.setAttribute("uri", uri);
  3126     item.setAttribute("label", entry.title || uri);
  3127     item.setAttribute("index", j);
  3129     if (j != index) {
  3130       PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) {
  3131         if (aURI) {
  3132           let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
  3133           item.style.listStyleImage = "url(" + iconURL + ")";
  3135       });
  3138     if (j < index) {
  3139       item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
  3140       item.setAttribute("tooltiptext", tooltipBack);
  3141     } else if (j == index) {
  3142       item.setAttribute("type", "radio");
  3143       item.setAttribute("checked", "true");
  3144       item.className = "unified-nav-current";
  3145       item.setAttribute("tooltiptext", tooltipCurrent);
  3146     } else {
  3147       item.className = "unified-nav-forward menuitem-iconic menuitem-with-favicon";
  3148       item.setAttribute("tooltiptext", tooltipForward);
  3151     aParent.appendChild(item);
  3153   return true;
  3156 function addToUrlbarHistory(aUrlToAdd) {
  3157   if (!PrivateBrowsingUtils.isWindowPrivate(window) &&
  3158       aUrlToAdd &&
  3159       !aUrlToAdd.contains(" ") &&
  3160       !/[\x00-\x1F]/.test(aUrlToAdd))
  3161     PlacesUIUtils.markPageAsTyped(aUrlToAdd);
  3164 function toJavaScriptConsole()
  3166   toOpenWindowByType("global:console", "chrome://global/content/console.xul");
  3169 function BrowserDownloadsUI()
  3171   Cc["@mozilla.org/download-manager-ui;1"].
  3172   getService(Ci.nsIDownloadManagerUI).show(window);
  3175 function toOpenWindowByType(inType, uri, features)
  3177   var topWindow = Services.wm.getMostRecentWindow(inType);
  3179   if (topWindow)
  3180     topWindow.focus();
  3181   else if (features)
  3182     window.open(uri, "_blank", features);
  3183   else
  3184     window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
  3187 function OpenBrowserWindow(options)
  3189   var telemetryObj = {};
  3190   TelemetryStopwatch.start("FX_NEW_WINDOW_MS", telemetryObj);
  3192   function newDocumentShown(doc, topic, data) {
  3193     if (topic == "document-shown" &&
  3194         doc != document &&
  3195         doc.defaultView == win) {
  3196       Services.obs.removeObserver(newDocumentShown, "document-shown");
  3197       Services.obs.removeObserver(windowClosed, "domwindowclosed");
  3198       TelemetryStopwatch.finish("FX_NEW_WINDOW_MS", telemetryObj);
  3202   function windowClosed(subject) {
  3203     if (subject == win) {
  3204       Services.obs.removeObserver(newDocumentShown, "document-shown");
  3205       Services.obs.removeObserver(windowClosed, "domwindowclosed");
  3209   // Make sure to remove the 'document-shown' observer in case the window
  3210   // is being closed right after it was opened to avoid leaking.
  3211   Services.obs.addObserver(newDocumentShown, "document-shown", false);
  3212   Services.obs.addObserver(windowClosed, "domwindowclosed", false);
  3214   var charsetArg = new String();
  3215   var handler = Components.classes["@mozilla.org/browser/clh;1"]
  3216                           .getService(Components.interfaces.nsIBrowserHandler);
  3217   var defaultArgs = handler.defaultArgs;
  3218   var wintype = document.documentElement.getAttribute('windowtype');
  3220   var extraFeatures = "";
  3221   if (options && options.private) {
  3222     extraFeatures = ",private";
  3223     if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
  3224       // Force the new window to load about:privatebrowsing instead of the default home page
  3225       defaultArgs = "about:privatebrowsing";
  3227   } else {
  3228     extraFeatures = ",non-private";
  3231   if (options && options.remote) {
  3232     let omtcEnabled = gPrefService.getBoolPref("layers.offmainthreadcomposition.enabled");
  3233     if (!omtcEnabled) {
  3234       alert("To use out-of-process tabs, you must set the layers.offmainthreadcomposition.enabled preference and restart. Opening a normal window instead.");
  3235     } else {
  3236       extraFeatures += ",remote";
  3238   } else if (options && options.remote === false) {
  3239     extraFeatures += ",non-remote";
  3242   // if and only if the current window is a browser window and it has a document with a character
  3243   // set, then extract the current charset menu setting from the current document and use it to
  3244   // initialize the new browser window...
  3245   var win;
  3246   if (window && (wintype == "navigator:browser") && window.content && window.content.document)
  3248     var DocCharset = window.content.document.characterSet;
  3249     charsetArg = "charset="+DocCharset;
  3251     //we should "inherit" the charset menu setting in a new window
  3252     win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no" + extraFeatures, defaultArgs, charsetArg);
  3254   else // forget about the charset information.
  3256     win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no" + extraFeatures, defaultArgs);
  3259   return win;
  3262 // Only here for backwards compat, we should remove this soon
  3263 function BrowserCustomizeToolbar() {
  3264   gCustomizeMode.enter();
  3267 /**
  3268  * Update the global flag that tracks whether or not any edit UI (the Edit menu,
  3269  * edit-related items in the context menu, and edit-related toolbar buttons
  3270  * is visible, then update the edit commands' enabled state accordingly.  We use
  3271  * this flag to skip updating the edit commands on focus or selection changes
  3272  * when no UI is visible to improve performance (including pageload performance,
  3273  * since focus changes when you load a new page).
  3275  * If UI is visible, we use goUpdateGlobalEditMenuItems to set the commands'
  3276  * enabled state so the UI will reflect it appropriately.
  3278  * If the UI isn't visible, we enable all edit commands so keyboard shortcuts
  3279  * still work and just lazily disable them as needed when the user presses a
  3280  * shortcut.
  3282  * This doesn't work on Mac, since Mac menus flash when users press their
  3283  * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
  3284  * and we need to always update the edit commands.  Thus on Mac this function
  3285  * is a no op.
  3286  */
  3287 function updateEditUIVisibility()
  3289 #ifndef XP_MACOSX
  3290   let editMenuPopupState = document.getElementById("menu_EditPopup").state;
  3291   let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
  3292   let placesContextMenuPopupState = document.getElementById("placesContext").state;
  3294   // The UI is visible if the Edit menu is opening or open, if the context menu
  3295   // is open, or if the toolbar has been customized to include the Cut, Copy,
  3296   // or Paste toolbar buttons.
  3297   gEditUIVisible = editMenuPopupState == "showing" ||
  3298                    editMenuPopupState == "open" ||
  3299                    contextMenuPopupState == "showing" ||
  3300                    contextMenuPopupState == "open" ||
  3301                    placesContextMenuPopupState == "showing" ||
  3302                    placesContextMenuPopupState == "open" ||
  3303                    document.getElementById("edit-controls") ? true : false;
  3305   // If UI is visible, update the edit commands' enabled state to reflect
  3306   // whether or not they are actually enabled for the current focus/selection.
  3307   if (gEditUIVisible)
  3308     goUpdateGlobalEditMenuItems();
  3310   // Otherwise, enable all commands, so that keyboard shortcuts still work,
  3311   // then lazily determine their actual enabled state when the user presses
  3312   // a keyboard shortcut.
  3313   else {
  3314     goSetCommandEnabled("cmd_undo", true);
  3315     goSetCommandEnabled("cmd_redo", true);
  3316     goSetCommandEnabled("cmd_cut", true);
  3317     goSetCommandEnabled("cmd_copy", true);
  3318     goSetCommandEnabled("cmd_paste", true);
  3319     goSetCommandEnabled("cmd_selectAll", true);
  3320     goSetCommandEnabled("cmd_delete", true);
  3321     goSetCommandEnabled("cmd_switchTextDirection", true);
  3323 #endif
  3326 /**
  3327  * Makes the Character Encoding menu enabled or disabled as appropriate.
  3328  * To be called when the View menu or the app menu is opened.
  3329  */
  3330 function updateCharacterEncodingMenuState()
  3332   let charsetMenu = document.getElementById("charsetMenu");
  3333   // gBrowser is null on Mac when the menubar shows in the context of
  3334   // non-browser windows. The above elements may be null depending on
  3335   // what parts of the menubar are present. E.g. no app menu on Mac.
  3336   if (gBrowser &&
  3337       gBrowser.docShell &&
  3338       gBrowser.docShell.mayEnableCharacterEncodingMenu) {
  3339     if (charsetMenu) {
  3340       charsetMenu.removeAttribute("disabled");
  3342   } else {
  3343     if (charsetMenu) {
  3344       charsetMenu.setAttribute("disabled", "true");
  3349 /**
  3350  * Returns true if |aMimeType| is text-based, false otherwise.
  3352  * @param aMimeType
  3353  *        The MIME type to check.
  3355  * If adding types to this function, please also check the similar
  3356  * function in findbar.xml
  3357  */
  3358 function mimeTypeIsTextBased(aMimeType)
  3360   return aMimeType.startsWith("text/") ||
  3361          aMimeType.endsWith("+xml") ||
  3362          aMimeType == "application/x-javascript" ||
  3363          aMimeType == "application/javascript" ||
  3364          aMimeType == "application/json" ||
  3365          aMimeType == "application/xml" ||
  3366          aMimeType == "mozilla.application/cached-xul";
  3369 var XULBrowserWindow = {
  3370   // Stored Status, Link and Loading values
  3371   status: "",
  3372   defaultStatus: "",
  3373   overLink: "",
  3374   startTime: 0,
  3375   statusText: "",
  3376   isBusy: false,
  3377   // Left here for add-on compatibility, see bug 752434
  3378   inContentWhitelist: [],
  3380   QueryInterface: function (aIID) {
  3381     if (aIID.equals(Ci.nsIWebProgressListener) ||
  3382         aIID.equals(Ci.nsIWebProgressListener2) ||
  3383         aIID.equals(Ci.nsISupportsWeakReference) ||
  3384         aIID.equals(Ci.nsIXULBrowserWindow) ||
  3385         aIID.equals(Ci.nsISupports))
  3386       return this;
  3387     throw Cr.NS_NOINTERFACE;
  3388   },
  3390   get stopCommand () {
  3391     delete this.stopCommand;
  3392     return this.stopCommand = document.getElementById("Browser:Stop");
  3393   },
  3394   get reloadCommand () {
  3395     delete this.reloadCommand;
  3396     return this.reloadCommand = document.getElementById("Browser:Reload");
  3397   },
  3398   get statusTextField () {
  3399     return gBrowser.getStatusPanel();
  3400   },
  3401   get isImage () {
  3402     delete this.isImage;
  3403     return this.isImage = document.getElementById("isImage");
  3404   },
  3406   init: function () {
  3407     // Initialize the security button's state and tooltip text.
  3408     var securityUI = gBrowser.securityUI;
  3409     this.onSecurityChange(null, null, securityUI.state);
  3410   },
  3412   setJSStatus: function () {
  3413     // unsupported
  3414   },
  3416   setDefaultStatus: function (status) {
  3417     this.defaultStatus = status;
  3418     this.updateStatusField();
  3419   },
  3421   setOverLink: function (url, anchorElt) {
  3422     // Encode bidirectional formatting characters.
  3423     // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
  3424     url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
  3425                       encodeURIComponent);
  3427     if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
  3428       url = trimURL(url);
  3430     this.overLink = url;
  3431     LinkTargetDisplay.update();
  3432   },
  3434   showTooltip: function (x, y, tooltip) {
  3435     // The x,y coordinates are relative to the <browser> element using
  3436     // the chrome zoom level.
  3437     let elt = document.getElementById("remoteBrowserTooltip");
  3438     elt.label = tooltip;
  3440     let anchor = gBrowser.selectedBrowser;
  3441     elt.openPopupAtScreen(anchor.boxObject.screenX + x, anchor.boxObject.screenY + y, false, null);
  3442   },
  3444   hideTooltip: function () {
  3445     let elt = document.getElementById("remoteBrowserTooltip");
  3446     elt.hidePopup();
  3447   },
  3449   updateStatusField: function () {
  3450     var text, type, types = ["overLink"];
  3451     if (this._busyUI)
  3452       types.push("status");
  3453     types.push("defaultStatus");
  3454     for (type of types) {
  3455       text = this[type];
  3456       if (text)
  3457         break;
  3460     // check the current value so we don't trigger an attribute change
  3461     // and cause needless (slow!) UI updates
  3462     if (this.statusText != text) {
  3463       let field = this.statusTextField;
  3464       field.setAttribute("previoustype", field.getAttribute("type"));
  3465       field.setAttribute("type", type);
  3466       field.label = text;
  3467       field.setAttribute("crop", type == "overLink" ? "center" : "end");
  3468       this.statusText = text;
  3470   },
  3472   // Called before links are navigated to to allow us to retarget them if needed.
  3473   onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
  3474     let target = this._onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
  3475     SocialUI.closeSocialPanelForLinkTraversal(target, linkNode);
  3476     return target;
  3477   },
  3479   _onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
  3480     // Don't modify non-default targets or targets that aren't in top-level app
  3481     // tab docshells (isAppTab will be false for app tab subframes).
  3482     if (originalTarget != "" || !isAppTab)
  3483       return originalTarget;
  3485     // External links from within app tabs should always open in new tabs
  3486     // instead of replacing the app tab's page (Bug 575561)
  3487     let linkHost;
  3488     let docHost;
  3489     try {
  3490       linkHost = linkURI.host;
  3491       docHost = linkNode.ownerDocument.documentURIObject.host;
  3492     } catch(e) {
  3493       // nsIURI.host can throw for non-nsStandardURL nsIURIs.
  3494       // If we fail to get either host, just return originalTarget.
  3495       return originalTarget;
  3498     if (docHost == linkHost)
  3499       return originalTarget;
  3501     // Special case: ignore "www" prefix if it is part of host string
  3502     let [longHost, shortHost] =
  3503       linkHost.length > docHost.length ? [linkHost, docHost] : [docHost, linkHost];
  3504     if (longHost == "www." + shortHost)
  3505       return originalTarget;
  3507     return "_blank";
  3508   },
  3510   onProgressChange: function (aWebProgress, aRequest,
  3511                               aCurSelfProgress, aMaxSelfProgress,
  3512                               aCurTotalProgress, aMaxTotalProgress) {
  3513     // Do nothing.
  3514   },
  3516   onProgressChange64: function (aWebProgress, aRequest,
  3517                                 aCurSelfProgress, aMaxSelfProgress,
  3518                                 aCurTotalProgress, aMaxTotalProgress) {
  3519     return this.onProgressChange(aWebProgress, aRequest,
  3520       aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
  3521       aMaxTotalProgress);
  3522   },
  3524   // This function fires only for the currently selected tab.
  3525   onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
  3526     const nsIWebProgressListener = Ci.nsIWebProgressListener;
  3527     const nsIChannel = Ci.nsIChannel;
  3529     let browser = gBrowser.selectedBrowser;
  3531     if (aStateFlags & nsIWebProgressListener.STATE_START &&
  3532         aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
  3534       if (aRequest && aWebProgress.isTopLevel) {
  3535         // clear out feed data
  3536         browser.feeds = null;
  3538         // clear out search-engine data
  3539         browser.engines = null;
  3542       this.isBusy = true;
  3544       if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
  3545         this._busyUI = true;
  3547         // XXX: This needs to be based on window activity...
  3548         this.stopCommand.removeAttribute("disabled");
  3549         CombinedStopReload.switchToStop();
  3552     else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
  3553       // This (thanks to the filter) is a network stop or the last
  3554       // request stop outside of loading the document, stop throbbers
  3555       // and progress bars and such
  3556       if (aRequest) {
  3557         let msg = "";
  3558         let location;
  3559         // Get the URI either from a channel or a pseudo-object
  3560         if (aRequest instanceof nsIChannel || "URI" in aRequest) {
  3561           location = aRequest.URI;
  3563           // For keyword URIs clear the user typed value since they will be changed into real URIs
  3564           if (location.scheme == "keyword" && aWebProgress.isTopLevel)
  3565             gBrowser.userTypedValue = null;
  3567           if (location.spec != "about:blank") {
  3568             switch (aStatus) {
  3569               case Components.results.NS_ERROR_NET_TIMEOUT:
  3570                 msg = gNavigatorBundle.getString("nv_timeout");
  3571                 break;
  3576         this.status = "";
  3577         this.setDefaultStatus(msg);
  3579         // Disable menu entries for images, enable otherwise
  3580         if (browser.documentContentType && mimeTypeIsTextBased(browser.documentContentType))
  3581           this.isImage.removeAttribute('disabled');
  3582         else
  3583           this.isImage.setAttribute('disabled', 'true');
  3586       this.isBusy = false;
  3588       if (this._busyUI) {
  3589         this._busyUI = false;
  3591         this.stopCommand.setAttribute("disabled", "true");
  3592         CombinedStopReload.switchToReload(aRequest instanceof Ci.nsIRequest);
  3595   },
  3597   onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
  3598     var location = aLocationURI ? aLocationURI.spec : "";
  3600     // Hide the form invalid popup.
  3601     if (gFormSubmitObserver.panel) {
  3602       gFormSubmitObserver.panel.hidePopup();
  3605     let pageTooltip = document.getElementById("aHTMLTooltip");
  3606     let tooltipNode = pageTooltip.triggerNode;
  3607     if (tooltipNode) {
  3608       // Optimise for the common case
  3609       if (aWebProgress.isTopLevel) {
  3610         pageTooltip.hidePopup();
  3612       else {
  3613         for (let tooltipWindow = tooltipNode.ownerDocument.defaultView;
  3614              tooltipWindow != tooltipWindow.parent;
  3615              tooltipWindow = tooltipWindow.parent) {
  3616           if (tooltipWindow == aWebProgress.DOMWindow) {
  3617             pageTooltip.hidePopup();
  3618             break;
  3624     let browser = gBrowser.selectedBrowser;
  3626     // Disable menu entries for images, enable otherwise
  3627     if (browser.documentContentType && mimeTypeIsTextBased(browser.documentContentType))
  3628       this.isImage.removeAttribute('disabled');
  3629     else
  3630       this.isImage.setAttribute('disabled', 'true');
  3632     this.hideOverLinkImmediately = true;
  3633     this.setOverLink("", null);
  3634     this.hideOverLinkImmediately = false;
  3636     // We should probably not do this if the value has changed since the user
  3637     // searched
  3638     // Update urlbar only if a new page was loaded on the primary content area
  3639     // Do not update urlbar if there was a subframe navigation
  3641     if (aWebProgress.isTopLevel) {
  3642       if ((location == "about:blank" && (gMultiProcessBrowser || !content.opener)) ||
  3643           location == "") {  // Second condition is for new tabs, otherwise
  3644                              // reload function is enabled until tab is refreshed.
  3645         this.reloadCommand.setAttribute("disabled", "true");
  3646       } else {
  3647         this.reloadCommand.removeAttribute("disabled");
  3650       if (gURLBar) {
  3651         URLBarSetURI(aLocationURI);
  3653         BookmarkingUI.onLocationChange();
  3654         SocialUI.updateState();
  3657       // Utility functions for disabling find
  3658       var shouldDisableFind = function shouldDisableFind(aDocument) {
  3659         let docElt = aDocument.documentElement;
  3660         return docElt && docElt.getAttribute("disablefastfind") == "true";
  3663       var disableFindCommands = function disableFindCommands(aDisable) {
  3664         let findCommands = [document.getElementById("cmd_find"),
  3665                             document.getElementById("cmd_findAgain"),
  3666                             document.getElementById("cmd_findPrevious")];
  3667         for (let elt of findCommands) {
  3668           if (aDisable)
  3669             elt.setAttribute("disabled", "true");
  3670           else
  3671             elt.removeAttribute("disabled");
  3675       var onContentRSChange = function onContentRSChange(e) {
  3676         if (e.target.readyState != "interactive" && e.target.readyState != "complete")
  3677           return;
  3679         e.target.removeEventListener("readystatechange", onContentRSChange);
  3680         disableFindCommands(shouldDisableFind(e.target));
  3683       // Disable find commands in documents that ask for them to be disabled.
  3684       if (!gMultiProcessBrowser && aLocationURI &&
  3685           (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) {
  3686         // Don't need to re-enable/disable find commands for same-document location changes
  3687         // (e.g. the replaceStates in about:addons)
  3688         if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
  3689           if (content.document.readyState == "interactive" || content.document.readyState == "complete")
  3690             disableFindCommands(shouldDisableFind(content.document));
  3691           else {
  3692             content.document.addEventListener("readystatechange", onContentRSChange);
  3695       } else
  3696         disableFindCommands(false);
  3698       // Try not to instantiate gCustomizeMode as much as possible,
  3699       // so don't use CustomizeMode.jsm to check for URI or customizing.
  3700       let customizingURI = "about:customizing";
  3701       if (location == customizingURI) {
  3702         gCustomizeMode.enter();
  3703       } else if (location != customizingURI &&
  3704                  (CustomizationHandler.isEnteringCustomizeMode ||
  3705                   CustomizationHandler.isCustomizing())) {
  3706         gCustomizeMode.exit();
  3709     UpdateBackForwardCommands(gBrowser.webNavigation);
  3711     gGestureSupport.restoreRotationState();
  3713     // See bug 358202, when tabs are switched during a drag operation,
  3714     // timers don't fire on windows (bug 203573)
  3715     if (aRequest)
  3716       setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
  3717     else
  3718       this.asyncUpdateUI();
  3720 #ifdef MOZ_CRASHREPORTER
  3721     if (aLocationURI) {
  3722       let uri = aLocationURI.clone();
  3723       try {
  3724         // If the current URI contains a username/password, remove it.
  3725         uri.userPass = "";
  3726       } catch (ex) { /* Ignore failures on about: URIs. */ }
  3728       try {
  3729         gCrashReporter.annotateCrashReport("URL", uri.spec);
  3730       } catch (ex if ex.result == Components.results.NS_ERROR_NOT_INITIALIZED) {
  3731         // Don't make noise when the crash reporter is built but not enabled.
  3734 #endif
  3735   },
  3737   asyncUpdateUI: function () {
  3738     FeedHandler.updateFeeds();
  3739   },
  3741   // Left here for add-on compatibility, see bug 752434
  3742   hideChromeForLocation: function() {},
  3744   onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
  3745     this.status = aMessage;
  3746     this.updateStatusField();
  3747   },
  3749   // Properties used to cache security state used to update the UI
  3750   _state: null,
  3751   _lastLocation: null,
  3753   onSecurityChange: function (aWebProgress, aRequest, aState) {
  3754     // Don't need to do anything if the data we use to update the UI hasn't
  3755     // changed
  3756     let uri = gBrowser.currentURI;
  3757     let spec = uri.spec;
  3758     if (this._state == aState &&
  3759         this._lastLocation == spec)
  3760       return;
  3761     this._state = aState;
  3762     this._lastLocation = spec;
  3764     // aState is defined as a bitmask that may be extended in the future.
  3765     // We filter out any unknown bits before testing for known values.
  3766     const wpl = Components.interfaces.nsIWebProgressListener;
  3767     const wpl_security_bits = wpl.STATE_IS_SECURE |
  3768                               wpl.STATE_IS_BROKEN |
  3769                               wpl.STATE_IS_INSECURE;
  3770     var level;
  3772     switch (this._state & wpl_security_bits) {
  3773       case wpl.STATE_IS_SECURE:
  3774         level = "high";
  3775         break;
  3776       case wpl.STATE_IS_BROKEN:
  3777         level = "broken";
  3778         break;
  3781     if (level) {
  3782       // We don't style the Location Bar based on the the 'level' attribute
  3783       // anymore, but still set it for third-party themes.
  3784       if (gURLBar)
  3785         gURLBar.setAttribute("level", level);
  3786     } else {
  3787       if (gURLBar)
  3788         gURLBar.removeAttribute("level");
  3791     try {
  3792       uri = Services.uriFixup.createExposableURI(uri);
  3793     } catch (e) {}
  3794     gIdentityHandler.checkIdentity(this._state, uri);
  3795   },
  3797   // simulate all change notifications after switching tabs
  3798   onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
  3799     if (FullZoom.updateBackgroundTabs)
  3800       FullZoom.onLocationChange(gBrowser.currentURI, true);
  3801     var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  3802     var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
  3803     // use a pseudo-object instead of a (potentially nonexistent) channel for getting
  3804     // a correct error message - and make sure that the UI is always either in
  3805     // loading (STATE_START) or done (STATE_STOP) mode
  3806     this.onStateChange(
  3807       gBrowser.webProgress,
  3808       { URI: gBrowser.currentURI },
  3809       loadingDone ? nsIWebProgressListener.STATE_STOP : nsIWebProgressListener.STATE_START,
  3810       aStatus
  3811     );
  3812     // status message and progress value are undefined if we're done with loading
  3813     if (loadingDone)
  3814       return;
  3815     this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
  3817 };
  3819 var LinkTargetDisplay = {
  3820   get DELAY_SHOW() {
  3821      delete this.DELAY_SHOW;
  3822      return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
  3823   },
  3825   DELAY_HIDE: 250,
  3826   _timer: 0,
  3828   get _isVisible () XULBrowserWindow.statusTextField.label != "",
  3830   update: function () {
  3831     clearTimeout(this._timer);
  3832     window.removeEventListener("mousemove", this, true);
  3834     if (!XULBrowserWindow.overLink) {
  3835       if (XULBrowserWindow.hideOverLinkImmediately)
  3836         this._hide();
  3837       else
  3838         this._timer = setTimeout(this._hide.bind(this), this.DELAY_HIDE);
  3839       return;
  3842     if (this._isVisible) {
  3843       XULBrowserWindow.updateStatusField();
  3844     } else {
  3845       // Let the display appear when the mouse doesn't move within the delay
  3846       this._showDelayed();
  3847       window.addEventListener("mousemove", this, true);
  3849   },
  3851   handleEvent: function (event) {
  3852     switch (event.type) {
  3853       case "mousemove":
  3854         // Restart the delay since the mouse was moved
  3855         clearTimeout(this._timer);
  3856         this._showDelayed();
  3857         break;
  3859   },
  3861   _showDelayed: function () {
  3862     this._timer = setTimeout(function (self) {
  3863       XULBrowserWindow.updateStatusField();
  3864       window.removeEventListener("mousemove", self, true);
  3865     }, this.DELAY_SHOW, this);
  3866   },
  3868   _hide: function () {
  3869     clearTimeout(this._timer);
  3871     XULBrowserWindow.updateStatusField();
  3873 };
  3875 var CombinedStopReload = {
  3876   init: function () {
  3877     if (this._initialized)
  3878       return;
  3880     let reload = document.getElementById("urlbar-reload-button");
  3881     let stop = document.getElementById("urlbar-stop-button");
  3882     if (!stop || !reload || reload.nextSibling != stop)
  3883       return;
  3885     this._initialized = true;
  3886     if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
  3887       reload.setAttribute("displaystop", "true");
  3888     stop.addEventListener("click", this, false);
  3889     this.reload = reload;
  3890     this.stop = stop;
  3891   },
  3893   uninit: function () {
  3894     if (!this._initialized)
  3895       return;
  3897     this._cancelTransition();
  3898     this._initialized = false;
  3899     this.stop.removeEventListener("click", this, false);
  3900     this.reload = null;
  3901     this.stop = null;
  3902   },
  3904   handleEvent: function (event) {
  3905     // the only event we listen to is "click" on the stop button
  3906     if (event.button == 0 &&
  3907         !this.stop.disabled)
  3908       this._stopClicked = true;
  3909   },
  3911   switchToStop: function () {
  3912     if (!this._initialized)
  3913       return;
  3915     this._cancelTransition();
  3916     this.reload.setAttribute("displaystop", "true");
  3917   },
  3919   switchToReload: function (aDelay) {
  3920     if (!this._initialized)
  3921       return;
  3923     this.reload.removeAttribute("displaystop");
  3925     if (!aDelay || this._stopClicked) {
  3926       this._stopClicked = false;
  3927       this._cancelTransition();
  3928       this.reload.disabled = XULBrowserWindow.reloadCommand
  3929                                              .getAttribute("disabled") == "true";
  3930       return;
  3933     if (this._timer)
  3934       return;
  3936     // Temporarily disable the reload button to prevent the user from
  3937     // accidentally reloading the page when intending to click the stop button
  3938     this.reload.disabled = true;
  3939     this._timer = setTimeout(function (self) {
  3940       self._timer = 0;
  3941       self.reload.disabled = XULBrowserWindow.reloadCommand
  3942                                              .getAttribute("disabled") == "true";
  3943     }, 650, this);
  3944   },
  3946   _cancelTransition: function () {
  3947     if (this._timer) {
  3948       clearTimeout(this._timer);
  3949       this._timer = 0;
  3952 };
  3954 var TabsProgressListener = {
  3955   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
  3956     // Collect telemetry data about tab load times.
  3957     if (aWebProgress.isTopLevel) {
  3958       if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
  3959         if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
  3960           TelemetryStopwatch.start("FX_PAGE_LOAD_MS", aBrowser);
  3961           Services.telemetry.getHistogramById("FX_TOTAL_TOP_VISITS").add(true);
  3962         } else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
  3963           TelemetryStopwatch.finish("FX_PAGE_LOAD_MS", aBrowser);
  3965       } else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
  3966                  aStatus == Cr.NS_BINDING_ABORTED) {
  3967         TelemetryStopwatch.cancel("FX_PAGE_LOAD_MS", aBrowser);
  3971     // Attach a listener to watch for "click" events bubbling up from error
  3972     // pages and other similar pages (like about:newtab). This lets us fix bugs
  3973     // like 401575 which require error page UI to do privileged things, without
  3974     // letting error pages have any privilege themselves.
  3975     // We can't look for this during onLocationChange since at that point the
  3976     // document URI is not yet the about:-uri of the error page.
  3978     let isRemoteBrowser = aBrowser.isRemoteBrowser;
  3979     // We check isRemoteBrowser here to avoid requesting the doc CPOW
  3980     let doc = isRemoteBrowser ? null : aWebProgress.DOMWindow.document;
  3982     if (!isRemoteBrowser &&
  3983         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
  3984         Components.isSuccessCode(aStatus) &&
  3985         doc.documentURI.startsWith("about:") &&
  3986         !doc.documentURI.toLowerCase().startsWith("about:blank") &&
  3987         !doc.documentURI.toLowerCase().startsWith("about:home") &&
  3988         !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
  3989       // STATE_STOP may be received twice for documents, thus store an
  3990       // attribute to ensure handling it just once.
  3991       doc.documentElement.setAttribute("hasBrowserHandlers", "true");
  3992       aBrowser.addEventListener("click", BrowserOnClick, true);
  3993       aBrowser.addEventListener("pagehide", function onPageHide(event) {
  3994         if (event.target.defaultView.frameElement)
  3995           return;
  3996         aBrowser.removeEventListener("click", BrowserOnClick, true);
  3997         aBrowser.removeEventListener("pagehide", onPageHide, true);
  3998         if (event.target.documentElement)
  3999           event.target.documentElement.removeAttribute("hasBrowserHandlers");
  4000       }, true);
  4002 #ifdef MOZ_CRASHREPORTER
  4003       if (doc.documentURI.startsWith("about:tabcrashed"))
  4004         TabCrashReporter.onAboutTabCrashedLoad(aBrowser);
  4005 #endif
  4007   },
  4009   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
  4010                               aFlags) {
  4011     // Filter out location changes caused by anchor navigation
  4012     // or history.push/pop/replaceState.
  4013     if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
  4014       return;
  4016     // Filter out location changes in sub documents.
  4017     if (!aWebProgress.isTopLevel)
  4018       return;
  4020     // Only need to call locationChange if the PopupNotifications object
  4021     // for this window has already been initialized (i.e. its getter no
  4022     // longer exists)
  4023     if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
  4024       PopupNotifications.locationChange(aBrowser);
  4026     gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
  4028     FullZoom.onLocationChange(aLocationURI, false, aBrowser);
  4029   },
  4031   onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
  4032     if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
  4033       let brandBundle = document.getElementById("bundle_brand");
  4034       let brandShortName = brandBundle.getString("brandShortName");
  4035       let refreshButtonText =
  4036         gNavigatorBundle.getString("refreshBlocked.goButton");
  4037       let refreshButtonAccesskey =
  4038         gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
  4039       let message =
  4040         gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
  4041                                                      : "refreshBlocked.redirectLabel",
  4042                                             [brandShortName]);
  4043       let docShell = aWebProgress.DOMWindow
  4044                                  .QueryInterface(Ci.nsIInterfaceRequestor)
  4045                                  .getInterface(Ci.nsIWebNavigation)
  4046                                  .QueryInterface(Ci.nsIDocShell);
  4047       let notificationBox = gBrowser.getNotificationBox(aBrowser);
  4048       let notification = notificationBox.getNotificationWithValue("refresh-blocked");
  4049       if (notification) {
  4050         notification.label = message;
  4051         notification.refreshURI = aURI;
  4052         notification.delay = aDelay;
  4053         notification.docShell = docShell;
  4054       } else {
  4055         let buttons = [{
  4056           label: refreshButtonText,
  4057           accessKey: refreshButtonAccesskey,
  4058           callback: function (aNotification, aButton) {
  4059             var refreshURI = aNotification.docShell
  4060                                           .QueryInterface(Ci.nsIRefreshURI);
  4061             refreshURI.forceRefreshURI(aNotification.refreshURI,
  4062                                        aNotification.delay, true);
  4064         }];
  4065         notification =
  4066           notificationBox.appendNotification(message, "refresh-blocked",
  4067                                              "chrome://browser/skin/Info.png",
  4068                                              notificationBox.PRIORITY_INFO_MEDIUM,
  4069                                              buttons);
  4070         notification.refreshURI = aURI;
  4071         notification.delay = aDelay;
  4072         notification.docShell = docShell;
  4074       return false;
  4076     return true;
  4080 function nsBrowserAccess() { }
  4082 nsBrowserAccess.prototype = {
  4083   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
  4085   _openURIInNewTab: function(aURI, aOpener, aIsExternal) {
  4086     let win, needToFocusWin;
  4088     // try the current window.  if we're in a popup, fall back on the most recent browser window
  4089     if (window.toolbar.visible)
  4090       win = window;
  4091     else {
  4092       let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aOpener || window);
  4093       win = RecentWindow.getMostRecentBrowserWindow({private: isPrivate});
  4094       needToFocusWin = true;
  4097     if (!win) {
  4098       // we couldn't find a suitable window, a new one needs to be opened.
  4099       return null;
  4102     if (aIsExternal && (!aURI || aURI.spec == "about:blank")) {
  4103       win.BrowserOpenTab(); // this also focuses the location bar
  4104       win.focus();
  4105       return win.gBrowser.selectedBrowser;
  4108     let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
  4109     let referrer = aOpener ? makeURI(aOpener.location.href) : null;
  4111     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
  4112                                       referrerURI: referrer,
  4113                                       fromExternal: aIsExternal,
  4114                                       inBackground: loadInBackground});
  4115     let browser = win.gBrowser.getBrowserForTab(tab);
  4117     if (needToFocusWin || (!loadInBackground && aIsExternal))
  4118       win.focus();
  4120     return browser;
  4121   },
  4123   openURI: function (aURI, aOpener, aWhere, aContext) {
  4124     var newWindow = null;
  4125     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
  4127     if (isExternal && aURI && aURI.schemeIs("chrome")) {
  4128       dump("use -chrome command-line option to load external chrome urls\n");
  4129       return null;
  4132     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
  4133       if (isExternal &&
  4134           gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
  4135         aWhere = gPrefService.getIntPref("browser.link.open_newwindow.override.external");
  4136       else
  4137         aWhere = gPrefService.getIntPref("browser.link.open_newwindow");
  4139     switch (aWhere) {
  4140       case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
  4141         // FIXME: Bug 408379. So how come this doesn't send the
  4142         // referrer like the other loads do?
  4143         var url = aURI ? aURI.spec : "about:blank";
  4144         // Pass all params to openDialog to ensure that "url" isn't passed through
  4145         // loadOneOrMoreURIs, which splits based on "|"
  4146         newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
  4147         break;
  4148       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
  4149         let browser = this._openURIInNewTab(aURI, aOpener, isExternal);
  4150         if (browser)
  4151           newWindow = browser.contentWindow;
  4152         break;
  4153       default : // OPEN_CURRENTWINDOW or an illegal value
  4154         newWindow = content;
  4155         if (aURI) {
  4156           let referrer = aOpener ? makeURI(aOpener.location.href) : null;
  4157           let loadflags = isExternal ?
  4158                             Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
  4159                             Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
  4160           gBrowser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
  4162         if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
  4163           window.focus();
  4165     return newWindow;
  4166   },
  4168   openURIInFrame: function browser_openURIInFrame(aURI, aOpener, aWhere, aContext) {
  4169     if (aWhere != Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
  4170       dump("Error: openURIInFrame can only open in new tabs");
  4171       return null;
  4174     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
  4175     let browser = this._openURIInNewTab(aURI, aOpener, isExternal);
  4176     if (browser)
  4177       return browser.QueryInterface(Ci.nsIFrameLoaderOwner);
  4179     return null;
  4180   },
  4182   isTabContentWindow: function (aWindow) {
  4183     return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
  4184   },
  4186   get contentWindow() {
  4187     return gBrowser.contentWindow;
  4191 function getTogglableToolbars() {
  4192   let toolbarNodes = Array.slice(gNavToolbox.childNodes);
  4193   toolbarNodes = toolbarNodes.concat(gNavToolbox.externalToolbars);
  4194   toolbarNodes = toolbarNodes.filter(node => node.getAttribute("toolbarname"));
  4195   return toolbarNodes;
  4198 function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
  4199   var popup = aEvent.target;
  4200   if (popup != aEvent.currentTarget)
  4201     return;
  4203   // Empty the menu
  4204   for (var i = popup.childNodes.length-1; i >= 0; --i) {
  4205     var deadItem = popup.childNodes[i];
  4206     if (deadItem.hasAttribute("toolbarId"))
  4207       popup.removeChild(deadItem);
  4210   var firstMenuItem = aInsertPoint || popup.firstChild;
  4212   let toolbarNodes = getTogglableToolbars();
  4214   for (let toolbar of toolbarNodes) {
  4215     let menuItem = document.createElement("menuitem");
  4216     let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
  4217                           "autohide" : "collapsed";
  4218     menuItem.setAttribute("id", "toggle_" + toolbar.id);
  4219     menuItem.setAttribute("toolbarId", toolbar.id);
  4220     menuItem.setAttribute("type", "checkbox");
  4221     menuItem.setAttribute("label", toolbar.getAttribute("toolbarname"));
  4222     menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
  4223     menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
  4224     if (popup.id != "toolbar-context-menu")
  4225       menuItem.setAttribute("key", toolbar.getAttribute("key"));
  4227     popup.insertBefore(menuItem, firstMenuItem);
  4229     menuItem.addEventListener("command", onViewToolbarCommand, false);
  4233   let moveToPanel = popup.querySelector(".customize-context-moveToPanel");
  4234   let removeFromToolbar = popup.querySelector(".customize-context-removeFromToolbar");
  4235   // View -> Toolbars menu doesn't have the moveToPanel or removeFromToolbar items.
  4236   if (!moveToPanel || !removeFromToolbar) {
  4237     return;
  4240   // triggerNode can be a nested child element of a toolbaritem.
  4241   let toolbarItem = popup.triggerNode;
  4243   if (toolbarItem && toolbarItem.localName == "toolbarpaletteitem") {
  4244     toolbarItem = toolbarItem.firstChild;
  4245   } else if (toolbarItem && toolbarItem.localName != "toolbar") {
  4246     while (toolbarItem && toolbarItem.parentNode) {
  4247       let parent = toolbarItem.parentNode;
  4248       if ((parent.classList && parent.classList.contains("customization-target")) ||
  4249           parent.getAttribute("overflowfortoolbar") || // Needs to work in the overflow list as well.
  4250           parent.localName == "toolbarpaletteitem" ||
  4251           parent.localName == "toolbar")
  4252         break;
  4253       toolbarItem = parent;
  4255   } else {
  4256     toolbarItem = null;
  4259   // Right-clicking on an empty part of the tabstrip will exit
  4260   // the above loop with toolbarItem being the xul:document.
  4261   // That has no parentNode, and we should disable the items in
  4262   // this case.
  4263   let movable = toolbarItem && toolbarItem.parentNode &&
  4264                 CustomizableUI.isWidgetRemovable(toolbarItem);
  4265   if (movable) {
  4266     moveToPanel.removeAttribute("disabled");
  4267     removeFromToolbar.removeAttribute("disabled");
  4268   } else {
  4269     moveToPanel.setAttribute("disabled", true);
  4270     removeFromToolbar.setAttribute("disabled", true);
  4274 function onViewToolbarCommand(aEvent) {
  4275   var toolbarId = aEvent.originalTarget.getAttribute("toolbarId");
  4276   var isVisible = aEvent.originalTarget.getAttribute("checked") == "true";
  4277   CustomizableUI.setToolbarVisibility(toolbarId, isVisible);
  4280 function setToolbarVisibility(toolbar, isVisible, persist=true) {
  4281   let hidingAttribute;
  4282   if (toolbar.getAttribute("type") == "menubar") {
  4283     hidingAttribute = "autohide";
  4284 #ifdef MOZ_WIDGET_GTK
  4285     Services.prefs.setBoolPref("ui.key.menuAccessKeyFocuses", !isVisible);
  4286 #endif
  4287   } else {
  4288     hidingAttribute = "collapsed";
  4291   toolbar.setAttribute(hidingAttribute, !isVisible);
  4292   if (persist) {
  4293     document.persist(toolbar.id, hidingAttribute);
  4296   let eventParams = {
  4297     detail: {
  4298       visible: isVisible
  4299     },
  4300     bubbles: true
  4301   };
  4302   let event = new CustomEvent("toolbarvisibilitychange", eventParams);
  4303   toolbar.dispatchEvent(event);
  4305   PlacesToolbarHelper.init();
  4306   BookmarkingUI.onToolbarVisibilityChange();
  4307   gBrowser.updateWindowResizers();
  4308   if (isVisible)
  4309     ToolbarIconColor.inferFromText();
  4312 var TabsInTitlebar = {
  4313   init: function () {
  4314 #ifdef CAN_DRAW_IN_TITLEBAR
  4315     this._readPref();
  4316     Services.prefs.addObserver(this._prefName, this, false);
  4318     // We need to update the appearance of the titlebar when the menu changes
  4319     // from the active to the inactive state. We can't, however, rely on
  4320     // DOMMenuBarInactive, because the menu fires this event and then removes
  4321     // the inactive attribute after an event-loop spin.
  4322     //
  4323     // Because updating the appearance involves sampling the heights and margins
  4324     // of various elements, it's important that the layout be more or less
  4325     // settled before updating the titlebar. So instead of listening to
  4326     // DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
  4327     // watch the "invalid" attribute directly.
  4328     let menu = document.getElementById("toolbar-menubar");
  4329     this._menuObserver = new MutationObserver(this._onMenuMutate);
  4330     this._menuObserver.observe(menu, {attributes: true});
  4332     this.onAreaReset = function(aArea) {
  4333       if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
  4334         this._update(true);
  4335     };
  4336     this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
  4337       if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
  4338         this._update(true);
  4339     };
  4340     CustomizableUI.addListener(this);
  4342     this._initialized = true;
  4343 #endif
  4344   },
  4346   allowedBy: function (condition, allow) {
  4347 #ifdef CAN_DRAW_IN_TITLEBAR
  4348     if (allow) {
  4349       if (condition in this._disallowed) {
  4350         delete this._disallowed[condition];
  4351         this._update(true);
  4353     } else {
  4354       if (!(condition in this._disallowed)) {
  4355         this._disallowed[condition] = null;
  4356         this._update(true);
  4359 #endif
  4360   },
  4362   updateAppearance: function updateAppearance(aForce) {
  4363 #ifdef CAN_DRAW_IN_TITLEBAR
  4364     this._update(aForce);
  4365 #endif
  4366   },
  4368   get enabled() {
  4369     return document.documentElement.getAttribute("tabsintitlebar") == "true";
  4370   },
  4372 #ifdef CAN_DRAW_IN_TITLEBAR
  4373   observe: function (subject, topic, data) {
  4374     if (topic == "nsPref:changed")
  4375       this._readPref();
  4376   },
  4378   _onMenuMutate: function (aMutations) {
  4379     for (let mutation of aMutations) {
  4380       if (mutation.attributeName == "inactive" ||
  4381           mutation.attributeName == "autohide") {
  4382         TabsInTitlebar._update(true);
  4383         return;
  4386   },
  4388   _initialized: false,
  4389   _disallowed: {},
  4390   _prefName: "browser.tabs.drawInTitlebar",
  4391   _lastSizeMode: null,
  4393   _readPref: function () {
  4394     this.allowedBy("pref",
  4395                    Services.prefs.getBoolPref(this._prefName));
  4396   },
  4398   _update: function (aForce=false) {
  4399     function $(id) document.getElementById(id);
  4400     function rect(ele) ele.getBoundingClientRect();
  4401     function verticalMargins(cstyle) parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
  4403     if (!this._initialized || window.fullScreen)
  4404       return;
  4406     let allowed = true;
  4408     if (!aForce) {
  4409       // _update is called on resize events, because the window is not ready
  4410       // after sizemode events. However, we only care about the event when the
  4411       // sizemode is different from the last time we updated the appearance of
  4412       // the tabs in the titlebar.
  4413       let sizemode = document.documentElement.getAttribute("sizemode");
  4414       if (this._lastSizeMode == sizemode) {
  4415         return;
  4417       this._lastSizeMode = sizemode;
  4420     for (let something in this._disallowed) {
  4421       allowed = false;
  4422       break;
  4425     let titlebar = $("titlebar");
  4426     let titlebarContent = $("titlebar-content");
  4427     let menubar = $("toolbar-menubar");
  4429     if (allowed) {
  4430       // We set the tabsintitlebar attribute first so that our CSS for
  4431       // tabsintitlebar manifests before we do our measurements.
  4432       document.documentElement.setAttribute("tabsintitlebar", "true");
  4433       updateTitlebarDisplay();
  4435       // Try to avoid reflows in this code by calculating dimensions first and
  4436       // then later set the properties affecting layout together in a batch.
  4438       // Get the full height of the tabs toolbar:
  4439       let tabsToolbar = $("TabsToolbar");
  4440       let fullTabsHeight = rect(tabsToolbar).height;
  4441       // Buttons first:
  4442       let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
  4444 #ifdef XP_MACOSX
  4445       let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
  4446       // No need to look up the menubar stuff on OS X:
  4447       let menuHeight = 0;
  4448       let fullMenuHeight = 0;
  4449       // Instead, look up the titlebar padding:
  4450       let titlebarPadding = parseInt(window.getComputedStyle(titlebar).paddingTop, 10);
  4451 #else
  4452       // Otherwise, get the height and margins separately for the menubar
  4453       let menuHeight = rect(menubar).height;
  4454       let menuStyles = window.getComputedStyle(menubar);
  4455       let fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
  4456       let tabsStyles = window.getComputedStyle(tabsToolbar);
  4457       fullTabsHeight += verticalMargins(tabsStyles);
  4458 #endif
  4460       // If the navbar overlaps the tabbar using negative margins, we need to take those into
  4461       // account so we don't overlap it
  4462       let navbarMarginTop = parseFloat(window.getComputedStyle($("nav-bar")).marginTop);
  4463       navbarMarginTop = Math.min(navbarMarginTop, 0);
  4465       // And get the height of what's in the titlebar:
  4466       let titlebarContentHeight = rect(titlebarContent).height;
  4468       // Begin setting CSS properties which will cause a reflow
  4470       // If the menubar is around (menuHeight is non-zero), try to adjust
  4471       // its full height (i.e. including margins) to match the titlebar,
  4472       // by changing the menubar's bottom padding
  4473       if (menuHeight) {
  4474         // Calculate the difference between the titlebar's height and that of the menubar
  4475         let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
  4476         let paddingBottom;
  4477         // The titlebar is bigger:
  4478         if (menuTitlebarDelta > 0) {
  4479           fullMenuHeight += menuTitlebarDelta;
  4480           // If there is already padding on the menubar, we need to add that
  4481           // to the difference so the total padding is correct:
  4482           if ((paddingBottom = menuStyles.paddingBottom)) {
  4483             menuTitlebarDelta += parseFloat(paddingBottom);
  4485           menubar.style.paddingBottom = menuTitlebarDelta + "px";
  4486         // The menubar is bigger, but has bottom padding we can remove:
  4487         } else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
  4488           let existingPadding = parseFloat(paddingBottom);
  4489           // menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
  4490           let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
  4491           menubar.style.paddingBottom = desiredPadding + "px";
  4492           // We've changed the menu height now:
  4493           fullMenuHeight += desiredPadding - existingPadding;
  4497       // Next, we calculate how much we need to stretch the titlebar down to
  4498       // go all the way to the bottom of the tab strip, if necessary.
  4499       let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
  4501       if (tabAndMenuHeight > titlebarContentHeight) {
  4502         // We need to increase the titlebar content's outer height (ie including margins)
  4503         // to match the tab and menu height:
  4504         let extraMargin = tabAndMenuHeight - titlebarContentHeight;
  4505         // We need to reduce the height by the amount of navbar overlap
  4506         // (this value is 0 or negative):
  4507         extraMargin += navbarMarginTop;
  4508         // On non-OSX, we can just use bottom margin:
  4509 #ifndef XP_MACOSX
  4510         titlebarContent.style.marginBottom = extraMargin + "px";
  4511 #endif
  4512         titlebarContentHeight += extraMargin;
  4515       // Then we bring up the titlebar by the same amount, but we add any negative margin:
  4516       titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
  4519       // Finally, size the placeholders:
  4520 #ifdef XP_MACOSX
  4521       this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
  4522 #endif
  4523       this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
  4525       if (!this._draghandles) {
  4526         this._draghandles = {};
  4527         let tmp = {};
  4528         Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
  4530         let mouseDownCheck = function () {
  4531           return !this._dragBindingAlive && TabsInTitlebar.enabled;
  4532         };
  4534         this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
  4535         this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
  4537         this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
  4538         this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
  4540     } else {
  4541       document.documentElement.removeAttribute("tabsintitlebar");
  4542       updateTitlebarDisplay();
  4544 #ifdef XP_MACOSX
  4545       let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
  4546       this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
  4547 #endif
  4548       // Reset the margins and padding that might have been modified:
  4549       titlebarContent.style.marginTop = "";
  4550       titlebarContent.style.marginBottom = "";
  4551       titlebar.style.marginBottom = "";
  4552       menubar.style.paddingBottom = "";
  4555     ToolbarIconColor.inferFromText();
  4556   },
  4558   _sizePlaceholder: function (type, width) {
  4559     Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
  4560                   function (node) { node.width = width; });
  4561   },
  4562 #endif
  4564   uninit: function () {
  4565 #ifdef CAN_DRAW_IN_TITLEBAR
  4566     this._initialized = false;
  4567     Services.prefs.removeObserver(this._prefName, this);
  4568     this._menuObserver.disconnect();
  4569     CustomizableUI.removeListener(this);
  4570 #endif
  4572 };
  4574 #ifdef CAN_DRAW_IN_TITLEBAR
  4575 function updateTitlebarDisplay() {
  4577 #ifdef XP_MACOSX
  4578   // OS X and the other platforms differ enough to necessitate this kind of
  4579   // special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR,
  4580   // we draw in the OS X titlebar when putting the tabs up there. However, OS X
  4581   // also draws in the titlebar when a lightweight theme is applied, regardless
  4582   // of whether or not the tabs are drawn in the titlebar.
  4583   if (TabsInTitlebar.enabled) {
  4584     document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1");
  4585     document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
  4586     document.documentElement.removeAttribute("drawtitle");
  4587   } else {
  4588     // We set chromemargin-nonlwtheme to "" instead of removing it as a way of
  4589     // making sure that LightweightThemeConsumer doesn't take it upon itself to
  4590     // detect this value again if and when we do a lwtheme state change.
  4591     document.documentElement.setAttribute("chromemargin-nonlwtheme", "");
  4592     let isCustomizing = document.documentElement.hasAttribute("customizing");
  4593     let hasLWTheme = document.documentElement.hasAttribute("lwtheme");
  4594     let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
  4595     if ((!hasLWTheme || isCustomizing) && !isPrivate) {
  4596       document.documentElement.removeAttribute("chromemargin");
  4598     document.documentElement.setAttribute("drawtitle", "true");
  4601 #else
  4603   if (TabsInTitlebar.enabled)
  4604     document.documentElement.setAttribute("chromemargin", "0,2,2,2");
  4605   else
  4606     document.documentElement.removeAttribute("chromemargin");
  4607 #endif
  4609 #endif
  4611 #ifdef CAN_DRAW_IN_TITLEBAR
  4612 function onTitlebarMaxClick() {
  4613   if (window.windowState == window.STATE_MAXIMIZED)
  4614     window.restore();
  4615   else
  4616     window.maximize();
  4618 #endif
  4620 function displaySecurityInfo()
  4622   BrowserPageInfo(null, "securityTab");
  4625 /**
  4626  * Opens or closes the sidebar identified by commandID.
  4628  * @param commandID a string identifying the sidebar to toggle; see the
  4629  *                  note below. (Optional if a sidebar is already open.)
  4630  * @param forceOpen boolean indicating whether the sidebar should be
  4631  *                  opened regardless of its current state (optional).
  4632  * @note
  4633  * We expect to find a xul:broadcaster element with the specified ID.
  4634  * The following attributes on that element may be used and/or modified:
  4635  *  - id           (required) the string to match commandID. The convention
  4636  *                 is to use this naming scheme: 'view<sidebar-name>Sidebar'.
  4637  *  - sidebarurl   (required) specifies the URL to load in this sidebar.
  4638  *  - sidebartitle or label (in that order) specify the title to
  4639  *                 display on the sidebar.
  4640  *  - checked      indicates whether the sidebar is currently displayed.
  4641  *                 Note that toggleSidebar updates this attribute when
  4642  *                 it changes the sidebar's visibility.
  4643  *  - group        this attribute must be set to "sidebar".
  4644  */
  4645 function toggleSidebar(commandID, forceOpen) {
  4647   var sidebarBox = document.getElementById("sidebar-box");
  4648   if (!commandID)
  4649     commandID = sidebarBox.getAttribute("sidebarcommand");
  4651   var sidebarBroadcaster = document.getElementById(commandID);
  4652   var sidebar = document.getElementById("sidebar"); // xul:browser
  4653   var sidebarTitle = document.getElementById("sidebar-title");
  4654   var sidebarSplitter = document.getElementById("sidebar-splitter");
  4656   if (sidebarBroadcaster.getAttribute("checked") == "true") {
  4657     if (!forceOpen) {
  4658       // Replace the document currently displayed in the sidebar with about:blank
  4659       // so that we can free memory by unloading the page. We need to explicitly
  4660       // create a new content viewer because the old one doesn't get destroyed
  4661       // until about:blank has loaded (which does not happen as long as the
  4662       // element is hidden).
  4663       sidebar.setAttribute("src", "about:blank");
  4664       sidebar.docShell.createAboutBlankContentViewer(null);
  4666       sidebarBroadcaster.removeAttribute("checked");
  4667       sidebarBox.setAttribute("sidebarcommand", "");
  4668       sidebarTitle.value = "";
  4669       sidebarBox.hidden = true;
  4670       sidebarSplitter.hidden = true;
  4671       gBrowser.selectedBrowser.focus();
  4672     } else {
  4673       fireSidebarFocusedEvent();
  4675     return;
  4678   // now we need to show the specified sidebar
  4680   // ..but first update the 'checked' state of all sidebar broadcasters
  4681   var broadcasters = document.getElementsByAttribute("group", "sidebar");
  4682   for (let broadcaster of broadcasters) {
  4683     // skip elements that observe sidebar broadcasters and random
  4684     // other elements
  4685     if (broadcaster.localName != "broadcaster")
  4686       continue;
  4688     if (broadcaster != sidebarBroadcaster)
  4689       broadcaster.removeAttribute("checked");
  4690     else
  4691       sidebarBroadcaster.setAttribute("checked", "true");
  4694   sidebarBox.hidden = false;
  4695   sidebarSplitter.hidden = false;
  4697   var url = sidebarBroadcaster.getAttribute("sidebarurl");
  4698   var title = sidebarBroadcaster.getAttribute("sidebartitle");
  4699   if (!title)
  4700     title = sidebarBroadcaster.getAttribute("label");
  4701   sidebar.setAttribute("src", url); // kick off async load
  4702   sidebarBox.setAttribute("sidebarcommand", sidebarBroadcaster.id);
  4703   sidebarTitle.value = title;
  4705   // We set this attribute here in addition to setting it on the <browser>
  4706   // element itself, because the code in gBrowserInit.onUnload persists this
  4707   // attribute, not the "src" of the <browser id="sidebar">. The reason it
  4708   // does that is that we want to delay sidebar load a bit when a browser
  4709   // window opens. See delayedStartup().
  4710   sidebarBox.setAttribute("src", url);
  4712   if (sidebar.contentDocument.location.href != url)
  4713     sidebar.addEventListener("load", sidebarOnLoad, true);
  4714   else // older code handled this case, so we do it too
  4715     fireSidebarFocusedEvent();
  4718 function sidebarOnLoad(event) {
  4719   var sidebar = document.getElementById("sidebar");
  4720   sidebar.removeEventListener("load", sidebarOnLoad, true);
  4721   // We're handling the 'load' event before it bubbles up to the usual
  4722   // (non-capturing) event handlers. Let it bubble up before firing the
  4723   // SidebarFocused event.
  4724   setTimeout(fireSidebarFocusedEvent, 0);
  4727 /**
  4728  * Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
  4729  * a chance to adjust focus as needed. An additional event is needed, because
  4730  * we don't want to focus the sidebar when it's opened on startup or in a new
  4731  * window, only when the user opens the sidebar.
  4732  */
  4733 function fireSidebarFocusedEvent() {
  4734   var sidebar = document.getElementById("sidebar");
  4735   var event = document.createEvent("Events");
  4736   event.initEvent("SidebarFocused", true, false);
  4737   sidebar.contentWindow.dispatchEvent(event);
  4741 var gHomeButton = {
  4742   prefDomain: "browser.startup.homepage",
  4743   observe: function (aSubject, aTopic, aPrefName)
  4745     if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
  4746       return;
  4748     this.updateTooltip();
  4749   },
  4751   updateTooltip: function (homeButton)
  4753     if (!homeButton)
  4754       homeButton = document.getElementById("home-button");
  4755     if (homeButton) {
  4756       var homePage = this.getHomePage();
  4757       homePage = homePage.replace(/\|/g,', ');
  4758       if (homePage.toLowerCase() == "about:home")
  4759         homeButton.setAttribute("tooltiptext", homeButton.getAttribute("aboutHomeOverrideTooltip"));
  4760       else
  4761         homeButton.setAttribute("tooltiptext", homePage);
  4763   },
  4765   getHomePage: function ()
  4767     var url;
  4768     try {
  4769       url = gPrefService.getComplexValue(this.prefDomain,
  4770                                 Components.interfaces.nsIPrefLocalizedString).data;
  4771     } catch (e) {
  4774     // use this if we can't find the pref
  4775     if (!url) {
  4776       var configBundle = Services.strings
  4777                                  .createBundle("chrome://branding/locale/browserconfig.properties");
  4778       url = configBundle.GetStringFromName(this.prefDomain);
  4781     return url;
  4782   },
  4784   updatePersonalToolbarStyle: function (homeButton)
  4786     if (!homeButton)
  4787       homeButton = document.getElementById("home-button");
  4788     if (homeButton)
  4789       homeButton.className = homeButton.parentNode.id == "PersonalToolbar"
  4790                                || homeButton.parentNode.parentNode.id == "PersonalToolbar" ?
  4791                              homeButton.className.replace("toolbarbutton-1", "bookmark-item") :
  4792                              homeButton.className.replace("bookmark-item", "toolbarbutton-1");
  4793   },
  4794 };
  4796 const nodeToTooltipMap = {
  4797   "bookmarks-menu-button": "bookmarksMenuButton.tooltip",
  4798 #ifdef XP_MACOSX
  4799   "print-button": "printButton.tooltip",
  4800 #endif
  4801   "new-window-button": "newWindowButton.tooltip",
  4802   "fullscreen-button": "fullscreenButton.tooltip",
  4803   "tabview-button": "tabviewButton.tooltip",
  4804 };
  4805 const nodeToShortcutMap = {
  4806   "bookmarks-menu-button": "manBookmarkKb",
  4807 #ifdef XP_MACOSX
  4808   "print-button": "printKb",
  4809 #endif
  4810   "new-window-button": "key_newNavigator",
  4811   "fullscreen-button": "key_fullScreen",
  4812   "tabview-button": "key_tabview",
  4813 };
  4814 const gDynamicTooltipCache = new Map();
  4815 function UpdateDynamicShortcutTooltipText(aTooltip) {
  4816   let nodeId = aTooltip.triggerNode.id;
  4817   if (!gDynamicTooltipCache.has(nodeId) && nodeId in nodeToTooltipMap) {
  4818     let strId = nodeToTooltipMap[nodeId];
  4819     let args = [];
  4820     if (nodeId in nodeToShortcutMap) {
  4821       let shortcutId = nodeToShortcutMap[nodeId];
  4822       let shortcut = document.getElementById(shortcutId);
  4823       if (shortcut) {
  4824         args.push(ShortcutUtils.prettifyShortcut(shortcut));
  4827     gDynamicTooltipCache.set(nodeId, gNavigatorBundle.getFormattedString(strId, args));
  4829   aTooltip.setAttribute("label", gDynamicTooltipCache.get(nodeId));
  4832 /**
  4833  * Gets the selected text in the active browser. Leading and trailing
  4834  * whitespace is removed, and consecutive whitespace is replaced by a single
  4835  * space. A maximum of 150 characters will be returned, regardless of the value
  4836  * of aCharLen.
  4838  * @param aCharLen
  4839  *        The maximum number of characters to return.
  4840  */
  4841 function getBrowserSelection(aCharLen) {
  4842   // selections of more than 150 characters aren't useful
  4843   const kMaxSelectionLen = 150;
  4844   const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
  4846   let [element, focusedWindow] = BrowserUtils.getFocusSync(document);
  4847   var selection = focusedWindow.getSelection().toString();
  4848   // try getting a selected text in text input.
  4849   if (!selection) {
  4850     var isOnTextInput = function isOnTextInput(elem) {
  4851       // we avoid to return a value if a selection is in password field.
  4852       // ref. bug 565717
  4853       return elem instanceof HTMLTextAreaElement ||
  4854              (elem instanceof HTMLInputElement && elem.mozIsTextField(true));
  4855     };
  4857     if (isOnTextInput(element)) {
  4858       selection = element.QueryInterface(Ci.nsIDOMNSEditableElement)
  4859                          .editor.selection.toString();
  4863   if (selection) {
  4864     if (selection.length > charLen) {
  4865       // only use the first charLen important chars. see bug 221361
  4866       var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
  4867       pattern.test(selection);
  4868       selection = RegExp.lastMatch;
  4871     selection = selection.trim().replace(/\s+/g, " ");
  4873     if (selection.length > charLen)
  4874       selection = selection.substr(0, charLen);
  4876   return selection;
  4879 var gWebPanelURI;
  4880 function openWebPanel(aTitle, aURI)
  4882     // Ensure that the web panels sidebar is open.
  4883     toggleSidebar('viewWebPanelsSidebar', true);
  4885     // Set the title of the panel.
  4886     document.getElementById("sidebar-title").value = aTitle;
  4888     // Tell the Web Panels sidebar to load the bookmark.
  4889     var sidebar = document.getElementById("sidebar");
  4890     if (sidebar.docShell && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser')) {
  4891         sidebar.contentWindow.loadWebPanel(aURI);
  4892         if (gWebPanelURI) {
  4893             gWebPanelURI = "";
  4894             sidebar.removeEventListener("load", asyncOpenWebPanel, true);
  4897     else {
  4898         // The panel is still being constructed.  Attach an onload handler.
  4899         if (!gWebPanelURI)
  4900             sidebar.addEventListener("load", asyncOpenWebPanel, true);
  4901         gWebPanelURI = aURI;
  4905 function asyncOpenWebPanel(event)
  4907     var sidebar = document.getElementById("sidebar");
  4908     if (gWebPanelURI && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser'))
  4909         sidebar.contentWindow.loadWebPanel(gWebPanelURI);
  4910     gWebPanelURI = "";
  4911     sidebar.removeEventListener("load", asyncOpenWebPanel, true);
  4914 /*
  4915  * - [ Dependencies ] ---------------------------------------------------------
  4916  *  utilityOverlay.js:
  4917  *    - gatherTextUnder
  4918  */
  4920 /**
  4921  * Extracts linkNode and href for the current click target.
  4923  * @param event
  4924  *        The click event.
  4925  * @return [href, linkNode].
  4927  * @note linkNode will be null if the click wasn't on an anchor
  4928  *       element (or XLink).
  4929  */
  4930 function hrefAndLinkNodeForClickEvent(event)
  4932   function isHTMLLink(aNode)
  4934     // Be consistent with what nsContextMenu.js does.
  4935     return ((aNode instanceof HTMLAnchorElement && aNode.href) ||
  4936             (aNode instanceof HTMLAreaElement && aNode.href) ||
  4937             aNode instanceof HTMLLinkElement);
  4940   let node = event.target;
  4941   while (node && !isHTMLLink(node)) {
  4942     node = node.parentNode;
  4945   if (node)
  4946     return [node.href, node];
  4948   // If there is no linkNode, try simple XLink.
  4949   let href, baseURI;
  4950   node = event.target;
  4951   while (node && !href) {
  4952     if (node.nodeType == Node.ELEMENT_NODE) {
  4953       href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
  4954       if (href)
  4955         baseURI = node.baseURI;
  4957     node = node.parentNode;
  4960   // In case of XLink, we don't return the node we got href from since
  4961   // callers expect <a>-like elements.
  4962   return [href ? makeURLAbsolute(baseURI, href) : null, null];
  4965 /**
  4966  * Called whenever the user clicks in the content area.
  4968  * @param event
  4969  *        The click event.
  4970  * @param isPanelClick
  4971  *        Whether the event comes from a web panel.
  4972  * @note default event is prevented if the click is handled.
  4973  */
  4974 function contentAreaClick(event, isPanelClick)
  4976   if (!event.isTrusted || event.defaultPrevented || event.button == 2)
  4977     return;
  4979   let [href, linkNode] = hrefAndLinkNodeForClickEvent(event);
  4980   if (!href) {
  4981     // Not a link, handle middle mouse navigation.
  4982     if (event.button == 1 &&
  4983         gPrefService.getBoolPref("middlemouse.contentLoadURL") &&
  4984         !gPrefService.getBoolPref("general.autoScroll")) {
  4985       middleMousePaste(event);
  4986       event.preventDefault();
  4988     return;
  4991   // This code only applies if we have a linkNode (i.e. clicks on real anchor
  4992   // elements, as opposed to XLink).
  4993   if (linkNode && event.button == 0 &&
  4994       !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
  4995     // A Web panel's links should target the main content area.  Do this
  4996     // if no modifier keys are down and if there's no target or the target
  4997     // equals _main (the IE convention) or _content (the Mozilla convention).
  4998     let target = linkNode.target;
  4999     let mainTarget = !target || target == "_content" || target  == "_main";
  5000     if (isPanelClick && mainTarget) {
  5001       // javascript and data links should be executed in the current browser.
  5002       if (linkNode.getAttribute("onclick") ||
  5003           href.startsWith("javascript:") ||
  5004           href.startsWith("data:"))
  5005         return;
  5007       try {
  5008         urlSecurityCheck(href, linkNode.ownerDocument.nodePrincipal);
  5010       catch(ex) {
  5011         // Prevent loading unsecure destinations.
  5012         event.preventDefault();
  5013         return;
  5016       loadURI(href, null, null, false);
  5017       event.preventDefault();
  5018       return;
  5021     if (linkNode.getAttribute("rel") == "sidebar") {
  5022       // This is the Opera convention for a special link that, when clicked,
  5023       // allows to add a sidebar panel.  The link's title attribute contains
  5024       // the title that should be used for the sidebar panel.
  5025       PlacesUIUtils.showBookmarkDialog({ action: "add"
  5026                                        , type: "bookmark"
  5027                                        , uri: makeURI(href)
  5028                                        , title: linkNode.getAttribute("title")
  5029                                        , loadBookmarkInSidebar: true
  5030                                        , hiddenRows: [ "description"
  5031                                                      , "location"
  5032                                                      , "keyword" ]
  5033                                        }, window);
  5034       event.preventDefault();
  5035       return;
  5039   handleLinkClick(event, href, linkNode);
  5041   // Mark the page as a user followed link.  This is done so that history can
  5042   // distinguish automatic embed visits from user activated ones.  For example
  5043   // pages loaded in frames are embed visits and lost with the session, while
  5044   // visits across frames should be preserved.
  5045   try {
  5046     if (!PrivateBrowsingUtils.isWindowPrivate(window))
  5047       PlacesUIUtils.markPageAsFollowedLink(href);
  5048   } catch (ex) { /* Skip invalid URIs. */ }
  5051 /**
  5052  * Handles clicks on links.
  5054  * @return true if the click event was handled, false otherwise.
  5055  */
  5056 function handleLinkClick(event, href, linkNode) {
  5057   if (event.button == 2) // right click
  5058     return false;
  5060   var where = whereToOpenLink(event);
  5061   if (where == "current")
  5062     return false;
  5064   var doc = event.target.ownerDocument;
  5066   if (where == "save") {
  5067     saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
  5068             true, doc.documentURIObject, doc);
  5069     event.preventDefault();
  5070     return true;
  5073   var referrerURI = doc.documentURIObject;
  5074   // if the mixedContentChannel is present and the referring URI passes
  5075   // a same origin check with the target URI, we can preserve the users
  5076   // decision of disabling MCB on a page for it's child tabs.
  5077   var persistDisableMCBInChildTab = false;
  5079   if (where == "tab" && gBrowser.docShell.mixedContentChannel) {
  5080     const sm = Services.scriptSecurityManager;
  5081     try {
  5082       var targetURI = makeURI(href);
  5083       sm.checkSameOriginURI(referrerURI, targetURI, false);
  5084       persistDisableMCBInChildTab = true;
  5086     catch (e) { }
  5089   urlSecurityCheck(href, doc.nodePrincipal);
  5090   openLinkIn(href, where, { referrerURI: referrerURI,
  5091                             charset: doc.characterSet,
  5092                             disableMCB: persistDisableMCBInChildTab});
  5093   event.preventDefault();
  5094   return true;
  5097 function middleMousePaste(event) {
  5098   let clipboard = readFromClipboard();
  5099   if (!clipboard)
  5100     return;
  5102   // Strip embedded newlines and surrounding whitespace, to match the URL
  5103   // bar's behavior (stripsurroundingwhitespace)
  5104   clipboard = clipboard.replace(/\s*\n\s*/g, "");
  5106   // if it's not the current tab, we don't need to do anything because the 
  5107   // browser doesn't exist.
  5108   let where = whereToOpenLink(event, true, false);
  5109   let lastLocationChange;
  5110   if (where == "current") {
  5111     lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
  5114   getShortcutOrURIAndPostData(clipboard, data => {
  5115     try {
  5116       makeURI(data.url);
  5117     } catch (ex) {
  5118       // Not a valid URI.
  5119       return;
  5122     try {
  5123       addToUrlbarHistory(data.url);
  5124     } catch (ex) {
  5125       // Things may go wrong when adding url to session history,
  5126       // but don't let that interfere with the loading of the url.
  5127       Cu.reportError(ex);
  5130     if (where != "current" ||
  5131         lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
  5132       openUILink(data.url, event,
  5133                  { ignoreButton: true,
  5134                    disallowInheritPrincipal: !data.mayInheritPrincipal });
  5136   });
  5138   event.stopPropagation();
  5141 function handleDroppedLink(event, url, name)
  5143   let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
  5145   getShortcutOrURIAndPostData(url, data => {
  5146     if (data.url &&
  5147         lastLocationChange == gBrowser.selectedBrowser.lastLocationChange)
  5148       loadURI(data.url, null, data.postData, false);
  5149   });
  5151   // Keep the event from being handled by the dragDrop listeners
  5152   // built-in to gecko if they happen to be above us.
  5153   event.preventDefault();
  5154 };
  5156 function BrowserSetForcedCharacterSet(aCharset)
  5158   if (aCharset) {
  5159     gBrowser.docShell.gatherCharsetMenuTelemetry();
  5160     gBrowser.docShell.charset = aCharset;
  5161     // Save the forced character-set
  5162     if (!PrivateBrowsingUtils.isWindowPrivate(window))
  5163       PlacesUtils.setCharsetForURI(getWebNavigation().currentURI, aCharset);
  5165   BrowserCharsetReload();
  5168 function BrowserCharsetReload()
  5170   BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
  5173 function charsetMenuGetElement(parent, charset) {
  5174   return parent.getElementsByAttribute("charset", charset)[0];
  5177 function UpdateCurrentCharset(target) {
  5178     // extract the charset from DOM
  5179     var wnd = document.commandDispatcher.focusedWindow;
  5180     if ((window == wnd) || (wnd == null)) wnd = window.content;
  5182     // Uncheck previous item
  5183     if (gPrevCharset) {
  5184         var pref_item = charsetMenuGetElement(target, gPrevCharset);
  5185         if (pref_item)
  5186           pref_item.setAttribute('checked', 'false');
  5189     var menuitem = charsetMenuGetElement(target, CharsetMenu.foldCharset(wnd.document.characterSet));
  5190     if (menuitem) {
  5191         menuitem.setAttribute('checked', 'true');
  5195 function charsetLoadListener() {
  5196   var charset = CharsetMenu.foldCharset(window.content.document.characterSet);
  5198   if (charset.length > 0 && (charset != gLastBrowserCharset)) {
  5199     gPrevCharset = gLastBrowserCharset;
  5200     gLastBrowserCharset = charset;
  5204 var gPageStyleMenu = {
  5206   // This maps from a <browser> element (or, more specifically, a
  5207   // browser's permanentKey) to a CPOW that gives synchronous access
  5208   // to the list of style sheets in a content window. The use of the
  5209   // permanentKey is to avoid issues with docshell swapping.
  5210   _pageStyleSyncHandlers: new WeakMap(),
  5212   init: function() {
  5213     let mm = window.messageManager;
  5214     mm.addMessageListener("PageStyle:SetSyncHandler", (msg) => {
  5215       this._pageStyleSyncHandlers.set(msg.target.permanentKey, msg.objects.syncHandler);
  5216     });
  5217   },
  5219   getAllStyleSheets: function () {
  5220     let handler = this._pageStyleSyncHandlers.get(gBrowser.selectedBrowser.permanentKey);
  5221     try {
  5222       return handler.getAllStyleSheets();
  5223     } catch (ex) {
  5224       // In case the child died or timed out.
  5225       return [];
  5227   },
  5229   _getStyleSheetInfo: function (browser) {
  5230     let handler = this._pageStyleSyncHandlers.get(gBrowser.selectedBrowser.permanentKey);
  5231     try {
  5232       return handler.getStyleSheetInfo();
  5233     } catch (ex) {
  5234       // In case the child died or timed out.
  5235       return {styleSheets: [], authorStyleDisabled: false, preferredStyleSheetSet: true};
  5237   },
  5239   fillPopup: function (menuPopup) {
  5240     let styleSheetInfo = this._getStyleSheetInfo(gBrowser.selectedBrowser);
  5241     var noStyle = menuPopup.firstChild;
  5242     var persistentOnly = noStyle.nextSibling;
  5243     var sep = persistentOnly.nextSibling;
  5244     while (sep.nextSibling)
  5245       menuPopup.removeChild(sep.nextSibling);
  5247     let styleSheets = styleSheetInfo.styleSheets;
  5248     var currentStyleSheets = {};
  5249     var styleDisabled = styleSheetInfo.authorStyleDisabled;
  5250     var haveAltSheets = false;
  5251     var altStyleSelected = false;
  5253     for (let currentStyleSheet of styleSheets) {
  5254       if (!currentStyleSheet.disabled)
  5255         altStyleSelected = true;
  5257       haveAltSheets = true;
  5259       let lastWithSameTitle = null;
  5260       if (currentStyleSheet.title in currentStyleSheets)
  5261         lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
  5263       if (!lastWithSameTitle) {
  5264         let menuItem = document.createElement("menuitem");
  5265         menuItem.setAttribute("type", "radio");
  5266         menuItem.setAttribute("label", currentStyleSheet.title);
  5267         menuItem.setAttribute("data", currentStyleSheet.title);
  5268         menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
  5269         menuItem.setAttribute("oncommand", "gPageStyleMenu.switchStyleSheet(this.getAttribute('data'));");
  5270         menuPopup.appendChild(menuItem);
  5271         currentStyleSheets[currentStyleSheet.title] = menuItem;
  5272       } else if (currentStyleSheet.disabled) {
  5273         lastWithSameTitle.removeAttribute("checked");
  5277     noStyle.setAttribute("checked", styleDisabled);
  5278     persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
  5279     persistentOnly.hidden = styleSheetInfo.preferredStyleSheetSet ? haveAltSheets : false;
  5280     sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
  5281   },
  5283   switchStyleSheet: function (title) {
  5284     let mm = gBrowser.selectedBrowser.messageManager;
  5285     mm.sendAsyncMessage("PageStyle:Switch", {title: title});
  5286   },
  5288   disableStyle: function () {
  5289     let mm = gBrowser.selectedBrowser.messageManager;
  5290     mm.sendAsyncMessage("PageStyle:Disable");
  5291   },
  5292 };
  5294 /* Legacy global page-style functions */
  5295 var getAllStyleSheets   = gPageStyleMenu.getAllStyleSheets.bind(gPageStyleMenu);
  5296 var stylesheetFillPopup = gPageStyleMenu.fillPopup.bind(gPageStyleMenu);
  5297 function stylesheetSwitchAll(contentWindow, title) {
  5298   // We ignore the contentWindow param. Add-ons don't appear to use
  5299   // it, and it's difficult to support in e10s (where it will be a
  5300   // CPOW).
  5301   gPageStyleMenu.switchStyleSheet(title);
  5303 function setStyleDisabled(disabled) {
  5304   if (disabled)
  5305     gPageStyleMenu.disableStyle();
  5309 var LanguageDetectionListener = {
  5310   init: function() {
  5311     window.messageManager.addMessageListener("LanguageDetection:Result", msg => {
  5312       Translation.languageDetected(msg.target, msg.data);
  5313     });
  5315 };
  5318 var BrowserOffline = {
  5319   _inited: false,
  5321   /////////////////////////////////////////////////////////////////////////////
  5322   // BrowserOffline Public Methods
  5323   init: function ()
  5325     if (!this._uiElement)
  5326       this._uiElement = document.getElementById("workOfflineMenuitemState");
  5328     Services.obs.addObserver(this, "network:offline-status-changed", false);
  5330     this._updateOfflineUI(Services.io.offline);
  5332     this._inited = true;
  5333   },
  5335   uninit: function ()
  5337     if (this._inited) {
  5338       Services.obs.removeObserver(this, "network:offline-status-changed");
  5340   },
  5342   toggleOfflineStatus: function ()
  5344     var ioService = Services.io;
  5346     // Stop automatic management of the offline status
  5347     try {
  5348       ioService.manageOfflineStatus = false;
  5349     } catch (ex) {
  5352     if (!ioService.offline && !this._canGoOffline()) {
  5353       this._updateOfflineUI(false);
  5354       return;
  5357     ioService.offline = !ioService.offline;
  5358   },
  5360   /////////////////////////////////////////////////////////////////////////////
  5361   // nsIObserver
  5362   observe: function (aSubject, aTopic, aState)
  5364     if (aTopic != "network:offline-status-changed")
  5365       return;
  5367     this._updateOfflineUI(aState == "offline");
  5368   },
  5370   /////////////////////////////////////////////////////////////////////////////
  5371   // BrowserOffline Implementation Methods
  5372   _canGoOffline: function ()
  5374     try {
  5375       var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
  5376       Services.obs.notifyObservers(cancelGoOffline, "offline-requested", null);
  5378       // Something aborted the quit process.
  5379       if (cancelGoOffline.data)
  5380         return false;
  5382     catch (ex) {
  5385     return true;
  5386   },
  5388   _uiElement: null,
  5389   _updateOfflineUI: function (aOffline)
  5391     var offlineLocked = gPrefService.prefIsLocked("network.online");
  5392     if (offlineLocked)
  5393       this._uiElement.setAttribute("disabled", "true");
  5395     this._uiElement.setAttribute("checked", aOffline);
  5397 };
  5399 var OfflineApps = {
  5400   /////////////////////////////////////////////////////////////////////////////
  5401   // OfflineApps Public Methods
  5402   init: function ()
  5404     Services.obs.addObserver(this, "offline-cache-update-completed", false);
  5405   },
  5407   uninit: function ()
  5409     Services.obs.removeObserver(this, "offline-cache-update-completed");
  5410   },
  5412   handleEvent: function(event) {
  5413     if (event.type == "MozApplicationManifest") {
  5414       this.offlineAppRequested(event.originalTarget.defaultView);
  5416   },
  5418   /////////////////////////////////////////////////////////////////////////////
  5419   // OfflineApps Implementation Methods
  5421   // XXX: _getBrowserWindowForContentWindow and _getBrowserForContentWindow
  5422   // were taken from browser/components/feeds/src/WebContentConverter.
  5423   _getBrowserWindowForContentWindow: function(aContentWindow) {
  5424     return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
  5425                          .getInterface(Ci.nsIWebNavigation)
  5426                          .QueryInterface(Ci.nsIDocShellTreeItem)
  5427                          .rootTreeItem
  5428                          .QueryInterface(Ci.nsIInterfaceRequestor)
  5429                          .getInterface(Ci.nsIDOMWindow)
  5430                          .wrappedJSObject;
  5431   },
  5433   _getBrowserForContentWindow: function(aBrowserWindow, aContentWindow) {
  5434     // This depends on pseudo APIs of browser.js and tabbrowser.xml
  5435     aContentWindow = aContentWindow.top;
  5436     var browsers = aBrowserWindow.gBrowser.browsers;
  5437     for (let browser of browsers) {
  5438       if (browser.contentWindow == aContentWindow)
  5439         return browser;
  5441     // handle other browser/iframe elements that may need popupnotifications
  5442     let browser = aContentWindow
  5443                           .QueryInterface(Ci.nsIInterfaceRequestor)
  5444                           .getInterface(Ci.nsIWebNavigation)
  5445                           .QueryInterface(Ci.nsIDocShell)
  5446                           .chromeEventHandler;
  5447     if (browser.getAttribute("popupnotificationanchor"))
  5448       return browser;
  5449     return null;
  5450   },
  5452   _getManifestURI: function(aWindow) {
  5453     if (!aWindow.document.documentElement)
  5454       return null;
  5456     var attr = aWindow.document.documentElement.getAttribute("manifest");
  5457     if (!attr)
  5458       return null;
  5460     try {
  5461       var contentURI = makeURI(aWindow.location.href, null, null);
  5462       return makeURI(attr, aWindow.document.characterSet, contentURI);
  5463     } catch (e) {
  5464       return null;
  5466   },
  5468   // A cache update isn't tied to a specific window.  Try to find
  5469   // the best browser in which to warn the user about space usage
  5470   _getBrowserForCacheUpdate: function(aCacheUpdate) {
  5471     // Prefer the current browser
  5472     var uri = this._getManifestURI(content);
  5473     if (uri && uri.equals(aCacheUpdate.manifestURI)) {
  5474       return gBrowser.selectedBrowser;
  5477     var browsers = gBrowser.browsers;
  5478     for (let browser of browsers) {
  5479       uri = this._getManifestURI(browser.contentWindow);
  5480       if (uri && uri.equals(aCacheUpdate.manifestURI)) {
  5481         return browser;
  5485     // is this from a non-tab browser/iframe?
  5486     browsers = document.querySelectorAll("iframe[popupnotificationanchor] | browser[popupnotificationanchor]");
  5487     for (let browser of browsers) {
  5488       uri = this._getManifestURI(browser.contentWindow);
  5489       if (uri && uri.equals(aCacheUpdate.manifestURI)) {
  5490         return browser;
  5494     return null;
  5495   },
  5497   _warnUsage: function(aBrowser, aURI) {
  5498     if (!aBrowser)
  5499       return;
  5501     let mainAction = {
  5502       label: gNavigatorBundle.getString("offlineApps.manageUsage"),
  5503       accessKey: gNavigatorBundle.getString("offlineApps.manageUsageAccessKey"),
  5504       callback: OfflineApps.manage
  5505     };
  5507     let warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
  5508     let message = gNavigatorBundle.getFormattedString("offlineApps.usage",
  5509                                                       [ aURI.host,
  5510                                                         warnQuota / 1024 ]);
  5512     let anchorID = "indexedDB-notification-icon";
  5513     PopupNotifications.show(aBrowser, "offline-app-usage", message,
  5514                             anchorID, mainAction);
  5516     // Now that we've warned once, prevent the warning from showing up
  5517     // again.
  5518     Services.perms.add(aURI, "offline-app",
  5519                        Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
  5520   },
  5522   // XXX: duplicated in preferences/advanced.js
  5523   _getOfflineAppUsage: function (host, groups)
  5525     var cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
  5526                        getService(Ci.nsIApplicationCacheService);
  5527     if (!groups)
  5528       groups = cacheService.getGroups();
  5530     var usage = 0;
  5531     for (let group of groups) {
  5532       var uri = Services.io.newURI(group, null, null);
  5533       if (uri.asciiHost == host) {
  5534         var cache = cacheService.getActiveCache(group);
  5535         usage += cache.usage;
  5539     return usage;
  5540   },
  5542   _checkUsage: function(aURI) {
  5543     // if the user has already allowed excessive usage, don't bother checking
  5544     if (Services.perms.testExactPermission(aURI, "offline-app") !=
  5545         Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
  5546       var usage = this._getOfflineAppUsage(aURI.asciiHost);
  5547       var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
  5548       if (usage >= warnQuota * 1024) {
  5549         return true;
  5553     return false;
  5554   },
  5556   offlineAppRequested: function(aContentWindow) {
  5557     if (!gPrefService.getBoolPref("browser.offline-apps.notify")) {
  5558       return;
  5561     let browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
  5562     let browser = this._getBrowserForContentWindow(browserWindow,
  5563                                                    aContentWindow);
  5565     let currentURI = aContentWindow.document.documentURIObject;
  5567     // don't bother showing UI if the user has already made a decision
  5568     if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
  5569       return;
  5571     try {
  5572       if (gPrefService.getBoolPref("offline-apps.allow_by_default")) {
  5573         // all pages can use offline capabilities, no need to ask the user
  5574         return;
  5576     } catch(e) {
  5577       // this pref isn't set by default, ignore failures
  5580     let host = currentURI.asciiHost;
  5581     let notificationID = "offline-app-requested-" + host;
  5582     let notification = PopupNotifications.getNotification(notificationID, browser);
  5584     if (notification) {
  5585       notification.options.documents.push(aContentWindow.document);
  5586     } else {
  5587       let mainAction = {
  5588         label: gNavigatorBundle.getString("offlineApps.allow"),
  5589         accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
  5590         callback: function() {
  5591           for (let document of notification.options.documents) {
  5592             OfflineApps.allowSite(document);
  5595       };
  5596       let secondaryActions = [{
  5597         label: gNavigatorBundle.getString("offlineApps.never"),
  5598         accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
  5599         callback: function() {
  5600           for (let document of notification.options.documents) {
  5601             OfflineApps.disallowSite(document);
  5604       }];
  5605       let message = gNavigatorBundle.getFormattedString("offlineApps.available",
  5606                                                         [ host ]);
  5607       let anchorID = "indexedDB-notification-icon";
  5608       let options= {
  5609         documents : [ aContentWindow.document ]
  5610       };
  5611       notification = PopupNotifications.show(browser, notificationID, message,
  5612                                              anchorID, mainAction,
  5613                                              secondaryActions, options);
  5615   },
  5617   allowSite: function(aDocument) {
  5618     Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION);
  5620     // When a site is enabled while loading, manifest resources will
  5621     // start fetching immediately.  This one time we need to do it
  5622     // ourselves.
  5623     this._startFetching(aDocument);
  5624   },
  5626   disallowSite: function(aDocument) {
  5627     Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION);
  5628   },
  5630   manage: function() {
  5631     openAdvancedPreferences("networkTab");
  5632   },
  5634   _startFetching: function(aDocument) {
  5635     if (!aDocument.documentElement)
  5636       return;
  5638     var manifest = aDocument.documentElement.getAttribute("manifest");
  5639     if (!manifest)
  5640       return;
  5642     var manifestURI = makeURI(manifest, aDocument.characterSet,
  5643                               aDocument.documentURIObject);
  5645     var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
  5646                         getService(Ci.nsIOfflineCacheUpdateService);
  5647     updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window);
  5648   },
  5650   /////////////////////////////////////////////////////////////////////////////
  5651   // nsIObserver
  5652   observe: function (aSubject, aTopic, aState)
  5654     if (aTopic == "offline-cache-update-completed") {
  5655       var cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
  5657       var uri = cacheUpdate.manifestURI;
  5658       if (OfflineApps._checkUsage(uri)) {
  5659         var browser = this._getBrowserForCacheUpdate(cacheUpdate);
  5660         if (browser) {
  5661           OfflineApps._warnUsage(browser, cacheUpdate.manifestURI);
  5666 };
  5668 var IndexedDBPromptHelper = {
  5669   _permissionsPrompt: "indexedDB-permissions-prompt",
  5670   _permissionsResponse: "indexedDB-permissions-response",
  5672   _quotaPrompt: "indexedDB-quota-prompt",
  5673   _quotaResponse: "indexedDB-quota-response",
  5674   _quotaCancel: "indexedDB-quota-cancel",
  5676   _notificationIcon: "indexedDB-notification-icon",
  5678   init:
  5679   function IndexedDBPromptHelper_init() {
  5680     Services.obs.addObserver(this, this._permissionsPrompt, false);
  5681     Services.obs.addObserver(this, this._quotaPrompt, false);
  5682     Services.obs.addObserver(this, this._quotaCancel, false);
  5683   },
  5685   uninit:
  5686   function IndexedDBPromptHelper_uninit() {
  5687     Services.obs.removeObserver(this, this._permissionsPrompt);
  5688     Services.obs.removeObserver(this, this._quotaPrompt);
  5689     Services.obs.removeObserver(this, this._quotaCancel);
  5690   },
  5692   observe:
  5693   function IndexedDBPromptHelper_observe(subject, topic, data) {
  5694     if (topic != this._permissionsPrompt &&
  5695         topic != this._quotaPrompt &&
  5696         topic != this._quotaCancel) {
  5697       throw new Error("Unexpected topic!");
  5700     var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
  5702     var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
  5703     var contentDocument = contentWindow.document;
  5704     var browserWindow =
  5705       OfflineApps._getBrowserWindowForContentWindow(contentWindow);
  5707     if (browserWindow != window) {
  5708       // Must belong to some other window.
  5709       return;
  5712     var browser =
  5713       OfflineApps._getBrowserForContentWindow(browserWindow, contentWindow);
  5715     var host = contentDocument.documentURIObject.asciiHost;
  5717     var message;
  5718     var responseTopic;
  5719     if (topic == this._permissionsPrompt) {
  5720       message = gNavigatorBundle.getFormattedString("offlineApps.available",
  5721                                                     [ host ]);
  5722       responseTopic = this._permissionsResponse;
  5724     else if (topic == this._quotaPrompt) {
  5725       message = gNavigatorBundle.getFormattedString("indexedDB.usage",
  5726                                                     [ host, data ]);
  5727       responseTopic = this._quotaResponse;
  5729     else if (topic == this._quotaCancel) {
  5730       responseTopic = this._quotaResponse;
  5733     const hiddenTimeoutDuration = 30000; // 30 seconds
  5734     const firstTimeoutDuration = 300000; // 5 minutes
  5736     var timeoutId;
  5738     var observer = requestor.getInterface(Ci.nsIObserver);
  5740     var mainAction = {
  5741       label: gNavigatorBundle.getString("offlineApps.allow"),
  5742       accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
  5743       callback: function() {
  5744         clearTimeout(timeoutId);
  5745         observer.observe(null, responseTopic,
  5746                          Ci.nsIPermissionManager.ALLOW_ACTION);
  5748     };
  5750     var secondaryActions = [
  5752         label: gNavigatorBundle.getString("offlineApps.never"),
  5753         accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
  5754         callback: function() {
  5755           clearTimeout(timeoutId);
  5756           observer.observe(null, responseTopic,
  5757                            Ci.nsIPermissionManager.DENY_ACTION);
  5760     ];
  5762     // This will be set to the result of PopupNotifications.show() below, or to
  5763     // the result of PopupNotifications.getNotification() if this is a
  5764     // quotaCancel notification.
  5765     var notification;
  5767     function timeoutNotification() {
  5768       // Remove the notification.
  5769       if (notification) {
  5770         notification.remove();
  5773       // Clear all of our timeout stuff. We may be called directly, not just
  5774       // when the timeout actually elapses.
  5775       clearTimeout(timeoutId);
  5777       // And tell the page that the popup timed out.
  5778       observer.observe(null, responseTopic,
  5779                        Ci.nsIPermissionManager.UNKNOWN_ACTION);
  5782     var options = {
  5783       eventCallback: function(state) {
  5784         // Don't do anything if the timeout has not been set yet.
  5785         if (!timeoutId) {
  5786           return;
  5789         // If the popup is being dismissed start the short timeout.
  5790         if (state == "dismissed") {
  5791           clearTimeout(timeoutId);
  5792           timeoutId = setTimeout(timeoutNotification, hiddenTimeoutDuration);
  5793           return;
  5796         // If the popup is being re-shown then clear the timeout allowing
  5797         // unlimited waiting.
  5798         if (state == "shown") {
  5799           clearTimeout(timeoutId);
  5802     };
  5804     if (topic == this._quotaCancel) {
  5805       notification = PopupNotifications.getNotification(this._quotaPrompt,
  5806                                                         browser);
  5807       timeoutNotification();
  5808       return;
  5811     notification = PopupNotifications.show(browser, topic, message,
  5812                                            this._notificationIcon, mainAction,
  5813                                            secondaryActions, options);
  5815     // Set the timeoutId after the popup has been created, and use the long
  5816     // timeout value. If the user doesn't notice the popup after this amount of
  5817     // time then it is most likely not visible and we want to alert the page.
  5818     timeoutId = setTimeout(timeoutNotification, firstTimeoutDuration);
  5820 };
  5822 var CanvasPermissionPromptHelper = {
  5823   _permissionsPrompt: "canvas-permissions-prompt",
  5824   _notificationIcon: "canvas-notification-icon",
  5826   init:
  5827   function CanvasPermissionPromptHelper_init() {
  5828     if (document.styleSheets && (document.styleSheets.length > 0)) try {
  5829       let ruleText = "panel[popupid=canvas-permissions-prompt] description { white-space: pre-wrap; }";
  5830       let sheet = document.styleSheets[0];
  5831       sheet.insertRule(ruleText, sheet.cssRules.length);
  5832     } catch (e) {};
  5834     Services.obs.addObserver(this, this._permissionsPrompt, false);
  5835   },
  5837   uninit:
  5838   function CanvasPermissionPromptHelper_uninit() {
  5839     Services.obs.removeObserver(this, this._permissionsPrompt, false);
  5840   },
  5842   // aSubject is an nsIDOMWindow.
  5843   // aData is an URL string.
  5844   observe:
  5845   function CanvasPermissionPromptHelper_observe(aSubject, aTopic, aData) {
  5846     if ((aTopic != this._permissionsPrompt) || !aData)
  5847       throw new Error("Unexpected topic or missing URL");
  5849     var uri = makeURI(aData);
  5850     var contentWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
  5851     var contentDocument = contentWindow.document;
  5852     var browserWindow =
  5853       OfflineApps._getBrowserWindowForContentWindow(contentWindow);
  5855     if (browserWindow != window) {
  5856       // Must belong to some other window.
  5857       return;
  5860     // If canvas prompt is already displayed, just return.  This is OK (and
  5861     // more efficient) since this permission is associated with the top
  5862     // browser's URL.
  5863     if (PopupNotifications.getNotification(aTopic, browser))
  5864       return;
  5866     var bundleSvc = Cc["@mozilla.org/intl/stringbundle;1"].
  5867                         getService(Ci.nsIStringBundleService);
  5868     var torBtnBundle;
  5869     try {
  5870       torBtnBundle = bundleSvc.createBundle(
  5871                              "chrome://torbutton/locale/torbutton.properties");
  5872     } catch (e) {}
  5874     var message = getLocalizedString("canvas.siteprompt", [ uri.asciiHost ]);
  5876     var mainAction = {
  5877       label: getLocalizedString("canvas.notNow"),
  5878       accessKey: getLocalizedString("canvas.notNowAccessKey"),
  5879       callback: function() {
  5880         return null;
  5882     };
  5884     var secondaryActions = [
  5886         label: getLocalizedString("canvas.never"),
  5887         accessKey: getLocalizedString("canvas.neverAccessKey"),
  5888         callback: function() {
  5889           setCanvasPermission(uri, Ci.nsIPermissionManager.DENY_ACTION);
  5891       },
  5893         label: getLocalizedString("canvas.allow"),
  5894         accessKey: getLocalizedString("canvas.allowAccessKey"),
  5895         callback: function() {
  5896             setCanvasPermission(uri, Ci.nsIPermissionManager.ALLOW_ACTION);
  5899     ];
  5901     // Since we have a process in place to perform localization for the
  5902     // Torbutton extension, get our strings from the extension if possible.
  5903     function getLocalizedString(aID, aParams) {
  5904       var s;
  5905       if (torBtnBundle) try {
  5906         if (aParams)
  5907           s = torBtnBundle.formatStringFromName(aID, aParams, aParams.length);
  5908         else
  5909           s = torBtnBundle.GetStringFromName(aID);
  5910       } catch (e) {}
  5912       if (!s) {
  5913         if (aParams)
  5914           s = gNavigatorBundle.getFormattedString(aID, aParams);
  5915         else
  5916           s = gNavigatorBundle.getString(aID);
  5919       return s;
  5922     function setCanvasPermission(aURI, aPerm) {
  5923       Services.perms.add(aURI, "canvas/extractData", aPerm,
  5924                          Ci.nsIPermissionManager.EXPIRE_NEVER);
  5927     var browser = OfflineApps._getBrowserForContentWindow(browserWindow,
  5928                                                           contentWindow);
  5929     notification = PopupNotifications.show(browser, aTopic, message,
  5930                                            this._notificationIcon, mainAction,
  5931                                            secondaryActions, null);
  5933 };
  5935 function WindowIsClosing()
  5937   if (TabView.isVisible()) {
  5938     TabView.hide();
  5939     return false;
  5942   if (!closeWindow(false, warnAboutClosingWindow))
  5943     return false;
  5945   // Bug 967873 - Proxy nsDocumentViewer::PermitUnload to the child process
  5946   if (gMultiProcessBrowser)
  5947     return true;
  5949   for (let browser of gBrowser.browsers) {
  5950     let ds = browser.docShell;
  5951     if (ds.contentViewer && !ds.contentViewer.permitUnload())
  5952       return false;
  5955   return true;
  5958 /**
  5959  * Checks if this is the last full *browser* window around. If it is, this will
  5960  * be communicated like quitting. Otherwise, we warn about closing multiple tabs.
  5961  * @returns true if closing can proceed, false if it got cancelled.
  5962  */
  5963 function warnAboutClosingWindow() {
  5964   // Popups aren't considered full browser windows; we also ignore private windows.
  5965   let isPBWindow = PrivateBrowsingUtils.isWindowPrivate(window) &&
  5966         !PrivateBrowsingUtils.permanentPrivateBrowsing;
  5967   if (!isPBWindow && !toolbar.visible)
  5968     return gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
  5970   // Figure out if there's at least one other browser window around.
  5971   let e = Services.wm.getEnumerator("navigator:browser");
  5972   let otherPBWindowExists = false;
  5973   let nonPopupPresent = false;
  5974   while (e.hasMoreElements()) {
  5975     let win = e.getNext();
  5976     if (!win.closed && win != window) {
  5977       if (isPBWindow && PrivateBrowsingUtils.isWindowPrivate(win))
  5978         otherPBWindowExists = true;
  5979       if (win.toolbar.visible)
  5980         nonPopupPresent = true;
  5981       // If the current window is not in private browsing mode we don't need to 
  5982       // look for other pb windows, we can leave the loop when finding the 
  5983       // first non-popup window. If however the current window is in private 
  5984       // browsing mode then we need at least one other pb and one non-popup 
  5985       // window to break out early.
  5986       if ((!isPBWindow || otherPBWindowExists) && nonPopupPresent)
  5987         break;
  5991   if (isPBWindow && !otherPBWindowExists) {
  5992     let exitingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
  5993                           createInstance(Ci.nsISupportsPRBool);
  5994     exitingCanceled.data = false;
  5995     Services.obs.notifyObservers(exitingCanceled,
  5996                                  "last-pb-context-exiting",
  5997                                  null);
  5998     if (exitingCanceled.data)
  5999       return false;
  6002   if (nonPopupPresent) {
  6003     return isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
  6006   let os = Services.obs;
  6008   let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
  6009                         createInstance(Ci.nsISupportsPRBool);
  6010   os.notifyObservers(closingCanceled,
  6011                      "browser-lastwindow-close-requested", null);
  6012   if (closingCanceled.data)
  6013     return false;
  6015   os.notifyObservers(null, "browser-lastwindow-close-granted", null);
  6017 #ifdef XP_MACOSX
  6018   // OS X doesn't quit the application when the last window is closed, but keeps
  6019   // the session alive. Hence don't prompt users to save tabs, but warn about
  6020   // closing multiple tabs.
  6021   return isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
  6022 #else
  6023   return true;
  6024 #endif
  6027 var MailIntegration = {
  6028   sendLinkForWindow: function (aWindow) {
  6029     this.sendMessage(aWindow.location.href,
  6030                      aWindow.document.title);
  6031   },
  6033   sendMessage: function (aBody, aSubject) {
  6034     // generate a mailto url based on the url and the url's title
  6035     var mailtoUrl = "mailto:";
  6036     if (aBody) {
  6037       mailtoUrl += "?body=" + encodeURIComponent(aBody);
  6038       mailtoUrl += "&subject=" + encodeURIComponent(aSubject);
  6041     var uri = makeURI(mailtoUrl);
  6043     // now pass this uri to the operating system
  6044     this._launchExternalUrl(uri);
  6045   },
  6047   // a generic method which can be used to pass arbitrary urls to the operating
  6048   // system.
  6049   // aURL --> a nsIURI which represents the url to launch
  6050   _launchExternalUrl: function (aURL) {
  6051     var extProtocolSvc =
  6052        Cc["@mozilla.org/uriloader/external-protocol-service;1"]
  6053          .getService(Ci.nsIExternalProtocolService);
  6054     if (extProtocolSvc)
  6055       extProtocolSvc.loadUrl(aURL);
  6057 };
  6059 function BrowserOpenAddonsMgr(aView) {
  6060   if (aView) {
  6061     let emWindow;
  6062     let browserWindow;
  6064     var receivePong = function receivePong(aSubject, aTopic, aData) {
  6065       let browserWin = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
  6066                                .getInterface(Ci.nsIWebNavigation)
  6067                                .QueryInterface(Ci.nsIDocShellTreeItem)
  6068                                .rootTreeItem
  6069                                .QueryInterface(Ci.nsIInterfaceRequestor)
  6070                                .getInterface(Ci.nsIDOMWindow);
  6071       if (!emWindow || browserWin == window /* favor the current window */) {
  6072         emWindow = aSubject;
  6073         browserWindow = browserWin;
  6076     Services.obs.addObserver(receivePong, "EM-pong", false);
  6077     Services.obs.notifyObservers(null, "EM-ping", "");
  6078     Services.obs.removeObserver(receivePong, "EM-pong");
  6080     if (emWindow) {
  6081       emWindow.loadView(aView);
  6082       browserWindow.gBrowser.selectedTab =
  6083         browserWindow.gBrowser._getTabForContentWindow(emWindow);
  6084       emWindow.focus();
  6085       return;
  6089   var newLoad = !switchToTabHavingURI("about:addons", true);
  6091   if (aView) {
  6092     // This must be a new load, else the ping/pong would have
  6093     // found the window above.
  6094     Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
  6095       Services.obs.removeObserver(observer, aTopic);
  6096       aSubject.loadView(aView);
  6097     }, "EM-loaded", false);
  6101 function GetSearchFieldBookmarkData(node) {
  6102   var charset = node.ownerDocument.characterSet;
  6104   var formBaseURI = makeURI(node.form.baseURI,
  6105                             charset);
  6107   var formURI = makeURI(node.form.getAttribute("action"),
  6108                         charset,
  6109                         formBaseURI);
  6111   var spec = formURI.spec;
  6113   var isURLEncoded =
  6114                (node.form.method.toUpperCase() == "POST"
  6115                 && (node.form.enctype == "application/x-www-form-urlencoded" ||
  6116                     node.form.enctype == ""));
  6118   var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
  6119                                                   [node.ownerDocument.title]);
  6120   var description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
  6122   var formData = [];
  6124   function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) {
  6125     if (aIsFormUrlEncoded)
  6126       return escape(aName + "=" + aValue);
  6127     else
  6128       return escape(aName) + "=" + escape(aValue);
  6131   for (let el of node.form.elements) {
  6132     if (!el.type) // happens with fieldsets
  6133       continue;
  6135     if (el == node) {
  6136       formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
  6137                                      // Don't escape "%s", just append
  6138                                      escapeNameValuePair(el.name, "", false) + "%s");
  6139       continue;
  6142     let type = el.type.toLowerCase();
  6144     if (((el instanceof HTMLInputElement && el.mozIsTextField(true)) ||
  6145         type == "hidden" || type == "textarea") ||
  6146         ((type == "checkbox" || type == "radio") && el.checked)) {
  6147       formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
  6148     } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
  6149       for (var j=0; j < el.options.length; j++) {
  6150         if (el.options[j].selected)
  6151           formData.push(escapeNameValuePair(el.name, el.options[j].value,
  6152                                             isURLEncoded));
  6157   var postData;
  6159   if (isURLEncoded)
  6160     postData = formData.join("&");
  6161   else
  6162     spec += "?" + formData.join("&");
  6164   return {
  6165     spec: spec,
  6166     title: title,
  6167     description: description,
  6168     postData: postData,
  6169     charSet: charset
  6170   };
  6174 function AddKeywordForSearchField() {
  6175   bookmarkData = GetSearchFieldBookmarkData(document.popupNode);
  6177   PlacesUIUtils.showBookmarkDialog({ action: "add"
  6178                                    , type: "bookmark"
  6179                                    , uri: makeURI(bookmarkData.spec)
  6180                                    , title: bookmarkData.title
  6181                                    , description: bookmarkData.description
  6182                                    , keyword: ""
  6183                                    , postData: bookmarkData.postData
  6184                                    , charSet: bookmarkData.charset
  6185                                    , hiddenRows: [ "location"
  6186                                                  , "description"
  6187                                                  , "tags"
  6188                                                  , "loadInSidebar" ]
  6189                                    }, window);
  6192 function SwitchDocumentDirection(aWindow) {
  6193   // document.dir can also be "auto", in which case it won't change
  6194   if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") {
  6195     aWindow.document.dir = "rtl";
  6196   } else if (aWindow.document.dir == "rtl") {
  6197     aWindow.document.dir = "ltr";
  6199   for (var run = 0; run < aWindow.frames.length; run++)
  6200     SwitchDocumentDirection(aWindow.frames[run]);
  6203 function convertFromUnicode(charset, str)
  6205   try {
  6206     var unicodeConverter = Components
  6207        .classes["@mozilla.org/intl/scriptableunicodeconverter"]
  6208        .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  6209     unicodeConverter.charset = charset;
  6210     str = unicodeConverter.ConvertFromUnicode(str);
  6211     return str + unicodeConverter.Finish();
  6212   } catch(ex) {
  6213     return null;
  6217 /**
  6218  * Re-open a closed tab.
  6219  * @param aIndex
  6220  *        The index of the tab (via SessionStore.getClosedTabData)
  6221  * @returns a reference to the reopened tab.
  6222  */
  6223 function undoCloseTab(aIndex) {
  6224   // wallpaper patch to prevent an unnecessary blank tab (bug 343895)
  6225   var blankTabToRemove = null;
  6226   if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab))
  6227     blankTabToRemove = gBrowser.selectedTab;
  6229   var tab = null;
  6230   if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) {
  6231     TabView.prepareUndoCloseTab(blankTabToRemove);
  6232     tab = SessionStore.undoCloseTab(window, aIndex || 0);
  6233     TabView.afterUndoCloseTab();
  6235     if (blankTabToRemove)
  6236       gBrowser.removeTab(blankTabToRemove);
  6239   return tab;
  6242 /**
  6243  * Re-open a closed window.
  6244  * @param aIndex
  6245  *        The index of the window (via SessionStore.getClosedWindowData)
  6246  * @returns a reference to the reopened window.
  6247  */
  6248 function undoCloseWindow(aIndex) {
  6249   let window = null;
  6250   if (SessionStore.getClosedWindowCount() > (aIndex || 0))
  6251     window = SessionStore.undoCloseWindow(aIndex || 0);
  6253   return window;
  6256 /*
  6257  * Determines if a tab is "empty", usually used in the context of determining
  6258  * if it's ok to close the tab.
  6259  */
  6260 function isTabEmpty(aTab) {
  6261   if (aTab.hasAttribute("busy"))
  6262     return false;
  6264   let browser = aTab.linkedBrowser;
  6265   if (!isBlankPageURL(browser.currentURI.spec))
  6266     return false;
  6268   // Bug 863515 - Make content.opener checks work in electrolysis.
  6269   if (!gMultiProcessBrowser && browser.contentWindow.opener)
  6270     return false;
  6272   if (browser.sessionHistory && browser.sessionHistory.count >= 2)
  6273     return false;
  6275   return true;
  6278 #ifdef MOZ_SERVICES_SYNC
  6279 function BrowserOpenSyncTabs() {
  6280   switchToTabHavingURI("about:sync-tabs", true);
  6282 #endif
  6284 /**
  6285  * Format a URL
  6286  * eg:
  6287  * echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
  6288  * > https://addons.mozilla.org/en-US/firefox/3.0a1/
  6290  * Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
  6291  */
  6292 function formatURL(aFormat, aIsPref) {
  6293   var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
  6294   return aIsPref ? formatter.formatURLPref(aFormat) : formatter.formatURL(aFormat);
  6297 /**
  6298  * Utility object to handle manipulations of the identity indicators in the UI
  6299  */
  6300 var gIdentityHandler = {
  6301   // Mode strings used to control CSS display
  6302   IDENTITY_MODE_IDENTIFIED                             : "verifiedIdentity", // High-quality identity information
  6303   IDENTITY_MODE_DOMAIN_VERIFIED                        : "verifiedDomain",   // Minimal SSL CA-signed domain verification
  6304   IDENTITY_MODE_UNKNOWN                                : "unknownIdentity",  // No trusted identity information
  6305   IDENTITY_MODE_MIXED_DISPLAY_LOADED                   : "unknownIdentity mixedContent mixedDisplayContent",  // SSL with unauthenticated display content
  6306   IDENTITY_MODE_MIXED_ACTIVE_LOADED                    : "unknownIdentity mixedContent mixedActiveContent",  // SSL with unauthenticated active (and perhaps also display) content
  6307   IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED    : "unknownIdentity mixedContent mixedDisplayContentLoadedActiveBlocked",  // SSL with unauthenticated display content; unauthenticated active content is blocked.
  6308   IDENTITY_MODE_CHROMEUI                               : "chromeUI",         // Part of the product's UI
  6310   // Cache the most recent SSLStatus and Location seen in checkIdentity
  6311   _lastStatus : null,
  6312   _lastUri : null,
  6313   _mode : "unknownIdentity",
  6315   // smart getters
  6316   get _encryptionLabel () {
  6317     delete this._encryptionLabel;
  6318     this._encryptionLabel = {};
  6319     this._encryptionLabel[this.IDENTITY_MODE_DOMAIN_VERIFIED] =
  6320       gNavigatorBundle.getString("identity.encrypted2");
  6321     this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
  6322       gNavigatorBundle.getString("identity.encrypted2");
  6323     this._encryptionLabel[this.IDENTITY_MODE_UNKNOWN] =
  6324       gNavigatorBundle.getString("identity.unencrypted");
  6325     this._encryptionLabel[this.IDENTITY_MODE_MIXED_DISPLAY_LOADED] =
  6326       gNavigatorBundle.getString("identity.mixed_display_loaded");
  6327     this._encryptionLabel[this.IDENTITY_MODE_MIXED_ACTIVE_LOADED] =
  6328       gNavigatorBundle.getString("identity.mixed_active_loaded2");
  6329     this._encryptionLabel[this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED] =
  6330       gNavigatorBundle.getString("identity.mixed_display_loaded");
  6331     return this._encryptionLabel;
  6332   },
  6333   get _identityPopup () {
  6334     delete this._identityPopup;
  6335     return this._identityPopup = document.getElementById("identity-popup");
  6336   },
  6337   get _identityBox () {
  6338     delete this._identityBox;
  6339     return this._identityBox = document.getElementById("identity-box");
  6340   },
  6341   get _identityPopupContentBox () {
  6342     delete this._identityPopupContentBox;
  6343     return this._identityPopupContentBox =
  6344       document.getElementById("identity-popup-content-box");
  6345   },
  6346   get _identityPopupChromeLabel () {
  6347     delete this._identityPopupChromeLabel;
  6348     return this._identityPopupChromeLabel =
  6349       document.getElementById("identity-popup-chromeLabel");
  6350   },
  6351   get _identityPopupContentHost () {
  6352     delete this._identityPopupContentHost;
  6353     return this._identityPopupContentHost =
  6354       document.getElementById("identity-popup-content-host");
  6355   },
  6356   get _identityPopupContentOwner () {
  6357     delete this._identityPopupContentOwner;
  6358     return this._identityPopupContentOwner =
  6359       document.getElementById("identity-popup-content-owner");
  6360   },
  6361   get _identityPopupContentSupp () {
  6362     delete this._identityPopupContentSupp;
  6363     return this._identityPopupContentSupp =
  6364       document.getElementById("identity-popup-content-supplemental");
  6365   },
  6366   get _identityPopupContentVerif () {
  6367     delete this._identityPopupContentVerif;
  6368     return this._identityPopupContentVerif =
  6369       document.getElementById("identity-popup-content-verifier");
  6370   },
  6371   get _identityPopupEncLabel () {
  6372     delete this._identityPopupEncLabel;
  6373     return this._identityPopupEncLabel =
  6374       document.getElementById("identity-popup-encryption-label");
  6375   },
  6376   get _identityIconLabel () {
  6377     delete this._identityIconLabel;
  6378     return this._identityIconLabel = document.getElementById("identity-icon-label");
  6379   },
  6380   get _overrideService () {
  6381     delete this._overrideService;
  6382     return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
  6383                                      .getService(Ci.nsICertOverrideService);
  6384   },
  6385   get _identityIconCountryLabel () {
  6386     delete this._identityIconCountryLabel;
  6387     return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
  6388   },
  6389   get _identityIcon () {
  6390     delete this._identityIcon;
  6391     return this._identityIcon = document.getElementById("page-proxy-favicon");
  6392   },
  6393   get _permissionsContainer () {
  6394     delete this._permissionsContainer;
  6395     return this._permissionsContainer = document.getElementById("identity-popup-permissions");
  6396   },
  6397   get _permissionList () {
  6398     delete this._permissionList;
  6399     return this._permissionList = document.getElementById("identity-popup-permission-list");
  6400   },
  6402   /**
  6403    * Rebuild cache of the elements that may or may not exist depending
  6404    * on whether there's a location bar.
  6405    */
  6406   _cacheElements : function() {
  6407     delete this._identityBox;
  6408     delete this._identityIconLabel;
  6409     delete this._identityIconCountryLabel;
  6410     delete this._identityIcon;
  6411     delete this._permissionsContainer;
  6412     delete this._permissionList;
  6413     this._identityBox = document.getElementById("identity-box");
  6414     this._identityIconLabel = document.getElementById("identity-icon-label");
  6415     this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
  6416     this._identityIcon = document.getElementById("page-proxy-favicon");
  6417     this._permissionsContainer = document.getElementById("identity-popup-permissions");
  6418     this._permissionList = document.getElementById("identity-popup-permission-list");
  6419   },
  6421   /**
  6422    * Handler for commands on the help button in the "identity-popup" panel.
  6423    */
  6424   handleHelpCommand : function(event) {
  6425     openHelpLink("secure-connection");
  6426     this._identityPopup.hidePopup();
  6427   },
  6429   /**
  6430    * Handler for mouseclicks on the "More Information" button in the
  6431    * "identity-popup" panel.
  6432    */
  6433   handleMoreInfoClick : function(event) {
  6434     displaySecurityInfo();
  6435     event.stopPropagation();
  6436     this._identityPopup.hidePopup();
  6437   },
  6439   /**
  6440    * Helper to parse out the important parts of _lastStatus (of the SSL cert in
  6441    * particular) for use in constructing identity UI strings
  6442   */
  6443   getIdentityData : function() {
  6444     var result = {};
  6445     var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
  6446     var cert = status.serverCert;
  6448     // Human readable name of Subject
  6449     result.subjectOrg = cert.organization;
  6451     // SubjectName fields, broken up for individual access
  6452     if (cert.subjectName) {
  6453       result.subjectNameFields = {};
  6454       cert.subjectName.split(",").forEach(function(v) {
  6455         var field = v.split("=");
  6456         this[field[0]] = field[1];
  6457       }, result.subjectNameFields);
  6459       // Call out city, state, and country specifically
  6460       result.city = result.subjectNameFields.L;
  6461       result.state = result.subjectNameFields.ST;
  6462       result.country = result.subjectNameFields.C;
  6465     // Human readable name of Certificate Authority
  6466     result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
  6467     result.cert = cert;
  6469     return result;
  6470   },
  6472   /**
  6473    * Determine the identity of the page being displayed by examining its SSL cert
  6474    * (if available) and, if necessary, update the UI to reflect this.  Intended to
  6475    * be called by onSecurityChange
  6477    * @param PRUint32 state
  6478    * @param nsIURI uri The address for which the UI should be updated.
  6479    */
  6480   checkIdentity : function(state, uri) {
  6481     var currentStatus = gBrowser.securityUI
  6482                                 .QueryInterface(Components.interfaces.nsISSLStatusProvider)
  6483                                 .SSLStatus;
  6484     this._lastStatus = currentStatus;
  6485     this._lastUri = uri;
  6487     let nsIWebProgressListener = Ci.nsIWebProgressListener;
  6489     // For some URIs like data: we can't get a host and so can't do
  6490     // anything useful here.
  6491     let unknown = false;
  6492     try {
  6493       uri.host;
  6494     } catch (e) { unknown = true; }
  6496     // Chrome URIs however get special treatment. Some chrome URIs are
  6497     // whitelisted to provide a positive security signal to the user.
  6498     let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|healthreport|home|newaddon|permissions|preferences|privatebrowsing|sessionrestore|support|welcomeback)/i;
  6499     let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.spec);
  6500     if (isChromeUI) {
  6501       this.setMode(this.IDENTITY_MODE_CHROMEUI);
  6502     } else if (unknown) {
  6503       this.setMode(this.IDENTITY_MODE_UNKNOWN);
  6504     } else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
  6505       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
  6506     } else if (state & nsIWebProgressListener.STATE_IS_SECURE) {
  6507       this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
  6508     } else if (state & nsIWebProgressListener.STATE_IS_BROKEN) {
  6509       if ((state & nsIWebProgressListener.STATE_LOADED_MIXED_ACTIVE_CONTENT) &&
  6510           gPrefService.getBoolPref("security.mixed_content.block_active_content")) {
  6511         this.setMode(this.IDENTITY_MODE_MIXED_ACTIVE_LOADED);
  6512       } else if ((state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT) &&
  6513                 gPrefService.getBoolPref("security.mixed_content.block_active_content")) {
  6514         this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED_ACTIVE_BLOCKED);
  6515       } else {
  6516         this.setMode(this.IDENTITY_MODE_MIXED_DISPLAY_LOADED);
  6518     } else {
  6519       this.setMode(this.IDENTITY_MODE_UNKNOWN);
  6522     // Ensure the doorhanger is shown when mixed active content is blocked.
  6523     if (state & nsIWebProgressListener.STATE_BLOCKED_MIXED_ACTIVE_CONTENT)
  6524       this.showMixedContentDoorhanger();
  6525   },
  6527   /**
  6528    * Display the Mixed Content Blocker doohanger, providing an option
  6529    * to the user to override mixed content blocking
  6530    */
  6531   showMixedContentDoorhanger : function() {
  6532     // If we've already got an active notification, bail out to avoid showing it repeatedly.
  6533     if (PopupNotifications.getNotification("mixed-content-blocked", gBrowser.selectedBrowser))
  6534       return;
  6536     let brandBundle = document.getElementById("bundle_brand");
  6537     let brandShortName = brandBundle.getString("brandShortName");
  6538     let messageString = gNavigatorBundle.getFormattedString("mixedContentBlocked.message", [brandShortName]);
  6539     let action = {
  6540       label: gNavigatorBundle.getString("mixedContentBlocked.keepBlockingButton.label"),
  6541       accessKey: gNavigatorBundle.getString("mixedContentBlocked.keepBlockingButton.accesskey"),
  6542       callback: function() { /* NOP */ }
  6543     };
  6544     let secondaryActions = [
  6546         label: gNavigatorBundle.getString("mixedContentBlocked.unblock.label"),
  6547         accessKey: gNavigatorBundle.getString("mixedContentBlocked.unblock.accesskey"),
  6548         callback: function() {
  6549           // Use telemetry to measure how often unblocking happens
  6550           const kMIXED_CONTENT_UNBLOCK_EVENT = 2;
  6551           let histogram =
  6552             Services.telemetry.getHistogramById("MIXED_CONTENT_UNBLOCK_COUNTER");
  6553           histogram.add(kMIXED_CONTENT_UNBLOCK_EVENT);
  6554           // Reload the page with the content unblocked
  6555           BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT);
  6558     ];
  6559     let options = {
  6560       dismissed: true,
  6561       learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "mixed-content",
  6562     };
  6563     PopupNotifications.show(gBrowser.selectedBrowser, "mixed-content-blocked",
  6564                             messageString, "mixed-content-blocked-notification-icon",
  6565                             action, secondaryActions, options);
  6566   },
  6568   /**
  6569    * Return the eTLD+1 version of the current hostname
  6570    */
  6571   getEffectiveHost : function() {
  6572     if (!this._IDNService)
  6573       this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
  6574                          .getService(Ci.nsIIDNService);
  6575     try {
  6576       let baseDomain =
  6577         Services.eTLD.getBaseDomainFromHost(this._lastUri.host);
  6578       return this._IDNService.convertToDisplayIDN(baseDomain, {});
  6579     } catch (e) {
  6580       // If something goes wrong (e.g. host is an IP address) just fail back
  6581       // to the full domain.
  6582       return this._lastUri.host;
  6584   },
  6586   /**
  6587    * Update the UI to reflect the specified mode, which should be one of the
  6588    * IDENTITY_MODE_* constants.
  6589    */
  6590   setMode : function(newMode) {
  6591     if (!this._identityBox) {
  6592       // No identity box means the identity box is not visible, in which
  6593       // case there's nothing to do.
  6594       return;
  6597     this._identityPopup.className = newMode;
  6598     this._identityBox.className = newMode;
  6599     this.setIdentityMessages(newMode);
  6601     // Update the popup too, if it's open
  6602     if (this._identityPopup.state == "open")
  6603       this.setPopupMessages(newMode);
  6605     this._mode = newMode;
  6606   },
  6608   /**
  6609    * Set up the messages for the primary identity UI based on the specified mode,
  6610    * and the details of the SSL cert, where applicable
  6612    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
  6613    */
  6614   setIdentityMessages : function(newMode) {
  6615     let icon_label = "";
  6616     let tooltip = "";
  6617     let icon_country_label = "";
  6618     let icon_labels_dir = "ltr";
  6620     switch (newMode) {
  6621     case this.IDENTITY_MODE_DOMAIN_VERIFIED: {
  6622       let iData = this.getIdentityData();
  6624       // Verifier is either the CA Org, for a normal cert, or a special string
  6625       // for certs that are trusted because of a security exception.
  6626       tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
  6627                                                     [iData.caOrg]);
  6629       // This can't throw, because URI's with a host that throw don't end up in this case.
  6630       let host = this._lastUri.host;
  6631       let port = 443;
  6632       try {
  6633         if (this._lastUri.port > 0)
  6634           port = this._lastUri.port;
  6635       } catch (e) {}
  6637       if (this._overrideService.hasMatchingOverride(host, port, iData.cert, {}, {}))
  6638         tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
  6640       break; }
  6641     case this.IDENTITY_MODE_IDENTIFIED: {
  6642       // If it's identified, then we can populate the dialog with credentials
  6643       let iData = this.getIdentityData();
  6644       tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
  6645                                                     [iData.caOrg]);
  6646       icon_label = iData.subjectOrg;
  6647       if (iData.country)
  6648         icon_country_label = "(" + iData.country + ")";
  6650       // If the organization name starts with an RTL character, then
  6651       // swap the positions of the organization and country code labels.
  6652       // The Unicode ranges reflect the definition of the UCS2_CHAR_IS_BIDI
  6653       // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
  6654       // fixed, this test should be replaced by one adhering to the
  6655       // Unicode Bidirectional Algorithm proper (at the paragraph level).
  6656       icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/.test(icon_label) ?
  6657                         "rtl" : "ltr";
  6658       break; }
  6659     case this.IDENTITY_MODE_CHROMEUI:
  6660       let brandBundle = document.getElementById("bundle_brand");
  6661       icon_label = brandBundle.getString("brandShortName");
  6662       break;
  6663     default:
  6664       tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
  6667     // Push the appropriate strings out to the UI
  6668     this._identityBox.tooltipText = tooltip;
  6669     this._identityIconLabel.value = icon_label;
  6670     this._identityIconCountryLabel.value = icon_country_label;
  6671     // Set cropping and direction
  6672     this._identityIconLabel.crop = icon_country_label ? "end" : "center";
  6673     this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
  6674     // Hide completely if the organization label is empty
  6675     this._identityIconLabel.parentNode.collapsed = icon_label ? false : true;
  6676   },
  6678   /**
  6679    * Set up the title and content messages for the identity message popup,
  6680    * based on the specified mode, and the details of the SSL cert, where
  6681    * applicable
  6683    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
  6684    */
  6685   setPopupMessages : function(newMode) {
  6687     this._identityPopup.className = newMode;
  6688     this._identityPopupContentBox.className = newMode;
  6690     // Set the static strings up front
  6691     this._identityPopupEncLabel.textContent = this._encryptionLabel[newMode];
  6693     // Initialize the optional strings to empty values
  6694     let supplemental = "";
  6695     let verifier = "";
  6696     let host = "";
  6697     let owner = "";
  6699     switch (newMode) {
  6700     case this.IDENTITY_MODE_DOMAIN_VERIFIED:
  6701       host = this.getEffectiveHost();
  6702       owner = gNavigatorBundle.getString("identity.ownerUnknown2");
  6703       verifier = this._identityBox.tooltipText;
  6704       break;
  6705     case this.IDENTITY_MODE_IDENTIFIED: {
  6706       // If it's identified, then we can populate the dialog with credentials
  6707       let iData = this.getIdentityData();
  6708       host = this.getEffectiveHost();
  6709       owner = iData.subjectOrg;
  6710       verifier = this._identityBox.tooltipText;
  6712       // Build an appropriate supplemental block out of whatever location data we have
  6713       if (iData.city)
  6714         supplemental += iData.city + "\n";
  6715       if (iData.state && iData.country)
  6716         supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
  6717                                                             [iData.state, iData.country]);
  6718       else if (iData.state) // State only
  6719         supplemental += iData.state;
  6720       else if (iData.country) // Country only
  6721         supplemental += iData.country;
  6722       break; }
  6723     case this.IDENTITY_MODE_CHROMEUI: {
  6724       let brandBundle = document.getElementById("bundle_brand");
  6725       let brandShortName = brandBundle.getString("brandShortName");
  6726       this._identityPopupChromeLabel.textContent = gNavigatorBundle.getFormattedString("identity.chrome",
  6727                                                                                        [brandShortName]);
  6728       break; }
  6731     // Push the appropriate strings out to the UI
  6732     this._identityPopupContentHost.textContent = host;
  6733     this._identityPopupContentOwner.textContent = owner;
  6734     this._identityPopupContentSupp.textContent = supplemental;
  6735     this._identityPopupContentVerif.textContent = verifier;
  6736   },
  6738   /**
  6739    * Click handler for the identity-box element in primary chrome.
  6740    */
  6741   handleIdentityButtonEvent : function(event) {
  6742     event.stopPropagation();
  6744     if ((event.type == "click" && event.button != 0) ||
  6745         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
  6746          event.keyCode != KeyEvent.DOM_VK_RETURN)) {
  6747       return; // Left click, space or enter only
  6750     // Don't allow left click, space or enter if the location has been modified.
  6751     if (gURLBar.getAttribute("pageproxystate") != "valid") {
  6752       return;
  6755     // Make sure that the display:none style we set in xul is removed now that
  6756     // the popup is actually needed
  6757     this._identityPopup.hidden = false;
  6759     // Update the popup strings
  6760     this.setPopupMessages(this._identityBox.className);
  6762     this.updateSitePermissions();
  6764     // Add the "open" attribute to the identity box for styling
  6765     this._identityBox.setAttribute("open", "true");
  6766     var self = this;
  6767     this._identityPopup.addEventListener("popuphidden", function onPopupHidden(e) {
  6768       e.currentTarget.removeEventListener("popuphidden", onPopupHidden, false);
  6769       self._identityBox.removeAttribute("open");
  6770     }, false);
  6772     // Now open the popup, anchored off the primary chrome element
  6773     this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
  6774   },
  6776   onPopupShown : function(event) {
  6777     document.getElementById('identity-popup-more-info-button').focus();
  6779     this._identityPopup.addEventListener("blur", this, true);
  6780     this._identityPopup.addEventListener("popuphidden", this);
  6781   },
  6783   onDragStart: function (event) {
  6784     if (gURLBar.getAttribute("pageproxystate") != "valid")
  6785       return;
  6787     var value = content.location.href;
  6788     var urlString = value + "\n" + content.document.title;
  6789     var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
  6791     var dt = event.dataTransfer;
  6792     dt.setData("text/x-moz-url", urlString);
  6793     dt.setData("text/uri-list", value);
  6794     dt.setData("text/plain", value);
  6795     dt.setData("text/html", htmlString);
  6796     dt.setDragImage(gProxyFavIcon, 16, 16);
  6797   },
  6799   handleEvent: function (event) {
  6800     switch (event.type) {
  6801       case "blur":
  6802         // Focus hasn't moved yet, need to wait until after the blur event.
  6803         setTimeout(() => {
  6804           if (document.activeElement &&
  6805               document.activeElement.compareDocumentPosition(this._identityPopup) &
  6806                 Node.DOCUMENT_POSITION_CONTAINS)
  6807             return;
  6809           this._identityPopup.hidePopup();
  6810         }, 0);
  6811         break;
  6812       case "popuphidden":
  6813         this._identityPopup.removeEventListener("blur", this, true);
  6814         this._identityPopup.removeEventListener("popuphidden", this);
  6815         break;
  6817   },
  6819   updateSitePermissions: function () {
  6820     while (this._permissionList.hasChildNodes())
  6821       this._permissionList.removeChild(this._permissionList.lastChild);
  6823     let uri = gBrowser.currentURI;
  6825     for (let permission of SitePermissions.listPermissions()) {
  6826       let state = SitePermissions.get(uri, permission);
  6828       if (state == SitePermissions.UNKNOWN)
  6829         continue;
  6831       let item = this._createPermissionItem(permission, state);
  6832       this._permissionList.appendChild(item);
  6835     this._permissionsContainer.hidden = !this._permissionList.hasChildNodes();
  6836   },
  6838   setPermission: function (aPermission, aState) {
  6839     if (aState == SitePermissions.getDefault(aPermission))
  6840       SitePermissions.remove(gBrowser.currentURI, aPermission);
  6841     else
  6842       SitePermissions.set(gBrowser.currentURI, aPermission, aState);
  6843   },
  6845   _createPermissionItem: function (aPermission, aState) {
  6846     let menulist = document.createElement("menulist");
  6847     let menupopup = document.createElement("menupopup");
  6848     for (let state of SitePermissions.getAvailableStates(aPermission)) {
  6849       let menuitem = document.createElement("menuitem");
  6850       menuitem.setAttribute("value", state);
  6851       menuitem.setAttribute("label", SitePermissions.getStateLabel(aPermission, state));
  6852       menupopup.appendChild(menuitem);
  6854     menulist.appendChild(menupopup);
  6855     menulist.setAttribute("value", aState);
  6856     menulist.setAttribute("oncommand", "gIdentityHandler.setPermission('" +
  6857                                        aPermission + "', this.value)");
  6858     menulist.setAttribute("id", "identity-popup-permission:" + aPermission);
  6860     let label = document.createElement("label");
  6861     label.setAttribute("flex", "1");
  6862     label.setAttribute("control", menulist.getAttribute("id"));
  6863     label.setAttribute("value", SitePermissions.getPermissionLabel(aPermission));
  6865     let container = document.createElement("hbox");
  6866     container.setAttribute("align", "center");
  6867     container.appendChild(label);
  6868     container.appendChild(menulist);
  6869     return container;
  6871 };
  6873 function getNotificationBox(aWindow) {
  6874   var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
  6875   if (foundBrowser)
  6876     return gBrowser.getNotificationBox(foundBrowser)
  6877   return null;
  6878 };
  6880 function getTabModalPromptBox(aWindow) {
  6881   var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
  6882   if (foundBrowser)
  6883     return gBrowser.getTabModalPromptBox(foundBrowser);
  6884   return null;
  6885 };
  6887 /* DEPRECATED */
  6888 function getBrowser() gBrowser;
  6889 function getNavToolbox() gNavToolbox;
  6891 let gPrivateBrowsingUI = {
  6892   init: function PBUI_init() {
  6893     // Do nothing for normal windows
  6894     if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
  6895       return;
  6898     // Disable the Clear Recent History... menu item when in PB mode
  6899     // temporary fix until bug 463607 is fixed
  6900     document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
  6902     if (window.location.href == getBrowserURL()) {
  6903       // Adjust the window's title
  6904       let docElement = document.documentElement;
  6905       if (!PrivateBrowsingUtils.permanentPrivateBrowsing) {
  6906         docElement.setAttribute("title",
  6907           docElement.getAttribute("title_privatebrowsing"));
  6908         docElement.setAttribute("titlemodifier",
  6909           docElement.getAttribute("titlemodifier_privatebrowsing"));
  6911       docElement.setAttribute("privatebrowsingmode",
  6912         PrivateBrowsingUtils.permanentPrivateBrowsing ? "permanent" : "temporary");
  6913       gBrowser.updateTitlebar();
  6915       if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
  6916         // Adjust the New Window menu entries
  6918           { normal: "menu_newNavigator", private: "menu_newPrivateWindow" },
  6919         ].forEach(function(menu) {
  6920           let newWindow = document.getElementById(menu.normal);
  6921           let newPrivateWindow = document.getElementById(menu.private);
  6922           if (newWindow && newPrivateWindow) {
  6923             newPrivateWindow.hidden = true;
  6924             newWindow.label = newPrivateWindow.label;
  6925             newWindow.accessKey = newPrivateWindow.accessKey;
  6926             newWindow.command = newPrivateWindow.command;
  6928         });
  6932     if (gURLBar &&
  6933         !PrivateBrowsingUtils.permanentPrivateBrowsing) {
  6934       // Disable switch to tab autocompletion for private windows 
  6935       // (not for "Always use private browsing" mode)
  6936       gURLBar.setAttribute("autocompletesearchparam", "");
  6939 };
  6941 let gRemoteTabsUI = {
  6942   init: function() {
  6943     if (window.location.href != getBrowserURL()) {
  6944       return;
  6947     let remoteTabs = gPrefService.getBoolPref("browser.tabs.remote");
  6948     let autostart = gPrefService.getBoolPref("browser.tabs.remote.autostart");
  6950     let newRemoteWindow = document.getElementById("menu_newRemoteWindow");
  6951     let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
  6953     if (!remoteTabs) {
  6954       newRemoteWindow.hidden = true;
  6955       newNonRemoteWindow.hidden = true;
  6956       return;
  6959     newRemoteWindow.hidden = autostart;
  6960     newNonRemoteWindow.hidden = !autostart;
  6962 };
  6964 /**
  6965  * Switch to a tab that has a given URI, and focusses its browser window.
  6966  * If a matching tab is in this window, it will be switched to. Otherwise, other
  6967  * windows will be searched.
  6969  * @param aURI
  6970  *        URI to search for
  6971  * @param aOpenNew
  6972  *        True to open a new tab and switch to it, if no existing tab is found.
  6973  *        If no suitable window is found, a new one will be opened.
  6974  * @param aOpenParams
  6975  *        If switching to this URI results in us opening a tab, aOpenParams
  6976  *        will be the parameter object that gets passed to openUILinkIn. Please
  6977  *        see the documentation for openUILinkIn to see what parameters can be
  6978  *        passed via this object.
  6979  * @return True if an existing tab was found, false otherwise
  6980  */
  6981 function switchToTabHavingURI(aURI, aOpenNew, aOpenParams) {
  6982   // Certain URLs can be switched to irrespective of the source or destination
  6983   // window being in private browsing mode:
  6984   const kPrivateBrowsingWhitelist = new Set([
  6985     "about:customizing",
  6986   ]);
  6987   // This will switch to the tab in aWindow having aURI, if present.
  6988   function switchIfURIInWindow(aWindow) {
  6989     // Only switch to the tab if neither the source nor the destination window
  6990     // are private and they are not in permanent private browsing mode
  6991     if (!kPrivateBrowsingWhitelist.has(aURI.spec) &&
  6992         (PrivateBrowsingUtils.isWindowPrivate(window) ||
  6993          PrivateBrowsingUtils.isWindowPrivate(aWindow)) &&
  6994         !PrivateBrowsingUtils.permanentPrivateBrowsing) {
  6995       return false;
  6998     let browsers = aWindow.gBrowser.browsers;
  6999     for (let i = 0; i < browsers.length; i++) {
  7000       let browser = browsers[i];
  7001       if (browser.currentURI.equals(aURI)) {
  7002         // Focus the matching window & tab
  7003         aWindow.focus();
  7004         aWindow.gBrowser.tabContainer.selectedIndex = i;
  7005         return true;
  7008     return false;
  7011   // This can be passed either nsIURI or a string.
  7012   if (!(aURI instanceof Ci.nsIURI))
  7013     aURI = Services.io.newURI(aURI, null, null);
  7015   let isBrowserWindow = !!window.gBrowser;
  7017   // Prioritise this window.
  7018   if (isBrowserWindow && switchIfURIInWindow(window))
  7019     return true;
  7021   let winEnum = Services.wm.getEnumerator("navigator:browser");
  7022   while (winEnum.hasMoreElements()) {
  7023     let browserWin = winEnum.getNext();
  7024     // Skip closed (but not yet destroyed) windows,
  7025     // and the current window (which was checked earlier).
  7026     if (browserWin.closed || browserWin == window)
  7027       continue;
  7028     if (switchIfURIInWindow(browserWin))
  7029       return true;
  7032   // No opened tab has that url.
  7033   if (aOpenNew) {
  7034     if (isBrowserWindow && isTabEmpty(gBrowser.selectedTab))
  7035       openUILinkIn(aURI.spec, "current", aOpenParams);
  7036     else
  7037       openUILinkIn(aURI.spec, "tab", aOpenParams);
  7040   return false;
  7043 let RestoreLastSessionObserver = {
  7044   init: function () {
  7045     if (SessionStore.canRestoreLastSession &&
  7046         !PrivateBrowsingUtils.isWindowPrivate(window)) {
  7047       Services.obs.addObserver(this, "sessionstore-last-session-cleared", true);
  7048       goSetCommandEnabled("Browser:RestoreLastSession", true);
  7050   },
  7052   observe: function () {
  7053     // The last session can only be restored once so there's
  7054     // no way we need to re-enable our menu item.
  7055     Services.obs.removeObserver(this, "sessionstore-last-session-cleared");
  7056     goSetCommandEnabled("Browser:RestoreLastSession", false);
  7057   },
  7059   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  7060                                          Ci.nsISupportsWeakReference])
  7061 };
  7063 function restoreLastSession() {
  7064   SessionStore.restoreLastSession();
  7067 var TabContextMenu = {
  7068   contextTab: null,
  7069   updateContextMenu: function updateContextMenu(aPopupMenu) {
  7070     this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
  7071                       aPopupMenu.triggerNode : gBrowser.selectedTab;
  7072     let disabled = gBrowser.tabs.length == 1;
  7074     var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
  7075     for (let menuItem of menuItems)
  7076       menuItem.disabled = disabled;
  7078     disabled = gBrowser.visibleTabs.length == 1;
  7079     menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
  7080     for (let menuItem of menuItems)
  7081       menuItem.disabled = disabled;
  7083     // Session store
  7084     document.getElementById("context_undoCloseTab").disabled =
  7085       SessionStore.getClosedTabCount(window) == 0;
  7087     // Only one of pin/unpin should be visible
  7088     document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
  7089     document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;
  7091     // Disable "Close Tabs to the Right" if there are no tabs
  7092     // following it and hide it when the user rightclicked on a pinned
  7093     // tab.
  7094     document.getElementById("context_closeTabsToTheEnd").disabled =
  7095       gBrowser.getTabsToTheEndFrom(this.contextTab).length == 0;
  7096     document.getElementById("context_closeTabsToTheEnd").hidden = this.contextTab.pinned;
  7098     // Disable "Close other Tabs" if there is only one unpinned tab and
  7099     // hide it when the user rightclicked on a pinned tab.
  7100     let unpinnedTabs = gBrowser.visibleTabs.length - gBrowser._numPinnedTabs;
  7101     document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1;
  7102     document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned;
  7104     // Hide "Bookmark All Tabs" for a pinned tab.  Update its state if visible.
  7105     let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
  7106     bookmarkAllTabs.hidden = this.contextTab.pinned;
  7107     if (!bookmarkAllTabs.hidden)
  7108       PlacesCommandHook.updateBookmarkAllTabsCommand();
  7110     // Hide "Move to Group" if it's a pinned tab.
  7111     document.getElementById("context_tabViewMenu").hidden =
  7112       (this.contextTab.pinned || !TabView.firstUseExperienced);
  7114 };
  7116 XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
  7117                                   "resource:///modules/devtools/gDevTools.jsm");
  7119 XPCOMUtils.defineLazyModuleGetter(this, "gDevToolsBrowser",
  7120                                   "resource:///modules/devtools/gDevTools.jsm");
  7122 Object.defineProperty(this, "HUDService", {
  7123   get: function HUDService_getter() {
  7124     let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
  7125     return devtools.require("devtools/webconsole/hudservice");
  7126   },
  7127   configurable: true,
  7128   enumerable: true
  7129 });
  7131 // Prompt user to restart the browser in safe mode
  7132 function safeModeRestart()
  7134   // prompt the user to confirm
  7135   let promptTitle = gNavigatorBundle.getString("safeModeRestartPromptTitle");
  7136   let promptMessage =
  7137     gNavigatorBundle.getString("safeModeRestartPromptMessage");
  7138   let restartText = gNavigatorBundle.getString("safeModeRestartButton");
  7139   let buttonFlags = (Services.prompt.BUTTON_POS_0 *
  7140                      Services.prompt.BUTTON_TITLE_IS_STRING) +
  7141                     (Services.prompt.BUTTON_POS_1 *
  7142                      Services.prompt.BUTTON_TITLE_CANCEL) +
  7143                     Services.prompt.BUTTON_POS_0_DEFAULT;
  7145   let rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
  7146                                      buttonFlags, restartText, null, null,
  7147                                      null, {});
  7148   if (rv != 0)
  7149     return;
  7151   let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
  7152                      .createInstance(Ci.nsISupportsPRBool);
  7153   Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
  7155   if (!cancelQuit.data) {
  7156     Services.startup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
  7160 /* duplicateTabIn duplicates tab in a place specified by the parameter |where|.
  7162  * |where| can be:
  7163  *  "tab"         new tab
  7164  *  "tabshifted"  same as "tab" but in background if default is to select new
  7165  *                tabs, and vice versa
  7166  *  "window"      new window
  7168  * delta is the offset to the history entry that you want to load.
  7169  */
  7170 function duplicateTabIn(aTab, where, delta) {
  7171   let newTab = SessionStore.duplicateTab(window, aTab, delta);
  7173   switch (where) {
  7174     case "window":
  7175       gBrowser.hideTab(newTab);
  7176       gBrowser.replaceTabWithWindow(newTab);
  7177       break;
  7178     case "tabshifted":
  7179       // A background tab has been opened, nothing else to do here.
  7180       break;
  7181     case "tab":
  7182       gBrowser.selectedTab = newTab;
  7183       break;
  7187 var Scratchpad = {
  7188   openScratchpad: function SP_openScratchpad() {
  7189     return this.ScratchpadManager.openScratchpad();
  7191 };
  7193 XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
  7194   let tmp = {};
  7195   Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", tmp);
  7196   return tmp.ScratchpadManager;
  7197 });
  7199 var ResponsiveUI = {
  7200   toggle: function RUI_toggle() {
  7201     this.ResponsiveUIManager.toggle(window, gBrowser.selectedTab);
  7203 };
  7205 XPCOMUtils.defineLazyGetter(ResponsiveUI, "ResponsiveUIManager", function() {
  7206   let tmp = {};
  7207   Cu.import("resource:///modules/devtools/responsivedesign.jsm", tmp);
  7208   return tmp.ResponsiveUIManager;
  7209 });
  7211 function openEyedropper() {
  7212   var eyedropper = new this.Eyedropper(this);
  7213   eyedropper.open();
  7216 Object.defineProperty(this, "Eyedropper", {
  7217   get: function() {
  7218     let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
  7219     return devtools.require("devtools/eyedropper/eyedropper").Eyedropper;
  7220   },
  7221   configurable: true,
  7222   enumerable: true
  7223 });
  7225 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
  7226 #ifdef XP_WIN
  7227   // Only show resizers on Windows 2000 and XP
  7228   return parseFloat(Services.sysinfo.getProperty("version")) < 6;
  7229 #else
  7230   return false;
  7231 #endif
  7232 });
  7234 var MousePosTracker = {
  7235   _listeners: new Set(),
  7236   _x: 0,
  7237   _y: 0,
  7238   get _windowUtils() {
  7239     delete this._windowUtils;
  7240     return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
  7241   },
  7243   addListener: function (listener) {
  7244     if (this._listeners.has(listener))
  7245       return;
  7247     listener._hover = false;
  7248     this._listeners.add(listener);
  7250     this._callListener(listener);
  7251   },
  7253   removeListener: function (listener) {
  7254     this._listeners.delete(listener);
  7255   },
  7257   handleEvent: function (event) {
  7258     var fullZoom = this._windowUtils.fullZoom;
  7259     this._x = event.screenX / fullZoom - window.mozInnerScreenX;
  7260     this._y = event.screenY / fullZoom - window.mozInnerScreenY;
  7262     this._listeners.forEach(function (listener) {
  7263       try {
  7264         this._callListener(listener);
  7265       } catch (e) {
  7266         Cu.reportError(e);
  7268     }, this);
  7269   },
  7271   _callListener: function (listener) {
  7272     let rect = listener.getMouseTargetRect();
  7273     let hover = this._x >= rect.left &&
  7274                 this._x <= rect.right &&
  7275                 this._y >= rect.top &&
  7276                 this._y <= rect.bottom;
  7278     if (hover == listener._hover)
  7279       return;
  7281     listener._hover = hover;
  7283     if (hover) {
  7284       if (listener.onMouseEnter)
  7285         listener.onMouseEnter();
  7286     } else {
  7287       if (listener.onMouseLeave)
  7288         listener.onMouseLeave();
  7291 };
  7293 function focusNextFrame(event) {
  7294   let fm = Services.focus;
  7295   let dir = event.shiftKey ? fm.MOVEFOCUS_BACKWARDDOC : fm.MOVEFOCUS_FORWARDDOC;
  7296   let element = fm.moveFocus(window, null, dir, fm.FLAG_BYKEY);
  7297   if (element.ownerDocument == document)
  7298     focusAndSelectUrlBar();
  7300 let BrowserChromeTest = {
  7301   _cb: null,
  7302   _ready: false,
  7303   markAsReady: function () {
  7304     this._ready = true;
  7305     if (this._cb) {
  7306       this._cb();
  7307       this._cb = null;
  7309   },
  7310   runWhenReady: function (cb) {
  7311     if (this._ready)
  7312       cb();
  7313     else
  7314       this._cb = cb;
  7316 };
  7318 function BrowserOpenNewTabOrWindow(event) {
  7319   if (event.shiftKey) {
  7320     OpenBrowserWindow();
  7321   } else {
  7322     BrowserOpenTab();
  7326 let ToolbarIconColor = {
  7327   init: function () {
  7328     this._initialized = true;
  7330     window.addEventListener("activate", this);
  7331     window.addEventListener("deactivate", this);
  7332     Services.obs.addObserver(this, "lightweight-theme-styling-update", false);
  7334     // If the window isn't active now, we assume that it has never been active
  7335     // before and will soon become active such that inferFromText will be
  7336     // called from the initial activate event.
  7337     if (Services.focus.activeWindow == window)
  7338       this.inferFromText();
  7339   },
  7341   uninit: function () {
  7342     this._initialized = false;
  7344     window.removeEventListener("activate", this);
  7345     window.removeEventListener("deactivate", this);
  7346     Services.obs.removeObserver(this, "lightweight-theme-styling-update");
  7347   },
  7349   handleEvent: function (event) {
  7350     switch (event.type) {
  7351       case "activate":
  7352       case "deactivate":
  7353         this.inferFromText();
  7354         break;
  7356   },
  7358   observe: function (aSubject, aTopic, aData) {
  7359     switch (aTopic) {
  7360       case "lightweight-theme-styling-update":
  7361         // inferFromText needs to run after LightweightThemeConsumer.jsm's
  7362         // lightweight-theme-styling-update observer.
  7363         setTimeout(() => { this.inferFromText(); }, 0);
  7364         break;
  7366   },
  7368   inferFromText: function () {
  7369     if (!this._initialized)
  7370       return;
  7372     function parseRGB(aColorString) {
  7373       let rgb = aColorString.match(/^rgba?\((\d+), (\d+), (\d+)/);
  7374       rgb.shift();
  7375       return rgb.map(x => parseInt(x));
  7378     let toolbarSelector = "#navigator-toolbox > toolbar:not([collapsed=true]):not(#addon-bar)";
  7379 #ifdef XP_MACOSX
  7380     toolbarSelector += ":not([type=menubar])";
  7381 #endif
  7383     for (let toolbar of document.querySelectorAll(toolbarSelector)) {
  7384       let [r, g, b] = parseRGB(getComputedStyle(toolbar).color);
  7385       let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
  7386       if (luminance <= 110)
  7387         toolbar.removeAttribute("brighttext");
  7388       else
  7389         toolbar.setAttribute("brighttext", "true");

mercurial