PageRenderTime 115ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 1ms

/browser/base/content/browser.js

https://bitbucket.org/MeeGoAdmin/mozilla-central/
JavaScript | 8709 lines | 6273 code | 1198 blank | 1238 comment | 1302 complexity | 41fa898ee7e534e7a4216d876f5859bf MD5 | raw file
Possible License(s): AGPL-1.0, MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, 0BSD, LGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-2.0, JSON
  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
  4. #
  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/
  9. #
  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
  13. # License.
  14. #
  15. # The Original Code is mozilla.org code.
  16. #
  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.
  21. #
  22. # Contributor(s):
  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 OShannessy <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>
  55. # David Dahl <ddahl@mozilla.com>
  56. # Patrick Walton <pcwalton@mozilla.com>
  57. # Mihai Sucan <mihai.sucan@gmail.com>
  58. #
  59. # Alternatively, the contents of this file may be used under the terms of
  60. # either the GNU General Public License Version 2 or later (the "GPL"), or
  61. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  62. # in which case the provisions of the GPL or the LGPL are applicable instead
  63. # of those above. If you wish to allow use of your version of this file only
  64. # under the terms of either the GPL or the LGPL, and not to allow others to
  65. # use your version of this file under the terms of the MPL, indicate your
  66. # decision by deleting the provisions above and replace them with the notice
  67. # and other provisions required by the GPL or the LGPL. If you do not delete
  68. # the provisions above, a recipient may use your version of this file under
  69. # the terms of any one of the MPL, the GPL or the LGPL.
  70. #
  71. # ***** END LICENSE BLOCK *****
  72. let Ci = Components.interfaces;
  73. let Cu = Components.utils;
  74. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  75. const nsIWebNavigation = Ci.nsIWebNavigation;
  76. var gCharsetMenu = null;
  77. var gLastBrowserCharset = null;
  78. var gPrevCharset = null;
  79. var gProxyFavIcon = null;
  80. var gLastValidURLStr = "";
  81. var gInPrintPreviewMode = false;
  82. var gDownloadMgr = null;
  83. var gContextMenu = null; // nsContextMenu instance
  84. var gDelayedStartupTimeoutId;
  85. var gStartupRan = false;
  86. #ifndef XP_MACOSX
  87. var gEditUIVisible = true;
  88. #endif
  89. [
  90. ["gBrowser", "content"],
  91. ["gNavToolbox", "navigator-toolbox"],
  92. ["gURLBar", "urlbar"],
  93. ["gNavigatorBundle", "bundle_browser"]
  94. ].forEach(function (elementGlobal) {
  95. var [name, id] = elementGlobal;
  96. window.__defineGetter__(name, function () {
  97. var element = document.getElementById(id);
  98. if (!element)
  99. return null;
  100. delete window[name];
  101. return window[name] = element;
  102. });
  103. window.__defineSetter__(name, function (val) {
  104. delete window[name];
  105. return window[name] = val;
  106. });
  107. });
  108. // Smart getter for the findbar. If you don't wish to force the creation of
  109. // the findbar, check gFindBarInitialized first.
  110. var gFindBarInitialized = false;
  111. XPCOMUtils.defineLazyGetter(window, "gFindBar", function() {
  112. let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  113. let findbar = document.createElementNS(XULNS, "findbar");
  114. findbar.id = "FindToolbar";
  115. let browserBottomBox = document.getElementById("browser-bottombox");
  116. browserBottomBox.insertBefore(findbar, browserBottomBox.firstChild);
  117. // Force a style flush to ensure that our binding is attached.
  118. findbar.clientTop;
  119. findbar.browser = gBrowser;
  120. window.gFindBarInitialized = true;
  121. return findbar;
  122. });
  123. __defineGetter__("gPrefService", function() {
  124. delete this.gPrefService;
  125. return this.gPrefService = Services.prefs;
  126. });
  127. __defineGetter__("AddonManager", function() {
  128. Cu.import("resource://gre/modules/AddonManager.jsm");
  129. return this.AddonManager;
  130. });
  131. __defineSetter__("AddonManager", function (val) {
  132. delete this.AddonManager;
  133. return this.AddonManager = val;
  134. });
  135. __defineGetter__("PluralForm", function() {
  136. Cu.import("resource://gre/modules/PluralForm.jsm");
  137. return this.PluralForm;
  138. });
  139. __defineSetter__("PluralForm", function (val) {
  140. delete this.PluralForm;
  141. return this.PluralForm = val;
  142. });
  143. #ifdef MOZ_SERVICES_SYNC
  144. XPCOMUtils.defineLazyGetter(this, "Weave", function() {
  145. let tmp = {};
  146. Cu.import("resource://services-sync/main.js", tmp);
  147. return tmp.Weave;
  148. });
  149. #endif
  150. XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
  151. let tmp = {};
  152. Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
  153. try {
  154. return new tmp.PopupNotifications(gBrowser,
  155. document.getElementById("notification-popup"),
  156. document.getElementById("notification-popup-box"));
  157. } catch (ex) {
  158. Cu.reportError(ex);
  159. }
  160. });
  161. let gInitialPages = [
  162. "about:blank",
  163. "about:privatebrowsing",
  164. "about:sessionrestore"
  165. ];
  166. #include browser-fullZoom.js
  167. #include inspector.js
  168. #include browser-places.js
  169. #include browser-tabPreviews.js
  170. #include browser-tabview.js
  171. #ifdef MOZ_SERVICES_SYNC
  172. #include browser-syncui.js
  173. #endif
  174. XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
  175. #ifdef XP_WIN
  176. const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
  177. if (WINTASKBAR_CONTRACTID in Cc &&
  178. Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
  179. let temp = {};
  180. Cu.import("resource://gre/modules/WindowsPreviewPerTab.jsm", temp);
  181. let AeroPeek = temp.AeroPeek;
  182. return {
  183. onOpenWindow: function () {
  184. AeroPeek.onOpenWindow(window);
  185. },
  186. onCloseWindow: function () {
  187. AeroPeek.onCloseWindow(window);
  188. }
  189. };
  190. }
  191. #endif
  192. return null;
  193. });
  194. #ifdef MOZ_CRASHREPORTER
  195. XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
  196. "@mozilla.org/xre/app-info;1",
  197. "nsICrashReporter");
  198. #endif
  199. XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
  200. let tmp = {};
  201. Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
  202. return new tmp.PageMenu();
  203. });
  204. /**
  205. * We can avoid adding multiple load event listeners and save some time by adding
  206. * one listener that calls all real handlers.
  207. */
  208. function pageShowEventHandlers(event) {
  209. // Filter out events that are not about the document load we are interested in
  210. if (event.originalTarget == content.document) {
  211. charsetLoadListener(event);
  212. XULBrowserWindow.asyncUpdateUI();
  213. }
  214. }
  215. function UpdateBackForwardCommands(aWebNavigation) {
  216. var backBroadcaster = document.getElementById("Browser:Back");
  217. var forwardBroadcaster = document.getElementById("Browser:Forward");
  218. // Avoid setting attributes on broadcasters if the value hasn't changed!
  219. // Remember, guys, setting attributes on elements is expensive! They
  220. // get inherited into anonymous content, broadcast to other widgets, etc.!
  221. // Don't do it if the value hasn't changed! - dwh
  222. var backDisabled = backBroadcaster.hasAttribute("disabled");
  223. var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
  224. if (backDisabled == aWebNavigation.canGoBack) {
  225. if (backDisabled)
  226. backBroadcaster.removeAttribute("disabled");
  227. else
  228. backBroadcaster.setAttribute("disabled", true);
  229. }
  230. if (forwardDisabled == aWebNavigation.canGoForward) {
  231. if (forwardDisabled)
  232. forwardBroadcaster.removeAttribute("disabled");
  233. else
  234. forwardBroadcaster.setAttribute("disabled", true);
  235. }
  236. }
  237. /**
  238. * Click-and-Hold implementation for the Back and Forward buttons
  239. * XXXmano: should this live in toolbarbutton.xml?
  240. */
  241. function SetClickAndHoldHandlers() {
  242. var timer;
  243. function openMenu(aButton) {
  244. cancelHold(aButton);
  245. aButton.firstChild.hidden = false;
  246. aButton.open = true;
  247. }
  248. function mousedownHandler(aEvent) {
  249. if (aEvent.button != 0 ||
  250. aEvent.currentTarget.open ||
  251. aEvent.currentTarget.disabled)
  252. return;
  253. // Prevent the menupopup from opening immediately
  254. aEvent.currentTarget.firstChild.hidden = true;
  255. aEvent.currentTarget.addEventListener("mouseout", mouseoutHandler, false);
  256. aEvent.currentTarget.addEventListener("mouseup", mouseupHandler, false);
  257. timer = setTimeout(openMenu, 500, aEvent.currentTarget);
  258. }
  259. function mouseoutHandler(aEvent) {
  260. let buttonRect = aEvent.currentTarget.getBoundingClientRect();
  261. if (aEvent.clientX >= buttonRect.left &&
  262. aEvent.clientX <= buttonRect.right &&
  263. aEvent.clientY >= buttonRect.bottom)
  264. openMenu(aEvent.currentTarget);
  265. else
  266. cancelHold(aEvent.currentTarget);
  267. }
  268. function mouseupHandler(aEvent) {
  269. cancelHold(aEvent.currentTarget);
  270. }
  271. function cancelHold(aButton) {
  272. clearTimeout(timer);
  273. aButton.removeEventListener("mouseout", mouseoutHandler, false);
  274. aButton.removeEventListener("mouseup", mouseupHandler, false);
  275. }
  276. function clickHandler(aEvent) {
  277. if (aEvent.button == 0 &&
  278. aEvent.target == aEvent.currentTarget &&
  279. !aEvent.currentTarget.open &&
  280. !aEvent.currentTarget.disabled) {
  281. let cmdEvent = document.createEvent("xulcommandevent");
  282. cmdEvent.initCommandEvent("command", true, true, window, 0,
  283. aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
  284. aEvent.metaKey, null);
  285. aEvent.currentTarget.dispatchEvent(cmdEvent);
  286. }
  287. }
  288. function _addClickAndHoldListenersOnElement(aElm) {
  289. aElm.addEventListener("mousedown", mousedownHandler, true);
  290. aElm.addEventListener("click", clickHandler, true);
  291. }
  292. // Bug 414797: Clone unified-back-forward-button's context menu into both the
  293. // back and the forward buttons.
  294. var unifiedButton = document.getElementById("unified-back-forward-button");
  295. if (unifiedButton && !unifiedButton._clickHandlersAttached) {
  296. unifiedButton._clickHandlersAttached = true;
  297. let popup = document.getElementById("backForwardMenu").cloneNode(true);
  298. popup.removeAttribute("id");
  299. // Prevent the context attribute on unified-back-forward-button from being
  300. // inherited.
  301. popup.setAttribute("context", "");
  302. let backButton = document.getElementById("back-button");
  303. backButton.setAttribute("type", "menu");
  304. backButton.appendChild(popup);
  305. _addClickAndHoldListenersOnElement(backButton);
  306. let forwardButton = document.getElementById("forward-button");
  307. popup = popup.cloneNode(true);
  308. forwardButton.setAttribute("type", "menu");
  309. forwardButton.appendChild(popup);
  310. _addClickAndHoldListenersOnElement(forwardButton);
  311. }
  312. }
  313. const gSessionHistoryObserver = {
  314. observe: function(subject, topic, data)
  315. {
  316. if (topic != "browser:purge-session-history")
  317. return;
  318. var backCommand = document.getElementById("Browser:Back");
  319. backCommand.setAttribute("disabled", "true");
  320. var fwdCommand = document.getElementById("Browser:Forward");
  321. fwdCommand.setAttribute("disabled", "true");
  322. // Hide session restore button on about:home
  323. window.messageManager.sendAsyncMessage("Browser:HideSessionRestoreButton");
  324. if (gURLBar) {
  325. // Clear undo history of the URL bar
  326. gURLBar.editor.transactionManager.clear()
  327. }
  328. }
  329. };
  330. /**
  331. * Given a starting docshell and a URI to look up, find the docshell the URI
  332. * is loaded in.
  333. * @param aDocument
  334. * A document to find instead of using just a URI - this is more specific.
  335. * @param aDocShell
  336. * The doc shell to start at
  337. * @param aSoughtURI
  338. * The URI that we're looking for
  339. * @returns The doc shell that the sought URI is loaded in. Can be in
  340. * subframes.
  341. */
  342. function findChildShell(aDocument, aDocShell, aSoughtURI) {
  343. aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation);
  344. aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  345. var doc = aDocShell.getInterface(Components.interfaces.nsIDOMDocument);
  346. if ((aDocument && doc == aDocument) ||
  347. (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
  348. return aDocShell;
  349. var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
  350. for (var i = 0; i < node.childCount; ++i) {
  351. var docShell = node.getChildAt(i);
  352. docShell = findChildShell(aDocument, docShell, aSoughtURI);
  353. if (docShell)
  354. return docShell;
  355. }
  356. return null;
  357. }
  358. var gPopupBlockerObserver = {
  359. _reportButton: null,
  360. onReportButtonClick: function (aEvent)
  361. {
  362. if (aEvent.button != 0 || aEvent.target != this._reportButton)
  363. return;
  364. document.getElementById("blockedPopupOptions")
  365. .openPopup(this._reportButton, "after_end", 0, 2, false, false, aEvent);
  366. },
  367. handleEvent: function (aEvent)
  368. {
  369. if (aEvent.originalTarget != gBrowser.selectedBrowser)
  370. return;
  371. if (!this._reportButton && gURLBar)
  372. this._reportButton = document.getElementById("page-report-button");
  373. if (!gBrowser.pageReport) {
  374. // Hide the icon in the location bar (if the location bar exists)
  375. if (gURLBar)
  376. this._reportButton.hidden = true;
  377. return;
  378. }
  379. if (gURLBar)
  380. this._reportButton.hidden = false;
  381. // Only show the notification again if we've not already shown it. Since
  382. // notifications are per-browser, we don't need to worry about re-adding
  383. // it.
  384. if (!gBrowser.pageReport.reported) {
  385. if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
  386. var brandBundle = document.getElementById("bundle_brand");
  387. var brandShortName = brandBundle.getString("brandShortName");
  388. var message;
  389. var popupCount = gBrowser.pageReport.length;
  390. #ifdef XP_WIN
  391. var popupButtonText = gNavigatorBundle.getString("popupWarningButton");
  392. var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButton.accesskey");
  393. #else
  394. var popupButtonText = gNavigatorBundle.getString("popupWarningButtonUnix");
  395. var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButtonUnix.accesskey");
  396. #endif
  397. if (popupCount > 1)
  398. message = gNavigatorBundle.getFormattedString("popupWarningMultiple", [brandShortName, popupCount]);
  399. else
  400. message = gNavigatorBundle.getFormattedString("popupWarning", [brandShortName]);
  401. var notificationBox = gBrowser.getNotificationBox();
  402. var notification = notificationBox.getNotificationWithValue("popup-blocked");
  403. if (notification) {
  404. notification.label = message;
  405. }
  406. else {
  407. var buttons = [{
  408. label: popupButtonText,
  409. accessKey: popupButtonAccesskey,
  410. popup: "blockedPopupOptions",
  411. callback: null
  412. }];
  413. const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
  414. notificationBox.appendNotification(message, "popup-blocked",
  415. "chrome://browser/skin/Info.png",
  416. priority, buttons);
  417. }
  418. }
  419. // Record the fact that we've reported this blocked popup, so we don't
  420. // show it again.
  421. gBrowser.pageReport.reported = true;
  422. }
  423. },
  424. toggleAllowPopupsForSite: function (aEvent)
  425. {
  426. var pm = Services.perms;
  427. var shouldBlock = aEvent.target.getAttribute("block") == "true";
  428. var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
  429. pm.add(gBrowser.currentURI, "popup", perm);
  430. gBrowser.getNotificationBox().removeCurrentNotification();
  431. },
  432. fillPopupList: function (aEvent)
  433. {
  434. // XXXben - rather than using |currentURI| here, which breaks down on multi-framed sites
  435. // we should really walk the pageReport and create a list of "allow for <host>"
  436. // menuitems for the common subset of hosts present in the report, this will
  437. // make us frame-safe.
  438. //
  439. // XXXjst - Note that when this is fixed to work with multi-framed sites,
  440. // also back out the fix for bug 343772 where
  441. // nsGlobalWindow::CheckOpenAllow() was changed to also
  442. // check if the top window's location is whitelisted.
  443. var uri = gBrowser.currentURI;
  444. var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
  445. try {
  446. blockedPopupAllowSite.removeAttribute("hidden");
  447. var pm = Services.perms;
  448. if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
  449. // Offer an item to block popups for this site, if a whitelist entry exists
  450. // already for it.
  451. let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host]);
  452. blockedPopupAllowSite.setAttribute("label", blockString);
  453. blockedPopupAllowSite.setAttribute("block", "true");
  454. }
  455. else {
  456. // Offer an item to allow popups for this site
  457. let allowString = gNavigatorBundle.getFormattedString("popupAllow", [uri.host]);
  458. blockedPopupAllowSite.setAttribute("label", allowString);
  459. blockedPopupAllowSite.removeAttribute("block");
  460. }
  461. }
  462. catch (e) {
  463. blockedPopupAllowSite.setAttribute("hidden", "true");
  464. }
  465. if (gPrivateBrowsingUI.privateBrowsingEnabled)
  466. blockedPopupAllowSite.setAttribute("disabled", "true");
  467. else
  468. blockedPopupAllowSite.removeAttribute("disabled");
  469. var foundUsablePopupURI = false;
  470. var pageReport = gBrowser.pageReport;
  471. if (pageReport) {
  472. for (var i = 0; i < pageReport.length; ++i) {
  473. // popupWindowURI will be null if the file picker popup is blocked.
  474. // xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
  475. if (!pageReport[i].popupWindowURI)
  476. continue;
  477. var popupURIspec = pageReport[i].popupWindowURI.spec;
  478. // Sometimes the popup URI that we get back from the pageReport
  479. // isn't useful (for instance, netscape.com's popup URI ends up
  480. // being "http://www.netscape.com", which isn't really the URI of
  481. // the popup they're trying to show). This isn't going to be
  482. // useful to the user, so we won't create a menu item for it.
  483. if (popupURIspec == "" || popupURIspec == "about:blank" ||
  484. popupURIspec == uri.spec)
  485. continue;
  486. // Because of the short-circuit above, we may end up in a situation
  487. // in which we don't have any usable popup addresses to show in
  488. // the menu, and therefore we shouldn't show the separator. However,
  489. // since we got past the short-circuit, we must've found at least
  490. // one usable popup URI and thus we'll turn on the separator later.
  491. foundUsablePopupURI = true;
  492. var menuitem = document.createElement("menuitem");
  493. var label = gNavigatorBundle.getFormattedString("popupShowPopupPrefix",
  494. [popupURIspec]);
  495. menuitem.setAttribute("label", label);
  496. menuitem.setAttribute("popupWindowURI", popupURIspec);
  497. menuitem.setAttribute("popupWindowFeatures", pageReport[i].popupWindowFeatures);
  498. menuitem.setAttribute("popupWindowName", pageReport[i].popupWindowName);
  499. menuitem.setAttribute("oncommand", "gPopupBlockerObserver.showBlockedPopup(event);");
  500. menuitem.requestingWindow = pageReport[i].requestingWindow;
  501. menuitem.requestingDocument = pageReport[i].requestingDocument;
  502. aEvent.target.appendChild(menuitem);
  503. }
  504. }
  505. // Show or hide the separator, depending on whether we added any
  506. // showable popup addresses to the menu.
  507. var blockedPopupsSeparator =
  508. document.getElementById("blockedPopupsSeparator");
  509. if (foundUsablePopupURI)
  510. blockedPopupsSeparator.removeAttribute("hidden");
  511. else
  512. blockedPopupsSeparator.setAttribute("hidden", true);
  513. var blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
  514. var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
  515. blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
  516. if (aEvent.target.anchorNode.id == "page-report-button") {
  517. aEvent.target.anchorNode.setAttribute("open", "true");
  518. blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromLocationbar"));
  519. } else
  520. blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
  521. },
  522. onPopupHiding: function (aEvent) {
  523. if (aEvent.target.anchorNode.id == "page-report-button")
  524. aEvent.target.anchorNode.removeAttribute("open");
  525. let item = aEvent.target.lastChild;
  526. while (item && item.getAttribute("observes") != "blockedPopupsSeparator") {
  527. let next = item.previousSibling;
  528. item.parentNode.removeChild(item);
  529. item = next;
  530. }
  531. },
  532. showBlockedPopup: function (aEvent)
  533. {
  534. var target = aEvent.target;
  535. var popupWindowURI = target.getAttribute("popupWindowURI");
  536. var features = target.getAttribute("popupWindowFeatures");
  537. var name = target.getAttribute("popupWindowName");
  538. var dwi = target.requestingWindow;
  539. // If we have a requesting window and the requesting document is
  540. // still the current document, open the popup.
  541. if (dwi && dwi.document == target.requestingDocument) {
  542. dwi.open(popupWindowURI, name, features);
  543. }
  544. },
  545. editPopupSettings: function ()
  546. {
  547. var host = "";
  548. try {
  549. host = gBrowser.currentURI.host;
  550. }
  551. catch (e) { }
  552. var bundlePreferences = document.getElementById("bundle_preferences");
  553. var params = { blockVisible : false,
  554. sessionVisible : false,
  555. allowVisible : true,
  556. prefilledHost : host,
  557. permissionType : "popup",
  558. windowTitle : bundlePreferences.getString("popuppermissionstitle"),
  559. introText : bundlePreferences.getString("popuppermissionstext") };
  560. var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
  561. if (existingWindow) {
  562. existingWindow.initWithParams(params);
  563. existingWindow.focus();
  564. }
  565. else
  566. window.openDialog("chrome://browser/content/preferences/permissions.xul",
  567. "_blank", "resizable,dialog=no,centerscreen", params);
  568. },
  569. dontShowMessage: function ()
  570. {
  571. var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
  572. gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
  573. gBrowser.getNotificationBox().removeCurrentNotification();
  574. }
  575. };
  576. const gXPInstallObserver = {
  577. _findChildShell: function (aDocShell, aSoughtShell)
  578. {
  579. if (aDocShell == aSoughtShell)
  580. return aDocShell;
  581. var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
  582. for (var i = 0; i < node.childCount; ++i) {
  583. var docShell = node.getChildAt(i);
  584. docShell = this._findChildShell(docShell, aSoughtShell);
  585. if (docShell == aSoughtShell)
  586. return docShell;
  587. }
  588. return null;
  589. },
  590. _getBrowser: function (aDocShell)
  591. {
  592. for (var i = 0; i < gBrowser.browsers.length; ++i) {
  593. var browser = gBrowser.getBrowserAtIndex(i);
  594. if (this._findChildShell(browser.docShell, aDocShell))
  595. return browser;
  596. }
  597. return null;
  598. },
  599. observe: function (aSubject, aTopic, aData)
  600. {
  601. var brandBundle = document.getElementById("bundle_brand");
  602. var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
  603. var win = installInfo.originatingWindow;
  604. var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  605. .getInterface(Components.interfaces.nsIWebNavigation)
  606. .QueryInterface(Components.interfaces.nsIDocShell);
  607. var browser = this._getBrowser(shell);
  608. if (!browser)
  609. return;
  610. const anchorID = "addons-notification-icon";
  611. var messageString, action;
  612. var brandShortName = brandBundle.getString("brandShortName");
  613. var notificationID = aTopic;
  614. // Make notifications persist a minimum of 30 seconds
  615. var options = {
  616. timeout: Date.now() + 30000
  617. };
  618. switch (aTopic) {
  619. case "addon-install-disabled":
  620. notificationID = "xpinstall-disabled"
  621. if (gPrefService.prefIsLocked("xpinstall.enabled")) {
  622. messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
  623. buttons = [];
  624. }
  625. else {
  626. messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
  627. action = {
  628. label: gNavigatorBundle.getString("xpinstallDisabledButton"),
  629. accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
  630. callback: function editPrefs() {
  631. gPrefService.setBoolPref("xpinstall.enabled", true);
  632. }
  633. };
  634. }
  635. PopupNotifications.show(browser, notificationID, messageString, anchorID,
  636. action, null, options);
  637. break;
  638. case "addon-install-blocked":
  639. messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
  640. [brandShortName, installInfo.originatingURI.host]);
  641. action = {
  642. label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
  643. accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
  644. callback: function() {
  645. installInfo.install();
  646. }
  647. };
  648. PopupNotifications.show(browser, notificationID, messageString, anchorID,
  649. action, null, options);
  650. break;
  651. case "addon-install-started":
  652. function needsDownload(aInstall) {
  653. return aInstall.state != AddonManager.STATE_DOWNLOADED;
  654. }
  655. // If all installs have already been downloaded then there is no need to
  656. // show the download progress
  657. if (!installInfo.installs.some(needsDownload))
  658. return;
  659. notificationID = "addon-progress";
  660. messageString = gNavigatorBundle.getString("addonDownloading");
  661. messageString = PluralForm.get(installInfo.installs.length, messageString);
  662. options.installs = installInfo.installs;
  663. options.contentWindow = browser.contentWindow;
  664. options.sourceURI = browser.currentURI;
  665. options.eventCallback = function(aEvent) {
  666. if (aEvent != "removed")
  667. return;
  668. options.contentWindow = null;
  669. options.sourceURI = null;
  670. };
  671. PopupNotifications.show(browser, notificationID, messageString, anchorID,
  672. null, null, options);
  673. break;
  674. case "addon-install-failed":
  675. // TODO This isn't terribly ideal for the multiple failure case
  676. installInfo.installs.forEach(function(aInstall) {
  677. var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
  678. installInfo.originatingURI.host;
  679. if (!host)
  680. host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
  681. aInstall.sourceURI.host;
  682. var error = (host || aInstall.error == 0) ? "addonError" : "addonLocalError";
  683. if (aInstall.error != 0)
  684. error += aInstall.error;
  685. else if (aInstall.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
  686. error += "Blocklisted";
  687. else
  688. error += "Incompatible";
  689. messageString = gNavigatorBundle.getString(error);
  690. messageString = messageString.replace("#1", aInstall.name);
  691. if (host)
  692. messageString = messageString.replace("#2", host);
  693. messageString = messageString.replace("#3", brandShortName);
  694. messageString = messageString.replace("#4", Services.appinfo.version);
  695. PopupNotifications.show(browser, notificationID, messageString, anchorID,
  696. action, null, options);
  697. });
  698. break;
  699. case "addon-install-complete":
  700. var needsRestart = installInfo.installs.some(function(i) {
  701. return i.addon.pendingOperations != AddonManager.PENDING_NONE;
  702. });
  703. if (needsRestart) {
  704. messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
  705. action = {
  706. label: gNavigatorBundle.getString("addonInstallRestartButton"),
  707. accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
  708. callback: function() {
  709. Application.restart();
  710. }
  711. };
  712. }
  713. else {
  714. messageString = gNavigatorBundle.getString("addonsInstalled");
  715. action = {
  716. label: gNavigatorBundle.getString("addonInstallManage"),
  717. accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
  718. callback: function() {
  719. // Calculate the add-on type that is most popular in the list of
  720. // installs
  721. var types = {};
  722. var bestType = null;
  723. installInfo.installs.forEach(function(aInstall) {
  724. if (aInstall.type in types)
  725. types[aInstall.type]++;
  726. else
  727. types[aInstall.type] = 1;
  728. if (!bestType || types[aInstall.type] > types[bestType])
  729. bestType = aInstall.type;
  730. });
  731. BrowserOpenAddonsMgr("addons://list/" + bestType);
  732. }
  733. };
  734. }
  735. messageString = PluralForm.get(installInfo.installs.length, messageString);
  736. messageString = messageString.replace("#1", installInfo.installs[0].name);
  737. messageString = messageString.replace("#2", installInfo.installs.length);
  738. messageString = messageString.replace("#3", brandShortName);
  739. // Remove notificaion on dismissal, since it's possible to cancel the
  740. // install through the addons manager UI, making the "restart" prompt
  741. // irrelevant.
  742. options.removeOnDismissal = true;
  743. PopupNotifications.show(browser, notificationID, messageString, anchorID,
  744. action, null, options);
  745. break;
  746. }
  747. }
  748. };
  749. const gFormSubmitObserver = {
  750. QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
  751. panel: null,
  752. init: function()
  753. {
  754. this.panel = document.getElementById('invalid-form-popup');
  755. },
  756. panelIsOpen: function()
  757. {
  758. return this.panel && this.panel.state != "hiding" &&
  759. this.panel.state != "closed";
  760. },
  761. notifyInvalidSubmit : function (aFormElement, aInvalidElements)
  762. {
  763. // We are going to handle invalid form submission attempt by focusing the
  764. // first invalid element and show the corresponding validation message in a
  765. // panel attached to the element.
  766. if (!aInvalidElements.length) {
  767. return;
  768. }
  769. // Don't show the popup if the current tab doesn't contain the invalid form.
  770. if (gBrowser.contentDocument !=
  771. aFormElement.ownerDocument.defaultView.top.document) {
  772. return;
  773. }
  774. let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
  775. if (!(element instanceof HTMLInputElement ||
  776. element instanceof HTMLTextAreaElement ||
  777. element instanceof HTMLSelectElement ||
  778. element instanceof HTMLButtonElement)) {
  779. return;
  780. }
  781. this.panel.firstChild.textContent = element.validationMessage;
  782. element.focus();
  783. // If the user interacts with the element and makes it valid or leaves it,
  784. // we want to remove the popup.
  785. // We could check for clicks but a click is already removing the popup.
  786. function blurHandler() {
  787. gFormSubmitObserver.panel.hidePopup();
  788. };
  789. function inputHandler(e) {
  790. if (e.originalTarget.validity.valid) {
  791. gFormSubmitObserver.panel.hidePopup();
  792. } else {
  793. // If the element is now invalid for a new reason, we should update the
  794. // error message.
  795. if (gFormSubmitObserver.panel.firstChild.textContent !=
  796. e.originalTarget.validationMessage) {
  797. gFormSubmitObserver.panel.firstChild.textContent =
  798. e.originalTarget.validationMessage;
  799. }
  800. }
  801. };
  802. element.addEventListener("input", inputHandler, false);
  803. element.addEventListener("blur", blurHandler, false);
  804. // One event to bring them all and in the darkness bind them.
  805. this.panel.addEventListener("popuphiding", function(aEvent) {
  806. aEvent.target.removeEventListener("popuphiding", arguments.callee, false);
  807. element.removeEventListener("input", inputHandler, false);
  808. element.removeEventListener("blur", blurHandler, false);
  809. }, false);
  810. this.panel.hidden = false;
  811. // We want to show the popup at the middle of checkbox and radio buttons
  812. // and where the content begin for the other elements.
  813. let offset = 0;
  814. let position = "";
  815. if (element.tagName == 'INPUT' &&
  816. (element.type == 'radio' || element.type == 'checkbox')) {
  817. position = "bottomcenter topleft";
  818. } else {
  819. let win = element.ownerDocument.defaultView;
  820. let style = win.getComputedStyle(element, null);
  821. let utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  822. .getInterface(Components.interfaces.nsIDOMWindowUtils);
  823. if (style.direction == 'rtl') {
  824. offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
  825. } else {
  826. offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
  827. }
  828. offset = Math.round(offset * utils.screenPixelsPerCSSPixel);
  829. position = "after_start";
  830. }
  831. this.panel.openPopup(element, position, offset, 0);
  832. }
  833. };
  834. // Simple gestures support
  835. //
  836. // As per bug #412486, web content must not be allowed to receive any
  837. // simple gesture events. Multi-touch gesture APIs are in their
  838. // infancy and we do NOT want to be forced into supporting an API that
  839. // will probably have to change in the future. (The current Mac OS X
  840. // API is undocumented and was reverse-engineered.) Until support is
  841. // implemented in the event dispatcher to keep these events as
  842. // chrome-only, we must listen for the simple gesture events during
  843. // the capturing phase and call stopPropagation on every event.
  844. let gGestureSupport = {
  845. /**
  846. * Add or remove mouse gesture event listeners
  847. *
  848. * @param aAddListener
  849. * True to add/init listeners and false to remove/uninit
  850. */
  851. init: function GS_init(aAddListener) {
  852. const gestureEvents = ["SwipeGesture",
  853. "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
  854. "RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
  855. "TapGesture", "PressTapGesture"];
  856. let addRemove = aAddListener ? window.addEventListener :
  857. window.removeEventListener;
  858. gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true),
  859. this);
  860. },
  861. /**
  862. * Dispatch events based on the type of mouse gesture event. For now, make
  863. * sure to stop propagation of every gesture event so that web content cannot
  864. * receive gesture events.
  865. *
  866. * @param aEvent
  867. * The gesture event to handle
  868. */
  869. handleEvent: function GS_handleEvent(aEvent) {
  870. aEvent.stopPropagation();
  871. // Create a preference object with some defaults
  872. let def = function(aThreshold, aLatched)
  873. ({ threshold: aThreshold, latched: !!aLatched });
  874. switch (aEvent.type) {
  875. case "MozSwipeGesture":
  876. aEvent.preventDefault();
  877. return this.onSwipe(aEvent);
  878. case "MozMagnifyGestureStart":
  879. aEvent.preventDefault();
  880. #ifdef XP_WIN
  881. return this._setupGesture(aEvent, "pinch", def(25, 0), "out", "in");
  882. #else
  883. return this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
  884. #endif
  885. case "MozRotateGestureStart":
  886. aEvent.preventDefault();
  887. return this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
  888. case "MozMagnifyGestureUpdate":
  889. case "MozRotateGestureUpdate":
  890. aEvent.preventDefault();
  891. return this._doUpdate(aEvent);
  892. case "MozTapGesture":
  893. aEvent.preventDefault();
  894. return this._doAction(aEvent, ["tap"]);
  895. case "MozPressTapGesture":
  896. // Fall through to default behavior
  897. return;
  898. }
  899. },
  900. /**
  901. * Called at the start of "pinch" and "twist" gestures to setup all of the
  902. * information needed to process the gesture
  903. *
  904. * @param aEvent
  905. * The continual motion start event to handle
  906. * @param aGesture
  907. * Name of the gesture to handle
  908. * @param aPref
  909. * Preference object with the names of preferences and defaults
  910. * @param aInc
  911. * Command to trigger for increasing motion (without gesture name)
  912. * @param aDec
  913. * Command to trigger for decreasing motion (without gesture name)
  914. */
  915. _setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
  916. // Try to load user-set values from preferences
  917. for (let [pref, def] in Iterator(aPref))
  918. aPref[pref] = this._getPref(aGesture + "." + pref, def);
  919. // Keep track of the total deltas and latching behavior
  920. let offset = 0;
  921. let latchDir = aEvent.delta > 0 ? 1 : -1;
  922. let isLatched = false;
  923. // Create the update function here to capture closure state
  924. this._doUpdate = function GS__doUpdate(aEvent) {
  925. // Update the offset with new event data
  926. offset += aEvent.delta;
  927. // Check if the cumulative deltas exceed the threshold
  928. if (Math.abs(offset) > aPref["threshold"]) {
  929. // Trigger the action if we don't care about latching; otherwise, make
  930. // sure either we're not latched and going the same direction of the
  931. // initial motion; or we're latched and going the opposite way
  932. let sameDir = (latchDir ^ offset) >= 0;
  933. if (!aPref["latched"] || (isLatched ^ sameDir)) {
  934. this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
  935. // We must be getting latched or leaving it, so just toggle
  936. isLatched = !isLatched;
  937. }
  938. // Reset motion counter to prepare for more of the same gesture
  939. offset = 0;
  940. }
  941. };
  942. // The start event also contains deltas, so handle an update right away
  943. this._doUpdate(aEvent);
  944. },
  945. /**
  946. * Generator producing the powerset of the input array where the first result
  947. * is the complete set and the last result (before StopIteration) is empty.
  948. *
  949. * @param aArray
  950. * Source array containing any number of elements
  951. * @yield Array that is a subset of the input array from full set to empty
  952. */
  953. _power: function GS__power(aArray) {
  954. // Create a bitmask based on the length of the array
  955. let num = 1 << aArray.length;
  956. while (--num >= 0) {
  957. // Only select array elements where the current bit is set
  958. yield aArray.reduce(function (aPrev, aCurr, aIndex) {
  959. if (num & 1 << aIndex)
  960. aPrev.push(aCurr);
  961. return aPrev;
  962. }, []);
  963. }
  964. },
  965. /**
  966. * Determine what action to do for the gesture based on which keys are
  967. * pressed and which commands are set
  968. *
  969. * @param aEvent
  970. * The original gesture event to convert into a fake click event
  971. * @param aGesture
  972. * Array of gesture name parts (to be joined by periods)
  973. * @return Name of the command found for the event's keys and gesture. If no
  974. * command is found, no value is returned (undefined).
  975. */
  976. _doAction: function GS__doAction(aEvent, aGesture) {
  977. // Create an array of pressed keys in a fixed order so that a command for
  978. // "meta" is preferred over "ctrl" when both buttons are pressed (and a
  979. // command for both don't exist)
  980. let keyCombos = [];
  981. ["shift", "alt", "ctrl", "meta"].forEach(function (key) {
  982. if (aEvent[key + "Key"])
  983. keyCombos.push(key);
  984. });
  985. // Try each combination of key presses in decreasing order for commands
  986. for each (let subCombo in this._power(keyCombos)) {
  987. // Convert a gesture and pressed keys into the corresponding command
  988. // action where the preference has the gesture before "shift" before
  989. // "alt" before "ctrl" before "meta" all separated by periods
  990. let command;
  991. try {
  992. command = this._getPref(aGesture.concat(subCombo).join("."));
  993. } catch (e) {}
  994. if (!command)
  995. continue;
  996. let node = document.getElementById(command);
  997. if (node) {
  998. if (node.getAttribute("disabled") != "true") {
  999. let cmdEvent = document.createEvent("xulcommandevent");
  1000. cmdEvent.initCommandEvent("command", true, true, window, 0,
  1001. aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
  1002. aEvent.metaKey, null);
  1003. node.dispatchEvent(cmdEvent);
  1004. }
  1005. } else {
  1006. goDoCommand(command);
  1007. }
  1008. return command;
  1009. }
  1010. return null;
  1011. },
  1012. /**
  1013. * Convert continual motion events into an action if it exceeds a threshold
  1014. * in a given direction. This function will be set by _setupGesture to
  1015. * capture state that needs to be shared across multiple gesture updates.
  1016. *
  1017. * @param aEvent
  1018. * The continual motion update event to handle
  1019. */
  1020. _doUpdate: function(aEvent) {},
  1021. /**
  1022. * Convert the swipe gesture into a browser action based on the direction
  1023. *
  1024. * @param aEvent
  1025. * The swipe event to handle
  1026. */
  1027. onSwipe: function GS_onSwipe(aEvent) {
  1028. // Figure out which one (and only one) direction was triggered
  1029. ["UP", "RIGHT", "DOWN", "LEFT"].forEach(function (dir) {
  1030. if (aEvent.direction == aEvent["DIRECTION_" + dir])
  1031. return this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
  1032. }, this);
  1033. },
  1034. /**
  1035. * Get a gesture preference or use a default if it doesn't exist
  1036. *
  1037. * @param aPref
  1038. * Name of the preference to load under the gesture branch
  1039. * @param aDef
  1040. * Default value if the preference doesn't exist
  1041. */
  1042. _getPref: function GS__getPref(aPref, aDef) {
  1043. // Preferences branch under which all gestures preferences are stored
  1044. const branch = "browser.gesture.";
  1045. try {
  1046. // Determine what type of data to load based on default value's type
  1047. let type = typeof aDef;
  1048. let getFunc = "get" + (type == "boolean" ? "Bool" :
  1049. type == "number" ? "Int" : "Char") + "Pref";
  1050. return gPrefService[getFunc](branch + aPref);
  1051. }
  1052. catch (e) {
  1053. return aDef;
  1054. }
  1055. },
  1056. };
  1057. function BrowserStartup() {
  1058. var uriToLoad = null;
  1059. // window.arguments[0]: URI to load (string), or an nsISupportsArray of
  1060. // nsISupportsStrings to load, or a xul:tab of
  1061. // a tabbrowser, which will be replaced by this
  1062. // window (for this case, all other arguments are
  1063. // ignored).
  1064. // [1]: character set (string)
  1065. // [2]: referrer (nsIURI)
  1066. // [3]: postData (nsIInputStream)
  1067. // [4]: allowThirdPartyFixup (bool)
  1068. if ("arguments" in window && window.arguments[0])
  1069. uriToLoad = window.arguments[0];
  1070. var isLoadingBlank = uriToLoad == "about:blank";
  1071. var mustLoadSidebar = false;
  1072. prepareForStartup();
  1073. if (uriToLoad && !isLoadingBlank) {
  1074. if (uriToLoad instanceof Ci.nsISupportsArray) {
  1075. let count = uriToLoad.Count();
  1076. let specs = [];
  1077. for (let i = 0; i < count; i++) {
  1078. let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
  1079. specs.push(urisstring.data);
  1080. }
  1081. // This function throws for certain malformed URIs, so use exception handling
  1082. // so that we don't disrupt startup
  1083. try {
  1084. gBrowser.loadTabs(specs, false, true);
  1085. } catch (e) {}
  1086. }
  1087. else if (uriToLoad instanceof XULElement) {
  1088. // swap the given tab with the default about:blank tab and then close
  1089. // the original tab in the other window.
  1090. // Stop the about:blank load
  1091. gBrowser.stop();
  1092. // make sure it has a docshell
  1093. gBrowser.docShell;
  1094. gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
  1095. }
  1096. else if (window.arguments.length >= 3) {
  1097. loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
  1098. window.arguments[4] || false);
  1099. content.focus();
  1100. }
  1101. // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
  1102. // Such callers expect that window.arguments[0] is handled as a single URI.
  1103. else
  1104. loadOneOrMoreURIs(uriToLoad);
  1105. }
  1106. if (window.opener && !window.opener.closed) {
  1107. let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
  1108. // If the opener had a sidebar, open the same sidebar in our window.
  1109. // The opener can be the hidden window too, if we're coming from the state
  1110. // where no windows are open, and the hidden window has no sidebar box.
  1111. if (openerSidebarBox && !openerSidebarBox.hidden) {
  1112. let sidebarCmd = openerSidebarBox.getAttribute("sidebarcommand");
  1113. let sidebarCmdElem = document.getElementById(sidebarCmd);
  1114. // dynamically generated sidebars will fail this check.
  1115. if (sidebarCmdElem) {
  1116. let sidebarBox = document.getElementById("sidebar-box");
  1117. let sidebarTitle = document.getElementById("sidebar-title");
  1118. sidebarTitle.setAttribute(
  1119. "value", window.opener.document.getElementById("sidebar-title").getAttribute("value"));
  1120. sidebarBox.setAttribute("width", openerSidebarBox.boxObject.width);
  1121. sidebarBox.setAttribute("sidebarcommand", sidebarCmd);
  1122. // Note: we're setting 'src' on sidebarBox, which is a <vbox>, not on
  1123. // the <browser id="sidebar">. This lets us delay the actual load until
  1124. // delayedStartup().
  1125. sidebarBox.setAttribute(
  1126. "src", window.opener.document.getElementById("sidebar").getAttribute("src"));
  1127. mustLoadSidebar = true;
  1128. sidebarBox.hidden = false;
  1129. document.getElementById("sidebar-splitter").hidden = false;
  1130. sidebarCmdElem.setAttribute("checked", "true");
  1131. }
  1132. }
  1133. }
  1134. else {
  1135. let box = document.getElementById("sidebar-box");
  1136. if (box.hasAttribute("sidebarcommand")) {
  1137. let commandID = box.getAttribute("sidebarcommand");
  1138. if (commandID) {
  1139. let command = document.getElementById(commandID);
  1140. if (command) {
  1141. mustLoadSidebar = true;
  1142. box.hidden = false;
  1143. document.getElementById("sidebar-splitter").hidden = false;
  1144. command.setAttribute("checked", "true");
  1145. }
  1146. else {
  1147. // Remove the |sidebarcommand| attribute, because the element it
  1148. // refers to no longer exists, so we should assume this sidebar
  1149. // panel has been uninstalled. (249883)
  1150. box.removeAttribute("sidebarcommand");
  1151. }
  1152. }
  1153. }
  1154. }
  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", "");
  1158. // Set a sane starting width/height for all resolutions on new profiles.
  1159. if (!document.documentElement.hasAttribute("width")) {
  1160. let defaultWidth = 994;
  1161. let defaultHeight;
  1162. if (screen.availHeight <= 600) {
  1163. document.documentElement.setAttribute("sizemode", "maximized");
  1164. defaultWidth = 610;
  1165. defaultHeight = 450;
  1166. }
  1167. else {
  1168. // Create a narrower window for large or wide-aspect displays, to suggest
  1169. // side-by-side page view.
  1170. if (screen.availWidth >= 1600)
  1171. defaultWidth = (screen.availWidth / 2) - 20;
  1172. defaultHeight = screen.availHeight - 10;
  1173. #ifdef MOZ_WIDGET_GTK2
  1174. // On X, we're not currently able to account for the size of the window
  1175. // border. Use 28px as a guess (titlebar + bottom window border)
  1176. defaultHeight -= 28;
  1177. #endif
  1178. }
  1179. document.documentElement.setAttribute("width", defaultWidth);
  1180. document.documentElement.setAttribute("height", defaultHeight);
  1181. }
  1182. if (!gShowPageResizers)
  1183. document.getElementById("status-bar").setAttribute("hideresizer", "true");
  1184. if (!window.toolbar.visible) {
  1185. // adjust browser UI for popups
  1186. if (gURLBar) {
  1187. gURLBar.setAttribute("readonly", "true");
  1188. gURLBar.setAttribute("enablehistory", "false");
  1189. }
  1190. goSetCommandEnabled("Browser:OpenLocation", false);
  1191. goSetCommandEnabled("cmd_newNavigatorTab", false);
  1192. }
  1193. #ifdef MENUBAR_CAN_AUTOHIDE
  1194. updateAppButtonDisplay();
  1195. #endif
  1196. CombinedStopReload.init();
  1197. allTabs.readPref();
  1198. TabsOnTop.syncCommand();
  1199. BookmarksMenuButton.init();
  1200. TabsInTitlebar.init();
  1201. gPrivateBrowsingUI.init();
  1202. retrieveToolbarIconsizesFromTheme();
  1203. gDelayedStartupTimeoutId = setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
  1204. gStartupRan = true;
  1205. }
  1206. function HandleAppCommandEvent(evt) {
  1207. evt.stopPropagation();
  1208. switch (evt.command) {
  1209. case "Back":
  1210. BrowserBack();
  1211. break;
  1212. case "Forward":
  1213. BrowserForward();
  1214. break;
  1215. case "Reload":
  1216. BrowserReloadSkipCache();
  1217. break;
  1218. case "Stop":
  1219. BrowserStop();
  1220. break;
  1221. case "Search":
  1222. BrowserSearch.webSearch();
  1223. break;
  1224. case "Bookmarks":
  1225. toggleSidebar('viewBookmarksSidebar');
  1226. break;
  1227. case "Home":
  1228. BrowserHome();
  1229. break;
  1230. default:
  1231. break;
  1232. }
  1233. }
  1234. function prepareForStartup() {
  1235. gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
  1236. gBrowser.addEventListener("PluginNotFound", gPluginHandler, true);
  1237. gBrowser.addEventListener("PluginCrashed", gPluginHandler, true);
  1238. gBrowser.addEventListener("PluginBlocklisted", gPluginHandler, true);
  1239. gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
  1240. gBrowser.addEventListener("PluginDisabled", gPluginHandler, true);
  1241. gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
  1242. #ifdef XP_MACOSX
  1243. gBrowser.addEventListener("npapi-carbon-event-model-failure", gPluginHandler, true);
  1244. #endif
  1245. Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);
  1246. window.addEventListener("AppCommand", HandleAppCommandEvent, true);
  1247. var webNavigation;
  1248. try {
  1249. webNavigation = getWebNavigation();
  1250. if (!webNavigation)
  1251. throw "no XBL binding for browser";
  1252. } catch (e) {
  1253. alert("Error launching browser window:" + e);
  1254. window.close(); // Give up.
  1255. return;
  1256. }
  1257. messageManager.loadFrameScript("chrome://browser/content/content.js", true);
  1258. // initialize observers and listeners
  1259. // and give C++ access to gBrowser
  1260. gBrowser.init();
  1261. XULBrowserWindow.init();
  1262. window.QueryInterface(Ci.nsIInterfaceRequestor)
  1263. .getInterface(nsIWebNavigation)
  1264. .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
  1265. .QueryInterface(Ci.nsIInterfaceRequestor)
  1266. .getInterface(Ci.nsIXULWindow)
  1267. .XULBrowserWindow = window.XULBrowserWindow;
  1268. window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
  1269. new nsBrowserAccess();
  1270. // set default character set if provided
  1271. if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) {
  1272. if (window.arguments[1].indexOf("charset=") != -1) {
  1273. var arrayArgComponents = window.arguments[1].split("=");
  1274. if (arrayArgComponents) {
  1275. //we should "inherit" the charset menu setting in a new window
  1276. getMarkupDocumentViewer().defaultCharacterSet = arrayArgComponents[1];
  1277. }
  1278. }
  1279. }
  1280. // Manually hook up session and global history for the first browser
  1281. // so that we don't have to load global history before bringing up a
  1282. // window.
  1283. // Wire up session and global history before any possible
  1284. // progress notifications for back/forward button updating
  1285. webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
  1286. .createInstance(Components.interfaces.nsISHistory);
  1287. Services.obs.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
  1288. // remove the disablehistory attribute so the browser cleans up, as
  1289. // though it had done this work itself
  1290. gBrowser.browsers[0].removeAttribute("disablehistory");
  1291. // enable global history
  1292. try {
  1293. gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
  1294. } catch(ex) {
  1295. Components.utils.reportError("Places database may be locked: " + ex);
  1296. }
  1297. #ifdef MOZ_E10S_COMPAT
  1298. // Bug 666801 - WebProgress support for e10s
  1299. #else
  1300. // hook up UI through progress listener
  1301. gBrowser.addProgressListener(window.XULBrowserWindow);
  1302. gBrowser.addTabsProgressListener(window.TabsProgressListener);
  1303. #endif
  1304. // setup our common DOMLinkAdded listener
  1305. gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
  1306. // setup our MozApplicationManifest listener
  1307. gBrowser.addEventListener("MozApplicationManifest",
  1308. OfflineApps, false);
  1309. // setup simple gestures support
  1310. gGestureSupport.init(true);
  1311. }
  1312. function delayedStartup(isLoadingBlank, mustLoadSidebar) {
  1313. gDelayedStartupTimeoutId = null;
  1314. Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
  1315. Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
  1316. Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
  1317. Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
  1318. Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
  1319. Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
  1320. Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
  1321. BrowserOffline.init();
  1322. OfflineApps.init();
  1323. IndexedDBPromptHelper.init();
  1324. gFormSubmitObserver.init();
  1325. AddonManager.addAddonListener(AddonsMgrListener);
  1326. gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
  1327. // Ensure login manager is up and running.
  1328. Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
  1329. if (mustLoadSidebar) {
  1330. let sidebar = document.getElementById("sidebar");
  1331. let sidebarBox = document.getElementById("sidebar-box");
  1332. sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
  1333. }
  1334. UpdateUrlbarSearchSplitterState();
  1335. if (isLoadingBlank && gURLBar && isElementVisible(gURLBar))
  1336. gURLBar.focus();
  1337. else
  1338. gBrowser.selectedBrowser.focus();
  1339. gNavToolbox.customizeDone = BrowserToolboxCustomizeDone;
  1340. gNavToolbox.customizeChange = BrowserToolboxCustomizeChange;
  1341. // Set up Sanitize Item
  1342. initializeSanitizer();
  1343. // Enable/Disable auto-hide tabbar
  1344. gBrowser.tabContainer.updateVisibility();
  1345. gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
  1346. var homeButton = document.getElementById("home-button");
  1347. gHomeButton.updateTooltip(homeButton);
  1348. gHomeButton.updatePersonalToolbarStyle(homeButton);
  1349. #ifdef HAVE_SHELL_SERVICE
  1350. // Perform default browser checking (after window opens).
  1351. var shell = getShellService();
  1352. if (shell) {
  1353. var shouldCheck = shell.shouldCheckDefaultBrowser;
  1354. var willRecoverSession = false;
  1355. try {
  1356. var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
  1357. getService(Ci.nsISessionStartup);
  1358. willRecoverSession =
  1359. (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
  1360. }
  1361. catch (ex) { /* never mind; suppose SessionStore is broken */ }
  1362. if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
  1363. var brandBundle = document.getElementById("bundle_brand");
  1364. var shellBundle = document.getElementById("bundle_shell");
  1365. var brandShortName = brandBundle.getString("brandShortName");
  1366. var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
  1367. var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
  1368. [brandShortName]);
  1369. var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
  1370. [brandShortName]);
  1371. var checkEveryTime = { value: shouldCheck };
  1372. var ps = Services.prompt;
  1373. var rv = ps.confirmEx(window, promptTitle, promptMessage,
  1374. ps.STD_YES_NO_BUTTONS,
  1375. null, null, null, checkboxLabel, checkEveryTime);
  1376. if (rv == 0)
  1377. shell.setDefaultBrowser(true, false);
  1378. shell.shouldCheckDefaultBrowser = checkEveryTime.value;
  1379. }
  1380. }
  1381. #endif
  1382. // BiDi UI
  1383. gBidiUI = isBidiEnabled();
  1384. if (gBidiUI) {
  1385. document.getElementById("documentDirection-separator").hidden = false;
  1386. document.getElementById("documentDirection-swap").hidden = false;
  1387. document.getElementById("textfieldDirection-separator").hidden = false;
  1388. document.getElementById("textfieldDirection-swap").hidden = false;
  1389. }
  1390. // Setup click-and-hold gestures access to the session history
  1391. // menus if global click-and-hold isn't turned on
  1392. if (!getBoolPref("ui.click_hold_context_menus", false))
  1393. SetClickAndHoldHandlers();
  1394. // Initialize the full zoom setting.
  1395. // We do this before the session restore service gets initialized so we can
  1396. // apply full zoom settings to tabs restored by the session restore service.
  1397. try {
  1398. FullZoom.init();
  1399. }
  1400. catch(ex) {
  1401. Components.utils.reportError("Failed to init content pref service:\n" + ex);
  1402. }
  1403. #ifdef MOZ_E10S_COMPAT
  1404. // Bug 666804 - NetworkPrioritizer support for e10s
  1405. #else
  1406. let NP = {};
  1407. Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
  1408. NP.trackBrowserWindow(window);
  1409. #endif
  1410. // initialize the session-restore service (in case it's not already running)
  1411. try {
  1412. Cc["@mozilla.org/browser/sessionstore;1"]
  1413. .getService(Ci.nsISessionStore)
  1414. .init(window);
  1415. } catch (ex) {
  1416. dump("nsSessionStore could not be initialized: " + ex + "\n");
  1417. }
  1418. PlacesToolbarHelper.init();
  1419. ctrlTab.readPref();
  1420. gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
  1421. gPrefService.addObserver(allTabs.prefName, allTabs, false);
  1422. // Delayed initialization of the livemarks update timer.
  1423. // Livemark updates don't need to start until after bookmark UI
  1424. // such as the toolbar has initialized. Starting 5 seconds after
  1425. // delayedStartup in order to stagger this before the download manager starts.
  1426. setTimeout(function() PlacesUtils.livemarks.start(), 5000);
  1427. // Initialize the download manager some time after the app starts so that
  1428. // auto-resume downloads begin (such as after crashing or quitting with
  1429. // active downloads) and speeds up the first-load of the download manager UI.
  1430. // If the user manually opens the download manager before the timeout, the
  1431. // downloads will start right away, and getting the service again won't hurt.
  1432. setTimeout(function() {
  1433. gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
  1434. getService(Ci.nsIDownloadManager);
  1435. if (Win7Features) {
  1436. let tempScope = {};
  1437. Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm",
  1438. tempScope);
  1439. tempScope.DownloadTaskbarProgress.onBrowserWindowLoad(window);
  1440. }
  1441. }, 10000);
  1442. #ifndef XP_MACOSX
  1443. updateEditUIVisibility();
  1444. let placesContext = document.getElementById("placesContext");
  1445. placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
  1446. placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
  1447. #endif
  1448. gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
  1449. gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
  1450. gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
  1451. #ifdef MOZ_E10S_COMPAT
  1452. // Bug 666808 - AeroPeek support for e10s
  1453. #else
  1454. if (Win7Features)
  1455. Win7Features.onOpenWindow();
  1456. #endif
  1457. // called when we go into full screen, even if it is
  1458. // initiated by a web page script
  1459. window.addEventListener("fullscreen", onFullScreen, true);
  1460. if (window.fullScreen)
  1461. onFullScreen();
  1462. #ifdef MOZ_SERVICES_SYNC
  1463. // initialize the sync UI
  1464. gSyncUI.init();
  1465. #endif
  1466. TabView.init();
  1467. // Enable Inspector?
  1468. let enabled = gPrefService.getBoolPref(InspectorUI.prefEnabledName);
  1469. if (enabled) {
  1470. document.getElementById("menu_pageinspect").hidden = false;
  1471. document.getElementById("Tools:Inspect").removeAttribute("disabled");
  1472. #ifdef MENUBAR_CAN_AUTOHIDE
  1473. document.getElementById("appmenu_pageInspect").hidden = false;
  1474. #endif
  1475. }
  1476. // Enable Error Console?
  1477. // XXX Temporarily always-enabled, see bug 601201
  1478. let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");
  1479. if (consoleEnabled) {
  1480. document.getElementById("javascriptConsole").hidden = false;
  1481. document.getElementById("key_errorConsole").removeAttribute("disabled");
  1482. #ifdef MENUBAR_CAN_AUTOHIDE
  1483. document.getElementById("appmenu_errorConsole").hidden = false;
  1484. #endif
  1485. }
  1486. // Enable Scratchpad in the UI, if the preference allows this.
  1487. let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName);
  1488. if (scratchpadEnabled) {
  1489. document.getElementById("menu_scratchpad").hidden = false;
  1490. document.getElementById("Tools:Scratchpad").removeAttribute("disabled");
  1491. #ifdef MENUBAR_CAN_AUTOHIDE
  1492. document.getElementById("appmenu_scratchpad").hidden = false;
  1493. #endif
  1494. }
  1495. #ifdef MENUBAR_CAN_AUTOHIDE
  1496. // If the user (or the locale) hasn't enabled the top-level "Character
  1497. // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
  1498. // hide it.
  1499. if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
  1500. Ci.nsIPrefLocalizedString).data)
  1501. document.getElementById("appmenu_charsetMenu").hidden = true;
  1502. #endif
  1503. Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
  1504. }
  1505. function BrowserShutdown() {
  1506. // In certain scenarios it's possible for unload to be fired before onload,
  1507. // (e.g. if the window is being closed after browser.js loads but before the
  1508. // load completes). In that case, there's nothing to do here.
  1509. if (!gStartupRan)
  1510. return;
  1511. // First clean up services initialized in BrowserStartup (or those whose
  1512. // uninit methods don't depend on the services having been initialized).
  1513. allTabs.uninit();
  1514. CombinedStopReload.uninit();
  1515. gGestureSupport.init(false);
  1516. FullScreen.cleanup();
  1517. Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
  1518. try {
  1519. gBrowser.removeProgressListener(window.XULBrowserWindow);
  1520. gBrowser.removeTabsProgressListener(window.TabsProgressListener);
  1521. } catch (ex) {
  1522. }
  1523. PlacesStarButton.uninit();
  1524. gPrivateBrowsingUI.uninit();
  1525. TabsInTitlebar.uninit();
  1526. var enumerator = Services.wm.getEnumerator(null);
  1527. enumerator.getNext();
  1528. if (!enumerator.hasMoreElements()) {
  1529. document.persist("sidebar-box", "sidebarcommand");
  1530. document.persist("sidebar-box", "width");
  1531. document.persist("sidebar-box", "src");
  1532. document.persist("sidebar-title", "value");
  1533. }
  1534. // Now either cancel delayedStartup, or clean up the services initialized from
  1535. // it.
  1536. if (gDelayedStartupTimeoutId) {
  1537. clearTimeout(gDelayedStartupTimeoutId);
  1538. } else {
  1539. if (Win7Features)
  1540. Win7Features.onCloseWindow();
  1541. gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
  1542. gPrefService.removeObserver(allTabs.prefName, allTabs);
  1543. ctrlTab.uninit();
  1544. TabView.uninit();
  1545. try {
  1546. FullZoom.destroy();
  1547. }
  1548. catch(ex) {
  1549. Components.utils.reportError(ex);
  1550. }
  1551. Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
  1552. Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
  1553. Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
  1554. Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
  1555. Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
  1556. Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
  1557. Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
  1558. try {
  1559. gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
  1560. } catch (ex) {
  1561. Components.utils.reportError(ex);
  1562. }
  1563. BrowserOffline.uninit();
  1564. OfflineApps.uninit();
  1565. IndexedDBPromptHelper.uninit();
  1566. AddonManager.removeAddonListener(AddonsMgrListener);
  1567. }
  1568. // Final window teardown, do this last.
  1569. window.XULBrowserWindow.destroy();
  1570. window.XULBrowserWindow = null;
  1571. window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  1572. .getInterface(Components.interfaces.nsIWebNavigation)
  1573. .QueryInterface(Components.interfaces.nsIDocShellTreeItem).treeOwner
  1574. .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  1575. .getInterface(Components.interfaces.nsIXULWindow)
  1576. .XULBrowserWindow = null;
  1577. window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
  1578. }
  1579. #ifdef XP_MACOSX
  1580. // nonBrowserWindowStartup(), nonBrowserWindowDelayedStartup(), and
  1581. // nonBrowserWindowShutdown() are used for non-browser windows in
  1582. // macBrowserOverlay
  1583. function nonBrowserWindowStartup() {
  1584. // Disable inappropriate commands / submenus
  1585. var disabledItems = ['Browser:SavePage',
  1586. 'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
  1587. 'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
  1588. 'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
  1589. 'viewHistorySidebar', 'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
  1590. 'View:PageInfo', 'Tasks:InspectPage', 'Browser:ToggleTabView', ];
  1591. var element;
  1592. for (var id in disabledItems) {
  1593. element = document.getElementById(disabledItems[id]);
  1594. if (element)
  1595. element.setAttribute("disabled", "true");
  1596. }
  1597. // If no windows are active (i.e. we're the hidden window), disable the close, minimize
  1598. // and zoom menu commands as well
  1599. if (window.location.href == "chrome://browser/content/hiddenWindow.xul") {
  1600. var hiddenWindowDisabledItems = ['cmd_close', 'minimizeWindow', 'zoomWindow'];
  1601. for (var id in hiddenWindowDisabledItems) {
  1602. element = document.getElementById(hiddenWindowDisabledItems[id]);
  1603. if (element)
  1604. element.setAttribute("disabled", "true");
  1605. }
  1606. // also hide the window-list separator
  1607. element = document.getElementById("sep-window-list");
  1608. element.setAttribute("hidden", "true");
  1609. // Setup the dock menu.
  1610. let dockMenuElement = document.getElementById("menu_mac_dockmenu");
  1611. if (dockMenuElement != null) {
  1612. let nativeMenu = Cc["@mozilla.org/widget/standalonenativemenu;1"]
  1613. .createInstance(Ci.nsIStandaloneNativeMenu);
  1614. try {
  1615. nativeMenu.init(dockMenuElement);
  1616. let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
  1617. .getService(Ci.nsIMacDockSupport);
  1618. dockSupport.dockMenu = nativeMenu;
  1619. }
  1620. catch (e) {
  1621. }
  1622. }
  1623. }
  1624. gDelayedStartupTimeoutId = setTimeout(nonBrowserWindowDelayedStartup, 0);
  1625. }
  1626. function nonBrowserWindowDelayedStartup() {
  1627. gDelayedStartupTimeoutId = null;
  1628. // initialise the offline listener
  1629. BrowserOffline.init();
  1630. // Set up Sanitize Item
  1631. initializeSanitizer();
  1632. // initialize the private browsing UI
  1633. gPrivateBrowsingUI.init();
  1634. #ifdef MOZ_SERVICES_SYNC
  1635. // initialize the sync UI
  1636. gSyncUI.init();
  1637. #endif
  1638. gStartupRan = true;
  1639. }
  1640. function nonBrowserWindowShutdown() {
  1641. // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
  1642. // just cancel the pending timeout and return;
  1643. if (gDelayedStartupTimeoutId) {
  1644. clearTimeout(gDelayedStartupTimeoutId);
  1645. return;
  1646. }
  1647. BrowserOffline.uninit();
  1648. gPrivateBrowsingUI.uninit();
  1649. }
  1650. #endif
  1651. function initializeSanitizer()
  1652. {
  1653. const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize";
  1654. if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) {
  1655. gPrefService.clearUserPref(kDidSanitizeDomain);
  1656. // We need to persist this preference change, since we want to
  1657. // check it at next app start even if the browser exits abruptly
  1658. gPrefService.savePrefFile(null);
  1659. }
  1660. /**
  1661. * Migrate Firefox 3.0 privacy.item prefs under one of these conditions:
  1662. *
  1663. * a) User has customized any privacy.item prefs
  1664. * b) privacy.sanitize.sanitizeOnShutdown is set
  1665. */
  1666. if (!gPrefService.getBoolPref("privacy.sanitize.migrateFx3Prefs")) {
  1667. let itemBranch = gPrefService.getBranch("privacy.item.");
  1668. let itemArray = itemBranch.getChildList("");
  1669. // See if any privacy.item prefs are set
  1670. let doMigrate = itemArray.some(function (name) itemBranch.prefHasUserValue(name));
  1671. // Or if sanitizeOnShutdown is set
  1672. if (!doMigrate)
  1673. doMigrate = gPrefService.getBoolPref("privacy.sanitize.sanitizeOnShutdown");
  1674. if (doMigrate) {
  1675. let cpdBranch = gPrefService.getBranch("privacy.cpd.");
  1676. let clearOnShutdownBranch = gPrefService.getBranch("privacy.clearOnShutdown.");
  1677. itemArray.forEach(function (name) {
  1678. try {
  1679. // don't migrate password or offlineApps clearing in the CRH dialog since
  1680. // there's no UI for those anymore. They default to false. bug 497656
  1681. if (name != "passwords" && name != "offlineApps")
  1682. cpdBranch.setBoolPref(name, itemBranch.getBoolPref(name));
  1683. clearOnShutdownBranch.setBoolPref(name, itemBranch.getBoolPref(name));
  1684. }
  1685. catch(e) {
  1686. Cu.reportError("Exception thrown during privacy pref migration: " + e);
  1687. }
  1688. });
  1689. }
  1690. gPrefService.setBoolPref("privacy.sanitize.migrateFx3Prefs", true);
  1691. }
  1692. }
  1693. function gotoHistoryIndex(aEvent) {
  1694. let index = aEvent.target.getAttribute("index");
  1695. if (!index)
  1696. return false;
  1697. let where = whereToOpenLink(aEvent);
  1698. if (where == "current") {
  1699. // Normal click. Go there in the current tab and update session history.
  1700. try {
  1701. gBrowser.gotoIndex(index);
  1702. }
  1703. catch(ex) {
  1704. return false;
  1705. }
  1706. return true;
  1707. }
  1708. // Modified click. Go there in a new tab/window.
  1709. duplicateTabIn(gBrowser.selectedTab, where, index - gBrowser.sessionHistory.index);
  1710. return true;
  1711. }
  1712. function BrowserForward(aEvent) {
  1713. let where = whereToOpenLink(aEvent, false, true);
  1714. if (where == "current") {
  1715. try {
  1716. gBrowser.goForward();
  1717. }
  1718. catch(ex) {
  1719. }
  1720. }
  1721. else {
  1722. duplicateTabIn(gBrowser.selectedTab, where, 1);
  1723. }
  1724. }
  1725. function BrowserBack(aEvent) {
  1726. let where = whereToOpenLink(aEvent, false, true);
  1727. if (where == "current") {
  1728. try {
  1729. gBrowser.goBack();
  1730. }
  1731. catch(ex) {
  1732. }
  1733. }
  1734. else {
  1735. duplicateTabIn(gBrowser.selectedTab, where, -1);
  1736. }
  1737. }
  1738. function BrowserHandleBackspace()
  1739. {
  1740. switch (gPrefService.getIntPref("browser.backspace_action")) {
  1741. case 0:
  1742. BrowserBack();
  1743. break;
  1744. case 1:
  1745. goDoCommand("cmd_scrollPageUp");
  1746. break;
  1747. }
  1748. }
  1749. function BrowserHandleShiftBackspace()
  1750. {
  1751. switch (gPrefService.getIntPref("browser.backspace_action")) {
  1752. case 0:
  1753. BrowserForward();
  1754. break;
  1755. case 1:
  1756. goDoCommand("cmd_scrollPageDown");
  1757. break;
  1758. }
  1759. }
  1760. function BrowserStop()
  1761. {
  1762. try {
  1763. const stopFlags = nsIWebNavigation.STOP_ALL;
  1764. getWebNavigation().stop(stopFlags);
  1765. }
  1766. catch(ex) {
  1767. }
  1768. }
  1769. function BrowserReloadOrDuplicate(aEvent) {
  1770. var backgroundTabModifier = aEvent.button == 1 ||
  1771. #ifdef XP_MACOSX
  1772. aEvent.metaKey;
  1773. #else
  1774. aEvent.ctrlKey;
  1775. #endif
  1776. if (aEvent.shiftKey && !backgroundTabModifier) {
  1777. BrowserReloadSkipCache();
  1778. return;
  1779. }
  1780. let where = whereToOpenLink(aEvent, false, true);
  1781. if (where == "current")
  1782. BrowserReload();
  1783. else
  1784. duplicateTabIn(gBrowser.selectedTab, where);
  1785. }
  1786. function BrowserReload() {
  1787. const reloadFlags = nsIWebNavigation.LOAD_FLAGS_NONE;
  1788. BrowserReloadWithFlags(reloadFlags);
  1789. }
  1790. function BrowserReloadSkipCache() {
  1791. // Bypass proxy and cache.
  1792. const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
  1793. BrowserReloadWithFlags(reloadFlags);
  1794. }
  1795. var BrowserHome = BrowserGoHome;
  1796. function BrowserGoHome(aEvent) {
  1797. if (aEvent && "button" in aEvent &&
  1798. aEvent.button == 2) // right-click: do nothing
  1799. return;
  1800. var homePage = gHomeButton.getHomePage();
  1801. var where = whereToOpenLink(aEvent, false, true);
  1802. var urls;
  1803. // Home page should open in a new tab when current tab is an app tab
  1804. if (where == "current" &&
  1805. gBrowser &&
  1806. gBrowser.selectedTab.pinned)
  1807. where = "tab";
  1808. // openUILinkIn in utilityOverlay.js doesn't handle loading multiple pages
  1809. switch (where) {
  1810. case "current":
  1811. loadOneOrMoreURIs(homePage);
  1812. break;
  1813. case "tabshifted":
  1814. case "tab":
  1815. urls = homePage.split("|");
  1816. var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
  1817. gBrowser.loadTabs(urls, loadInBackground);
  1818. break;
  1819. case "window":
  1820. OpenBrowserWindow();
  1821. break;
  1822. }
  1823. }
  1824. function loadOneOrMoreURIs(aURIString)
  1825. {
  1826. #ifdef XP_MACOSX
  1827. // we're not a browser window, pass the URI string to a new browser window
  1828. if (window.location.href != getBrowserURL())
  1829. {
  1830. window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
  1831. return;
  1832. }
  1833. #endif
  1834. // This function throws for certain malformed URIs, so use exception handling
  1835. // so that we don't disrupt startup
  1836. try {
  1837. gBrowser.loadTabs(aURIString.split("|"), false, true);
  1838. }
  1839. catch (e) {
  1840. }
  1841. }
  1842. function focusAndSelectUrlBar() {
  1843. if (gURLBar && !gURLBar.readOnly) {
  1844. if (window.fullScreen)
  1845. FullScreen.mouseoverToggle(true);
  1846. if (isElementVisible(gURLBar)) {
  1847. gURLBar.focus();
  1848. gURLBar.select();
  1849. return true;
  1850. }
  1851. }
  1852. return false;
  1853. }
  1854. function openLocation() {
  1855. if (focusAndSelectUrlBar())
  1856. return;
  1857. #ifdef XP_MACOSX
  1858. if (window.location.href != getBrowserURL()) {
  1859. var win = getTopWin();
  1860. if (win) {
  1861. // If there's an open browser window, it should handle this command
  1862. win.focus()
  1863. win.openLocation();
  1864. }
  1865. else {
  1866. // If there are no open browser windows, open a new one
  1867. win = window.openDialog("chrome://browser/content/", "_blank",
  1868. "chrome,all,dialog=no", "about:blank");
  1869. win.addEventListener("load", openLocationCallback, false);
  1870. }
  1871. return;
  1872. }
  1873. #endif
  1874. openDialog("chrome://browser/content/openLocation.xul", "_blank",
  1875. "chrome,modal,titlebar", window);
  1876. }
  1877. function openLocationCallback()
  1878. {
  1879. // make sure the DOM is ready
  1880. setTimeout(function() { this.openLocation(); }, 0);
  1881. }
  1882. function BrowserOpenTab()
  1883. {
  1884. if (!gBrowser) {
  1885. // If there are no open browser windows, open a new one
  1886. window.openDialog("chrome://browser/content/", "_blank",
  1887. "chrome,all,dialog=no", "about:blank");
  1888. return;
  1889. }
  1890. gBrowser.loadOneTab("about:blank", {inBackground: false});
  1891. focusAndSelectUrlBar();
  1892. }
  1893. /* Called from the openLocation dialog. This allows that dialog to instruct
  1894. its opener to open a new window and then step completely out of the way.
  1895. Anything less byzantine is causing horrible crashes, rather believably,
  1896. though oddly only on Linux. */
  1897. function delayedOpenWindow(chrome, flags, href, postData)
  1898. {
  1899. // The other way to use setTimeout,
  1900. // setTimeout(openDialog, 10, chrome, "_blank", flags, url),
  1901. // doesn't work here. The extra "magic" extra argument setTimeout adds to
  1902. // the callback function would confuse prepareForStartup() by making
  1903. // window.arguments[1] be an integer instead of null.
  1904. setTimeout(function() { openDialog(chrome, "_blank", flags, href, null, null, postData); }, 10);
  1905. }
  1906. /* Required because the tab needs time to set up its content viewers and get the load of
  1907. the URI kicked off before becoming the active content area. */
  1908. function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFixup)
  1909. {
  1910. gBrowser.loadOneTab(aUrl, {
  1911. referrerURI: aReferrer,
  1912. charset: aCharset,
  1913. postData: aPostData,
  1914. inBackground: false,
  1915. allowThirdPartyFixup: aAllowThirdPartyFixup});
  1916. }
  1917. var gLastOpenDirectory = {
  1918. _lastDir: null,
  1919. get path() {
  1920. if (!this._lastDir || !this._lastDir.exists()) {
  1921. try {
  1922. this._lastDir = gPrefService.getComplexValue("browser.open.lastDir",
  1923. Ci.nsILocalFile);
  1924. if (!this._lastDir.exists())
  1925. this._lastDir = null;
  1926. }
  1927. catch(e) {}
  1928. }
  1929. return this._lastDir;
  1930. },
  1931. set path(val) {
  1932. if (!val || !val.exists() || !val.isDirectory())
  1933. return;
  1934. this._lastDir = val.clone();
  1935. // Don't save the last open directory pref inside the Private Browsing mode
  1936. if (!gPrivateBrowsingUI.privateBrowsingEnabled)
  1937. gPrefService.setComplexValue("browser.open.lastDir", Ci.nsILocalFile,
  1938. this._lastDir);
  1939. },
  1940. reset: function() {
  1941. this._lastDir = null;
  1942. }
  1943. };
  1944. function BrowserOpenFileWindow()
  1945. {
  1946. // Get filepicker component.
  1947. try {
  1948. const nsIFilePicker = Components.interfaces.nsIFilePicker;
  1949. var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  1950. fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
  1951. fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
  1952. nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
  1953. fp.displayDirectory = gLastOpenDirectory.path;
  1954. if (fp.show() == nsIFilePicker.returnOK) {
  1955. if (fp.file && fp.file.exists())
  1956. gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
  1957. openTopWin(fp.fileURL.spec);
  1958. }
  1959. } catch (ex) {
  1960. }
  1961. }
  1962. function BrowserCloseTabOrWindow() {
  1963. #ifdef XP_MACOSX
  1964. // If we're not a browser window, just close the window
  1965. if (window.location.href != getBrowserURL()) {
  1966. closeWindow(true);
  1967. return;
  1968. }
  1969. #endif
  1970. // If the current tab is the last one, this will close the window.
  1971. gBrowser.removeCurrentTab({animate: true});
  1972. }
  1973. function BrowserTryToCloseWindow()
  1974. {
  1975. if (WindowIsClosing())
  1976. window.close(); // WindowIsClosing does all the necessary checks
  1977. }
  1978. function loadURI(uri, referrer, postData, allowThirdPartyFixup)
  1979. {
  1980. try {
  1981. if (postData === undefined)
  1982. postData = null;
  1983. var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
  1984. if (allowThirdPartyFixup) {
  1985. flags = nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
  1986. }
  1987. gBrowser.loadURIWithFlags(uri, flags, referrer, null, postData);
  1988. } catch (e) {
  1989. }
  1990. }
  1991. function getShortcutOrURI(aURL, aPostDataRef, aMayInheritPrincipal) {
  1992. // Initialize outparam to false
  1993. if (aMayInheritPrincipal)
  1994. aMayInheritPrincipal.value = false;
  1995. var shortcutURL = null;
  1996. var keyword = aURL;
  1997. var param = "";
  1998. var offset = aURL.indexOf(" ");
  1999. if (offset > 0) {
  2000. keyword = aURL.substr(0, offset);
  2001. param = aURL.substr(offset + 1);
  2002. }
  2003. if (!aPostDataRef)
  2004. aPostDataRef = {};
  2005. var engine = Services.search.getEngineByAlias(keyword);
  2006. if (engine) {
  2007. var submission = engine.getSubmission(param);
  2008. aPostDataRef.value = submission.postData;
  2009. return submission.uri.spec;
  2010. }
  2011. [shortcutURL, aPostDataRef.value] =
  2012. PlacesUtils.getURLAndPostDataForKeyword(keyword);
  2013. if (!shortcutURL)
  2014. return aURL;
  2015. var postData = "";
  2016. if (aPostDataRef.value)
  2017. postData = unescape(aPostDataRef.value);
  2018. if (/%s/i.test(shortcutURL) || /%s/i.test(postData)) {
  2019. var charset = "";
  2020. const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
  2021. var matches = shortcutURL.match(re);
  2022. if (matches)
  2023. [, shortcutURL, charset] = matches;
  2024. else {
  2025. // Try to get the saved character-set.
  2026. try {
  2027. // makeURI throws if URI is invalid.
  2028. // Will return an empty string if character-set is not found.
  2029. charset = PlacesUtils.history.getCharsetForURI(makeURI(shortcutURL));
  2030. } catch (e) {}
  2031. }
  2032. // encodeURIComponent produces UTF-8, and cannot be used for other charsets.
  2033. // escape() works in those cases, but it doesn't uri-encode +, @, and /.
  2034. // Therefore we need to manually replace these ASCII characters by their
  2035. // encodeURIComponent result, to match the behavior of nsEscape() with
  2036. // url_XPAlphas
  2037. var encodedParam = "";
  2038. if (charset && charset != "UTF-8")
  2039. encodedParam = escape(convertFromUnicode(charset, param)).
  2040. replace(/[+@\/]+/g, encodeURIComponent);
  2041. else // Default charset is UTF-8
  2042. encodedParam = encodeURIComponent(param);
  2043. shortcutURL = shortcutURL.replace(/%s/g, encodedParam).replace(/%S/g, param);
  2044. if (/%s/i.test(postData)) // POST keyword
  2045. aPostDataRef.value = getPostDataStream(postData, param, encodedParam,
  2046. "application/x-www-form-urlencoded");
  2047. }
  2048. else if (param) {
  2049. // This keyword doesn't take a parameter, but one was provided. Just return
  2050. // the original URL.
  2051. aPostDataRef.value = null;
  2052. return aURL;
  2053. }
  2054. // This URL came from a bookmark, so it's safe to let it inherit the current
  2055. // document's principal.
  2056. if (aMayInheritPrincipal)
  2057. aMayInheritPrincipal.value = true;
  2058. return shortcutURL;
  2059. }
  2060. function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
  2061. var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
  2062. createInstance(Ci.nsIStringInputStream);
  2063. aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
  2064. dataStream.data = aStringData;
  2065. var mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"].
  2066. createInstance(Ci.nsIMIMEInputStream);
  2067. mimeStream.addHeader("Content-Type", aType);
  2068. mimeStream.addContentLength = true;
  2069. mimeStream.setData(dataStream);
  2070. return mimeStream.QueryInterface(Ci.nsIInputStream);
  2071. }
  2072. function readFromClipboard()
  2073. {
  2074. var url;
  2075. try {
  2076. // Get clipboard.
  2077. var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
  2078. .getService(Components.interfaces.nsIClipboard);
  2079. // Create transferable that will transfer the text.
  2080. var trans = Components.classes["@mozilla.org/widget/transferable;1"]
  2081. .createInstance(Components.interfaces.nsITransferable);
  2082. trans.addDataFlavor("text/unicode");
  2083. // If available, use selection clipboard, otherwise global one
  2084. if (clipboard.supportsSelectionClipboard())
  2085. clipboard.getData(trans, clipboard.kSelectionClipboard);
  2086. else
  2087. clipboard.getData(trans, clipboard.kGlobalClipboard);
  2088. var data = {};
  2089. var dataLen = {};
  2090. trans.getTransferData("text/unicode", data, dataLen);
  2091. if (data) {
  2092. data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
  2093. url = data.data.substring(0, dataLen.value / 2);
  2094. }
  2095. } catch (ex) {
  2096. }
  2097. return url;
  2098. }
  2099. function BrowserViewSourceOfDocument(aDocument)
  2100. {
  2101. var pageCookie;
  2102. var webNav;
  2103. // Get the document charset
  2104. var docCharset = "charset=" + aDocument.characterSet;
  2105. // Get the nsIWebNavigation associated with the document
  2106. try {
  2107. var win;
  2108. var ifRequestor;
  2109. // Get the DOMWindow for the requested document. If the DOMWindow
  2110. // cannot be found, then just use the content window...
  2111. //
  2112. // XXX: This is a bit of a hack...
  2113. win = aDocument.defaultView;
  2114. if (win == window) {
  2115. win = content;
  2116. }
  2117. ifRequestor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  2118. webNav = ifRequestor.getInterface(nsIWebNavigation);
  2119. } catch(err) {
  2120. // If nsIWebNavigation cannot be found, just get the one for the whole
  2121. // window...
  2122. webNav = getWebNavigation();
  2123. }
  2124. //
  2125. // Get the 'PageDescriptor' for the current document. This allows the
  2126. // view-source to access the cached copy of the content rather than
  2127. // refetching it from the network...
  2128. //
  2129. try{
  2130. var PageLoader = webNav.QueryInterface(Components.interfaces.nsIWebPageDescriptor);
  2131. pageCookie = PageLoader.currentDescriptor;
  2132. } catch(err) {
  2133. // If no page descriptor is available, just use the view-source URL...
  2134. }
  2135. top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
  2136. }
  2137. // doc - document to use for source, or null for this window's document
  2138. // initialTab - name of the initial tab to display, or null for the first tab
  2139. // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
  2140. function BrowserPageInfo(doc, initialTab, imageElement) {
  2141. var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
  2142. var windows = Cc['@mozilla.org/appshell/window-mediator;1']
  2143. .getService(Ci.nsIWindowMediator)
  2144. .getEnumerator("Browser:page-info");
  2145. var documentURL = doc ? doc.location : window.content.document.location;
  2146. // Check for windows matching the url
  2147. while (windows.hasMoreElements()) {
  2148. var currentWindow = windows.getNext();
  2149. if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
  2150. currentWindow.focus();
  2151. currentWindow.resetPageInfo(args);
  2152. return currentWindow;
  2153. }
  2154. }
  2155. // We didn't find a matching window, so open a new one.
  2156. return openDialog("chrome://browser/content/pageinfo/pageInfo.xul", "",
  2157. "chrome,toolbar,dialog=no,resizable", args);
  2158. }
  2159. function URLBarSetURI(aURI) {
  2160. var value = gBrowser.userTypedValue;
  2161. var valid = false;
  2162. if (value == null) {
  2163. let uri = aURI || getWebNavigation().currentURI;
  2164. // Replace initial page URIs with an empty string
  2165. // only if there's no opener (bug 370555).
  2166. if (gInitialPages.indexOf(uri.spec) != -1)
  2167. value = content.opener ? uri.spec : "";
  2168. else
  2169. value = losslessDecodeURI(uri);
  2170. valid = (uri.spec != "about:blank");
  2171. }
  2172. gURLBar.value = value;
  2173. SetPageProxyState(valid ? "valid" : "invalid");
  2174. }
  2175. function losslessDecodeURI(aURI) {
  2176. var value = aURI.spec;
  2177. // Try to decode as UTF-8 if there's no encoding sequence that we would break.
  2178. if (!/%25(?:3B|2F|3F|3A|40|26|3D|2B|24|2C|23)/i.test(value))
  2179. try {
  2180. value = decodeURI(value)
  2181. // 1. decodeURI decodes %25 to %, which creates unintended
  2182. // encoding sequences. Re-encode it, unless it's part of
  2183. // a sequence that survived decodeURI, i.e. one for:
  2184. // ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#'
  2185. // (RFC 3987 section 3.2)
  2186. // 2. Re-encode whitespace so that it doesn't get eaten away
  2187. // by the location bar (bug 410726).
  2188. .replace(/%(?!3B|2F|3F|3A|40|26|3D|2B|24|2C|23)|[\r\n\t]/ig,
  2189. encodeURIComponent);
  2190. } catch (e) {}
  2191. // Encode invisible characters (line and paragraph separator,
  2192. // object replacement character) (bug 452979)
  2193. value = value.replace(/[\v\x0c\x1c\x1d\x1e\x1f\u2028\u2029\ufffc]/g,
  2194. encodeURIComponent);
  2195. // Encode default ignorable characters (bug 546013)
  2196. // except ZWNJ (U+200C) and ZWJ (U+200D) (bug 582186).
  2197. // This includes all bidirectional formatting characters.
  2198. // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
  2199. value = value.replace(/[\u00ad\u034f\u115f-\u1160\u17b4-\u17b5\u180b-\u180d\u200b\u200e-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g,
  2200. encodeURIComponent);
  2201. return value;
  2202. }
  2203. function UpdateUrlbarSearchSplitterState()
  2204. {
  2205. var splitter = document.getElementById("urlbar-search-splitter");
  2206. var urlbar = document.getElementById("urlbar-container");
  2207. var searchbar = document.getElementById("search-container");
  2208. var stop = document.getElementById("stop-button");
  2209. var ibefore = null;
  2210. if (urlbar && searchbar) {
  2211. if (urlbar.nextSibling == searchbar ||
  2212. urlbar.getAttribute("combined") &&
  2213. stop && stop.nextSibling == searchbar)
  2214. ibefore = searchbar;
  2215. else if (searchbar.nextSibling == urlbar)
  2216. ibefore = urlbar;
  2217. }
  2218. if (ibefore) {
  2219. if (!splitter) {
  2220. splitter = document.createElement("splitter");
  2221. splitter.id = "urlbar-search-splitter";
  2222. splitter.setAttribute("resizebefore", "flex");
  2223. splitter.setAttribute("resizeafter", "flex");
  2224. splitter.className = "chromeclass-toolbar-additional";
  2225. }
  2226. urlbar.parentNode.insertBefore(splitter, ibefore);
  2227. } else if (splitter)
  2228. splitter.parentNode.removeChild(splitter);
  2229. }
  2230. var LocationBarHelpers = {
  2231. _timeoutID: null,
  2232. _searchBegin: function LocBar_searchBegin() {
  2233. function delayedBegin(self) {
  2234. self._timeoutID = null;
  2235. document.getElementById("urlbar-throbber").setAttribute("busy", "true");
  2236. }
  2237. this._timeoutID = setTimeout(delayedBegin, 500, this);
  2238. },
  2239. _searchComplete: function LocBar_searchComplete() {
  2240. // Did we finish the search before delayedBegin was invoked?
  2241. if (this._timeoutID) {
  2242. clearTimeout(this._timeoutID);
  2243. this._timeoutID = null;
  2244. }
  2245. document.getElementById("urlbar-throbber").removeAttribute("busy");
  2246. }
  2247. };
  2248. function UpdatePageProxyState()
  2249. {
  2250. if (gURLBar && gURLBar.value != gLastValidURLStr)
  2251. SetPageProxyState("invalid");
  2252. }
  2253. function SetPageProxyState(aState)
  2254. {
  2255. if (!gURLBar)
  2256. return;
  2257. if (!gProxyFavIcon)
  2258. gProxyFavIcon = document.getElementById("page-proxy-favicon");
  2259. gURLBar.setAttribute("pageproxystate", aState);
  2260. gProxyFavIcon.setAttribute("pageproxystate", aState);
  2261. // the page proxy state is set to valid via OnLocationChange, which
  2262. // gets called when we switch tabs.
  2263. if (aState == "valid") {
  2264. gLastValidURLStr = gURLBar.value;
  2265. gURLBar.addEventListener("input", UpdatePageProxyState, false);
  2266. PageProxySetIcon(gBrowser.getIcon());
  2267. } else if (aState == "invalid") {
  2268. gURLBar.removeEventListener("input", UpdatePageProxyState, false);
  2269. PageProxyClearIcon();
  2270. }
  2271. }
  2272. function PageProxySetIcon (aURL)
  2273. {
  2274. if (!gProxyFavIcon)
  2275. return;
  2276. if (!aURL)
  2277. PageProxyClearIcon();
  2278. else if (gProxyFavIcon.getAttribute("src") != aURL)
  2279. gProxyFavIcon.setAttribute("src", aURL);
  2280. }
  2281. function PageProxyClearIcon ()
  2282. {
  2283. gProxyFavIcon.removeAttribute("src");
  2284. }
  2285. function PageProxyClickHandler(aEvent)
  2286. {
  2287. if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
  2288. middleMousePaste(aEvent);
  2289. }
  2290. /**
  2291. * Handle load of some pages (about:*) so that we can make modifications
  2292. * to the DOM for unprivileged pages.
  2293. */
  2294. function BrowserOnAboutPageLoad(document) {
  2295. if (/^about:home$/i.test(document.documentURI)) {
  2296. let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
  2297. getService(Components.interfaces.nsISessionStore);
  2298. if (!ss.canRestoreLastSession)
  2299. document.getElementById("sessionRestoreContainer").hidden = true;
  2300. }
  2301. }
  2302. /**
  2303. * Handle command events bubbling up from error page content
  2304. */
  2305. function BrowserOnClick(event) {
  2306. // Don't trust synthetic events
  2307. if (!event.isTrusted || event.target.localName != "button")
  2308. return;
  2309. var ot = event.originalTarget;
  2310. var errorDoc = ot.ownerDocument;
  2311. // If the event came from an ssl error page, it is probably either the "Add
  2312. // Exception…" or "Get me out of here!" button
  2313. if (/^about:certerror/.test(errorDoc.documentURI)) {
  2314. if (ot == errorDoc.getElementById('exceptionDialogButton')) {
  2315. var params = { exceptionAdded : false, handlePrivateBrowsing : true };
  2316. try {
  2317. switch (gPrefService.getIntPref("browser.ssl_override_behavior")) {
  2318. case 2 : // Pre-fetch & pre-populate
  2319. params.prefetchCert = true;
  2320. case 1 : // Pre-populate
  2321. params.location = errorDoc.location.href;
  2322. }
  2323. } catch (e) {
  2324. Components.utils.reportError("Couldn't get ssl_override pref: " + e);
  2325. }
  2326. window.openDialog('chrome://pippki/content/exceptionDialog.xul',
  2327. '','chrome,centerscreen,modal', params);
  2328. // If the user added the exception cert, attempt to reload the page
  2329. if (params.exceptionAdded)
  2330. errorDoc.location.reload();
  2331. }
  2332. else if (ot == errorDoc.getElementById('getMeOutOfHereButton')) {
  2333. getMeOutOfHere();
  2334. }
  2335. }
  2336. else if (/^about:blocked/.test(errorDoc.documentURI)) {
  2337. // The event came from a button on a malware/phishing block page
  2338. // First check whether it's malware or phishing, so that we can
  2339. // use the right strings/links
  2340. var isMalware = /e=malwareBlocked/.test(errorDoc.documentURI);
  2341. if (ot == errorDoc.getElementById('getMeOutButton')) {
  2342. getMeOutOfHere();
  2343. }
  2344. else if (ot == errorDoc.getElementById('reportButton')) {
  2345. // This is the "Why is this site blocked" button. For malware,
  2346. // we can fetch a site-specific report, for phishing, we redirect
  2347. // to the generic page describing phishing protection.
  2348. if (isMalware) {
  2349. // Get the stop badware "why is this blocked" report url,
  2350. // append the current url, and go there.
  2351. try {
  2352. let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
  2353. reportURL += errorDoc.location.href;
  2354. content.location = reportURL;
  2355. } catch (e) {
  2356. Components.utils.reportError("Couldn't get malware report URL: " + e);
  2357. }
  2358. }
  2359. else { // It's a phishing site, not malware
  2360. try {
  2361. content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
  2362. } catch (e) {
  2363. Components.utils.reportError("Couldn't get phishing info URL: " + e);
  2364. }
  2365. }
  2366. }
  2367. else if (ot == errorDoc.getElementById('ignoreWarningButton')) {
  2368. // Allow users to override and continue through to the site,
  2369. // but add a notify bar as a reminder, so that they don't lose
  2370. // track after, e.g., tab switching.
  2371. gBrowser.loadURIWithFlags(content.location.href,
  2372. nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
  2373. null, null, null);
  2374. Services.perms.add(makeURI(content.location.href), "safe-browsing",
  2375. Ci.nsIPermissionManager.ALLOW_ACTION,
  2376. Ci.nsIPermissionManager.EXPIRE_SESSION);
  2377. let buttons = [{
  2378. label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
  2379. accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
  2380. callback: function() { getMeOutOfHere(); }
  2381. }];
  2382. let title;
  2383. if (isMalware) {
  2384. title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
  2385. buttons[1] = {
  2386. label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
  2387. accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
  2388. callback: function() {
  2389. openUILinkIn(safebrowsing.getReportURL('MalwareError'), 'tab');
  2390. }
  2391. };
  2392. } else {
  2393. title = gNavigatorBundle.getString("safebrowsing.reportedWebForgery");
  2394. buttons[1] = {
  2395. label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
  2396. accessKey: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.accessKey"),
  2397. callback: function() {
  2398. openUILinkIn(safebrowsing.getReportURL('Error'), 'tab');
  2399. }
  2400. };
  2401. }
  2402. let notificationBox = gBrowser.getNotificationBox();
  2403. let value = "blocked-badware-page";
  2404. let previousNotification = notificationBox.getNotificationWithValue(value);
  2405. if (previousNotification)
  2406. notificationBox.removeNotification(previousNotification);
  2407. notificationBox.appendNotification(
  2408. title,
  2409. value,
  2410. "chrome://global/skin/icons/blacklist_favicon.png",
  2411. notificationBox.PRIORITY_CRITICAL_HIGH,
  2412. buttons
  2413. );
  2414. }
  2415. }
  2416. else if (/^about:home$/i.test(errorDoc.documentURI)) {
  2417. if (ot == errorDoc.getElementById("restorePreviousSession")) {
  2418. let ss = Cc["@mozilla.org/browser/sessionstore;1"].
  2419. getService(Ci.nsISessionStore);
  2420. if (ss.canRestoreLastSession)
  2421. ss.restoreLastSession();
  2422. errorDoc.getElementById("sessionRestoreContainer").hidden = true;
  2423. }
  2424. }
  2425. }
  2426. /**
  2427. * Re-direct the browser to a known-safe page. This function is
  2428. * used when, for example, the user browses to a known malware page
  2429. * and is presented with about:blocked. The "Get me out of here!"
  2430. * button should take the user to the default start page so that even
  2431. * when their own homepage is infected, we can get them somewhere safe.
  2432. */
  2433. function getMeOutOfHere() {
  2434. // Get the start page from the *default* pref branch, not the user's
  2435. var prefs = Cc["@mozilla.org/preferences-service;1"]
  2436. .getService(Ci.nsIPrefService).getDefaultBranch(null);
  2437. var url = "about:blank";
  2438. try {
  2439. url = prefs.getComplexValue("browser.startup.homepage",
  2440. Ci.nsIPrefLocalizedString).data;
  2441. // If url is a pipe-delimited set of pages, just take the first one.
  2442. if (url.indexOf("|") != -1)
  2443. url = url.split("|")[0];
  2444. } catch(e) {
  2445. Components.utils.reportError("Couldn't get homepage pref: " + e);
  2446. }
  2447. content.location = url;
  2448. }
  2449. function BrowserFullScreen()
  2450. {
  2451. window.fullScreen = !window.fullScreen;
  2452. }
  2453. function onFullScreen(event) {
  2454. FullScreen.toggle(event);
  2455. }
  2456. function getWebNavigation()
  2457. {
  2458. try {
  2459. return gBrowser.webNavigation;
  2460. } catch (e) {
  2461. return null;
  2462. }
  2463. }
  2464. function BrowserReloadWithFlags(reloadFlags) {
  2465. /* First, we'll try to use the session history object to reload so
  2466. * that framesets are handled properly. If we're in a special
  2467. * window (such as view-source) that has no session history, fall
  2468. * back on using the web navigation's reload method.
  2469. */
  2470. var webNav = getWebNavigation();
  2471. try {
  2472. var sh = webNav.sessionHistory;
  2473. if (sh)
  2474. webNav = sh.QueryInterface(nsIWebNavigation);
  2475. } catch (e) {
  2476. }
  2477. try {
  2478. webNav.reload(reloadFlags);
  2479. } catch (e) {
  2480. }
  2481. }
  2482. var PrintPreviewListener = {
  2483. _printPreviewTab: null,
  2484. _tabBeforePrintPreview: null,
  2485. getPrintPreviewBrowser: function () {
  2486. if (!this._printPreviewTab) {
  2487. this._tabBeforePrintPreview = gBrowser.selectedTab;
  2488. this._printPreviewTab = gBrowser.loadOneTab("about:blank",
  2489. { inBackground: false });
  2490. gBrowser.selectedTab = this._printPreviewTab;
  2491. }
  2492. return gBrowser.getBrowserForTab(this._printPreviewTab);
  2493. },
  2494. getSourceBrowser: function () {
  2495. return this._tabBeforePrintPreview ?
  2496. this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
  2497. },
  2498. getNavToolbox: function () {
  2499. return gNavToolbox;
  2500. },
  2501. onEnter: function () {
  2502. gInPrintPreviewMode = true;
  2503. this._toggleAffectedChrome();
  2504. },
  2505. onExit: function () {
  2506. gBrowser.selectedTab = this._tabBeforePrintPreview;
  2507. this._tabBeforePrintPreview = null;
  2508. gInPrintPreviewMode = false;
  2509. this._toggleAffectedChrome();
  2510. gBrowser.removeTab(this._printPreviewTab);
  2511. this._printPreviewTab = null;
  2512. },
  2513. _toggleAffectedChrome: function () {
  2514. gNavToolbox.collapsed = gInPrintPreviewMode;
  2515. if (gInPrintPreviewMode)
  2516. this._hideChrome();
  2517. else
  2518. this._showChrome();
  2519. if (this._chromeState.sidebarOpen)
  2520. toggleSidebar(this._sidebarCommand);
  2521. #ifdef MENUBAR_CAN_AUTOHIDE
  2522. updateAppButtonDisplay();
  2523. #endif
  2524. },
  2525. _hideChrome: function () {
  2526. this._chromeState = {};
  2527. var sidebar = document.getElementById("sidebar-box");
  2528. this._chromeState.sidebarOpen = !sidebar.hidden;
  2529. this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
  2530. var notificationBox = gBrowser.getNotificationBox();
  2531. this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
  2532. notificationBox.notificationsHidden = true;
  2533. document.getElementById("sidebar").setAttribute("src", "about:blank");
  2534. var addonBar = document.getElementById("addon-bar");
  2535. this._chromeState.addonBarOpen = !addonBar.collapsed;
  2536. addonBar.collapsed = true;
  2537. gBrowser.updateWindowResizers();
  2538. this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
  2539. if (gFindBarInitialized)
  2540. gFindBar.close();
  2541. this._chromeState.syncNotificationsOpen = false;
  2542. var syncNotifications = document.getElementById("sync-notifications");
  2543. if (syncNotifications) {
  2544. this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
  2545. syncNotifications.notificationsHidden = true;
  2546. }
  2547. },
  2548. _showChrome: function () {
  2549. if (this._chromeState.notificationsOpen)
  2550. gBrowser.getNotificationBox().notificationsHidden = false;
  2551. if (this._chromeState.addonBarOpen) {
  2552. document.getElementById("addon-bar").collapsed = false;
  2553. gBrowser.updateWindowResizers();
  2554. }
  2555. if (this._chromeState.findOpen)
  2556. gFindBar.open();
  2557. if (this._chromeState.syncNotificationsOpen)
  2558. document.getElementById("sync-notifications").notificationsHidden = false;
  2559. }
  2560. }
  2561. function getMarkupDocumentViewer()
  2562. {
  2563. return gBrowser.markupDocumentViewer;
  2564. }
  2565. /**
  2566. * Content area tooltip.
  2567. * XXX - this must move into XBL binding/equiv! Do not want to pollute
  2568. * browser.js with functionality that can be encapsulated into
  2569. * browser widget. TEMPORARY!
  2570. *
  2571. * NOTE: Any changes to this routine need to be mirrored in DefaultTooltipTextProvider::GetNodeText()
  2572. * (located in mozilla/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp)
  2573. * which performs the same function, but for embedded clients that
  2574. * don't use a XUL/JS layer. It is important that the logic of
  2575. * these two routines be kept more or less in sync.
  2576. * (pinkerton)
  2577. **/
  2578. function FillInHTMLTooltip(tipElement)
  2579. {
  2580. var retVal = false;
  2581. // Don't show the tooltip if the tooltip node is a XUL element or a document.
  2582. if (tipElement.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" ||
  2583. !tipElement.ownerDocument)
  2584. return retVal;
  2585. const XLinkNS = "http://www.w3.org/1999/xlink";
  2586. var titleText = null;
  2587. var XLinkTitleText = null;
  2588. var SVGTitleText = null;
  2589. var lookingForSVGTitle = true;
  2590. var direction = tipElement.ownerDocument.dir;
  2591. // If the element is invalid per HTML5 Forms specifications and has no title,
  2592. // show the constraint validation error message.
  2593. if ((tipElement instanceof HTMLInputElement ||
  2594. tipElement instanceof HTMLTextAreaElement ||
  2595. tipElement instanceof HTMLSelectElement ||
  2596. tipElement instanceof HTMLButtonElement) &&
  2597. !tipElement.hasAttribute('title') &&
  2598. (!tipElement.form || !tipElement.form.noValidate)) {
  2599. // If the element is barred from constraint validation or valid,
  2600. // the validation message will be the empty string.
  2601. titleText = tipElement.validationMessage;
  2602. }
  2603. while (!titleText && !XLinkTitleText && !SVGTitleText && tipElement) {
  2604. if (tipElement.nodeType == Node.ELEMENT_NODE) {
  2605. titleText = tipElement.getAttribute("title");
  2606. if ((tipElement instanceof HTMLAnchorElement && tipElement.href) ||
  2607. (tipElement instanceof HTMLAreaElement && tipElement.href) ||
  2608. (tipElement instanceof HTMLLinkElement && tipElement.href) ||
  2609. (tipElement instanceof SVGAElement && tipElement.hasAttributeNS(XLinkNS, "href"))) {
  2610. XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
  2611. }
  2612. if (lookingForSVGTitle &&
  2613. (!(tipElement instanceof SVGElement) ||
  2614. tipElement.parentNode.nodeType == Node.DOCUMENT_NODE)) {
  2615. lookingForSVGTitle = false;
  2616. }
  2617. if (lookingForSVGTitle) {
  2618. let length = tipElement.childNodes.length;
  2619. for (let i = 0; i < length; i++) {
  2620. let childNode = tipElement.childNodes[i];
  2621. if (childNode instanceof SVGTitleElement) {
  2622. SVGTitleText = childNode.textContent;
  2623. break;
  2624. }
  2625. }
  2626. }
  2627. var defView = tipElement.ownerDocument.defaultView;
  2628. // XXX Work around bug 350679:
  2629. // "Tooltips can be fired in documents with no view".
  2630. if (!defView)
  2631. return retVal;
  2632. direction = defView.getComputedStyle(tipElement, "")
  2633. .getPropertyValue("direction");
  2634. }
  2635. tipElement = tipElement.parentNode;
  2636. }
  2637. var tipNode = document.getElementById("aHTMLTooltip");
  2638. tipNode.style.direction = direction;
  2639. [titleText, XLinkTitleText, SVGTitleText].forEach(function (t) {
  2640. if (t && /\S/.test(t)) {
  2641. // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be
  2642. // replaced with spaces, and LFs should be removed entirely.
  2643. // XXX Bug 322270: We don't preserve the result of entities like &#13;,
  2644. // which should result in a line break in the tooltip, because we can't
  2645. // distinguish that from a literal character in the source by this point.
  2646. t = t.replace(/[\r\t]/g, ' ');
  2647. t = t.replace(/\n/g, '');
  2648. tipNode.setAttribute("label", t);
  2649. retVal = true;
  2650. }
  2651. });
  2652. return retVal;
  2653. }
  2654. var browserDragAndDrop = {
  2655. canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
  2656. dragOver: function (aEvent)
  2657. {
  2658. if (this.canDropLink(aEvent)) {
  2659. aEvent.preventDefault();
  2660. }
  2661. },
  2662. drop: function (aEvent, aName) Services.droppedLinkHandler.dropLink(aEvent, aName)
  2663. };
  2664. var homeButtonObserver = {
  2665. onDrop: function (aEvent)
  2666. {
  2667. setTimeout(openHomeDialog, 0, browserDragAndDrop.drop(aEvent, { }));
  2668. },
  2669. onDragOver: function (aEvent)
  2670. {
  2671. browserDragAndDrop.dragOver(aEvent);
  2672. aEvent.dropEffect = "link";
  2673. },
  2674. onDragExit: function (aEvent)
  2675. {
  2676. }
  2677. }
  2678. function openHomeDialog(aURL)
  2679. {
  2680. var promptTitle = gNavigatorBundle.getString("droponhometitle");
  2681. var promptMsg = gNavigatorBundle.getString("droponhomemsg");
  2682. var pressedVal = Services.prompt.confirmEx(window, promptTitle, promptMsg,
  2683. Services.prompt.STD_YES_NO_BUTTONS,
  2684. null, null, null, null, {value:0});
  2685. if (pressedVal == 0) {
  2686. try {
  2687. var str = Components.classes["@mozilla.org/supports-string;1"]
  2688. .createInstance(Components.interfaces.nsISupportsString);
  2689. str.data = aURL;
  2690. gPrefService.setComplexValue("browser.startup.homepage",
  2691. Components.interfaces.nsISupportsString, str);
  2692. } catch (ex) {
  2693. dump("Failed to set the home page.\n"+ex+"\n");
  2694. }
  2695. }
  2696. }
  2697. var bookmarksButtonObserver = {
  2698. onDrop: function (aEvent)
  2699. {
  2700. let name = { };
  2701. let url = browserDragAndDrop.drop(aEvent, name);
  2702. try {
  2703. PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(url), name);
  2704. } catch(ex) { }
  2705. },
  2706. onDragOver: function (aEvent)
  2707. {
  2708. browserDragAndDrop.dragOver(aEvent);
  2709. aEvent.dropEffect = "link";
  2710. },
  2711. onDragExit: function (aEvent)
  2712. {
  2713. }
  2714. }
  2715. var newTabButtonObserver = {
  2716. onDragOver: function (aEvent)
  2717. {
  2718. browserDragAndDrop.dragOver(aEvent);
  2719. },
  2720. onDragExit: function (aEvent)
  2721. {
  2722. },
  2723. onDrop: function (aEvent)
  2724. {
  2725. let url = browserDragAndDrop.drop(aEvent, { });
  2726. var postData = {};
  2727. url = getShortcutOrURI(url, postData);
  2728. if (url) {
  2729. // allow third-party services to fixup this URL
  2730. openNewTabWith(url, null, postData.value, aEvent, true);
  2731. }
  2732. }
  2733. }
  2734. var newWindowButtonObserver = {
  2735. onDragOver: function (aEvent)
  2736. {
  2737. browserDragAndDrop.dragOver(aEvent);
  2738. },
  2739. onDragExit: function (aEvent)
  2740. {
  2741. },
  2742. onDrop: function (aEvent)
  2743. {
  2744. let url = browserDragAndDrop.drop(aEvent, { });
  2745. var postData = {};
  2746. url = getShortcutOrURI(url, postData);
  2747. if (url) {
  2748. // allow third-party services to fixup this URL
  2749. openNewWindowWith(url, null, postData.value, true);
  2750. }
  2751. }
  2752. }
  2753. var DownloadsButtonDNDObserver = {
  2754. onDragOver: function (aEvent)
  2755. {
  2756. var types = aEvent.dataTransfer.types;
  2757. if (types.contains("text/x-moz-url") ||
  2758. types.contains("text/uri-list") ||
  2759. types.contains("text/plain"))
  2760. aEvent.preventDefault();
  2761. },
  2762. onDragExit: function (aEvent)
  2763. {
  2764. },
  2765. onDrop: function (aEvent)
  2766. {
  2767. let name = { };
  2768. let url = browserDragAndDrop.drop(aEvent, name);
  2769. if (url)
  2770. saveURL(url, name, null, true, true);
  2771. }
  2772. }
  2773. const DOMLinkHandler = {
  2774. handleEvent: function (event) {
  2775. switch (event.type) {
  2776. case "DOMLinkAdded":
  2777. this.onLinkAdded(event);
  2778. break;
  2779. }
  2780. },
  2781. onLinkAdded: function (event) {
  2782. var link = event.originalTarget;
  2783. var rel = link.rel && link.rel.toLowerCase();
  2784. if (!link || !link.ownerDocument || !rel || !link.href)
  2785. return;
  2786. var feedAdded = false;
  2787. var iconAdded = false;
  2788. var searchAdded = false;
  2789. var relStrings = rel.split(/\s+/);
  2790. var rels = {};
  2791. for (let i = 0; i < relStrings.length; i++)
  2792. rels[relStrings[i]] = true;
  2793. for (let relVal in rels) {
  2794. switch (relVal) {
  2795. case "feed":
  2796. case "alternate":
  2797. if (!feedAdded) {
  2798. if (!rels.feed && rels.alternate && rels.stylesheet)
  2799. break;
  2800. if (isValidFeed(link, link.ownerDocument.nodePrincipal, rels.feed)) {
  2801. FeedHandler.addFeed(link, link.ownerDocument);
  2802. feedAdded = true;
  2803. }
  2804. }
  2805. break;
  2806. case "icon":
  2807. if (!iconAdded) {
  2808. if (!gPrefService.getBoolPref("browser.chrome.site_icons"))
  2809. break;
  2810. var targetDoc = link.ownerDocument;
  2811. var uri = makeURI(link.href, targetDoc.characterSet);
  2812. if (gBrowser.isFailedIcon(uri))
  2813. break;
  2814. // Verify that the load of this icon is legal.
  2815. // Some error or special pages can load their favicon.
  2816. // To be on the safe side, only allow chrome:// favicons.
  2817. var isAllowedPage = [
  2818. /^about:neterror\?/,
  2819. /^about:blocked\?/,
  2820. /^about:certerror\?/,
  2821. /^about:home$/,
  2822. ].some(function (re) re.test(targetDoc.documentURI));
  2823. if (!isAllowedPage || !uri.schemeIs("chrome")) {
  2824. var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].
  2825. getService(Ci.nsIScriptSecurityManager);
  2826. try {
  2827. ssm.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri,
  2828. Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
  2829. } catch(e) {
  2830. break;
  2831. }
  2832. }
  2833. try {
  2834. var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"].
  2835. getService(Ci.nsIContentPolicy);
  2836. } catch(e) {
  2837. break; // Refuse to load if we can't do a security check.
  2838. }
  2839. // Security says okay, now ask content policy
  2840. if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
  2841. uri, targetDoc.documentURIObject,
  2842. link, link.type, null)
  2843. != Ci.nsIContentPolicy.ACCEPT)
  2844. break;
  2845. var browserIndex = gBrowser.getBrowserIndexForDocument(targetDoc);
  2846. // no browser? no favicon.
  2847. if (browserIndex == -1)
  2848. break;
  2849. let tab = gBrowser.tabs[browserIndex];
  2850. gBrowser.setIcon(tab, link.href);
  2851. iconAdded = true;
  2852. }
  2853. break;
  2854. case "search":
  2855. if (!searchAdded) {
  2856. var type = link.type && link.type.toLowerCase();
  2857. type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
  2858. if (type == "application/opensearchdescription+xml" && link.title &&
  2859. /^(?:https?|ftp):/i.test(link.href) &&
  2860. !gPrivateBrowsingUI.privateBrowsingEnabled) {
  2861. var engine = { title: link.title, href: link.href };
  2862. BrowserSearch.addEngine(engine, link.ownerDocument);
  2863. searchAdded = true;
  2864. }
  2865. }
  2866. break;
  2867. }
  2868. }
  2869. }
  2870. }
  2871. const BrowserSearch = {
  2872. addEngine: function(engine, targetDoc) {
  2873. if (!this.searchBar)
  2874. return;
  2875. var browser = gBrowser.getBrowserForDocument(targetDoc);
  2876. // ignore search engines from subframes (see bug 479408)
  2877. if (!browser)
  2878. return;
  2879. // Check to see whether we've already added an engine with this title
  2880. if (browser.engines) {
  2881. if (browser.engines.some(function (e) e.title == engine.title))
  2882. return;
  2883. }
  2884. // Append the URI and an appropriate title to the browser data.
  2885. // Use documentURIObject in the check for shouldLoadFavIcon so that we
  2886. // do the right thing with about:-style error pages. Bug 453442
  2887. var iconURL = null;
  2888. if (gBrowser.shouldLoadFavIcon(targetDoc.documentURIObject))
  2889. iconURL = targetDoc.documentURIObject.prePath + "/favicon.ico";
  2890. var hidden = false;
  2891. // If this engine (identified by title) is already in the list, add it
  2892. // to the list of hidden engines rather than to the main list.
  2893. // XXX This will need to be changed when engines are identified by URL;
  2894. // see bug 335102.
  2895. if (Services.search.getEngineByName(engine.title))
  2896. hidden = true;
  2897. var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
  2898. engines.push({ uri: engine.href,
  2899. title: engine.title,
  2900. icon: iconURL });
  2901. if (hidden)
  2902. browser.hiddenEngines = engines;
  2903. else
  2904. browser.engines = engines;
  2905. },
  2906. /**
  2907. * Gives focus to the search bar, if it is present on the toolbar, or loads
  2908. * the default engine's search form otherwise. For Mac, opens a new window
  2909. * or focuses an existing window, if necessary.
  2910. */
  2911. webSearch: function BrowserSearch_webSearch() {
  2912. #ifdef XP_MACOSX
  2913. if (window.location.href != getBrowserURL()) {
  2914. var win = getTopWin();
  2915. if (win) {
  2916. // If there's an open browser window, it should handle this command
  2917. win.focus();
  2918. win.BrowserSearch.webSearch();
  2919. } else {
  2920. // If there are no open browser windows, open a new one
  2921. function observer(subject, topic, data) {
  2922. if (subject == win) {
  2923. BrowserSearch.webSearch();
  2924. Services.obs.removeObserver(observer, "browser-delayed-startup-finished");
  2925. }
  2926. }
  2927. win = window.openDialog(getBrowserURL(), "_blank",
  2928. "chrome,all,dialog=no", "about:blank");
  2929. Services.obs.addObserver(observer, "browser-delayed-startup-finished", false);
  2930. }
  2931. return;
  2932. }
  2933. #endif
  2934. var searchBar = this.searchBar;
  2935. if (searchBar && window.fullScreen)
  2936. FullScreen.mouseoverToggle(true);
  2937. if (isElementVisible(searchBar)) {
  2938. searchBar.select();
  2939. searchBar.focus();
  2940. } else {
  2941. openUILinkIn(Services.search.defaultEngine.searchForm, "current");
  2942. }
  2943. },
  2944. /**
  2945. * Loads a search results page, given a set of search terms. Uses the current
  2946. * engine if the search bar is visible, or the default engine otherwise.
  2947. *
  2948. * @param searchText
  2949. * The search terms to use for the search.
  2950. *
  2951. * @param useNewTab
  2952. * Boolean indicating whether or not the search should load in a new
  2953. * tab.
  2954. */
  2955. loadSearch: function BrowserSearch_search(searchText, useNewTab) {
  2956. var engine;
  2957. // If the search bar is visible, use the current engine, otherwise, fall
  2958. // back to the default engine.
  2959. if (isElementVisible(this.searchBar))
  2960. engine = Services.search.currentEngine;
  2961. else
  2962. engine = Services.search.defaultEngine;
  2963. var submission = engine.getSubmission(searchText); // HTML response
  2964. // getSubmission can return null if the engine doesn't have a URL
  2965. // with a text/html response type. This is unlikely (since
  2966. // SearchService._addEngineToStore() should fail for such an engine),
  2967. // but let's be on the safe side.
  2968. if (!submission)
  2969. return;
  2970. openLinkIn(submission.uri.spec,
  2971. useNewTab ? "tab" : "current",
  2972. { postData: submission.postData,
  2973. relatedToCurrent: true });
  2974. },
  2975. /**
  2976. * Returns the search bar element if it is present in the toolbar, null otherwise.
  2977. */
  2978. get searchBar() {
  2979. return document.getElementById("searchbar");
  2980. },
  2981. loadAddEngines: function BrowserSearch_loadAddEngines() {
  2982. var newWindowPref = gPrefService.getIntPref("browser.link.open_newwindow");
  2983. var where = newWindowPref == 3 ? "tab" : "window";
  2984. var regionBundle = document.getElementById("bundle_browser_region");
  2985. var searchEnginesURL = formatURL("browser.search.searchEnginesURL", true);
  2986. openUILinkIn(searchEnginesURL, where);
  2987. }
  2988. }
  2989. function FillHistoryMenu(aParent) {
  2990. // Lazily add the hover listeners on first showing and never remove them
  2991. if (!aParent.hasStatusListener) {
  2992. // Show history item's uri in the status bar when hovering, and clear on exit
  2993. aParent.addEventListener("DOMMenuItemActive", function(aEvent) {
  2994. // Only the current page should have the checked attribute, so skip it
  2995. if (!aEvent.target.hasAttribute("checked"))
  2996. XULBrowserWindow.setOverLink(aEvent.target.getAttribute("uri"));
  2997. }, false);
  2998. aParent.addEventListener("DOMMenuItemInactive", function() {
  2999. XULBrowserWindow.setOverLink("");
  3000. }, false);
  3001. aParent.hasStatusListener = true;
  3002. }
  3003. // Remove old entries if any
  3004. var children = aParent.childNodes;
  3005. for (var i = children.length - 1; i >= 0; --i) {
  3006. if (children[i].hasAttribute("index"))
  3007. aParent.removeChild(children[i]);
  3008. }
  3009. var webNav = getWebNavigation();
  3010. var sessionHistory = webNav.sessionHistory;
  3011. var count = sessionHistory.count;
  3012. if (count <= 1) // don't display the popup for a single item
  3013. return false;
  3014. const MAX_HISTORY_MENU_ITEMS = 15;
  3015. var index = sessionHistory.index;
  3016. var half_length = Math.floor(MAX_HISTORY_MENU_ITEMS / 2);
  3017. var start = Math.max(index - half_length, 0);
  3018. var end = Math.min(start == 0 ? MAX_HISTORY_MENU_ITEMS : index + half_length + 1, count);
  3019. if (end == count)
  3020. start = Math.max(count - MAX_HISTORY_MENU_ITEMS, 0);
  3021. var tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
  3022. var tooltipCurrent = gNavigatorBundle.getString("tabHistory.current");
  3023. var tooltipForward = gNavigatorBundle.getString("tabHistory.goForward");
  3024. for (var j = end - 1; j >= start; j--) {
  3025. let item = document.createElement("menuitem");
  3026. let entry = sessionHistory.getEntryAtIndex(j, false);
  3027. let uri = entry.URI.spec;
  3028. item.setAttribute("uri", uri);
  3029. item.setAttribute("label", entry.title || uri);
  3030. item.setAttribute("index", j);
  3031. if (j != index) {
  3032. try {
  3033. let iconURL = Cc["@mozilla.org/browser/favicon-service;1"]
  3034. .getService(Ci.nsIFaviconService)
  3035. .getFaviconForPage(entry.URI).spec;
  3036. item.style.listStyleImage = "url(" + iconURL + ")";
  3037. } catch (ex) {}
  3038. }
  3039. if (j < index) {
  3040. item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
  3041. item.setAttribute("tooltiptext", tooltipBack);
  3042. } else if (j == index) {
  3043. item.setAttribute("type", "radio");
  3044. item.setAttribute("checked", "true");
  3045. item.className = "unified-nav-current";
  3046. item.setAttribute("tooltiptext", tooltipCurrent);
  3047. } else {
  3048. item.className = "unified-nav-forward menuitem-iconic menuitem-with-favicon";
  3049. item.setAttribute("tooltiptext", tooltipForward);
  3050. }
  3051. aParent.appendChild(item);
  3052. }
  3053. return true;
  3054. }
  3055. function addToUrlbarHistory(aUrlToAdd) {
  3056. if (aUrlToAdd &&
  3057. aUrlToAdd.indexOf(" ") == -1 &&
  3058. !/[\x00-\x1F]/.test(aUrlToAdd))
  3059. PlacesUIUtils.markPageAsTyped(aUrlToAdd);
  3060. }
  3061. function toJavaScriptConsole()
  3062. {
  3063. toOpenWindowByType("global:console", "chrome://global/content/console.xul");
  3064. }
  3065. function BrowserDownloadsUI()
  3066. {
  3067. Cc["@mozilla.org/download-manager-ui;1"].
  3068. getService(Ci.nsIDownloadManagerUI).show(window);
  3069. }
  3070. function toOpenWindowByType(inType, uri, features)
  3071. {
  3072. var topWindow = Services.wm.getMostRecentWindow(inType);
  3073. if (topWindow)
  3074. topWindow.focus();
  3075. else if (features)
  3076. window.open(uri, "_blank", features);
  3077. else
  3078. window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
  3079. }
  3080. function OpenBrowserWindow()
  3081. {
  3082. var charsetArg = new String();
  3083. var handler = Components.classes["@mozilla.org/browser/clh;1"]
  3084. .getService(Components.interfaces.nsIBrowserHandler);
  3085. var defaultArgs = handler.defaultArgs;
  3086. var wintype = document.documentElement.getAttribute('windowtype');
  3087. // if and only if the current window is a browser window and it has a document with a character
  3088. // set, then extract the current charset menu setting from the current document and use it to
  3089. // initialize the new browser window...
  3090. var win;
  3091. if (window && (wintype == "navigator:browser") && window.content && window.content.document)
  3092. {
  3093. var DocCharset = window.content.document.characterSet;
  3094. charsetArg = "charset="+DocCharset;
  3095. //we should "inherit" the charset menu setting in a new window
  3096. win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no", defaultArgs, charsetArg);
  3097. }
  3098. else // forget about the charset information.
  3099. {
  3100. win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no", defaultArgs);
  3101. }
  3102. return win;
  3103. }
  3104. var gCustomizeSheet = false;
  3105. // Returns a reference to the window in which the toolbar
  3106. // customization document is loaded.
  3107. function BrowserCustomizeToolbar()
  3108. {
  3109. // Disable the toolbar context menu items
  3110. var menubar = document.getElementById("main-menubar");
  3111. for (var i = 0; i < menubar.childNodes.length; ++i)
  3112. menubar.childNodes[i].setAttribute("disabled", true);
  3113. var cmd = document.getElementById("cmd_CustomizeToolbars");
  3114. cmd.setAttribute("disabled", "true");
  3115. var splitter = document.getElementById("urlbar-search-splitter");
  3116. if (splitter)
  3117. splitter.parentNode.removeChild(splitter);
  3118. CombinedStopReload.uninit();
  3119. PlacesToolbarHelper.customizeStart();
  3120. BookmarksMenuButton.customizeStart();
  3121. TabsInTitlebar.allowedBy("customizing-toolbars", false);
  3122. var customizeURL = "chrome://global/content/customizeToolbar.xul";
  3123. gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
  3124. if (gCustomizeSheet) {
  3125. var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
  3126. var panel = document.getElementById("customizeToolbarSheetPopup");
  3127. sheetFrame.hidden = false;
  3128. sheetFrame.toolbox = gNavToolbox;
  3129. sheetFrame.panel = panel;
  3130. // The document might not have been loaded yet, if this is the first time.
  3131. // If it is already loaded, reload it so that the onload initialization code
  3132. // re-runs.
  3133. if (sheetFrame.getAttribute("src") == customizeURL)
  3134. sheetFrame.contentWindow.location.reload()
  3135. else
  3136. sheetFrame.setAttribute("src", customizeURL);
  3137. // Open the panel, but make it invisible until the iframe has loaded so
  3138. // that the user doesn't see a white flash.
  3139. panel.style.visibility = "hidden";
  3140. gNavToolbox.addEventListener("beforecustomization", function () {
  3141. gNavToolbox.removeEventListener("beforecustomization", arguments.callee, false);
  3142. panel.style.removeProperty("visibility");
  3143. }, false);
  3144. panel.openPopup(gNavToolbox, "after_start", 0, 0);
  3145. return sheetFrame.contentWindow;
  3146. } else {
  3147. return window.openDialog(customizeURL,
  3148. "CustomizeToolbar",
  3149. "chrome,titlebar,toolbar,location,resizable,dependent",
  3150. gNavToolbox);
  3151. }
  3152. }
  3153. function BrowserToolboxCustomizeDone(aToolboxChanged) {
  3154. if (gCustomizeSheet) {
  3155. document.getElementById("customizeToolbarSheetIFrame").hidden = true;
  3156. document.getElementById("customizeToolbarSheetPopup").hidePopup();
  3157. }
  3158. // Update global UI elements that may have been added or removed
  3159. if (aToolboxChanged) {
  3160. gURLBar = document.getElementById("urlbar");
  3161. gProxyFavIcon = document.getElementById("page-proxy-favicon");
  3162. gHomeButton.updateTooltip();
  3163. gIdentityHandler._cacheElements();
  3164. window.XULBrowserWindow.init();
  3165. #ifndef XP_MACOSX
  3166. updateEditUIVisibility();
  3167. #endif
  3168. // Hacky: update the PopupNotifications' object's reference to the iconBox,
  3169. // if it already exists, since it may have changed if the URL bar was
  3170. // added/removed.
  3171. if (!__lookupGetter__("PopupNotifications"))
  3172. PopupNotifications.iconBox = document.getElementById("notification-popup-box");
  3173. }
  3174. PlacesToolbarHelper.customizeDone();
  3175. BookmarksMenuButton.customizeDone();
  3176. // The url bar splitter state is dependent on whether stop/reload
  3177. // and the location bar are combined, so we need this ordering
  3178. CombinedStopReload.init();
  3179. UpdateUrlbarSearchSplitterState();
  3180. // Update the urlbar
  3181. if (gURLBar) {
  3182. URLBarSetURI();
  3183. XULBrowserWindow.asyncUpdateUI();
  3184. PlacesStarButton.updateState();
  3185. }
  3186. TabsInTitlebar.allowedBy("customizing-toolbars", true);
  3187. // Re-enable parts of the UI we disabled during the dialog
  3188. var menubar = document.getElementById("main-menubar");
  3189. for (var i = 0; i < menubar.childNodes.length; ++i)
  3190. menubar.childNodes[i].setAttribute("disabled", false);
  3191. var cmd = document.getElementById("cmd_CustomizeToolbars");
  3192. cmd.removeAttribute("disabled");
  3193. // make sure to re-enable click-and-hold
  3194. if (!getBoolPref("ui.click_hold_context_menus", false))
  3195. SetClickAndHoldHandlers();
  3196. window.content.focus();
  3197. }
  3198. function BrowserToolboxCustomizeChange(aType) {
  3199. switch (aType) {
  3200. case "iconsize":
  3201. case "mode":
  3202. retrieveToolbarIconsizesFromTheme();
  3203. break;
  3204. default:
  3205. gHomeButton.updatePersonalToolbarStyle();
  3206. BookmarksMenuButton.customizeChange();
  3207. allTabs.readPref();
  3208. }
  3209. }
  3210. /**
  3211. * Allows themes to override the "iconsize" attribute on toolbars.
  3212. */
  3213. function retrieveToolbarIconsizesFromTheme() {
  3214. function retrieveToolbarIconsize(aToolbar) {
  3215. if (aToolbar.localName != "toolbar")
  3216. return;
  3217. // The theme indicates that it wants to override the "iconsize" attribute
  3218. // by specifying a special value for the "counter-reset" property on the
  3219. // toolbar. A custom property cannot be used because getComputedStyle can
  3220. // only return the values of standard CSS properties.
  3221. let counterReset = getComputedStyle(aToolbar).counterReset;
  3222. if (counterReset == "smallicons 0")
  3223. aToolbar.setAttribute("iconsize", "small");
  3224. else if (counterReset == "largeicons 0")
  3225. aToolbar.setAttribute("iconsize", "large");
  3226. }
  3227. Array.forEach(gNavToolbox.childNodes, retrieveToolbarIconsize);
  3228. gNavToolbox.externalToolbars.forEach(retrieveToolbarIconsize);
  3229. }
  3230. /**
  3231. * Update the global flag that tracks whether or not any edit UI (the Edit menu,
  3232. * edit-related items in the context menu, and edit-related toolbar buttons
  3233. * is visible, then update the edit commands' enabled state accordingly. We use
  3234. * this flag to skip updating the edit commands on focus or selection changes
  3235. * when no UI is visible to improve performance (including pageload performance,
  3236. * since focus changes when you load a new page).
  3237. *
  3238. * If UI is visible, we use goUpdateGlobalEditMenuItems to set the commands'
  3239. * enabled state so the UI will reflect it appropriately.
  3240. *
  3241. * If the UI isn't visible, we enable all edit commands so keyboard shortcuts
  3242. * still work and just lazily disable them as needed when the user presses a
  3243. * shortcut.
  3244. *
  3245. * This doesn't work on Mac, since Mac menus flash when users press their
  3246. * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
  3247. * and we need to always update the edit commands. Thus on Mac this function
  3248. * is a no op.
  3249. */
  3250. function updateEditUIVisibility()
  3251. {
  3252. #ifndef XP_MACOSX
  3253. let editMenuPopupState = document.getElementById("menu_EditPopup").state;
  3254. let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
  3255. let placesContextMenuPopupState = document.getElementById("placesContext").state;
  3256. #ifdef MENUBAR_CAN_AUTOHIDE
  3257. let appMenuPopupState = document.getElementById("appmenu-popup").state;
  3258. #endif
  3259. // The UI is visible if the Edit menu is opening or open, if the context menu
  3260. // is open, or if the toolbar has been customized to include the Cut, Copy,
  3261. // or Paste toolbar buttons.
  3262. gEditUIVisible = editMenuPopupState == "showing" ||
  3263. editMenuPopupState == "open" ||
  3264. contextMenuPopupState == "showing" ||
  3265. contextMenuPopupState == "open" ||
  3266. placesContextMenuPopupState == "showing" ||
  3267. placesContextMenuPopupState == "open" ||
  3268. #ifdef MENUBAR_CAN_AUTOHIDE
  3269. appMenuPopupState == "showing" ||
  3270. appMenuPopupState == "open" ||
  3271. #endif
  3272. document.getElementById("cut-button") ||
  3273. document.getElementById("copy-button") ||
  3274. document.getElementById("paste-button") ? true : false;
  3275. // If UI is visible, update the edit commands' enabled state to reflect
  3276. // whether or not they are actually enabled for the current focus/selection.
  3277. if (gEditUIVisible)
  3278. goUpdateGlobalEditMenuItems();
  3279. // Otherwise, enable all commands, so that keyboard shortcuts still work,
  3280. // then lazily determine their actual enabled state when the user presses
  3281. // a keyboard shortcut.
  3282. else {
  3283. goSetCommandEnabled("cmd_undo", true);
  3284. goSetCommandEnabled("cmd_redo", true);
  3285. goSetCommandEnabled("cmd_cut", true);
  3286. goSetCommandEnabled("cmd_copy", true);
  3287. goSetCommandEnabled("cmd_paste", true);
  3288. goSetCommandEnabled("cmd_selectAll", true);
  3289. goSetCommandEnabled("cmd_delete", true);
  3290. goSetCommandEnabled("cmd_switchTextDirection", true);
  3291. }
  3292. #endif
  3293. }
  3294. var FullScreen = {
  3295. _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  3296. toggle: function (event) {
  3297. var enterFS = window.fullScreen;
  3298. // We get the fullscreen event _before_ the window transitions into or out of FS mode.
  3299. if (event && event.type == "fullscreen")
  3300. enterFS = !enterFS;
  3301. // show/hide all menubars, toolbars (except the full screen toolbar)
  3302. this.showXULChrome("toolbar", !enterFS);
  3303. document.getElementById("View:FullScreen").setAttribute("checked", enterFS);
  3304. if (enterFS) {
  3305. // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
  3306. // This will help simulate the "collapse" metaphor while also requiring less code and
  3307. // events than raw listening of mouse coords.
  3308. let fullScrToggler = document.getElementById("fullscr-toggler");
  3309. if (!fullScrToggler) {
  3310. fullScrToggler = document.createElement("hbox");
  3311. fullScrToggler.id = "fullscr-toggler";
  3312. fullScrToggler.collapsed = true;
  3313. gNavToolbox.parentNode.insertBefore(fullScrToggler, gNavToolbox.nextSibling);
  3314. }
  3315. fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
  3316. fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
  3317. if (gPrefService.getBoolPref("browser.fullscreen.autohide"))
  3318. gBrowser.mPanelContainer.addEventListener("mousemove",
  3319. this._collapseCallback, false);
  3320. document.addEventListener("keypress", this._keyToggleCallback, false);
  3321. document.addEventListener("popupshown", this._setPopupOpen, false);
  3322. document.addEventListener("popuphidden", this._setPopupOpen, false);
  3323. this._shouldAnimate = true;
  3324. this.mouseoverToggle(false);
  3325. // Autohide prefs
  3326. gPrefService.addObserver("browser.fullscreen", this, false);
  3327. }
  3328. else {
  3329. // The user may quit fullscreen during an animation
  3330. clearInterval(this._animationInterval);
  3331. clearTimeout(this._animationTimeout);
  3332. gNavToolbox.style.marginTop = "";
  3333. if (this._isChromeCollapsed)
  3334. this.mouseoverToggle(true);
  3335. this._isAnimating = false;
  3336. // This is needed if they use the context menu to quit fullscreen
  3337. this._isPopupOpen = false;
  3338. this.cleanup();
  3339. }
  3340. },
  3341. cleanup: function () {
  3342. if (window.fullScreen) {
  3343. gBrowser.mPanelContainer.removeEventListener("mousemove",
  3344. this._collapseCallback, false);
  3345. document.removeEventListener("keypress", this._keyToggleCallback, false);
  3346. document.removeEventListener("popupshown", this._setPopupOpen, false);
  3347. document.removeEventListener("popuphidden", this._setPopupOpen, false);
  3348. gPrefService.removeObserver("browser.fullscreen", this);
  3349. let fullScrToggler = document.getElementById("fullscr-toggler");
  3350. if (fullScrToggler) {
  3351. fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
  3352. fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
  3353. }
  3354. }
  3355. },
  3356. observe: function(aSubject, aTopic, aData)
  3357. {
  3358. if (aData == "browser.fullscreen.autohide") {
  3359. if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
  3360. gBrowser.mPanelContainer.addEventListener("mousemove",
  3361. this._collapseCallback, false);
  3362. }
  3363. else {
  3364. gBrowser.mPanelContainer.removeEventListener("mousemove",
  3365. this._collapseCallback, false);
  3366. }
  3367. }
  3368. },
  3369. // Event callbacks
  3370. _expandCallback: function()
  3371. {
  3372. FullScreen.mouseoverToggle(true);
  3373. },
  3374. _collapseCallback: function()
  3375. {
  3376. FullScreen.mouseoverToggle(false);
  3377. },
  3378. _keyToggleCallback: function(aEvent)
  3379. {
  3380. // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
  3381. // should provide a way to collapse them too.
  3382. if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
  3383. FullScreen._shouldAnimate = false;
  3384. FullScreen.mouseoverToggle(false, true);
  3385. }
  3386. // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
  3387. else if (aEvent.keyCode == aEvent.DOM_VK_F6)
  3388. FullScreen.mouseoverToggle(true);
  3389. },
  3390. // Checks whether we are allowed to collapse the chrome
  3391. _isPopupOpen: false,
  3392. _isChromeCollapsed: false,
  3393. _safeToCollapse: function(forceHide)
  3394. {
  3395. if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
  3396. return false;
  3397. // a popup menu is open in chrome: don't collapse chrome
  3398. if (!forceHide && this._isPopupOpen)
  3399. return false;
  3400. // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
  3401. if (document.commandDispatcher.focusedElement &&
  3402. document.commandDispatcher.focusedElement.ownerDocument == document &&
  3403. document.commandDispatcher.focusedElement.localName == "input") {
  3404. if (forceHide)
  3405. // hidden textboxes that still have focus are bad bad bad
  3406. document.commandDispatcher.focusedElement.blur();
  3407. else
  3408. return false;
  3409. }
  3410. return true;
  3411. },
  3412. _setPopupOpen: function(aEvent)
  3413. {
  3414. // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
  3415. // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
  3416. // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
  3417. // toggles chrome when moving mouse to the top, it doesn't go away again.
  3418. if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
  3419. aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
  3420. FullScreen._isPopupOpen = true;
  3421. else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
  3422. aEvent.target.localName != "window")
  3423. FullScreen._isPopupOpen = false;
  3424. },
  3425. // Autohide helpers for the context menu item
  3426. getAutohide: function(aItem)
  3427. {
  3428. aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
  3429. },
  3430. setAutohide: function()
  3431. {
  3432. gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
  3433. },
  3434. // Animate the toolbars disappearing
  3435. _shouldAnimate: true,
  3436. _isAnimating: false,
  3437. _animationTimeout: null,
  3438. _animationInterval: null,
  3439. _animateUp: function()
  3440. {
  3441. // check again, the user may have done something before the animation was due to start
  3442. if (!window.fullScreen || !FullScreen._safeToCollapse(false)) {
  3443. FullScreen._isAnimating = false;
  3444. FullScreen._shouldAnimate = true;
  3445. return;
  3446. }
  3447. var animateFrameAmount = 2;
  3448. function animateUpFrame() {
  3449. animateFrameAmount *= 2;
  3450. if (animateFrameAmount >= gNavToolbox.boxObject.height) {
  3451. // We've animated enough
  3452. clearInterval(FullScreen._animationInterval);
  3453. gNavToolbox.style.marginTop = "";
  3454. FullScreen._isAnimating = false;
  3455. FullScreen._shouldAnimate = false; // Just to make sure
  3456. FullScreen.mouseoverToggle(false);
  3457. return;
  3458. }
  3459. gNavToolbox.style.marginTop = (animateFrameAmount * -1) + "px";
  3460. }
  3461. FullScreen._animationInterval = setInterval(animateUpFrame, 70);
  3462. },
  3463. mouseoverToggle: function(aShow, forceHide)
  3464. {
  3465. // Don't do anything if:
  3466. // a) we're already in the state we want,
  3467. // b) we're animating and will become collapsed soon, or
  3468. // c) we can't collapse because it would be undesirable right now
  3469. if (aShow != this._isChromeCollapsed || (!aShow && this._isAnimating) ||
  3470. (!aShow && !this._safeToCollapse(forceHide)))
  3471. return;
  3472. // browser.fullscreen.animateUp
  3473. // 0 - never animate up
  3474. // 1 - animate only for first collapse after entering fullscreen (default for perf's sake)
  3475. // 2 - animate every time it collapses
  3476. if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 0)
  3477. this._shouldAnimate = false;
  3478. if (!aShow && this._shouldAnimate) {
  3479. this._isAnimating = true;
  3480. this._shouldAnimate = false;
  3481. this._animationTimeout = setTimeout(this._animateUp, 800);
  3482. return;
  3483. }
  3484. // The chrome is collapsed so don't spam needless mousemove events
  3485. if (aShow) {
  3486. gBrowser.mPanelContainer.addEventListener("mousemove",
  3487. this._collapseCallback, false);
  3488. }
  3489. else {
  3490. gBrowser.mPanelContainer.removeEventListener("mousemove",
  3491. this._collapseCallback, false);
  3492. }
  3493. // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox,
  3494. // so we just move it off-screen instead. See bug 430687.
  3495. gNavToolbox.style.marginTop =
  3496. aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px";
  3497. document.getElementById("fullscr-toggler").collapsed = aShow;
  3498. this._isChromeCollapsed = !aShow;
  3499. if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2)
  3500. this._shouldAnimate = true;
  3501. },
  3502. showXULChrome: function(aTag, aShow)
  3503. {
  3504. var els = document.getElementsByTagNameNS(this._XULNS, aTag);
  3505. for (var i = 0; i < els.length; ++i) {
  3506. // XXX don't interfere with previously collapsed toolbars
  3507. if (els[i].getAttribute("fullscreentoolbar") == "true") {
  3508. if (!aShow) {
  3509. var toolbarMode = els[i].getAttribute("mode");
  3510. if (toolbarMode != "text") {
  3511. els[i].setAttribute("saved-mode", toolbarMode);
  3512. els[i].setAttribute("saved-iconsize",
  3513. els[i].getAttribute("iconsize"));
  3514. els[i].setAttribute("mode", "icons");
  3515. els[i].setAttribute("iconsize", "small");
  3516. }
  3517. // Give the main nav bar and the tab bar the fullscreen context menu,
  3518. // otherwise remove context menu to prevent breakage
  3519. els[i].setAttribute("saved-context",
  3520. els[i].getAttribute("context"));
  3521. if (els[i].id == "nav-bar" || els[i].id == "TabsToolbar")
  3522. els[i].setAttribute("context", "autohide-context");
  3523. else
  3524. els[i].removeAttribute("context");
  3525. // Set the inFullscreen attribute to allow specific styling
  3526. // in fullscreen mode
  3527. els[i].setAttribute("inFullscreen", true);
  3528. }
  3529. else {
  3530. function restoreAttr(attrName) {
  3531. var savedAttr = "saved-" + attrName;
  3532. if (els[i].hasAttribute(savedAttr)) {
  3533. els[i].setAttribute(attrName, els[i].getAttribute(savedAttr));
  3534. els[i].removeAttribute(savedAttr);
  3535. }
  3536. }
  3537. restoreAttr("mode");
  3538. restoreAttr("iconsize");
  3539. restoreAttr("context");
  3540. els[i].removeAttribute("inFullscreen");
  3541. }
  3542. } else {
  3543. // use moz-collapsed so it doesn't persist hidden/collapsed,
  3544. // so that new windows don't have missing toolbars
  3545. if (aShow)
  3546. els[i].removeAttribute("moz-collapsed");
  3547. else
  3548. els[i].setAttribute("moz-collapsed", "true");
  3549. }
  3550. }
  3551. if (aShow) {
  3552. gNavToolbox.removeAttribute("inFullscreen");
  3553. document.documentElement.removeAttribute("inFullscreen");
  3554. } else {
  3555. gNavToolbox.setAttribute("inFullscreen", true);
  3556. document.documentElement.setAttribute("inFullscreen", true);
  3557. }
  3558. // In tabs-on-top mode, move window controls to the tab bar,
  3559. // and in tabs-on-bottom mode, move them back to the navigation toolbar.
  3560. // When there is a chance the tab bar may be collapsed, put window
  3561. // controls on nav bar.
  3562. var fullscreenflex = document.getElementById("fullscreenflex");
  3563. var fullscreenctls = document.getElementById("window-controls");
  3564. var navbar = document.getElementById("nav-bar");
  3565. var ctlsOnTabbar = window.toolbar.visible &&
  3566. (navbar.collapsed ||
  3567. (TabsOnTop.enabled &&
  3568. !gPrefService.getBoolPref("browser.tabs.autoHide")));
  3569. if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
  3570. document.getElementById("TabsToolbar").appendChild(fullscreenctls);
  3571. // we don't need this space in tabs-on-top mode, so prevent it from
  3572. // being shown
  3573. fullscreenflex.removeAttribute("fullscreencontrol");
  3574. }
  3575. else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
  3576. navbar.appendChild(fullscreenctls);
  3577. fullscreenflex.setAttribute("fullscreencontrol", "true");
  3578. }
  3579. var controls = document.getElementsByAttribute("fullscreencontrol", "true");
  3580. for (var i = 0; i < controls.length; ++i)
  3581. controls[i].hidden = aShow;
  3582. }
  3583. };
  3584. /**
  3585. * Returns true if |aMimeType| is text-based, false otherwise.
  3586. *
  3587. * @param aMimeType
  3588. * The MIME type to check.
  3589. *
  3590. * If adding types to this function, please also check the similar
  3591. * function in findbar.xml
  3592. */
  3593. function mimeTypeIsTextBased(aMimeType)
  3594. {
  3595. return /^text\/|\+xml$/.test(aMimeType) ||
  3596. aMimeType == "application/x-javascript" ||
  3597. aMimeType == "application/javascript" ||
  3598. aMimeType == "application/xml" ||
  3599. aMimeType == "mozilla.application/cached-xul";
  3600. }
  3601. var XULBrowserWindow = {
  3602. // Stored Status, Link and Loading values
  3603. status: "",
  3604. defaultStatus: "",
  3605. jsStatus: "",
  3606. jsDefaultStatus: "",
  3607. overLink: "",
  3608. startTime: 0,
  3609. statusText: "",
  3610. isBusy: false,
  3611. inContentWhitelist: ["about:addons", "about:permissions"],
  3612. QueryInterface: function (aIID) {
  3613. if (aIID.equals(Ci.nsIWebProgressListener) ||
  3614. aIID.equals(Ci.nsIWebProgressListener2) ||
  3615. aIID.equals(Ci.nsISupportsWeakReference) ||
  3616. aIID.equals(Ci.nsIXULBrowserWindow) ||
  3617. aIID.equals(Ci.nsISupports))
  3618. return this;
  3619. throw Cr.NS_NOINTERFACE;
  3620. },
  3621. get stopCommand () {
  3622. delete this.stopCommand;
  3623. return this.stopCommand = document.getElementById("Browser:Stop");
  3624. },
  3625. get reloadCommand () {
  3626. delete this.reloadCommand;
  3627. return this.reloadCommand = document.getElementById("Browser:Reload");
  3628. },
  3629. get statusTextField () {
  3630. delete this.statusTextField;
  3631. return this.statusTextField = document.getElementById("statusbar-display");
  3632. },
  3633. get isImage () {
  3634. delete this.isImage;
  3635. return this.isImage = document.getElementById("isImage");
  3636. },
  3637. get _uriFixup () {
  3638. delete this._uriFixup;
  3639. return this._uriFixup = Cc["@mozilla.org/docshell/urifixup;1"]
  3640. .getService(Ci.nsIURIFixup);
  3641. },
  3642. init: function () {
  3643. this.throbberElement = document.getElementById("navigator-throbber");
  3644. #ifdef MOZ_E10S_COMPAT
  3645. // Bug 666809 - SecurityUI support for e10s
  3646. #else
  3647. // Initialize the security button's state and tooltip text. Remember to reset
  3648. // _hostChanged, otherwise onSecurityChange will short circuit.
  3649. var securityUI = gBrowser.securityUI;
  3650. this._hostChanged = true;
  3651. this.onSecurityChange(null, null, securityUI.state);
  3652. #endif
  3653. },
  3654. destroy: function () {
  3655. // XXXjag to avoid leaks :-/, see bug 60729
  3656. delete this.throbberElement;
  3657. delete this.stopCommand;
  3658. delete this.reloadCommand;
  3659. delete this.statusTextField;
  3660. delete this.statusText;
  3661. },
  3662. setJSStatus: function (status) {
  3663. this.jsStatus = status;
  3664. this.updateStatusField();
  3665. },
  3666. setJSDefaultStatus: function (status) {
  3667. this.jsDefaultStatus = status;
  3668. this.updateStatusField();
  3669. },
  3670. setDefaultStatus: function (status) {
  3671. this.defaultStatus = status;
  3672. this.updateStatusField();
  3673. },
  3674. setOverLink: function (url, anchorElt) {
  3675. // Encode bidirectional formatting characters.
  3676. // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
  3677. this.overLink = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
  3678. encodeURIComponent);
  3679. LinkTargetDisplay.update();
  3680. },
  3681. updateStatusField: function () {
  3682. var text, type, types = ["overLink"];
  3683. if (this._busyUI)
  3684. types.push("status");
  3685. types.push("jsStatus", "jsDefaultStatus", "defaultStatus");
  3686. for (let i = 0; !text && i < types.length; i++) {
  3687. type = types[i];
  3688. text = this[type];
  3689. }
  3690. // check the current value so we don't trigger an attribute change
  3691. // and cause needless (slow!) UI updates
  3692. if (this.statusText != text) {
  3693. let field = this.statusTextField;
  3694. field.setAttribute("previoustype", field.getAttribute("type"));
  3695. field.setAttribute("type", type);
  3696. field.label = text;
  3697. field.setAttribute("crop", type == "overLink" ? "center" : "end");
  3698. this.statusText = text;
  3699. }
  3700. },
  3701. // Called before links are navigated to to allow us to retarget them if needed.
  3702. onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
  3703. // Don't modify non-default targets or targets that aren't in top-level app
  3704. // tab docshells (isAppTab will be false for app tab subframes).
  3705. if (originalTarget != "" || !isAppTab)
  3706. return originalTarget;
  3707. // External links from within app tabs should always open in new tabs
  3708. // instead of replacing the app tab's page (Bug 575561)
  3709. let linkHost;
  3710. let docHost;
  3711. try {
  3712. linkHost = linkURI.host;
  3713. docHost = linkNode.ownerDocument.documentURIObject.host;
  3714. } catch(e) {
  3715. // nsIURI.host can throw for non-nsStandardURL nsIURIs.
  3716. // If we fail to get either host, just return originalTarget.
  3717. return originalTarget;
  3718. }
  3719. if (docHost == linkHost)
  3720. return originalTarget;
  3721. // Special case: ignore "www" prefix if it is part of host string
  3722. let [longHost, shortHost] =
  3723. linkHost.length > docHost.length ? [linkHost, docHost] : [docHost, linkHost];
  3724. if (longHost == "www." + shortHost)
  3725. return originalTarget;
  3726. return "_blank";
  3727. },
  3728. onLinkIconAvailable: function (aIconURL) {
  3729. if (gProxyFavIcon && gBrowser.userTypedValue === null)
  3730. PageProxySetIcon(aIconURL); // update the favicon in the URL bar
  3731. },
  3732. onProgressChange: function (aWebProgress, aRequest,
  3733. aCurSelfProgress, aMaxSelfProgress,
  3734. aCurTotalProgress, aMaxTotalProgress) {
  3735. // Do nothing.
  3736. },
  3737. onProgressChange64: function (aWebProgress, aRequest,
  3738. aCurSelfProgress, aMaxSelfProgress,
  3739. aCurTotalProgress, aMaxTotalProgress) {
  3740. return this.onProgressChange(aWebProgress, aRequest,
  3741. aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
  3742. aMaxTotalProgress);
  3743. },
  3744. onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
  3745. const nsIWebProgressListener = Ci.nsIWebProgressListener;
  3746. const nsIChannel = Ci.nsIChannel;
  3747. if (aStateFlags & nsIWebProgressListener.STATE_START &&
  3748. aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
  3749. if (aRequest && aWebProgress.DOMWindow == content)
  3750. this.startDocumentLoad(aRequest);
  3751. this.isBusy = true;
  3752. if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
  3753. this._busyUI = true;
  3754. // Turn the throbber on.
  3755. if (this.throbberElement)
  3756. this.throbberElement.setAttribute("busy", "true");
  3757. // XXX: This needs to be based on window activity...
  3758. this.stopCommand.removeAttribute("disabled");
  3759. CombinedStopReload.switchToStop();
  3760. }
  3761. }
  3762. else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
  3763. if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
  3764. aWebProgress.DOMWindow == content &&
  3765. aRequest)
  3766. this.endDocumentLoad(aRequest, aStatus);
  3767. // This (thanks to the filter) is a network stop or the last
  3768. // request stop outside of loading the document, stop throbbers
  3769. // and progress bars and such
  3770. if (aRequest) {
  3771. let msg = "";
  3772. let location;
  3773. // Get the URI either from a channel or a pseudo-object
  3774. if (aRequest instanceof nsIChannel || "URI" in aRequest) {
  3775. location = aRequest.URI;
  3776. // For keyword URIs clear the user typed value since they will be changed into real URIs
  3777. if (location.scheme == "keyword" && aWebProgress.DOMWindow == content)
  3778. gBrowser.userTypedValue = null;
  3779. if (location.spec != "about:blank") {
  3780. switch (aStatus) {
  3781. case Components.results.NS_ERROR_NET_TIMEOUT:
  3782. msg = gNavigatorBundle.getString("nv_timeout");
  3783. break;
  3784. }
  3785. }
  3786. }
  3787. this.status = "";
  3788. this.setDefaultStatus(msg);
  3789. // Disable menu entries for images, enable otherwise
  3790. if (content.document && mimeTypeIsTextBased(content.document.contentType))
  3791. this.isImage.removeAttribute('disabled');
  3792. else
  3793. this.isImage.setAttribute('disabled', 'true');
  3794. }
  3795. this.isBusy = false;
  3796. if (this._busyUI) {
  3797. this._busyUI = false;
  3798. // Turn the throbber off.
  3799. if (this.throbberElement)
  3800. this.throbberElement.removeAttribute("busy");
  3801. this.stopCommand.setAttribute("disabled", "true");
  3802. CombinedStopReload.switchToReload(aRequest instanceof Ci.nsIRequest);
  3803. }
  3804. }
  3805. },
  3806. onLocationChange: function (aWebProgress, aRequest, aLocationURI) {
  3807. var location = aLocationURI ? aLocationURI.spec : "";
  3808. this._hostChanged = true;
  3809. // Hide the form invalid popup.
  3810. if (gFormSubmitObserver.panelIsOpen()) {
  3811. gFormSubmitObserver.panel.hidePopup();
  3812. }
  3813. if (document.tooltipNode) {
  3814. // Optimise for the common case
  3815. if (aWebProgress.DOMWindow == content) {
  3816. document.getElementById("aHTMLTooltip").hidePopup();
  3817. document.tooltipNode = null;
  3818. }
  3819. else {
  3820. for (let tooltipWindow =
  3821. document.tooltipNode.ownerDocument.defaultView;
  3822. tooltipWindow != tooltipWindow.parent;
  3823. tooltipWindow = tooltipWindow.parent) {
  3824. if (tooltipWindow == aWebProgress.DOMWindow) {
  3825. document.getElementById("aHTMLTooltip").hidePopup();
  3826. document.tooltipNode = null;
  3827. break;
  3828. }
  3829. }
  3830. }
  3831. }
  3832. // This code here does not compare uris exactly when determining
  3833. // whether or not the message should be hidden since the message
  3834. // may be prematurely hidden when an install is invoked by a click
  3835. // on a link that looks like this:
  3836. //
  3837. // <a href="#" onclick="return install();">Install Foo</a>
  3838. //
  3839. // - which fires a onLocationChange message to uri + '#'...
  3840. var selectedBrowser = gBrowser.selectedBrowser;
  3841. if (selectedBrowser.lastURI) {
  3842. let oldSpec = selectedBrowser.lastURI.spec;
  3843. let oldIndexOfHash = oldSpec.indexOf("#");
  3844. if (oldIndexOfHash != -1)
  3845. oldSpec = oldSpec.substr(0, oldIndexOfHash);
  3846. let newSpec = location;
  3847. let newIndexOfHash = newSpec.indexOf("#");
  3848. if (newIndexOfHash != -1)
  3849. newSpec = newSpec.substr(0, newSpec.indexOf("#"));
  3850. if (newSpec != oldSpec) {
  3851. // Remove all the notifications, except for those which want to
  3852. // persist across the first location change.
  3853. let nBox = gBrowser.getNotificationBox(selectedBrowser);
  3854. nBox.removeTransientNotifications();
  3855. // Only need to call locationChange if the PopupNotifications object
  3856. // for this window has already been initialized (i.e. its getter no
  3857. // longer exists)
  3858. if (!__lookupGetter__("PopupNotifications"))
  3859. PopupNotifications.locationChange();
  3860. }
  3861. }
  3862. // Disable menu entries for images, enable otherwise
  3863. if (content.document && mimeTypeIsTextBased(content.document.contentType))
  3864. this.isImage.removeAttribute('disabled');
  3865. else
  3866. this.isImage.setAttribute('disabled', 'true');
  3867. this.hideOverLinkImmediately = true;
  3868. this.setOverLink("", null);
  3869. this.hideOverLinkImmediately = false;
  3870. // We should probably not do this if the value has changed since the user
  3871. // searched
  3872. // Update urlbar only if a new page was loaded on the primary content area
  3873. // Do not update urlbar if there was a subframe navigation
  3874. var browser = gBrowser.selectedBrowser;
  3875. if (aWebProgress.DOMWindow == content) {
  3876. if ((location == "about:blank" && !content.opener) ||
  3877. location == "") { // Second condition is for new tabs, otherwise
  3878. // reload function is enabled until tab is refreshed.
  3879. this.reloadCommand.setAttribute("disabled", "true");
  3880. } else {
  3881. this.reloadCommand.removeAttribute("disabled");
  3882. }
  3883. if (gURLBar) {
  3884. // Strip off "wyciwyg://" and passwords for the location bar
  3885. let uri = aLocationURI;
  3886. try {
  3887. uri = this._uriFixup.createExposableURI(uri);
  3888. } catch (e) {}
  3889. URLBarSetURI(uri);
  3890. // Update starring UI
  3891. PlacesStarButton.updateState();
  3892. }
  3893. // Show or hide browser chrome based on the whitelist
  3894. if (this.hideChromeForLocation(location))
  3895. document.documentElement.setAttribute("disablechrome", "true");
  3896. else
  3897. document.documentElement.removeAttribute("disablechrome");
  3898. // Disable find commands in documents that ask for them to be disabled.
  3899. let docElt = content.document.documentElement;
  3900. let disableFind = aLocationURI &&
  3901. (docElt && docElt.getAttribute("disablefastfind") == "true") &&
  3902. (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"));
  3903. let findCommands = [document.getElementById("cmd_find"),
  3904. document.getElementById("cmd_findAgain"),
  3905. document.getElementById("cmd_findPrevious")];
  3906. findCommands.forEach(function (elt) {
  3907. if (disableFind)
  3908. elt.setAttribute("disabled", "true");
  3909. else
  3910. elt.removeAttribute("disabled");
  3911. });
  3912. if (gFindBarInitialized) {
  3913. if (gFindBar.findMode != gFindBar.FIND_NORMAL) {
  3914. // Close the Find toolbar if we're in old-style TAF mode
  3915. gFindBar.close();
  3916. }
  3917. // fix bug 253793 - turn off highlight when page changes
  3918. gFindBar.getElement("highlight").checked = false;
  3919. }
  3920. }
  3921. UpdateBackForwardCommands(gBrowser.webNavigation);
  3922. // See bug 358202, when tabs are switched during a drag operation,
  3923. // timers don't fire on windows (bug 203573)
  3924. if (aRequest)
  3925. setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
  3926. else
  3927. this.asyncUpdateUI();
  3928. },
  3929. asyncUpdateUI: function () {
  3930. FeedHandler.updateFeeds();
  3931. },
  3932. hideChromeForLocation: function(aLocation) {
  3933. return this.inContentWhitelist.some(function(aSpec) {
  3934. return aSpec == aLocation;
  3935. });
  3936. },
  3937. onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
  3938. this.status = aMessage;
  3939. this.updateStatusField();
  3940. },
  3941. // Properties used to cache security state used to update the UI
  3942. _state: null,
  3943. _hostChanged: false, // onLocationChange will flip this bit
  3944. onSecurityChange: function (aWebProgress, aRequest, aState) {
  3945. // Don't need to do anything if the data we use to update the UI hasn't
  3946. // changed
  3947. if (this._state == aState &&
  3948. !this._hostChanged) {
  3949. #ifdef DEBUG
  3950. try {
  3951. var contentHost = gBrowser.contentWindow.location.host;
  3952. if (this._host !== undefined && this._host != contentHost) {
  3953. Components.utils.reportError(
  3954. "ASSERTION: browser.js host is inconsistent. Content window has " +
  3955. "<" + contentHost + "> but cached host is <" + this._host + ">.\n"
  3956. );
  3957. }
  3958. } catch (ex) {}
  3959. #endif
  3960. return;
  3961. }
  3962. this._state = aState;
  3963. #ifdef DEBUG
  3964. try {
  3965. this._host = gBrowser.contentWindow.location.host;
  3966. } catch(ex) {
  3967. this._host = null;
  3968. }
  3969. #endif
  3970. this._hostChanged = false;
  3971. // aState is defined as a bitmask that may be extended in the future.
  3972. // We filter out any unknown bits before testing for known values.
  3973. const wpl = Components.interfaces.nsIWebProgressListener;
  3974. const wpl_security_bits = wpl.STATE_IS_SECURE |
  3975. wpl.STATE_IS_BROKEN |
  3976. wpl.STATE_IS_INSECURE |
  3977. wpl.STATE_SECURE_HIGH |
  3978. wpl.STATE_SECURE_MED |
  3979. wpl.STATE_SECURE_LOW;
  3980. var level;
  3981. switch (this._state & wpl_security_bits) {
  3982. case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_HIGH:
  3983. level = "high";
  3984. break;
  3985. case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_MED:
  3986. case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_LOW:
  3987. level = "low";
  3988. break;
  3989. case wpl.STATE_IS_BROKEN:
  3990. level = "broken";
  3991. break;
  3992. }
  3993. if (level) {
  3994. // We don't style the Location Bar based on the the 'level' attribute
  3995. // anymore, but still set it for third-party themes.
  3996. if (gURLBar)
  3997. gURLBar.setAttribute("level", level);
  3998. } else {
  3999. if (gURLBar)
  4000. gURLBar.removeAttribute("level");
  4001. }
  4002. // Don't pass in the actual location object, since it can cause us to
  4003. // hold on to the window object too long. Just pass in the fields we
  4004. // care about. (bug 424829)
  4005. var location = gBrowser.contentWindow.location;
  4006. var locationObj = {};
  4007. try {
  4008. // about:blank can be used by webpages so pretend it is http
  4009. locationObj.protocol = location == "about:blank" ? "http:" : location.protocol;
  4010. locationObj.host = location.host;
  4011. locationObj.hostname = location.hostname;
  4012. locationObj.port = location.port;
  4013. } catch (ex) {
  4014. // Can sometimes throw if the URL being visited has no host/hostname,
  4015. // e.g. about:blank. The _state for these pages means we won't need these
  4016. // properties anyways, though.
  4017. }
  4018. gIdentityHandler.checkIdentity(this._state, locationObj);
  4019. },
  4020. // simulate all change notifications after switching tabs
  4021. onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
  4022. if (FullZoom.updateBackgroundTabs)
  4023. FullZoom.onLocationChange(gBrowser.currentURI, true);
  4024. var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  4025. var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
  4026. // use a pseudo-object instead of a (potentially nonexistent) channel for getting
  4027. // a correct error message - and make sure that the UI is always either in
  4028. // loading (STATE_START) or done (STATE_STOP) mode
  4029. this.onStateChange(
  4030. gBrowser.webProgress,
  4031. { URI: gBrowser.currentURI },
  4032. loadingDone ? nsIWebProgressListener.STATE_STOP : nsIWebProgressListener.STATE_START,
  4033. aStatus
  4034. );
  4035. // status message and progress value are undefined if we're done with loading
  4036. if (loadingDone)
  4037. return;
  4038. this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
  4039. },
  4040. startDocumentLoad: function XWB_startDocumentLoad(aRequest) {
  4041. // clear out feed data
  4042. gBrowser.selectedBrowser.feeds = null;
  4043. // clear out search-engine data
  4044. gBrowser.selectedBrowser.engines = null;
  4045. var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
  4046. try {
  4047. Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
  4048. } catch (e) {
  4049. }
  4050. },
  4051. endDocumentLoad: function XWB_endDocumentLoad(aRequest, aStatus) {
  4052. var urlStr = aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
  4053. var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
  4054. try {
  4055. Services.obs.notifyObservers(content, notification, urlStr);
  4056. } catch (e) {
  4057. }
  4058. }
  4059. };
  4060. var LinkTargetDisplay = {
  4061. get DELAY_SHOW() {
  4062. delete this.DELAY_SHOW;
  4063. return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
  4064. },
  4065. DELAY_HIDE: 150,
  4066. _timer: 0,
  4067. get _isVisible () XULBrowserWindow.statusTextField.label != "",
  4068. update: function () {
  4069. clearTimeout(this._timer);
  4070. window.removeEventListener("mousemove", this, true);
  4071. if (!XULBrowserWindow.overLink) {
  4072. if (XULBrowserWindow.hideOverLinkImmediately)
  4073. this._hide();
  4074. else
  4075. this._timer = setTimeout(this._hide.bind(this), this.DELAY_HIDE);
  4076. return;
  4077. }
  4078. if (this._isVisible) {
  4079. XULBrowserWindow.updateStatusField();
  4080. } else {
  4081. // Let the display appear when the mouse doesn't move within the delay
  4082. this._showDelayed();
  4083. window.addEventListener("mousemove", this, true);
  4084. }
  4085. },
  4086. handleEvent: function (event) {
  4087. switch (event.type) {
  4088. case "mousemove":
  4089. // Restart the delay since the mouse was moved
  4090. clearTimeout(this._timer);
  4091. this._showDelayed();
  4092. break;
  4093. }
  4094. },
  4095. _showDelayed: function () {
  4096. this._timer = setTimeout(function (self) {
  4097. XULBrowserWindow.updateStatusField();
  4098. window.removeEventListener("mousemove", self, true);
  4099. }, this.DELAY_SHOW, this);
  4100. },
  4101. _hide: function () {
  4102. clearTimeout(this._timer);
  4103. XULBrowserWindow.updateStatusField();
  4104. }
  4105. };
  4106. var CombinedStopReload = {
  4107. init: function () {
  4108. if (this._initialized)
  4109. return;
  4110. var urlbar = document.getElementById("urlbar-container");
  4111. var reload = document.getElementById("reload-button");
  4112. var stop = document.getElementById("stop-button");
  4113. if (urlbar) {
  4114. if (urlbar.parentNode.getAttribute("mode") != "icons" ||
  4115. !reload || urlbar.nextSibling != reload ||
  4116. !stop || reload.nextSibling != stop)
  4117. urlbar.removeAttribute("combined");
  4118. else {
  4119. urlbar.setAttribute("combined", "true");
  4120. reload = document.getElementById("urlbar-reload-button");
  4121. stop = document.getElementById("urlbar-stop-button");
  4122. }
  4123. }
  4124. if (!stop || !reload || reload.nextSibling != stop)
  4125. return;
  4126. this._initialized = true;
  4127. if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
  4128. reload.setAttribute("displaystop", "true");
  4129. stop.addEventListener("click", this, false);
  4130. this.reload = reload;
  4131. this.stop = stop;
  4132. },
  4133. uninit: function () {
  4134. if (!this._initialized)
  4135. return;
  4136. this._cancelTransition();
  4137. this._initialized = false;
  4138. this.stop.removeEventListener("click", this, false);
  4139. this.reload = null;
  4140. this.stop = null;
  4141. },
  4142. handleEvent: function (event) {
  4143. // the only event we listen to is "click" on the stop button
  4144. if (event.button == 0 &&
  4145. !this.stop.disabled)
  4146. this._stopClicked = true;
  4147. },
  4148. switchToStop: function () {
  4149. if (!this._initialized)
  4150. return;
  4151. this._cancelTransition();
  4152. this.reload.setAttribute("displaystop", "true");
  4153. },
  4154. switchToReload: function (aDelay) {
  4155. if (!this._initialized)
  4156. return;
  4157. this.reload.removeAttribute("displaystop");
  4158. if (!aDelay || this._stopClicked) {
  4159. this._stopClicked = false;
  4160. this._cancelTransition();
  4161. this.reload.disabled = XULBrowserWindow.reloadCommand
  4162. .getAttribute("disabled") == "true";
  4163. return;
  4164. }
  4165. if (this._timer)
  4166. return;
  4167. // Temporarily disable the reload button to prevent the user from
  4168. // accidentally reloading the page when intending to click the stop button
  4169. this.reload.disabled = true;
  4170. this._timer = setTimeout(function (self) {
  4171. self._timer = 0;
  4172. self.reload.disabled = XULBrowserWindow.reloadCommand
  4173. .getAttribute("disabled") == "true";
  4174. }, 650, this);
  4175. },
  4176. _cancelTransition: function () {
  4177. if (this._timer) {
  4178. clearTimeout(this._timer);
  4179. this._timer = 0;
  4180. }
  4181. }
  4182. };
  4183. var TabsProgressListener = {
  4184. onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
  4185. #ifdef MOZ_CRASHREPORTER
  4186. if (aRequest instanceof Ci.nsIChannel &&
  4187. aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
  4188. aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT &&
  4189. gCrashReporter.enabled) {
  4190. gCrashReporter.annotateCrashReport("URL", aRequest.URI.spec);
  4191. }
  4192. #endif
  4193. // Attach a listener to watch for "click" events bubbling up from error
  4194. // pages and other similar page. This lets us fix bugs like 401575 which
  4195. // require error page UI to do privileged things, without letting error
  4196. // pages have any privilege themselves.
  4197. // We can't look for this during onLocationChange since at that point the
  4198. // document URI is not yet the about:-uri of the error page.
  4199. if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
  4200. /^about:/.test(aWebProgress.DOMWindow.document.documentURI)) {
  4201. aBrowser.addEventListener("click", BrowserOnClick, false);
  4202. aBrowser.addEventListener("pagehide", function () {
  4203. aBrowser.removeEventListener("click", BrowserOnClick, false);
  4204. aBrowser.removeEventListener("pagehide", arguments.callee, true);
  4205. }, true);
  4206. // We also want to make changes to page UI for unprivileged about pages.
  4207. BrowserOnAboutPageLoad(aWebProgress.DOMWindow.document);
  4208. }
  4209. },
  4210. onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI) {
  4211. // Filter out any sub-frame loads
  4212. if (aBrowser.contentWindow == aWebProgress.DOMWindow)
  4213. FullZoom.onLocationChange(aLocationURI, false, aBrowser);
  4214. },
  4215. onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
  4216. if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
  4217. let brandBundle = document.getElementById("bundle_brand");
  4218. let brandShortName = brandBundle.getString("brandShortName");
  4219. let refreshButtonText =
  4220. gNavigatorBundle.getString("refreshBlocked.goButton");
  4221. let refreshButtonAccesskey =
  4222. gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
  4223. let message =
  4224. gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
  4225. : "refreshBlocked.redirectLabel",
  4226. [brandShortName]);
  4227. let docShell = aWebProgress.DOMWindow
  4228. .QueryInterface(Ci.nsIInterfaceRequestor)
  4229. .getInterface(Ci.nsIWebNavigation)
  4230. .QueryInterface(Ci.nsIDocShell);
  4231. let notificationBox = gBrowser.getNotificationBox(aBrowser);
  4232. let notification = notificationBox.getNotificationWithValue("refresh-blocked");
  4233. if (notification) {
  4234. notification.label = message;
  4235. notification.refreshURI = aURI;
  4236. notification.delay = aDelay;
  4237. notification.docShell = docShell;
  4238. } else {
  4239. let buttons = [{
  4240. label: refreshButtonText,
  4241. accessKey: refreshButtonAccesskey,
  4242. callback: function (aNotification, aButton) {
  4243. var refreshURI = aNotification.docShell
  4244. .QueryInterface(Ci.nsIRefreshURI);
  4245. refreshURI.forceRefreshURI(aNotification.refreshURI,
  4246. aNotification.delay, true);
  4247. }
  4248. }];
  4249. notification =
  4250. notificationBox.appendNotification(message, "refresh-blocked",
  4251. "chrome://browser/skin/Info.png",
  4252. notificationBox.PRIORITY_INFO_MEDIUM,
  4253. buttons);
  4254. notification.refreshURI = aURI;
  4255. notification.delay = aDelay;
  4256. notification.docShell = docShell;
  4257. }
  4258. return false;
  4259. }
  4260. return true;
  4261. }
  4262. }
  4263. function nsBrowserAccess() { }
  4264. nsBrowserAccess.prototype = {
  4265. QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
  4266. openURI: function (aURI, aOpener, aWhere, aContext) {
  4267. var newWindow = null;
  4268. var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
  4269. if (isExternal && aURI && aURI.schemeIs("chrome")) {
  4270. dump("use -chrome command-line option to load external chrome urls\n");
  4271. return null;
  4272. }
  4273. if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW)
  4274. aWhere = gPrefService.getIntPref("browser.link.open_newwindow");
  4275. switch (aWhere) {
  4276. case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
  4277. // FIXME: Bug 408379. So how come this doesn't send the
  4278. // referrer like the other loads do?
  4279. var url = aURI ? aURI.spec : "about:blank";
  4280. // Pass all params to openDialog to ensure that "url" isn't passed through
  4281. // loadOneOrMoreURIs, which splits based on "|"
  4282. newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
  4283. break;
  4284. case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
  4285. let win, needToFocusWin;
  4286. // try the current window. if we're in a popup, fall back on the most recent browser window
  4287. if (!window.document.documentElement.getAttribute("chromehidden"))
  4288. win = window;
  4289. else {
  4290. win = Cc["@mozilla.org/browser/browserglue;1"]
  4291. .getService(Ci.nsIBrowserGlue)
  4292. .getMostRecentBrowserWindow();
  4293. needToFocusWin = true;
  4294. }
  4295. if (!win) {
  4296. // we couldn't find a suitable window, a new one needs to be opened.
  4297. return null;
  4298. }
  4299. if (isExternal && (!aURI || aURI.spec == "about:blank")) {
  4300. win.BrowserOpenTab(); // this also focuses the location bar
  4301. win.focus();
  4302. newWindow = win.content;
  4303. break;
  4304. }
  4305. let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
  4306. let referrer = aOpener ? makeURI(aOpener.location.href) : null;
  4307. let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
  4308. referrerURI: referrer,
  4309. fromExternal: isExternal,
  4310. inBackground: loadInBackground});
  4311. let browser = win.gBrowser.getBrowserForTab(tab);
  4312. newWindow = browser.contentWindow;
  4313. if (needToFocusWin || (!loadInBackground && isExternal))
  4314. newWindow.focus();
  4315. break;
  4316. default : // OPEN_CURRENTWINDOW or an illegal value
  4317. newWindow = content;
  4318. if (aURI) {
  4319. let referrer = aOpener ? makeURI(aOpener.location.href) : null;
  4320. let loadflags = isExternal ?
  4321. Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
  4322. Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
  4323. gBrowser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
  4324. }
  4325. if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
  4326. content.focus();
  4327. }
  4328. return newWindow;
  4329. },
  4330. isTabContentWindow: function (aWindow) {
  4331. return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
  4332. }
  4333. }
  4334. function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
  4335. var popup = aEvent.target;
  4336. if (popup != aEvent.currentTarget)
  4337. return;
  4338. // Empty the menu
  4339. for (var i = popup.childNodes.length-1; i >= 0; --i) {
  4340. var deadItem = popup.childNodes[i];
  4341. if (deadItem.hasAttribute("toolbarId"))
  4342. popup.removeChild(deadItem);
  4343. }
  4344. var firstMenuItem = aInsertPoint || popup.firstChild;
  4345. let toolbarNodes = Array.slice(gNavToolbox.childNodes);
  4346. toolbarNodes.push(document.getElementById("addon-bar"));
  4347. toolbarNodes.forEach(function(toolbar) {
  4348. var toolbarName = toolbar.getAttribute("toolbarname");
  4349. if (toolbarName) {
  4350. let menuItem = document.createElement("menuitem");
  4351. let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
  4352. "autohide" : "collapsed";
  4353. menuItem.setAttribute("id", "toggle_" + toolbar.id);
  4354. menuItem.setAttribute("toolbarId", toolbar.id);
  4355. menuItem.setAttribute("type", "checkbox");
  4356. menuItem.setAttribute("label", toolbarName);
  4357. menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
  4358. if (popup.id != "appmenu_customizeMenu")
  4359. menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
  4360. if (popup.id != "toolbar-context-menu")
  4361. menuItem.setAttribute("key", toolbar.getAttribute("key"));
  4362. popup.insertBefore(menuItem, firstMenuItem);
  4363. menuItem.addEventListener("command", onViewToolbarCommand, false);
  4364. }
  4365. }, this);
  4366. }
  4367. function onViewToolbarCommand(aEvent) {
  4368. var toolbarId = aEvent.originalTarget.getAttribute("toolbarId");
  4369. var toolbar = document.getElementById(toolbarId);
  4370. var isVisible = aEvent.originalTarget.getAttribute("checked") == "true";
  4371. setToolbarVisibility(toolbar, isVisible);
  4372. }
  4373. function setToolbarVisibility(toolbar, isVisible) {
  4374. var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
  4375. "autohide" : "collapsed";
  4376. toolbar.setAttribute(hidingAttribute, !isVisible);
  4377. document.persist(toolbar.id, hidingAttribute);
  4378. PlacesToolbarHelper.init();
  4379. BookmarksMenuButton.updatePosition();
  4380. gBrowser.updateWindowResizers();
  4381. #ifdef MENUBAR_CAN_AUTOHIDE
  4382. updateAppButtonDisplay();
  4383. #endif
  4384. }
  4385. var TabsOnTop = {
  4386. toggle: function () {
  4387. this.enabled = !this.enabled;
  4388. },
  4389. syncCommand: function () {
  4390. let enabled = this.enabled;
  4391. document.getElementById("cmd_ToggleTabsOnTop")
  4392. .setAttribute("checked", enabled);
  4393. document.documentElement.setAttribute("tabsontop", enabled);
  4394. document.getElementById("TabsToolbar").setAttribute("tabsontop", enabled);
  4395. gBrowser.tabContainer.setAttribute("tabsontop", enabled);
  4396. TabsInTitlebar.allowedBy("tabs-on-top", enabled);
  4397. },
  4398. get enabled () {
  4399. return gNavToolbox.getAttribute("tabsontop") == "true";
  4400. },
  4401. set enabled (val) {
  4402. gNavToolbox.setAttribute("tabsontop", !!val);
  4403. this.syncCommand();
  4404. return val;
  4405. }
  4406. }
  4407. var TabsInTitlebar = {
  4408. init: function () {
  4409. #ifdef CAN_DRAW_IN_TITLEBAR
  4410. this._readPref();
  4411. Services.prefs.addObserver(this._prefName, this, false);
  4412. // Don't trust the initial value of the sizemode attribute; wait for the resize event.
  4413. this.allowedBy("sizemode", false);
  4414. window.addEventListener("resize", function (event) {
  4415. if (event.target != window)
  4416. return;
  4417. let sizemode = document.documentElement.getAttribute("sizemode");
  4418. TabsInTitlebar.allowedBy("sizemode",
  4419. sizemode == "maximized" || sizemode == "fullscreen");
  4420. }, false);
  4421. this._initialized = true;
  4422. #endif
  4423. },
  4424. allowedBy: function (condition, allow) {
  4425. #ifdef CAN_DRAW_IN_TITLEBAR
  4426. if (allow) {
  4427. if (condition in this._disallowed) {
  4428. delete this._disallowed[condition];
  4429. this._update();
  4430. }
  4431. } else {
  4432. if (!(condition in this._disallowed)) {
  4433. this._disallowed[condition] = null;
  4434. this._update();
  4435. }
  4436. }
  4437. #endif
  4438. },
  4439. get enabled() {
  4440. return document.documentElement.getAttribute("tabsintitlebar") == "true";
  4441. },
  4442. #ifdef CAN_DRAW_IN_TITLEBAR
  4443. observe: function (subject, topic, data) {
  4444. if (topic == "nsPref:changed")
  4445. this._readPref();
  4446. },
  4447. _initialized: false,
  4448. _disallowed: {},
  4449. _prefName: "browser.tabs.drawInTitlebar",
  4450. _readPref: function () {
  4451. this.allowedBy("pref",
  4452. Services.prefs.getBoolPref(this._prefName));
  4453. },
  4454. _update: function () {
  4455. if (!this._initialized || window.fullScreen)
  4456. return;
  4457. let allowed = true;
  4458. for (let something in this._disallowed) {
  4459. allowed = false;
  4460. break;
  4461. }
  4462. if (allowed == this.enabled)
  4463. return;
  4464. function $(id) document.getElementById(id);
  4465. let titlebar = $("titlebar");
  4466. if (allowed) {
  4467. function rect(ele) ele.getBoundingClientRect();
  4468. let tabsToolbar = $("TabsToolbar");
  4469. let appmenuButtonBox = $("appmenu-button-container");
  4470. let captionButtonsBox = $("titlebar-buttonbox");
  4471. this._sizePlaceholder("appmenu-button", rect(appmenuButtonBox).width);
  4472. this._sizePlaceholder("caption-buttons", rect(captionButtonsBox).width);
  4473. let tabsToolbarRect = rect(tabsToolbar);
  4474. let titlebarTop = rect($("titlebar-content")).top;
  4475. titlebar.style.marginBottom = - Math.min(tabsToolbarRect.top - titlebarTop,
  4476. tabsToolbarRect.height) + "px";
  4477. document.documentElement.setAttribute("tabsintitlebar", "true");
  4478. if (!this._draghandle) {
  4479. let tmp = {};
  4480. Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
  4481. this._draghandle = new tmp.WindowDraggingElement(tabsToolbar, window);
  4482. this._draghandle.mouseDownCheck = function () {
  4483. return !this._dragBindingAlive && TabsInTitlebar.enabled;
  4484. };
  4485. }
  4486. } else {
  4487. document.documentElement.removeAttribute("tabsintitlebar");
  4488. titlebar.style.marginBottom = "";
  4489. }
  4490. },
  4491. _sizePlaceholder: function (type, width) {
  4492. Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
  4493. function (node) { node.width = width; });
  4494. },
  4495. #endif
  4496. uninit: function () {
  4497. #ifdef CAN_DRAW_IN_TITLEBAR
  4498. this._initialized = false;
  4499. Services.prefs.removeObserver(this._prefName, this);
  4500. #endif
  4501. }
  4502. };
  4503. #ifdef MENUBAR_CAN_AUTOHIDE
  4504. function updateAppButtonDisplay() {
  4505. var displayAppButton =
  4506. !gInPrintPreviewMode &&
  4507. window.menubar.visible &&
  4508. document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
  4509. #ifdef CAN_DRAW_IN_TITLEBAR
  4510. document.getElementById("titlebar").hidden = !displayAppButton;
  4511. if (displayAppButton)
  4512. document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
  4513. else
  4514. document.documentElement.removeAttribute("chromemargin");
  4515. TabsInTitlebar.allowedBy("drawing-in-titlebar", displayAppButton);
  4516. #else
  4517. document.getElementById("appmenu-toolbar-button").hidden =
  4518. !displayAppButton;
  4519. #endif
  4520. }
  4521. #endif
  4522. #ifdef CAN_DRAW_IN_TITLEBAR
  4523. function onTitlebarMaxClick() {
  4524. if (window.windowState == window.STATE_MAXIMIZED)
  4525. window.restore();
  4526. else
  4527. window.maximize();
  4528. }
  4529. #endif
  4530. function displaySecurityInfo()
  4531. {
  4532. BrowserPageInfo(null, "securityTab");
  4533. }
  4534. /**
  4535. * Opens or closes the sidebar identified by commandID.
  4536. *
  4537. * @param commandID a string identifying the sidebar to toggle; see the
  4538. * note below. (Optional if a sidebar is already open.)
  4539. * @param forceOpen boolean indicating whether the sidebar should be
  4540. * opened regardless of its current state (optional).
  4541. * @note
  4542. * We expect to find a xul:broadcaster element with the specified ID.
  4543. * The following attributes on that element may be used and/or modified:
  4544. * - id (required) the string to match commandID. The convention
  4545. * is to use this naming scheme: 'view<sidebar-name>Sidebar'.
  4546. * - sidebarurl (required) specifies the URL to load in this sidebar.
  4547. * - sidebartitle or label (in that order) specify the title to
  4548. * display on the sidebar.
  4549. * - checked indicates whether the sidebar is currently displayed.
  4550. * Note that toggleSidebar updates this attribute when
  4551. * it changes the sidebar's visibility.
  4552. * - group this attribute must be set to "sidebar".
  4553. */
  4554. function toggleSidebar(commandID, forceOpen) {
  4555. var sidebarBox = document.getElementById("sidebar-box");
  4556. if (!commandID)
  4557. commandID = sidebarBox.getAttribute("sidebarcommand");
  4558. var sidebarBroadcaster = document.getElementById(commandID);
  4559. var sidebar = document.getElementById("sidebar"); // xul:browser
  4560. var sidebarTitle = document.getElementById("sidebar-title");
  4561. var sidebarSplitter = document.getElementById("sidebar-splitter");
  4562. if (sidebarBroadcaster.getAttribute("checked") == "true") {
  4563. if (!forceOpen) {
  4564. sidebarBroadcaster.removeAttribute("checked");
  4565. sidebarBox.setAttribute("sidebarcommand", "");
  4566. sidebarTitle.value = "";
  4567. sidebar.setAttribute("src", "about:blank");
  4568. sidebarBox.hidden = true;
  4569. sidebarSplitter.hidden = true;
  4570. content.focus();
  4571. } else {
  4572. fireSidebarFocusedEvent();
  4573. }
  4574. return;
  4575. }
  4576. // now we need to show the specified sidebar
  4577. // ..but first update the 'checked' state of all sidebar broadcasters
  4578. var broadcasters = document.getElementsByAttribute("group", "sidebar");
  4579. for (var i = 0; i < broadcasters.length; ++i) {
  4580. // skip elements that observe sidebar broadcasters and random
  4581. // other elements
  4582. if (broadcasters[i].localName != "broadcaster")
  4583. continue;
  4584. if (broadcasters[i] != sidebarBroadcaster)
  4585. broadcasters[i].removeAttribute("checked");
  4586. else
  4587. sidebarBroadcaster.setAttribute("checked", "true");
  4588. }
  4589. sidebarBox.hidden = false;
  4590. sidebarSplitter.hidden = false;
  4591. var url = sidebarBroadcaster.getAttribute("sidebarurl");
  4592. var title = sidebarBroadcaster.getAttribute("sidebartitle");
  4593. if (!title)
  4594. title = sidebarBroadcaster.getAttribute("label");
  4595. sidebar.setAttribute("src", url); // kick off async load
  4596. sidebarBox.setAttribute("sidebarcommand", sidebarBroadcaster.id);
  4597. sidebarTitle.value = title;
  4598. // We set this attribute here in addition to setting it on the <browser>
  4599. // element itself, because the code in BrowserShutdown persists this
  4600. // attribute, not the "src" of the <browser id="sidebar">. The reason it
  4601. // does that is that we want to delay sidebar load a bit when a browser
  4602. // window opens. See delayedStartup().
  4603. sidebarBox.setAttribute("src", url);
  4604. if (sidebar.contentDocument.location.href != url)
  4605. sidebar.addEventListener("load", sidebarOnLoad, true);
  4606. else // older code handled this case, so we do it too
  4607. fireSidebarFocusedEvent();
  4608. }
  4609. function sidebarOnLoad(event) {
  4610. var sidebar = document.getElementById("sidebar");
  4611. sidebar.removeEventListener("load", sidebarOnLoad, true);
  4612. // We're handling the 'load' event before it bubbles up to the usual
  4613. // (non-capturing) event handlers. Let it bubble up before firing the
  4614. // SidebarFocused event.
  4615. setTimeout(fireSidebarFocusedEvent, 0);
  4616. }
  4617. /**
  4618. * Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
  4619. * a chance to adjust focus as needed. An additional event is needed, because
  4620. * we don't want to focus the sidebar when it's opened on startup or in a new
  4621. * window, only when the user opens the sidebar.
  4622. */
  4623. function fireSidebarFocusedEvent() {
  4624. var sidebar = document.getElementById("sidebar");
  4625. var event = document.createEvent("Events");
  4626. event.initEvent("SidebarFocused", true, false);
  4627. sidebar.contentWindow.dispatchEvent(event);
  4628. }
  4629. var gHomeButton = {
  4630. prefDomain: "browser.startup.homepage",
  4631. observe: function (aSubject, aTopic, aPrefName)
  4632. {
  4633. if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
  4634. return;
  4635. this.updateTooltip();
  4636. },
  4637. updateTooltip: function (homeButton)
  4638. {
  4639. if (!homeButton)
  4640. homeButton = document.getElementById("home-button");
  4641. if (homeButton) {
  4642. var homePage = this.getHomePage();
  4643. homePage = homePage.replace(/\|/g,', ');
  4644. if (homePage.toLowerCase() == "about:home")
  4645. homeButton.setAttribute("tooltiptext", homeButton.getAttribute("aboutHomeOverrideTooltip"));
  4646. else
  4647. homeButton.setAttribute("tooltiptext", homePage);
  4648. }
  4649. },
  4650. getHomePage: function ()
  4651. {
  4652. var url;
  4653. try {
  4654. url = gPrefService.getComplexValue(this.prefDomain,
  4655. Components.interfaces.nsIPrefLocalizedString).data;
  4656. } catch (e) {
  4657. }
  4658. // use this if we can't find the pref
  4659. if (!url) {
  4660. var SBS = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
  4661. var configBundle = SBS.createBundle("chrome://branding/locale/browserconfig.properties");
  4662. url = configBundle.GetStringFromName(this.prefDomain);
  4663. }
  4664. return url;
  4665. },
  4666. updatePersonalToolbarStyle: function (homeButton)
  4667. {
  4668. if (!homeButton)
  4669. homeButton = document.getElementById("home-button");
  4670. if (homeButton)
  4671. homeButton.className = homeButton.parentNode.id == "PersonalToolbar"
  4672. || homeButton.parentNode.parentNode.id == "PersonalToolbar" ?
  4673. homeButton.className.replace("toolbarbutton-1", "bookmark-item") :
  4674. homeButton.className.replace("bookmark-item", "toolbarbutton-1");
  4675. }
  4676. };
  4677. /**
  4678. * Gets the selected text in the active browser. Leading and trailing
  4679. * whitespace is removed, and consecutive whitespace is replaced by a single
  4680. * space. A maximum of 150 characters will be returned, regardless of the value
  4681. * of aCharLen.
  4682. *
  4683. * @param aCharLen
  4684. * The maximum number of characters to return.
  4685. */
  4686. function getBrowserSelection(aCharLen) {
  4687. // selections of more than 150 characters aren't useful
  4688. const kMaxSelectionLen = 150;
  4689. const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
  4690. var focusedWindow = document.commandDispatcher.focusedWindow;
  4691. var selection = focusedWindow.getSelection().toString();
  4692. if (selection) {
  4693. if (selection.length > charLen) {
  4694. // only use the first charLen important chars. see bug 221361
  4695. var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
  4696. pattern.test(selection);
  4697. selection = RegExp.lastMatch;
  4698. }
  4699. selection = selection.replace(/^\s+/, "")
  4700. .replace(/\s+$/, "")
  4701. .replace(/\s+/g, " ");
  4702. if (selection.length > charLen)
  4703. selection = selection.substr(0, charLen);
  4704. }
  4705. return selection;
  4706. }
  4707. var gWebPanelURI;
  4708. function openWebPanel(aTitle, aURI)
  4709. {
  4710. // Ensure that the web panels sidebar is open.
  4711. toggleSidebar('viewWebPanelsSidebar', true);
  4712. // Set the title of the panel.
  4713. document.getElementById("sidebar-title").value = aTitle;
  4714. // Tell the Web Panels sidebar to load the bookmark.
  4715. var sidebar = document.getElementById("sidebar");
  4716. if (sidebar.docShell && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser')) {
  4717. sidebar.contentWindow.loadWebPanel(aURI);
  4718. if (gWebPanelURI) {
  4719. gWebPanelURI = "";
  4720. sidebar.removeEventListener("load", asyncOpenWebPanel, true);
  4721. }
  4722. }
  4723. else {
  4724. // The panel is still being constructed. Attach an onload handler.
  4725. if (!gWebPanelURI)
  4726. sidebar.addEventListener("load", asyncOpenWebPanel, true);
  4727. gWebPanelURI = aURI;
  4728. }
  4729. }
  4730. function asyncOpenWebPanel(event)
  4731. {
  4732. var sidebar = document.getElementById("sidebar");
  4733. if (gWebPanelURI && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser'))
  4734. sidebar.contentWindow.loadWebPanel(gWebPanelURI);
  4735. gWebPanelURI = "";
  4736. sidebar.removeEventListener("load", asyncOpenWebPanel, true);
  4737. }
  4738. /*
  4739. * - [ Dependencies ] ---------------------------------------------------------
  4740. * utilityOverlay.js:
  4741. * - gatherTextUnder
  4742. */
  4743. /**
  4744. * Extracts linkNode and href for the current click target.
  4745. *
  4746. * @param event
  4747. * The click event.
  4748. * @return [href, linkNode].
  4749. *
  4750. * @note linkNode will be null if the click wasn't on an anchor
  4751. * element (or XLink).
  4752. */
  4753. function hrefAndLinkNodeForClickEvent(event)
  4754. {
  4755. function isHTMLLink(aNode)
  4756. {
  4757. // Be consistent with what nsContextMenu.js does.
  4758. return ((aNode instanceof HTMLAnchorElement && aNode.href) ||
  4759. (aNode instanceof HTMLAreaElement && aNode.href) ||
  4760. aNode instanceof HTMLLinkElement);
  4761. }
  4762. let node = event.target;
  4763. while (node && !isHTMLLink(node)) {
  4764. node = node.parentNode;
  4765. }
  4766. if (node)
  4767. return [node.href, node];
  4768. // If there is no linkNode, try simple XLink.
  4769. let href, baseURI;
  4770. node = event.target;
  4771. while (node && !href) {
  4772. if (node.nodeType == Node.ELEMENT_NODE) {
  4773. href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
  4774. if (href)
  4775. baseURI = node.baseURI;
  4776. }
  4777. node = node.parentNode;
  4778. }
  4779. // In case of XLink, we don't return the node we got href from since
  4780. // callers expect <a>-like elements.
  4781. return [href ? makeURLAbsolute(baseURI, href) : null, null];
  4782. }
  4783. /**
  4784. * Called whenever the user clicks in the content area.
  4785. *
  4786. * @param event
  4787. * The click event.
  4788. * @param isPanelClick
  4789. * Whether the event comes from a web panel.
  4790. * @note default event is prevented if the click is handled.
  4791. */
  4792. function contentAreaClick(event, isPanelClick)
  4793. {
  4794. if (!event.isTrusted || event.getPreventDefault() || event.button == 2)
  4795. return true;
  4796. let [href, linkNode] = hrefAndLinkNodeForClickEvent(event);
  4797. if (!href) {
  4798. // Not a link, handle middle mouse navigation.
  4799. if (event.button == 1 &&
  4800. gPrefService.getBoolPref("middlemouse.contentLoadURL") &&
  4801. !gPrefService.getBoolPref("general.autoScroll")) {
  4802. middleMousePaste(event);
  4803. event.preventDefault();
  4804. }
  4805. return true;
  4806. }
  4807. // This code only applies if we have a linkNode (i.e. clicks on real anchor
  4808. // elements, as opposed to XLink).
  4809. if (linkNode && event.button == 0 &&
  4810. !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
  4811. // A Web panel's links should target the main content area. Do this
  4812. // if no modifier keys are down and if there's no target or the target
  4813. // equals _main (the IE convention) or _content (the Mozilla convention).
  4814. let target = linkNode.target;
  4815. let mainTarget = !target || target == "_content" || target == "_main";
  4816. if (isPanelClick && mainTarget) {
  4817. // javascript and data links should be executed in the current browser.
  4818. if (linkNode.getAttribute("onclick") ||
  4819. href.substr(0, 11) === "javascript:" ||
  4820. href.substr(0, 5) === "data:")
  4821. return true;
  4822. try {
  4823. urlSecurityCheck(href, linkNode.ownerDocument.nodePrincipal);
  4824. }
  4825. catch(ex) {
  4826. // Prevent loading unsecure destinations.
  4827. event.preventDefault();
  4828. return true;
  4829. }
  4830. let postData = {};
  4831. let url = getShortcutOrURI(href, postData);
  4832. if (!url)
  4833. return true;
  4834. loadURI(url, null, postData.value, false);
  4835. event.preventDefault();
  4836. return true;
  4837. }
  4838. if (linkNode.getAttribute("rel") == "sidebar") {
  4839. // This is the Opera convention for a special link that, when clicked,
  4840. // allows to add a sidebar panel. The link's title attribute contains
  4841. // the title that should be used for the sidebar panel.
  4842. PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(href),
  4843. linkNode.getAttribute("title"),
  4844. null, null, true, true);
  4845. event.preventDefault();
  4846. return true;
  4847. }
  4848. }
  4849. handleLinkClick(event, href, linkNode);
  4850. // Mark the page as a user followed link. This is done so that history can
  4851. // distinguish automatic embed visits from user activated ones. For example
  4852. // pages loaded in frames are embed visits and lost with the session, while
  4853. // visits across frames should be preserved.
  4854. try {
  4855. PlacesUIUtils.markPageAsFollowedLink(href);
  4856. } catch (ex) { /* Skip invalid URIs. */ }
  4857. return true;
  4858. }
  4859. /**
  4860. * Handles clicks on links.
  4861. *
  4862. * @return true if the click event was handled, false otherwise.
  4863. */
  4864. function handleLinkClick(event, href, linkNode) {
  4865. if (event.button == 2) // right click
  4866. return false;
  4867. var where = whereToOpenLink(event);
  4868. if (where == "current")
  4869. return false;
  4870. var doc = event.target.ownerDocument;
  4871. if (where == "save") {
  4872. saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
  4873. true, doc.documentURIObject);
  4874. event.preventDefault();
  4875. return true;
  4876. }
  4877. urlSecurityCheck(href, doc.nodePrincipal);
  4878. openLinkIn(href, where, { referrerURI: doc.documentURIObject,
  4879. charset: doc.characterSet });
  4880. event.preventDefault();
  4881. return true;
  4882. }
  4883. function middleMousePaste(event) {
  4884. let clipboard = readFromClipboard();
  4885. if (!clipboard)
  4886. return;
  4887. // Strip embedded newlines and surrounding whitespace, to match the URL
  4888. // bar's behavior (stripsurroundingwhitespace)
  4889. clipboard = clipboard.replace(/\s*\n\s*/g, "");
  4890. let url = getShortcutOrURI(clipboard);
  4891. try {
  4892. makeURI(url);
  4893. } catch (ex) {
  4894. // Not a valid URI.
  4895. return;
  4896. }
  4897. try {
  4898. addToUrlbarHistory(url);
  4899. } catch (ex) {
  4900. // Things may go wrong when adding url to session history,
  4901. // but don't let that interfere with the loading of the url.
  4902. Cu.reportError(ex);
  4903. }
  4904. openUILink(url,
  4905. event,
  4906. true /* ignore the fact this is a middle click */);
  4907. event.stopPropagation();
  4908. }
  4909. function handleDroppedLink(event, url, name)
  4910. {
  4911. let postData = { };
  4912. let uri = getShortcutOrURI(url, postData);
  4913. if (uri)
  4914. loadURI(uri, null, postData.value, false);
  4915. // Keep the event from being handled by the dragDrop listeners
  4916. // built-in to gecko if they happen to be above us.
  4917. event.preventDefault();
  4918. };
  4919. function MultiplexHandler(event)
  4920. { try {
  4921. var node = event.target;
  4922. var name = node.getAttribute('name');
  4923. if (name == 'detectorGroup') {
  4924. SetForcedDetector(true);
  4925. SelectDetector(event, false);
  4926. } else if (name == 'charsetGroup') {
  4927. var charset = node.getAttribute('id');
  4928. charset = charset.substring('charset.'.length, charset.length)
  4929. SetForcedCharset(charset);
  4930. } else if (name == 'charsetCustomize') {
  4931. //do nothing - please remove this else statement, once the charset prefs moves to the pref window
  4932. } else {
  4933. SetForcedCharset(node.getAttribute('id'));
  4934. }
  4935. } catch(ex) { alert(ex); }
  4936. }
  4937. function SelectDetector(event, doReload)
  4938. {
  4939. var uri = event.target.getAttribute("id");
  4940. var prefvalue = uri.substring('chardet.'.length, uri.length);
  4941. if ("off" == prefvalue) { // "off" is special value to turn off the detectors
  4942. prefvalue = "";
  4943. }
  4944. try {
  4945. var str = Cc["@mozilla.org/supports-string;1"].
  4946. createInstance(Ci.nsISupportsString);
  4947. str.data = prefvalue;
  4948. gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
  4949. if (doReload)
  4950. window.content.location.reload();
  4951. }
  4952. catch (ex) {
  4953. dump("Failed to set the intl.charset.detector preference.\n");
  4954. }
  4955. }
  4956. function SetForcedDetector(doReload)
  4957. {
  4958. BrowserSetForcedDetector(doReload);
  4959. }
  4960. function SetForcedCharset(charset)
  4961. {
  4962. BrowserSetForcedCharacterSet(charset);
  4963. }
  4964. function BrowserSetForcedCharacterSet(aCharset)
  4965. {
  4966. var docCharset = gBrowser.docShell.QueryInterface(Ci.nsIDocCharset);
  4967. docCharset.charset = aCharset;
  4968. // Save the forced character-set
  4969. PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, aCharset);
  4970. BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
  4971. }
  4972. function BrowserSetForcedDetector(doReload)
  4973. {
  4974. gBrowser.documentCharsetInfo.forcedDetector = true;
  4975. if (doReload)
  4976. BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
  4977. }
  4978. function charsetMenuGetElement(parent, id) {
  4979. return parent.getElementsByAttribute("id", id)[0];
  4980. }
  4981. function UpdateCurrentCharset(target) {
  4982. // extract the charset from DOM
  4983. var wnd = document.commandDispatcher.focusedWindow;
  4984. if ((window == wnd) || (wnd == null)) wnd = window.content;
  4985. // Uncheck previous item
  4986. if (gPrevCharset) {
  4987. var pref_item = charsetMenuGetElement(target, "charset." + gPrevCharset);
  4988. if (pref_item)
  4989. pref_item.setAttribute('checked', 'false');
  4990. }
  4991. var menuitem = charsetMenuGetElement(target, "charset." + wnd.document.characterSet);
  4992. if (menuitem) {
  4993. menuitem.setAttribute('checked', 'true');
  4994. }
  4995. }
  4996. function UpdateCharsetDetector(target) {
  4997. var prefvalue;
  4998. try {
  4999. prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
  5000. }
  5001. catch (ex) {}
  5002. if (!prefvalue)
  5003. prefvalue = "off";
  5004. var menuitem = charsetMenuGetElement(target, "chardet." + prefvalue);
  5005. if (menuitem)
  5006. menuitem.setAttribute("checked", "true");
  5007. }
  5008. function UpdateMenus(event) {
  5009. UpdateCurrentCharset(event.target);
  5010. UpdateCharsetDetector(event.target);
  5011. }
  5012. function CreateMenu(node) {
  5013. Services.obs.notifyObservers(null, "charsetmenu-selected", node);
  5014. }
  5015. function charsetLoadListener(event) {
  5016. var charset = window.content.document.characterSet;
  5017. if (charset.length > 0 && (charset != gLastBrowserCharset)) {
  5018. if (!gCharsetMenu)
  5019. gCharsetMenu = Cc['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService(Ci.nsICurrentCharsetListener);
  5020. gCharsetMenu.SetCurrentCharset(charset);
  5021. gPrevCharset = gLastBrowserCharset;
  5022. gLastBrowserCharset = charset;
  5023. }
  5024. }
  5025. /* Begin Page Style Functions */
  5026. function getAllStyleSheets(frameset) {
  5027. var styleSheetsArray = Array.slice(frameset.document.styleSheets);
  5028. for (let i = 0; i < frameset.frames.length; i++) {
  5029. let frameSheets = getAllStyleSheets(frameset.frames[i]);
  5030. styleSheetsArray = styleSheetsArray.concat(frameSheets);
  5031. }
  5032. return styleSheetsArray;
  5033. }
  5034. function stylesheetFillPopup(menuPopup) {
  5035. var noStyle = menuPopup.firstChild;
  5036. var persistentOnly = noStyle.nextSibling;
  5037. var sep = persistentOnly.nextSibling;
  5038. while (sep.nextSibling)
  5039. menuPopup.removeChild(sep.nextSibling);
  5040. var styleSheets = getAllStyleSheets(window.content);
  5041. var currentStyleSheets = {};
  5042. var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
  5043. var haveAltSheets = false;
  5044. var altStyleSelected = false;
  5045. for (let i = 0; i < styleSheets.length; ++i) {
  5046. let currentStyleSheet = styleSheets[i];
  5047. if (!currentStyleSheet.title)
  5048. continue;
  5049. // Skip any stylesheets that don't match the screen media type.
  5050. if (currentStyleSheet.media.length > 0) {
  5051. let media = currentStyleSheet.media.mediaText.split(", ");
  5052. if (media.indexOf("screen") == -1 &&
  5053. media.indexOf("all") == -1)
  5054. continue;
  5055. }
  5056. if (!currentStyleSheet.disabled)
  5057. altStyleSelected = true;
  5058. haveAltSheets = true;
  5059. let lastWithSameTitle = null;
  5060. if (currentStyleSheet.title in currentStyleSheets)
  5061. lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
  5062. if (!lastWithSameTitle) {
  5063. let menuItem = document.createElement("menuitem");
  5064. menuItem.setAttribute("type", "radio");
  5065. menuItem.setAttribute("label", currentStyleSheet.title);
  5066. menuItem.setAttribute("data", currentStyleSheet.title);
  5067. menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
  5068. menuPopup.appendChild(menuItem);
  5069. currentStyleSheets[currentStyleSheet.title] = menuItem;
  5070. } else if (currentStyleSheet.disabled) {
  5071. lastWithSameTitle.removeAttribute("checked");
  5072. }
  5073. }
  5074. noStyle.setAttribute("checked", styleDisabled);
  5075. persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
  5076. persistentOnly.hidden = (window.content.document.preferredStyleSheetSet) ? haveAltSheets : false;
  5077. sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
  5078. return true;
  5079. }
  5080. function stylesheetInFrame(frame, title) {
  5081. return Array.some(frame.document.styleSheets,
  5082. function (stylesheet) stylesheet.title == title);
  5083. }
  5084. function stylesheetSwitchFrame(frame, title) {
  5085. var docStyleSheets = frame.document.styleSheets;
  5086. for (let i = 0; i < docStyleSheets.length; ++i) {
  5087. let docStyleSheet = docStyleSheets[i];
  5088. if (title == "_nostyle")
  5089. docStyleSheet.disabled = true;
  5090. else if (docStyleSheet.title)
  5091. docStyleSheet.disabled = (docStyleSheet.title != title);
  5092. else if (docStyleSheet.disabled)
  5093. docStyleSheet.disabled = false;
  5094. }
  5095. }
  5096. function stylesheetSwitchAll(frameset, title) {
  5097. if (!title || title == "_nostyle" || stylesheetInFrame(frameset, title))
  5098. stylesheetSwitchFrame(frameset, title);
  5099. for (let i = 0; i < frameset.frames.length; i++)
  5100. stylesheetSwitchAll(frameset.frames[i], title);
  5101. }
  5102. function setStyleDisabled(disabled) {
  5103. getMarkupDocumentViewer().authorStyleDisabled = disabled;
  5104. }
  5105. /* End of the Page Style functions */
  5106. var BrowserOffline = {
  5107. _inited: false,
  5108. /////////////////////////////////////////////////////////////////////////////
  5109. // BrowserOffline Public Methods
  5110. init: function ()
  5111. {
  5112. if (!this._uiElement)
  5113. this._uiElement = document.getElementById("workOfflineMenuitemState");
  5114. Services.obs.addObserver(this, "network:offline-status-changed", false);
  5115. this._updateOfflineUI(Services.io.offline);
  5116. this._inited = true;
  5117. },
  5118. uninit: function ()
  5119. {
  5120. if (this._inited) {
  5121. Services.obs.removeObserver(this, "network:offline-status-changed");
  5122. }
  5123. },
  5124. toggleOfflineStatus: function ()
  5125. {
  5126. var ioService = Services.io;
  5127. // Stop automatic management of the offline status
  5128. try {
  5129. ioService.manageOfflineStatus = false;
  5130. } catch (ex) {
  5131. }
  5132. if (!ioService.offline && !this._canGoOffline()) {
  5133. this._updateOfflineUI(false);
  5134. return;
  5135. }
  5136. ioService.offline = !ioService.offline;
  5137. },
  5138. /////////////////////////////////////////////////////////////////////////////
  5139. // nsIObserver
  5140. observe: function (aSubject, aTopic, aState)
  5141. {
  5142. if (aTopic != "network:offline-status-changed")
  5143. return;
  5144. this._updateOfflineUI(aState == "offline");
  5145. },
  5146. /////////////////////////////////////////////////////////////////////////////
  5147. // BrowserOffline Implementation Methods
  5148. _canGoOffline: function ()
  5149. {
  5150. try {
  5151. var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
  5152. Services.obs.notifyObservers(cancelGoOffline, "offline-requested", null);
  5153. // Something aborted the quit process.
  5154. if (cancelGoOffline.data)
  5155. return false;
  5156. }
  5157. catch (ex) {
  5158. }
  5159. return true;
  5160. },
  5161. _uiElement: null,
  5162. _updateOfflineUI: function (aOffline)
  5163. {
  5164. var offlineLocked = gPrefService.prefIsLocked("network.online");
  5165. if (offlineLocked)
  5166. this._uiElement.setAttribute("disabled", "true");
  5167. this._uiElement.setAttribute("checked", aOffline);
  5168. }
  5169. };
  5170. var OfflineApps = {
  5171. /////////////////////////////////////////////////////////////////////////////
  5172. // OfflineApps Public Methods
  5173. init: function ()
  5174. {
  5175. Services.obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
  5176. Services.obs.addObserver(this, "offline-cache-update-completed", false);
  5177. },
  5178. uninit: function ()
  5179. {
  5180. Services.obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
  5181. Services.obs.removeObserver(this, "offline-cache-update-completed");
  5182. },
  5183. handleEvent: function(event) {
  5184. if (event.type == "MozApplicationManifest") {
  5185. this.offlineAppRequested(event.originalTarget.defaultView);
  5186. }
  5187. },
  5188. /////////////////////////////////////////////////////////////////////////////
  5189. // OfflineApps Implementation Methods
  5190. // XXX: _getBrowserWindowForContentWindow and _getBrowserForContentWindow
  5191. // were taken from browser/components/feeds/src/WebContentConverter.
  5192. _getBrowserWindowForContentWindow: function(aContentWindow) {
  5193. return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
  5194. .getInterface(Ci.nsIWebNavigation)
  5195. .QueryInterface(Ci.nsIDocShellTreeItem)
  5196. .rootTreeItem
  5197. .QueryInterface(Ci.nsIInterfaceRequestor)
  5198. .getInterface(Ci.nsIDOMWindow)
  5199. .wrappedJSObject;
  5200. },
  5201. _getBrowserForContentWindow: function(aBrowserWindow, aContentWindow) {
  5202. // This depends on pseudo APIs of browser.js and tabbrowser.xml
  5203. aContentWindow = aContentWindow.top;
  5204. var browsers = aBrowserWindow.gBrowser.browsers;
  5205. for (var i = 0; i < browsers.length; ++i) {
  5206. if (browsers[i].contentWindow == aContentWindow)
  5207. return browsers[i];
  5208. }
  5209. return null;
  5210. },
  5211. _getManifestURI: function(aWindow) {
  5212. if (!aWindow.document.documentElement)
  5213. return null;
  5214. var attr = aWindow.document.documentElement.getAttribute("manifest");
  5215. if (!attr)
  5216. return null;
  5217. try {
  5218. var contentURI = makeURI(aWindow.location.href, null, null);
  5219. return makeURI(attr, aWindow.document.characterSet, contentURI);
  5220. } catch (e) {
  5221. return null;
  5222. }
  5223. },
  5224. // A cache update isn't tied to a specific window. Try to find
  5225. // the best browser in which to warn the user about space usage
  5226. _getBrowserForCacheUpdate: function(aCacheUpdate) {
  5227. // Prefer the current browser
  5228. var uri = this._getManifestURI(content);
  5229. if (uri && uri.equals(aCacheUpdate.manifestURI)) {
  5230. return gBrowser.selectedBrowser;
  5231. }
  5232. var browsers = gBrowser.browsers;
  5233. for (var i = 0; i < browsers.length; ++i) {
  5234. uri = this._getManifestURI(browsers[i].contentWindow);
  5235. if (uri && uri.equals(aCacheUpdate.manifestURI)) {
  5236. return browsers[i];
  5237. }
  5238. }
  5239. return null;
  5240. },
  5241. _warnUsage: function(aBrowser, aURI) {
  5242. if (!aBrowser)
  5243. return;
  5244. var notificationBox = gBrowser.getNotificationBox(aBrowser);
  5245. var notification = notificationBox.getNotificationWithValue("offline-app-usage");
  5246. if (!notification) {
  5247. var buttons = [{
  5248. label: gNavigatorBundle.getString("offlineApps.manageUsage"),
  5249. accessKey: gNavigatorBundle.getString("offlineApps.manageUsageAccessKey"),
  5250. callback: OfflineApps.manage
  5251. }];
  5252. var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
  5253. const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
  5254. var message = gNavigatorBundle.getFormattedString("offlineApps.usage",
  5255. [ aURI.host,
  5256. warnQuota / 1024 ]);
  5257. notificationBox.appendNotification(message, "offline-app-usage",
  5258. "chrome://browser/skin/Info.png",
  5259. priority, buttons);
  5260. }
  5261. // Now that we've warned once, prevent the warning from showing up
  5262. // again.
  5263. Services.perms.add(aURI, "offline-app",
  5264. Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
  5265. },
  5266. // XXX: duplicated in preferences/advanced.js
  5267. _getOfflineAppUsage: function (host, groups)
  5268. {
  5269. var cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
  5270. getService(Ci.nsIApplicationCacheService);
  5271. if (!groups)
  5272. groups = cacheService.getGroups();
  5273. var usage = 0;
  5274. for (var i = 0; i < groups.length; i++) {
  5275. var uri = Services.io.newURI(groups[i], null, null);
  5276. if (uri.asciiHost == host) {
  5277. var cache = cacheService.getActiveCache(groups[i]);
  5278. usage += cache.usage;
  5279. }
  5280. }
  5281. var storageManager = Cc["@mozilla.org/dom/storagemanager;1"].
  5282. getService(Ci.nsIDOMStorageManager);
  5283. usage += storageManager.getUsage(host);
  5284. return usage;
  5285. },
  5286. _checkUsage: function(aURI) {
  5287. // if the user has already allowed excessive usage, don't bother checking
  5288. if (Services.perms.testExactPermission(aURI, "offline-app") !=
  5289. Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
  5290. var usage = this._getOfflineAppUsage(aURI.asciiHost);
  5291. var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
  5292. if (usage >= warnQuota * 1024) {
  5293. return true;
  5294. }
  5295. }
  5296. return false;
  5297. },
  5298. offlineAppRequested: function(aContentWindow) {
  5299. if (!gPrefService.getBoolPref("browser.offline-apps.notify")) {
  5300. return;
  5301. }
  5302. var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
  5303. var browser = this._getBrowserForContentWindow(browserWindow,
  5304. aContentWindow);
  5305. var currentURI = aContentWindow.document.documentURIObject;
  5306. // don't bother showing UI if the user has already made a decision
  5307. if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
  5308. return;
  5309. try {
  5310. if (gPrefService.getBoolPref("offline-apps.allow_by_default")) {
  5311. // all pages can use offline capabilities, no need to ask the user
  5312. return;
  5313. }
  5314. } catch(e) {
  5315. // this pref isn't set by default, ignore failures
  5316. }
  5317. var host = currentURI.asciiHost;
  5318. var notificationBox = gBrowser.getNotificationBox(browser);
  5319. var notificationID = "offline-app-requested-" + host;
  5320. var notification = notificationBox.getNotificationWithValue(notificationID);
  5321. if (notification) {
  5322. notification.documents.push(aContentWindow.document);
  5323. } else {
  5324. var buttons = [{
  5325. label: gNavigatorBundle.getString("offlineApps.allow"),
  5326. accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
  5327. callback: function() {
  5328. for (var i = 0; i < notification.documents.length; i++) {
  5329. OfflineApps.allowSite(notification.documents[i]);
  5330. }
  5331. }
  5332. },{
  5333. label: gNavigatorBundle.getString("offlineApps.never"),
  5334. accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
  5335. callback: function() {
  5336. for (var i = 0; i < notification.documents.length; i++) {
  5337. OfflineApps.disallowSite(notification.documents[i]);
  5338. }
  5339. }
  5340. },{
  5341. label: gNavigatorBundle.getString("offlineApps.notNow"),
  5342. accessKey: gNavigatorBundle.getString("offlineApps.notNowAccessKey"),
  5343. callback: function() { /* noop */ }
  5344. }];
  5345. const priority = notificationBox.PRIORITY_INFO_LOW;
  5346. var message = gNavigatorBundle.getFormattedString("offlineApps.available",
  5347. [ host ]);
  5348. notification =
  5349. notificationBox.appendNotification(message, notificationID,
  5350. "chrome://browser/skin/Info.png",
  5351. priority, buttons);
  5352. notification.documents = [ aContentWindow.document ];
  5353. }
  5354. },
  5355. allowSite: function(aDocument) {
  5356. Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION);
  5357. // When a site is enabled while loading, manifest resources will
  5358. // start fetching immediately. This one time we need to do it
  5359. // ourselves.
  5360. this._startFetching(aDocument);
  5361. },
  5362. disallowSite: function(aDocument) {
  5363. Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION);
  5364. },
  5365. manage: function() {
  5366. openAdvancedPreferences("networkTab");
  5367. },
  5368. _startFetching: function(aDocument) {
  5369. if (!aDocument.documentElement)
  5370. return;
  5371. var manifest = aDocument.documentElement.getAttribute("manifest");
  5372. if (!manifest)
  5373. return;
  5374. var manifestURI = makeURI(manifest, aDocument.characterSet,
  5375. aDocument.documentURIObject);
  5376. var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
  5377. getService(Ci.nsIOfflineCacheUpdateService);
  5378. updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window);
  5379. },
  5380. /////////////////////////////////////////////////////////////////////////////
  5381. // nsIObserver
  5382. observe: function (aSubject, aTopic, aState)
  5383. {
  5384. if (aTopic == "dom-storage-warn-quota-exceeded") {
  5385. if (aSubject) {
  5386. var uri = makeURI(aSubject.location.href);
  5387. if (OfflineApps._checkUsage(uri)) {
  5388. var browserWindow =
  5389. this._getBrowserWindowForContentWindow(aSubject);
  5390. var browser = this._getBrowserForContentWindow(browserWindow,
  5391. aSubject);
  5392. OfflineApps._warnUsage(browser, uri);
  5393. }
  5394. }
  5395. } else if (aTopic == "offline-cache-update-completed") {
  5396. var cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
  5397. var uri = cacheUpdate.manifestURI;
  5398. if (OfflineApps._checkUsage(uri)) {
  5399. var browser = this._getBrowserForCacheUpdate(cacheUpdate);
  5400. if (browser) {
  5401. OfflineApps._warnUsage(browser, cacheUpdate.manifestURI);
  5402. }
  5403. }
  5404. }
  5405. }
  5406. };
  5407. var IndexedDBPromptHelper = {
  5408. _permissionsPrompt: "indexedDB-permissions-prompt",
  5409. _permissionsResponse: "indexedDB-permissions-response",
  5410. _quotaPrompt: "indexedDB-quota-prompt",
  5411. _quotaResponse: "indexedDB-quota-response",
  5412. _quotaCancel: "indexedDB-quota-cancel",
  5413. _notificationIcon: "indexedDB-notification-icon",
  5414. init:
  5415. function IndexedDBPromptHelper_init() {
  5416. Services.obs.addObserver(this, this._permissionsPrompt, false);
  5417. Services.obs.addObserver(this, this._quotaPrompt, false);
  5418. Services.obs.addObserver(this, this._quotaCancel, false);
  5419. },
  5420. uninit:
  5421. function IndexedDBPromptHelper_uninit() {
  5422. Services.obs.removeObserver(this, this._permissionsPrompt, false);
  5423. Services.obs.removeObserver(this, this._quotaPrompt, false);
  5424. Services.obs.removeObserver(this, this._quotaCancel, false);
  5425. },
  5426. observe:
  5427. function IndexedDBPromptHelper_observe(subject, topic, data) {
  5428. if (topic != this._permissionsPrompt &&
  5429. topic != this._quotaPrompt &&
  5430. topic != this._quotaCancel) {
  5431. throw new Error("Unexpected topic!");
  5432. }
  5433. var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
  5434. var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
  5435. var contentDocument = contentWindow.document;
  5436. var browserWindow =
  5437. OfflineApps._getBrowserWindowForContentWindow(contentWindow);
  5438. if (browserWindow != window) {
  5439. // Must belong to some other window.
  5440. return;
  5441. }
  5442. var browser =
  5443. OfflineApps._getBrowserForContentWindow(browserWindow, contentWindow);
  5444. var host = contentDocument.documentURIObject.asciiHost;
  5445. var message;
  5446. var responseTopic;
  5447. if (topic == this._permissionsPrompt) {
  5448. message = gNavigatorBundle.getFormattedString("offlineApps.available",
  5449. [ host ]);
  5450. responseTopic = this._permissionsResponse;
  5451. }
  5452. else if (topic == this._quotaPrompt) {
  5453. message = gNavigatorBundle.getFormattedString("indexedDB.usage",
  5454. [ host, data ]);
  5455. responseTopic = this._quotaResponse;
  5456. }
  5457. else if (topic == this._quotaCancel) {
  5458. responseTopic = this._quotaResponse;
  5459. }
  5460. const hiddenTimeoutDuration = 30000; // 30 seconds
  5461. const firstTimeoutDuration = 360000; // 5 minutes
  5462. var timeoutId;
  5463. var observer = requestor.getInterface(Ci.nsIObserver);
  5464. var mainAction = {
  5465. label: gNavigatorBundle.getString("offlineApps.allow"),
  5466. accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
  5467. callback: function() {
  5468. clearTimeout(timeoutId);
  5469. observer.observe(null, responseTopic,
  5470. Ci.nsIPermissionManager.ALLOW_ACTION);
  5471. }
  5472. };
  5473. var secondaryActions = [
  5474. {
  5475. label: gNavigatorBundle.getString("offlineApps.never"),
  5476. accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
  5477. callback: function() {
  5478. clearTimeout(timeoutId);
  5479. observer.observe(null, responseTopic,
  5480. Ci.nsIPermissionManager.DENY_ACTION);
  5481. }
  5482. }
  5483. ];
  5484. // This will be set to the result of PopupNotifications.show() below, or to
  5485. // the result of PopupNotifications.getNotification() if this is a
  5486. // quotaCancel notification.
  5487. var notification;
  5488. function timeoutNotification() {
  5489. // Remove the notification.
  5490. if (notification) {
  5491. notification.remove();
  5492. }
  5493. // Clear all of our timeout stuff. We may be called directly, not just
  5494. // when the timeout actually elapses.
  5495. clearTimeout(timeoutId);
  5496. // And tell the page that the popup timed out.
  5497. observer.observe(null, responseTopic,
  5498. Ci.nsIPermissionManager.UNKNOWN_ACTION);
  5499. }
  5500. var options = {
  5501. eventCallback: function(state) {
  5502. // Don't do anything if the timeout has not been set yet.
  5503. if (!timeoutId) {
  5504. return;
  5505. }
  5506. // If the popup is being dismissed start the short timeout.
  5507. if (state == "dismissed") {
  5508. clearTimeout(timeoutId);
  5509. timeoutId = setTimeout(timeoutNotification, hiddenTimeoutDuration);
  5510. return;
  5511. }
  5512. // If the popup is being re-shown then clear the timeout allowing
  5513. // unlimited waiting.
  5514. if (state == "shown") {
  5515. clearTimeout(timeoutId);
  5516. }
  5517. }
  5518. };
  5519. if (topic == this._quotaCancel) {
  5520. notification = PopupNotifications.getNotification(this._quotaPrompt,
  5521. browser);
  5522. timeoutNotification();
  5523. return;
  5524. }
  5525. notification = PopupNotifications.show(browser, topic, message,
  5526. this._notificationIcon, mainAction,
  5527. secondaryActions, options);
  5528. // Set the timeoutId after the popup has been created, and use the long
  5529. // timeout value. If the user doesn't notice the popup after this amount of
  5530. // time then it is most likely not visible and we want to alert the page.
  5531. timeoutId = setTimeout(timeoutNotification, firstTimeoutDuration);
  5532. }
  5533. };
  5534. function WindowIsClosing()
  5535. {
  5536. if (TabView.isVisible()) {
  5537. TabView.hide();
  5538. return false;
  5539. }
  5540. var reallyClose = closeWindow(false, warnAboutClosingWindow);
  5541. if (!reallyClose)
  5542. return false;
  5543. var numBrowsers = gBrowser.browsers.length;
  5544. for (let i = 0; reallyClose && i < numBrowsers; ++i) {
  5545. let ds = gBrowser.browsers[i].docShell;
  5546. if (ds.contentViewer && !ds.contentViewer.permitUnload())
  5547. reallyClose = false;
  5548. }
  5549. return reallyClose;
  5550. }
  5551. /**
  5552. * Checks if this is the last full *browser* window around. If it is, this will
  5553. * be communicated like quitting. Otherwise, we warn about closing multiple tabs.
  5554. * @returns true if closing can proceed, false if it got cancelled.
  5555. */
  5556. function warnAboutClosingWindow() {
  5557. // Popups aren't considered full browser windows.
  5558. if (!toolbar.visible)
  5559. return gBrowser.warnAboutClosingTabs(true);
  5560. // Figure out if there's at least one other browser window around.
  5561. let e = Services.wm.getEnumerator("navigator:browser");
  5562. while (e.hasMoreElements()) {
  5563. let win = e.getNext();
  5564. if (win != window && win.toolbar.visible)
  5565. return gBrowser.warnAboutClosingTabs(true);
  5566. }
  5567. let os = Services.obs;
  5568. let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
  5569. createInstance(Ci.nsISupportsPRBool);
  5570. os.notifyObservers(closingCanceled,
  5571. "browser-lastwindow-close-requested", null);
  5572. if (closingCanceled.data)
  5573. return false;
  5574. os.notifyObservers(null, "browser-lastwindow-close-granted", null);
  5575. #ifdef XP_MACOSX
  5576. // OS X doesn't quit the application when the last window is closed, but keeps
  5577. // the session alive. Hence don't prompt users to save tabs, but warn about
  5578. // closing multiple tabs.
  5579. return gBrowser.warnAboutClosingTabs(true);
  5580. #else
  5581. return true;
  5582. #endif
  5583. }
  5584. var MailIntegration = {
  5585. sendLinkForWindow: function (aWindow) {
  5586. this.sendMessage(aWindow.location.href,
  5587. aWindow.document.title);
  5588. },
  5589. sendMessage: function (aBody, aSubject) {
  5590. // generate a mailto url based on the url and the url's title
  5591. var mailtoUrl = "mailto:";
  5592. if (aBody) {
  5593. mailtoUrl += "?body=" + encodeURIComponent(aBody);
  5594. mailtoUrl += "&subject=" + encodeURIComponent(aSubject);
  5595. }
  5596. var uri = makeURI(mailtoUrl);
  5597. // now pass this uri to the operating system
  5598. this._launchExternalUrl(uri);
  5599. },
  5600. // a generic method which can be used to pass arbitrary urls to the operating
  5601. // system.
  5602. // aURL --> a nsIURI which represents the url to launch
  5603. _launchExternalUrl: function (aURL) {
  5604. var extProtocolSvc =
  5605. Cc["@mozilla.org/uriloader/external-protocol-service;1"]
  5606. .getService(Ci.nsIExternalProtocolService);
  5607. if (extProtocolSvc)
  5608. extProtocolSvc.loadUrl(aURL);
  5609. }
  5610. };
  5611. function BrowserOpenAddonsMgr(aView) {
  5612. if (aView) {
  5613. let emWindow;
  5614. let browserWindow;
  5615. function receivePong(aSubject, aTopic, aData) {
  5616. let browserWin = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
  5617. .getInterface(Ci.nsIWebNavigation)
  5618. .QueryInterface(Ci.nsIDocShellTreeItem)
  5619. .rootTreeItem
  5620. .QueryInterface(Ci.nsIInterfaceRequestor)
  5621. .getInterface(Ci.nsIDOMWindow);
  5622. if (!emWindow || browserWin == window /* favor the current window */) {
  5623. emWindow = aSubject;
  5624. browserWindow = browserWin;
  5625. }
  5626. }
  5627. Services.obs.addObserver(receivePong, "EM-pong", false);
  5628. Services.obs.notifyObservers(null, "EM-ping", "");
  5629. Services.obs.removeObserver(receivePong, "EM-pong");
  5630. if (emWindow) {
  5631. emWindow.loadView(aView);
  5632. browserWindow.gBrowser.selectedTab =
  5633. browserWindow.gBrowser._getTabForContentWindow(emWindow);
  5634. emWindow.focus();
  5635. return;
  5636. }
  5637. }
  5638. var newLoad = !switchToTabHavingURI("about:addons", true);
  5639. if (aView) {
  5640. // This must be a new load, else the ping/pong would have
  5641. // found the window above.
  5642. Services.obs.addObserver(function (aSubject, aTopic, aData) {
  5643. Services.obs.removeObserver(arguments.callee, aTopic);
  5644. aSubject.loadView(aView);
  5645. }, "EM-loaded", false);
  5646. }
  5647. }
  5648. function AddKeywordForSearchField() {
  5649. var node = document.popupNode;
  5650. var charset = node.ownerDocument.characterSet;
  5651. var docURI = makeURI(node.ownerDocument.URL,
  5652. charset);
  5653. var formURI = makeURI(node.form.getAttribute("action"),
  5654. charset,
  5655. docURI);
  5656. var spec = formURI.spec;
  5657. var isURLEncoded =
  5658. (node.form.method.toUpperCase() == "POST"
  5659. && (node.form.enctype == "application/x-www-form-urlencoded" ||
  5660. node.form.enctype == ""));
  5661. var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
  5662. [node.ownerDocument.title]);
  5663. var description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
  5664. var el, type;
  5665. var formData = [];
  5666. function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) {
  5667. if (aIsFormUrlEncoded)
  5668. return escape(aName + "=" + aValue);
  5669. else
  5670. return escape(aName) + "=" + escape(aValue);
  5671. }
  5672. for (var i=0; i < node.form.elements.length; i++) {
  5673. el = node.form.elements[i];
  5674. if (!el.type) // happens with fieldsets
  5675. continue;
  5676. if (el == node) {
  5677. formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
  5678. // Don't escape "%s", just append
  5679. escapeNameValuePair(el.name, "", false) + "%s");
  5680. continue;
  5681. }
  5682. type = el.type.toLowerCase();
  5683. if (((el instanceof HTMLInputElement && el.mozIsTextField(true)) ||
  5684. type == "hidden" || type == "textarea") ||
  5685. ((type == "checkbox" || type == "radio") && el.checked)) {
  5686. formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
  5687. } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
  5688. for (var j=0; j < el.options.length; j++) {
  5689. if (el.options[j].selected)
  5690. formData.push(escapeNameValuePair(el.name, el.options[j].value,
  5691. isURLEncoded));
  5692. }
  5693. }
  5694. }
  5695. var postData;
  5696. if (isURLEncoded)
  5697. postData = formData.join("&");
  5698. else
  5699. spec += "?" + formData.join("&");
  5700. PlacesUIUtils.showMinimalAddBookmarkUI(makeURI(spec), title, description, null,
  5701. null, null, "", postData, charset);
  5702. }
  5703. function SwitchDocumentDirection(aWindow) {
  5704. aWindow.document.dir = (aWindow.document.dir == "ltr" ? "rtl" : "ltr");
  5705. for (var run = 0; run < aWindow.frames.length; run++)
  5706. SwitchDocumentDirection(aWindow.frames[run]);
  5707. }
  5708. function getPluginInfo(pluginElement)
  5709. {
  5710. var tagMimetype;
  5711. var pluginsPage;
  5712. if (pluginElement instanceof HTMLAppletElement) {
  5713. tagMimetype = "application/x-java-vm";
  5714. } else {
  5715. if (pluginElement instanceof HTMLObjectElement) {
  5716. pluginsPage = pluginElement.getAttribute("codebase");
  5717. } else {
  5718. pluginsPage = pluginElement.getAttribute("pluginspage");
  5719. }
  5720. // only attempt if a pluginsPage is defined.
  5721. if (pluginsPage) {
  5722. var doc = pluginElement.ownerDocument;
  5723. var docShell = findChildShell(doc, gBrowser.docShell, null);
  5724. try {
  5725. pluginsPage = makeURI(pluginsPage, doc.characterSet, docShell.currentURI).spec;
  5726. } catch (ex) {
  5727. pluginsPage = "";
  5728. }
  5729. }
  5730. tagMimetype = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent)
  5731. .actualType;
  5732. if (tagMimetype == "") {
  5733. tagMimetype = pluginElement.type;
  5734. }
  5735. }
  5736. return {mimetype: tagMimetype, pluginsPage: pluginsPage};
  5737. }
  5738. var gPluginHandler = {
  5739. get CrashSubmit() {
  5740. delete this.CrashSubmit;
  5741. Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
  5742. return this.CrashSubmit;
  5743. },
  5744. get crashReportHelpURL() {
  5745. delete this.crashReportHelpURL;
  5746. let url = formatURL("app.support.baseURL", true);
  5747. url += "plugin-crashed";
  5748. this.crashReportHelpURL = url;
  5749. return this.crashReportHelpURL;
  5750. },
  5751. // Map the plugin's name to a filtered version more suitable for user UI.
  5752. makeNicePluginName : function (aName, aFilename) {
  5753. if (aName == "Shockwave Flash")
  5754. return "Adobe Flash";
  5755. // Clean up the plugin name by stripping off any trailing version numbers
  5756. // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
  5757. let newName = aName.replace(/\bplug-?in\b/i, "").replace(/[\s\d\.\-\_\(\)]+$/, "");
  5758. return newName;
  5759. },
  5760. isTooSmall : function (plugin, overlay) {
  5761. // Is the <object>'s size too small to hold what we want to show?
  5762. let pluginRect = plugin.getBoundingClientRect();
  5763. // XXX bug 446693. The text-shadow on the submitted-report text at
  5764. // the bottom causes scrollHeight to be larger than it should be.
  5765. let overflows = (overlay.scrollWidth > pluginRect.width) ||
  5766. (overlay.scrollHeight - 5 > pluginRect.height);
  5767. return overflows;
  5768. },
  5769. addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
  5770. // XXX just doing (callback)(arg) was giving a same-origin error. bug?
  5771. let self = this;
  5772. let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
  5773. linkNode.addEventListener("click",
  5774. function(evt) {
  5775. if (!evt.isTrusted)
  5776. return;
  5777. evt.preventDefault();
  5778. if (callbackArgs.length == 0)
  5779. callbackArgs = [ evt ];
  5780. (self[callbackName]).apply(self, callbackArgs);
  5781. },
  5782. true);
  5783. linkNode.addEventListener("keydown",
  5784. function(evt) {
  5785. if (!evt.isTrusted)
  5786. return;
  5787. if (evt.keyCode == evt.DOM_VK_RETURN) {
  5788. evt.preventDefault();
  5789. if (callbackArgs.length == 0)
  5790. callbackArgs = [ evt ];
  5791. evt.preventDefault();
  5792. (self[callbackName]).apply(self, callbackArgs);
  5793. }
  5794. },
  5795. true);
  5796. },
  5797. handleEvent : function(event) {
  5798. let self = gPluginHandler;
  5799. let plugin = event.target;
  5800. let doc = plugin.ownerDocument;
  5801. // We're expecting the target to be a plugin.
  5802. if (!(plugin instanceof Ci.nsIObjectLoadingContent))
  5803. return;
  5804. // Force a style flush, so that we ensure our binding is attached.
  5805. plugin.clientTop;
  5806. switch (event.type) {
  5807. case "PluginCrashed":
  5808. self.pluginInstanceCrashed(plugin, event);
  5809. break;
  5810. case "PluginNotFound":
  5811. // For non-object plugin tags, register a click handler to install the
  5812. // plugin. Object tags can, and often do, deal with that themselves,
  5813. // so don't stomp on the page developers toes.
  5814. if (!(plugin instanceof HTMLObjectElement)) {
  5815. // We don't yet check to see if there's actually an installer available.
  5816. let installStatus = doc.getAnonymousElementByAttribute(plugin, "class", "installStatus");
  5817. installStatus.setAttribute("status", "ready");
  5818. let iconStatus = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
  5819. iconStatus.setAttribute("status", "ready");
  5820. let installLink = doc.getAnonymousElementByAttribute(plugin, "class", "installPluginLink");
  5821. self.addLinkClickCallback(installLink, "installSinglePlugin", plugin);
  5822. }
  5823. /* FALLTHRU */
  5824. case "PluginBlocklisted":
  5825. case "PluginOutdated":
  5826. #ifdef XP_MACOSX
  5827. case "npapi-carbon-event-model-failure":
  5828. #endif
  5829. self.pluginUnavailable(plugin, event.type);
  5830. break;
  5831. case "PluginDisabled":
  5832. let manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink");
  5833. self.addLinkClickCallback(manageLink, "managePlugins");
  5834. break;
  5835. }
  5836. // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
  5837. if (event.type != "PluginCrashed") {
  5838. let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
  5839. if (self.isTooSmall(plugin, overlay))
  5840. overlay.style.visibility = "hidden";
  5841. }
  5842. },
  5843. newPluginInstalled : function(event) {
  5844. // browser elements are anonymous so we can't just use target.
  5845. var browser = event.originalTarget;
  5846. // clear the plugin list, now that at least one plugin has been installed
  5847. browser.missingPlugins = null;
  5848. var notificationBox = gBrowser.getNotificationBox(browser);
  5849. var notification = notificationBox.getNotificationWithValue("missing-plugins");
  5850. if (notification)
  5851. notificationBox.removeNotification(notification);
  5852. // reload the browser to make the new plugin show.
  5853. browser.reload();
  5854. },
  5855. // Callback for user clicking on a missing (unsupported) plugin.
  5856. installSinglePlugin: function (plugin) {
  5857. var missingPluginsArray = {};
  5858. var pluginInfo = getPluginInfo(plugin);
  5859. missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
  5860. openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
  5861. "PFSWindow", "chrome,centerscreen,resizable=yes",
  5862. {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
  5863. },
  5864. // Callback for user clicking on a disabled plugin
  5865. managePlugins: function (aEvent) {
  5866. BrowserOpenAddonsMgr("addons://list/plugin");
  5867. },
  5868. // Callback for user clicking "submit a report" link
  5869. submitReport : function(pluginDumpID, browserDumpID) {
  5870. // The crash reporter wants a DOM element it can append an IFRAME to,
  5871. // which it uses to submit a form. Let's just give it gBrowser.
  5872. this.CrashSubmit.submit(pluginDumpID);
  5873. if (browserDumpID)
  5874. this.CrashSubmit.submit(browserDumpID);
  5875. },
  5876. // Callback for user clicking a "reload page" link
  5877. reloadPage: function (browser) {
  5878. browser.reload();
  5879. },
  5880. // Callback for user clicking the help icon
  5881. openHelpPage: function () {
  5882. openHelpLink("plugin-crashed", false);
  5883. },
  5884. // event listener for missing/blocklisted/outdated/carbonFailure plugins.
  5885. pluginUnavailable: function (plugin, eventType) {
  5886. let browser = gBrowser.getBrowserForDocument(plugin.ownerDocument
  5887. .defaultView.top.document);
  5888. if (!browser.missingPlugins)
  5889. browser.missingPlugins = {};
  5890. var pluginInfo = getPluginInfo(plugin);
  5891. browser.missingPlugins[pluginInfo.mimetype] = pluginInfo;
  5892. var notificationBox = gBrowser.getNotificationBox(browser);
  5893. // Should only display one of these warnings per page.
  5894. // In order of priority, they are: outdated > missing > blocklisted
  5895. let outdatedNotification = notificationBox.getNotificationWithValue("outdated-plugins");
  5896. let blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins");
  5897. let missingNotification = notificationBox.getNotificationWithValue("missing-plugins");
  5898. function showBlocklistInfo() {
  5899. var url = formatURL("extensions.blocklist.detailsURL", true);
  5900. gBrowser.loadOneTab(url, {inBackground: false});
  5901. return true;
  5902. }
  5903. function showOutdatedPluginsInfo() {
  5904. gPrefService.setBoolPref("plugins.update.notifyUser", false);
  5905. var url = formatURL("plugins.update.url", true);
  5906. gBrowser.loadOneTab(url, {inBackground: false});
  5907. return true;
  5908. }
  5909. function showPluginsMissing() {
  5910. // get the urls of missing plugins
  5911. var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins;
  5912. if (missingPluginsArray) {
  5913. openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
  5914. "PFSWindow", "chrome,centerscreen,resizable=yes",
  5915. {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
  5916. }
  5917. }
  5918. #ifdef XP_MACOSX
  5919. function carbonFailurePluginsRestartBrowser()
  5920. {
  5921. // Notify all windows that an application quit has been requested.
  5922. let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
  5923. createInstance(Ci.nsISupportsPRBool);
  5924. Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
  5925. // Something aborted the quit process.
  5926. if (cancelQuit.data)
  5927. return;
  5928. let as = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
  5929. as.quit(Ci.nsIAppStartup.eRestarti386 | Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
  5930. }
  5931. #endif
  5932. let notifications = {
  5933. PluginBlocklisted : {
  5934. barID : "blocked-plugins",
  5935. iconURL : "chrome://mozapps/skin/plugins/notifyPluginBlocked.png",
  5936. message : gNavigatorBundle.getString("blockedpluginsMessage.title"),
  5937. buttons : [{
  5938. label : gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"),
  5939. accessKey : gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"),
  5940. popup : null,
  5941. callback : showBlocklistInfo
  5942. },
  5943. {
  5944. label : gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"),
  5945. accessKey : gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"),
  5946. popup : null,
  5947. callback : showOutdatedPluginsInfo
  5948. }],
  5949. },
  5950. PluginOutdated : {
  5951. barID : "outdated-plugins",
  5952. iconURL : "chrome://mozapps/skin/plugins/notifyPluginOutdated.png",
  5953. message : gNavigatorBundle.getString("outdatedpluginsMessage.title"),
  5954. buttons : [{
  5955. label : gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"),
  5956. accessKey : gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"),
  5957. popup : null,
  5958. callback : showOutdatedPluginsInfo
  5959. }],
  5960. },
  5961. PluginNotFound : {
  5962. barID : "missing-plugins",
  5963. iconURL : "chrome://mozapps/skin/plugins/notifyPluginGeneric.png",
  5964. message : gNavigatorBundle.getString("missingpluginsMessage.title"),
  5965. buttons : [{
  5966. label : gNavigatorBundle.getString("missingpluginsMessage.button.label"),
  5967. accessKey : gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"),
  5968. popup : null,
  5969. callback : showPluginsMissing
  5970. }],
  5971. },
  5972. #ifdef XP_MACOSX
  5973. "npapi-carbon-event-model-failure" : {
  5974. barID : "carbon-failure-plugins",
  5975. iconURL : "chrome://mozapps/skin/plugins/notifyPluginGeneric.png",
  5976. message : gNavigatorBundle.getString("carbonFailurePluginsMessage.message"),
  5977. buttons: [{
  5978. label : gNavigatorBundle.getString("carbonFailurePluginsMessage.restartButton.label"),
  5979. accessKey : gNavigatorBundle.getString("carbonFailurePluginsMessage.restartButton.accesskey"),
  5980. popup : null,
  5981. callback : carbonFailurePluginsRestartBrowser
  5982. }],
  5983. }
  5984. #endif
  5985. };
  5986. // If there is already an outdated plugin notification then do nothing
  5987. if (outdatedNotification)
  5988. return;
  5989. #ifdef XP_MACOSX
  5990. if (eventType == "npapi-carbon-event-model-failure") {
  5991. if (gPrefService.getBoolPref("plugins.hide_infobar_for_carbon_failure_plugin"))
  5992. return;
  5993. let carbonFailureNotification =
  5994. notificationBox.getNotificationWithValue("carbon-failure-plugins");
  5995. if (carbonFailureNotification)
  5996. carbonFailureNotification.close();
  5997. let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].getService(Ci.nsIMacUtils);
  5998. // if this is not a Universal build, just follow PluginNotFound path
  5999. if (!macutils.isUniversalBinary)
  6000. eventType = "PluginNotFound";
  6001. }
  6002. #endif
  6003. if (eventType == "PluginBlocklisted") {
  6004. if (gPrefService.getBoolPref("plugins.hide_infobar_for_missing_plugin")) // XXX add a new pref?
  6005. return;
  6006. if (blockedNotification || missingNotification)
  6007. return;
  6008. }
  6009. else if (eventType == "PluginOutdated") {
  6010. if (gPrefService.getBoolPref("plugins.hide_infobar_for_outdated_plugin"))
  6011. return;
  6012. // Cancel any notification about blocklisting/missing plugins
  6013. if (blockedNotification)
  6014. blockedNotification.close();
  6015. if (missingNotification)
  6016. missingNotification.close();
  6017. }
  6018. else if (eventType == "PluginNotFound") {
  6019. if (gPrefService.getBoolPref("plugins.hide_infobar_for_missing_plugin"))
  6020. return;
  6021. if (missingNotification)
  6022. return;
  6023. // Cancel any notification about blocklisting plugins
  6024. if (blockedNotification)
  6025. blockedNotification.close();
  6026. }
  6027. let notify = notifications[eventType];
  6028. notificationBox.appendNotification(notify.message, notify.barID, notify.iconURL,
  6029. notificationBox.PRIORITY_WARNING_MEDIUM,
  6030. notify.buttons);
  6031. },
  6032. // Crashed-plugin observer. Notified once per plugin crash, before events
  6033. // are dispatched to individual plugin instances.
  6034. pluginCrashed : function(subject, topic, data) {
  6035. let propertyBag = subject;
  6036. if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
  6037. !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
  6038. return;
  6039. #ifdef MOZ_CRASHREPORTER
  6040. let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
  6041. let browserDumpID= propertyBag.getPropertyAsAString("browserDumpID");
  6042. let shouldSubmit = gCrashReporter.submitReports;
  6043. let doPrompt = true; // XXX followup to get via gCrashReporter
  6044. // Submit automatically when appropriate.
  6045. if (pluginDumpID && shouldSubmit && !doPrompt) {
  6046. this.submitReport(pluginDumpID, browserDumpID);
  6047. // Submission is async, so we can't easily show failure UI.
  6048. propertyBag.setPropertyAsBool("submittedCrashReport", true);
  6049. }
  6050. #endif
  6051. },
  6052. // Crashed-plugin event listener. Called for every instance of a
  6053. // plugin in content.
  6054. pluginInstanceCrashed: function (plugin, aEvent) {
  6055. // Ensure the plugin and event are of the right type.
  6056. if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent))
  6057. return;
  6058. let submittedReport = aEvent.getData("submittedCrashReport");
  6059. let doPrompt = true; // XXX followup for .getData("doPrompt");
  6060. let submitReports = true; // XXX followup for .getData("submitReports");
  6061. let pluginName = aEvent.getData("pluginName");
  6062. let pluginFilename = aEvent.getData("pluginFilename");
  6063. let pluginDumpID = aEvent.getData("pluginDumpID");
  6064. let browserDumpID = aEvent.getData("browserDumpID");
  6065. // Remap the plugin name to a more user-presentable form.
  6066. pluginName = this.makeNicePluginName(pluginName, pluginFilename);
  6067. let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
  6068. //
  6069. // Configure the crashed-plugin placeholder.
  6070. //
  6071. let doc = plugin.ownerDocument;
  6072. let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
  6073. let statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus");
  6074. #ifdef MOZ_CRASHREPORTER
  6075. let status;
  6076. // Determine which message to show regarding crash reports.
  6077. if (submittedReport) { // submitReports && !doPrompt, handled in observer
  6078. status = "submitted";
  6079. }
  6080. else if (!submitReports && !doPrompt) {
  6081. status = "noSubmit";
  6082. }
  6083. else { // doPrompt
  6084. status = "please";
  6085. // XXX can we make the link target actually be blank?
  6086. let pleaseLink = doc.getAnonymousElementByAttribute(
  6087. plugin, "class", "pleaseSubmitLink");
  6088. this.addLinkClickCallback(pleaseLink, "submitReport",
  6089. pluginDumpID, browserDumpID);
  6090. }
  6091. // If we don't have a minidumpID, we can't (or didn't) submit anything.
  6092. // This can happen if the plugin is killed from the task manager.
  6093. if (!pluginDumpID) {
  6094. status = "noReport";
  6095. }
  6096. statusDiv.setAttribute("status", status);
  6097. let bottomLinks = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgBottomLinks");
  6098. bottomLinks.style.display = "block";
  6099. let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
  6100. this.addLinkClickCallback(helpIcon, "openHelpPage");
  6101. // If we're showing the link to manually trigger report submission, we'll
  6102. // want to be able to update all the instances of the UI for this crash to
  6103. // show an updated message when a report is submitted.
  6104. if (doPrompt) {
  6105. let observer = {
  6106. QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  6107. Ci.nsISupportsWeakReference]),
  6108. observe : function(subject, topic, data) {
  6109. let propertyBag = subject;
  6110. if (!(propertyBag instanceof Ci.nsIPropertyBag2))
  6111. return;
  6112. // Ignore notifications for other crashes.
  6113. if (propertyBag.get("minidumpID") != pluginDumpID)
  6114. return;
  6115. statusDiv.setAttribute("status", data);
  6116. },
  6117. handleEvent : function(event) {
  6118. // Not expected to be called, just here for the closure.
  6119. }
  6120. }
  6121. // Use a weak reference, so we don't have to remove it...
  6122. Services.obs.addObserver(observer, "crash-report-status", true);
  6123. // ...alas, now we need something to hold a strong reference to prevent
  6124. // it from being GC. But I don't want to manually manage the reference's
  6125. // lifetime (which should be no greater than the page).
  6126. // Clever solution? Use a closue with an event listener on the document.
  6127. // When the doc goes away, so do the listener references and the closure.
  6128. doc.addEventListener("mozCleverClosureHack", observer, false);
  6129. }
  6130. #endif
  6131. let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
  6132. crashText.textContent = messageString;
  6133. let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
  6134. let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
  6135. this.addLinkClickCallback(link, "reloadPage", browser);
  6136. let notificationBox = gBrowser.getNotificationBox(browser);
  6137. // Is the <object>'s size too small to hold what we want to show?
  6138. if (this.isTooSmall(plugin, overlay)) {
  6139. // Hide the overlay's contents. Use visibility style, so that it
  6140. // doesn't collapse down to 0x0.
  6141. overlay.style.visibility = "hidden";
  6142. // If another plugin on the page was large enough to show our UI, we
  6143. // don't want to show a notification bar.
  6144. if (!doc.mozNoPluginCrashedNotification)
  6145. showNotificationBar(pluginDumpID, browserDumpID);
  6146. } else {
  6147. // If a previous plugin on the page was too small and resulted in
  6148. // adding a notification bar, then remove it because this plugin
  6149. // instance it big enough to serve as in-content notification.
  6150. hideNotificationBar();
  6151. doc.mozNoPluginCrashedNotification = true;
  6152. }
  6153. function hideNotificationBar() {
  6154. let notification = notificationBox.getNotificationWithValue("plugin-crashed");
  6155. if (notification)
  6156. notificationBox.removeNotification(notification, true);
  6157. }
  6158. function showNotificationBar(pluginDumpID, browserDumpID) {
  6159. // If there's already an existing notification bar, don't do anything.
  6160. let notification = notificationBox.getNotificationWithValue("plugin-crashed");
  6161. if (notification)
  6162. return;
  6163. // Configure the notification bar
  6164. let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
  6165. let iconURL = "chrome://mozapps/skin/plugins/notifyPluginCrashed.png";
  6166. let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
  6167. let reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
  6168. let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
  6169. let submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
  6170. let buttons = [{
  6171. label: reloadLabel,
  6172. accessKey: reloadKey,
  6173. popup: null,
  6174. callback: function() { browser.reload(); },
  6175. }];
  6176. #ifdef MOZ_CRASHREPORTER
  6177. let submitButton = {
  6178. label: submitLabel,
  6179. accessKey: submitKey,
  6180. popup: null,
  6181. callback: function() { gPluginHandler.submitReport(pluginDumpID, browserDumpID); },
  6182. };
  6183. if (pluginDumpID)
  6184. buttons.push(submitButton);
  6185. #endif
  6186. let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
  6187. iconURL, priority, buttons);
  6188. // Add the "learn more" link.
  6189. let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  6190. let link = notification.ownerDocument.createElementNS(XULNS, "label");
  6191. link.className = "text-link";
  6192. link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
  6193. link.href = gPluginHandler.crashReportHelpURL;
  6194. let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
  6195. description.appendChild(link);
  6196. // Remove the notfication when the page is reloaded.
  6197. doc.defaultView.top.addEventListener("unload", function() {
  6198. notificationBox.removeNotification(notification);
  6199. }, false);
  6200. }
  6201. }
  6202. };
  6203. function convertFromUnicode(charset, str)
  6204. {
  6205. try {
  6206. var unicodeConverter = Components
  6207. .classes["@mozilla.org/intl/scriptableunicodeconverter"]
  6208. .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  6209. unicodeConverter.charset = charset;
  6210. str = unicodeConverter.ConvertFromUnicode(str);
  6211. return str + unicodeConverter.Finish();
  6212. } catch(ex) {
  6213. return null;
  6214. }
  6215. }
  6216. /**
  6217. * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
  6218. * and shows UI when they are discovered.
  6219. */
  6220. var FeedHandler = {
  6221. /**
  6222. * The click handler for the Feed icon in the toolbar. Opens the
  6223. * subscription page if user is not given a choice of feeds.
  6224. * (Otherwise the list of available feeds will be presented to the
  6225. * user in a popup menu.)
  6226. */
  6227. onFeedButtonClick: function(event) {
  6228. event.stopPropagation();
  6229. let feeds = gBrowser.selectedBrowser.feeds || [];
  6230. // If there are multiple feeds, the menu will open, so no need to do
  6231. // anything. If there are no feeds, nothing to do either.
  6232. if (feeds.length != 1)
  6233. return;
  6234. if (event.eventPhase == Event.AT_TARGET &&
  6235. (event.button == 0 || event.button == 1)) {
  6236. this.subscribeToFeed(feeds[0].href, event);
  6237. }
  6238. },
  6239. /** Called when the user clicks on the Subscribe to This Page... menu item.
  6240. * Builds a menu of unique feeds associated with the page, and if there
  6241. * is only one, shows the feed inline in the browser window.
  6242. * @param menuPopup
  6243. * The feed list menupopup to be populated.
  6244. * @returns true if the menu should be shown, false if there was only
  6245. * one feed and the feed should be shown inline in the browser
  6246. * window (do not show the menupopup).
  6247. */
  6248. buildFeedList: function(menuPopup) {
  6249. var feeds = gBrowser.selectedBrowser.feeds;
  6250. if (feeds == null) {
  6251. // XXX hack -- menu opening depends on setting of an "open"
  6252. // attribute, and the menu refuses to open if that attribute is
  6253. // set (because it thinks it's already open). onpopupshowing gets
  6254. // called after the attribute is unset, and it doesn't get unset
  6255. // if we return false. so we unset it here; otherwise, the menu
  6256. // refuses to work past this point.
  6257. menuPopup.parentNode.removeAttribute("open");
  6258. return false;
  6259. }
  6260. while (menuPopup.firstChild)
  6261. menuPopup.removeChild(menuPopup.firstChild);
  6262. if (feeds.length <= 1)
  6263. return false;
  6264. // Build the menu showing the available feed choices for viewing.
  6265. for (var i = 0; i < feeds.length; ++i) {
  6266. var feedInfo = feeds[i];
  6267. var menuItem = document.createElement("menuitem");
  6268. var baseTitle = feedInfo.title || feedInfo.href;
  6269. var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
  6270. menuItem.setAttribute("class", "feed-menuitem");
  6271. menuItem.setAttribute("label", labelStr);
  6272. menuItem.setAttribute("feed", feedInfo.href);
  6273. menuItem.setAttribute("tooltiptext", feedInfo.href);
  6274. menuItem.setAttribute("crop", "center");
  6275. menuPopup.appendChild(menuItem);
  6276. }
  6277. return true;
  6278. },
  6279. /**
  6280. * Subscribe to a given feed. Called when
  6281. * 1. Page has a single feed and user clicks feed icon in location bar
  6282. * 2. Page has a single feed and user selects Subscribe menu item
  6283. * 3. Page has multiple feeds and user selects from feed icon popup
  6284. * 4. Page has multiple feeds and user selects from Subscribe submenu
  6285. * @param href
  6286. * The feed to subscribe to. May be null, in which case the
  6287. * event target's feed attribute is examined.
  6288. * @param event
  6289. * The event this method is handling. Used to decide where
  6290. * to open the preview UI. (Optional, unless href is null)
  6291. */
  6292. subscribeToFeed: function(href, event) {
  6293. // Just load the feed in the content area to either subscribe or show the
  6294. // preview UI
  6295. if (!href)
  6296. href = event.target.getAttribute("feed");
  6297. urlSecurityCheck(href, gBrowser.contentPrincipal,
  6298. Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
  6299. var feedURI = makeURI(href, document.characterSet);
  6300. // Use the feed scheme so X-Moz-Is-Feed will be set
  6301. // The value doesn't matter
  6302. if (/^https?/.test(feedURI.scheme))
  6303. href = "feed:" + href;
  6304. this.loadFeed(href, event);
  6305. },
  6306. loadFeed: function(href, event) {
  6307. var feeds = gBrowser.selectedBrowser.feeds;
  6308. try {
  6309. openUILink(href, event, false, true, false, null);
  6310. }
  6311. finally {
  6312. // We might default to a livebookmarks modal dialog,
  6313. // so reset that if the user happens to click it again
  6314. gBrowser.selectedBrowser.feeds = feeds;
  6315. }
  6316. },
  6317. get _feedMenuitem() {
  6318. delete this._feedMenuitem;
  6319. return this._feedMenuitem = document.getElementById("singleFeedMenuitemState");
  6320. },
  6321. get _feedMenupopup() {
  6322. delete this._feedMenupopup;
  6323. return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
  6324. },
  6325. /**
  6326. * Update the browser UI to show whether or not feeds are available when
  6327. * a page is loaded or the user switches tabs to a page that has feeds.
  6328. */
  6329. updateFeeds: function() {
  6330. clearTimeout(this._updateFeedTimeout);
  6331. var feeds = gBrowser.selectedBrowser.feeds;
  6332. var haveFeeds = feeds && feeds.length > 0;
  6333. var feedButton = document.getElementById("feed-button");
  6334. if (feedButton)
  6335. feedButton.disabled = !haveFeeds;
  6336. if (!haveFeeds) {
  6337. this._feedMenuitem.setAttribute("disabled", "true");
  6338. this._feedMenuitem.removeAttribute("hidden");
  6339. this._feedMenupopup.setAttribute("hidden", "true");
  6340. return;
  6341. }
  6342. if (feeds.length > 1) {
  6343. this._feedMenuitem.setAttribute("hidden", "true");
  6344. this._feedMenupopup.removeAttribute("hidden");
  6345. } else {
  6346. this._feedMenuitem.setAttribute("feed", feeds[0].href);
  6347. this._feedMenuitem.removeAttribute("disabled");
  6348. this._feedMenuitem.removeAttribute("hidden");
  6349. this._feedMenupopup.setAttribute("hidden", "true");
  6350. }
  6351. },
  6352. addFeed: function(link, targetDoc) {
  6353. // find which tab this is for, and set the attribute on the browser
  6354. var browserForLink = gBrowser.getBrowserForDocument(targetDoc);
  6355. if (!browserForLink) {
  6356. // ignore feeds loaded in subframes (see bug 305472)
  6357. return;
  6358. }
  6359. if (!browserForLink.feeds)
  6360. browserForLink.feeds = [];
  6361. browserForLink.feeds.push({ href: link.href, title: link.title });
  6362. // If this addition was for the current browser, update the UI. For
  6363. // background browsers, we'll update on tab switch.
  6364. if (browserForLink == gBrowser.selectedBrowser) {
  6365. // Batch updates to avoid updating the UI for multiple onLinkAdded events
  6366. // fired within 100ms of each other.
  6367. clearTimeout(this._updateFeedTimeout);
  6368. this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
  6369. }
  6370. }
  6371. };
  6372. /**
  6373. * Re-open a closed tab.
  6374. * @param aIndex
  6375. * The index of the tab (via nsSessionStore.getClosedTabData)
  6376. * @returns a reference to the reopened tab.
  6377. */
  6378. function undoCloseTab(aIndex) {
  6379. // wallpaper patch to prevent an unnecessary blank tab (bug 343895)
  6380. var blankTabToRemove = null;
  6381. if (gBrowser.tabs.length == 1 &&
  6382. !gPrefService.getBoolPref("browser.tabs.autoHide") &&
  6383. isTabEmpty(gBrowser.selectedTab))
  6384. blankTabToRemove = gBrowser.selectedTab;
  6385. var tab = null;
  6386. var ss = Cc["@mozilla.org/browser/sessionstore;1"].
  6387. getService(Ci.nsISessionStore);
  6388. if (ss.getClosedTabCount(window) > (aIndex || 0)) {
  6389. TabView.prepareUndoCloseTab(blankTabToRemove);
  6390. tab = ss.undoCloseTab(window, aIndex || 0);
  6391. TabView.afterUndoCloseTab();
  6392. if (blankTabToRemove)
  6393. gBrowser.removeTab(blankTabToRemove);
  6394. }
  6395. return tab;
  6396. }
  6397. /**
  6398. * Re-open a closed window.
  6399. * @param aIndex
  6400. * The index of the window (via nsSessionStore.getClosedWindowData)
  6401. * @returns a reference to the reopened window.
  6402. */
  6403. function undoCloseWindow(aIndex) {
  6404. let ss = Cc["@mozilla.org/browser/sessionstore;1"].
  6405. getService(Ci.nsISessionStore);
  6406. let window = null;
  6407. if (ss.getClosedWindowCount() > (aIndex || 0))
  6408. window = ss.undoCloseWindow(aIndex || 0);
  6409. return window;
  6410. }
  6411. /*
  6412. * Determines if a tab is "empty", usually used in the context of determining
  6413. * if it's ok to close the tab.
  6414. */
  6415. function isTabEmpty(aTab) {
  6416. let browser = aTab.linkedBrowser;
  6417. return browser.sessionHistory.count < 2 &&
  6418. browser.currentURI.spec == "about:blank" &&
  6419. !browser.contentDocument.body.hasChildNodes() &&
  6420. !aTab.hasAttribute("busy");
  6421. }
  6422. #ifdef MOZ_SERVICES_SYNC
  6423. function BrowserOpenSyncTabs() {
  6424. switchToTabHavingURI("about:sync-tabs", true);
  6425. }
  6426. #endif
  6427. /**
  6428. * Format a URL
  6429. * eg:
  6430. * echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
  6431. * > https://addons.mozilla.org/en-US/firefox/3.0a1/
  6432. *
  6433. * Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
  6434. */
  6435. function formatURL(aFormat, aIsPref) {
  6436. var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
  6437. return aIsPref ? formatter.formatURLPref(aFormat) : formatter.formatURL(aFormat);
  6438. }
  6439. /**
  6440. * Utility object to handle manipulations of the identity indicators in the UI
  6441. */
  6442. var gIdentityHandler = {
  6443. // Mode strings used to control CSS display
  6444. IDENTITY_MODE_IDENTIFIED : "verifiedIdentity", // High-quality identity information
  6445. IDENTITY_MODE_DOMAIN_VERIFIED : "verifiedDomain", // Minimal SSL CA-signed domain verification
  6446. IDENTITY_MODE_UNKNOWN : "unknownIdentity", // No trusted identity information
  6447. IDENTITY_MODE_MIXED_CONTENT : "unknownIdentity mixedContent", // SSL with unauthenticated content
  6448. IDENTITY_MODE_CHROMEUI : "chromeUI", // Part of the product's UI
  6449. // Cache the most recent SSLStatus and Location seen in checkIdentity
  6450. _lastStatus : null,
  6451. _lastLocation : null,
  6452. _mode : "unknownIdentity",
  6453. // smart getters
  6454. get _encryptionLabel () {
  6455. delete this._encryptionLabel;
  6456. this._encryptionLabel = {};
  6457. this._encryptionLabel[this.IDENTITY_MODE_DOMAIN_VERIFIED] =
  6458. gNavigatorBundle.getString("identity.encrypted");
  6459. this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
  6460. gNavigatorBundle.getString("identity.encrypted");
  6461. this._encryptionLabel[this.IDENTITY_MODE_UNKNOWN] =
  6462. gNavigatorBundle.getString("identity.unencrypted");
  6463. this._encryptionLabel[this.IDENTITY_MODE_MIXED_CONTENT] =
  6464. gNavigatorBundle.getString("identity.mixed_content");
  6465. return this._encryptionLabel;
  6466. },
  6467. get _identityPopup () {
  6468. delete this._identityPopup;
  6469. return this._identityPopup = document.getElementById("identity-popup");
  6470. },
  6471. get _identityBox () {
  6472. delete this._identityBox;
  6473. return this._identityBox = document.getElementById("identity-box");
  6474. },
  6475. get _identityPopupContentBox () {
  6476. delete this._identityPopupContentBox;
  6477. return this._identityPopupContentBox =
  6478. document.getElementById("identity-popup-content-box");
  6479. },
  6480. get _identityPopupContentHost () {
  6481. delete this._identityPopupContentHost;
  6482. return this._identityPopupContentHost =
  6483. document.getElementById("identity-popup-content-host");
  6484. },
  6485. get _identityPopupContentOwner () {
  6486. delete this._identityPopupContentOwner;
  6487. return this._identityPopupContentOwner =
  6488. document.getElementById("identity-popup-content-owner");
  6489. },
  6490. get _identityPopupContentSupp () {
  6491. delete this._identityPopupContentSupp;
  6492. return this._identityPopupContentSupp =
  6493. document.getElementById("identity-popup-content-supplemental");
  6494. },
  6495. get _identityPopupContentVerif () {
  6496. delete this._identityPopupContentVerif;
  6497. return this._identityPopupContentVerif =
  6498. document.getElementById("identity-popup-content-verifier");
  6499. },
  6500. get _identityPopupEncLabel () {
  6501. delete this._identityPopupEncLabel;
  6502. return this._identityPopupEncLabel =
  6503. document.getElementById("identity-popup-encryption-label");
  6504. },
  6505. get _identityIconLabel () {
  6506. delete this._identityIconLabel;
  6507. return this._identityIconLabel = document.getElementById("identity-icon-label");
  6508. },
  6509. get _overrideService () {
  6510. delete this._overrideService;
  6511. return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
  6512. .getService(Ci.nsICertOverrideService);
  6513. },
  6514. get _identityIconCountryLabel () {
  6515. delete this._identityIconCountryLabel;
  6516. return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
  6517. },
  6518. /**
  6519. * Rebuild cache of the elements that may or may not exist depending
  6520. * on whether there's a location bar.
  6521. */
  6522. _cacheElements : function() {
  6523. delete this._identityBox;
  6524. delete this._identityIconLabel;
  6525. delete this._identityIconCountryLabel;
  6526. this._identityBox = document.getElementById("identity-box");
  6527. this._identityIconLabel = document.getElementById("identity-icon-label");
  6528. this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
  6529. },
  6530. /**
  6531. * Handler for mouseclicks on the "More Information" button in the
  6532. * "identity-popup" panel.
  6533. */
  6534. handleMoreInfoClick : function(event) {
  6535. displaySecurityInfo();
  6536. event.stopPropagation();
  6537. },
  6538. /**
  6539. * Helper to parse out the important parts of _lastStatus (of the SSL cert in
  6540. * particular) for use in constructing identity UI strings
  6541. */
  6542. getIdentityData : function() {
  6543. var result = {};
  6544. var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
  6545. var cert = status.serverCert;
  6546. // Human readable name of Subject
  6547. result.subjectOrg = cert.organization;
  6548. // SubjectName fields, broken up for individual access
  6549. if (cert.subjectName) {
  6550. result.subjectNameFields = {};
  6551. cert.subjectName.split(",").forEach(function(v) {
  6552. var field = v.split("=");
  6553. this[field[0]] = field[1];
  6554. }, result.subjectNameFields);
  6555. // Call out city, state, and country specifically
  6556. result.city = result.subjectNameFields.L;
  6557. result.state = result.subjectNameFields.ST;
  6558. result.country = result.subjectNameFields.C;
  6559. }
  6560. // Human readable name of Certificate Authority
  6561. result.caOrg = cert.issuerOrganization || cert.issuerCommonName;
  6562. result.cert = cert;
  6563. return result;
  6564. },
  6565. /**
  6566. * Determine the identity of the page being displayed by examining its SSL cert
  6567. * (if available) and, if necessary, update the UI to reflect this. Intended to
  6568. * be called by onSecurityChange
  6569. *
  6570. * @param PRUint32 state
  6571. * @param JS Object location that mirrors an nsLocation (i.e. has .host and
  6572. * .hostname and .port)
  6573. */
  6574. checkIdentity : function(state, location) {
  6575. var currentStatus = gBrowser.securityUI
  6576. .QueryInterface(Components.interfaces.nsISSLStatusProvider)
  6577. .SSLStatus;
  6578. this._lastStatus = currentStatus;
  6579. this._lastLocation = location;
  6580. let nsIWebProgressListener = Ci.nsIWebProgressListener;
  6581. if (location.protocol == "chrome:" || location.protocol == "about:")
  6582. this.setMode(this.IDENTITY_MODE_CHROMEUI);
  6583. else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
  6584. this.setMode(this.IDENTITY_MODE_IDENTIFIED);
  6585. else if (state & nsIWebProgressListener.STATE_SECURE_HIGH)
  6586. this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
  6587. else if (state & nsIWebProgressListener.STATE_IS_BROKEN)
  6588. this.setMode(this.IDENTITY_MODE_MIXED_CONTENT);
  6589. else
  6590. this.setMode(this.IDENTITY_MODE_UNKNOWN);
  6591. },
  6592. /**
  6593. * Return the eTLD+1 version of the current hostname
  6594. */
  6595. getEffectiveHost : function() {
  6596. // Cache the eTLDService if this is our first time through
  6597. if (!this._eTLDService)
  6598. this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
  6599. .getService(Ci.nsIEffectiveTLDService);
  6600. if (!this._IDNService)
  6601. this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
  6602. .getService(Ci.nsIIDNService);
  6603. try {
  6604. let baseDomain =
  6605. this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
  6606. return this._IDNService.convertToDisplayIDN(baseDomain, {});
  6607. } catch (e) {
  6608. // If something goes wrong (e.g. hostname is an IP address) just fail back
  6609. // to the full domain.
  6610. return this._lastLocation.hostname;
  6611. }
  6612. },
  6613. /**
  6614. * Update the UI to reflect the specified mode, which should be one of the
  6615. * IDENTITY_MODE_* constants.
  6616. */
  6617. setMode : function(newMode) {
  6618. if (!this._identityBox) {
  6619. // No identity box means the identity box is not visible, in which
  6620. // case there's nothing to do.
  6621. return;
  6622. }
  6623. this._identityBox.className = newMode;
  6624. this.setIdentityMessages(newMode);
  6625. // Update the popup too, if it's open
  6626. if (this._identityPopup.state == "open")
  6627. this.setPopupMessages(newMode);
  6628. this._mode = newMode;
  6629. },
  6630. /**
  6631. * Set up the messages for the primary identity UI based on the specified mode,
  6632. * and the details of the SSL cert, where applicable
  6633. *
  6634. * @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
  6635. */
  6636. setIdentityMessages : function(newMode) {
  6637. if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
  6638. var iData = this.getIdentityData();
  6639. // It would be sort of nice to use the CN= field in the cert, since that's
  6640. // typically what we want here, but thanks to x509 certs being extensible,
  6641. // it's not the only place you have to check, there can be more than one domain,
  6642. // et cetera, ad nauseum. We know the cert is valid for location.host, so
  6643. // let's just use that. Check the pref to determine how much of the verified
  6644. // hostname to show
  6645. var icon_label = "";
  6646. var icon_country_label = "";
  6647. var icon_labels_dir = "ltr";
  6648. switch (gPrefService.getIntPref("browser.identity.ssl_domain_display")) {
  6649. case 2 : // Show full domain
  6650. icon_label = this._lastLocation.hostname;
  6651. break;
  6652. case 1 : // Show eTLD.
  6653. icon_label = this.getEffectiveHost();
  6654. }
  6655. // Verifier is either the CA Org, for a normal cert, or a special string
  6656. // for certs that are trusted because of a security exception.
  6657. var tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
  6658. [iData.caOrg]);
  6659. // Check whether this site is a security exception. XPConnect does the right
  6660. // thing here in terms of converting _lastLocation.port from string to int, but
  6661. // the overrideService doesn't like undefined ports, so make sure we have
  6662. // something in the default case (bug 432241).
  6663. // .hostname can return an empty string in some exceptional cases -
  6664. // hasMatchingOverride does not handle that, so avoid calling it.
  6665. // Updating the tooltip value in those cases isn't critical.
  6666. // FIXME: Fixing bug 646690 would probably makes this check unnecessary
  6667. if (this._lastLocation.hostname &&
  6668. this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
  6669. (this._lastLocation.port || 443),
  6670. iData.cert, {}, {}))
  6671. tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
  6672. }
  6673. else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
  6674. // If it's identified, then we can populate the dialog with credentials
  6675. iData = this.getIdentityData();
  6676. tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
  6677. [iData.caOrg]);
  6678. icon_label = iData.subjectOrg;
  6679. if (iData.country)
  6680. icon_country_label = "(" + iData.country + ")";
  6681. // If the organization name starts with an RTL character, then
  6682. // swap the positions of the organization and country code labels.
  6683. // The Unicode ranges reflect the definition of the UCS2_CHAR_IS_BIDI
  6684. // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
  6685. // fixed, this test should be replaced by one adhering to the
  6686. // Unicode Bidirectional Algorithm proper (at the paragraph level).
  6687. icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/.test(icon_label) ?
  6688. "rtl" : "ltr";
  6689. }
  6690. else if (newMode == this.IDENTITY_MODE_CHROMEUI) {
  6691. icon_label = "";
  6692. tooltip = "";
  6693. icon_country_label = "";
  6694. icon_labels_dir = "ltr";
  6695. }
  6696. else {
  6697. tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
  6698. icon_label = "";
  6699. icon_country_label = "";
  6700. icon_labels_dir = "ltr";
  6701. }
  6702. // Push the appropriate strings out to the UI
  6703. this._identityBox.tooltipText = tooltip;
  6704. this._identityIconLabel.value = icon_label;
  6705. this._identityIconCountryLabel.value = icon_country_label;
  6706. // Set cropping and direction
  6707. this._identityIconLabel.crop = icon_country_label ? "end" : "center";
  6708. this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
  6709. // Hide completely if the organization label is empty
  6710. this._identityIconLabel.parentNode.collapsed = icon_label ? false : true;
  6711. },
  6712. /**
  6713. * Set up the title and content messages for the identity message popup,
  6714. * based on the specified mode, and the details of the SSL cert, where
  6715. * applicable
  6716. *
  6717. * @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
  6718. */
  6719. setPopupMessages : function(newMode) {
  6720. this._identityPopup.className = newMode;
  6721. this._identityPopupContentBox.className = newMode;
  6722. // Set the static strings up front
  6723. this._identityPopupEncLabel.textContent = this._encryptionLabel[newMode];
  6724. // Initialize the optional strings to empty values
  6725. var supplemental = "";
  6726. var verifier = "";
  6727. if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
  6728. var iData = this.getIdentityData();
  6729. var host = this.getEffectiveHost();
  6730. var owner = gNavigatorBundle.getString("identity.ownerUnknown2");
  6731. verifier = this._identityBox.tooltipText;
  6732. supplemental = "";
  6733. }
  6734. else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
  6735. // If it's identified, then we can populate the dialog with credentials
  6736. iData = this.getIdentityData();
  6737. host = this.getEffectiveHost();
  6738. owner = iData.subjectOrg;
  6739. verifier = this._identityBox.tooltipText;
  6740. // Build an appropriate supplemental block out of whatever location data we have
  6741. if (iData.city)
  6742. supplemental += iData.city + "\n";
  6743. if (iData.state && iData.country)
  6744. supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
  6745. [iData.state, iData.country]);
  6746. else if (iData.state) // State only
  6747. supplemental += iData.state;
  6748. else if (iData.country) // Country only
  6749. supplemental += iData.country;
  6750. }
  6751. else {
  6752. // These strings will be hidden in CSS anyhow
  6753. host = "";
  6754. owner = "";
  6755. }
  6756. // Push the appropriate strings out to the UI
  6757. this._identityPopupContentHost.textContent = host;
  6758. this._identityPopupContentOwner.textContent = owner;
  6759. this._identityPopupContentSupp.textContent = supplemental;
  6760. this._identityPopupContentVerif.textContent = verifier;
  6761. },
  6762. hideIdentityPopup : function() {
  6763. this._identityPopup.hidePopup();
  6764. },
  6765. /**
  6766. * Click handler for the identity-box element in primary chrome.
  6767. */
  6768. handleIdentityButtonEvent : function(event) {
  6769. event.stopPropagation();
  6770. if ((event.type == "click" && event.button != 0) ||
  6771. (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
  6772. event.keyCode != KeyEvent.DOM_VK_RETURN))
  6773. return; // Left click, space or enter only
  6774. // Revert the contents of the location bar, see bug 406779
  6775. gURLBar.handleRevert();
  6776. if (this._mode == this.IDENTITY_MODE_CHROMEUI)
  6777. return;
  6778. // Make sure that the display:none style we set in xul is removed now that
  6779. // the popup is actually needed
  6780. this._identityPopup.hidden = false;
  6781. // Tell the popup to consume dismiss clicks, to avoid bug 395314
  6782. this._identityPopup.popupBoxObject
  6783. .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
  6784. // Update the popup strings
  6785. this.setPopupMessages(this._identityBox.className);
  6786. // Add the "open" attribute to the identity box for styling
  6787. this._identityBox.setAttribute("open", "true");
  6788. var self = this;
  6789. this._identityPopup.addEventListener("popuphidden", function (e) {
  6790. e.currentTarget.removeEventListener("popuphidden", arguments.callee, false);
  6791. self._identityBox.removeAttribute("open");
  6792. }, false);
  6793. // Now open the popup, anchored off the primary chrome element
  6794. this._identityPopup.openPopup(this._identityBox, "bottomcenter topleft");
  6795. },
  6796. onDragStart: function (event) {
  6797. if (gURLBar.getAttribute("pageproxystate") != "valid")
  6798. return;
  6799. var value = content.location.href;
  6800. var urlString = value + "\n" + content.document.title;
  6801. var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
  6802. var dt = event.dataTransfer;
  6803. dt.setData("text/x-moz-url", urlString);
  6804. dt.setData("text/uri-list", value);
  6805. dt.setData("text/plain", value);
  6806. dt.setData("text/html", htmlString);
  6807. dt.setDragImage(gProxyFavIcon, 16, 16);
  6808. }
  6809. };
  6810. let DownloadMonitorPanel = {
  6811. //////////////////////////////////////////////////////////////////////////////
  6812. //// DownloadMonitorPanel Member Variables
  6813. _panel: null,
  6814. _activeStr: null,
  6815. _pausedStr: null,
  6816. _lastTime: Infinity,
  6817. _listening: false,
  6818. get DownloadUtils() {
  6819. delete this.DownloadUtils;
  6820. Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
  6821. return this.DownloadUtils;
  6822. },
  6823. //////////////////////////////////////////////////////////////////////////////
  6824. //// DownloadMonitorPanel Public Methods
  6825. /**
  6826. * Initialize the status panel and member variables
  6827. */
  6828. init: function DMP_init() {
  6829. // Initialize "private" member variables
  6830. this._panel = document.getElementById("download-monitor");
  6831. // Cache the status strings
  6832. this._activeStr = gNavigatorBundle.getString("activeDownloads1");
  6833. this._pausedStr = gNavigatorBundle.getString("pausedDownloads1");
  6834. gDownloadMgr.addListener(this);
  6835. this._listening = true;
  6836. this.updateStatus();
  6837. },
  6838. uninit: function DMP_uninit() {
  6839. if (this._listening)
  6840. gDownloadMgr.removeListener(this);
  6841. },
  6842. inited: function DMP_inited() {
  6843. return this._panel != null;
  6844. },
  6845. /**
  6846. * Update status based on the number of active and paused downloads
  6847. */
  6848. updateStatus: function DMP_updateStatus() {
  6849. if (!this.inited())
  6850. return;
  6851. let numActive = gDownloadMgr.activeDownloadCount;
  6852. // Hide the panel and reset the "last time" if there's no downloads
  6853. if (numActive == 0) {
  6854. this._panel.hidden = true;
  6855. this._lastTime = Infinity;
  6856. return;
  6857. }
  6858. // Find the download with the longest remaining time
  6859. let numPaused = 0;
  6860. let maxTime = -Infinity;
  6861. let dls = gDownloadMgr.activeDownloads;
  6862. while (dls.hasMoreElements()) {
  6863. let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
  6864. if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
  6865. // Figure out if this download takes longer
  6866. if (dl.speed > 0 && dl.size > 0)
  6867. maxTime = Math.max(maxTime, (dl.size - dl.amountTransferred) / dl.speed);
  6868. else
  6869. maxTime = -1;
  6870. }
  6871. else if (dl.state == gDownloadMgr.DOWNLOAD_PAUSED)
  6872. numPaused++;
  6873. }
  6874. // Get the remaining time string and last sec for time estimation
  6875. let timeLeft;
  6876. [timeLeft, this._lastTime] =
  6877. this.DownloadUtils.getTimeLeft(maxTime, this._lastTime);
  6878. // Figure out how many downloads are currently downloading
  6879. let numDls = numActive - numPaused;
  6880. let status = this._activeStr;
  6881. // If all downloads are paused, show the paused message instead
  6882. if (numDls == 0) {
  6883. numDls = numPaused;
  6884. status = this._pausedStr;
  6885. }
  6886. // Get the correct plural form and insert the number of downloads and time
  6887. // left message if necessary
  6888. status = PluralForm.get(numDls, status);
  6889. status = status.replace("#1", numDls);
  6890. status = status.replace("#2", timeLeft);
  6891. // Update the panel and show it
  6892. this._panel.label = status;
  6893. this._panel.hidden = false;
  6894. },
  6895. //////////////////////////////////////////////////////////////////////////////
  6896. //// nsIDownloadProgressListener
  6897. /**
  6898. * Update status for download progress changes
  6899. */
  6900. onProgressChange: function() {
  6901. this.updateStatus();
  6902. },
  6903. /**
  6904. * Update status for download state changes
  6905. */
  6906. onDownloadStateChange: function() {
  6907. this.updateStatus();
  6908. },
  6909. onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus, aDownload) {
  6910. },
  6911. onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) {
  6912. },
  6913. //////////////////////////////////////////////////////////////////////////////
  6914. //// nsISupports
  6915. QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener]),
  6916. };
  6917. function getNotificationBox(aWindow) {
  6918. var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
  6919. if (foundBrowser)
  6920. return gBrowser.getNotificationBox(foundBrowser)
  6921. return null;
  6922. };
  6923. function getTabModalPromptBox(aWindow) {
  6924. var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
  6925. if (foundBrowser)
  6926. return gBrowser.getTabModalPromptBox(foundBrowser);
  6927. return null;
  6928. };
  6929. /* DEPRECATED */
  6930. function getBrowser() gBrowser;
  6931. function getNavToolbox() gNavToolbox;
  6932. let gPrivateBrowsingUI = {
  6933. _privateBrowsingService: null,
  6934. _searchBarValue: null,
  6935. _findBarValue: null,
  6936. _inited: false,
  6937. init: function PBUI_init() {
  6938. Services.obs.addObserver(this, "private-browsing", false);
  6939. Services.obs.addObserver(this, "private-browsing-transition-complete", false);
  6940. this._privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"].
  6941. getService(Ci.nsIPrivateBrowsingService);
  6942. if (this.privateBrowsingEnabled)
  6943. this.onEnterPrivateBrowsing(true);
  6944. this._inited = true;
  6945. },
  6946. uninit: function PBUI_unint() {
  6947. if (!this._inited)
  6948. return;
  6949. Services.obs.removeObserver(this, "private-browsing");
  6950. Services.obs.removeObserver(this, "private-browsing-transition-complete");
  6951. },
  6952. get _disableUIOnToggle() {
  6953. if (this._privateBrowsingService.autoStarted)
  6954. return false;
  6955. try {
  6956. return !gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session");
  6957. }
  6958. catch (e) {
  6959. return true;
  6960. }
  6961. },
  6962. observe: function PBUI_observe(aSubject, aTopic, aData) {
  6963. if (aTopic == "private-browsing") {
  6964. if (aData == "enter")
  6965. this.onEnterPrivateBrowsing();
  6966. else if (aData == "exit")
  6967. this.onExitPrivateBrowsing();
  6968. }
  6969. else if (aTopic == "private-browsing-transition-complete") {
  6970. if (this._disableUIOnToggle) {
  6971. document.getElementById("Tools:PrivateBrowsing")
  6972. .removeAttribute("disabled");
  6973. }
  6974. }
  6975. },
  6976. _shouldEnter: function PBUI__shouldEnter() {
  6977. try {
  6978. // Never prompt if the session is not going to be closed, or if user has
  6979. // already requested not to be prompted.
  6980. if (gPrefService.getBoolPref("browser.privatebrowsing.dont_prompt_on_enter") ||
  6981. gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session"))
  6982. return true;
  6983. }
  6984. catch (ex) { }
  6985. var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
  6986. getService(Ci.nsIStringBundleService);
  6987. var pbBundle = bundleService.createBundle("chrome://browser/locale/browser.properties");
  6988. var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
  6989. var appName = brandBundle.GetStringFromName("brandShortName");
  6990. # On Mac, use the header as the title.
  6991. #ifdef XP_MACOSX
  6992. var dialogTitle = pbBundle.GetStringFromName("privateBrowsingMessageHeader");
  6993. var header = "";
  6994. #else
  6995. var dialogTitle = pbBundle.GetStringFromName("privateBrowsingDialogTitle");
  6996. var header = pbBundle.GetStringFromName("privateBrowsingMessageHeader") + "\n\n";
  6997. #endif
  6998. var message = pbBundle.formatStringFromName("privateBrowsingMessage", [appName], 1);
  6999. var ps = Services.prompt;
  7000. var flags = ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
  7001. ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
  7002. ps.BUTTON_POS_0_DEFAULT;
  7003. var neverAsk = {value:false};
  7004. var button0Title = pbBundle.GetStringFromName("privateBrowsingYesTitle");
  7005. var button1Title = pbBundle.GetStringFromName("privateBrowsingNoTitle");
  7006. var neverAskText = pbBundle.GetStringFromName("privateBrowsingNeverAsk");
  7007. var result;
  7008. var choice = ps.confirmEx(null, dialogTitle, header + message,
  7009. flags, button0Title, button1Title, null,
  7010. neverAskText, neverAsk);
  7011. switch (choice) {
  7012. case 0: // Start Private Browsing
  7013. result = true;
  7014. if (neverAsk.value)
  7015. gPrefService.setBoolPref("browser.privatebrowsing.dont_prompt_on_enter", true);
  7016. break;
  7017. case 1: // Keep
  7018. result = false;
  7019. break;
  7020. }
  7021. return result;
  7022. },
  7023. onEnterPrivateBrowsing: function PBUI_onEnterPrivateBrowsing(aOnWindowOpen) {
  7024. if (BrowserSearch.searchBar)
  7025. this._searchBarValue = BrowserSearch.searchBar.textbox.value;
  7026. if (gFindBarInitialized)
  7027. this._findBarValue = gFindBar.getElement("findbar-textbox").value;
  7028. this._setPBMenuTitle("stop");
  7029. // Disable the Clear Recent History... menu item when in PB mode
  7030. // temporary fix until bug 463607 is fixed
  7031. document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
  7032. let docElement = document.documentElement;
  7033. if (this._privateBrowsingService.autoStarted) {
  7034. // Disable the menu item in auto-start mode
  7035. document.getElementById("privateBrowsingItem")
  7036. .setAttribute("disabled", "true");
  7037. #ifdef MENUBAR_CAN_AUTOHIDE
  7038. document.getElementById("appmenu_privateBrowsing")
  7039. .setAttribute("disabled", "true");
  7040. #endif
  7041. document.getElementById("Tools:PrivateBrowsing")
  7042. .setAttribute("disabled", "true");
  7043. if (window.location.href == getBrowserURL())
  7044. docElement.setAttribute("privatebrowsingmode", "permanent");
  7045. }
  7046. else if (window.location.href == getBrowserURL()) {
  7047. // Adjust the window's title
  7048. docElement.setAttribute("title",
  7049. docElement.getAttribute("title_privatebrowsing"));
  7050. docElement.setAttribute("titlemodifier",
  7051. docElement.getAttribute("titlemodifier_privatebrowsing"));
  7052. docElement.setAttribute("privatebrowsingmode", "temporary");
  7053. gBrowser.updateTitlebar();
  7054. }
  7055. if (!aOnWindowOpen && this._disableUIOnToggle)
  7056. document.getElementById("Tools:PrivateBrowsing")
  7057. .setAttribute("disabled", "true");
  7058. },
  7059. onExitPrivateBrowsing: function PBUI_onExitPrivateBrowsing() {
  7060. if (BrowserSearch.searchBar) {
  7061. let searchBox = BrowserSearch.searchBar.textbox;
  7062. searchBox.reset();
  7063. if (this._searchBarValue) {
  7064. searchBox.value = this._searchBarValue;
  7065. this._searchBarValue = null;
  7066. }
  7067. }
  7068. if (gURLBar) {
  7069. gURLBar.editor.transactionManager.clear();
  7070. }
  7071. // Re-enable the Clear Recent History... menu item on exit of PB mode
  7072. // temporary fix until bug 463607 is fixed
  7073. document.getElementById("Tools:Sanitize").removeAttribute("disabled");
  7074. if (gFindBarInitialized) {
  7075. let findbox = gFindBar.getElement("findbar-textbox");
  7076. findbox.reset();
  7077. if (this._findBarValue) {
  7078. findbox.value = this._findBarValue;
  7079. this._findBarValue = null;
  7080. }
  7081. }
  7082. this._setPBMenuTitle("start");
  7083. if (window.location.href == getBrowserURL()) {
  7084. // Adjust the window's title
  7085. let docElement = document.documentElement;
  7086. docElement.setAttribute("title",
  7087. docElement.getAttribute("title_normal"));
  7088. docElement.setAttribute("titlemodifier",
  7089. docElement.getAttribute("titlemodifier_normal"));
  7090. docElement.removeAttribute("privatebrowsingmode");
  7091. }
  7092. // Enable the menu item in after exiting the auto-start mode
  7093. document.getElementById("privateBrowsingItem")
  7094. .removeAttribute("disabled");
  7095. #ifdef MENUBAR_CAN_AUTOHIDE
  7096. document.getElementById("appmenu_privateBrowsing")
  7097. .removeAttribute("disabled");
  7098. #endif
  7099. document.getElementById("Tools:PrivateBrowsing")
  7100. .removeAttribute("disabled");
  7101. gLastOpenDirectory.reset();
  7102. if (this._disableUIOnToggle)
  7103. document.getElementById("Tools:PrivateBrowsing")
  7104. .setAttribute("disabled", "true");
  7105. },
  7106. _setPBMenuTitle: function PBUI__setPBMenuTitle(aMode) {
  7107. let pbMenuItem = document.getElementById("privateBrowsingItem");
  7108. pbMenuItem.setAttribute("label", pbMenuItem.getAttribute(aMode + "label"));
  7109. pbMenuItem.setAttribute("accesskey", pbMenuItem.getAttribute(aMode + "accesskey"));
  7110. #ifdef MENUBAR_CAN_AUTOHIDE
  7111. let appmenupbMenuItem = document.getElementById("appmenu_privateBrowsing");
  7112. appmenupbMenuItem.setAttribute("label", appmenupbMenuItem.getAttribute(aMode + "label"));
  7113. appmenupbMenuItem.setAttribute("accesskey", appmenupbMenuItem.getAttribute(aMode + "accesskey"));
  7114. #endif
  7115. },
  7116. toggleMode: function PBUI_toggleMode() {
  7117. // prompt the users on entering the private mode, if needed
  7118. if (!this.privateBrowsingEnabled)
  7119. if (!this._shouldEnter())
  7120. return;
  7121. this._privateBrowsingService.privateBrowsingEnabled =
  7122. !this.privateBrowsingEnabled;
  7123. },
  7124. get privateBrowsingEnabled() {
  7125. return this._privateBrowsingService.privateBrowsingEnabled;
  7126. }
  7127. };
  7128. var LightWeightThemeWebInstaller = {
  7129. handleEvent: function (event) {
  7130. switch (event.type) {
  7131. case "InstallBrowserTheme":
  7132. case "PreviewBrowserTheme":
  7133. case "ResetBrowserThemePreview":
  7134. // ignore requests from background tabs
  7135. if (event.target.ownerDocument.defaultView.top != content)
  7136. return;
  7137. }
  7138. switch (event.type) {
  7139. case "InstallBrowserTheme":
  7140. this._installRequest(event);
  7141. break;
  7142. case "PreviewBrowserTheme":
  7143. this._preview(event);
  7144. break;
  7145. case "ResetBrowserThemePreview":
  7146. this._resetPreview(event);
  7147. break;
  7148. case "pagehide":
  7149. case "TabSelect":
  7150. this._resetPreview();
  7151. break;
  7152. }
  7153. },
  7154. get _manager () {
  7155. var temp = {};
  7156. Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
  7157. delete this._manager;
  7158. return this._manager = temp.LightweightThemeManager;
  7159. },
  7160. _installRequest: function (event) {
  7161. var node = event.target;
  7162. var data = this._getThemeFromNode(node);
  7163. if (!data)
  7164. return;
  7165. if (this._isAllowed(node)) {
  7166. this._install(data);
  7167. return;
  7168. }
  7169. var allowButtonText =
  7170. gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
  7171. var allowButtonAccesskey =
  7172. gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
  7173. var message =
  7174. gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
  7175. [node.ownerDocument.location.host]);
  7176. var buttons = [{
  7177. label: allowButtonText,
  7178. accessKey: allowButtonAccesskey,
  7179. callback: function () {
  7180. LightWeightThemeWebInstaller._install(data);
  7181. }
  7182. }];
  7183. this._removePreviousNotifications();
  7184. var notificationBox = gBrowser.getNotificationBox();
  7185. var notificationBar =
  7186. notificationBox.appendNotification(message, "lwtheme-install-request", "",
  7187. notificationBox.PRIORITY_INFO_MEDIUM,
  7188. buttons);
  7189. notificationBar.persistence = 1;
  7190. },
  7191. _install: function (newLWTheme) {
  7192. var previousLWTheme = this._manager.currentTheme;
  7193. var listener = {
  7194. onEnabling: function(aAddon, aRequiresRestart) {
  7195. if (!aRequiresRestart)
  7196. return;
  7197. let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
  7198. [aAddon.name], 1);
  7199. let action = {
  7200. label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
  7201. accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
  7202. callback: function () {
  7203. Application.restart();
  7204. }
  7205. };
  7206. let options = {
  7207. timeout: Date.now() + 30000
  7208. };
  7209. PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
  7210. messageString, "addons-notification-icon",
  7211. action, null, options);
  7212. },
  7213. onEnabled: function(aAddon) {
  7214. LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
  7215. }
  7216. };
  7217. AddonManager.addAddonListener(listener);
  7218. this._manager.currentTheme = newLWTheme;
  7219. AddonManager.removeAddonListener(listener);
  7220. },
  7221. _postInstallNotification: function (newTheme, previousTheme) {
  7222. function text(id) {
  7223. return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
  7224. }
  7225. var buttons = [{
  7226. label: text("undoButton"),
  7227. accessKey: text("undoButton.accesskey"),
  7228. callback: function () {
  7229. LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
  7230. LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
  7231. }
  7232. }, {
  7233. label: text("manageButton"),
  7234. accessKey: text("manageButton.accesskey"),
  7235. callback: function () {
  7236. BrowserOpenAddonsMgr("addons://list/theme");
  7237. }
  7238. }];
  7239. this._removePreviousNotifications();
  7240. var notificationBox = gBrowser.getNotificationBox();
  7241. var notificationBar =
  7242. notificationBox.appendNotification(text("message"),
  7243. "lwtheme-install-notification", "",
  7244. notificationBox.PRIORITY_INFO_MEDIUM,
  7245. buttons);
  7246. notificationBar.persistence = 1;
  7247. notificationBar.timeout = Date.now() + 20000; // 20 seconds
  7248. },
  7249. _removePreviousNotifications: function () {
  7250. var box = gBrowser.getNotificationBox();
  7251. ["lwtheme-install-request",
  7252. "lwtheme-install-notification"].forEach(function (value) {
  7253. var notification = box.getNotificationWithValue(value);
  7254. if (notification)
  7255. box.removeNotification(notification);
  7256. });
  7257. },
  7258. _previewWindow: null,
  7259. _preview: function (event) {
  7260. if (!this._isAllowed(event.target))
  7261. return;
  7262. var data = this._getThemeFromNode(event.target);
  7263. if (!data)
  7264. return;
  7265. this._resetPreview();
  7266. this._previewWindow = event.target.ownerDocument.defaultView;
  7267. this._previewWindow.addEventListener("pagehide", this, true);
  7268. gBrowser.tabContainer.addEventListener("TabSelect", this, false);
  7269. this._manager.previewTheme(data);
  7270. },
  7271. _resetPreview: function (event) {
  7272. if (!this._previewWindow ||
  7273. event && !this._isAllowed(event.target))
  7274. return;
  7275. this._previewWindow.removeEventListener("pagehide", this, true);
  7276. this._previewWindow = null;
  7277. gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
  7278. this._manager.resetPreview();
  7279. },
  7280. _isAllowed: function (node) {
  7281. var pm = Services.perms;
  7282. var uri = node.ownerDocument.documentURIObject;
  7283. return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
  7284. },
  7285. _getThemeFromNode: function (node) {
  7286. return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
  7287. node.baseURI);
  7288. }
  7289. }
  7290. /**
  7291. * Switch to a tab that has a given URI, and focusses its browser window.
  7292. * If a matching tab is in this window, it will be switched to. Otherwise, other
  7293. * windows will be searched.
  7294. *
  7295. * @param aURI
  7296. * URI to search for
  7297. * @param aOpenNew
  7298. * True to open a new tab and switch to it, if no existing tab is found.
  7299. * If no suitable window is found, a new one will be opened.
  7300. * @return True if an existing tab was found, false otherwise
  7301. */
  7302. function switchToTabHavingURI(aURI, aOpenNew) {
  7303. // This will switch to the tab in aWindow having aURI, if present.
  7304. function switchIfURIInWindow(aWindow) {
  7305. let browsers = aWindow.gBrowser.browsers;
  7306. for (let i = 0; i < browsers.length; i++) {
  7307. let browser = browsers[i];
  7308. if (browser.currentURI.equals(aURI)) {
  7309. // Focus the matching window & tab
  7310. aWindow.focus();
  7311. aWindow.gBrowser.tabContainer.selectedIndex = i;
  7312. return true;
  7313. }
  7314. }
  7315. return false;
  7316. }
  7317. // This can be passed either nsIURI or a string.
  7318. if (!(aURI instanceof Ci.nsIURI))
  7319. aURI = Services.io.newURI(aURI, null, null);
  7320. let isBrowserWindow = !!window.gBrowser;
  7321. // Prioritise this window.
  7322. if (isBrowserWindow && switchIfURIInWindow(window))
  7323. return true;
  7324. let winEnum = Services.wm.getEnumerator("navigator:browser");
  7325. while (winEnum.hasMoreElements()) {
  7326. let browserWin = winEnum.getNext();
  7327. // Skip closed (but not yet destroyed) windows,
  7328. // and the current window (which was checked earlier).
  7329. if (browserWin.closed || browserWin == window)
  7330. continue;
  7331. if (switchIfURIInWindow(browserWin))
  7332. return true;
  7333. }
  7334. // No opened tab has that url.
  7335. if (aOpenNew) {
  7336. if (isBrowserWindow && isTabEmpty(gBrowser.selectedTab))
  7337. gBrowser.selectedBrowser.loadURI(aURI.spec);
  7338. else
  7339. openUILinkIn(aURI.spec, "tab");
  7340. }
  7341. return false;
  7342. }
  7343. function restoreLastSession() {
  7344. let ss = Cc["@mozilla.org/browser/sessionstore;1"].
  7345. getService(Ci.nsISessionStore);
  7346. ss.restoreLastSession();
  7347. }
  7348. var TabContextMenu = {
  7349. contextTab: null,
  7350. updateContextMenu: function updateContextMenu(aPopupMenu) {
  7351. this.contextTab = document.popupNode.localName == "tab" ?
  7352. document.popupNode : gBrowser.selectedTab;
  7353. let disabled = gBrowser.tabs.length == 1;
  7354. // Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
  7355. document.getElementById("context_closeTab").disabled =
  7356. disabled && gBrowser.tabContainer._closeWindowWithLastTab;
  7357. var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
  7358. for (var i = 0; i < menuItems.length; i++)
  7359. menuItems[i].disabled = disabled;
  7360. disabled = gBrowser.visibleTabs.length == 1;
  7361. menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
  7362. for (var i = 0; i < menuItems.length; i++)
  7363. menuItems[i].disabled = disabled;
  7364. // Session store
  7365. document.getElementById("context_undoCloseTab").disabled =
  7366. Cc["@mozilla.org/browser/sessionstore;1"].
  7367. getService(Ci.nsISessionStore).
  7368. getClosedTabCount(window) == 0;
  7369. // Only one of pin/unpin should be visible
  7370. document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
  7371. document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;
  7372. // Disable "Close other Tabs" if there is only one unpinned tab and
  7373. // hide it when the user rightclicked on a pinned tab.
  7374. let unpinnedTabs = gBrowser.visibleTabs.length - gBrowser._numPinnedTabs;
  7375. document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1;
  7376. document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned;
  7377. // Hide "Bookmark All Tabs" for a pinned tab. Update its state if visible.
  7378. let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
  7379. bookmarkAllTabs.hidden = this.contextTab.pinned;
  7380. if (!bookmarkAllTabs.hidden)
  7381. PlacesCommandHook.updateBookmarkAllTabsCommand();
  7382. // Hide "Move to Group" if it's a pinned tab.
  7383. document.getElementById("context_tabViewMenu").hidden =
  7384. (this.contextTab.pinned || !TabView.firstUseExperienced);
  7385. }
  7386. };
  7387. XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () {
  7388. Cu.import("resource:///modules/HUDService.jsm");
  7389. try {
  7390. return HUDService.consoleUI;
  7391. }
  7392. catch (ex) {
  7393. Components.utils.reportError(ex);
  7394. }
  7395. });
  7396. // Prompt user to restart the browser in safe mode
  7397. function safeModeRestart()
  7398. {
  7399. // prompt the user to confirm
  7400. let promptTitle = gNavigatorBundle.getString("safeModeRestartPromptTitle");
  7401. let promptMessage =
  7402. gNavigatorBundle.getString("safeModeRestartPromptMessage");
  7403. let restartText = gNavigatorBundle.getString("safeModeRestartButton");
  7404. let buttonFlags = (Services.prompt.BUTTON_POS_0 *
  7405. Services.prompt.BUTTON_TITLE_IS_STRING) +
  7406. (Services.prompt.BUTTON_POS_1 *
  7407. Services.prompt.BUTTON_TITLE_CANCEL) +
  7408. Services.prompt.BUTTON_POS_0_DEFAULT;
  7409. let rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
  7410. buttonFlags, restartText, null, null,
  7411. null, {});
  7412. if (rv == 0) {
  7413. let environment = Components.classes["@mozilla.org/process/environment;1"].
  7414. getService(Components.interfaces.nsIEnvironment);
  7415. environment.set("MOZ_SAFE_MODE_RESTART", "1");
  7416. Application.restart();
  7417. }
  7418. }
  7419. /* duplicateTabIn duplicates tab in a place specified by the parameter |where|.
  7420. *
  7421. * |where| can be:
  7422. * "tab" new tab
  7423. * "tabshifted" same as "tab" but in background if default is to select new
  7424. * tabs, and vice versa
  7425. * "window" new window
  7426. *
  7427. * delta is the offset to the history entry that you want to load.
  7428. */
  7429. function duplicateTabIn(aTab, where, delta) {
  7430. let newTab = Cc['@mozilla.org/browser/sessionstore;1']
  7431. .getService(Ci.nsISessionStore)
  7432. .duplicateTab(window, aTab, delta);
  7433. var loadInBackground =
  7434. getBoolPref("browser.tabs.loadBookmarksInBackground", false);
  7435. switch (where) {
  7436. case "window":
  7437. gBrowser.hideTab(newTab);
  7438. gBrowser.replaceTabWithWindow(newTab);
  7439. break;
  7440. case "tabshifted":
  7441. loadInBackground = !loadInBackground;
  7442. // fall through
  7443. case "tab":
  7444. if (!loadInBackground)
  7445. gBrowser.selectedTab = newTab;
  7446. break;
  7447. }
  7448. }
  7449. /*
  7450. * When addons are installed/uninstalled, check and see if the number of items
  7451. * on the add-on bar changed:
  7452. * - If an add-on was installed, incrementing the count, show the bar.
  7453. * - If an add-on was uninstalled, and no more items are left, hide the bar.
  7454. */
  7455. let AddonsMgrListener = {
  7456. get addonBar() document.getElementById("addon-bar"),
  7457. get statusBar() document.getElementById("status-bar"),
  7458. getAddonBarItemCount: function() {
  7459. // Take into account the contents of the status bar shim for the count.
  7460. var itemCount = this.statusBar.childNodes.length;
  7461. var defaultOrNoninteractive = this.addonBar.getAttribute("defaultset")
  7462. .split(",")
  7463. .concat(["separator", "spacer", "spring"]);
  7464. this.addonBar.currentSet.split(",").forEach(function (item) {
  7465. if (defaultOrNoninteractive.indexOf(item) == -1)
  7466. itemCount++;
  7467. });
  7468. return itemCount;
  7469. },
  7470. onInstalling: function(aAddon) {
  7471. this.lastAddonBarCount = this.getAddonBarItemCount();
  7472. },
  7473. onInstalled: function(aAddon) {
  7474. if (this.getAddonBarItemCount() > this.lastAddonBarCount)
  7475. setToolbarVisibility(this.addonBar, true);
  7476. },
  7477. onUninstalling: function(aAddon) {
  7478. this.lastAddonBarCount = this.getAddonBarItemCount();
  7479. },
  7480. onUninstalled: function(aAddon) {
  7481. if (this.getAddonBarItemCount() == 0)
  7482. setToolbarVisibility(this.addonBar, false);
  7483. },
  7484. onEnabling: function(aAddon) this.onInstalling(),
  7485. onEnabled: function(aAddon) this.onInstalled(),
  7486. onDisabling: function(aAddon) this.onUninstalling(),
  7487. onDisabled: function(aAddon) this.onUninstalled(),
  7488. };
  7489. function toggleAddonBar() {
  7490. let addonBar = document.getElementById("addon-bar");
  7491. setToolbarVisibility(addonBar, addonBar.collapsed);
  7492. }
  7493. var Scratchpad = {
  7494. prefEnabledName: "devtools.scratchpad.enabled",
  7495. openScratchpad: function SP_openScratchpad() {
  7496. const SCRATCHPAD_WINDOW_URL = "chrome://browser/content/scratchpad.xul";
  7497. const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
  7498. return Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank",
  7499. SCRATCHPAD_WINDOW_FEATURES, null);
  7500. },
  7501. };
  7502. XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
  7503. #ifdef XP_WIN
  7504. // Only show resizers on Windows 2000 and XP
  7505. let sysInfo = Components.classes["@mozilla.org/system-info;1"]
  7506. .getService(Components.interfaces.nsIPropertyBag2);
  7507. return parseFloat(sysInfo.getProperty("version")) < 6;
  7508. #else
  7509. return false;
  7510. #endif
  7511. });