browser/base/content/browser-fullScreen.js

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

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

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

     1 # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2 # This Source Code Form is subject to the terms of the Mozilla Public
     3 # License, v. 2.0. If a copy of the MPL was not distributed with this
     4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6 var FullScreen = {
     7   _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
     8   get _fullScrToggler() {
     9     delete this._fullScrToggler;
    10     return this._fullScrToggler = document.getElementById("fullscr-toggler");
    11   },
    12   toggle: function (event) {
    13     var enterFS = window.fullScreen;
    15     // We get the fullscreen event _before_ the window transitions into or out of FS mode.
    16     if (event && event.type == "fullscreen")
    17       enterFS = !enterFS;
    19     // Toggle the View:FullScreen command, which controls elements like the
    20     // fullscreen menuitem, and menubars.
    21     let fullscreenCommand = document.getElementById("View:FullScreen");
    22     if (enterFS) {
    23       fullscreenCommand.setAttribute("checked", enterFS);
    24     } else {
    25       fullscreenCommand.removeAttribute("checked");
    26     }
    28 #ifdef XP_MACOSX
    29     // Make sure the menu items are adjusted.
    30     document.getElementById("enterFullScreenItem").hidden = enterFS;
    31     document.getElementById("exitFullScreenItem").hidden = !enterFS;
    32 #endif
    34     // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless
    35     // we're entering DOM fullscreen, in which case we should hide the toolbars.
    36     // If we're leaving fullscreen, then we'll go through the exit code below to
    37     // make sure toolbars are made visible in the case of DOM fullscreen.
    38     if (enterFS && this.useLionFullScreen) {
    39       if (document.mozFullScreen) {
    40         this.showXULChrome("toolbar", false);
    41       }
    42       else {
    43         gNavToolbox.setAttribute("inFullscreen", true);
    44         document.documentElement.setAttribute("inFullscreen", true);
    45       }
    46       return;
    47     }
    49     // show/hide menubars, toolbars (except the full screen toolbar)
    50     this.showXULChrome("toolbar", !enterFS);
    52     if (enterFS) {
    53       // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
    54       // This will help simulate the "collapse" metaphor while also requiring less code and
    55       // events than raw listening of mouse coords. We don't add the toolbar in DOM full-screen
    56       // mode, only browser full-screen mode.
    57       if (!document.mozFullScreen) {
    58         this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
    59         this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
    60       }
    61       if (gPrefService.getBoolPref("browser.fullscreen.autohide"))
    62         gBrowser.mPanelContainer.addEventListener("mousemove",
    63                                                   this._collapseCallback, false);
    65       document.addEventListener("keypress", this._keyToggleCallback, false);
    66       document.addEventListener("popupshown", this._setPopupOpen, false);
    67       document.addEventListener("popuphidden", this._setPopupOpen, false);
    68       // We don't animate the toolbar collapse if in DOM full-screen mode,
    69       // as the size of the content area would still be changing after the
    70       // mozfullscreenchange event fired, which could confuse content script.
    71       this._shouldAnimate = !document.mozFullScreen;
    72       this.mouseoverToggle(false);
    74       // Autohide prefs
    75       gPrefService.addObserver("browser.fullscreen", this, false);
    76     }
    77     else {
    78       // The user may quit fullscreen during an animation
    79       this._cancelAnimation();
    80       gNavToolbox.style.marginTop = "";
    81       if (this._isChromeCollapsed)
    82         this.mouseoverToggle(true);
    83       // This is needed if they use the context menu to quit fullscreen
    84       this._isPopupOpen = false;
    86       this.cleanup();
    87     }
    88   },
    90   exitDomFullScreen : function() {
    91     document.mozCancelFullScreen();
    92   },
    94   handleEvent: function (event) {
    95     switch (event.type) {
    96       case "activate":
    97         if (document.mozFullScreen) {
    98           this.showWarning(this.fullscreenDoc);
    99         }
   100         break;
   101       case "transitionend":
   102         if (event.propertyName == "opacity")
   103           this.cancelWarning();
   104         break;
   105     }
   106   },
   108   enterDomFullscreen : function(event) {
   109     if (!document.mozFullScreen)
   110       return;
   112     // However, if we receive a "MozEnteredDomFullScreen" event for a document
   113     // which is not a subdocument of a currently active (ie. visible) browser
   114     // or iframe, we know that we've switched to a different frame since the
   115     // request to enter full-screen was made, so we should exit full-screen
   116     // since the "full-screen document" isn't acutally visible.
   117     if (!event.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
   118                                  .getInterface(Ci.nsIWebNavigation)
   119                                  .QueryInterface(Ci.nsIDocShell).isActive) {
   120       document.mozCancelFullScreen();
   121       return;
   122     }
   124     let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   125     if (focusManager.activeWindow != window) {
   126       // The top-level window has lost focus since the request to enter
   127       // full-screen was made. Cancel full-screen.
   128       document.mozCancelFullScreen();
   129       return;
   130     }
   132     // Ensure the sidebar is hidden.
   133     if (!document.getElementById("sidebar-box").hidden)
   134       toggleSidebar();
   136     if (gFindBarInitialized)
   137       gFindBar.close();
   139     this.showWarning(event.target);
   141     // Exit DOM full-screen mode upon open, close, or change tab.
   142     gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
   143     gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
   144     gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
   146     // Add listener to detect when the fullscreen window is re-focused.
   147     // If a fullscreen window loses focus, we show a warning when the
   148     // fullscreen window is refocused.
   149     if (!this.useLionFullScreen) {
   150       window.addEventListener("activate", this);
   151     }
   153     // Cancel any "hide the toolbar" animation which is in progress, and make
   154     // the toolbar hide immediately.
   155     this._cancelAnimation();
   156     this.mouseoverToggle(false);
   158     // Remove listeners on the full-screen toggler, so that mouseover
   159     // the top of the screen will not cause the toolbar to re-appear.
   160     this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
   161     this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
   162   },
   164   cleanup: function () {
   165     if (window.fullScreen) {
   166       gBrowser.mPanelContainer.removeEventListener("mousemove",
   167                                                    this._collapseCallback, false);
   168       document.removeEventListener("keypress", this._keyToggleCallback, false);
   169       document.removeEventListener("popupshown", this._setPopupOpen, false);
   170       document.removeEventListener("popuphidden", this._setPopupOpen, false);
   171       gPrefService.removeObserver("browser.fullscreen", this);
   173       this._fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
   174       this._fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
   175       this.cancelWarning();
   176       gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
   177       gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
   178       gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
   179       if (!this.useLionFullScreen)
   180         window.removeEventListener("activate", this);
   181       this.fullscreenDoc = null;
   182     }
   183   },
   185   observe: function(aSubject, aTopic, aData)
   186   {
   187     if (aData == "browser.fullscreen.autohide") {
   188       if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
   189         gBrowser.mPanelContainer.addEventListener("mousemove",
   190                                                   this._collapseCallback, false);
   191       }
   192       else {
   193         gBrowser.mPanelContainer.removeEventListener("mousemove",
   194                                                      this._collapseCallback, false);
   195       }
   196     }
   197   },
   199   // Event callbacks
   200   _expandCallback: function()
   201   {
   202     FullScreen.mouseoverToggle(true);
   203   },
   204   _collapseCallback: function()
   205   {
   206     FullScreen.mouseoverToggle(false);
   207   },
   208   _keyToggleCallback: function(aEvent)
   209   {
   210     // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
   211     // should provide a way to collapse them too.
   212     if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
   213       FullScreen._shouldAnimate = false;
   214       FullScreen.mouseoverToggle(false, true);
   215     }
   216     // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
   217     else if (aEvent.keyCode == aEvent.DOM_VK_F6)
   218       FullScreen.mouseoverToggle(true);
   219   },
   221   // Checks whether we are allowed to collapse the chrome
   222   _isPopupOpen: false,
   223   _isChromeCollapsed: false,
   224   _safeToCollapse: function(forceHide)
   225   {
   226     if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
   227       return false;
   229     // a popup menu is open in chrome: don't collapse chrome
   230     if (!forceHide && this._isPopupOpen)
   231       return false;
   233     // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
   234     if (document.commandDispatcher.focusedElement &&
   235         document.commandDispatcher.focusedElement.ownerDocument == document &&
   236         document.commandDispatcher.focusedElement.localName == "input") {
   237       if (forceHide)
   238         // hidden textboxes that still have focus are bad bad bad
   239         document.commandDispatcher.focusedElement.blur();
   240       else
   241         return false;
   242     }
   243     return true;
   244   },
   246   _setPopupOpen: function(aEvent)
   247   {
   248     // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
   249     // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
   250     // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
   251     // toggles chrome when moving mouse to the top, it doesn't go away again.
   252     if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
   253         aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
   254       FullScreen._isPopupOpen = true;
   255     else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
   256              aEvent.target.localName != "window")
   257       FullScreen._isPopupOpen = false;
   258   },
   260   // Autohide helpers for the context menu item
   261   getAutohide: function(aItem)
   262   {
   263     aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
   264   },
   265   setAutohide: function()
   266   {
   267     gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
   268   },
   270   // Animate the toolbars disappearing
   271   _shouldAnimate: true,
   272   _isAnimating: false,
   273   _animationTimeout: 0,
   274   _animationHandle: 0,
   275   _animateUp: function() {
   276     // check again, the user may have done something before the animation was due to start
   277     if (!window.fullScreen || !this._safeToCollapse(false)) {
   278       this._isAnimating = false;
   279       this._shouldAnimate = true;
   280       return;
   281     }
   283     this._animateStartTime = window.mozAnimationStartTime;
   284     if (!this._animationHandle)
   285       this._animationHandle = window.mozRequestAnimationFrame(this);
   286   },
   288   sample: function (timeStamp) {
   289     const duration = 1500;
   290     const timePassed = timeStamp - this._animateStartTime;
   291     const pos = timePassed >= duration ? 1 :
   292                 1 - Math.pow(1 - timePassed / duration, 4);
   294     if (pos >= 1) {
   295       // We've animated enough
   296       this._cancelAnimation();
   297       gNavToolbox.style.marginTop = "";
   298       this.mouseoverToggle(false);
   299       return;
   300     }
   302     gNavToolbox.style.marginTop = (gNavToolbox.boxObject.height * pos * -1) + "px";
   303     this._animationHandle = window.mozRequestAnimationFrame(this);
   304   },
   306   _cancelAnimation: function() {
   307     window.mozCancelAnimationFrame(this._animationHandle);
   308     this._animationHandle = 0;
   309     clearTimeout(this._animationTimeout);
   310     this._isAnimating = false;
   311     this._shouldAnimate = false;
   312   },
   314   cancelWarning: function(event) {
   315     if (!this.warningBox)
   316       return;
   317     this.warningBox.removeEventListener("transitionend", this);
   318     if (this.warningFadeOutTimeout) {
   319       clearTimeout(this.warningFadeOutTimeout);
   320       this.warningFadeOutTimeout = null;
   321     }
   323     // Ensure focus switches away from the (now hidden) warning box. If the user
   324     // clicked buttons in the fullscreen key authorization UI, it would have been
   325     // focused, and any key events would be directed at the (now hidden) chrome
   326     // document instead of the target document.
   327     gBrowser.selectedBrowser.focus();
   329     this.warningBox.setAttribute("hidden", true);
   330     this.warningBox.removeAttribute("fade-warning-out");
   331     this.warningBox.removeAttribute("obscure-browser");
   332     this.warningBox = null;
   333   },
   335   setFullscreenAllowed: function(isApproved) {
   336     // The "remember decision" checkbox is hidden when showing for documents that
   337     // the permission manager can't handle (documents with URIs without a host).
   338     // We simply require those to be approved every time instead.
   339     let rememberCheckbox = document.getElementById("full-screen-remember-decision");
   340     let uri = this.fullscreenDoc.nodePrincipal.URI;
   341     if (!rememberCheckbox.hidden) {
   342       if (rememberCheckbox.checked)
   343         Services.perms.add(uri,
   344                            "fullscreen",
   345                            isApproved ? Services.perms.ALLOW_ACTION : Services.perms.DENY_ACTION,
   346                            Services.perms.EXPIRE_NEVER);
   347       else if (isApproved) {
   348         // The user has only temporarily approved fullscren for this fullscreen
   349         // session only. Add the permission (so Gecko knows to approve any further
   350         // fullscreen requests for this host in this fullscreen session) but add
   351         // a listener to revoke the permission when the chrome document exits
   352         // fullscreen.
   353         Services.perms.add(uri,
   354                            "fullscreen",
   355                            Services.perms.ALLOW_ACTION,
   356                            Services.perms.EXPIRE_SESSION);
   357         let host = uri.host;
   358         var onFullscreenchange = function onFullscreenchange(event) {
   359           if (event.target == document && document.mozFullScreenElement == null) {
   360             // The chrome document has left fullscreen. Remove the temporary permission grant.
   361             Services.perms.remove(host, "fullscreen");
   362             document.removeEventListener("mozfullscreenchange", onFullscreenchange);
   363           }
   364         }
   365         document.addEventListener("mozfullscreenchange", onFullscreenchange);
   366       }
   367     }
   368     if (this.warningBox)
   369       this.warningBox.setAttribute("fade-warning-out", "true");
   370     // If the document has been granted fullscreen, notify Gecko so it can resume
   371     // any pending pointer lock requests, otherwise exit fullscreen; the user denied
   372     // the fullscreen request.
   373     if (isApproved)
   374       Services.obs.notifyObservers(this.fullscreenDoc, "fullscreen-approved", "");
   375     else
   376       document.mozCancelFullScreen();
   377   },
   379   warningBox: null,
   380   warningFadeOutTimeout: null,
   381   fullscreenDoc: null,
   383   // Shows the fullscreen approval UI, or if the domain has already been approved
   384   // for fullscreen, shows a warning that the site has entered fullscreen for a short
   385   // duration.
   386   showWarning: function(targetDoc) {
   387     if (!document.mozFullScreen ||
   388         !gPrefService.getBoolPref("full-screen-api.approval-required"))
   389       return;
   391     // Set the strings on the fullscreen approval UI.
   392     this.fullscreenDoc = targetDoc;
   393     let uri = this.fullscreenDoc.nodePrincipal.URI;
   394     let host = null;
   395     try {
   396       host = uri.host;
   397     } catch (e) { }
   398     let hostLabel = document.getElementById("full-screen-domain-text");
   399     let rememberCheckbox = document.getElementById("full-screen-remember-decision");
   400     let isApproved = false;
   401     if (host) {
   402       // Document's principal's URI has a host. Display a warning including the hostname and
   403       // show UI to enable the user to permanently grant this host permission to enter fullscreen.
   404       let utils = {};
   405       Cu.import("resource://gre/modules/DownloadUtils.jsm", utils);
   406       let displayHost = utils.DownloadUtils.getURIHost(uri.spec)[0];
   407       let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
   409       hostLabel.textContent = bundle.formatStringFromName("fullscreen.entered", [displayHost], 1);
   410       hostLabel.removeAttribute("hidden");
   412       rememberCheckbox.label = bundle.formatStringFromName("fullscreen.rememberDecision", [displayHost], 1);
   413       rememberCheckbox.checked = false;
   414       rememberCheckbox.removeAttribute("hidden");
   416       // Note we only allow documents whose principal's URI has a host to
   417       // store permission grants.
   418       isApproved = Services.perms.testPermission(uri, "fullscreen") == Services.perms.ALLOW_ACTION;
   419     } else {
   420       hostLabel.setAttribute("hidden", "true");
   421       rememberCheckbox.setAttribute("hidden", "true");
   422     }
   424     // Note: the warning box can be non-null if the warning box from the previous request
   425     // wasn't hidden before another request was made.
   426     if (!this.warningBox) {
   427       this.warningBox = document.getElementById("full-screen-warning-container");
   428       // Add a listener to clean up state after the warning is hidden.
   429       this.warningBox.addEventListener("transitionend", this);
   430       this.warningBox.removeAttribute("hidden");
   431     } else {
   432       if (this.warningFadeOutTimeout) {
   433         clearTimeout(this.warningFadeOutTimeout);
   434         this.warningFadeOutTimeout = null;
   435       }
   436       this.warningBox.removeAttribute("fade-warning-out");
   437     }
   439     // If fullscreen mode has not yet been approved for the fullscreen
   440     // document's domain, show the approval UI and don't auto fade out the
   441     // fullscreen warning box. Otherwise, we're just notifying of entry into
   442     // fullscreen mode. Note if the resource's host is null, we must be
   443     // showing a local file or a local data URI, and we require explicit
   444     // approval every time.
   445     let authUI = document.getElementById("full-screen-approval-pane");
   446     if (isApproved) {
   447       authUI.setAttribute("hidden", "true");
   448       this.warningBox.removeAttribute("obscure-browser");
   449     } else {
   450       // Partially obscure the <browser> element underneath the approval UI.
   451       this.warningBox.setAttribute("obscure-browser", "true");
   452       authUI.removeAttribute("hidden");
   453     }
   455     // If we're not showing the fullscreen approval UI, we're just notifying the user
   456     // of the transition, so set a timeout to fade the warning out after a few moments.
   457     if (isApproved)
   458       this.warningFadeOutTimeout =
   459         setTimeout(
   460           function() {
   461             if (this.warningBox)
   462               this.warningBox.setAttribute("fade-warning-out", "true");
   463           }.bind(this),
   464           3000);
   465   },
   467   mouseoverToggle: function(aShow, forceHide)
   468   {
   469     // Don't do anything if:
   470     // a) we're already in the state we want,
   471     // b) we're animating and will become collapsed soon, or
   472     // c) we can't collapse because it would be undesirable right now
   473     if (aShow != this._isChromeCollapsed || (!aShow && this._isAnimating) ||
   474         (!aShow && !this._safeToCollapse(forceHide)))
   475       return;
   477     // browser.fullscreen.animateUp
   478     // 0 - never animate up
   479     // 1 - animate only for first collapse after entering fullscreen (default for perf's sake)
   480     // 2 - animate every time it collapses
   481     if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 0)
   482       this._shouldAnimate = false;
   484     if (!aShow && this._shouldAnimate) {
   485       this._isAnimating = true;
   486       this._shouldAnimate = false;
   487       this._animationTimeout = setTimeout(this._animateUp.bind(this), 800);
   488       return;
   489     }
   491     // The chrome is collapsed so don't spam needless mousemove events
   492     if (aShow) {
   493       gBrowser.mPanelContainer.addEventListener("mousemove",
   494                                                 this._collapseCallback, false);
   495     }
   496     else {
   497       gBrowser.mPanelContainer.removeEventListener("mousemove",
   498                                                    this._collapseCallback, false);
   499     }
   501     // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox,
   502     // so we just move it off-screen instead. See bug 430687.
   503     gNavToolbox.style.marginTop =
   504       aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px";
   506     this._fullScrToggler.collapsed = aShow;
   507     this._isChromeCollapsed = !aShow;
   508     if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2)
   509       this._shouldAnimate = true;
   510   },
   512   showXULChrome: function(aTag, aShow)
   513   {
   514     var els = document.getElementsByTagNameNS(this._XULNS, aTag);
   516     for (let el of els) {
   517       // XXX don't interfere with previously collapsed toolbars
   518       if (el.getAttribute("fullscreentoolbar") == "true") {
   519         if (!aShow) {
   520           // Give the main nav bar and the tab bar the fullscreen context menu,
   521           // otherwise remove context menu to prevent breakage
   522           el.setAttribute("saved-context", el.getAttribute("context"));
   523           if (el.id == "nav-bar" || el.id == "TabsToolbar")
   524             el.setAttribute("context", "autohide-context");
   525           else
   526             el.removeAttribute("context");
   528           // Set the inFullscreen attribute to allow specific styling
   529           // in fullscreen mode
   530           el.setAttribute("inFullscreen", true);
   531         }
   532         else {
   533           if (el.hasAttribute("saved-context")) {
   534             el.setAttribute("context", el.getAttribute("saved-context"));
   535             el.removeAttribute("saved-context");
   536           }
   537           el.removeAttribute("inFullscreen");
   538         }
   539       } else {
   540         // use moz-collapsed so it doesn't persist hidden/collapsed,
   541         // so that new windows don't have missing toolbars
   542         if (aShow)
   543           el.removeAttribute("moz-collapsed");
   544         else
   545           el.setAttribute("moz-collapsed", "true");
   546       }
   547     }
   549     if (aShow) {
   550       gNavToolbox.removeAttribute("inFullscreen");
   551       document.documentElement.removeAttribute("inFullscreen");
   552     } else {
   553       gNavToolbox.setAttribute("inFullscreen", true);
   554       document.documentElement.setAttribute("inFullscreen", true);
   555     }
   557     var fullscreenctls = document.getElementById("window-controls");
   558     var navbar = document.getElementById("nav-bar");
   559     var ctlsOnTabbar = window.toolbar.visible;
   560     if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
   561       fullscreenctls.removeAttribute("flex");
   562       document.getElementById("TabsToolbar").appendChild(fullscreenctls);
   563     }
   564     else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
   565       fullscreenctls.setAttribute("flex", "1");
   566       navbar.appendChild(fullscreenctls);
   567     }
   568     fullscreenctls.hidden = aShow;
   570     ToolbarIconColor.inferFromText();
   571   }
   572 };
   573 XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
   574   // We'll only use OS X Lion full screen if we're
   575   // * on OS X
   576   // * on Lion or higher (Darwin 11+)
   577   // * have fullscreenbutton="true"
   578 #ifdef XP_MACOSX
   579   return parseFloat(Services.sysinfo.getProperty("version")) >= 11 &&
   580          document.documentElement.getAttribute("fullscreenbutton") == "true";
   581 #else
   582   return false;
   583 #endif
   584 });

mercurial