1 # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
15 # The Original Code is mozilla.org code.
17 # The Initial Developer of the Original Code is
18 # Netscape Communications Corporation.
19 # Portions created by the Initial Developer are Copyright (C) 1998
20 # the Initial Developer. All Rights Reserved.
23 # Blake Ross <blake@cs.stanford.edu>
24 # David Hyatt <hyatt@mozilla.org>
25 # Peter Annema <disttsc@bart.nl>
26 # Dean Tessman <dean_tessman@hotmail.com>
27 # Kevin Puetz <puetzk@iastate.edu>
28 # Ben Goodger <ben@netscape.com>
29 # Pierre Chanial <chanial@noos.fr>
30 # Jason Eager <jce2@po.cwru.edu>
31 # Joe Hewitt <hewitt@netscape.com>
32 # Alec Flett <alecf@netscape.com>
33 # Asaf Romano <mozilla.mano@sent.com>
34 # Jason Barnabe <jason_barnabe@fastmail.fm>
35 # Peter Parente <parente@cs.unc.edu>
36 # Giorgio Maone <g.maone@informaction.com>
37 # Tom Germeau <tom.germeau@epigoon.com>
38 # Jesse Ruderman <jruderman@gmail.com>
39 # Joe Hughes <joe@retrovirus.com>
40 # Pamela Greene <pamg.bugs@gmail.com>
41 # Michael Ventnor <m.ventnor@gmail.com>
42 # Simon Bünzli <zeniko@gmail.com>
43 # Johnathan Nightingale <johnath@mozilla.com>
44 # Ehsan Akhgari <ehsan.akhgari@gmail.com>
45 # Dão Gottwald <dao@mozilla.com>
46 # Thomas K. Dyas <tdyas@zecador.org>
47 # Edward Lee <edward.lee@engineering.uiuc.edu>
48 # Paul O’Shannessy <paul@oshannessy.com>
49 # Nils Maier <maierman@web.de>
50 # Rob Arnold <robarnold@cmu.edu>
51 # Dietrich Ayala <dietrich@mozilla.com>
52 # Gavin Sharp <gavin@gavinsharp.com>
53 # Justin Dolske <dolske@mozilla.com>
54 # Rob Campbell <rcampbell@mozilla.com>
56 # Alternatively, the contents of this file may be used under the terms of
57 # either the GNU General Public License Version 2 or later (the "GPL"), or
58 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
59 # in which case the provisions of the GPL or the LGPL are applicable instead
60 # of those above. If you wish to allow use of your version of this file only
61 # under the terms of either the GPL or the LGPL, and not to allow others to
62 # use your version of this file under the terms of the MPL, indicate your
63 # decision by deleting the provisions above and replace them with the notice
64 # and other provisions required by the GPL or the LGPL. If you do not delete
65 # the provisions above, a recipient may use your version of this file under
66 # the terms of any one of the MPL, the GPL or the LGPL.
68 # ***** END LICENSE BLOCK *****
70 let Ci = Components.interfaces;
71 let Cu = Components.utils;
73 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
75 const nsIWebNavigation = Ci.nsIWebNavigation;
77 var gCharsetMenu = null;
78 var gLastBrowserCharset = null;
79 var gPrevCharset = null;
80 var gProxyFavIcon = null;
81 var gLastValidURLStr = "";
82 var gInPrintPreviewMode = false;
83 var gDownloadMgr = null;
84 var gContextMenu = null; // nsContextMenu instance
87 var gEditUIVisible = true;
91 ["gBrowser", "content"],
92 ["gNavToolbox", "navigator-toolbox"],
93 ["gURLBar", "urlbar"],
94 ["gNavigatorBundle", "bundle_browser"]
95 ].forEach(function (elementGlobal) {
96 var [name, id] = elementGlobal;
97 window.__defineGetter__(name, function () {
98 var element = document.getElementById(id);
102 return window[name] = element;
104 window.__defineSetter__(name, function (val) {
106 return window[name] = val;
110 // Smart getter for the findbar. If you don't wish to force the creation of
111 // the findbar, check gFindBarInitialized first.
112 var gFindBarInitialized = false;
113 XPCOMUtils.defineLazyGetter(window, "gFindBar", function() {
114 let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
115 let findbar = document.createElementNS(XULNS, "findbar");
116 findbar.setAttribute("browserid", "content");
117 findbar.id = "FindToolbar";
119 let browserBottomBox = document.getElementById("browser-bottombox");
120 browserBottomBox.insertBefore(findbar, browserBottomBox.firstChild);
122 // Force a style flush to ensure that our binding is attached.
124 window.gFindBarInitialized = true;
128 __defineGetter__("gPrefService", function() {
129 delete this.gPrefService;
130 return this.gPrefService = Services.prefs;
133 __defineGetter__("AddonManager", function() {
134 Cu.import("resource://gre/modules/AddonManager.jsm");
135 return this.AddonManager;
137 __defineSetter__("AddonManager", function (val) {
138 delete this.AddonManager;
139 return this.AddonManager = val;
142 __defineGetter__("PluralForm", function() {
143 Cu.import("resource://gre/modules/PluralForm.jsm");
144 return this.PluralForm;
146 __defineSetter__("PluralForm", function (val) {
147 delete this.PluralForm;
148 return this.PluralForm = val;
151 #ifdef MOZ_SERVICES_SYNC
152 XPCOMUtils.defineLazyGetter(this, "Weave", function() {
154 Cu.import("resource://services-sync/service.js", tmp);
159 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
161 Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
162 return new tmp.PopupNotifications(gBrowser,
163 document.getElementById("notification-popup"),
164 document.getElementById("notification-popup-box"));
167 let gInitialPages = [
169 "about:privatebrowsing",
170 "about:sessionrestore"
173 #include browser-fullZoom.js
174 #include inspector.js
175 #include browser-places.js
176 #include browser-tabPreviews.js
178 #ifdef MOZ_SERVICES_SYNC
179 #include browser-syncui.js
182 XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
185 const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
186 if (WINTASKBAR_CONTRACTID in Cc &&
187 Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
189 Cu.import("resource://gre/modules/WindowsPreviewPerTab.jsm", temp);
190 let AeroPeek = temp.AeroPeek;
192 onOpenWindow: function () {
193 AeroPeek.onOpenWindow(window);
195 onCloseWindow: function () {
196 AeroPeek.onCloseWindow(window);
205 #ifdef MOZ_CRASHREPORTER
206 XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
207 "@mozilla.org/xre/app-info;1",
212 * We can avoid adding multiple load event listeners and save some time by adding
213 * one listener that calls all real handlers.
215 function pageShowEventHandlers(event) {
216 // Filter out events that are not about the document load we are interested in
217 if (event.originalTarget == content.document) {
218 charsetLoadListener(event);
219 XULBrowserWindow.asyncUpdateUI();
223 function UpdateBackForwardCommands(aWebNavigation) {
224 var backBroadcaster = document.getElementById("Browser:Back");
225 var forwardBroadcaster = document.getElementById("Browser:Forward");
227 // Avoid setting attributes on broadcasters if the value hasn't changed!
228 // Remember, guys, setting attributes on elements is expensive! They
229 // get inherited into anonymous content, broadcast to other widgets, etc.!
230 // Don't do it if the value hasn't changed! - dwh
232 var backDisabled = backBroadcaster.hasAttribute("disabled");
233 var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
234 if (backDisabled == aWebNavigation.canGoBack) {
236 backBroadcaster.removeAttribute("disabled");
238 backBroadcaster.setAttribute("disabled", true);
241 if (forwardDisabled == aWebNavigation.canGoForward) {
243 forwardBroadcaster.removeAttribute("disabled");
245 forwardBroadcaster.setAttribute("disabled", true);
251 * Click-and-Hold implementation for the Back and Forward buttons
252 * XXXmano: should this live in toolbarbutton.xml?
254 function SetClickAndHoldHandlers() {
257 function timerCallback(aButton) {
258 aButton.firstChild.hidden = false;
263 function mousedownHandler(aEvent) {
264 if (aEvent.button != 0 ||
265 aEvent.currentTarget.open ||
266 aEvent.currentTarget.disabled)
269 // Prevent the menupopup from opening immediately
270 aEvent.currentTarget.firstChild.hidden = true;
272 timer = setTimeout(timerCallback, 500, aEvent.currentTarget);
275 function clickHandler(aEvent) {
276 if (aEvent.button == 0 &&
277 aEvent.target == aEvent.currentTarget &&
278 !aEvent.currentTarget.open &&
279 !aEvent.currentTarget.disabled) {
280 let cmdEvent = document.createEvent("xulcommandevent");
281 cmdEvent.initCommandEvent("command", true, true, window, 0,
282 aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
283 aEvent.metaKey, null);
284 aEvent.currentTarget.dispatchEvent(cmdEvent);
288 function stopTimer(aEvent) {
295 function _addClickAndHoldListenersOnElement(aElm) {
296 aElm.addEventListener("mousedown", mousedownHandler, true);
297 aElm.addEventListener("mouseup", stopTimer, false);
298 aElm.addEventListener("mouseout", stopTimer, false);
299 aElm.addEventListener("click", clickHandler, true);
302 // Bug 414797: Clone the dropmarker's menu into both the back and
303 // the forward buttons.
304 var unifiedButton = document.getElementById("unified-back-forward-button");
305 if (unifiedButton && !unifiedButton._clickHandlersAttached) {
306 var popup = document.getElementById("back-forward-dropmarker")
307 .firstChild.cloneNode(true);
308 var backButton = document.getElementById("back-button");
309 backButton.setAttribute("type", "menu");
310 backButton.appendChild(popup);
311 _addClickAndHoldListenersOnElement(backButton);
312 var forwardButton = document.getElementById("forward-button");
313 popup = popup.cloneNode(true);
314 forwardButton.setAttribute("type", "menu");
315 forwardButton.appendChild(popup);
316 _addClickAndHoldListenersOnElement(forwardButton);
317 unifiedButton._clickHandlersAttached = true;
322 function BookmarkThisTab(aTab) {
323 PlacesCommandHook.bookmarkPage(aTab.linkedBrowser,
324 PlacesUtils.bookmarksMenuFolderId, true);
327 const gSessionHistoryObserver = {
328 observe: function(subject, topic, data)
330 if (topic != "browser:purge-session-history")
333 var backCommand = document.getElementById("Browser:Back");
334 backCommand.setAttribute("disabled", "true");
335 var fwdCommand = document.getElementById("Browser:Forward");
336 fwdCommand.setAttribute("disabled", "true");
339 // Clear undo history of the URL bar
340 gURLBar.editor.transactionManager.clear()
346 * Given a starting docshell and a URI to look up, find the docshell the URI
349 * A document to find instead of using just a URI - this is more specific.
351 * The doc shell to start at
353 * The URI that we're looking for
354 * @returns The doc shell that the sought URI is loaded in. Can be in
357 function findChildShell(aDocument, aDocShell, aSoughtURI) {
358 aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation);
359 aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
360 var doc = aDocShell.getInterface(Components.interfaces.nsIDOMDocument);
361 if ((aDocument && doc == aDocument) ||
362 (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
365 var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
366 for (var i = 0; i < node.childCount; ++i) {
367 var docShell = node.getChildAt(i);
368 docShell = findChildShell(aDocument, docShell, aSoughtURI);
375 const gPopupBlockerObserver = {
378 onUpdatePageReport: function (aEvent)
380 if (aEvent.originalTarget != gBrowser.selectedBrowser)
383 if (!this._reportButton)
384 this._reportButton = document.getElementById("page-report-button");
386 if (!gBrowser.pageReport) {
387 // Hide the popup blocker statusbar button
388 this._reportButton.hidden = true;
393 this._reportButton.hidden = false;
395 // Only show the notification again if we've not already shown it. Since
396 // notifications are per-browser, we don't need to worry about re-adding
398 if (!gBrowser.pageReport.reported) {
399 if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
400 var brandBundle = document.getElementById("bundle_brand");
401 var brandShortName = brandBundle.getString("brandShortName");
403 var popupCount = gBrowser.pageReport.length;
405 var popupButtonText = gNavigatorBundle.getString("popupWarningButton");
406 var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButton.accesskey");
408 var popupButtonText = gNavigatorBundle.getString("popupWarningButtonUnix");
409 var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButtonUnix.accesskey");
412 message = gNavigatorBundle.getFormattedString("popupWarningMultiple", [brandShortName, popupCount]);
414 message = gNavigatorBundle.getFormattedString("popupWarning", [brandShortName]);
416 var notificationBox = gBrowser.getNotificationBox();
417 var notification = notificationBox.getNotificationWithValue("popup-blocked");
419 notification.label = message;
423 label: popupButtonText,
424 accessKey: popupButtonAccesskey,
425 popup: "blockedPopupOptions",
429 const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
430 notificationBox.appendNotification(message, "popup-blocked",
431 "chrome://browser/skin/Info.png",
436 // Record the fact that we've reported this blocked popup, so we don't
438 gBrowser.pageReport.reported = true;
442 toggleAllowPopupsForSite: function (aEvent)
444 var pm = Services.perms;
445 var shouldBlock = aEvent.target.getAttribute("block") == "true";
446 var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
447 pm.add(gBrowser.currentURI, "popup", perm);
449 gBrowser.getNotificationBox().removeCurrentNotification();
452 fillPopupList: function (aEvent)
454 // XXXben - rather than using |currentURI| here, which breaks down on multi-framed sites
455 // we should really walk the pageReport and create a list of "allow for <host>"
456 // menuitems for the common subset of hosts present in the report, this will
457 // make us frame-safe.
459 // XXXjst - Note that when this is fixed to work with multi-framed sites,
460 // also back out the fix for bug 343772 where
461 // nsGlobalWindow::CheckOpenAllow() was changed to also
462 // check if the top window's location is whitelisted.
463 var uri = gBrowser.currentURI;
464 var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
466 blockedPopupAllowSite.removeAttribute("hidden");
468 var pm = Services.perms;
469 if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
470 // Offer an item to block popups for this site, if a whitelist entry exists
472 let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host]);
473 blockedPopupAllowSite.setAttribute("label", blockString);
474 blockedPopupAllowSite.setAttribute("block", "true");
477 // Offer an item to allow popups for this site
478 let allowString = gNavigatorBundle.getFormattedString("popupAllow", [uri.host]);
479 blockedPopupAllowSite.setAttribute("label", allowString);
480 blockedPopupAllowSite.removeAttribute("block");
484 blockedPopupAllowSite.setAttribute("hidden", "true");
487 if (gPrivateBrowsingUI.privateBrowsingEnabled)
488 blockedPopupAllowSite.setAttribute("disabled", "true");
490 blockedPopupAllowSite.removeAttribute("disabled");
492 var item = aEvent.target.lastChild;
493 while (item && item.getAttribute("observes") != "blockedPopupsSeparator") {
494 var next = item.previousSibling;
495 item.parentNode.removeChild(item);
499 var foundUsablePopupURI = false;
500 var pageReport = gBrowser.pageReport;
502 for (var i = 0; i < pageReport.length; ++i) {
503 var popupURIspec = pageReport[i].popupWindowURI.spec;
505 // Sometimes the popup URI that we get back from the pageReport
506 // isn't useful (for instance, netscape.com's popup URI ends up
507 // being "http://www.netscape.com", which isn't really the URI of
508 // the popup they're trying to show). This isn't going to be
509 // useful to the user, so we won't create a menu item for it.
510 if (popupURIspec == "" || popupURIspec == "about:blank" ||
511 popupURIspec == uri.spec)
514 // Because of the short-circuit above, we may end up in a situation
515 // in which we don't have any usable popup addresses to show in
516 // the menu, and therefore we shouldn't show the separator. However,
517 // since we got past the short-circuit, we must've found at least
518 // one usable popup URI and thus we'll turn on the separator later.
519 foundUsablePopupURI = true;
521 var menuitem = document.createElement("menuitem");
522 var label = gNavigatorBundle.getFormattedString("popupShowPopupPrefix",
524 menuitem.setAttribute("label", label);
525 menuitem.setAttribute("popupWindowURI", popupURIspec);
526 menuitem.setAttribute("popupWindowFeatures", pageReport[i].popupWindowFeatures);
527 menuitem.setAttribute("popupWindowName", pageReport[i].popupWindowName);
528 menuitem.setAttribute("oncommand", "gPopupBlockerObserver.showBlockedPopup(event);");
529 menuitem.requestingWindow = pageReport[i].requestingWindow;
530 menuitem.requestingDocument = pageReport[i].requestingDocument;
531 aEvent.target.appendChild(menuitem);
535 // Show or hide the separator, depending on whether we added any
536 // showable popup addresses to the menu.
537 var blockedPopupsSeparator =
538 document.getElementById("blockedPopupsSeparator");
539 if (foundUsablePopupURI)
540 blockedPopupsSeparator.removeAttribute("hidden");
542 blockedPopupsSeparator.setAttribute("hidden", true);
544 var blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
545 var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
546 blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
547 if (aEvent.target.localName == "popup")
548 blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
550 blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromStatusbar"));
553 showBlockedPopup: function (aEvent)
555 var target = aEvent.target;
556 var popupWindowURI = target.getAttribute("popupWindowURI");
557 var features = target.getAttribute("popupWindowFeatures");
558 var name = target.getAttribute("popupWindowName");
560 var dwi = target.requestingWindow;
562 // If we have a requesting window and the requesting document is
563 // still the current document, open the popup.
564 if (dwi && dwi.document == target.requestingDocument) {
565 dwi.open(popupWindowURI, name, features);
569 editPopupSettings: function ()
573 host = gBrowser.currentURI.host;
577 var bundlePreferences = document.getElementById("bundle_preferences");
578 var params = { blockVisible : false,
579 sessionVisible : false,
581 prefilledHost : host,
582 permissionType : "popup",
583 windowTitle : bundlePreferences.getString("popuppermissionstitle"),
584 introText : bundlePreferences.getString("popuppermissionstext") };
585 var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
586 if (existingWindow) {
587 existingWindow.initWithParams(params);
588 existingWindow.focus();
591 window.openDialog("chrome://browser/content/preferences/permissions.xul",
592 "_blank", "resizable,dialog=no,centerscreen", params);
595 dontShowMessage: function ()
597 var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
598 var firstTime = gPrefService.getBoolPref("privacy.popups.firstTime");
600 // If the info message is showing at the top of the window, and the user has never
601 // hidden the message before, show an info box telling the user where the info
602 // will be displayed.
603 if (showMessage && firstTime)
604 this._displayPageReportFirstTime();
606 gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
608 gBrowser.getNotificationBox().removeCurrentNotification();
611 _displayPageReportFirstTime: function ()
613 window.openDialog("chrome://browser/content/pageReportFirstTime.xul", "_blank",
618 const gXPInstallObserver = {
619 _findChildShell: function (aDocShell, aSoughtShell)
621 if (aDocShell == aSoughtShell)
624 var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
625 for (var i = 0; i < node.childCount; ++i) {
626 var docShell = node.getChildAt(i);
627 docShell = this._findChildShell(docShell, aSoughtShell);
628 if (docShell == aSoughtShell)
634 _getBrowser: function (aDocShell)
636 for (var i = 0; i < gBrowser.browsers.length; ++i) {
637 var browser = gBrowser.getBrowserAtIndex(i);
638 if (this._findChildShell(browser.docShell, aDocShell))
644 observe: function (aSubject, aTopic, aData)
646 var brandBundle = document.getElementById("bundle_brand");
647 var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
648 var win = installInfo.originatingWindow;
649 var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
650 .getInterface(Components.interfaces.nsIWebNavigation)
651 .QueryInterface(Components.interfaces.nsIDocShell);
652 var browser = this._getBrowser(shell);
655 const anchorID = "addons-notification-icon";
656 var messageString, action;
657 var brandShortName = brandBundle.getString("brandShortName");
659 var notificationID = aTopic;
660 // Make notifications persist a minimum of 30 seconds
662 timeout: Date.now() + 30000
666 case "addon-install-blocked":
669 enabled = gPrefService.getBoolPref("xpinstall.enabled");
675 notificationID = "xpinstall-disabled"
676 if (PopupNotifications.getNotification(notificationID, browser))
679 if (gPrefService.prefIsLocked("xpinstall.enabled")) {
680 messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
684 messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
687 label: gNavigatorBundle.getString("xpinstallDisabledButton"),
688 accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
689 callback: function editPrefs() {
690 gPrefService.setBoolPref("xpinstall.enabled", true);
696 if (PopupNotifications.getNotification(notificationID, browser))
699 messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
700 [brandShortName, installInfo.originatingURI.host]);
703 label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
704 accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
705 callback: function() {
706 installInfo.install();
711 PopupNotifications.show(browser, notificationID, messageString, anchorID,
712 action, null, options);
714 case "addon-install-failed":
715 // TODO This isn't terribly ideal for the multiple failure case
716 installInfo.installs.forEach(function(aInstall) {
717 var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
718 installInfo.originatingURI.host;
720 host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
721 aInstall.sourceURI.host;
723 var error = (host || aInstall.error == 0) ? "addonError" : "addonLocalError";
724 if (aInstall.error != 0)
725 error += aInstall.error;
726 else if (aInstall.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
727 error += "Blocklisted";
729 error += "Incompatible";
731 messageString = gNavigatorBundle.getString(error);
732 messageString = messageString.replace("#1", aInstall.name);
734 messageString = messageString.replace("#2", host);
735 messageString = messageString.replace("#3", brandShortName);
736 messageString = messageString.replace("#4", Services.appinfo.version);
738 PopupNotifications.show(browser, notificationID, messageString, anchorID,
739 action, null, options);
742 case "addon-install-complete":
743 var notification = PopupNotifications.getNotification(notificationID, browser);
745 PopupNotifications.remove(notification);
747 var needsRestart = installInfo.installs.some(function(i) {
748 return (i.addon.pendingOperations & AddonManager.PENDING_INSTALL) != 0;
752 messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
754 label: gNavigatorBundle.getString("addonInstallRestartButton"),
755 accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
756 callback: function() {
757 Application.restart();
762 messageString = gNavigatorBundle.getString("addonsInstalled");
764 label: gNavigatorBundle.getString("addonInstallManage"),
765 accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
766 callback: function() {
767 // Calculate the add-on type that is most popular in the list of
771 installInfo.installs.forEach(function(aInstall) {
772 if (aInstall.type in types)
773 types[aInstall.type]++;
775 types[aInstall.type] = 1;
776 if (!bestType || types[aInstall.type] > types[bestType])
777 bestType = aInstall.type;
780 BrowserOpenAddonsMgr("addons://list/" + bestType);
785 messageString = PluralForm.get(installInfo.installs.length, messageString);
786 messageString = messageString.replace("#1", installInfo.installs[0].name);
787 messageString = messageString.replace("#2", installInfo.installs.length);
788 messageString = messageString.replace("#3", brandShortName);
790 PopupNotifications.show(browser, notificationID, messageString, anchorID,
791 action, null, options);
797 // Simple gestures support
799 // As per bug #412486, web content must not be allowed to receive any
800 // simple gesture events. Multi-touch gesture APIs are in their
801 // infancy and we do NOT want to be forced into supporting an API that
802 // will probably have to change in the future. (The current Mac OS X
803 // API is undocumented and was reverse-engineered.) Until support is
804 // implemented in the event dispatcher to keep these events as
805 // chrome-only, we must listen for the simple gesture events during
806 // the capturing phase and call stopPropagation on every event.
808 let gGestureSupport = {
810 * Add or remove mouse gesture event listeners
812 * @param aAddListener
813 * True to add/init listeners and false to remove/uninit
815 init: function GS_init(aAddListener) {
816 const gestureEvents = ["SwipeGesture",
817 "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
818 "RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
819 "TapGesture", "PressTapGesture"];
821 let addRemove = aAddListener ? window.addEventListener :
822 window.removeEventListener;
824 gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true),
829 * Dispatch events based on the type of mouse gesture event. For now, make
830 * sure to stop propagation of every gesture event so that web content cannot
831 * receive gesture events.
834 * The gesture event to handle
836 handleEvent: function GS_handleEvent(aEvent) {
837 aEvent.stopPropagation();
839 // Create a preference object with some defaults
840 let def = function(aThreshold, aLatched)
841 ({ threshold: aThreshold, latched: !!aLatched });
843 switch (aEvent.type) {
844 case "MozSwipeGesture":
845 aEvent.preventDefault();
846 return this.onSwipe(aEvent);
847 case "MozMagnifyGestureStart":
848 aEvent.preventDefault();
850 return this._setupGesture(aEvent, "pinch", def(25, 0), "out", "in");
852 return this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
854 case "MozRotateGestureStart":
855 aEvent.preventDefault();
856 return this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
857 case "MozMagnifyGestureUpdate":
858 case "MozRotateGestureUpdate":
859 aEvent.preventDefault();
860 return this._doUpdate(aEvent);
861 case "MozTapGesture":
862 aEvent.preventDefault();
863 return this._doAction(aEvent, ["tap"]);
864 case "MozPressTapGesture":
865 // Fall through to default behavior
871 * Called at the start of "pinch" and "twist" gestures to setup all of the
872 * information needed to process the gesture
875 * The continual motion start event to handle
877 * Name of the gesture to handle
879 * Preference object with the names of preferences and defaults
881 * Command to trigger for increasing motion (without gesture name)
883 * Command to trigger for decreasing motion (without gesture name)
885 _setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
886 // Try to load user-set values from preferences
887 for (let [pref, def] in Iterator(aPref))
888 aPref[pref] = this._getPref(aGesture + "." + pref, def);
890 // Keep track of the total deltas and latching behavior
892 let latchDir = aEvent.delta > 0 ? 1 : -1;
893 let isLatched = false;
895 // Create the update function here to capture closure state
896 this._doUpdate = function GS__doUpdate(aEvent) {
897 // Update the offset with new event data
898 offset += aEvent.delta;
900 // Check if the cumulative deltas exceed the threshold
901 if (Math.abs(offset) > aPref["threshold"]) {
902 // Trigger the action if we don't care about latching; otherwise, make
903 // sure either we're not latched and going the same direction of the
904 // initial motion; or we're latched and going the opposite way
905 let sameDir = (latchDir ^ offset) >= 0;
906 if (!aPref["latched"] || (isLatched ^ sameDir)) {
907 this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
909 // We must be getting latched or leaving it, so just toggle
910 isLatched = !isLatched;
913 // Reset motion counter to prepare for more of the same gesture
918 // The start event also contains deltas, so handle an update right away
919 this._doUpdate(aEvent);
923 * Generator producing the powerset of the input array where the first result
924 * is the complete set and the last result (before StopIteration) is empty.
927 * Source array containing any number of elements
928 * @yield Array that is a subset of the input array from full set to empty
930 _power: function GS__power(aArray) {
931 // Create a bitmask based on the length of the array
932 let num = 1 << aArray.length;
934 // Only select array elements where the current bit is set
935 yield aArray.reduce(function (aPrev, aCurr, aIndex) {
936 if (num & 1 << aIndex)
944 * Determine what action to do for the gesture based on which keys are
945 * pressed and which commands are set
948 * The original gesture event to convert into a fake click event
950 * Array of gesture name parts (to be joined by periods)
951 * @return Name of the command found for the event's keys and gesture. If no
952 * command is found, no value is returned (undefined).
954 _doAction: function GS__doAction(aEvent, aGesture) {
955 // Create an array of pressed keys in a fixed order so that a command for
956 // "meta" is preferred over "ctrl" when both buttons are pressed (and a
957 // command for both don't exist)
959 ["shift", "alt", "ctrl", "meta"].forEach(function (key) {
960 if (aEvent[key + "Key"])
964 // Try each combination of key presses in decreasing order for commands
965 for each (let subCombo in this._power(keyCombos)) {
966 // Convert a gesture and pressed keys into the corresponding command
967 // action where the preference has the gesture before "shift" before
968 // "alt" before "ctrl" before "meta" all separated by periods
971 command = this._getPref(aGesture.concat(subCombo).join("."));
977 let node = document.getElementById(command);
979 if (node.getAttribute("disabled") != "true") {
980 let cmdEvent = document.createEvent("xulcommandevent");
981 cmdEvent.initCommandEvent("command", true, true, window, 0,
982 aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
983 aEvent.metaKey, null);
984 node.dispatchEvent(cmdEvent);
987 goDoCommand(command);
996 * Convert continual motion events into an action if it exceeds a threshold
997 * in a given direction. This function will be set by _setupGesture to
998 * capture state that needs to be shared across multiple gesture updates.
1001 * The continual motion update event to handle
1003 _doUpdate: function(aEvent) {},
1006 * Convert the swipe gesture into a browser action based on the direction
1009 * The swipe event to handle
1011 onSwipe: function GS_onSwipe(aEvent) {
1012 // Figure out which one (and only one) direction was triggered
1013 ["UP", "RIGHT", "DOWN", "LEFT"].forEach(function (dir) {
1014 if (aEvent.direction == aEvent["DIRECTION_" + dir])
1015 return this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
1020 * Get a gesture preference or use a default if it doesn't exist
1023 * Name of the preference to load under the gesture branch
1025 * Default value if the preference doesn't exist
1027 _getPref: function GS__getPref(aPref, aDef) {
1028 // Preferences branch under which all gestures preferences are stored
1029 const branch = "browser.gesture.";
1032 // Determine what type of data to load based on default value's type
1033 let type = typeof aDef;
1034 let getFunc = "get" + (type == "boolean" ? "Bool" :
1035 type == "number" ? "Int" : "Char") + "Pref";
1036 return gPrefService[getFunc](branch + aPref);
1044 function BrowserStartup() {
1045 var uriToLoad = null;
1047 // window.arguments[0]: URI to load (string), or an nsISupportsArray of
1048 // nsISupportsStrings to load, or a xul:tab of
1049 // a tabbrowser, which will be replaced by this
1050 // window (for this case, all other arguments are
1052 // [1]: character set (string)
1053 // [2]: referrer (nsIURI)
1054 // [3]: postData (nsIInputStream)
1055 // [4]: allowThirdPartyFixup (bool)
1056 if ("arguments" in window && window.arguments[0])
1057 uriToLoad = window.arguments[0];
1059 var isLoadingBlank = uriToLoad == "about:blank";
1060 var mustLoadSidebar = false;
1062 prepareForStartup();
1064 if (uriToLoad && !isLoadingBlank) {
1065 if (uriToLoad instanceof Ci.nsISupportsArray) {
1066 let count = uriToLoad.Count();
1068 for (let i = 0; i < count; i++) {
1069 let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
1070 specs.push(urisstring.data);
1073 // This function throws for certain malformed URIs, so use exception handling
1074 // so that we don't disrupt startup
1076 gBrowser.loadTabs(specs, false, true);
1079 else if (uriToLoad instanceof XULElement) {
1080 // swap the given tab with the default about:blank tab and then close
1081 // the original tab in the other window.
1083 // Stop the about:blank load
1085 // make sure it has a docshell
1088 gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
1090 else if (window.arguments.length >= 3) {
1091 loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
1092 window.arguments[4] || false);
1095 // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
1096 // Such callers expect that window.arguments[0] is handled as a single URI.
1098 loadOneOrMoreURIs(uriToLoad);
1101 if (window.opener && !window.opener.closed) {
1102 let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
1103 // If the opener had a sidebar, open the same sidebar in our window.
1104 // The opener can be the hidden window too, if we're coming from the state
1105 // where no windows are open, and the hidden window has no sidebar box.
1106 if (openerSidebarBox && !openerSidebarBox.hidden) {
1107 let sidebarCmd = openerSidebarBox.getAttribute("sidebarcommand");
1108 let sidebarCmdElem = document.getElementById(sidebarCmd);
1110 // dynamically generated sidebars will fail this check.
1111 if (sidebarCmdElem) {
1112 let sidebarBox = document.getElementById("sidebar-box");
1113 let sidebarTitle = document.getElementById("sidebar-title");
1115 sidebarTitle.setAttribute(
1116 "value", window.opener.document.getElementById("sidebar-title").getAttribute("value"));
1117 sidebarBox.setAttribute("width", openerSidebarBox.boxObject.width);
1119 sidebarBox.setAttribute("sidebarcommand", sidebarCmd);
1120 // Note: we're setting 'src' on sidebarBox, which is a <vbox>, not on
1121 // the <browser id="sidebar">. This lets us delay the actual load until
1122 // delayedStartup().
1123 sidebarBox.setAttribute(
1124 "src", window.opener.document.getElementById("sidebar").getAttribute("src"));
1125 mustLoadSidebar = true;
1127 sidebarBox.hidden = false;
1128 document.getElementById("sidebar-splitter").hidden = false;
1129 sidebarCmdElem.setAttribute("checked", "true");
1134 let box = document.getElementById("sidebar-box");
1135 if (box.hasAttribute("sidebarcommand")) {
1136 let commandID = box.getAttribute("sidebarcommand");
1138 let command = document.getElementById(commandID);
1140 mustLoadSidebar = true;
1142 document.getElementById("sidebar-splitter").hidden = false;
1143 command.setAttribute("checked", "true");
1146 // Remove the |sidebarcommand| attribute, because the element it
1147 // refers to no longer exists, so we should assume this sidebar
1148 // panel has been uninstalled. (249883)
1149 box.removeAttribute("sidebarcommand");
1155 // Certain kinds of automigration rely on this notification to complete their
1156 // tasks BEFORE the browser window is shown.
1157 Services.obs.notifyObservers(null, "browser-window-before-show", "");
1159 // Set a sane starting width/height for all resolutions on new profiles.
1160 if (!document.documentElement.hasAttribute("width")) {
1161 let defaultWidth = 994;
1163 if (screen.availHeight <= 600) {
1164 document.documentElement.setAttribute("sizemode", "maximized");
1166 defaultHeight = 450;
1169 // Create a narrower window for large or wide-aspect displays, to suggest
1170 // side-by-side page view.
1171 if (screen.availWidth >= 1600)
1172 defaultWidth = (screen.availWidth / 2) - 20;
1173 defaultHeight = screen.availHeight - 10;
1174 #ifdef MOZ_WIDGET_GTK2
1175 // On X, we're not currently able to account for the size of the window
1176 // border. Use 28px as a guess (titlebar + bottom window border)
1177 defaultHeight -= 28;
1180 document.documentElement.setAttribute("width", defaultWidth);
1181 document.documentElement.setAttribute("height", defaultHeight);
1184 if (!window.toolbar.visible) {
1185 // adjust browser UI for popups
1187 gURLBar.setAttribute("readonly", "true");
1188 gURLBar.setAttribute("enablehistory", "false");
1190 goSetCommandEnabled("Browser:OpenLocation", false);
1191 goSetCommandEnabled("cmd_newNavigatorTab", false);
1194 #ifdef MENUBAR_CAN_AUTOHIDE
1195 updateAppButtonDisplay();
1198 CombinedStopReload.init();
1202 TabsOnTop.syncCommand();
1204 BookmarksMenuButton.init();
1206 setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
1209 function HandleAppCommandEvent(evt) {
1210 evt.stopPropagation();
1211 switch (evt.command) {
1219 BrowserReloadSkipCache();
1225 BrowserSearch.webSearch();
1228 toggleSidebar('viewBookmarksSidebar');
1238 function prepareForStartup() {
1239 gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
1241 gBrowser.addEventListener("PluginNotFound", gPluginHandler, true);
1242 gBrowser.addEventListener("PluginCrashed", gPluginHandler, true);
1243 gBrowser.addEventListener("PluginBlocklisted", gPluginHandler, true);
1244 gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
1245 gBrowser.addEventListener("PluginDisabled", gPluginHandler, true);
1246 gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
1248 Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);
1250 window.addEventListener("AppCommand", HandleAppCommandEvent, true);
1254 webNavigation = getWebNavigation();
1256 throw "no XBL binding for browser";
1258 alert("Error launching browser window:" + e);
1259 window.close(); // Give up.
1263 // initialize observers and listeners
1264 // and give C++ access to gBrowser
1265 XULBrowserWindow.init();
1266 window.QueryInterface(Ci.nsIInterfaceRequestor)
1267 .getInterface(nsIWebNavigation)
1268 .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
1269 .QueryInterface(Ci.nsIInterfaceRequestor)
1270 .getInterface(Ci.nsIXULWindow)
1271 .XULBrowserWindow = window.XULBrowserWindow;
1272 window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
1273 new nsBrowserAccess();
1275 // set default character set if provided
1276 if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) {
1277 if (window.arguments[1].indexOf("charset=") != -1) {
1278 var arrayArgComponents = window.arguments[1].split("=");
1279 if (arrayArgComponents) {
1280 //we should "inherit" the charset menu setting in a new window
1281 getMarkupDocumentViewer().defaultCharacterSet = arrayArgComponents[1];
1286 // Manually hook up session and global history for the first browser
1287 // so that we don't have to load global history before bringing up a
1289 // Wire up session and global history before any possible
1290 // progress notifications for back/forward button updating
1291 webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
1292 .createInstance(Components.interfaces.nsISHistory);
1293 Services.obs.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
1295 // remove the disablehistory attribute so the browser cleans up, as
1296 // though it had done this work itself
1297 gBrowser.browsers[0].removeAttribute("disablehistory");
1299 // enable global history
1301 gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
1303 Components.utils.reportError("Places database may be locked: " + ex);
1306 // hook up UI through progress listener
1307 gBrowser.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
1308 gBrowser.addTabsProgressListener(window.TabsProgressListener);
1310 // setup our common DOMLinkAdded listener
1311 gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
1313 // setup our MozApplicationManifest listener
1314 gBrowser.addEventListener("MozApplicationManifest",
1315 OfflineApps, false);
1317 // setup simple gestures support
1318 gGestureSupport.init(true);
1320 #ifdef MENUBAR_CAN_AUTOHIDE
1321 // update the visibility of the titlebar buttons after the window is
1322 // displayed. (required by theme code.)
1323 window.addEventListener("MozAfterPaint", function () {
1324 window.removeEventListener("MozAfterPaint", arguments.callee, false);
1325 document.getElementById("titlebar-buttonbox").collapsed = false;
1330 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
1331 Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
1332 Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
1333 Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
1334 Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
1336 BrowserOffline.init();
1339 gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
1341 // Ensure login manager is up and running.
1342 Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
1344 if (mustLoadSidebar) {
1345 let sidebar = document.getElementById("sidebar");
1346 let sidebarBox = document.getElementById("sidebar-box");
1347 sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
1350 UpdateUrlbarSearchSplitterState();
1352 PlacesStarButton.init();
1354 // called when we go into full screen, even if it is
1355 // initiated by a web page script
1356 window.addEventListener("fullscreen", onFullScreen, true);
1358 if (isLoadingBlank && gURLBar && isElementVisible(gURLBar))
1361 gBrowser.selectedBrowser.focus();
1363 gNavToolbox.customizeDone = BrowserToolboxCustomizeDone;
1364 gNavToolbox.customizeChange = BrowserToolboxCustomizeChange;
1366 // Set up Sanitize Item
1367 initializeSanitizer();
1369 // Enable/Disable auto-hide tabbar
1370 gBrowser.tabContainer.updateVisibility();
1372 gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
1374 var homeButton = document.getElementById("home-button");
1375 gHomeButton.updateTooltip(homeButton);
1376 gHomeButton.updatePersonalToolbarStyle(homeButton);
1378 #ifdef HAVE_SHELL_SERVICE
1379 // Perform default browser checking (after window opens).
1380 var shell = getShellService();
1382 var shouldCheck = shell.shouldCheckDefaultBrowser;
1383 var willRecoverSession = false;
1385 var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
1386 getService(Ci.nsISessionStartup);
1387 willRecoverSession =
1388 (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
1390 catch (ex) { /* never mind; suppose SessionStore is broken */ }
1391 if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
1392 var brandBundle = document.getElementById("bundle_brand");
1393 var shellBundle = document.getElementById("bundle_shell");
1395 var brandShortName = brandBundle.getString("brandShortName");
1396 var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
1397 var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
1399 var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
1401 var checkEveryTime = { value: shouldCheck };
1402 var ps = Services.prompt;
1403 var rv = ps.confirmEx(window, promptTitle, promptMessage,
1404 ps.STD_YES_NO_BUTTONS,
1405 null, null, null, checkboxLabel, checkEveryTime);
1407 shell.setDefaultBrowser(true, false);
1408 shell.shouldCheckDefaultBrowser = checkEveryTime.value;
1414 gBidiUI = isBidiEnabled();
1416 document.getElementById("documentDirection-separator").hidden = false;
1417 document.getElementById("documentDirection-swap").hidden = false;
1418 document.getElementById("textfieldDirection-separator").hidden = false;
1419 document.getElementById("textfieldDirection-swap").hidden = false;
1423 // Setup click-and-hold gestures access to the session history
1424 // menus if global click-and-hold isn't turned on
1425 if (!getBoolPref("ui.click_hold_context_menus", false))
1426 SetClickAndHoldHandlers();
1429 // Initialize the full zoom setting.
1430 // We do this before the session restore service gets initialized so we can
1431 // apply full zoom settings to tabs restored by the session restore service.
1436 Components.utils.reportError("Failed to init content pref service:\n" + ex);
1440 Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
1441 NP.trackBrowserWindow(window);
1443 // initialize the session-restore service (in case it's not already running)
1445 Cc["@mozilla.org/browser/sessionstore;1"]
1446 .getService(Ci.nsISessionStore)
1449 dump("nsSessionStore could not be initialized: " + ex + "\n");
1452 PlacesToolbarHelper.init();
1454 // bookmark-all-tabs command
1455 gBookmarkAllTabsHandler.init();
1457 // Attach a listener to watch for "command" events bubbling up from error
1458 // pages. This lets us fix bugs like 401575 which require error page UI to
1459 // do privileged things, without letting error pages have any privilege
1461 gBrowser.addEventListener("command", BrowserOnCommand, false);
1464 gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
1465 gPrefService.addObserver(allTabs.prefName, allTabs, false);
1467 // Initialize the microsummary service by retrieving it, prompting its factory
1468 // to create its singleton, whose constructor initializes the service.
1469 // Started 4 seconds after delayedStartup (before the livemarks service below).
1470 setTimeout(function() {
1472 Cc["@mozilla.org/microsummary/service;1"].getService(Ci.nsIMicrosummaryService);
1474 Components.utils.reportError("Failed to init microsummary service:\n" + ex);
1478 // Delayed initialization of the livemarks update timer.
1479 // Livemark updates don't need to start until after bookmark UI
1480 // such as the toolbar has initialized. Starting 5 seconds after
1481 // delayedStartup in order to stagger this after the microsummary
1482 // service (see above) and before the download manager starts (see below).
1483 setTimeout(function() PlacesUtils.livemarks.start(), 5000);
1485 // Initialize the download manager some time after the app starts so that
1486 // auto-resume downloads begin (such as after crashing or quitting with
1487 // active downloads) and speeds up the first-load of the download manager UI.
1488 // If the user manually opens the download manager before the timeout, the
1489 // downloads will start right away, and getting the service again won't hurt.
1490 setTimeout(function() {
1491 gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
1492 getService(Ci.nsIDownloadManager);
1494 // Initialize the downloads monitor panel listener
1495 DownloadMonitorPanel.init();
1499 Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
1501 tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
1505 // Delayed initialization of PlacesDBUtils.
1506 // This component checks for database coherence once per day, on
1507 // an idle timer, taking corrective actions where needed.
1508 setTimeout(function() PlacesUtils.startPlacesDBUtils(), 15000);
1511 updateEditUIVisibility();
1512 let placesContext = document.getElementById("placesContext");
1513 placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
1514 placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
1517 // initialize the private browsing UI
1518 gPrivateBrowsingUI.init();
1520 gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
1521 gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
1522 gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
1525 Win7Features.onOpenWindow();
1527 #ifdef MOZ_SERVICES_SYNC
1528 // initialize the sync UI
1532 Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
1535 function BrowserShutdown()
1538 Win7Features.onCloseWindow();
1540 gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
1541 gPrefService.removeObserver(allTabs.prefName, allTabs);
1545 CombinedStopReload.uninit();
1547 gGestureSupport.init(false);
1549 FullScreen.cleanup();
1555 Components.utils.reportError(ex);
1558 Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
1559 Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
1560 Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
1561 Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
1562 Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
1565 gBrowser.removeProgressListener(window.XULBrowserWindow);
1566 gBrowser.removeTabsProgressListener(window.TabsProgressListener);
1570 PlacesStarButton.uninit();
1573 gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
1575 Components.utils.reportError(ex);
1578 BrowserOffline.uninit();
1579 OfflineApps.uninit();
1580 DownloadMonitorPanel.uninit();
1581 gPrivateBrowsingUI.uninit();
1583 var enumerator = Services.wm.getEnumerator(null);
1584 enumerator.getNext();
1585 if (!enumerator.hasMoreElements()) {
1586 document.persist("sidebar-box", "sidebarcommand");
1587 document.persist("sidebar-box", "width");
1588 document.persist("sidebar-box", "src");
1589 document.persist("sidebar-title", "value");
1592 window.XULBrowserWindow.destroy();
1593 window.XULBrowserWindow = null;
1594 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
1595 .getInterface(Components.interfaces.nsIWebNavigation)
1596 .QueryInterface(Components.interfaces.nsIDocShellTreeItem).treeOwner
1597 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
1598 .getInterface(Components.interfaces.nsIXULWindow)
1599 .XULBrowserWindow = null;
1600 window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
1604 // nonBrowserWindowStartup(), nonBrowserWindowDelayedStartup(), and
1605 // nonBrowserWindowShutdown() are used for non-browser windows in
1606 // macBrowserOverlay
1607 function nonBrowserWindowStartup()
1609 // Disable inappropriate commands / submenus
1610 var disabledItems = ['Browser:SavePage',
1611 'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
1612 'viewToolbarsMenu', 'cmd_toggleTaskbar', 'viewSidebarMenuMenu', 'Browser:Reload',
1613 'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
1614 'viewHistorySidebar', 'Browser:AddBookmarkAs', 'View:PageInfo', 'Tasks:InspectPage'];
1617 for (var id in disabledItems)
1619 element = document.getElementById(disabledItems[id]);
1621 element.setAttribute("disabled", "true");
1624 // If no windows are active (i.e. we're the hidden window), disable the close, minimize
1625 // and zoom menu commands as well
1626 if (window.location.href == "chrome://browser/content/hiddenWindow.xul")
1628 var hiddenWindowDisabledItems = ['cmd_close', 'minimizeWindow', 'zoomWindow'];
1629 for (var id in hiddenWindowDisabledItems)
1631 element = document.getElementById(hiddenWindowDisabledItems[id]);
1633 element.setAttribute("disabled", "true");
1636 // also hide the window-list separator
1637 element = document.getElementById("sep-window-list");
1638 element.setAttribute("hidden", "true");
1640 // Setup the dock menu.
1641 let dockMenuElement = document.getElementById("menu_mac_dockmenu");
1642 if (dockMenuElement != null) {
1643 let nativeMenu = Cc["@mozilla.org/widget/standalonenativemenu;1"]
1644 .createInstance(Ci.nsIStandaloneNativeMenu);
1647 nativeMenu.init(dockMenuElement);
1649 let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
1650 .getService(Ci.nsIMacDockSupport);
1651 dockSupport.dockMenu = nativeMenu;
1659 setTimeout(nonBrowserWindowDelayedStartup, 0);
1662 function nonBrowserWindowDelayedStartup()
1664 // initialise the offline listener
1665 BrowserOffline.init();
1667 // Set up Sanitize Item
1668 initializeSanitizer();
1670 // initialize the private browsing UI
1671 gPrivateBrowsingUI.init();
1673 #ifdef MOZ_SERVICES_SYNC
1674 // initialize the sync UI
1679 function nonBrowserWindowShutdown()
1681 BrowserOffline.uninit();
1683 gPrivateBrowsingUI.uninit();
1687 function initializeSanitizer()
1689 const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize";
1690 if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) {
1691 gPrefService.clearUserPref(kDidSanitizeDomain);
1692 // We need to persist this preference change, since we want to
1693 // check it at next app start even if the browser exits abruptly
1694 gPrefService.savePrefFile(null);
1698 * Migrate Firefox 3.0 privacy.item prefs under one of these conditions:
1700 * a) User has customized any privacy.item prefs
1701 * b) privacy.sanitize.sanitizeOnShutdown is set
1703 if (!gPrefService.getBoolPref("privacy.sanitize.migrateFx3Prefs")) {
1704 let itemBranch = gPrefService.getBranch("privacy.item.");
1705 let itemArray = itemBranch.getChildList("");
1707 // See if any privacy.item prefs are set
1708 let doMigrate = itemArray.some(function (name) itemBranch.prefHasUserValue(name));
1709 // Or if sanitizeOnShutdown is set
1711 doMigrate = gPrefService.getBoolPref("privacy.sanitize.sanitizeOnShutdown");
1714 let cpdBranch = gPrefService.getBranch("privacy.cpd.");
1715 let clearOnShutdownBranch = gPrefService.getBranch("privacy.clearOnShutdown.");
1716 itemArray.forEach(function (name) {
1718 // don't migrate password or offlineApps clearing in the CRH dialog since
1719 // there's no UI for those anymore. They default to false. bug 497656
1720 if (name != "passwords" && name != "offlineApps")
1721 cpdBranch.setBoolPref(name, itemBranch.getBoolPref(name));
1722 clearOnShutdownBranch.setBoolPref(name, itemBranch.getBoolPref(name));
1725 Cu.reportError("Exception thrown during privacy pref migration: " + e);
1730 gPrefService.setBoolPref("privacy.sanitize.migrateFx3Prefs", true);
1734 function gotoHistoryIndex(aEvent)
1736 var index = aEvent.target.getAttribute("index");
1740 var where = whereToOpenLink(aEvent);
1742 if (where == "current") {
1743 // Normal click. Go there in the current tab and update session history.
1746 gBrowser.gotoIndex(index);
1754 // Modified click. Go there in a new tab/window.
1755 // This code doesn't copy history or work well with framed pages.
1757 var sessionHistory = getWebNavigation().sessionHistory;
1758 var entry = sessionHistory.getEntryAtIndex(index, false);
1759 var url = entry.URI.spec;
1760 openUILinkIn(url, where, {relatedToCurrent: true});
1765 function BrowserForward(aEvent) {
1766 var where = whereToOpenLink(aEvent, false, true);
1768 if (where == "current") {
1770 gBrowser.goForward();
1776 var sessionHistory = getWebNavigation().sessionHistory;
1777 var currentIndex = sessionHistory.index;
1778 var entry = sessionHistory.getEntryAtIndex(currentIndex + 1, false);
1779 var url = entry.URI.spec;
1780 openUILinkIn(url, where, {relatedToCurrent: true});
1784 function BrowserBack(aEvent) {
1785 var where = whereToOpenLink(aEvent, false, true);
1787 if (where == "current") {
1795 var sessionHistory = getWebNavigation().sessionHistory;
1796 var currentIndex = sessionHistory.index;
1797 var entry = sessionHistory.getEntryAtIndex(currentIndex - 1, false);
1798 var url = entry.URI.spec;
1799 openUILinkIn(url, where, {relatedToCurrent: true});
1803 function BrowserHandleBackspace()
1805 switch (gPrefService.getIntPref("browser.backspace_action")) {
1810 goDoCommand("cmd_scrollPageUp");
1815 function BrowserHandleShiftBackspace()
1817 switch (gPrefService.getIntPref("browser.backspace_action")) {
1822 goDoCommand("cmd_scrollPageDown");
1827 function BrowserStop()
1830 const stopFlags = nsIWebNavigation.STOP_ALL;
1831 getWebNavigation().stop(stopFlags);
1837 function BrowserReloadOrDuplicate(aEvent) {
1838 var backgroundTabModifier = aEvent.button == 1 ||
1844 if (aEvent.shiftKey && !backgroundTabModifier) {
1845 BrowserReloadSkipCache();
1849 var where = whereToOpenLink(aEvent, false, true);
1850 if (where == "current")
1853 openUILinkIn(getWebNavigation().currentURI.spec, where,
1854 {relatedToCurrent: true});
1857 function BrowserReload() {
1858 const reloadFlags = nsIWebNavigation.LOAD_FLAGS_NONE;
1859 BrowserReloadWithFlags(reloadFlags);
1862 function BrowserReloadSkipCache() {
1863 // Bypass proxy and cache.
1864 const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
1865 BrowserReloadWithFlags(reloadFlags);
1868 function BrowserHome()
1870 var homePage = gHomeButton.getHomePage();
1871 loadOneOrMoreURIs(homePage);
1874 function BrowserGoHome(aEvent) {
1875 if (aEvent && "button" in aEvent &&
1876 aEvent.button == 2) // right-click: do nothing
1879 var homePage = gHomeButton.getHomePage();
1880 var where = whereToOpenLink(aEvent, false, true);
1883 // openUILinkIn in utilityOverlay.js doesn't handle loading multiple pages
1886 loadOneOrMoreURIs(homePage);
1890 urls = homePage.split("|");
1891 var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
1892 gBrowser.loadTabs(urls, loadInBackground);
1895 OpenBrowserWindow();
1900 function loadOneOrMoreURIs(aURIString)
1903 // we're not a browser window, pass the URI string to a new browser window
1904 if (window.location.href != getBrowserURL())
1906 window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
1910 // This function throws for certain malformed URIs, so use exception handling
1911 // so that we don't disrupt startup
1913 gBrowser.loadTabs(aURIString.split("|"), false, true);
1919 function focusAndSelectUrlBar() {
1920 if (gURLBar && !gURLBar.readOnly) {
1921 if (window.fullScreen)
1922 FullScreen.mouseoverToggle(true);
1923 if (isElementVisible(gURLBar)) {
1932 function openLocation() {
1933 if (focusAndSelectUrlBar())
1937 if (window.location.href != getBrowserURL()) {
1938 var win = getTopWin();
1940 // If there's an open browser window, it should handle this command
1945 // If there are no open browser windows, open a new one
1946 win = window.openDialog("chrome://browser/content/", "_blank",
1947 "chrome,all,dialog=no", "about:blank");
1948 win.addEventListener("load", openLocationCallback, false);
1953 openDialog("chrome://browser/content/openLocation.xul", "_blank",
1954 "chrome,modal,titlebar", window);
1957 function openLocationCallback()
1959 // make sure the DOM is ready
1960 setTimeout(function() { this.openLocation(); }, 0);
1963 function BrowserOpenTab()
1966 // If there are no open browser windows, open a new one
1967 window.openDialog("chrome://browser/content/", "_blank",
1968 "chrome,all,dialog=no", "about:blank");
1971 gBrowser.loadOneTab("about:blank", {inBackground: false});
1972 focusAndSelectUrlBar();
1975 /* Called from the openLocation dialog. This allows that dialog to instruct
1976 its opener to open a new window and then step completely out of the way.
1977 Anything less byzantine is causing horrible crashes, rather believably,
1978 though oddly only on Linux. */
1979 function delayedOpenWindow(chrome, flags, href, postData)
1981 // The other way to use setTimeout,
1982 // setTimeout(openDialog, 10, chrome, "_blank", flags, url),
1983 // doesn't work here. The extra "magic" extra argument setTimeout adds to
1984 // the callback function would confuse prepareForStartup() by making
1985 // window.arguments[1] be an integer instead of null.
1986 setTimeout(function() { openDialog(chrome, "_blank", flags, href, null, null, postData); }, 10);
1989 /* Required because the tab needs time to set up its content viewers and get the load of
1990 the URI kicked off before becoming the active content area. */
1991 function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFixup)
1993 gBrowser.loadOneTab(aUrl, {
1994 referrerURI: aReferrer,
1996 postData: aPostData,
1997 inBackground: false,
1998 allowThirdPartyFixup: aAllowThirdPartyFixup});
2001 var gLastOpenDirectory = {
2004 if (!this._lastDir || !this._lastDir.exists()) {
2006 this._lastDir = gPrefService.getComplexValue("browser.open.lastDir",
2008 if (!this._lastDir.exists())
2009 this._lastDir = null;
2013 return this._lastDir;
2016 if (!val || !val.exists() || !val.isDirectory())
2018 this._lastDir = val.clone();
2020 // Don't save the last open directory pref inside the Private Browsing mode
2021 if (!gPrivateBrowsingUI.privateBrowsingEnabled)
2022 gPrefService.setComplexValue("browser.open.lastDir", Ci.nsILocalFile,
2026 this._lastDir = null;
2030 function BrowserOpenFileWindow()
2032 // Get filepicker component.
2034 const nsIFilePicker = Components.interfaces.nsIFilePicker;
2035 var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
2036 fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
2037 fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
2038 nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
2039 fp.displayDirectory = gLastOpenDirectory.path;
2041 if (fp.show() == nsIFilePicker.returnOK) {
2042 if (fp.file && fp.file.exists())
2043 gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
2044 openTopWin(fp.fileURL.spec);
2050 function BrowserCloseTabOrWindow() {
2052 // If we're not a browser window, just close the window
2053 if (window.location.href != getBrowserURL()) {
2059 // If the current tab is the last one, this will close the window.
2060 gBrowser.removeCurrentTab({animate: true});
2063 function BrowserTryToCloseWindow()
2065 if (WindowIsClosing())
2066 window.close(); // WindowIsClosing does all the necessary checks
2069 function loadURI(uri, referrer, postData, allowThirdPartyFixup)
2072 if (postData === undefined)
2074 var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
2075 if (allowThirdPartyFixup) {
2076 flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
2078 gBrowser.loadURIWithFlags(uri, flags, referrer, null, postData);
2083 function getShortcutOrURI(aURL, aPostDataRef) {
2084 var shortcutURL = null;
2088 var offset = aURL.indexOf(" ");
2090 keyword = aURL.substr(0, offset);
2091 param = aURL.substr(offset + 1);
2097 var engine = Services.search.getEngineByAlias(keyword);
2099 var submission = engine.getSubmission(param);
2100 aPostDataRef.value = submission.postData;
2101 return submission.uri.spec;
2104 [shortcutURL, aPostDataRef.value] =
2105 PlacesUtils.getURLAndPostDataForKeyword(keyword);
2111 if (aPostDataRef.value)
2112 postData = unescape(aPostDataRef.value);
2114 if (/%s/i.test(shortcutURL) || /%s/i.test(postData)) {
2116 const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
2117 var matches = shortcutURL.match(re);
2119 [, shortcutURL, charset] = matches;
2121 // Try to get the saved character-set.
2123 // makeURI throws if URI is invalid.
2124 // Will return an empty string if character-set is not found.
2125 charset = PlacesUtils.history.getCharsetForURI(makeURI(shortcutURL));
2129 var encodedParam = "";
2131 encodedParam = escape(convertFromUnicode(charset, param));
2132 else // Default charset is UTF-8
2133 encodedParam = encodeURIComponent(param);
2135 shortcutURL = shortcutURL.replace(/%s/g, encodedParam).replace(/%S/g, param);
2137 if (/%s/i.test(postData)) // POST keyword
2138 aPostDataRef.value = getPostDataStream(postData, param, encodedParam,
2139 "application/x-www-form-urlencoded");
2142 // This keyword doesn't take a parameter, but one was provided. Just return
2143 // the original URL.
2144 aPostDataRef.value = null;
2152 function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
2153 var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
2154 createInstance(Ci.nsIStringInputStream);
2155 aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
2156 dataStream.data = aStringData;
2158 var mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"].
2159 createInstance(Ci.nsIMIMEInputStream);
2160 mimeStream.addHeader("Content-Type", aType);
2161 mimeStream.addContentLength = true;
2162 mimeStream.setData(dataStream);
2163 return mimeStream.QueryInterface(Ci.nsIInputStream);
2166 function readFromClipboard()
2172 var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
2173 .getService(Components.interfaces.nsIClipboard);
2175 // Create transferable that will transfer the text.
2176 var trans = Components.classes["@mozilla.org/widget/transferable;1"]
2177 .createInstance(Components.interfaces.nsITransferable);
2179 trans.addDataFlavor("text/unicode");
2181 // If available, use selection clipboard, otherwise global one
2182 if (clipboard.supportsSelectionClipboard())
2183 clipboard.getData(trans, clipboard.kSelectionClipboard);
2185 clipboard.getData(trans, clipboard.kGlobalClipboard);
2189 trans.getTransferData("text/unicode", data, dataLen);
2192 data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
2193 url = data.data.substring(0, dataLen.value / 2);
2201 function BrowserViewSourceOfDocument(aDocument)
2206 // Get the document charset
2207 var docCharset = "charset=" + aDocument.characterSet;
2209 // Get the nsIWebNavigation associated with the document
2214 // Get the DOMWindow for the requested document. If the DOMWindow
2215 // cannot be found, then just use the content window...
2217 // XXX: This is a bit of a hack...
2218 win = aDocument.defaultView;
2219 if (win == window) {
2222 ifRequestor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
2224 webNav = ifRequestor.getInterface(nsIWebNavigation);
2226 // If nsIWebNavigation cannot be found, just get the one for the whole
2228 webNav = getWebNavigation();
2231 // Get the 'PageDescriptor' for the current document. This allows the
2232 // view-source to access the cached copy of the content rather than
2233 // refetching it from the network...
2236 var PageLoader = webNav.QueryInterface(Components.interfaces.nsIWebPageDescriptor);
2238 pageCookie = PageLoader.currentDescriptor;
2240 // If no page descriptor is available, just use the view-source URL...
2243 top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
2246 // doc - document to use for source, or null for this window's document
2247 // initialTab - name of the initial tab to display, or null for the first tab
2248 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
2249 function BrowserPageInfo(doc, initialTab, imageElement) {
2250 var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
2251 var windows = Cc['@mozilla.org/appshell/window-mediator;1']
2252 .getService(Ci.nsIWindowMediator)
2253 .getEnumerator("Browser:page-info");
2255 var documentURL = doc ? doc.location : window.content.document.location;
2257 // Check for windows matching the url
2258 while (windows.hasMoreElements()) {
2259 var currentWindow = windows.getNext();
2260 if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
2261 currentWindow.focus();
2262 currentWindow.resetPageInfo(args);
2263 return currentWindow;
2267 // We didn't find a matching window, so open a new one.
2268 return openDialog("chrome://browser/content/pageinfo/pageInfo.xul", "",
2269 "chrome,toolbar,dialog=no,resizable", args);
2273 // Initialize the LeakDetector class.
2274 function LeakDetector(verbose)
2276 this.verbose = verbose;
2279 const NS_LEAKDETECTOR_CONTRACTID = "@mozilla.org/xpcom/leakdetector;1";
2281 if (NS_LEAKDETECTOR_CONTRACTID in Components.classes) {
2283 LeakDetector.prototype = Components.classes[NS_LEAKDETECTOR_CONTRACTID]
2284 .createInstance(Components.interfaces.nsILeakDetector);
2286 LeakDetector.prototype = Object.prototype;
2289 LeakDetector.prototype = Object.prototype;
2292 var leakDetector = new LeakDetector(false);
2294 // Dumps current set of memory leaks.
2295 function dumpMemoryLeaks()
2297 leakDetector.dumpLeaks();
2300 // Traces all objects reachable from the chrome document.
2301 function traceChrome()
2303 leakDetector.traceObject(document, leakDetector.verbose);
2306 // Traces all objects reachable from the content document.
2307 function traceDocument()
2309 // keep the chrome document out of the dump.
2310 leakDetector.markObject(document, true);
2311 leakDetector.traceObject(content, leakDetector.verbose);
2312 leakDetector.markObject(document, false);
2315 // Controls whether or not we do verbose tracing.
2316 function traceVerbose(verbose)
2318 leakDetector.verbose = (verbose == "true");
2322 function URLBarSetURI(aURI) {
2323 var value = gBrowser.userTypedValue;
2326 if (value == null) {
2327 let uri = aURI || getWebNavigation().currentURI;
2329 // Replace initial page URIs with an empty string
2330 // only if there's no opener (bug 370555).
2331 if (gInitialPages.indexOf(uri.spec) != -1)
2332 value = content.opener ? uri.spec : "";
2334 value = losslessDecodeURI(uri);
2336 valid = (uri.spec != "about:blank");
2339 gURLBar.value = value;
2340 SetPageProxyState(valid ? "valid" : "invalid");
2343 function losslessDecodeURI(aURI) {
2344 var value = aURI.spec;
2345 // Try to decode as UTF-8 if there's no encoding sequence that we would break.
2346 if (!/%25(?:3B|2F|3F|3A|40|26|3D|2B|24|2C|23)/i.test(value))
2348 value = decodeURI(value)
2349 // 1. decodeURI decodes %25 to %, which creates unintended
2350 // encoding sequences. Re-encode it, unless it's part of
2351 // a sequence that survived decodeURI, i.e. one for:
2352 // ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#'
2353 // (RFC 3987 section 3.2)
2354 // 2. Re-encode whitespace so that it doesn't get eaten away
2355 // by the location bar (bug 410726).
2356 .replace(/%(?!3B|2F|3F|3A|40|26|3D|2B|24|2C|23)|[\r\n\t]/ig,
2357 encodeURIComponent);
2360 // Encode invisible characters (line and paragraph separator,
2361 // object replacement character) (bug 452979)
2362 value = value.replace(/[\v\x0c\x1c\x1d\x1e\x1f\u2028\u2029\ufffc]/g,
2363 encodeURIComponent);
2365 // Encode default ignorable characters. (bug 546013)
2366 // This includes all bidirectional formatting characters.
2367 // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
2368 value = value.replace(/[\u00ad\u034f\u115f-\u1160\u17b4-\u17b5\u180b-\u180d\u200b-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g,
2369 encodeURIComponent);
2373 function UpdateUrlbarSearchSplitterState()
2375 var splitter = document.getElementById("urlbar-search-splitter");
2376 var urlbar = document.getElementById("urlbar-container");
2377 var searchbar = document.getElementById("search-container");
2380 if (urlbar && searchbar) {
2381 if (urlbar.nextSibling == searchbar)
2382 ibefore = searchbar;
2383 else if (searchbar.nextSibling == urlbar)
2389 splitter = document.createElement("splitter");
2390 splitter.id = "urlbar-search-splitter";
2391 splitter.setAttribute("resizebefore", "flex");
2392 splitter.setAttribute("resizeafter", "flex");
2393 splitter.className = "chromeclass-toolbar-additional";
2395 urlbar.parentNode.insertBefore(splitter, ibefore);
2396 } else if (splitter)
2397 splitter.parentNode.removeChild(splitter);
2400 var LocationBarHelpers = {
2403 _searchBegin: function LocBar_searchBegin() {
2404 function delayedBegin(self) {
2405 self._timeoutID = null;
2406 document.getElementById("urlbar-throbber").setAttribute("busy", "true");
2409 this._timeoutID = setTimeout(delayedBegin, 500, this);
2412 _searchComplete: function LocBar_searchComplete() {
2413 // Did we finish the search before delayedBegin was invoked?
2414 if (this._timeoutID) {
2415 clearTimeout(this._timeoutID);
2416 this._timeoutID = null;
2418 document.getElementById("urlbar-throbber").removeAttribute("busy");
2422 function UpdatePageProxyState()
2424 if (gURLBar && gURLBar.value != gLastValidURLStr)
2425 SetPageProxyState("invalid");
2428 function SetPageProxyState(aState)
2434 gProxyFavIcon = document.getElementById("page-proxy-favicon");
2436 gURLBar.setAttribute("pageproxystate", aState);
2437 gProxyFavIcon.setAttribute("pageproxystate", aState);
2439 // the page proxy state is set to valid via OnLocationChange, which
2440 // gets called when we switch tabs.
2441 if (aState == "valid") {
2442 gLastValidURLStr = gURLBar.value;
2443 gURLBar.addEventListener("input", UpdatePageProxyState, false);
2445 PageProxySetIcon(gBrowser.getIcon());
2446 } else if (aState == "invalid") {
2447 gURLBar.removeEventListener("input", UpdatePageProxyState, false);
2448 PageProxyClearIcon();
2452 function PageProxySetIcon (aURL)
2458 PageProxyClearIcon();
2459 else if (gProxyFavIcon.getAttribute("src") != aURL)
2460 gProxyFavIcon.setAttribute("src", aURL);
2463 function PageProxyClearIcon ()
2465 gProxyFavIcon.removeAttribute("src");
2468 function PageProxyClickHandler(aEvent)
2470 if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
2471 middleMousePaste(aEvent);
2474 function BrowserImport()
2477 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
2478 .getService(Components.interfaces.nsIWindowMediator);
2479 var win = wm.getMostRecentWindow("Browser:MigrationWizard");
2483 window.openDialog("chrome://browser/content/migration/migration.xul",
2484 "migration", "centerscreen,chrome,resizable=no");
2487 window.openDialog("chrome://browser/content/migration/migration.xul",
2488 "migration", "modal,centerscreen,chrome,resizable=no");
2493 * Handle command events bubbling up from error page content
2495 function BrowserOnCommand(event) {
2496 // Don't trust synthetic events
2497 if (!event.isTrusted)
2500 var ot = event.originalTarget;
2501 var errorDoc = ot.ownerDocument;
2503 // If the event came from an ssl error page, it is probably either the "Add
2504 // Exception…" or "Get me out of here!" button
2505 if (/^about:certerror/.test(errorDoc.documentURI)) {
2506 if (ot == errorDoc.getElementById('exceptionDialogButton')) {
2507 var params = { exceptionAdded : false, handlePrivateBrowsing : true };
2510 switch (gPrefService.getIntPref("browser.ssl_override_behavior")) {
2511 case 2 : // Pre-fetch & pre-populate
2512 params.prefetchCert = true;
2513 case 1 : // Pre-populate
2514 params.location = errorDoc.location.href;
2517 Components.utils.reportError("Couldn't get ssl_override pref: " + e);
2520 window.openDialog('chrome://pippki/content/exceptionDialog.xul',
2521 '','chrome,centerscreen,modal', params);
2523 // If the user added the exception cert, attempt to reload the page
2524 if (params.exceptionAdded)
2525 errorDoc.location.reload();
2527 else if (ot == errorDoc.getElementById('getMeOutOfHereButton')) {
2531 else if (/^about:blocked/.test(errorDoc.documentURI)) {
2532 // The event came from a button on a malware/phishing block page
2533 // First check whether it's malware or phishing, so that we can
2534 // use the right strings/links
2535 var isMalware = /e=malwareBlocked/.test(errorDoc.documentURI);
2537 if (ot == errorDoc.getElementById('getMeOutButton')) {
2540 else if (ot == errorDoc.getElementById('reportButton')) {
2541 // This is the "Why is this site blocked" button. For malware,
2542 // we can fetch a site-specific report, for phishing, we redirect
2543 // to the generic page describing phishing protection.
2546 // Get the stop badware "why is this blocked" report url,
2547 // append the current url, and go there.
2549 let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
2550 reportURL += errorDoc.location.href;
2551 content.location = reportURL;
2553 Components.utils.reportError("Couldn't get malware report URL: " + e);
2556 else { // It's a phishing site, not malware
2558 content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
2560 Components.utils.reportError("Couldn't get phishing info URL: " + e);
2564 else if (ot == errorDoc.getElementById('ignoreWarningButton')) {
2565 // Allow users to override and continue through to the site,
2566 // but add a notify bar as a reminder, so that they don't lose
2567 // track after, e.g., tab switching.
2568 gBrowser.loadURIWithFlags(content.location.href,
2569 nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
2572 label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
2573 accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
2574 callback: function() { getMeOutOfHere(); }
2579 title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
2581 label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
2582 accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
2583 callback: function() {
2584 openUILinkIn(safebrowsing.getReportURL('MalwareError'), 'tab');
2588 title = gNavigatorBundle.getString("safebrowsing.reportedWebForgery");
2590 label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
2591 accessKey: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.accessKey"),
2592 callback: function() {
2593 openUILinkIn(safebrowsing.getReportURL('Error'), 'tab');
2598 let notificationBox = gBrowser.getNotificationBox();
2599 let value = "blocked-badware-page";
2601 let previousNotification = notificationBox.getNotificationWithValue(value);
2602 if (previousNotification)
2603 notificationBox.removeNotification(previousNotification);
2605 notificationBox.appendNotification(
2608 "chrome://global/skin/icons/blacklist_favicon.png",
2609 notificationBox.PRIORITY_CRITICAL_HIGH,
2614 else if (/^about:privatebrowsing/.test(errorDoc.documentURI)) {
2615 if (ot == errorDoc.getElementById("startPrivateBrowsing")) {
2616 gPrivateBrowsingUI.toggleMode();
2622 * Re-direct the browser to a known-safe page. This function is
2623 * used when, for example, the user browses to a known malware page
2624 * and is presented with about:blocked. The "Get me out of here!"
2625 * button should take the user to the default start page so that even
2626 * when their own homepage is infected, we can get them somewhere safe.
2628 function getMeOutOfHere() {
2629 // Get the start page from the *default* pref branch, not the user's
2630 var prefs = Cc["@mozilla.org/preferences-service;1"]
2631 .getService(Ci.nsIPrefService).getDefaultBranch(null);
2632 var url = "about:blank";
2634 url = prefs.getComplexValue("browser.startup.homepage",
2635 Ci.nsIPrefLocalizedString).data;
2636 // If url is a pipe-delimited set of pages, just take the first one.
2637 if (url.indexOf("|") != -1)
2638 url = url.split("|")[0];
2640 Components.utils.reportError("Couldn't get homepage pref: " + e);
2642 content.location = url;
2645 function BrowserFullScreen()
2647 window.fullScreen = !window.fullScreen;
2650 function onFullScreen()
2652 FullScreen.toggle();
2655 function getWebNavigation()
2658 return gBrowser.webNavigation;
2664 function BrowserReloadWithFlags(reloadFlags) {
2665 /* First, we'll try to use the session history object to reload so
2666 * that framesets are handled properly. If we're in a special
2667 * window (such as view-source) that has no session history, fall
2668 * back on using the web navigation's reload method.
2671 var webNav = getWebNavigation();
2673 var sh = webNav.sessionHistory;
2675 webNav = sh.QueryInterface(nsIWebNavigation);
2680 webNav.reload(reloadFlags);
2685 var PrintPreviewListener = {
2686 _printPreviewTab: null,
2687 _tabBeforePrintPreview: null,
2689 getPrintPreviewBrowser: function () {
2690 if (!this._printPreviewTab) {
2691 this._tabBeforePrintPreview = gBrowser.selectedTab;
2692 this._printPreviewTab = gBrowser.loadOneTab("about:blank",
2693 { inBackground: false });
2694 gBrowser.selectedTab = this._printPreviewTab;
2696 return gBrowser.getBrowserForTab(this._printPreviewTab);
2698 getSourceBrowser: function () {
2699 return this._tabBeforePrintPreview ?
2700 this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
2702 getNavToolbox: function () {
2705 onEnter: function () {
2706 gInPrintPreviewMode = true;
2707 this._toggleAffectedChrome();
2709 onExit: function () {
2710 gBrowser.selectedTab = this._tabBeforePrintPreview;
2711 this._tabBeforePrintPreview = null;
2712 gBrowser.removeTab(this._printPreviewTab);
2713 this._printPreviewTab = null;
2714 gInPrintPreviewMode = false;
2715 this._toggleAffectedChrome();
2717 _toggleAffectedChrome: function () {
2718 #ifdef MENUBAR_CAN_AUTOHIDE
2719 updateAppButtonDisplay();
2722 gNavToolbox.hidden = gInPrintPreviewMode;
2724 if (gInPrintPreviewMode)
2729 if (this._chromeState.sidebarOpen)
2730 toggleSidebar(this._sidebarCommand);
2732 _hideChrome: function () {
2733 this._chromeState = {};
2735 var sidebar = document.getElementById("sidebar-box");
2736 this._chromeState.sidebarOpen = !sidebar.hidden;
2737 this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
2739 var notificationBox = gBrowser.getNotificationBox();
2740 this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
2741 notificationBox.notificationsHidden = true;
2743 document.getElementById("sidebar").setAttribute("src", "about:blank");
2744 var statusbar = document.getElementById("status-bar");
2745 this._chromeState.statusbarOpen = !statusbar.hidden;
2746 statusbar.hidden = true;
2748 this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
2749 if (gFindBarInitialized)
2752 _showChrome: function () {
2753 if (this._chromeState.notificationsOpen)
2754 gBrowser.getNotificationBox().notificationsHidden = false;
2756 if (this._chromeState.statusbarOpen)
2757 document.getElementById("status-bar").hidden = false;
2759 if (this._chromeState.findOpen)
2764 function getMarkupDocumentViewer()
2766 return gBrowser.markupDocumentViewer;
2770 * Content area tooltip.
2771 * XXX - this must move into XBL binding/equiv! Do not want to pollute
2772 * browser.js with functionality that can be encapsulated into
2773 * browser widget. TEMPORARY!
2775 * NOTE: Any changes to this routine need to be mirrored in DefaultTooltipTextProvider::GetNodeText()
2776 * (located in mozilla/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp)
2777 * which performs the same function, but for embedded clients that
2778 * don't use a XUL/JS layer. It is important that the logic of
2779 * these two routines be kept more or less in sync.
2782 function FillInHTMLTooltip(tipElement)
2785 // Don't show the tooltip if the tooltip node is a XUL element or a document.
2786 if (tipElement.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" ||
2787 !tipElement.ownerDocument)
2790 const XLinkNS = "http://www.w3.org/1999/xlink";
2793 var titleText = null;
2794 var XLinkTitleText = null;
2795 var SVGTitleText = null;
2797 var lookingForSVGTitle = true;
2799 var lookingForSVGTitle = false;
2801 var direction = tipElement.ownerDocument.dir;
2803 while (!titleText && !XLinkTitleText && !SVGTitleText && tipElement) {
2804 if (tipElement.nodeType == Node.ELEMENT_NODE) {
2805 titleText = tipElement.getAttribute("title");
2806 if ((tipElement instanceof HTMLAnchorElement && tipElement.href) ||
2807 (tipElement instanceof HTMLAreaElement && tipElement.href) ||
2808 (tipElement instanceof HTMLLinkElement && tipElement.href)
2810 || (tipElement instanceof SVGAElement && tipElement.hasAttributeNS(XLinkNS, "href"))
2813 XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
2815 if (lookingForSVGTitle &&
2816 !(tipElement instanceof SVGElement &&
2817 tipElement.parentNode instanceof SVGElement &&
2818 !(tipElement.parentNode instanceof SVGForeignObjectElement))) {
2819 lookingForSVGTitle = false;
2821 if (lookingForSVGTitle) {
2822 let length = tipElement.childNodes.length;
2823 for (let i = 0; i < length; i++) {
2824 let childNode = tipElement.childNodes[i];
2825 if (childNode instanceof SVGTitleElement) {
2826 SVGTitleText = childNode.textContent;
2831 var defView = tipElement.ownerDocument.defaultView;
2832 // XXX Work around bug 350679:
2833 // "Tooltips can be fired in documents with no view".
2836 direction = defView.getComputedStyle(tipElement, "")
2837 .getPropertyValue("direction");
2839 tipElement = tipElement.parentNode;
2842 var tipNode = document.getElementById("aHTMLTooltip");
2843 tipNode.style.direction = direction;
2845 [titleText, XLinkTitleText, SVGTitleText].forEach(function (t) {
2846 if (t && /\S/.test(t)) {
2848 // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be
2849 // replaced with spaces, and LFs should be removed entirely.
2850 // XXX Bug 322270: We don't preserve the result of entities like ,
2851 // which should result in a line break in the tooltip, because we can't
2852 // distinguish that from a literal character in the source by this point.
2853 t = t.replace(/[\r\t]/g, ' ');
2854 t = t.replace(/\n/g, '');
2856 tipNode.setAttribute("label", t);
2864 var browserDragAndDrop = {
2865 canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
2867 dragOver: function (aEvent, statusString)
2869 if (this.canDropLink(aEvent)) {
2870 aEvent.preventDefault();
2873 var statusTextFld = document.getElementById("statusbar-display");
2874 statusTextFld.label = gNavigatorBundle.getString(statusString);
2879 drop: function (aEvent, aName) Services.droppedLinkHandler.dropLink(aEvent, aName)
2882 var proxyIconDNDObserver = {
2883 onDragStart: function (aEvent, aXferData, aDragAction)
2885 if (gProxyFavIcon.getAttribute("pageproxystate") != "valid")
2888 var value = content.location.href;
2889 var urlString = value + "\n" + content.document.title;
2890 var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
2892 var dt = aEvent.dataTransfer;
2893 dt.setData("text/x-moz-url", urlString);
2894 dt.setData("text/uri-list", value);
2895 dt.setData("text/plain", value);
2896 dt.setData("text/html", htmlString);
2900 var homeButtonObserver = {
2901 onDrop: function (aEvent)
2903 setTimeout(openHomeDialog, 0, browserDragAndDrop.drop(aEvent, { }));
2906 onDragOver: function (aEvent)
2908 browserDragAndDrop.dragOver(aEvent, "droponhomebutton");
2909 aEvent.dropEffect = "link";
2911 onDragLeave: function (aEvent)
2913 var statusTextFld = document.getElementById("statusbar-display");
2914 statusTextFld.label = "";
2918 function openHomeDialog(aURL)
2920 var promptTitle = gNavigatorBundle.getString("droponhometitle");
2921 var promptMsg = gNavigatorBundle.getString("droponhomemsg");
2922 var pressedVal = Services.prompt.confirmEx(window, promptTitle, promptMsg,
2923 Services.prompt.STD_YES_NO_BUTTONS,
2924 null, null, null, null, {value:0});
2926 if (pressedVal == 0) {
2928 var str = Components.classes["@mozilla.org/supports-string;1"]
2929 .createInstance(Components.interfaces.nsISupportsString);
2931 gPrefService.setComplexValue("browser.startup.homepage",
2932 Components.interfaces.nsISupportsString, str);
2933 var homeButton = document.getElementById("home-button");
2934 homeButton.setAttribute("tooltiptext", aURL);
2936 dump("Failed to set the home page.\n"+ex+"\n");
2941 var bookmarksButtonObserver = {
2942 onDrop: function (aEvent)
2945 let url = browserDragAndDrop.drop(aEvent, name);
2947 PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(url), name);
2951 onDragOver: function (aEvent)
2953 browserDragAndDrop.dragOver(aEvent, "droponbookmarksbutton");
2954 aEvent.dropEffect = "link";
2957 onDragLeave: function (aEvent)
2959 var statusTextFld = document.getElementById("statusbar-display");
2960 statusTextFld.label = "";
2964 var newTabButtonObserver = {
2965 onDragOver: function (aEvent)
2967 browserDragAndDrop.dragOver(aEvent, "droponnewtabbutton");
2970 onDragLeave: function (aEvent)
2972 var statusTextFld = document.getElementById("statusbar-display");
2973 statusTextFld.label = "";
2976 onDrop: function (aEvent)
2978 let url = browserDragAndDrop.drop(aEvent, { });
2980 url = getShortcutOrURI(url, postData);
2982 // allow third-party services to fixup this URL
2983 openNewTabWith(url, null, postData.value, aEvent, true);
2988 var newWindowButtonObserver = {
2989 onDragOver: function (aEvent)
2991 browserDragAndDrop.dragOver(aEvent, "droponnewwindowbutton");
2993 onDragLeave: function (aEvent)
2995 var statusTextFld = document.getElementById("statusbar-display");
2996 statusTextFld.label = "";
2998 onDrop: function (aEvent)
3000 let url = browserDragAndDrop.drop(aEvent, { });
3002 url = getShortcutOrURI(url, postData);
3004 // allow third-party services to fixup this URL
3005 openNewWindowWith(url, null, postData.value, true);
3010 var DownloadsButtonDNDObserver = {
3011 onDragOver: function (aEvent)
3013 var statusTextFld = document.getElementById("statusbar-display");
3014 statusTextFld.label = gNavigatorBundle.getString("dropondownloadsbutton");
3015 var types = aEvent.dataTransfer.types;
3016 if (types.contains("text/x-moz-url") ||
3017 types.contains("text/uri-list") ||
3018 types.contains("text/plain"))
3019 aEvent.preventDefault();
3022 onDragLeave: function (aEvent)
3024 var statusTextFld = document.getElementById("statusbar-display");
3025 statusTextFld.label = "";
3028 onDrop: function (aEvent)
3031 let url = browserDragAndDrop.drop(aEvent, name);
3033 saveURL(url, name, null, true, true);
3037 const DOMLinkHandler = {
3038 handleEvent: function (event) {
3039 switch (event.type) {
3040 case "DOMLinkAdded":
3041 this.onLinkAdded(event);
3045 onLinkAdded: function (event) {
3046 var link = event.originalTarget;
3047 var rel = link.rel && link.rel.toLowerCase();
3048 if (!link || !link.ownerDocument || !rel || !link.href)
3051 var feedAdded = false;
3052 var iconAdded = false;
3053 var searchAdded = false;
3054 var relStrings = rel.split(/\s+/);
3056 for (let i = 0; i < relStrings.length; i++)
3057 rels[relStrings[i]] = true;
3059 for (let relVal in rels) {
3064 if (!rels.feed && rels.alternate && rels.stylesheet)
3067 if (isValidFeed(link, link.ownerDocument.nodePrincipal, rels.feed)) {
3068 FeedHandler.addFeed(link, link.ownerDocument);
3075 if (!gPrefService.getBoolPref("browser.chrome.site_icons"))
3078 var targetDoc = link.ownerDocument;
3079 var uri = makeURI(link.href, targetDoc.characterSet);
3081 if (gBrowser.isFailedIcon(uri))
3084 // Verify that the load of this icon is legal.
3085 // error pages can load their favicon, to be on the safe side,
3086 // only allow chrome:// favicons
3087 const aboutNeterr = /^about:neterror\?/;
3088 const aboutBlocked = /^about:blocked\?/;
3089 const aboutCert = /^about:certerror\?/;
3090 if (!(aboutNeterr.test(targetDoc.documentURI) ||
3091 aboutBlocked.test(targetDoc.documentURI) ||
3092 aboutCert.test(targetDoc.documentURI)) ||
3093 !uri.schemeIs("chrome")) {
3094 var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].
3095 getService(Ci.nsIScriptSecurityManager);
3097 ssm.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri,
3098 Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
3105 var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"].
3106 getService(Ci.nsIContentPolicy);
3108 break; // Refuse to load if we can't do a security check.
3111 // Security says okay, now ask content policy
3112 if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
3113 uri, targetDoc.documentURIObject,
3114 link, link.type, null)
3115 != Ci.nsIContentPolicy.ACCEPT)
3118 var browserIndex = gBrowser.getBrowserIndexForDocument(targetDoc);
3119 // no browser? no favicon.
3120 if (browserIndex == -1)
3123 let tab = gBrowser.tabs[browserIndex];
3124 gBrowser.setIcon(tab, link.href);
3130 var type = link.type && link.type.toLowerCase();
3131 type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
3133 if (type == "application/opensearchdescription+xml" && link.title &&
3134 /^(?:https?|ftp):/i.test(link.href) &&
3135 !gPrivateBrowsingUI.privateBrowsingEnabled) {
3136 var engine = { title: link.title, href: link.href };
3137 BrowserSearch.addEngine(engine, link.ownerDocument);
3147 const BrowserSearch = {
3148 addEngine: function(engine, targetDoc) {
3149 if (!this.searchBar)
3152 var browser = gBrowser.getBrowserForDocument(targetDoc);
3153 // ignore search engines from subframes (see bug 479408)
3157 // Check to see whether we've already added an engine with this title
3158 if (browser.engines) {
3159 if (browser.engines.some(function (e) e.title == engine.title))
3163 // Append the URI and an appropriate title to the browser data.
3164 // Use documentURIObject in the check for shouldLoadFavIcon so that we
3165 // do the right thing with about:-style error pages. Bug 453442
3167 if (gBrowser.shouldLoadFavIcon(targetDoc.documentURIObject))
3168 iconURL = targetDoc.documentURIObject.prePath + "/favicon.ico";
3171 // If this engine (identified by title) is already in the list, add it
3172 // to the list of hidden engines rather than to the main list.
3173 // XXX This will need to be changed when engines are identified by URL;
3175 if (Services.search.getEngineByName(engine.title))
3178 var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
3180 engines.push({ uri: engine.href,
3181 title: engine.title,
3185 browser.hiddenEngines = engines;
3187 browser.engines = engines;
3188 if (browser == gBrowser.selectedBrowser)
3189 this.updateSearchButton();
3194 * Update the browser UI to show whether or not additional engines are
3195 * available when a page is loaded or the user switches tabs to a page that
3196 * has search engines.
3198 updateSearchButton: function() {
3199 var searchBar = this.searchBar;
3201 // The search bar binding might not be applied even though the element is
3202 // in the document (e.g. when the navigation toolbar is hidden), so check
3203 // for .searchButton specifically.
3204 if (!searchBar || !searchBar.searchButton)
3207 var engines = gBrowser.selectedBrowser.engines;
3208 if (engines && engines.length > 0)
3209 searchBar.searchButton.setAttribute("addengines", "true");
3211 searchBar.searchButton.removeAttribute("addengines");
3215 * Gives focus to the search bar, if it is present on the toolbar, or loads
3216 * the default engine's search form otherwise. For Mac, opens a new window
3217 * or focuses an existing window, if necessary.
3219 webSearch: function BrowserSearch_webSearch() {
3221 if (window.location.href != getBrowserURL()) {
3222 var win = getTopWin();
3224 // If there's an open browser window, it should handle this command
3226 win.BrowserSearch.webSearch();
3228 // If there are no open browser windows, open a new one
3230 // This needs to be in a timeout so that we don't end up refocused
3232 function webSearchCallback() {
3233 setTimeout(BrowserSearch.webSearch, 0);
3236 win = window.openDialog("chrome://browser/content/", "_blank",
3237 "chrome,all,dialog=no", "about:blank");
3238 win.addEventListener("load", webSearchCallback, false);
3243 var searchBar = this.searchBar;
3244 if (searchBar && window.fullScreen)
3245 FullScreen.mouseoverToggle(true);
3247 if (isElementVisible(searchBar)) {
3251 openUILinkIn(Services.search.defaultEngine.searchForm, "current");
3256 * Loads a search results page, given a set of search terms. Uses the current
3257 * engine if the search bar is visible, or the default engine otherwise.
3260 * The search terms to use for the search.
3263 * Boolean indicating whether or not the search should load in a new
3266 loadSearch: function BrowserSearch_search(searchText, useNewTab) {
3269 // If the search bar is visible, use the current engine, otherwise, fall
3270 // back to the default engine.
3271 if (isElementVisible(this.searchBar))
3272 engine = Services.search.currentEngine;
3274 engine = Services.search.defaultEngine;
3276 var submission = engine.getSubmission(searchText); // HTML response
3278 // getSubmission can return null if the engine doesn't have a URL
3279 // with a text/html response type. This is unlikely (since
3280 // SearchService._addEngineToStore() should fail for such an engine),
3281 // but let's be on the safe side.
3286 gBrowser.loadOneTab(submission.uri.spec, {
3287 postData: submission.postData,
3288 relatedToCurrent: true});
3290 loadURI(submission.uri.spec, null, submission.postData, false);
3294 * Returns the search bar element if it is present in the toolbar, null otherwise.
3297 return document.getElementById("searchbar");
3300 loadAddEngines: function BrowserSearch_loadAddEngines() {
3301 var newWindowPref = gPrefService.getIntPref("browser.link.open_newwindow");
3302 var where = newWindowPref == 3 ? "tab" : "window";
3303 var regionBundle = document.getElementById("bundle_browser_region");
3304 var searchEnginesURL = formatURL("browser.search.searchEnginesURL", true);
3305 openUILinkIn(searchEnginesURL, where);
3309 function FillHistoryMenu(aParent) {
3310 // Lazily add the hover listeners on first showing and never remove them
3311 if (!aParent.hasStatusListener) {
3312 // Show history item's uri in the status bar when hovering, and clear on exit
3313 aParent.addEventListener("DOMMenuItemActive", function(aEvent) {
3314 // Only the current page should have the checked attribute, so skip it
3315 if (!aEvent.target.hasAttribute("checked"))
3316 XULBrowserWindow.setOverLink(aEvent.target.getAttribute("uri"));
3318 aParent.addEventListener("DOMMenuItemInactive", function() {
3319 XULBrowserWindow.setOverLink("");
3322 aParent.hasStatusListener = true;
3325 // Remove old entries if any
3326 var children = aParent.childNodes;
3327 for (var i = children.length - 1; i >= 0; --i) {
3328 if (children[i].hasAttribute("index"))
3329 aParent.removeChild(children[i]);
3332 var webNav = getWebNavigation();
3333 var sessionHistory = webNav.sessionHistory;
3335 var count = sessionHistory.count;
3336 if (count <= 1) // don't display the popup for a single item
3339 const MAX_HISTORY_MENU_ITEMS = 15;
3340 var index = sessionHistory.index;
3341 var half_length = Math.floor(MAX_HISTORY_MENU_ITEMS / 2);
3342 var start = Math.max(index - half_length, 0);
3343 var end = Math.min(start == 0 ? MAX_HISTORY_MENU_ITEMS : index + half_length + 1, count);
3345 start = Math.max(count - MAX_HISTORY_MENU_ITEMS, 0);
3347 var tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
3348 var tooltipCurrent = gNavigatorBundle.getString("tabHistory.current");
3349 var tooltipForward = gNavigatorBundle.getString("tabHistory.goForward");
3351 for (var j = end - 1; j >= start; j--) {
3352 let item = document.createElement("menuitem");
3353 let entry = sessionHistory.getEntryAtIndex(j, false);
3354 let uri = entry.URI.spec;
3356 item.setAttribute("uri", uri);
3357 item.setAttribute("label", entry.title || uri);
3358 item.setAttribute("index", j);
3362 let iconURL = Cc["@mozilla.org/browser/favicon-service;1"]
3363 .getService(Ci.nsIFaviconService)
3364 .getFaviconForPage(entry.URI).spec;
3365 item.style.listStyleImage = "url(" + iconURL + ")";
3370 item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
3371 item.setAttribute("tooltiptext", tooltipBack);
3372 } else if (j == index) {
3373 item.setAttribute("type", "radio");
3374 item.setAttribute("checked", "true");
3375 item.className = "unified-nav-current";
3376 item.setAttribute("tooltiptext", tooltipCurrent);
3378 item.className = "unified-nav-forward menuitem-iconic menuitem-with-favicon";
3379 item.setAttribute("tooltiptext", tooltipForward);
3382 aParent.appendChild(item);
3387 function addToUrlbarHistory(aUrlToAdd) {
3389 aUrlToAdd.indexOf(" ") == -1 &&
3390 !/[\x00-\x1F]/.test(aUrlToAdd))
3391 PlacesUIUtils.markPageAsTyped(aUrlToAdd);
3394 function toJavaScriptConsole()
3396 toOpenWindowByType("global:console", "chrome://global/content/console.xul");
3399 function BrowserDownloadsUI()
3401 Cc["@mozilla.org/download-manager-ui;1"].
3402 getService(Ci.nsIDownloadManagerUI).show(window);
3405 function toOpenWindowByType(inType, uri, features)
3407 var topWindow = Services.wm.getMostRecentWindow(inType);
3412 window.open(uri, "_blank", features);
3414 window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
3417 function OpenBrowserWindow()
3419 var charsetArg = new String();
3420 var handler = Components.classes["@mozilla.org/browser/clh;1"]
3421 .getService(Components.interfaces.nsIBrowserHandler);
3422 var defaultArgs = handler.defaultArgs;
3423 var wintype = document.documentElement.getAttribute('windowtype');
3425 // if and only if the current window is a browser window and it has a document with a character
3426 // set, then extract the current charset menu setting from the current document and use it to
3427 // initialize the new browser window...
3429 if (window && (wintype == "navigator:browser") && window.content && window.content.document)
3431 var DocCharset = window.content.document.characterSet;
3432 charsetArg = "charset="+DocCharset;
3434 //we should "inherit" the charset menu setting in a new window
3435 win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no", defaultArgs, charsetArg);
3437 else // forget about the charset information.
3439 win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no", defaultArgs);
3445 var gCustomizeSheet = false;
3446 // Returns a reference to the window in which the toolbar
3447 // customization document is loaded.
3448 function BrowserCustomizeToolbar()
3450 // Disable the toolbar context menu items
3451 var menubar = document.getElementById("main-menubar");
3452 for (var i = 0; i < menubar.childNodes.length; ++i)
3453 menubar.childNodes[i].setAttribute("disabled", true);
3455 var cmd = document.getElementById("cmd_CustomizeToolbars");
3456 cmd.setAttribute("disabled", "true");
3458 var splitter = document.getElementById("urlbar-search-splitter");
3460 splitter.parentNode.removeChild(splitter);
3462 CombinedStopReload.uninit();
3464 PlacesToolbarHelper.customizeStart();
3465 BookmarksMenuButton.customizeStart();
3467 var customizeURL = "chrome://global/content/customizeToolbar.xul";
3468 gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
3470 if (gCustomizeSheet) {
3471 var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
3472 var panel = document.getElementById("customizeToolbarSheetPopup");
3473 sheetFrame.hidden = false;
3474 sheetFrame.toolbox = gNavToolbox;
3475 sheetFrame.panel = panel;
3477 // The document might not have been loaded yet, if this is the first time.
3478 // If it is already loaded, reload it so that the onload initialization code
3480 if (sheetFrame.getAttribute("src") == customizeURL)
3481 sheetFrame.contentWindow.location.reload()
3483 sheetFrame.setAttribute("src", customizeURL);
3485 // Open the panel, but make it invisible until the iframe has loaded so
3486 // that the user doesn't see a white flash.
3487 panel.style.visibility = "hidden";
3488 gNavToolbox.addEventListener("beforecustomization", function () {
3489 gNavToolbox.removeEventListener("beforecustomization", arguments.callee, false);
3490 panel.style.removeProperty("visibility");
3492 panel.openPopup(gNavToolbox, "after_start", 0, 0);
3493 return sheetFrame.contentWindow;
3495 return window.openDialog(customizeURL,
3497 "chrome,titlebar,toolbar,location,resizable,dependent",
3502 function BrowserToolboxCustomizeDone(aToolboxChanged) {
3503 if (gCustomizeSheet) {
3504 document.getElementById("customizeToolbarSheetIFrame").hidden = true;
3505 document.getElementById("customizeToolbarSheetPopup").hidePopup();
3508 // Update global UI elements that may have been added or removed
3509 if (aToolboxChanged) {
3510 gURLBar = document.getElementById("urlbar");
3512 gProxyFavIcon = document.getElementById("page-proxy-favicon");
3513 gHomeButton.updateTooltip();
3514 gIdentityHandler._cacheElements();
3515 window.XULBrowserWindow.init();
3517 var backForwardDropmarker = document.getElementById("back-forward-dropmarker");
3518 if (backForwardDropmarker)
3519 backForwardDropmarker.disabled =
3520 document.getElementById('Browser:Back').hasAttribute('disabled') &&
3521 document.getElementById('Browser:Forward').hasAttribute('disabled');
3524 updateEditUIVisibility();
3528 PlacesToolbarHelper.customizeDone();
3529 BookmarksMenuButton.customizeDone();
3531 UpdateUrlbarSearchSplitterState();
3533 CombinedStopReload.init();
3535 // Update the urlbar
3538 XULBrowserWindow.asyncUpdateUI();
3539 PlacesStarButton.updateState();
3542 // Re-enable parts of the UI we disabled during the dialog
3543 var menubar = document.getElementById("main-menubar");
3544 for (var i = 0; i < menubar.childNodes.length; ++i)
3545 menubar.childNodes[i].setAttribute("disabled", false);
3546 var cmd = document.getElementById("cmd_CustomizeToolbars");
3547 cmd.removeAttribute("disabled");
3550 // make sure to re-enable click-and-hold
3551 if (!getBoolPref("ui.click_hold_context_menus", false))
3552 SetClickAndHoldHandlers();
3555 // XXX Shouldn't have to do this, but I do
3556 if (!gCustomizeSheet)
3560 function BrowserToolboxCustomizeChange() {
3561 gHomeButton.updatePersonalToolbarStyle();
3562 BookmarksMenuButton.customizeChange();
3567 * Update the global flag that tracks whether or not any edit UI (the Edit menu,
3568 * edit-related items in the context menu, and edit-related toolbar buttons
3569 * is visible, then update the edit commands' enabled state accordingly. We use
3570 * this flag to skip updating the edit commands on focus or selection changes
3571 * when no UI is visible to improve performance (including pageload performance,
3572 * since focus changes when you load a new page).
3574 * If UI is visible, we use goUpdateGlobalEditMenuItems to set the commands'
3575 * enabled state so the UI will reflect it appropriately.
3577 * If the UI isn't visible, we enable all edit commands so keyboard shortcuts
3578 * still work and just lazily disable them as needed when the user presses a
3581 * This doesn't work on Mac, since Mac menus flash when users press their
3582 * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
3583 * and we need to always update the edit commands. Thus on Mac this function
3586 function updateEditUIVisibility()
3589 let editMenuPopupState = document.getElementById("menu_EditPopup").state;
3590 let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
3591 let placesContextMenuPopupState = document.getElementById("placesContext").state;
3593 // The UI is visible if the Edit menu is opening or open, if the context menu
3594 // is open, or if the toolbar has been customized to include the Cut, Copy,
3595 // or Paste toolbar buttons.
3596 gEditUIVisible = editMenuPopupState == "showing" ||
3597 editMenuPopupState == "open" ||
3598 contextMenuPopupState == "showing" ||
3599 contextMenuPopupState == "open" ||
3600 placesContextMenuPopupState == "showing" ||
3601 placesContextMenuPopupState == "open" ||
3602 document.getElementById("cut-button") ||
3603 document.getElementById("copy-button") ||
3604 document.getElementById("paste-button") ? true : false;
3606 // If UI is visible, update the edit commands' enabled state to reflect
3607 // whether or not they are actually enabled for the current focus/selection.
3609 goUpdateGlobalEditMenuItems();
3611 // Otherwise, enable all commands, so that keyboard shortcuts still work,
3612 // then lazily determine their actual enabled state when the user presses
3613 // a keyboard shortcut.
3615 goSetCommandEnabled("cmd_undo", true);
3616 goSetCommandEnabled("cmd_redo", true);
3617 goSetCommandEnabled("cmd_cut", true);
3618 goSetCommandEnabled("cmd_copy", true);
3619 goSetCommandEnabled("cmd_paste", true);
3620 goSetCommandEnabled("cmd_selectAll", true);
3621 goSetCommandEnabled("cmd_delete", true);
3622 goSetCommandEnabled("cmd_switchTextDirection", true);
3629 _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
3632 // show/hide all menubars, toolbars, and statusbars (except the full screen toolbar)
3633 this.showXULChrome("toolbar", window.fullScreen);
3634 this.showXULChrome("statusbar", window.fullScreen);
3635 document.getElementById("View:FullScreen").setAttribute("checked", !window.fullScreen);
3637 if (!window.fullScreen) {
3638 // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
3639 // This will help simulate the "collapse" metaphor while also requiring less code and
3640 // events than raw listening of mouse coords.
3641 let fullScrToggler = document.getElementById("fullscr-toggler");
3642 if (!fullScrToggler) {
3643 fullScrToggler = document.createElement("hbox");
3644 fullScrToggler.id = "fullscr-toggler";
3645 fullScrToggler.collapsed = true;
3646 gNavToolbox.parentNode.insertBefore(fullScrToggler, gNavToolbox.nextSibling);
3648 fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
3649 fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
3651 if (gPrefService.getBoolPref("browser.fullscreen.autohide"))
3652 gBrowser.mPanelContainer.addEventListener("mousemove",
3653 this._collapseCallback, false);
3655 document.addEventListener("keypress", this._keyToggleCallback, false);
3656 document.addEventListener("popupshown", this._setPopupOpen, false);
3657 document.addEventListener("popuphidden", this._setPopupOpen, false);
3658 this._shouldAnimate = true;
3659 this.mouseoverToggle(false);
3662 gPrefService.addObserver("browser.fullscreen", this, false);
3665 // The user may quit fullscreen during an animation
3666 clearInterval(this._animationInterval);
3667 clearTimeout(this._animationTimeout);
3668 gNavToolbox.style.marginTop = "0px";
3669 if (this._isChromeCollapsed)
3670 this.mouseoverToggle(true);
3671 this._isAnimating = false;
3672 // This is needed if they use the context menu to quit fullscreen
3673 this._isPopupOpen = false;
3679 cleanup: function () {
3680 if (window.fullScreen) {
3681 gBrowser.mPanelContainer.removeEventListener("mousemove",
3682 this._collapseCallback, false);
3683 document.removeEventListener("keypress", this._keyToggleCallback, false);
3684 document.removeEventListener("popupshown", this._setPopupOpen, false);
3685 document.removeEventListener("popuphidden", this._setPopupOpen, false);
3686 gPrefService.removeObserver("browser.fullscreen", this);
3688 let fullScrToggler = document.getElementById("fullscr-toggler");
3689 if (fullScrToggler) {
3690 fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
3691 fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
3696 observe: function(aSubject, aTopic, aData)
3698 if (aData == "browser.fullscreen.autohide") {
3699 if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
3700 gBrowser.mPanelContainer.addEventListener("mousemove",
3701 this._collapseCallback, false);
3704 gBrowser.mPanelContainer.removeEventListener("mousemove",
3705 this._collapseCallback, false);
3711 _expandCallback: function()
3713 FullScreen.mouseoverToggle(true);
3715 _collapseCallback: function()
3717 FullScreen.mouseoverToggle(false);
3719 _keyToggleCallback: function(aEvent)
3721 // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
3722 // should provide a way to collapse them too.
3723 if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
3724 FullScreen._shouldAnimate = false;
3725 FullScreen.mouseoverToggle(false, true);
3727 // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
3728 else if (aEvent.keyCode == aEvent.DOM_VK_F6)
3729 FullScreen.mouseoverToggle(true);
3732 // Checks whether we are allowed to collapse the chrome
3733 _isPopupOpen: false,
3734 _isChromeCollapsed: false,
3735 _safeToCollapse: function(forceHide)
3737 if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
3740 // a popup menu is open in chrome: don't collapse chrome
3741 if (!forceHide && this._isPopupOpen)
3744 // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
3745 if (document.commandDispatcher.focusedElement &&
3746 document.commandDispatcher.focusedElement.ownerDocument == document &&
3747 document.commandDispatcher.focusedElement.localName == "input") {
3749 // hidden textboxes that still have focus are bad bad bad
3750 document.commandDispatcher.focusedElement.blur();
3757 _setPopupOpen: function(aEvent)
3759 // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
3760 // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
3761 // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
3762 // toggles chrome when moving mouse to the top, it doesn't go away again.
3763 if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
3764 aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
3765 FullScreen._isPopupOpen = true;
3766 else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
3767 aEvent.target.localName != "window")
3768 FullScreen._isPopupOpen = false;
3771 // Autohide helpers for the context menu item
3772 getAutohide: function(aItem)
3774 aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
3776 setAutohide: function()
3778 gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
3781 // Animate the toolbars disappearing
3782 _shouldAnimate: true,
3783 _isAnimating: false,
3784 _animationTimeout: null,
3785 _animationInterval: null,
3786 _animateUp: function()
3788 // check again, the user may have done something before the animation was due to start
3789 if (!window.fullScreen || !FullScreen._safeToCollapse(false)) {
3790 FullScreen._isAnimating = false;
3791 FullScreen._shouldAnimate = true;
3795 var animateFrameAmount = 2;
3796 function animateUpFrame() {
3797 animateFrameAmount *= 2;
3798 if (animateFrameAmount >= gNavToolbox.boxObject.height) {
3799 // We've animated enough
3800 clearInterval(FullScreen._animationInterval);
3801 gNavToolbox.style.marginTop = "0px";
3802 FullScreen._isAnimating = false;
3803 FullScreen._shouldAnimate = false; // Just to make sure
3804 FullScreen.mouseoverToggle(false);
3807 gNavToolbox.style.marginTop = (animateFrameAmount * -1) + "px";
3810 FullScreen._animationInterval = setInterval(animateUpFrame, 70);
3813 mouseoverToggle: function(aShow, forceHide)
3815 // Don't do anything if:
3816 // a) we're already in the state we want,
3817 // b) we're animating and will become collapsed soon, or
3818 // c) we can't collapse because it would be undesirable right now
3819 if (aShow != this._isChromeCollapsed || (!aShow && this._isAnimating) ||
3820 (!aShow && !this._safeToCollapse(forceHide)))
3823 // browser.fullscreen.animateUp
3824 // 0 - never animate up
3825 // 1 - animate only for first collapse after entering fullscreen (default for perf's sake)
3826 // 2 - animate every time it collapses
3827 if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 0)
3828 this._shouldAnimate = false;
3830 if (!aShow && this._shouldAnimate) {
3831 this._isAnimating = true;
3832 this._shouldAnimate = false;
3833 this._animationTimeout = setTimeout(this._animateUp, 800);
3837 // The chrome is collapsed so don't spam needless mousemove events
3839 gBrowser.mPanelContainer.addEventListener("mousemove",
3840 this._collapseCallback, false);
3843 gBrowser.mPanelContainer.removeEventListener("mousemove",
3844 this._collapseCallback, false);
3847 // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox,
3848 // so we just move it off-screen instead. See bug 430687.
3849 gNavToolbox.style.marginTop =
3850 aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px";
3852 document.getElementById("fullscr-toggler").collapsed = aShow;
3853 this._isChromeCollapsed = !aShow;
3854 if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2)
3855 this._shouldAnimate = true;
3858 showXULChrome: function(aTag, aShow)
3860 var els = document.getElementsByTagNameNS(this._XULNS, aTag);
3862 for (var i = 0; i < els.length; ++i) {
3863 // XXX don't interfere with previously collapsed toolbars
3864 if (els[i].getAttribute("fullscreentoolbar") == "true") {
3867 var toolbarMode = els[i].getAttribute("mode");
3868 if (toolbarMode != "text") {
3869 els[i].setAttribute("saved-mode", toolbarMode);
3870 els[i].setAttribute("saved-iconsize",
3871 els[i].getAttribute("iconsize"));
3872 els[i].setAttribute("mode", "icons");
3873 els[i].setAttribute("iconsize", "small");
3876 // Give the main nav bar the fullscreen context menu, otherwise remove it
3877 // to prevent breakage
3878 els[i].setAttribute("saved-context",
3879 els[i].getAttribute("context"));
3880 if (els[i].id == "nav-bar")
3881 els[i].setAttribute("context", "autohide-context");
3883 els[i].removeAttribute("context");
3885 // Set the inFullscreen attribute to allow specific styling
3886 // in fullscreen mode
3887 els[i].setAttribute("inFullscreen", true);
3890 function restoreAttr(attrName) {
3891 var savedAttr = "saved-" + attrName;
3892 if (els[i].hasAttribute(savedAttr)) {
3893 els[i].setAttribute(attrName, els[i].getAttribute(savedAttr));
3894 els[i].removeAttribute(savedAttr);
3898 restoreAttr("mode");
3899 restoreAttr("iconsize");
3900 restoreAttr("context");
3902 els[i].removeAttribute("inFullscreen");
3905 // use moz-collapsed so it doesn't persist hidden/collapsed,
3906 // so that new windows don't have missing toolbars
3908 els[i].removeAttribute("moz-collapsed");
3910 els[i].setAttribute("moz-collapsed", "true");
3915 gNavToolbox.removeAttribute("inFullscreen");
3916 document.documentElement.removeAttribute("inFullscreen");
3918 gNavToolbox.setAttribute("inFullscreen", true);
3919 document.documentElement.setAttribute("inFullscreen", true);
3922 var controls = document.getElementsByAttribute("fullscreencontrol", "true");
3923 for (var i = 0; i < controls.length; ++i)
3924 controls[i].hidden = aShow;
3929 * Returns true if |aMimeType| is text-based, false otherwise.
3932 * The MIME type to check.
3934 * If adding types to this function, please also check the similar
3935 * function in findbar.xml
3937 function mimeTypeIsTextBased(aMimeType)
3939 return /^text\/|\+xml$/.test(aMimeType) ||
3940 aMimeType == "application/x-javascript" ||
3941 aMimeType == "application/javascript" ||
3942 aMimeType == "application/xml" ||
3943 aMimeType == "mozilla.application/cached-xul";
3946 var XULBrowserWindow = {
3947 // Stored Status, Link and Loading values
3951 jsDefaultStatus: "",
3957 _progressCollapseTimer: 0,
3959 QueryInterface: function (aIID) {
3960 if (aIID.equals(Ci.nsIWebProgressListener) ||
3961 aIID.equals(Ci.nsIWebProgressListener2) ||
3962 aIID.equals(Ci.nsISupportsWeakReference) ||
3963 aIID.equals(Ci.nsIXULBrowserWindow) ||
3964 aIID.equals(Ci.nsISupports))
3966 throw Cr.NS_NOINTERFACE;
3969 get statusMeter () {
3970 delete this.statusMeter;
3971 return this.statusMeter = document.getElementById("statusbar-icon");
3973 get stopCommand () {
3974 delete this.stopCommand;
3975 return this.stopCommand = document.getElementById("Browser:Stop");
3977 get reloadCommand () {
3978 delete this.reloadCommand;
3979 return this.reloadCommand = document.getElementById("Browser:Reload");
3981 get statusTextField () {
3982 delete this.statusTextField;
3983 return this.statusTextField = document.getElementById("statusbar-display");
3985 get securityButton () {
3986 delete this.securityButton;
3987 return this.securityButton = document.getElementById("security-button");
3990 delete this.isImage;
3991 return this.isImage = document.getElementById("isImage");
3994 delete this._uriFixup;
3995 return this._uriFixup = Cc["@mozilla.org/docshell/urifixup;1"]
3996 .getService(Ci.nsIURIFixup);
4000 this.throbberElement = document.getElementById("navigator-throbber");
4002 // Initialize the security button's state and tooltip text. Remember to reset
4003 // _hostChanged, otherwise onSecurityChange will short circuit.
4004 var securityUI = gBrowser.securityUI;
4005 this._hostChanged = true;
4006 this.onSecurityChange(null, null, securityUI.state);
4009 destroy: function () {
4010 // XXXjag to avoid leaks :-/, see bug 60729
4011 delete this.throbberElement;
4012 delete this.statusMeter;
4013 delete this.stopCommand;
4014 delete this.reloadCommand;
4015 delete this.statusTextField;
4016 delete this.securityButton;
4017 delete this.statusText;
4020 setJSStatus: function (status) {
4021 this.jsStatus = status;
4022 this.updateStatusField();
4025 setJSDefaultStatus: function (status) {
4026 this.jsDefaultStatus = status;
4027 this.updateStatusField();
4030 setDefaultStatus: function (status) {
4031 this.defaultStatus = status;
4032 this.updateStatusField();
4035 setOverLink: function (link, b) {
4036 // Encode bidirectional formatting characters.
4037 // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
4038 this.overLink = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
4039 encodeURIComponent);
4040 this.updateStatusField();
4043 updateStatusField: function () {
4044 var text = this.overLink || this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
4046 // check the current value so we don't trigger an attribute change
4047 // and cause needless (slow!) UI updates
4048 if (this.statusText != text) {
4049 this.statusTextField.label = text;
4050 this.statusText = text;
4054 onLinkIconAvailable: function (aIconURL) {
4055 if (gProxyFavIcon && gBrowser.userTypedValue === null)
4056 PageProxySetIcon(aIconURL); // update the favicon in the URL bar
4059 onProgressChange: function (aWebProgress, aRequest,
4060 aCurSelfProgress, aMaxSelfProgress,
4061 aCurTotalProgress, aMaxTotalProgress) {
4062 // Check this._busyUI to be safe, because we don't want to update
4063 // the progress meter when restoring a page from bfcache.
4064 if (aMaxTotalProgress > 0 && this._busyUI) {
4065 // This is highly optimized. Don't touch this code unless
4066 // you are intimately familiar with the cost of setting
4067 // attrs on XUL elements. -- hyatt
4068 let percentage = (aCurTotalProgress * 100) / aMaxTotalProgress;
4069 this.statusMeter.value = percentage;
4073 onProgressChange64: function (aWebProgress, aRequest,
4074 aCurSelfProgress, aMaxSelfProgress,
4075 aCurTotalProgress, aMaxTotalProgress) {
4076 return this.onProgressChange(aWebProgress, aRequest,
4077 aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
4081 onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
4082 const nsIWebProgressListener = Ci.nsIWebProgressListener;
4083 const nsIChannel = Ci.nsIChannel;
4084 if (aStateFlags & nsIWebProgressListener.STATE_START &&
4085 aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
4087 if (aRequest && aWebProgress.DOMWindow == content)
4088 this.startDocumentLoad(aRequest);
4092 if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
4093 this._busyUI = true;
4095 // Turn the throbber on.
4096 if (this.throbberElement)
4097 this.throbberElement.setAttribute("busy", "true");
4099 // Turn the status meter on.
4100 this.statusMeter.value = 0; // be sure to clear the progress bar
4101 if (this._progressCollapseTimer) {
4102 clearTimeout(this._progressCollapseTimer);
4103 this._progressCollapseTimer = 0;
4106 this.statusMeter.parentNode.collapsed = false;
4108 // XXX: This needs to be based on window activity...
4109 this.stopCommand.removeAttribute("disabled");
4110 CombinedStopReload.switchToStop();
4113 else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
4114 if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
4115 if (aWebProgress.DOMWindow == content) {
4117 this.endDocumentLoad(aRequest, aStatus);
4118 if (!gBrowser.mTabbedMode && !gBrowser.getIcon())
4119 gBrowser.useDefaultIcon(gBrowser.selectedTab);
4123 // This (thanks to the filter) is a network stop or the last
4124 // request stop outside of loading the document, stop throbbers
4125 // and progress bars and such
4129 // Get the URI either from a channel or a pseudo-object
4130 if (aRequest instanceof nsIChannel || "URI" in aRequest) {
4131 location = aRequest.URI;
4133 // For keyword URIs clear the user typed value since they will be changed into real URIs
4134 if (location.scheme == "keyword" && aWebProgress.DOMWindow == content)
4135 gBrowser.userTypedValue = null;
4137 if (location.spec != "about:blank") {
4139 case Components.results.NS_BINDING_ABORTED:
4140 msg = gNavigatorBundle.getString("nv_stopped");
4142 case Components.results.NS_ERROR_NET_TIMEOUT:
4143 msg = gNavigatorBundle.getString("nv_timeout");
4148 // If msg is false then we did not have an error (channel may have
4149 // been null, in the case of a stray image load).
4150 if (!msg && (!location || location.spec != "about:blank"))
4151 msg = gNavigatorBundle.getString("nv_done");
4154 this.setDefaultStatus(msg);
4156 // Disable menu entries for images, enable otherwise
4157 if (content.document && mimeTypeIsTextBased(content.document.contentType))
4158 this.isImage.removeAttribute('disabled');
4160 this.isImage.setAttribute('disabled', 'true');
4163 this.isBusy = false;
4166 this._busyUI = false;
4168 // Turn the progress meter and throbber off.
4169 this._progressCollapseTimer = setTimeout(function (self) {
4170 self.statusMeter.parentNode.collapsed = true;
4171 self._progressCollapseTimer = 0;
4174 if (this.throbberElement)
4175 this.throbberElement.removeAttribute("busy");
4177 this.stopCommand.setAttribute("disabled", "true");
4178 CombinedStopReload.switchToReload(aRequest instanceof Ci.nsIRequest);
4183 onLocationChange: function (aWebProgress, aRequest, aLocationURI) {
4184 var location = aLocationURI ? aLocationURI.spec : "";
4185 this._hostChanged = true;
4187 if (document.tooltipNode) {
4188 // Optimise for the common case
4189 if (aWebProgress.DOMWindow == content) {
4190 document.getElementById("aHTMLTooltip").hidePopup();
4191 document.tooltipNode = null;
4194 for (let tooltipWindow =
4195 document.tooltipNode.ownerDocument.defaultView;
4196 tooltipWindow != tooltipWindow.parent;
4197 tooltipWindow = tooltipWindow.parent) {
4198 if (tooltipWindow == aWebProgress.DOMWindow) {
4199 document.getElementById("aHTMLTooltip").hidePopup();
4200 document.tooltipNode = null;
4207 // This code here does not compare uris exactly when determining
4208 // whether or not the message should be hidden since the message
4209 // may be prematurely hidden when an install is invoked by a click
4210 // on a link that looks like this:
4212 // <a href="#" onclick="return install();">Install Foo</a>
4214 // - which fires a onLocationChange message to uri + '#'...
4215 var selectedBrowser = gBrowser.selectedBrowser;
4216 if (selectedBrowser.lastURI) {
4217 let oldSpec = selectedBrowser.lastURI.spec;
4218 let oldIndexOfHash = oldSpec.indexOf("#");
4219 if (oldIndexOfHash != -1)
4220 oldSpec = oldSpec.substr(0, oldIndexOfHash);
4221 let newSpec = location;
4222 let newIndexOfHash = newSpec.indexOf("#");
4223 if (newIndexOfHash != -1)
4224 newSpec = newSpec.substr(0, newSpec.indexOf("#"));
4225 if (newSpec != oldSpec) {
4226 // Remove all the notifications, except for those which want to
4227 // persist across the first location change.
4228 let nBox = gBrowser.getNotificationBox(selectedBrowser);
4229 nBox.removeTransientNotifications();
4231 // Only need to call locationChange if the PopupNotifications object
4232 // for this window has already been initialized (i.e. its getter no
4234 if (!__lookupGetter__("PopupNotifications"))
4235 PopupNotifications.locationChange();
4239 // Disable menu entries for images, enable otherwise
4240 if (content.document && mimeTypeIsTextBased(content.document.contentType))
4241 this.isImage.removeAttribute('disabled');
4243 this.isImage.setAttribute('disabled', 'true');
4245 this.setOverLink("", null);
4247 // We should probably not do this if the value has changed since the user
4249 // Update urlbar only if a new page was loaded on the primary content area
4250 // Do not update urlbar if there was a subframe navigation
4252 var browser = gBrowser.selectedBrowser;
4253 if (aWebProgress.DOMWindow == content) {
4254 if ((location == "about:blank" && !content.opener) ||
4255 location == "") { // Second condition is for new tabs, otherwise
4256 // reload function is enabled until tab is refreshed.
4257 this.reloadCommand.setAttribute("disabled", "true");
4259 this.reloadCommand.removeAttribute("disabled");
4262 if (!gBrowser.mTabbedMode && aWebProgress.isLoadingDocument)
4263 gBrowser.setIcon(gBrowser.selectedTab, null);
4266 // Strip off "wyciwyg://" and passwords for the location bar
4267 let uri = aLocationURI;
4269 uri = this._uriFixup.createExposableURI(uri);
4273 // Update starring UI
4274 PlacesStarButton.updateState();
4277 UpdateBackForwardCommands(gBrowser.webNavigation);
4279 if (gFindBarInitialized) {
4280 if (gFindBar.findMode != gFindBar.FIND_NORMAL) {
4281 // Close the Find toolbar if we're in old-style TAF mode
4285 // XXXmano new-findbar, do something useful once it lands.
4286 // Of course, this is especially wrong with bfcache on...
4288 // fix bug 253793 - turn off highlight when page changes
4289 gFindBar.getElement("highlight").checked = false;
4292 // See bug 358202, when tabs are switched during a drag operation,
4293 // timers don't fire on windows (bug 203573)
4295 setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
4297 this.asyncUpdateUI();
4300 asyncUpdateUI: function () {
4301 FeedHandler.updateFeeds();
4302 BrowserSearch.updateSearchButton();
4305 onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
4306 this.status = aMessage;
4307 this.updateStatusField();
4310 // Properties used to cache security state used to update the UI
4313 _hostChanged: false, // onLocationChange will flip this bit
4315 onSecurityChange: function (aWebProgress, aRequest, aState) {
4316 // Don't need to do anything if the data we use to update the UI hasn't
4318 if (this._state == aState &&
4319 this._tooltipText == gBrowser.securityUI.tooltipText &&
4320 !this._hostChanged) {
4323 var contentHost = gBrowser.contentWindow.location.host;
4324 if (this._host !== undefined && this._host != contentHost) {
4325 Components.utils.reportError(
4326 "ASSERTION: browser.js host is inconsistent. Content window has " +
4327 "<" + contentHost + "> but cached host is <" + this._host + ">.\n"
4334 this._state = aState;
4338 this._host = gBrowser.contentWindow.location.host;
4344 this._hostChanged = false;
4345 this._tooltipText = gBrowser.securityUI.tooltipText
4347 // aState is defined as a bitmask that may be extended in the future.
4348 // We filter out any unknown bits before testing for known values.
4349 const wpl = Components.interfaces.nsIWebProgressListener;
4350 const wpl_security_bits = wpl.STATE_IS_SECURE |
4351 wpl.STATE_IS_BROKEN |
4352 wpl.STATE_IS_INSECURE |
4353 wpl.STATE_SECURE_HIGH |
4354 wpl.STATE_SECURE_MED |
4355 wpl.STATE_SECURE_LOW;
4358 switch (this._state & wpl_security_bits) {
4359 case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_HIGH:
4362 case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_MED:
4363 case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_LOW:
4366 case wpl.STATE_IS_BROKEN:
4372 this.securityButton.setAttribute("level", level);
4373 this.securityButton.hidden = false;
4374 // We don't style the Location Bar based on the the 'level' attribute
4375 // anymore, but still set it for third-party themes.
4377 gURLBar.setAttribute("level", level);
4379 this.securityButton.hidden = true;
4380 this.securityButton.removeAttribute("level");
4382 gURLBar.removeAttribute("level");
4385 this.securityButton.setAttribute("tooltiptext", this._tooltipText);
4387 // Don't pass in the actual location object, since it can cause us to
4388 // hold on to the window object too long. Just pass in the fields we
4389 // care about. (bug 424829)
4390 var location = gBrowser.contentWindow.location;
4391 var locationObj = {};
4393 locationObj.host = location.host;
4394 locationObj.hostname = location.hostname;
4395 locationObj.port = location.port;
4397 // Can sometimes throw if the URL being visited has no host/hostname,
4398 // e.g. about:blank. The _state for these pages means we won't need these
4399 // properties anyways, though.
4401 gIdentityHandler.checkIdentity(this._state, locationObj);
4404 // simulate all change notifications after switching tabs
4405 onUpdateCurrentBrowser: function (aStateFlags, aStatus, aMessage, aTotalProgress) {
4406 if (FullZoom.updateBackgroundTabs)
4407 FullZoom.onLocationChange(gBrowser.currentURI, true);
4408 var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
4409 var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
4410 // use a pseudo-object instead of a (potentially nonexistent) channel for getting
4411 // a correct error message - and make sure that the UI is always either in
4412 // loading (STATE_START) or done (STATE_STOP) mode
4414 gBrowser.webProgress,
4415 { URI: gBrowser.currentURI },
4416 loadingDone ? nsIWebProgressListener.STATE_STOP : nsIWebProgressListener.STATE_START,
4419 // status message and progress value are undefined if we're done with loading
4422 this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
4423 this.onProgressChange(gBrowser.webProgress, 0, 0, aTotalProgress, 1);
4426 startDocumentLoad: function (aRequest) {
4427 // clear out feed data
4428 gBrowser.selectedBrowser.feeds = null;
4430 // clear out search-engine data
4431 gBrowser.selectedBrowser.engines = null;
4433 var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
4435 Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
4440 endDocumentLoad: function (aRequest, aStatus) {
4441 var urlStr = aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
4443 var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
4445 Services.obs.notifyObservers(content, notification, urlStr);
4451 var CombinedStopReload = {
4453 if (this._initialized)
4456 var stop = document.getElementById("stop-button");
4460 var reload = document.getElementById("reload-button");
4464 if (!(reload.nextSibling == stop))
4467 this._initialized = true;
4468 if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
4469 reload.setAttribute("displaystop", "true");
4470 stop.addEventListener("click", this, false);
4472 this.reload = reload;
4475 uninit: function () {
4476 if (!this._initialized)
4479 this._cancelTransition();
4480 this._initialized = false;
4481 this.stop.removeEventListener("click", this, false);
4486 handleEvent: function (event) {
4487 // the only event we listen to is "click" on the stop button
4488 if (event.button == 0 &&
4489 !this.stop.disabled)
4490 this._stopClicked = true;
4493 switchToStop: function () {
4494 if (!this._initialized)
4497 this._cancelTransition();
4498 this.reload.setAttribute("displaystop", "true");
4501 switchToReload: function (aDelay) {
4502 if (!this._initialized)
4505 if (!aDelay || this._stopClicked) {
4506 this._stopClicked = false;
4507 this._cancelTransition();
4508 this.reload.removeAttribute("displaystop");
4515 this._timer = setTimeout(function (self) {
4517 self.reload.removeAttribute("displaystop");
4521 _cancelTransition: function () {
4523 clearTimeout(this._timer);
4529 var TabsProgressListener = {
4530 #ifdef MOZ_CRASHREPORTER
4531 onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
4532 if (aRequest instanceof Ci.nsIChannel &&
4533 aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
4534 aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT &&
4535 gCrashReporter.enabled) {
4536 gCrashReporter.annotateCrashReport("URL", aRequest.URI.spec);
4541 onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI) {
4542 // Filter out any sub-frame loads
4543 if (aBrowser.contentWindow == aWebProgress.DOMWindow)
4544 FullZoom.onLocationChange(aLocationURI, false, aBrowser);
4547 onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
4548 if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
4549 let brandBundle = document.getElementById("bundle_brand");
4550 let brandShortName = brandBundle.getString("brandShortName");
4551 let refreshButtonText =
4552 gNavigatorBundle.getString("refreshBlocked.goButton");
4553 let refreshButtonAccesskey =
4554 gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
4556 gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
4557 : "refreshBlocked.redirectLabel",
4559 let docShell = aWebProgress.DOMWindow
4560 .QueryInterface(Ci.nsIInterfaceRequestor)
4561 .getInterface(Ci.nsIWebNavigation)
4562 .QueryInterface(Ci.nsIDocShell);
4563 let notificationBox = gBrowser.getNotificationBox(aBrowser);
4564 let notification = notificationBox.getNotificationWithValue("refresh-blocked");
4566 notification.label = message;
4567 notification.refreshURI = aURI;
4568 notification.delay = aDelay;
4569 notification.docShell = docShell;
4572 label: refreshButtonText,
4573 accessKey: refreshButtonAccesskey,
4574 callback: function (aNotification, aButton) {
4575 var refreshURI = aNotification.docShell
4576 .QueryInterface(Ci.nsIRefreshURI);
4577 refreshURI.forceRefreshURI(aNotification.refreshURI,
4578 aNotification.delay, true);
4582 notificationBox.appendNotification(message, "refresh-blocked",
4583 "chrome://browser/skin/Info.png",
4584 notificationBox.PRIORITY_INFO_MEDIUM,
4586 notification.refreshURI = aURI;
4587 notification.delay = aDelay;
4588 notification.docShell = docShell;
4596 function nsBrowserAccess() { }
4598 nsBrowserAccess.prototype = {
4599 QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
4601 openURI: function (aURI, aOpener, aWhere, aContext) {
4602 var newWindow = null;
4603 var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
4605 if (isExternal && aURI && aURI.schemeIs("chrome")) {
4606 dump("use -chrome command-line option to load external chrome urls\n");
4610 if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW)
4611 aWhere = gPrefService.getIntPref("browser.link.open_newwindow");
4613 case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
4614 // FIXME: Bug 408379. So how come this doesn't send the
4615 // referrer like the other loads do?
4616 var url = aURI ? aURI.spec : "about:blank";
4617 // Pass all params to openDialog to ensure that "url" isn't passed through
4618 // loadOneOrMoreURIs, which splits based on "|"
4619 newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
4621 case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
4622 let win, needToFocusWin;
4624 // try the current window. if we're in a popup, fall back on the most recent browser window
4625 if (!window.document.documentElement.getAttribute("chromehidden"))
4628 win = Cc["@mozilla.org/browser/browserglue;1"]
4629 .getService(Ci.nsIBrowserGlue)
4630 .getMostRecentBrowserWindow();
4631 needToFocusWin = true;
4635 // we couldn't find a suitable window, a new one needs to be opened.
4639 if (isExternal && (!aURI || aURI.spec == "about:blank")) {
4640 win.BrowserOpenTab(); // this also focuses the location bar
4642 newWindow = win.content;
4646 let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
4647 let referrer = aOpener ? makeURI(aOpener.location.href) : null;
4649 let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
4650 referrerURI: referrer,
4651 fromExternal: isExternal,
4652 inBackground: loadInBackground});
4653 let browser = win.gBrowser.getBrowserForTab(tab);
4655 newWindow = browser.contentWindow;
4656 if (needToFocusWin || (!loadInBackground && isExternal))
4659 default : // OPEN_CURRENTWINDOW or an illegal value
4660 newWindow = content;
4662 let referrer = aOpener ? makeURI(aOpener.location.href) : null;
4663 let loadflags = isExternal ?
4664 Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
4665 Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
4666 gBrowser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
4668 if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
4674 isTabContentWindow: function (aWindow) {
4675 return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
4679 function onViewToolbarsPopupShowing(aEvent) {
4680 var popup = aEvent.target;
4681 if (popup != aEvent.currentTarget)
4687 for (i = popup.childNodes.length-1; i >= 0; --i) {
4688 var deadItem = popup.childNodes[i];
4689 if (deadItem.hasAttribute("toolbarindex"))
4690 popup.removeChild(deadItem);
4693 var firstMenuItem = popup.firstChild;
4695 for (i = 0; i < gNavToolbox.childNodes.length; ++i) {
4696 var toolbar = gNavToolbox.childNodes[i];
4697 var toolbarName = toolbar.getAttribute("toolbarname");
4699 let menuItem = document.createElement("menuitem");
4700 let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
4701 "autohide" : "collapsed";
4702 menuItem.setAttribute("toolbarindex", i);
4703 menuItem.setAttribute("type", "checkbox");
4704 menuItem.setAttribute("label", toolbarName);
4705 menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
4706 if (popup.id != "appmenu_customizeMenu")
4707 menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
4708 popup.insertBefore(menuItem, firstMenuItem);
4710 menuItem.addEventListener("command", onViewToolbarCommand, false);
4712 toolbar = toolbar.nextSibling;
4716 function onViewToolbarCommand(aEvent) {
4717 var index = aEvent.originalTarget.getAttribute("toolbarindex");
4718 var toolbar = gNavToolbox.childNodes[index];
4719 var visible = aEvent.originalTarget.getAttribute("checked") == "true";
4720 setToolbarVisibility(toolbar, visible);
4723 function setToolbarVisibility(toolbar, visible) {
4724 var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
4725 "autohide" : "collapsed";
4727 toolbar.setAttribute(hidingAttribute, !visible);
4728 document.persist(toolbar.id, hidingAttribute);
4730 PlacesToolbarHelper.init();
4731 BookmarksMenuButton.updatePosition();
4733 #ifdef MENUBAR_CAN_AUTOHIDE
4734 updateAppButtonDisplay();
4739 toggle: function () {
4740 this.enabled = !this.enabled;
4742 syncCommand: function () {
4743 let enabled = this.enabled;
4744 document.getElementById("cmd_ToggleTabsOnTop")
4745 .setAttribute("checked", enabled);
4746 document.documentElement.setAttribute("tabsontop", enabled);
4747 document.getElementById("TabsToolbar").setAttribute("tabsontop", enabled);
4748 gBrowser.tabContainer.setAttribute("tabsontop", enabled);
4751 return gNavToolbox.getAttribute("tabsontop") == "true";
4754 gNavToolbox.setAttribute("tabsontop", !!val);
4761 #ifdef MENUBAR_CAN_AUTOHIDE
4762 function updateAppButtonDisplay() {
4763 var displayAppButton =
4764 !gInPrintPreviewMode &&
4765 window.menubar.visible &&
4766 document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
4768 document.getElementById("titlebar").hidden = !displayAppButton;
4770 if (displayAppButton)
4771 document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
4773 document.documentElement.removeAttribute("chromemargin");
4776 function onTitlebarMaxClick() {
4777 if (window.windowState == window.STATE_MAXIMIZED)
4784 function displaySecurityInfo()
4786 BrowserPageInfo(null, "securityTab");
4790 * Opens or closes the sidebar identified by commandID.
4792 * @param commandID a string identifying the sidebar to toggle; see the
4793 * note below. (Optional if a sidebar is already open.)
4794 * @param forceOpen boolean indicating whether the sidebar should be
4795 * opened regardless of its current state (optional).
4797 * We expect to find a xul:broadcaster element with the specified ID.
4798 * The following attributes on that element may be used and/or modified:
4799 * - id (required) the string to match commandID. The convention
4800 * is to use this naming scheme: 'view<sidebar-name>Sidebar'.
4801 * - sidebarurl (required) specifies the URL to load in this sidebar.
4802 * - sidebartitle or label (in that order) specify the title to
4803 * display on the sidebar.
4804 * - checked indicates whether the sidebar is currently displayed.
4805 * Note that toggleSidebar updates this attribute when
4806 * it changes the sidebar's visibility.
4807 * - group this attribute must be set to "sidebar".
4809 function toggleSidebar(commandID, forceOpen) {
4811 var sidebarBox = document.getElementById("sidebar-box");
4813 commandID = sidebarBox.getAttribute("sidebarcommand");
4815 var sidebarBroadcaster = document.getElementById(commandID);
4816 var sidebar = document.getElementById("sidebar"); // xul:browser
4817 var sidebarTitle = document.getElementById("sidebar-title");
4818 var sidebarSplitter = document.getElementById("sidebar-splitter");
4820 if (sidebarBroadcaster.getAttribute("checked") == "true") {
4822 sidebarBroadcaster.removeAttribute("checked");
4823 sidebarBox.setAttribute("sidebarcommand", "");
4824 sidebarTitle.value = "";
4825 sidebar.setAttribute("src", "about:blank");
4826 sidebarBox.hidden = true;
4827 sidebarSplitter.hidden = true;
4830 fireSidebarFocusedEvent();
4835 // now we need to show the specified sidebar
4837 // ..but first update the 'checked' state of all sidebar broadcasters
4838 var broadcasters = document.getElementsByAttribute("group", "sidebar");
4839 for (var i = 0; i < broadcasters.length; ++i) {
4840 // skip elements that observe sidebar broadcasters and random
4842 if (broadcasters[i].localName != "broadcaster")
4845 if (broadcasters[i] != sidebarBroadcaster)
4846 broadcasters[i].removeAttribute("checked");
4848 sidebarBroadcaster.setAttribute("checked", "true");
4851 sidebarBox.hidden = false;
4852 sidebarSplitter.hidden = false;
4854 var url = sidebarBroadcaster.getAttribute("sidebarurl");
4855 var title = sidebarBroadcaster.getAttribute("sidebartitle");
4857 title = sidebarBroadcaster.getAttribute("label");
4858 sidebar.setAttribute("src", url); // kick off async load
4859 sidebarBox.setAttribute("sidebarcommand", sidebarBroadcaster.id);
4860 sidebarTitle.value = title;
4862 // We set this attribute here in addition to setting it on the <browser>
4863 // element itself, because the code in BrowserShutdown persists this
4864 // attribute, not the "src" of the <browser id="sidebar">. The reason it
4865 // does that is that we want to delay sidebar load a bit when a browser
4866 // window opens. See delayedStartup().
4867 sidebarBox.setAttribute("src", url);
4869 if (sidebar.contentDocument.location.href != url)
4870 sidebar.addEventListener("load", sidebarOnLoad, true);
4871 else // older code handled this case, so we do it too
4872 fireSidebarFocusedEvent();
4875 function sidebarOnLoad(event) {
4876 var sidebar = document.getElementById("sidebar");
4877 sidebar.removeEventListener("load", sidebarOnLoad, true);
4878 // We're handling the 'load' event before it bubbles up to the usual
4879 // (non-capturing) event handlers. Let it bubble up before firing the
4880 // SidebarFocused event.
4881 setTimeout(fireSidebarFocusedEvent, 0);
4885 * Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
4886 * a chance to adjust focus as needed. An additional event is needed, because
4887 * we don't want to focus the sidebar when it's opened on startup or in a new
4888 * window, only when the user opens the sidebar.
4890 function fireSidebarFocusedEvent() {
4891 var sidebar = document.getElementById("sidebar");
4892 var event = document.createEvent("Events");
4893 event.initEvent("SidebarFocused", true, false);
4894 sidebar.contentWindow.dispatchEvent(event);
4898 prefDomain: "browser.startup.homepage",
4899 observe: function (aSubject, aTopic, aPrefName)
4901 if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
4904 this.updateTooltip();
4907 updateTooltip: function (homeButton)
4910 homeButton = document.getElementById("home-button");
4912 var homePage = this.getHomePage();
4913 homePage = homePage.replace(/\|/g,', ');
4914 homeButton.setAttribute("tooltiptext", homePage);
4918 getHomePage: function ()
4922 url = gPrefService.getComplexValue(this.prefDomain,
4923 Components.interfaces.nsIPrefLocalizedString).data;
4927 // use this if we can't find the pref
4929 var SBS = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
4930 var configBundle = SBS.createBundle("chrome://branding/locale/browserconfig.properties");
4931 url = configBundle.GetStringFromName(this.prefDomain);
4937 updatePersonalToolbarStyle: function (homeButton)
4940 homeButton = document.getElementById("home-button");
4942 homeButton.className = homeButton.parentNode.id == "PersonalToolbar"
4943 || homeButton.parentNode.parentNode.id == "PersonalToolbar" ?
4944 homeButton.className.replace("toolbarbutton-1", "bookmark-item") :
4945 homeButton.className.replace("bookmark-item", "toolbarbutton-1");
4950 * Gets the selected text in the active browser. Leading and trailing
4951 * whitespace is removed, and consecutive whitespace is replaced by a single
4952 * space. A maximum of 150 characters will be returned, regardless of the value
4956 * The maximum number of characters to return.
4958 function getBrowserSelection(aCharLen) {
4959 // selections of more than 150 characters aren't useful
4960 const kMaxSelectionLen = 150;
4961 const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
4963 var focusedWindow = document.commandDispatcher.focusedWindow;
4964 var selection = focusedWindow.getSelection().toString();
4967 if (selection.length > charLen) {
4968 // only use the first charLen important chars. see bug 221361
4969 var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
4970 pattern.test(selection);
4971 selection = RegExp.lastMatch;
4974 selection = selection.replace(/^\s+/, "")
4975 .replace(/\s+$/, "")
4976 .replace(/\s+/g, " ");
4978 if (selection.length > charLen)
4979 selection = selection.substr(0, charLen);
4985 function openWebPanel(aTitle, aURI)
4987 // Ensure that the web panels sidebar is open.
4988 toggleSidebar('viewWebPanelsSidebar', true);
4990 // Set the title of the panel.
4991 document.getElementById("sidebar-title").value = aTitle;
4993 // Tell the Web Panels sidebar to load the bookmark.
4994 var sidebar = document.getElementById("sidebar");
4995 if (sidebar.docShell && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser')) {
4996 sidebar.contentWindow.loadWebPanel(aURI);
4999 sidebar.removeEventListener("load", asyncOpenWebPanel, true);
5003 // The panel is still being constructed. Attach an onload handler.
5005 sidebar.addEventListener("load", asyncOpenWebPanel, true);
5006 gWebPanelURI = aURI;
5010 function asyncOpenWebPanel(event)
5012 var sidebar = document.getElementById("sidebar");
5013 if (gWebPanelURI && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser'))
5014 sidebar.contentWindow.loadWebPanel(gWebPanelURI);
5016 sidebar.removeEventListener("load", asyncOpenWebPanel, true);
5020 * - [ Dependencies ] ---------------------------------------------------------
5021 * utilityOverlay.js:
5025 // Called whenever the user clicks in the content area,
5026 // except when left-clicking on links (special case)
5027 // should always return true for click to go through
5028 function contentAreaClick(event, fieldNormalClicks)
5030 if (!event.isTrusted || event.getPreventDefault()) {
5034 var target = event.target;
5037 if (target instanceof HTMLAnchorElement ||
5038 target instanceof HTMLAreaElement ||
5039 target instanceof HTMLLinkElement) {
5040 if (target.hasAttribute("href"))
5043 // xxxmpc: this is kind of a hack to work around a Gecko bug (see bug 266932)
5044 // we're going to walk up the DOM looking for a parent link node,
5045 // this shouldn't be necessary, but we're matching the existing behaviour for left click
5046 var parent = target.parentNode;
5048 if (parent instanceof HTMLAnchorElement ||
5049 parent instanceof HTMLAreaElement ||
5050 parent instanceof HTMLLinkElement) {
5051 if (parent.hasAttribute("href"))
5054 parent = parent.parentNode;
5058 linkNode = event.originalTarget;
5059 while (linkNode && !(linkNode instanceof HTMLAnchorElement))
5060 linkNode = linkNode.parentNode;
5061 // <a> cannot be nested. So if we find an anchor without an
5062 // href, there is no useful <a> around the target
5063 if (linkNode && !linkNode.hasAttribute("href"))
5069 if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
5070 !event.altKey && !event.metaKey) {
5071 // A Web panel's links should target the main content area. Do this
5072 // if no modifier keys are down and if there's no target or the target equals
5073 // _main (the IE convention) or _content (the Mozilla convention).
5074 // XXX Now that markLinkVisited is gone, we may not need to field _main and
5076 target = wrapper.getAttribute("target");
5077 if (fieldNormalClicks &&
5078 (!target || target == "_content" || target == "_main"))
5079 // IE uses _main, SeaMonkey uses _content, we support both
5083 if (wrapper.getAttribute("onclick"))
5085 // javascript links should be executed in the current browser
5086 if (wrapper.href.substr(0, 11) === "javascript:")
5088 // data links should be executed in the current browser
5089 if (wrapper.href.substr(0, 5) === "data:")
5093 urlSecurityCheck(wrapper.href, wrapper.ownerDocument.nodePrincipal);
5100 var url = getShortcutOrURI(wrapper.href, postData);
5103 loadURI(url, null, postData.value, false);
5104 event.preventDefault();
5107 else if (linkNode.getAttribute("rel") == "sidebar") {
5108 // This is the Opera convention for a special link that - when clicked - allows
5109 // you to add a sidebar panel. We support the Opera convention here. The link's
5110 // title attribute contains the title that should be used for the sidebar panel.
5111 PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(wrapper.href),
5112 wrapper.getAttribute("title"),
5113 null, null, true, true);
5114 event.preventDefault();
5119 handleLinkClick(event, wrapper.href, linkNode);
5125 var href, realHref, baseURI;
5128 if (linkNode.nodeType == Node.ELEMENT_NODE) {
5131 realHref = wrapper.getAttributeNS("http://www.w3.org/1999/xlink", "href");
5134 baseURI = wrapper.baseURI
5137 linkNode = linkNode.parentNode;
5140 href = makeURLAbsolute(baseURI, href);
5141 handleLinkClick(event, href, null);
5145 if (event.button == 1 &&
5146 gPrefService.getBoolPref("middlemouse.contentLoadURL") &&
5147 !gPrefService.getBoolPref("general.autoScroll")) {
5148 middleMousePaste(event);
5153 function handleLinkClick(event, href, linkNode)
5155 var doc = event.target.ownerDocument;
5157 switch (event.button) {
5158 case 0: // if left button clicked
5160 if (event.metaKey) { // Cmd
5162 if (event.ctrlKey) {
5164 openNewTabWith(href, doc, null, event, false);
5165 event.stopPropagation();
5169 if (event.shiftKey && event.altKey) {
5171 Cc["@mozilla.org/browser/feeds/result-service;1"].
5172 getService(Ci.nsIFeedResultService);
5173 feedService.forcePreviewPage = true;
5174 loadURI(href, null, null, false);
5178 if (event.shiftKey) {
5179 openNewWindowWith(href, doc, null, false);
5180 event.stopPropagation();
5185 saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
5186 true, doc.documentURIObject);
5191 case 1: // if middle button clicked
5192 var tab = gPrefService.getBoolPref("browser.tabs.opentabfor.middleclick");
5194 openNewTabWith(href, doc, null, event, false);
5196 openNewWindowWith(href, doc, null, false);
5197 event.stopPropagation();
5203 function middleMousePaste(event) {
5204 var url = getShortcutOrURI(readFromClipboard());
5213 addToUrlbarHistory(url);
5215 // Things may go wrong when adding url to session history,
5216 // but don't let that interfere with the loading of the url.
5222 true /* ignore the fact this is a middle click */);
5224 event.stopPropagation();
5227 function handleDroppedLink(event, url, name)
5230 let uri = getShortcutOrURI(url, postData);
5232 loadURI(uri, null, postData.value, false);
5234 // Keep the event from being handled by the dragDrop listeners
5235 // built-in to gecko if they happen to be above us.
5236 event.preventDefault();
5239 function MultiplexHandler(event)
5241 var node = event.target;
5242 var name = node.getAttribute('name');
5244 if (name == 'detectorGroup') {
5245 SetForcedDetector(true);
5246 SelectDetector(event, false);
5247 } else if (name == 'charsetGroup') {
5248 var charset = node.getAttribute('id');
5249 charset = charset.substring('charset.'.length, charset.length)
5250 SetForcedCharset(charset);
5251 } else if (name == 'charsetCustomize') {
5252 //do nothing - please remove this else statement, once the charset prefs moves to the pref window
5254 SetForcedCharset(node.getAttribute('id'));
5256 } catch(ex) { alert(ex); }
5259 function SelectDetector(event, doReload)
5261 var uri = event.target.getAttribute("id");
5262 var prefvalue = uri.substring('chardet.'.length, uri.length);
5263 if ("off" == prefvalue) { // "off" is special value to turn off the detectors
5268 var str = Cc["@mozilla.org/supports-string;1"].
5269 createInstance(Ci.nsISupportsString);
5271 str.data = prefvalue;
5272 gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
5274 window.content.location.reload();
5277 dump("Failed to set the intl.charset.detector preference.\n");
5281 function SetForcedDetector(doReload)
5283 BrowserSetForcedDetector(doReload);
5286 function SetForcedCharset(charset)
5288 BrowserSetForcedCharacterSet(charset);
5291 function BrowserSetForcedCharacterSet(aCharset)
5293 var docCharset = gBrowser.docShell.QueryInterface(Ci.nsIDocCharset);
5294 docCharset.charset = aCharset;
5295 // Save the forced character-set
5296 PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, aCharset);
5297 BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
5300 function BrowserSetForcedDetector(doReload)
5302 gBrowser.documentCharsetInfo.forcedDetector = true;
5304 BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
5307 function UpdateCurrentCharset()
5309 // extract the charset from DOM
5310 var wnd = document.commandDispatcher.focusedWindow;
5311 if ((window == wnd) || (wnd == null)) wnd = window.content;
5313 // Uncheck previous item
5315 var pref_item = document.getElementById('charset.' + gPrevCharset);
5317 pref_item.setAttribute('checked', 'false');
5320 var menuitem = document.getElementById('charset.' + wnd.document.characterSet);
5322 menuitem.setAttribute('checked', 'true');
5326 function UpdateCharsetDetector() {
5327 var prefvalue = "off";
5330 prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
5334 prefvalue = "chardet." + prefvalue;
5336 var menuitem = document.getElementById(prefvalue);
5338 menuitem.setAttribute("checked", "true");
5341 function UpdateMenus(event) {
5342 // use setTimeout workaround to delay checkmark the menu
5343 // when onmenucomplete is ready then use it instead of oncreate
5344 // see bug 78290 for the detail
5345 UpdateCurrentCharset();
5346 setTimeout(UpdateCurrentCharset, 0);
5347 UpdateCharsetDetector();
5348 setTimeout(UpdateCharsetDetector, 0);
5351 function CreateMenu(node) {
5352 Services.obs.notifyObservers(null, "charsetmenu-selected", node);
5355 function charsetLoadListener(event) {
5356 var charset = window.content.document.characterSet;
5358 if (charset.length > 0 && (charset != gLastBrowserCharset)) {
5360 gCharsetMenu = Cc['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService(Ci.nsICurrentCharsetListener);
5361 gCharsetMenu.SetCurrentCharset(charset);
5362 gPrevCharset = gLastBrowserCharset;
5363 gLastBrowserCharset = charset;
5367 /* Begin Page Style Functions */
5368 function getAllStyleSheets(frameset) {
5369 var styleSheetsArray = Array.slice(frameset.document.styleSheets);
5370 for (let i = 0; i < frameset.frames.length; i++) {
5371 let frameSheets = getAllStyleSheets(frameset.frames[i]);
5372 styleSheetsArray = styleSheetsArray.concat(frameSheets);
5374 return styleSheetsArray;
5377 function stylesheetFillPopup(menuPopup) {
5378 var noStyle = menuPopup.firstChild;
5379 var persistentOnly = noStyle.nextSibling;
5380 var sep = persistentOnly.nextSibling;
5381 while (sep.nextSibling)
5382 menuPopup.removeChild(sep.nextSibling);
5384 var styleSheets = getAllStyleSheets(window.content);
5385 var currentStyleSheets = {};
5386 var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
5387 var haveAltSheets = false;
5388 var altStyleSelected = false;
5390 for (let i = 0; i < styleSheets.length; ++i) {
5391 let currentStyleSheet = styleSheets[i];
5393 if (!currentStyleSheet.title)
5396 // Skip any stylesheets that don't match the screen media type.
5397 if (currentStyleSheet.media.length > 0) {
5398 let media = currentStyleSheet.media.mediaText.split(", ");
5399 if (media.indexOf("screen") == -1 &&
5400 media.indexOf("all") == -1)
5404 if (!currentStyleSheet.disabled)
5405 altStyleSelected = true;
5407 haveAltSheets = true;
5409 let lastWithSameTitle = null;
5410 if (currentStyleSheet.title in currentStyleSheets)
5411 lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
5413 if (!lastWithSameTitle) {
5414 let menuItem = document.createElement("menuitem");
5415 menuItem.setAttribute("type", "radio");
5416 menuItem.setAttribute("label", currentStyleSheet.title);
5417 menuItem.setAttribute("data", currentStyleSheet.title);
5418 menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
5419 menuPopup.appendChild(menuItem);
5420 currentStyleSheets[currentStyleSheet.title] = menuItem;
5421 } else if (currentStyleSheet.disabled) {
5422 lastWithSameTitle.removeAttribute("checked");
5426 noStyle.setAttribute("checked", styleDisabled);
5427 persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
5428 persistentOnly.hidden = (window.content.document.preferredStyleSheetSet) ? haveAltSheets : false;
5429 sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
5433 function stylesheetInFrame(frame, title) {
5434 return Array.some(frame.document.styleSheets,
5435 function (stylesheet) stylesheet.title == title);
5438 function stylesheetSwitchFrame(frame, title) {
5439 var docStyleSheets = frame.document.styleSheets;
5441 for (let i = 0; i < docStyleSheets.length; ++i) {
5442 let docStyleSheet = docStyleSheets[i];
5444 if (title == "_nostyle")
5445 docStyleSheet.disabled = true;
5446 else if (docStyleSheet.title)
5447 docStyleSheet.disabled = (docStyleSheet.title != title);
5448 else if (docStyleSheet.disabled)
5449 docStyleSheet.disabled = false;
5453 function stylesheetSwitchAll(frameset, title) {
5454 if (!title || title == "_nostyle" || stylesheetInFrame(frameset, title))
5455 stylesheetSwitchFrame(frameset, title);
5457 for (let i = 0; i < frameset.frames.length; i++)
5458 stylesheetSwitchAll(frameset.frames[i], title);
5461 function setStyleDisabled(disabled) {
5462 getMarkupDocumentViewer().authorStyleDisabled = disabled;
5464 /* End of the Page Style functions */
5466 var BrowserOffline = {
5467 /////////////////////////////////////////////////////////////////////////////
5468 // BrowserOffline Public Methods
5471 if (!this._uiElement)
5472 this._uiElement = document.getElementById("goOfflineMenuitem");
5474 Services.obs.addObserver(this, "network:offline-status-changed", false);
5476 this._updateOfflineUI(Services.io.offline);
5482 Services.obs.removeObserver(this, "network:offline-status-changed");
5487 toggleOfflineStatus: function ()
5489 var ioService = Services.io;
5491 // Stop automatic management of the offline status
5493 ioService.manageOfflineStatus = false;
5497 if (!ioService.offline && !this._canGoOffline()) {
5498 this._updateOfflineUI(false);
5502 ioService.offline = !ioService.offline;
5504 // Save the current state for later use as the initial state
5505 // (if there is no netLinkService)
5506 gPrefService.setBoolPref("browser.offline", ioService.offline);
5509 /////////////////////////////////////////////////////////////////////////////
5511 observe: function (aSubject, aTopic, aState)
5513 if (aTopic != "network:offline-status-changed")
5516 this._updateOfflineUI(aState == "offline");
5519 /////////////////////////////////////////////////////////////////////////////
5520 // BrowserOffline Implementation Methods
5521 _canGoOffline: function ()
5524 var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
5525 Services.obs.notifyObservers(cancelGoOffline, "offline-requested", null);
5527 // Something aborted the quit process.
5528 if (cancelGoOffline.data)
5538 _updateOfflineUI: function (aOffline)
5540 var offlineLocked = gPrefService.prefIsLocked("network.online");
5542 this._uiElement.setAttribute("disabled", "true");
5544 this._uiElement.setAttribute("checked", aOffline);
5549 /////////////////////////////////////////////////////////////////////////////
5550 // OfflineApps Public Methods
5553 Services.obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
5554 Services.obs.addObserver(this, "offline-cache-update-completed", false);
5559 Services.obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
5560 Services.obs.removeObserver(this, "offline-cache-update-completed");
5563 handleEvent: function(event) {
5564 if (event.type == "MozApplicationManifest") {
5565 this.offlineAppRequested(event.originalTarget.defaultView);
5569 /////////////////////////////////////////////////////////////////////////////
5570 // OfflineApps Implementation Methods
5572 // XXX: _getBrowserWindowForContentWindow and _getBrowserForContentWindow
5573 // were taken from browser/components/feeds/src/WebContentConverter.
5574 _getBrowserWindowForContentWindow: function(aContentWindow) {
5575 return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
5576 .getInterface(Ci.nsIWebNavigation)
5577 .QueryInterface(Ci.nsIDocShellTreeItem)
5579 .QueryInterface(Ci.nsIInterfaceRequestor)
5580 .getInterface(Ci.nsIDOMWindow)
5584 _getBrowserForContentWindow: function(aBrowserWindow, aContentWindow) {
5585 // This depends on pseudo APIs of browser.js and tabbrowser.xml
5586 aContentWindow = aContentWindow.top;
5587 var browsers = aBrowserWindow.gBrowser.browsers;
5588 for (var i = 0; i < browsers.length; ++i) {
5589 if (browsers[i].contentWindow == aContentWindow)
5595 _getManifestURI: function(aWindow) {
5596 if (!aWindow.document.documentElement)
5599 var attr = aWindow.document.documentElement.getAttribute("manifest");
5604 var contentURI = makeURI(aWindow.location.href, null, null);
5605 return makeURI(attr, aWindow.document.characterSet, contentURI);
5611 // A cache update isn't tied to a specific window. Try to find
5612 // the best browser in which to warn the user about space usage
5613 _getBrowserForCacheUpdate: function(aCacheUpdate) {
5614 // Prefer the current browser
5615 var uri = this._getManifestURI(content);
5616 if (uri && uri.equals(aCacheUpdate.manifestURI)) {
5617 return gBrowser.selectedBrowser;
5620 var browsers = gBrowser.browsers;
5621 for (var i = 0; i < browsers.length; ++i) {
5622 uri = this._getManifestURI(browsers[i].contentWindow);
5623 if (uri && uri.equals(aCacheUpdate.manifestURI)) {
5631 _warnUsage: function(aBrowser, aURI) {
5635 var notificationBox = gBrowser.getNotificationBox(aBrowser);
5636 var notification = notificationBox.getNotificationWithValue("offline-app-usage");
5637 if (!notification) {
5639 label: gNavigatorBundle.getString("offlineApps.manageUsage"),
5640 accessKey: gNavigatorBundle.getString("offlineApps.manageUsageAccessKey"),
5641 callback: OfflineApps.manage
5644 var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
5645 const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
5646 var message = gNavigatorBundle.getFormattedString("offlineApps.usage",
5648 warnQuota / 1024 ]);
5650 notificationBox.appendNotification(message, "offline-app-usage",
5651 "chrome://browser/skin/Info.png",
5655 // Now that we've warned once, prevent the warning from showing up
5657 Services.perms.add(aURI, "offline-app",
5658 Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
5661 // XXX: duplicated in preferences/advanced.js
5662 _getOfflineAppUsage: function (host, groups)
5664 var cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
5665 getService(Ci.nsIApplicationCacheService);
5667 groups = cacheService.getGroups();
5670 for (var i = 0; i < groups.length; i++) {
5671 var uri = Services.io.newURI(groups[i], null, null);
5672 if (uri.asciiHost == host) {
5673 var cache = cacheService.getActiveCache(groups[i]);
5674 usage += cache.usage;
5678 var storageManager = Cc["@mozilla.org/dom/storagemanager;1"].
5679 getService(Ci.nsIDOMStorageManager);
5680 usage += storageManager.getUsage(host);
5685 _checkUsage: function(aURI) {
5686 // if the user has already allowed excessive usage, don't bother checking
5687 if (Services.perms.testExactPermission(aURI, "offline-app") !=
5688 Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
5689 var usage = this._getOfflineAppUsage(aURI.asciiHost);
5690 var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
5691 if (usage >= warnQuota * 1024) {
5699 offlineAppRequested: function(aContentWindow) {
5700 if (!gPrefService.getBoolPref("browser.offline-apps.notify")) {
5704 var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
5705 var browser = this._getBrowserForContentWindow(browserWindow,
5708 var currentURI = aContentWindow.document.documentURIObject;
5710 // don't bother showing UI if the user has already made a decision
5711 if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
5715 if (gPrefService.getBoolPref("offline-apps.allow_by_default")) {
5716 // all pages can use offline capabilities, no need to ask the user
5720 // this pref isn't set by default, ignore failures
5723 var host = currentURI.asciiHost;
5724 var notificationBox = gBrowser.getNotificationBox(browser);
5725 var notificationID = "offline-app-requested-" + host;
5726 var notification = notificationBox.getNotificationWithValue(notificationID);
5729 notification.documents.push(aContentWindow.document);
5732 label: gNavigatorBundle.getString("offlineApps.allow"),
5733 accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
5734 callback: function() {
5735 for (var i = 0; i < notification.documents.length; i++) {
5736 OfflineApps.allowSite(notification.documents[i]);
5740 label: gNavigatorBundle.getString("offlineApps.never"),
5741 accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
5742 callback: function() {
5743 for (var i = 0; i < notification.documents.length; i++) {
5744 OfflineApps.disallowSite(notification.documents[i]);
5748 label: gNavigatorBundle.getString("offlineApps.notNow"),
5749 accessKey: gNavigatorBundle.getString("offlineApps.notNowAccessKey"),
5750 callback: function() { /* noop */ }
5753 const priority = notificationBox.PRIORITY_INFO_LOW;
5754 var message = gNavigatorBundle.getFormattedString("offlineApps.available",
5757 notificationBox.appendNotification(message, notificationID,
5758 "chrome://browser/skin/Info.png",
5760 notification.documents = [ aContentWindow.document ];
5764 allowSite: function(aDocument) {
5765 Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION);
5767 // When a site is enabled while loading, manifest resources will
5768 // start fetching immediately. This one time we need to do it
5770 this._startFetching(aDocument);
5773 disallowSite: function(aDocument) {
5774 Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION);
5777 manage: function() {
5778 openAdvancedPreferences("networkTab");
5781 _startFetching: function(aDocument) {
5782 if (!aDocument.documentElement)
5785 var manifest = aDocument.documentElement.getAttribute("manifest");
5789 var manifestURI = makeURI(manifest, aDocument.characterSet,
5790 aDocument.documentURIObject);
5792 var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
5793 getService(Ci.nsIOfflineCacheUpdateService);
5794 updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject);
5797 /////////////////////////////////////////////////////////////////////////////
5799 observe: function (aSubject, aTopic, aState)
5801 if (aTopic == "dom-storage-warn-quota-exceeded") {
5803 var uri = makeURI(aSubject.location.href);
5805 if (OfflineApps._checkUsage(uri)) {
5807 this._getBrowserWindowForContentWindow(aSubject);
5808 var browser = this._getBrowserForContentWindow(browserWindow,
5810 OfflineApps._warnUsage(browser, uri);
5813 } else if (aTopic == "offline-cache-update-completed") {
5814 var cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
5816 var uri = cacheUpdate.manifestURI;
5817 if (OfflineApps._checkUsage(uri)) {
5818 var browser = this._getBrowserForCacheUpdate(cacheUpdate);
5820 OfflineApps._warnUsage(browser, cacheUpdate.manifestURI);
5827 function WindowIsClosing()
5829 var reallyClose = closeWindow(false, warnAboutClosingWindow);
5833 var numBrowsers = gBrowser.browsers.length;
5834 for (let i = 0; reallyClose && i < numBrowsers; ++i) {
5835 let ds = gBrowser.browsers[i].docShell;
5837 if (ds.contentViewer && !ds.contentViewer.permitUnload())
5838 reallyClose = false;
5845 * Checks if this is the last full *browser* window around. If it is, this will
5846 * be communicated like quitting. Otherwise, we warn about closing multiple tabs.
5847 * @returns true if closing can proceed, false if it got cancelled.
5849 function warnAboutClosingWindow() {
5850 // Popups aren't considered full browser windows.
5851 if (!toolbar.visible)
5852 return gBrowser.warnAboutClosingTabs(true);
5854 // Figure out if there's at least one other browser window around.
5855 let foundOtherBrowserWindow = false;
5856 let e = Services.wm.getEnumerator("navigator:browser");
5857 while (e.hasMoreElements() && !foundOtherBrowserWindow) {
5858 let win = e.getNext();
5859 if (win != window && win.toolbar.visible)
5860 foundOtherBrowserWindow = true;
5862 if (foundOtherBrowserWindow)
5863 return gBrowser.warnAboutClosingTabs(true);
5865 let os = Services.obs;
5867 let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
5868 createInstance(Ci.nsISupportsPRBool);
5869 os.notifyObservers(closingCanceled,
5870 "browser-lastwindow-close-requested", null);
5871 if (closingCanceled.data)
5874 os.notifyObservers(null, "browser-lastwindow-close-granted", null);
5877 // OS X doesn't quit the application when the last window is closed, but keeps
5878 // the session alive. Hence don't prompt users to save tabs, but warn about
5879 // closing multiple tabs.
5880 return gBrowser.warnAboutClosingTabs(true);
5886 var MailIntegration = {
5887 sendLinkForWindow: function (aWindow) {
5888 this.sendMessage(aWindow.location.href,
5889 aWindow.document.title);
5892 sendMessage: function (aBody, aSubject) {
5893 // generate a mailto url based on the url and the url's title
5894 var mailtoUrl = "mailto:";
5896 mailtoUrl += "?body=" + encodeURIComponent(aBody);
5897 mailtoUrl += "&subject=" + encodeURIComponent(aSubject);
5900 var uri = makeURI(mailtoUrl);
5902 // now pass this uri to the operating system
5903 this._launchExternalUrl(uri);
5906 // a generic method which can be used to pass arbitrary urls to the operating
5908 // aURL --> a nsIURI which represents the url to launch
5909 _launchExternalUrl: function (aURL) {
5910 var extProtocolSvc =
5911 Cc["@mozilla.org/uriloader/external-protocol-service;1"]
5912 .getService(Ci.nsIExternalProtocolService);
5914 extProtocolSvc.loadUrl(aURL);
5918 function BrowserOpenAddonsMgr(aView) {
5919 switchToTabHavingURI("about:addons", true, function(browser) {
5921 browser.contentWindow.wrappedJSObject.loadView(aView);
5925 function AddKeywordForSearchField() {
5926 var node = document.popupNode;
5928 var charset = node.ownerDocument.characterSet;
5930 var docURI = makeURI(node.ownerDocument.URL,
5933 var formURI = makeURI(node.form.getAttribute("action"),
5937 var spec = formURI.spec;
5940 (node.form.method.toUpperCase() == "POST"
5941 && (node.form.enctype == "application/x-www-form-urlencoded" ||
5942 node.form.enctype == ""));
5944 var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
5945 [node.ownerDocument.title]);
5946 var description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
5951 function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) {
5952 if (aIsFormUrlEncoded)
5953 return escape(aName + "=" + aValue);
5955 return escape(aName) + "=" + escape(aValue);
5958 for (var i=0; i < node.form.elements.length; i++) {
5959 el = node.form.elements[i];
5961 if (!el.type) // happens with fieldsets
5965 formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
5966 // Don't escape "%s", just append
5967 escapeNameValuePair(el.name, "", false) + "%s");
5971 type = el.type.toLowerCase();
5973 if (((el instanceof HTMLInputElement && el.mozIsTextField(true)) ||
5974 type == "hidden" || type == "textarea") ||
5975 ((type == "checkbox" || type == "radio") && el.checked)) {
5976 formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
5977 } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
5978 for (var j=0; j < el.options.length; j++) {
5979 if (el.options[j].selected)
5980 formData.push(escapeNameValuePair(el.name, el.options[j].value,
5989 postData = formData.join("&");
5991 spec += "?" + formData.join("&");
5993 PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(spec), title, description, null,
5994 null, null, "", postData, charset);
5997 function SwitchDocumentDirection(aWindow) {
5998 aWindow.document.dir = (aWindow.document.dir == "ltr" ? "rtl" : "ltr");
5999 for (var run = 0; run < aWindow.frames.length; run++)
6000 SwitchDocumentDirection(aWindow.frames[run]);
6003 function getPluginInfo(pluginElement)
6007 if (pluginElement instanceof HTMLAppletElement) {
6008 tagMimetype = "application/x-java-vm";
6010 if (pluginElement instanceof HTMLObjectElement) {
6011 pluginsPage = pluginElement.getAttribute("codebase");
6013 pluginsPage = pluginElement.getAttribute("pluginspage");
6016 // only attempt if a pluginsPage is defined.
6018 var doc = pluginElement.ownerDocument;
6019 var docShell = findChildShell(doc, gBrowser.docShell, null);
6021 pluginsPage = makeURI(pluginsPage, doc.characterSet, docShell.currentURI).spec;
6027 tagMimetype = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent)
6030 if (tagMimetype == "") {
6031 tagMimetype = pluginElement.type;
6035 return {mimetype: tagMimetype, pluginsPage: pluginsPage};
6038 var gPluginHandler = {
6041 delete this.CrashSubmit;
6042 Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
6043 return this.CrashSubmit;
6046 get crashReportHelpURL() {
6047 delete this.crashReportHelpURL;
6048 let url = formatURL("app.support.baseURL", true);
6049 url += "plugin-crashed";
6050 this.crashReportHelpURL = url;
6051 return this.crashReportHelpURL;
6054 // Map the plugin's name to a filtered version more suitable for user UI.
6055 makeNicePluginName : function (aName, aFilename) {
6056 if (aName == "Shockwave Flash")
6057 return "Adobe Flash";
6059 // Clean up the plugin name by stripping off any trailing version numbers
6060 // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
6061 let newName = aName.replace(/\bplug-?in\b/i, "").replace(/[\s\d\.\-\_\(\)]+$/, "");
6065 isTooSmall : function (plugin, overlay) {
6066 // Is the <object>'s size too small to hold what we want to show?
6067 let pluginRect = plugin.getBoundingClientRect();
6068 // XXX bug 446693. The text-shadow on the submitted-report text at
6069 // the bottom causes scrollHeight to be larger than it should be.
6070 let overflows = (overlay.scrollWidth > pluginRect.width) ||
6071 (overlay.scrollHeight - 5 > pluginRect.height);
6075 addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
6076 // XXX just doing (callback)(arg) was giving a same-origin error. bug?
6078 let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
6079 linkNode.addEventListener("click",
6083 evt.preventDefault();
6084 if (callbackArgs.length == 0)
6085 callbackArgs = [ evt ];
6086 (self[callbackName]).apply(self, callbackArgs);
6090 linkNode.addEventListener("keydown",
6094 if (evt.keyCode == evt.DOM_VK_RETURN) {
6095 evt.preventDefault();
6096 if (callbackArgs.length == 0)
6097 callbackArgs = [ evt ];
6098 evt.preventDefault();
6099 (self[callbackName]).apply(self, callbackArgs);
6105 handleEvent : function(event) {
6106 let self = gPluginHandler;
6107 let plugin = event.target;
6109 // We're expecting the target to be a plugin.
6110 if (!(plugin instanceof Ci.nsIObjectLoadingContent))
6113 switch (event.type) {
6114 case "PluginCrashed":
6115 self.pluginInstanceCrashed(plugin, event);
6118 case "PluginNotFound":
6119 // For non-object plugin tags, register a click handler to install the
6120 // plugin. Object tags can, and often do, deal with that themselves,
6121 // so don't stomp on the page developers toes.
6122 if (!(plugin instanceof HTMLObjectElement))
6123 self.addLinkClickCallback(plugin, "installSinglePlugin");
6125 case "PluginBlocklisted":
6126 case "PluginOutdated":
6127 let hideBarPrefName = event.type == "PluginOutdated" ?
6128 "plugins.hide_infobar_for_outdated_plugin" :
6129 "plugins.hide_infobar_for_missing_plugin";
6130 if (gPrefService.getBoolPref(hideBarPrefName))
6133 self.pluginUnavailable(plugin, event.type);
6136 case "PluginDisabled":
6137 self.addLinkClickCallback(plugin, "managePlugins");
6142 newPluginInstalled : function(event) {
6143 // browser elements are anonymous so we can't just use target.
6144 var browser = event.originalTarget;
6145 // clear the plugin list, now that at least one plugin has been installed
6146 browser.missingPlugins = null;
6148 var notificationBox = gBrowser.getNotificationBox(browser);
6149 var notification = notificationBox.getNotificationWithValue("missing-plugins");
6151 notificationBox.removeNotification(notification);
6153 // reload the browser to make the new plugin show.
6157 // Callback for user clicking on a missing (unsupported) plugin.
6158 installSinglePlugin: function (aEvent) {
6159 var missingPluginsArray = {};
6161 var pluginInfo = getPluginInfo(aEvent.target);
6162 missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
6164 openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
6165 "PFSWindow", "chrome,centerscreen,resizable=yes",
6166 {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
6169 // Callback for user clicking on a disabled plugin
6170 managePlugins: function (aEvent) {
6171 BrowserOpenAddonsMgr("addons://list/plugin");
6174 // Callback for user clicking "submit a report" link
6175 submitReport : function(pluginDumpID, browserDumpID) {
6176 // The crash reporter wants a DOM element it can append an IFRAME to,
6177 // which it uses to submit a form. Let's just give it gBrowser.
6178 this.CrashSubmit.submit(pluginDumpID, gBrowser, null, null);
6180 this.CrashSubmit.submit(browserDumpID, gBrowser, null, null);
6183 // Callback for user clicking a "reload page" link
6184 reloadPage: function (browser) {
6188 // Callback for user clicking the help icon
6189 openHelpPage: function () {
6190 openHelpLink("plugin-crashed", false);
6194 // event listener for missing/blocklisted/outdated plugins.
6195 pluginUnavailable: function (plugin, eventType) {
6196 let browser = gBrowser.getBrowserForDocument(plugin.ownerDocument
6197 .defaultView.top.document);
6198 if (!browser.missingPlugins)
6199 browser.missingPlugins = {};
6201 var pluginInfo = getPluginInfo(plugin);
6202 browser.missingPlugins[pluginInfo.mimetype] = pluginInfo;
6204 var notificationBox = gBrowser.getNotificationBox(browser);
6206 // Should only display one of these warnings per page.
6207 // In order of priority, they are: outdated > missing > blocklisted
6208 let outdatedNotification = notificationBox.getNotificationWithValue("outdated-plugins");
6209 let blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins");
6210 let missingNotification = notificationBox.getNotificationWithValue("missing-plugins");
6212 // If there is already an outdated plugin notification then do nothing
6213 if (outdatedNotification)
6216 function showBlocklistInfo() {
6217 var url = formatURL("extensions.blocklist.detailsURL", true);
6218 gBrowser.loadOneTab(url, {inBackground: false});
6222 function showOutdatedPluginsInfo() {
6223 gPrefService.setBoolPref("plugins.update.notifyUser", false);
6224 var url = formatURL("plugins.update.url", true);
6225 gBrowser.loadOneTab(url, {inBackground: false});
6229 function showPluginsMissing() {
6230 // get the urls of missing plugins
6231 var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins;
6232 if (missingPluginsArray) {
6233 openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
6234 "PFSWindow", "chrome,centerscreen,resizable=yes",
6235 {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
6239 let notifications = {
6240 PluginBlocklisted : {
6241 barID : "blocked-plugins",
6242 iconURL : "chrome://mozapps/skin/plugins/notifyPluginBlocked.png",
6243 message : gNavigatorBundle.getString("blockedpluginsMessage.title"),
6245 label : gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"),
6246 accessKey : gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"),
6248 callback : showBlocklistInfo
6251 label : gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"),
6252 accessKey : gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"),
6254 callback : showOutdatedPluginsInfo
6258 barID : "outdated-plugins",
6259 iconURL : "chrome://mozapps/skin/plugins/notifyPluginOutdated.png",
6260 message : gNavigatorBundle.getString("outdatedpluginsMessage.title"),
6262 label : gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"),
6263 accessKey : gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"),
6265 callback : showOutdatedPluginsInfo
6269 barID : "missing-plugins",
6270 iconURL : "chrome://mozapps/skin/plugins/notifyPluginGeneric.png",
6271 message : gNavigatorBundle.getString("missingpluginsMessage.title"),
6273 label : gNavigatorBundle.getString("missingpluginsMessage.button.label"),
6274 accessKey : gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"),
6276 callback : showPluginsMissing
6281 if (eventType == "PluginBlocklisted") {
6282 if (blockedNotification || missingNotification)
6285 else if (eventType == "PluginOutdated") {
6286 // Cancel any notification about blocklisting/missing plugins
6287 if (blockedNotification)
6288 blockedNotification.close();
6289 if (missingNotification)
6290 missingNotification.close();
6292 else if (eventType == "PluginNotFound") {
6293 if (missingNotification)
6296 // Cancel any notification about blocklisting plugins
6297 if (blockedNotification)
6298 blockedNotification.close();
6301 let notify = notifications[eventType];
6302 notificationBox.appendNotification(notify.message, notify.barID, notify.iconURL,
6303 notificationBox.PRIORITY_WARNING_MEDIUM,
6307 // Crashed-plugin observer. Notified once per plugin crash, before events
6308 // are dispatched to individual plugin instances.
6309 pluginCrashed : function(subject, topic, data) {
6310 let propertyBag = subject;
6311 if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
6312 !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
6315 #ifdef MOZ_CRASHREPORTER
6316 let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
6317 let browserDumpID= propertyBag.getPropertyAsAString("browserDumpID");
6318 let shouldSubmit = gCrashReporter.submitReports;
6319 let doPrompt = true; // XXX followup to get via gCrashReporter
6321 // Submit automatically when appropriate.
6322 if (pluginDumpID && shouldSubmit && !doPrompt) {
6323 this.submitReport(pluginDumpID, browserDumpID);
6324 // Submission is async, so we can't easily show failure UI.
6325 propertyBag.setPropertyAsBool("submittedCrashReport", true);
6330 // Crashed-plugin event listener. Called for every instance of a
6331 // plugin in content.
6332 pluginInstanceCrashed: function (plugin, aEvent) {
6333 // Ensure the plugin and event are of the right type.
6334 if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent))
6337 let submittedReport = aEvent.getData("submittedCrashReport");
6338 let doPrompt = true; // XXX followup for .getData("doPrompt");
6339 let submitReports = true; // XXX followup for .getData("submitReports");
6340 let pluginName = aEvent.getData("pluginName");
6341 let pluginFilename = aEvent.getData("pluginFilename");
6342 let pluginDumpID = aEvent.getData("pluginDumpID");
6343 let browserDumpID = aEvent.getData("browserDumpID");
6345 // Remap the plugin name to a more user-presentable form.
6346 pluginName = this.makeNicePluginName(pluginName, pluginFilename);
6348 // Force a style flush, so that we ensure our binding is attached.
6351 let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
6354 // Configure the crashed-plugin placeholder.
6356 let doc = plugin.ownerDocument;
6357 let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
6359 // The binding has role="link" here, since missing/disabled/blocked
6360 // plugin UI has a onclick handler on the whole thing. This isn't needed
6361 // for the plugin-crashed UI, because we use actual HTML links in the text.
6362 overlay.removeAttribute("role");
6364 let statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus");
6365 #ifdef MOZ_CRASHREPORTER
6368 // Determine which message to show regarding crash reports.
6369 if (submittedReport) { // submitReports && !doPrompt, handled in observer
6370 status = "submitted";
6372 else if (!submitReports && !doPrompt) {
6373 status = "noSubmit";
6377 // XXX can we make the link target actually be blank?
6378 let pleaseLink = doc.getAnonymousElementByAttribute(
6379 plugin, "class", "pleaseSubmitLink");
6380 this.addLinkClickCallback(pleaseLink, "submitReport",
6381 pluginDumpID, browserDumpID);
6384 // If we don't have a minidumpID, we can't (or didn't) submit anything.
6385 // This can happen if the plugin is killed from the task manager.
6386 if (!pluginDumpID) {
6387 status = "noReport";
6390 statusDiv.setAttribute("status", status);
6392 let bottomLinks = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgBottomLinks");
6393 bottomLinks.style.display = "block";
6394 let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
6395 this.addLinkClickCallback(helpIcon, "openHelpPage");
6397 // If we're showing the link to manually trigger report submission, we'll
6398 // want to be able to update all the instances of the UI for this crash to
6399 // show an updated message when a report is submitted.
6402 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
6403 Ci.nsISupportsWeakReference]),
6404 observe : function(subject, topic, data) {
6405 let propertyBag = subject;
6406 if (!(propertyBag instanceof Ci.nsIPropertyBag2))
6408 // Ignore notifications for other crashes.
6409 if (propertyBag.get("minidumpID") != pluginDumpID)
6411 statusDiv.setAttribute("status", data);
6414 handleEvent : function(event) {
6415 // Not expected to be called, just here for the closure.
6419 // Use a weak reference, so we don't have to remove it...
6420 Services.obs.addObserver(observer, "crash-report-status", true);
6421 // ...alas, now we need something to hold a strong reference to prevent
6422 // it from being GC. But I don't want to manually manage the reference's
6423 // lifetime (which should be no greater than the page).
6424 // Clever solution? Use a closue with an event listener on the document.
6425 // When the doc goes away, so do the listener references and the closure.
6426 doc.addEventListener("mozCleverClosureHack", observer, false);
6430 let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
6431 crashText.textContent = messageString;
6433 let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
6435 let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
6436 this.addLinkClickCallback(link, "reloadPage", browser);
6438 let notificationBox = gBrowser.getNotificationBox(browser);
6440 // Is the <object>'s size too small to hold what we want to show?
6441 if (this.isTooSmall(plugin, overlay)) {
6442 // Hide the overlay's contents. Use visibility style, so that it
6443 // doesn't collapse down to 0x0.
6444 overlay.style.visibility = "hidden";
6445 // If another plugin on the page was large enough to show our UI, we
6446 // don't want to show a notification bar.
6447 if (!doc.mozNoPluginCrashedNotification)
6448 showNotificationBar(pluginDumpID, browserDumpID);
6450 // If a previous plugin on the page was too small and resulted in
6451 // adding a notification bar, then remove it because this plugin
6452 // instance it big enough to serve as in-content notification.
6453 hideNotificationBar();
6454 doc.mozNoPluginCrashedNotification = true;
6457 function hideNotificationBar() {
6458 let notification = notificationBox.getNotificationWithValue("plugin-crashed");
6460 notificationBox.removeNotification(notification, true);
6463 function showNotificationBar(pluginDumpID, browserDumpID) {
6464 // If there's already an existing notification bar, don't do anything.
6465 let notification = notificationBox.getNotificationWithValue("plugin-crashed");
6469 // Configure the notification bar
6470 let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
6471 let iconURL = "chrome://mozapps/skin/plugins/notifyPluginCrashed.png";
6472 let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
6473 let reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
6474 let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
6475 let submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
6479 accessKey: reloadKey,
6481 callback: function() { browser.reload(); },
6483 #ifdef MOZ_CRASHREPORTER
6484 let submitButton = {
6486 accessKey: submitKey,
6488 callback: function() { gPluginHandler.submitReport(pluginDumpID, browserDumpID); },
6491 buttons.push(submitButton);
6494 let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
6495 iconURL, priority, buttons);
6497 // Add the "learn more" link.
6498 let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
6499 let link = notification.ownerDocument.createElementNS(XULNS, "label");
6500 link.className = "text-link";
6501 link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
6502 link.href = gPluginHandler.crashReportHelpURL;
6503 let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
6504 description.appendChild(link);
6506 // Remove the notfication when the page is reloaded.
6507 doc.defaultView.top.addEventListener("unload", function() {
6508 notificationBox.removeNotification(notification);
6515 function convertFromUnicode(charset, str)
6518 var unicodeConverter = Components
6519 .classes["@mozilla.org/intl/scriptableunicodeconverter"]
6520 .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
6521 unicodeConverter.charset = charset;
6522 str = unicodeConverter.ConvertFromUnicode(str);
6523 return str + unicodeConverter.Finish();
6530 * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
6531 * and shows UI when they are discovered.
6535 * The click handler for the Feed icon in the location bar. Opens the
6536 * subscription page if user is not given a choice of feeds.
6537 * (Otherwise the list of available feeds will be presented to the
6538 * user in a popup menu.)
6540 onFeedButtonClick: function(event) {
6541 event.stopPropagation();
6543 if (event.target.hasAttribute("feed") &&
6544 event.eventPhase == Event.AT_TARGET &&
6545 (event.button == 0 || event.button == 1)) {
6546 this.subscribeToFeed(null, event);
6551 * Called when the user clicks on the Feed icon in the location bar.
6552 * Builds a menu of unique feeds associated with the page, and if there
6553 * is only one, shows the feed inline in the browser window.
6555 * The feed list menupopup to be populated.
6556 * @returns true if the menu should be shown, false if there was only
6557 * one feed and the feed should be shown inline in the browser
6558 * window (do not show the menupopup).
6560 buildFeedList: function(menuPopup) {
6561 var feeds = gBrowser.selectedBrowser.feeds;
6562 if (feeds == null) {
6563 // XXX hack -- menu opening depends on setting of an "open"
6564 // attribute, and the menu refuses to open if that attribute is
6565 // set (because it thinks it's already open). onpopupshowing gets
6566 // called after the attribute is unset, and it doesn't get unset
6567 // if we return false. so we unset it here; otherwise, the menu
6568 // refuses to work past this point.
6569 menuPopup.parentNode.removeAttribute("open");
6573 while (menuPopup.firstChild)
6574 menuPopup.removeChild(menuPopup.firstChild);
6576 if (feeds.length == 1) {
6577 var feedButton = document.getElementById("feed-button");
6579 feedButton.setAttribute("feed", feeds[0].href);
6583 // Build the menu showing the available feed choices for viewing.
6584 for (var i = 0; i < feeds.length; ++i) {
6585 var feedInfo = feeds[i];
6586 var menuItem = document.createElement("menuitem");
6587 var baseTitle = feedInfo.title || feedInfo.href;
6588 var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
6589 menuItem.setAttribute("class", "feed-menuitem");
6590 menuItem.setAttribute("label", labelStr);
6591 menuItem.setAttribute("feed", feedInfo.href);
6592 menuItem.setAttribute("tooltiptext", feedInfo.href);
6593 menuItem.setAttribute("crop", "center");
6594 menuPopup.appendChild(menuItem);
6600 * Subscribe to a given feed. Called when
6601 * 1. Page has a single feed and user clicks feed icon in location bar
6602 * 2. Page has a single feed and user selects Subscribe menu item
6603 * 3. Page has multiple feeds and user selects from feed icon popup
6604 * 4. Page has multiple feeds and user selects from Subscribe submenu
6606 * The feed to subscribe to. May be null, in which case the
6607 * event target's feed attribute is examined.
6609 * The event this method is handling. Used to decide where
6610 * to open the preview UI. (Optional, unless href is null)
6612 subscribeToFeed: function(href, event) {
6613 // Just load the feed in the content area to either subscribe or show the
6616 href = event.target.getAttribute("feed");
6617 urlSecurityCheck(href, gBrowser.contentPrincipal,
6618 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
6619 var feedURI = makeURI(href, document.characterSet);
6620 // Use the feed scheme so X-Moz-Is-Feed will be set
6621 // The value doesn't matter
6622 if (/^https?/.test(feedURI.scheme))
6623 href = "feed:" + href;
6624 this.loadFeed(href, event);
6627 loadFeed: function(href, event) {
6628 var feeds = gBrowser.selectedBrowser.feeds;
6630 openUILink(href, event, false, true, false, null);
6633 // We might default to a livebookmarks modal dialog,
6634 // so reset that if the user happens to click it again
6635 gBrowser.selectedBrowser.feeds = feeds;
6640 * Update the browser UI to show whether or not feeds are available when
6641 * a page is loaded or the user switches tabs to a page that has feeds.
6643 updateFeeds: function() {
6644 var feedButton = document.getElementById("feed-button");
6645 if (!this._feedMenuitem)
6646 this._feedMenuitem = document.getElementById("subscribeToPageMenuitem");
6647 if (!this._feedMenupopup)
6648 this._feedMenupopup = document.getElementById("subscribeToPageMenupopup");
6650 var feeds = gBrowser.selectedBrowser.feeds;
6651 if (!feeds || feeds.length == 0) {
6653 feedButton.collapsed = true;
6654 feedButton.removeAttribute("feed");
6656 this._feedMenuitem.setAttribute("disabled", "true");
6657 this._feedMenupopup.setAttribute("hidden", "true");
6658 this._feedMenuitem.removeAttribute("hidden");
6661 feedButton.collapsed = false;
6663 if (feeds.length > 1) {
6664 this._feedMenuitem.setAttribute("hidden", "true");
6665 this._feedMenupopup.removeAttribute("hidden");
6667 feedButton.removeAttribute("feed");
6670 feedButton.setAttribute("feed", feeds[0].href);
6672 this._feedMenuitem.setAttribute("feed", feeds[0].href);
6673 this._feedMenuitem.removeAttribute("disabled");
6674 this._feedMenuitem.removeAttribute("hidden");
6675 this._feedMenupopup.setAttribute("hidden", "true");
6680 addFeed: function(link, targetDoc) {
6681 // find which tab this is for, and set the attribute on the browser
6682 var browserForLink = gBrowser.getBrowserForDocument(targetDoc);
6683 if (!browserForLink) {
6684 // ignore feeds loaded in subframes (see bug 305472)
6688 if (!browserForLink.feeds)
6689 browserForLink.feeds = [];
6691 browserForLink.feeds.push({ href: link.href, title: link.title });
6693 if (browserForLink == gBrowser.selectedBrowser) {
6694 var feedButton = document.getElementById("feed-button");
6696 feedButton.collapsed = false;
6702 * Re-open a closed tab.
6704 * The index of the tab (via nsSessionStore.getClosedTabData)
6705 * @returns a reference to the reopened tab.
6707 function undoCloseTab(aIndex) {
6708 // wallpaper patch to prevent an unnecessary blank tab (bug 343895)
6709 var blankTabToRemove = null;
6710 if (gBrowser.tabs.length == 1 &&
6711 !gPrefService.getBoolPref("browser.tabs.autoHide") &&
6712 isTabEmpty(gBrowser.selectedTab))
6713 blankTabToRemove = gBrowser.selectedTab;
6716 var ss = Cc["@mozilla.org/browser/sessionstore;1"].
6717 getService(Ci.nsISessionStore);
6718 if (ss.getClosedTabCount(window) > (aIndex || 0)) {
6719 tab = ss.undoCloseTab(window, aIndex || 0);
6721 if (blankTabToRemove)
6722 gBrowser.removeTab(blankTabToRemove);
6729 * Re-open a closed window.
6731 * The index of the window (via nsSessionStore.getClosedWindowData)
6732 * @returns a reference to the reopened window.
6734 function undoCloseWindow(aIndex) {
6735 let ss = Cc["@mozilla.org/browser/sessionstore;1"].
6736 getService(Ci.nsISessionStore);
6738 if (ss.getClosedWindowCount() > (aIndex || 0))
6739 window = ss.undoCloseWindow(aIndex || 0);
6745 * Determines if a tab is "empty", usually used in the context of determining
6746 * if it's ok to close the tab.
6748 function isTabEmpty(aTab) {
6749 let browser = aTab.linkedBrowser;
6750 return browser.sessionHistory.count < 2 &&
6751 browser.currentURI.spec == "about:blank" &&
6752 !browser.contentDocument.body.hasChildNodes() &&
6753 !aTab.hasAttribute("busy");
6756 #ifdef MOZ_SERVICES_SYNC
6757 function BrowserOpenSyncTabs() {
6758 switchToTabHavingURI("about:sync-tabs", true);
6765 * echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
6766 * > https://addons.mozilla.org/en-US/firefox/3.0a1/
6768 * Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
6770 function formatURL(aFormat, aIsPref) {
6771 var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
6772 return aIsPref ? formatter.formatURLPref(aFormat) : formatter.formatURL(aFormat);
6776 * This also takes care of updating the command enabled-state when tabs are
6777 * created or removed.
6779 var gBookmarkAllTabsHandler = {
6781 this._command = document.getElementById("Browser:BookmarkAllTabs");
6782 gBrowser.tabContainer.addEventListener("TabOpen", this, true);
6783 gBrowser.tabContainer.addEventListener("TabClose", this, true);
6784 this._updateCommandState();
6787 _updateCommandState: function BATH__updateCommandState(aTabClose) {
6788 var numTabs = gBrowser.tabs.length;
6790 // The TabClose event is fired before the tab is removed from the DOM
6795 this._command.removeAttribute("disabled");
6797 this._command.setAttribute("disabled", "true");
6800 doCommand: function BATH_doCommand() {
6801 PlacesCommandHook.bookmarkCurrentPages();
6804 // nsIDOMEventListener
6805 handleEvent: function(aEvent) {
6806 this._updateCommandState(aEvent.type == "TabClose");
6811 * Utility object to handle manipulations of the identity indicators in the UI
6813 var gIdentityHandler = {
6814 // Mode strings used to control CSS display
6815 IDENTITY_MODE_IDENTIFIED : "verifiedIdentity", // High-quality identity information
6816 IDENTITY_MODE_DOMAIN_VERIFIED : "verifiedDomain", // Minimal SSL CA-signed domain verification
6817 IDENTITY_MODE_UNKNOWN : "unknownIdentity", // No trusted identity information
6818 IDENTITY_MODE_MIXED_CONTENT : "unknownIdentity mixedContent", // SSL with unauthenticated content
6820 // Cache the most recent SSLStatus and Location seen in checkIdentity
6822 _lastLocation : null,
6825 get _encryptionLabel () {
6826 delete this._encryptionLabel;
6827 this._encryptionLabel = {};
6828 this._encryptionLabel[this.IDENTITY_MODE_DOMAIN_VERIFIED] =
6829 gNavigatorBundle.getString("identity.encrypted");
6830 this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
6831 gNavigatorBundle.getString("identity.encrypted");
6832 this._encryptionLabel[this.IDENTITY_MODE_UNKNOWN] =
6833 gNavigatorBundle.getString("identity.unencrypted");
6834 this._encryptionLabel[this.IDENTITY_MODE_MIXED_CONTENT] =
6835 gNavigatorBundle.getString("identity.mixed_content");
6836 return this._encryptionLabel;
6838 get _identityPopup () {
6839 delete this._identityPopup;
6840 return this._identityPopup = document.getElementById("identity-popup");
6842 get _identityBox () {
6843 delete this._identityBox;
6844 return this._identityBox = document.getElementById("identity-box");
6846 get _identityPopupContentBox () {
6847 delete this._identityPopupContentBox;
6848 return this._identityPopupContentBox =
6849 document.getElementById("identity-popup-content-box");
6851 get _identityPopupContentHost () {
6852 delete this._identityPopupContentHost;
6853 return this._identityPopupContentHost =
6854 document.getElementById("identity-popup-content-host");
6856 get _identityPopupContentOwner () {
6857 delete this._identityPopupContentOwner;
6858 return this._identityPopupContentOwner =
6859 document.getElementById("identity-popup-content-owner");
6861 get _identityPopupContentSupp () {
6862 delete this._identityPopupContentSupp;
6863 return this._identityPopupContentSupp =
6864 document.getElementById("identity-popup-content-supplemental");
6866 get _identityPopupContentVerif () {
6867 delete this._identityPopupContentVerif;
6868 return this._identityPopupContentVerif =
6869 document.getElementById("identity-popup-content-verifier");
6871 get _identityPopupEncLabel () {
6872 delete this._identityPopupEncLabel;
6873 return this._identityPopupEncLabel =
6874 document.getElementById("identity-popup-encryption-label");
6876 get _identityIconLabel () {
6877 delete this._identityIconLabel;
6878 return this._identityIconLabel = document.getElementById("identity-icon-label");
6880 get _overrideService () {
6881 delete this._overrideService;
6882 return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
6883 .getService(Ci.nsICertOverrideService);
6885 get _identityIconCountryLabel () {
6886 delete this._identityIconCountryLabel;
6887 return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
6891 * Rebuild cache of the elements that may or may not exist depending
6892 * on whether there's a location bar.
6894 _cacheElements : function() {
6895 delete this._identityBox;
6896 delete this._identityIconLabel;
6897 delete this._identityIconCountryLabel;
6898 this._identityBox = document.getElementById("identity-box");
6899 this._identityIconLabel = document.getElementById("identity-icon-label");
6900 this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
6904 * Handler for mouseclicks on the "More Information" button in the
6905 * "identity-popup" panel.
6907 handleMoreInfoClick : function(event) {
6908 displaySecurityInfo();
6909 event.stopPropagation();
6913 * Helper to parse out the important parts of _lastStatus (of the SSL cert in
6914 * particular) for use in constructing identity UI strings
6916 getIdentityData : function() {
6918 var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
6919 var cert = status.serverCert;
6921 // Human readable name of Subject
6922 result.subjectOrg = cert.organization;
6924 // SubjectName fields, broken up for individual access
6925 if (cert.subjectName) {
6926 result.subjectNameFields = {};
6927 cert.subjectName.split(",").forEach(function(v) {
6928 var field = v.split("=");
6929 this[field[0]] = field[1];
6930 }, result.subjectNameFields);
6932 // Call out city, state, and country specifically
6933 result.city = result.subjectNameFields.L;
6934 result.state = result.subjectNameFields.ST;
6935 result.country = result.subjectNameFields.C;
6938 // Human readable name of Certificate Authority
6939 result.caOrg = cert.issuerOrganization || cert.issuerCommonName;
6946 * Determine the identity of the page being displayed by examining its SSL cert
6947 * (if available) and, if necessary, update the UI to reflect this. Intended to
6948 * be called by onSecurityChange
6950 * @param PRUint32 state
6951 * @param JS Object location that mirrors an nsLocation (i.e. has .host and
6952 * .hostname and .port)
6954 checkIdentity : function(state, location) {
6955 var currentStatus = gBrowser.securityUI
6956 .QueryInterface(Components.interfaces.nsISSLStatusProvider)
6958 this._lastStatus = currentStatus;
6959 this._lastLocation = location;
6961 let nsIWebProgressListener = Ci.nsIWebProgressListener;
6962 if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
6963 this.setMode(this.IDENTITY_MODE_IDENTIFIED);
6964 else if (state & nsIWebProgressListener.STATE_SECURE_HIGH)
6965 this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
6966 else if (state & nsIWebProgressListener.STATE_IS_BROKEN)
6967 this.setMode(this.IDENTITY_MODE_MIXED_CONTENT);
6969 this.setMode(this.IDENTITY_MODE_UNKNOWN);
6973 * Return the eTLD+1 version of the current hostname
6975 getEffectiveHost : function() {
6976 // Cache the eTLDService if this is our first time through
6977 if (!this._eTLDService)
6978 this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
6979 .getService(Ci.nsIEffectiveTLDService);
6981 return this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
6983 // If something goes wrong (e.g. hostname is an IP address) just fail back
6984 // to the full domain.
6985 return this._lastLocation.hostname;
6990 * Update the UI to reflect the specified mode, which should be one of the
6991 * IDENTITY_MODE_* constants.
6993 setMode : function(newMode) {
6994 if (!this._identityBox) {
6995 // No identity box means the identity box is not visible, in which
6996 // case there's nothing to do.
7000 this._identityBox.className = newMode;
7001 this.setIdentityMessages(newMode);
7003 // Update the popup too, if it's open
7004 if (this._identityPopup.state == "open")
7005 this.setPopupMessages(newMode);
7009 * Set up the messages for the primary identity UI based on the specified mode,
7010 * and the details of the SSL cert, where applicable
7012 * @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
7014 setIdentityMessages : function(newMode) {
7015 if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
7016 var iData = this.getIdentityData();
7018 // It would be sort of nice to use the CN= field in the cert, since that's
7019 // typically what we want here, but thanks to x509 certs being extensible,
7020 // it's not the only place you have to check, there can be more than one domain,
7021 // et cetera, ad nauseum. We know the cert is valid for location.host, so
7022 // let's just use that. Check the pref to determine how much of the verified
7024 var icon_label = "";
7025 var icon_country_label = "";
7026 var icon_labels_dir = "ltr";
7027 switch (gPrefService.getIntPref("browser.identity.ssl_domain_display")) {
7028 case 2 : // Show full domain
7029 icon_label = this._lastLocation.hostname;
7031 case 1 : // Show eTLD.
7032 icon_label = this.getEffectiveHost();
7035 // We need a port number for all lookups. If one hasn't been specified, use
7036 // the https default
7037 var lookupHost = this._lastLocation.host;
7038 if (lookupHost.indexOf(':') < 0)
7039 lookupHost += ":443";
7041 // Verifier is either the CA Org, for a normal cert, or a special string
7042 // for certs that are trusted because of a security exception.
7043 var tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
7046 // Check whether this site is a security exception. XPConnect does the right
7047 // thing here in terms of converting _lastLocation.port from string to int, but
7048 // the overrideService doesn't like undefined ports, so make sure we have
7049 // something in the default case (bug 432241).
7050 if (this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
7051 (this._lastLocation.port || 443),
7052 iData.cert, {}, {}))
7053 tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
7055 else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
7056 // If it's identified, then we can populate the dialog with credentials
7057 iData = this.getIdentityData();
7058 tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
7060 icon_label = iData.subjectOrg;
7062 icon_country_label = "(" + iData.country + ")";
7063 // If the organization name starts with an RTL character, then
7064 // swap the positions of the organization and country code labels.
7065 // The Unicode ranges reflect the definition of the UCS2_CHAR_IS_BIDI
7066 // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
7067 // fixed, this test should be replaced by one adhering to the
7068 // Unicode Bidirectional Algorithm proper (at the paragraph level).
7069 icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/.test(icon_label) ?
7073 tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
7075 icon_country_label = "";
7076 icon_labels_dir = "ltr";
7079 // Push the appropriate strings out to the UI
7080 this._identityBox.tooltipText = tooltip;
7081 this._identityIconLabel.value = icon_label;
7082 this._identityIconCountryLabel.value = icon_country_label;
7083 // Set cropping and direction
7084 this._identityIconLabel.crop = icon_country_label ? "end" : "center";
7085 this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
7086 // Hide completely if the organization label is empty
7087 this._identityIconLabel.parentNode.collapsed = icon_label ? false : true;
7091 * Set up the title and content messages for the identity message popup,
7092 * based on the specified mode, and the details of the SSL cert, where
7095 * @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
7097 setPopupMessages : function(newMode) {
7099 this._identityPopup.className = newMode;
7100 this._identityPopupContentBox.className = newMode;
7102 // Set the static strings up front
7103 this._identityPopupEncLabel.textContent = this._encryptionLabel[newMode];
7105 // Initialize the optional strings to empty values
7106 var supplemental = "";
7109 if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
7110 var iData = this.getIdentityData();
7111 var host = this.getEffectiveHost();
7112 var owner = gNavigatorBundle.getString("identity.ownerUnknown2");
7113 verifier = this._identityBox.tooltipText;
7116 else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
7117 // If it's identified, then we can populate the dialog with credentials
7118 iData = this.getIdentityData();
7119 host = this.getEffectiveHost();
7120 owner = iData.subjectOrg;
7121 verifier = this._identityBox.tooltipText;
7123 // Build an appropriate supplemental block out of whatever location data we have
7125 supplemental += iData.city + "\n";
7126 if (iData.state && iData.country)
7127 supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
7128 [iData.state, iData.country]);
7129 else if (iData.state) // State only
7130 supplemental += iData.state;
7131 else if (iData.country) // Country only
7132 supplemental += iData.country;
7135 // These strings will be hidden in CSS anyhow
7140 // Push the appropriate strings out to the UI
7141 this._identityPopupContentHost.textContent = host;
7142 this._identityPopupContentOwner.textContent = owner;
7143 this._identityPopupContentSupp.textContent = supplemental;
7144 this._identityPopupContentVerif.textContent = verifier;
7147 hideIdentityPopup : function() {
7148 this._identityPopup.hidePopup();
7152 * Click handler for the identity-box element in primary chrome.
7154 handleIdentityButtonEvent : function(event) {
7156 event.stopPropagation();
7158 if ((event.type == "click" && event.button != 0) ||
7159 (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
7160 event.keyCode != KeyEvent.DOM_VK_RETURN))
7161 return; // Left click, space or enter only
7163 // Revert the contents of the location bar, see bug 406779
7164 gURLBar.handleRevert();
7166 // Make sure that the display:none style we set in xul is removed now that
7167 // the popup is actually needed
7168 this._identityPopup.hidden = false;
7170 // Tell the popup to consume dismiss clicks, to avoid bug 395314
7171 this._identityPopup.popupBoxObject
7172 .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
7174 // Update the popup strings
7175 this.setPopupMessages(this._identityBox.className);
7177 // Make sure the identity popup hangs toward the middle of the location bar
7179 var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_end' : 'after_start';
7181 // Add the "open" attribute to the identity box for styling
7182 this._identityBox.setAttribute("open", "true");
7184 this._identityPopup.addEventListener("popuphidden", function (e) {
7185 e.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
7186 self._identityBox.removeAttribute("open");
7189 // Now open the popup, anchored off the primary chrome element
7190 this._identityPopup.openPopup(this._identityBox, position);
7194 let DownloadMonitorPanel = {
7195 //////////////////////////////////////////////////////////////////////////////
7196 //// DownloadMonitorPanel Member Variables
7201 _lastTime: Infinity,
7204 get DownloadUtils() {
7205 delete this.DownloadUtils;
7206 Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
7207 return this.DownloadUtils;
7210 //////////////////////////////////////////////////////////////////////////////
7211 //// DownloadMonitorPanel Public Methods
7214 * Initialize the status panel and member variables
7216 init: function DMP_init() {
7217 // Initialize "private" member variables
7218 this._panel = document.getElementById("download-monitor");
7220 // Cache the status strings
7221 this._activeStr = gNavigatorBundle.getString("activeDownloads1");
7222 this._pausedStr = gNavigatorBundle.getString("pausedDownloads1");
7224 gDownloadMgr.addListener(this);
7225 this._listening = true;
7227 this.updateStatus();
7230 uninit: function DMP_uninit() {
7231 if (this._listening)
7232 gDownloadMgr.removeListener(this);
7235 inited: function DMP_inited() {
7236 return this._panel != null;
7240 * Update status based on the number of active and paused downloads
7242 updateStatus: function DMP_updateStatus() {
7246 let numActive = gDownloadMgr.activeDownloadCount;
7248 // Hide the panel and reset the "last time" if there's no downloads
7249 if (numActive == 0) {
7250 this._panel.hidden = true;
7251 this._lastTime = Infinity;
7256 // Find the download with the longest remaining time
7258 let maxTime = -Infinity;
7259 let dls = gDownloadMgr.activeDownloads;
7260 while (dls.hasMoreElements()) {
7261 let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
7262 if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
7263 // Figure out if this download takes longer
7264 if (dl.speed > 0 && dl.size > 0)
7265 maxTime = Math.max(maxTime, (dl.size - dl.amountTransferred) / dl.speed);
7269 else if (dl.state == gDownloadMgr.DOWNLOAD_PAUSED)
7273 // Get the remaining time string and last sec for time estimation
7275 [timeLeft, this._lastTime] =
7276 this.DownloadUtils.getTimeLeft(maxTime, this._lastTime);
7278 // Figure out how many downloads are currently downloading
7279 let numDls = numActive - numPaused;
7280 let status = this._activeStr;
7282 // If all downloads are paused, show the paused message instead
7285 status = this._pausedStr;
7288 // Get the correct plural form and insert the number of downloads and time
7289 // left message if necessary
7290 status = PluralForm.get(numDls, status);
7291 status = status.replace("#1", numDls);
7292 status = status.replace("#2", timeLeft);
7294 // Update the panel and show it
7295 this._panel.label = status;
7296 this._panel.hidden = false;
7299 //////////////////////////////////////////////////////////////////////////////
7300 //// nsIDownloadProgressListener
7303 * Update status for download progress changes
7305 onProgressChange: function() {
7306 this.updateStatus();
7310 * Update status for download state changes
7312 onDownloadStateChange: function() {
7313 this.updateStatus();
7316 onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus, aDownload) {
7319 onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) {
7322 //////////////////////////////////////////////////////////////////////////////
7325 QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener]),
7328 function getNotificationBox(aWindow) {
7329 var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
7331 return gBrowser.getNotificationBox(foundBrowser)
7336 function getBrowser() gBrowser;
7337 function getNavToolbox() gNavToolbox;
7339 let gPrivateBrowsingUI = {
7340 _privateBrowsingService: null,
7341 _searchBarValue: null,
7342 _findBarValue: null,
7344 init: function PBUI_init() {
7345 Services.obs.addObserver(this, "private-browsing", false);
7346 Services.obs.addObserver(this, "private-browsing-transition-complete", false);
7348 this._privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"].
7349 getService(Ci.nsIPrivateBrowsingService);
7351 if (this.privateBrowsingEnabled)
7352 this.onEnterPrivateBrowsing(true);
7355 uninit: function PBUI_unint() {
7356 Services.obs.removeObserver(this, "private-browsing");
7357 Services.obs.removeObserver(this, "private-browsing-transition-complete");
7360 get _disableUIOnToggle() {
7361 if (this._privateBrowsingService.autoStarted)
7365 return !gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session");
7372 observe: function PBUI_observe(aSubject, aTopic, aData) {
7373 if (aTopic == "private-browsing") {
7374 if (aData == "enter")
7375 this.onEnterPrivateBrowsing();
7376 else if (aData == "exit")
7377 this.onExitPrivateBrowsing();
7379 else if (aTopic == "private-browsing-transition-complete") {
7380 if (this._disableUIOnToggle) {
7381 // use setTimeout here in order to make the code testable
7382 setTimeout(function() {
7383 document.getElementById("Tools:PrivateBrowsing")
7384 .removeAttribute("disabled");
7390 _shouldEnter: function PBUI__shouldEnter() {
7392 // Never prompt if the session is not going to be closed, or if user has
7393 // already requested not to be prompted.
7394 if (gPrefService.getBoolPref("browser.privatebrowsing.dont_prompt_on_enter") ||
7395 gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session"))
7400 var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
7401 getService(Ci.nsIStringBundleService);
7402 var pbBundle = bundleService.createBundle("chrome://browser/locale/browser.properties");
7403 var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
7405 var appName = brandBundle.GetStringFromName("brandShortName");
7406 # On Mac, use the header as the title.
7408 var dialogTitle = pbBundle.GetStringFromName("privateBrowsingMessageHeader");
7411 var dialogTitle = pbBundle.GetStringFromName("privateBrowsingDialogTitle");
7412 var header = pbBundle.GetStringFromName("privateBrowsingMessageHeader") + "\n\n";
7414 var message = pbBundle.formatStringFromName("privateBrowsingMessage", [appName], 1);
7416 var ps = Services.prompt;
7418 var flags = ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
7419 ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
7420 ps.BUTTON_POS_0_DEFAULT;
7422 var neverAsk = {value:false};
7423 var button0Title = pbBundle.GetStringFromName("privateBrowsingYesTitle");
7424 var button1Title = pbBundle.GetStringFromName("privateBrowsingNoTitle");
7425 var neverAskText = pbBundle.GetStringFromName("privateBrowsingNeverAsk");
7428 var choice = ps.confirmEx(null, dialogTitle, header + message,
7429 flags, button0Title, button1Title, null,
7430 neverAskText, neverAsk);
7433 case 0: // Start Private Browsing
7436 gPrefService.setBoolPref("browser.privatebrowsing.dont_prompt_on_enter", true);
7446 onEnterPrivateBrowsing: function PBUI_onEnterPrivateBrowsing(aOnWindowOpen) {
7447 if (BrowserSearch.searchBar)
7448 this._searchBarValue = BrowserSearch.searchBar.textbox.value;
7450 if (gFindBarInitialized)
7451 this._findBarValue = gFindBar.getElement("findbar-textbox").value;
7453 this._setPBMenuTitle("stop");
7455 document.getElementById("menu_import").setAttribute("disabled", "true");
7457 // Disable the Clear Recent History... menu item when in PB mode
7458 // temporary fix until bug 463607 is fixed
7459 document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
7461 if (this._privateBrowsingService.autoStarted) {
7462 // Disable the menu item in auto-start mode
7463 document.getElementById("privateBrowsingItem")
7464 .setAttribute("disabled", "true");
7465 document.getElementById("Tools:PrivateBrowsing")
7466 .setAttribute("disabled", "true");
7468 else if (window.location.href == getBrowserURL()) {
7469 // Adjust the window's title
7470 let docElement = document.documentElement;
7471 docElement.setAttribute("title",
7472 docElement.getAttribute("title_privatebrowsing"));
7473 docElement.setAttribute("titlemodifier",
7474 docElement.getAttribute("titlemodifier_privatebrowsing"));
7475 docElement.setAttribute("browsingmode", "private");
7476 gBrowser.updateTitlebar();
7479 setTimeout(function () {
7480 DownloadMonitorPanel.updateStatus();
7483 if (!aOnWindowOpen && this._disableUIOnToggle)
7484 document.getElementById("Tools:PrivateBrowsing")
7485 .setAttribute("disabled", "true");
7488 onExitPrivateBrowsing: function PBUI_onExitPrivateBrowsing() {
7489 if (BrowserSearch.searchBar) {
7490 let searchBox = BrowserSearch.searchBar.textbox;
7492 if (this._searchBarValue) {
7493 searchBox.value = this._searchBarValue;
7494 this._searchBarValue = null;
7499 gURLBar.editor.transactionManager.clear();
7502 document.getElementById("menu_import").removeAttribute("disabled");
7504 // Re-enable the Clear Recent History... menu item on exit of PB mode
7505 // temporary fix until bug 463607 is fixed
7506 document.getElementById("Tools:Sanitize").removeAttribute("disabled");
7508 if (gFindBarInitialized) {
7509 let findbox = gFindBar.getElement("findbar-textbox");
7511 if (this._findBarValue) {
7512 findbox.value = this._findBarValue;
7513 this._findBarValue = null;
7517 this._setPBMenuTitle("start");
7519 if (window.location.href == getBrowserURL()) {
7520 // Adjust the window's title
7521 let docElement = document.documentElement;
7522 docElement.setAttribute("title",
7523 docElement.getAttribute("title_normal"));
7524 docElement.setAttribute("titlemodifier",
7525 docElement.getAttribute("titlemodifier_normal"));
7526 docElement.setAttribute("browsingmode", "normal");
7529 // Enable the menu item in after exiting the auto-start mode
7530 document.getElementById("privateBrowsingItem")
7531 .removeAttribute("disabled");
7532 document.getElementById("Tools:PrivateBrowsing")
7533 .removeAttribute("disabled");
7535 gLastOpenDirectory.reset();
7537 setTimeout(function () {
7538 DownloadMonitorPanel.updateStatus();
7541 if (this._disableUIOnToggle)
7542 document.getElementById("Tools:PrivateBrowsing")
7543 .setAttribute("disabled", "true");
7546 _setPBMenuTitle: function PBUI__setPBMenuTitle(aMode) {
7547 let pbMenuItem = document.getElementById("privateBrowsingItem");
7548 pbMenuItem.setAttribute("label", pbMenuItem.getAttribute(aMode + "label"));
7549 pbMenuItem.setAttribute("accesskey", pbMenuItem.getAttribute(aMode + "accesskey"));
7552 toggleMode: function PBUI_toggleMode() {
7553 // prompt the users on entering the private mode, if needed
7554 if (!this.privateBrowsingEnabled)
7555 if (!this._shouldEnter())
7558 this._privateBrowsingService.privateBrowsingEnabled =
7559 !this.privateBrowsingEnabled;
7562 get privateBrowsingEnabled() {
7563 return this._privateBrowsingService.privateBrowsingEnabled;
7567 var LightWeightThemeWebInstaller = {
7568 handleEvent: function (event) {
7569 switch (event.type) {
7570 case "InstallBrowserTheme":
7571 case "PreviewBrowserTheme":
7572 case "ResetBrowserThemePreview":
7573 // ignore requests from background tabs
7574 if (event.target.ownerDocument.defaultView.top != content)
7577 switch (event.type) {
7578 case "InstallBrowserTheme":
7579 this._installRequest(event);
7581 case "PreviewBrowserTheme":
7582 this._preview(event);
7584 case "ResetBrowserThemePreview":
7585 this._resetPreview(event);
7589 this._resetPreview();
7596 Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
7597 delete this._manager;
7598 return this._manager = temp.LightweightThemeManager;
7601 _installRequest: function (event) {
7602 var node = event.target;
7603 var data = this._getThemeFromNode(node);
7607 if (this._isAllowed(node)) {
7608 this._install(data);
7612 var allowButtonText =
7613 gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
7614 var allowButtonAccesskey =
7615 gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
7617 gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
7618 [node.ownerDocument.location.host]);
7620 label: allowButtonText,
7621 accessKey: allowButtonAccesskey,
7622 callback: function () {
7623 LightWeightThemeWebInstaller._install(data);
7627 this._removePreviousNotifications();
7629 var notificationBox = gBrowser.getNotificationBox();
7630 var notificationBar =
7631 notificationBox.appendNotification(message, "lwtheme-install-request", "",
7632 notificationBox.PRIORITY_INFO_MEDIUM,
7634 notificationBar.persistence = 1;
7637 _install: function (newTheme) {
7638 var previousTheme = this._manager.currentTheme;
7639 this._manager.currentTheme = newTheme;
7640 if (this._manager.currentTheme &&
7641 this._manager.currentTheme.id == newTheme.id)
7642 this._postInstallNotification(newTheme, previousTheme);
7645 _postInstallNotification: function (newTheme, previousTheme) {
7647 return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
7651 label: text("undoButton"),
7652 accessKey: text("undoButton.accesskey"),
7653 callback: function () {
7654 LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
7655 LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
7658 label: text("manageButton"),
7659 accessKey: text("manageButton.accesskey"),
7660 callback: function () {
7661 BrowserOpenAddonsMgr("addons://list/theme");
7665 this._removePreviousNotifications();
7667 var notificationBox = gBrowser.getNotificationBox();
7668 var notificationBar =
7669 notificationBox.appendNotification(text("message"),
7670 "lwtheme-install-notification", "",
7671 notificationBox.PRIORITY_INFO_MEDIUM,
7673 notificationBar.persistence = 1;
7674 notificationBar.timeout = Date.now() + 20000; // 20 seconds
7677 _removePreviousNotifications: function () {
7678 var box = gBrowser.getNotificationBox();
7680 ["lwtheme-install-request",
7681 "lwtheme-install-notification"].forEach(function (value) {
7682 var notification = box.getNotificationWithValue(value);
7684 box.removeNotification(notification);
7688 _previewWindow: null,
7689 _preview: function (event) {
7690 if (!this._isAllowed(event.target))
7693 var data = this._getThemeFromNode(event.target);
7697 this._resetPreview();
7699 this._previewWindow = event.target.ownerDocument.defaultView;
7700 this._previewWindow.addEventListener("pagehide", this, true);
7701 gBrowser.tabContainer.addEventListener("TabSelect", this, false);
7703 this._manager.previewTheme(data);
7706 _resetPreview: function (event) {
7707 if (!this._previewWindow ||
7708 event && !this._isAllowed(event.target))
7711 this._previewWindow.removeEventListener("pagehide", this, true);
7712 this._previewWindow = null;
7713 gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
7715 this._manager.resetPreview();
7718 _isAllowed: function (node) {
7719 var pm = Services.perms;
7721 var uri = node.ownerDocument.documentURIObject;
7722 return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
7725 _getThemeFromNode: function (node) {
7726 return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
7732 * Switch to a tab that has a given URI, and focusses its browser window.
7733 * If a matching tab is in this window, it will be switched to. Otherwise, other
7734 * windows will be searched.
7739 * True to open a new tab and switch to it, if no existing tab is found
7740 * @param A callback to call when the tab is open, the tab's browser will be
7741 * passed as an argument
7742 * @return True if a tab was switched to (or opened), false otherwise
7744 function switchToTabHavingURI(aURI, aOpenNew, aCallback) {
7745 function switchIfURIInWindow(aWindow) {
7746 if (!("gBrowser" in aWindow))
7748 let browsers = aWindow.gBrowser.browsers;
7749 for (let i = 0; i < browsers.length; i++) {
7750 let browser = browsers[i];
7751 if (browser.currentURI.equals(aURI)) {
7752 gURLBar.handleRevert();
7753 // We need the current tab so we can check if we should close it
7754 let prevTab = gBrowser.selectedTab;
7755 // Focus the matching window & tab
7757 aWindow.gBrowser.tabContainer.selectedIndex = i;
7760 // Close the previously selected tab if it was empty
7761 if (isTabEmpty(prevTab))
7762 gBrowser.removeTab(prevTab);
7769 // This can be passed either nsIURI or a string.
7770 if (!(aURI instanceof Ci.nsIURI))
7771 aURI = makeURI(aURI);
7773 // Prioritise this window.
7774 if (switchIfURIInWindow(window))
7777 let winEnum = Services.wm.getEnumerator("navigator:browser");
7778 while (winEnum.hasMoreElements()) {
7779 let browserWin = winEnum.getNext();
7780 // Skip closed (but not yet destroyed) windows,
7781 // and the current window (which was checked earlier).
7782 if (browserWin.closed || browserWin == window)
7784 if (switchIfURIInWindow(browserWin))
7788 // No opened tab has that url.
7790 if (isTabEmpty(gBrowser.selectedTab))
7791 gBrowser.selectedBrowser.loadURI(aURI.spec);
7793 gBrowser.selectedTab = gBrowser.addTab(aURI.spec);
7795 let browser = gBrowser.selectedBrowser;
7796 browser.addEventListener("pageshow", function(event) {
7797 if (event.target.location.href != aURI.spec)
7799 browser.removeEventListener("pageshow", arguments.callee, true);
7809 var TabContextMenu = {
7811 updateContextMenu: function updateContextMenu(aPopupMenu) {
7812 this.contextTab = document.popupNode.localName == "tab" ?
7813 document.popupNode : gBrowser.selectedTab;
7814 var disabled = gBrowser.tabs.length == 1;
7816 // Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
7817 document.getElementById("context_closeTab").disabled =
7818 disabled && gBrowser.tabContainer._closeWindowWithLastTab;
7820 var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
7821 for (var i = 0; i < menuItems.length; i++)
7822 menuItems[i].disabled = disabled;
7825 document.getElementById("context_undoCloseTab").disabled =
7826 Cc["@mozilla.org/browser/sessionstore;1"].
7827 getService(Ci.nsISessionStore).
7828 getClosedTabCount(window) == 0;
7830 // Only one of pin/unpin should be visible
7831 document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
7832 document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;
7834 // Disable "Close other Tabs" if there is only one unpinned tab and
7835 // hide it when the user rightclicked on a pinned tab.
7836 var unpinnedTabs = gBrowser.tabs.length - gBrowser._numPinnedTabs;
7837 document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1;
7838 document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned;
7842 XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () {
7843 Cu.import("resource://gre/modules/HUDService.jsm");
7845 return HUDService.consoleUI;
7848 Components.utils.reportError(ex);