diff options
Diffstat (limited to 'application/palemoon/base/content/browser-fullScreen.js')
-rw-r--r-- | application/palemoon/base/content/browser-fullScreen.js | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/application/palemoon/base/content/browser-fullScreen.js b/application/palemoon/base/content/browser-fullScreen.js new file mode 100644 index 0000000000..b8a29199ee --- /dev/null +++ b/application/palemoon/base/content/browser-fullScreen.js @@ -0,0 +1,607 @@ +# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +var FullScreen = { + _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", + get _fullScrToggler() { + delete this._fullScrToggler; + return this._fullScrToggler = document.getElementById("fullscr-toggler"); + }, + toggle: function (event) { + var enterFS = window.fullScreen; + + // We get the fullscreen event _before_ the window transitions into or out of FS mode. + if (event && event.type == "fullscreen") + enterFS = !enterFS; + + // Toggle the View:FullScreen command, which controls elements like the + // fullscreen menuitem, menubars, and the appmenu. + let fullscreenCommand = document.getElementById("View:FullScreen"); + if (enterFS) { + fullscreenCommand.setAttribute("checked", enterFS); + } else { + fullscreenCommand.removeAttribute("checked"); + } + +#ifdef XP_MACOSX + // Make sure the menu items are adjusted. + document.getElementById("enterFullScreenItem").hidden = enterFS; + document.getElementById("exitFullScreenItem").hidden = !enterFS; +#endif + + // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless + // we're entering DOM fullscreen, in which case we should hide the toolbars. + // If we're leaving fullscreen, then we'll go through the exit code below to + // make sure toolbars are made visible in the case of DOM fullscreen. + if (enterFS && this.useLionFullScreen) { + if (document.mozFullScreen) { + this.showXULChrome("toolbar", false); + } + else { + gNavToolbox.setAttribute("inFullscreen", true); + document.documentElement.setAttribute("inFullscreen", true); + } + return; + } + + // show/hide menubars, toolbars (except the full screen toolbar) + this.showXULChrome("toolbar", !enterFS); + + if (enterFS) { + // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance. + // This will help simulate the "collapse" metaphor while also requiring less code and + // events than raw listening of mouse coords. We don't add the toolbar in DOM full-screen + // mode, only browser full-screen mode. + if (!document.mozFullScreen) { + this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false); + this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false); + } + if (gPrefService.getBoolPref("browser.fullscreen.autohide")) + gBrowser.mPanelContainer.addEventListener("mousemove", + this._collapseCallback, false); + + document.addEventListener("keypress", this._keyToggleCallback, false); + document.addEventListener("popupshown", this._setPopupOpen, false); + document.addEventListener("popuphidden", this._setPopupOpen, false); + // We don't animate the toolbar collapse if in DOM full-screen mode, + // as the size of the content area would still be changing after the + // mozfullscreenchange event fired, which could confuse content script. + this._shouldAnimate = !document.mozFullScreen; + this.mouseoverToggle(false); + + // Autohide prefs + gPrefService.addObserver("browser.fullscreen", this, false); + } + else { + // The user may quit fullscreen during an animation + this._cancelAnimation(); + gNavToolbox.style.marginTop = ""; + if (this._isChromeCollapsed) + this.mouseoverToggle(true); + // This is needed if they use the context menu to quit fullscreen + this._isPopupOpen = false; + + document.documentElement.removeAttribute("inDOMFullscreen"); + + this.cleanup(); + } + }, + + exitDomFullScreen : function() { + document.mozCancelFullScreen(); + }, + + handleEvent: function (event) { + switch (event.type) { + case "activate": + if (document.mozFullScreen) { + this.showWarning(this.fullscreenDoc); + } + break; + case "transitionend": + if (event.propertyName == "opacity") + this.cancelWarning(); + break; + } + }, + + enterDomFullscreen : function(event) { + if (!document.mozFullScreen) + return; + + // However, if we receive a "MozEnteredDomFullScreen" event for a document + // which is not a subdocument of a currently active (ie. visible) browser + // or iframe, we know that we've switched to a different frame since the + // request to enter full-screen was made, so we should exit full-screen + // since the "full-screen document" isn't acutally visible. + if (!event.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell).isActive) { + document.mozCancelFullScreen(); + return; + } + + let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); + if (focusManager.activeWindow != window) { + // The top-level window has lost focus since the request to enter + // full-screen was made. Cancel full-screen. + document.mozCancelFullScreen(); + return; + } + + document.documentElement.setAttribute("inDOMFullscreen", true); + + if (gFindBarInitialized) + gFindBar.close(); + + this.showWarning(event.target); + + // Exit DOM full-screen mode upon open, close, or change tab. + gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen); + gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen); + gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen); + + // Add listener to detect when the fullscreen window is re-focused. + // If a fullscreen window loses focus, we show a warning when the + // fullscreen window is refocused. + if (!this.useLionFullScreen) { + window.addEventListener("activate", this); + } + + // Cancel any "hide the toolbar" animation which is in progress, and make + // the toolbar hide immediately. + this._cancelAnimation(); + this.mouseoverToggle(false); + + // Remove listeners on the full-screen toggler, so that mouseover + // the top of the screen will not cause the toolbar to re-appear. + this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false); + this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false); + }, + + cleanup: function () { + if (window.fullScreen) { + gBrowser.mPanelContainer.removeEventListener("mousemove", + this._collapseCallback, false); + document.removeEventListener("keypress", this._keyToggleCallback, false); + document.removeEventListener("popupshown", this._setPopupOpen, false); + document.removeEventListener("popuphidden", this._setPopupOpen, false); + gPrefService.removeObserver("browser.fullscreen", this); + + this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false); + this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false); + this.cancelWarning(); + gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen); + gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen); + gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen); + if (!this.useLionFullScreen) + window.removeEventListener("activate", this); + this.fullscreenDoc = null; + } + }, + + observe: function(aSubject, aTopic, aData) + { + if (aData == "browser.fullscreen.autohide") { + if (gPrefService.getBoolPref("browser.fullscreen.autohide")) { + gBrowser.mPanelContainer.addEventListener("mousemove", + this._collapseCallback, false); + } + else { + gBrowser.mPanelContainer.removeEventListener("mousemove", + this._collapseCallback, false); + } + } + }, + + // Event callbacks + _expandCallback: function() + { + FullScreen.mouseoverToggle(true); + }, + _collapseCallback: function() + { + FullScreen.mouseoverToggle(false); + }, + _keyToggleCallback: function(aEvent) + { + // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we + // should provide a way to collapse them too. + if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) { + FullScreen._shouldAnimate = false; + FullScreen.mouseoverToggle(false, true); + } + // F6 is another shortcut to the address bar, but its not covered in OpenLocation() + else if (aEvent.keyCode == aEvent.DOM_VK_F6) + FullScreen.mouseoverToggle(true); + }, + + // Checks whether we are allowed to collapse the chrome + _isPopupOpen: false, + _isChromeCollapsed: false, + _safeToCollapse: function(forceHide) + { + if (!gPrefService.getBoolPref("browser.fullscreen.autohide")) + return false; + + // a popup menu is open in chrome: don't collapse chrome + if (!forceHide && this._isPopupOpen) + return false; + + // a textbox in chrome is focused (location bar anyone?): don't collapse chrome + if (document.commandDispatcher.focusedElement && + document.commandDispatcher.focusedElement.ownerDocument == document && + document.commandDispatcher.focusedElement.localName == "input") { + if (forceHide) + // hidden textboxes that still have focus are bad bad bad + document.commandDispatcher.focusedElement.blur(); + else + return false; + } + return true; + }, + + _setPopupOpen: function(aEvent) + { + // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed. + // Otherwise, they would not affect chrome and the user would expect the chrome to go away. + // e.g. we wouldn't want the autoscroll icon firing this event, so when the user + // toggles chrome when moving mouse to the top, it doesn't go away again. + if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed && + aEvent.target.localName != "tooltip" && aEvent.target.localName != "window") + FullScreen._isPopupOpen = true; + else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" && + aEvent.target.localName != "window") + FullScreen._isPopupOpen = false; + }, + + // Autohide helpers for the context menu item + getAutohide: function(aItem) + { + aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide")); + }, + setAutohide: function() + { + gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide")); + }, + + // Animate the toolbars disappearing + _shouldAnimate: true, + _isAnimating: false, + _animationTimeout: 0, + _animationHandle: 0, + _animateUp: function() { + // check again, the user may have done something before the animation was due to start + if (!window.fullScreen || !this._safeToCollapse(false)) { + this._isAnimating = false; + this._shouldAnimate = true; + return; + } + + this._animateStartTime = window.mozAnimationStartTime; + if (!this._animationHandle) + this._animationHandle = window.mozRequestAnimationFrame(this); + }, + + sample: function (timeStamp) { + const duration = 1500; + const timePassed = timeStamp - this._animateStartTime; + const pos = timePassed >= duration ? 1 : + 1 - Math.pow(1 - timePassed / duration, 4); + + if (pos >= 1) { + // We've animated enough + this._cancelAnimation(); + gNavToolbox.style.marginTop = ""; + this.mouseoverToggle(false); + return; + } + + gNavToolbox.style.marginTop = (gNavToolbox.boxObject.height * pos * -1) + "px"; + this._animationHandle = window.mozRequestAnimationFrame(this); + }, + + _cancelAnimation: function() { + window.mozCancelAnimationFrame(this._animationHandle); + this._animationHandle = 0; + clearTimeout(this._animationTimeout); + this._isAnimating = false; + this._shouldAnimate = false; + }, + + cancelWarning: function(event) { + if (!this.warningBox) + return; + this.warningBox.removeEventListener("transitionend", this); + if (this.warningFadeOutTimeout) { + clearTimeout(this.warningFadeOutTimeout); + this.warningFadeOutTimeout = null; + } + + // Ensure focus switches away from the (now hidden) warning box. If the user + // clicked buttons in the fullscreen key authorization UI, it would have been + // focused, and any key events would be directed at the (now hidden) chrome + // document instead of the target document. + gBrowser.selectedBrowser.focus(); + + this.warningBox.setAttribute("hidden", true); + this.warningBox.removeAttribute("fade-warning-out"); + this.warningBox.removeAttribute("obscure-browser"); + this.warningBox = null; + }, + + setFullscreenAllowed: function(isApproved) { + // The "remember decision" checkbox is hidden when showing for documents that + // the permission manager can't handle (documents with URIs without a host). + // We simply require those to be approved every time instead. + let rememberCheckbox = document.getElementById("full-screen-remember-decision"); + let uri = this.fullscreenDoc.nodePrincipal.URI; + if (!rememberCheckbox.hidden) { + if (rememberCheckbox.checked) + Services.perms.add(uri, + "fullscreen", + isApproved ? Services.perms.ALLOW_ACTION : Services.perms.DENY_ACTION, + Services.perms.EXPIRE_NEVER); + else if (isApproved) { + // The user has only temporarily approved fullscren for this fullscreen + // session only. Add the permission (so Goanna knows to approve any further + // fullscreen requests for this host in this fullscreen session) but add + // a listener to revoke the permission when the chrome document exits + // fullscreen. + Services.perms.add(uri, + "fullscreen", + Services.perms.ALLOW_ACTION, + Services.perms.EXPIRE_SESSION); + let host = uri.host; + var onFullscreenchange = function onFullscreenchange(event) { + if (event.target == document && document.mozFullScreenElement == null) { + // The chrome document has left fullscreen. Remove the temporary permission grant. + Services.perms.remove(host, "fullscreen"); + document.removeEventListener("mozfullscreenchange", onFullscreenchange); + } + } + document.addEventListener("mozfullscreenchange", onFullscreenchange); + } + } + if (this.warningBox) + this.warningBox.setAttribute("fade-warning-out", "true"); + // If the document has been granted fullscreen, notify Goanna so it can resume + // any pending pointer lock requests, otherwise exit fullscreen; the user denied + // the fullscreen request. + if (isApproved) + Services.obs.notifyObservers(this.fullscreenDoc, "fullscreen-approved", ""); + else + document.mozCancelFullScreen(); + }, + + warningBox: null, + warningFadeOutTimeout: null, + fullscreenDoc: null, + + // Shows the fullscreen approval UI, or if the domain has already been approved + // for fullscreen, shows a warning that the site has entered fullscreen for a short + // duration. + showWarning: function(targetDoc) { + if (!document.mozFullScreen || + !gPrefService.getBoolPref("full-screen-api.approval-required")) + return; + + // Set the strings on the fullscreen approval UI. + this.fullscreenDoc = targetDoc; + let uri = this.fullscreenDoc.nodePrincipal.URI; + let host = null; + try { + host = uri.host; + } catch (e) { } + let hostLabel = document.getElementById("full-screen-domain-text"); + let rememberCheckbox = document.getElementById("full-screen-remember-decision"); + let isApproved = false; + if (host) { + // Document's principal's URI has a host. Display a warning including the hostname and + // show UI to enable the user to permanently grant this host permission to enter fullscreen. + let utils = {}; + Cu.import("resource://gre/modules/DownloadUtils.jsm", utils); + let displayHost = utils.DownloadUtils.getURIHost(uri.spec)[0]; + let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); + + hostLabel.textContent = bundle.formatStringFromName("fullscreen.entered", [displayHost], 1); + hostLabel.removeAttribute("hidden"); + + rememberCheckbox.label = bundle.formatStringFromName("fullscreen.rememberDecision", [displayHost], 1); + rememberCheckbox.checked = false; + rememberCheckbox.removeAttribute("hidden"); + + // Note we only allow documents whose principal's URI has a host to + // store permission grants. + isApproved = Services.perms.testPermission(uri, "fullscreen") == Services.perms.ALLOW_ACTION; + } else { + hostLabel.setAttribute("hidden", "true"); + rememberCheckbox.setAttribute("hidden", "true"); + } + + // Note: the warning box can be non-null if the warning box from the previous request + // wasn't hidden before another request was made. + if (!this.warningBox) { + this.warningBox = document.getElementById("full-screen-warning-container"); + // Add a listener to clean up state after the warning is hidden. + this.warningBox.addEventListener("transitionend", this); + this.warningBox.removeAttribute("hidden"); + } else { + if (this.warningFadeOutTimeout) { + clearTimeout(this.warningFadeOutTimeout); + this.warningFadeOutTimeout = null; + } + this.warningBox.removeAttribute("fade-warning-out"); + } + + // If fullscreen mode has not yet been approved for the fullscreen + // document's domain, show the approval UI and don't auto fade out the + // fullscreen warning box. Otherwise, we're just notifying of entry into + // fullscreen mode. Note if the resource's host is null, we must be + // showing a local file or a local data URI, and we require explicit + // approval every time. + let authUI = document.getElementById("full-screen-approval-pane"); + if (isApproved) { + authUI.setAttribute("hidden", "true"); + this.warningBox.removeAttribute("obscure-browser"); + } else { + // Partially obscure the <browser> element underneath the approval UI. + this.warningBox.setAttribute("obscure-browser", "true"); + authUI.removeAttribute("hidden"); + } + + // If we're not showing the fullscreen approval UI, we're just notifying the user + // of the transition, so set a timeout to fade the warning out after a few moments. + if (isApproved) + this.warningFadeOutTimeout = + setTimeout( + function() { + if (this.warningBox) + this.warningBox.setAttribute("fade-warning-out", "true"); + }.bind(this), + 3000); + }, + + mouseoverToggle: function(aShow, forceHide) + { + // Don't do anything if: + // a) we're already in the state we want, + // b) we're animating and will become collapsed soon, or + // c) we can't collapse because it would be undesirable right now + if (aShow != this._isChromeCollapsed || (!aShow && this._isAnimating) || + (!aShow && !this._safeToCollapse(forceHide))) + return; + + // browser.fullscreen.animateUp + // 0 - never animate up + // 1 - animate only for first collapse after entering fullscreen (default for perf's sake) + // 2 - animate every time it collapses + if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 0) + this._shouldAnimate = false; + + if (!aShow && this._shouldAnimate) { + this._isAnimating = true; + this._shouldAnimate = false; + this._animationTimeout = setTimeout(this._animateUp.bind(this), 800); + return; + } + + // The chrome is collapsed so don't spam needless mousemove events + if (aShow) { + gBrowser.mPanelContainer.addEventListener("mousemove", + this._collapseCallback, false); + } + else { + gBrowser.mPanelContainer.removeEventListener("mousemove", + this._collapseCallback, false); + } + + // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox, + // so we just move it off-screen instead. See bug 430687. + gNavToolbox.style.marginTop = + aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px"; + + this._fullScrToggler.collapsed = aShow; + this._isChromeCollapsed = !aShow; + if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2) + this._shouldAnimate = true; + }, + + showXULChrome: function(aTag, aShow) + { + var els = document.getElementsByTagNameNS(this._XULNS, aTag); + + for (let el of els) { + // XXX don't interfere with previously collapsed toolbars + if (el.getAttribute("fullscreentoolbar") == "true") { + if (!aShow) { + + var toolbarMode = el.getAttribute("mode"); + if (toolbarMode != "text") { + el.setAttribute("saved-mode", toolbarMode); + el.setAttribute("saved-iconsize", el.getAttribute("iconsize")); + el.setAttribute("mode", "icons"); + el.setAttribute("iconsize", "small"); + } + + // Give the main nav bar and the tab bar the fullscreen context menu, + // otherwise remove context menu to prevent breakage + el.setAttribute("saved-context", el.getAttribute("context")); + if (el.id == "nav-bar" || el.id == "TabsToolbar") + el.setAttribute("context", "autohide-context"); + else + el.removeAttribute("context"); + + // Set the inFullscreen attribute to allow specific styling + // in fullscreen mode + el.setAttribute("inFullscreen", true); + } + else { + var restoreAttr = function restoreAttr(attrName) { + var savedAttr = "saved-" + attrName; + if (el.hasAttribute(savedAttr)) { + el.setAttribute(attrName, el.getAttribute(savedAttr)); + el.removeAttribute(savedAttr); + } + } + + restoreAttr("mode"); + restoreAttr("iconsize"); + restoreAttr("context"); + + el.removeAttribute("inFullscreen"); + } + } else { + // use moz-collapsed so it doesn't persist hidden/collapsed, + // so that new windows don't have missing toolbars + if (aShow) + el.removeAttribute("moz-collapsed"); + else + el.setAttribute("moz-collapsed", "true"); + } + } + + if (aShow) { + gNavToolbox.removeAttribute("inFullscreen"); + document.documentElement.removeAttribute("inFullscreen"); + } else { + gNavToolbox.setAttribute("inFullscreen", true); + document.documentElement.setAttribute("inFullscreen", true); + } + + // In tabs-on-top mode, move window controls to the tab bar, + // and in tabs-on-bottom mode, move them back to the navigation toolbar. + // When there is a chance the tab bar may be collapsed, put window + // controls on nav bar. + var fullscreenctls = document.getElementById("window-controls"); + var navbar = document.getElementById("nav-bar"); + var ctlsOnTabbar = window.toolbar.visible && + (navbar.collapsed || (TabsOnTop.enabled && + !gPrefService.getBoolPref("browser.tabs.autoHide"))); + if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) { + fullscreenctls.removeAttribute("flex"); + document.getElementById("TabsToolbar").appendChild(fullscreenctls); + } + else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) { + fullscreenctls.setAttribute("flex", "1"); + navbar.appendChild(fullscreenctls); + } + fullscreenctls.hidden = aShow; + + ToolbarIconColor.inferFromText(); + } +}; +XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() { + // We'll only use OS X Lion full screen if we're + // * on OS X + // * on Lion or higher (Darwin 11+) + // * have fullscreenbutton="true" +#ifdef XP_MACOSX + return parseFloat(Services.sysinfo.getProperty("version")) >= 11 && + document.documentElement.getAttribute("fullscreenbutton") == "true"; +#else + return false; +#endif +}); |