browser-fullScreen.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. # This Source Code Form is subject to the terms of the Mozilla Public
  3. # License, v. 2.0. If a copy of the MPL was not distributed with this
  4. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  5. var FullScreen = {
  6. _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  7. toggle: function () {
  8. var enterFS = window.fullScreen;
  9. // Toggle the View:FullScreen command, which controls elements like the
  10. // fullscreen menuitem, menubars, and the appmenu.
  11. let fullscreenCommand = document.getElementById("View:FullScreen");
  12. if (enterFS) {
  13. fullscreenCommand.setAttribute("checked", enterFS);
  14. } else {
  15. fullscreenCommand.removeAttribute("checked");
  16. }
  17. if (!this._fullScrToggler) {
  18. this._fullScrToggler = document.getElementById("fullscr-toggler");
  19. this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
  20. this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
  21. }
  22. // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless
  23. // we're entering DOM fullscreen, in which case we should hide the toolbars.
  24. // If we're leaving fullscreen, then we'll go through the exit code below to
  25. // make sure toolbars are made visible in the case of DOM fullscreen.
  26. if (enterFS && this.useLionFullScreen) {
  27. if (document.mozFullScreen) {
  28. this.showXULChrome("toolbar", false);
  29. } else {
  30. gNavToolbox.setAttribute("inFullscreen", true);
  31. document.documentElement.setAttribute("inFullscreen", true);
  32. }
  33. return;
  34. }
  35. // show/hide menubars, toolbars (except the full screen toolbar)
  36. this.showXULChrome("toolbar", !enterFS);
  37. if (enterFS) {
  38. document.addEventListener("keypress", this._keyToggleCallback, false);
  39. document.addEventListener("popupshown", this._setPopupOpen, false);
  40. document.addEventListener("popuphidden", this._setPopupOpen, false);
  41. this._shouldAnimate = true;
  42. if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
  43. gBrowser.mPanelContainer.addEventListener("mousemove",
  44. this._collapseCallback, false);
  45. }
  46. // We don't animate the toolbar collapse if in DOM full-screen mode,
  47. // as the size of the content area would still be changing after the
  48. // mozfullscreenchange event fired, which could confuse content script.
  49. this.hideNavToolbox(document.mozFullScreen);
  50. } else {
  51. this.showNavToolbox(false);
  52. // This is needed if they use the context menu to quit fullscreen
  53. this._isPopupOpen = false;
  54. document.documentElement.removeAttribute("inDOMFullscreen");
  55. this.cleanup();
  56. }
  57. },
  58. exitDomFullScreen : function() {
  59. document.mozCancelFullScreen();
  60. },
  61. handleEvent: function (event) {
  62. switch (event.type) {
  63. case "activate":
  64. if (document.mozFullScreen) {
  65. this.showWarning(this.fullscreenDoc);
  66. }
  67. break;
  68. case "transitionend":
  69. if (event.propertyName == "opacity") {
  70. this.cancelWarning();
  71. }
  72. break;
  73. }
  74. },
  75. enterDomFullscreen : function(event) {
  76. if (!document.mozFullScreen) {
  77. return;
  78. }
  79. // However, if we receive a "MozDOMFullscreen:NewOrigin" event for a document
  80. // which is not a subdocument of a currently active (ie. visible) browser
  81. // or iframe, we know that we've switched to a different frame since the
  82. // request to enter full-screen was made, so we should exit full-screen
  83. // since the "full-screen document" isn't acutally visible.
  84. if (!event.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
  85. .getInterface(Ci.nsIWebNavigation)
  86. .QueryInterface(Ci.nsIDocShell).isActive) {
  87. document.mozCancelFullScreen();
  88. return;
  89. }
  90. let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
  91. if (focusManager.activeWindow != window) {
  92. // The top-level window has lost focus since the request to enter
  93. // full-screen was made. Cancel full-screen.
  94. document.mozCancelFullScreen();
  95. return;
  96. }
  97. document.documentElement.setAttribute("inDOMFullscreen", true);
  98. if (gFindBarInitialized) {
  99. gFindBar.close();
  100. }
  101. this.showWarning(event.target);
  102. // Exit DOM full-screen mode upon open, close, or change tab.
  103. gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
  104. gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
  105. gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
  106. // Add listener to detect when the fullscreen window is re-focused.
  107. // If a fullscreen window loses focus, we show a warning when the
  108. // fullscreen window is refocused.
  109. if (!this.useLionFullScreen) {
  110. window.addEventListener("activate", this);
  111. }
  112. // Cancel any "hide the toolbar" animation which is in progress, and make
  113. // the toolbar hide immediately.
  114. this.hideNavToolbox(true);
  115. },
  116. cleanup: function () {
  117. if (!window.fullScreen) {
  118. gBrowser.mPanelContainer.removeEventListener("mousemove",
  119. this._collapseCallback, false);
  120. document.removeEventListener("keypress", this._keyToggleCallback, false);
  121. document.removeEventListener("popupshown", this._setPopupOpen, false);
  122. document.removeEventListener("popuphidden", this._setPopupOpen, false);
  123. this.cancelWarning();
  124. gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
  125. gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
  126. gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
  127. if (!this.useLionFullScreen) {
  128. window.removeEventListener("activate", this);
  129. }
  130. this.fullscreenDoc = null;
  131. }
  132. },
  133. // Event callbacks
  134. _expandCallback: function() {
  135. FullScreen.showNavToolbox();
  136. },
  137. _collapseCallback: function() {
  138. FullScreen.hideNavToolbox();
  139. },
  140. _keyToggleCallback: function(aEvent) {
  141. // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
  142. // should provide a way to collapse them too.
  143. if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
  144. FullScreen.hideNavToolbox(true);
  145. } else if (aEvent.keyCode == aEvent.DOM_VK_F6) {
  146. // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
  147. FullScreen.showNavToolbox();
  148. }
  149. },
  150. // Checks whether we are allowed to collapse the chrome
  151. _isPopupOpen: false,
  152. _isChromeCollapsed: false,
  153. _safeToCollapse: function(forceHide) {
  154. if (!gPrefService.getBoolPref("browser.fullscreen.autohide")) {
  155. return false;
  156. }
  157. // a popup menu is open in chrome: don't collapse chrome
  158. if (!forceHide && this._isPopupOpen) {
  159. return false;
  160. }
  161. // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
  162. if (document.commandDispatcher.focusedElement &&
  163. document.commandDispatcher.focusedElement.ownerDocument == document &&
  164. document.commandDispatcher.focusedElement.localName == "input") {
  165. if (forceHide) {
  166. // hidden textboxes that still have focus are bad bad bad
  167. document.commandDispatcher.focusedElement.blur();
  168. } else {
  169. return false;
  170. }
  171. }
  172. return true;
  173. },
  174. _setPopupOpen: function(aEvent)
  175. {
  176. // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
  177. // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
  178. // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
  179. // toggles chrome when moving mouse to the top, it doesn't go away again.
  180. if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
  181. aEvent.target.localName != "tooltip" && aEvent.target.localName != "window") {
  182. FullScreen._isPopupOpen = true;
  183. } else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
  184. aEvent.target.localName != "window") {
  185. FullScreen._isPopupOpen = false;
  186. }
  187. },
  188. // Autohide helpers for the context menu item
  189. getAutohide: function(aItem) {
  190. aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
  191. },
  192. setAutohide: function() {
  193. gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
  194. },
  195. // Animate the toolbars disappearing
  196. _shouldAnimate: true,
  197. cancelWarning: function(event) {
  198. if (!this.warningBox) {
  199. return;
  200. }
  201. this.warningBox.removeEventListener("transitionend", this);
  202. if (this.warningFadeOutTimeout) {
  203. clearTimeout(this.warningFadeOutTimeout);
  204. this.warningFadeOutTimeout = null;
  205. }
  206. // Ensure focus switches away from the (now hidden) warning box. If the user
  207. // clicked buttons in the fullscreen key authorization UI, it would have been
  208. // focused, and any key events would be directed at the (now hidden) chrome
  209. // document instead of the target document.
  210. gBrowser.selectedBrowser.focus();
  211. this.warningBox.setAttribute("hidden", true);
  212. this.warningBox.removeAttribute("fade-warning-out");
  213. this.warningBox = null;
  214. },
  215. warningBox: null,
  216. warningFadeOutTimeout: null,
  217. fullscreenDoc: null,
  218. // Shows a warning that the site has entered fullscreen for a short duration.
  219. showWarning: function(targetDoc) {
  220. let timeout = gPrefService.getIntPref("full-screen-api.warning.timeout");
  221. if (!document.mozFullScreen || timeout <= 0) {
  222. return;
  223. }
  224. // Set the strings on the fullscreen warning UI.
  225. this.fullscreenDoc = targetDoc;
  226. let uri = this.fullscreenDoc.nodePrincipal.URI;
  227. let host = null;
  228. try {
  229. host = uri.host;
  230. } catch(e) {}
  231. let hostLabel = document.getElementById("full-screen-domain-text");
  232. if (host) {
  233. // Document's principal's URI has a host. Display a warning including the hostname.
  234. let utils = {};
  235. Cu.import("resource://gre/modules/DownloadUtils.jsm", utils);
  236. let displayHost = utils.DownloadUtils.getURIHost(uri.spec)[0];
  237. let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
  238. hostLabel.textContent = bundle.formatStringFromName("fullscreen.entered", [displayHost], 1);
  239. hostLabel.removeAttribute("hidden");
  240. } else {
  241. hostLabel.setAttribute("hidden", "true");
  242. }
  243. // Note: the warning box can be non-null if the warning box from the previous request
  244. // wasn't hidden before another request was made.
  245. if (!this.warningBox) {
  246. this.warningBox = document.getElementById("full-screen-warning-container");
  247. // Add a listener to clean up state after the warning is hidden.
  248. this.warningBox.addEventListener("transitionend", this);
  249. this.warningBox.removeAttribute("hidden");
  250. } else {
  251. if (this.warningFadeOutTimeout) {
  252. clearTimeout(this.warningFadeOutTimeout);
  253. this.warningFadeOutTimeout = null;
  254. }
  255. this.warningBox.removeAttribute("fade-warning-out");
  256. }
  257. // Set a timeout to fade the warning out after a few moments.
  258. this.warningFadeOutTimeout = setTimeout(() => {
  259. if (this.warningBox) {
  260. this.warningBox.setAttribute("fade-warning-out", "true");
  261. }
  262. }, timeout);
  263. },
  264. showNavToolbox: function(trackMouse = true) {
  265. this._fullScrToggler.hidden = true;
  266. gNavToolbox.removeAttribute("fullscreenShouldAnimate");
  267. gNavToolbox.style.marginTop = "";
  268. if (!this._isChromeCollapsed) {
  269. return;
  270. }
  271. // Track whether mouse is near the toolbox
  272. this._isChromeCollapsed = false;
  273. if (trackMouse) {
  274. gBrowser.mPanelContainer.addEventListener("mousemove",
  275. this._collapseCallback, false);
  276. }
  277. },
  278. hideNavToolbox: function(forceHide = false) {
  279. this._fullScrToggler.hidden = document.mozFullScreen;
  280. if (this._isChromeCollapsed) {
  281. if (forceHide) {
  282. gNavToolbox.removeAttribute("fullscreenShouldAnimate");
  283. }
  284. return;
  285. }
  286. if (!this._safeToCollapse(forceHide)) {
  287. this._fullScrToggler.hidden = true;
  288. return;
  289. }
  290. // browser.fullscreen.animateUp
  291. // 0 - never animate up
  292. // 1 - animate only for first collapse after entering fullscreen (default for perf's sake)
  293. // 2 - animate every time it collapses
  294. let animateUp = gPrefService.getIntPref("browser.fullscreen.animateUp");
  295. if (animateUp == 0) {
  296. this._shouldAnimate = false;
  297. } else if (animateUp == 2) {
  298. this._shouldAnimate = true;
  299. }
  300. if (this._shouldAnimate && !forceHide) {
  301. gNavToolbox.setAttribute("fullscreenShouldAnimate", true);
  302. this._shouldAnimate = false;
  303. // Hide the fullscreen toggler until the transition ends.
  304. let listener = () => {
  305. gNavToolbox.removeEventListener("transitionend", listener, true);
  306. if (this._isChromeCollapsed)
  307. this._fullScrToggler.hidden = false;
  308. };
  309. gNavToolbox.addEventListener("transitionend", listener, true);
  310. this._fullScrToggler.hidden = true;
  311. }
  312. gNavToolbox.style.marginTop =
  313. -gNavToolbox.getBoundingClientRect().height + "px";
  314. this._isChromeCollapsed = true;
  315. gBrowser.mPanelContainer.removeEventListener("mousemove",
  316. this._collapseCallback, false);
  317. },
  318. showXULChrome: function(aTag, aShow)
  319. {
  320. var els = document.getElementsByTagNameNS(this._XULNS, aTag);
  321. for (let el of els) {
  322. // XXX don't interfere with previously collapsed toolbars
  323. if (el.getAttribute("fullscreentoolbar") == "true") {
  324. if (!aShow) {
  325. var toolbarMode = el.getAttribute("mode");
  326. if (toolbarMode != "text") {
  327. el.setAttribute("saved-mode", toolbarMode);
  328. el.setAttribute("saved-iconsize", el.getAttribute("iconsize"));
  329. el.setAttribute("mode", "icons");
  330. el.setAttribute("iconsize", "small");
  331. }
  332. // Give the main nav bar and the tab bar the fullscreen context menu,
  333. // otherwise remove context menu to prevent breakage
  334. el.setAttribute("saved-context", el.getAttribute("context"));
  335. if (el.id == "nav-bar" || el.id == "TabsToolbar") {
  336. el.setAttribute("context", "autohide-context");
  337. } else {
  338. el.removeAttribute("context");
  339. }
  340. // Set the inFullscreen attribute to allow specific styling
  341. // in fullscreen mode
  342. el.setAttribute("inFullscreen", true);
  343. } else {
  344. var restoreAttr = function restoreAttr(attrName) {
  345. var savedAttr = "saved-" + attrName;
  346. if (el.hasAttribute(savedAttr)) {
  347. el.setAttribute(attrName, el.getAttribute(savedAttr));
  348. el.removeAttribute(savedAttr);
  349. }
  350. }
  351. restoreAttr("mode");
  352. restoreAttr("iconsize");
  353. restoreAttr("context");
  354. el.removeAttribute("inFullscreen");
  355. }
  356. } else {
  357. // use moz-collapsed so it doesn't persist hidden/collapsed,
  358. // so that new windows don't have missing toolbars
  359. if (aShow) {
  360. el.removeAttribute("moz-collapsed");
  361. } else {
  362. el.setAttribute("moz-collapsed", "true");
  363. }
  364. }
  365. }
  366. if (aShow) {
  367. gNavToolbox.removeAttribute("inFullscreen");
  368. document.documentElement.removeAttribute("inFullscreen");
  369. } else {
  370. gNavToolbox.setAttribute("inFullscreen", true);
  371. document.documentElement.setAttribute("inFullscreen", true);
  372. }
  373. // In tabs-on-top mode, move window controls to the tab bar,
  374. // and in tabs-on-bottom mode, move them back to the navigation toolbar.
  375. // When there is a chance the tab bar may be collapsed, put window
  376. // controls on nav bar.
  377. var fullscreenctls = document.getElementById("window-controls");
  378. var navbar = document.getElementById("nav-bar");
  379. var ctlsOnTabbar = window.toolbar.visible &&
  380. (navbar.collapsed ||
  381. (TabsOnTop.enabled &&
  382. !gPrefService.getBoolPref("browser.tabs.autoHide")));
  383. if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
  384. fullscreenctls.removeAttribute("flex");
  385. document.getElementById("TabsToolbar").appendChild(fullscreenctls);
  386. } else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
  387. fullscreenctls.setAttribute("flex", "1");
  388. navbar.appendChild(fullscreenctls);
  389. }
  390. fullscreenctls.hidden = aShow;
  391. ToolbarIconColor.inferFromText();
  392. }
  393. };
  394. XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
  395. // We'll only use OS X Lion full screen if we're
  396. // * on OS X
  397. // * on Lion or higher (Darwin 11+)
  398. // * have fullscreenbutton="true"
  399. return false;
  400. });