Bug 158968 - Implement kiosk mode. r=Gijs
authorMichael Kaply <mozilla@kaply.com>
Fri, 04 Oct 2019 19:47:56 +0000
changeset 496387 23839eda99ddf769769fefe89a12bb35ede464fb
parent 496386 e4085fae0f8b77dcfdec85d52d518624f8b6593d
child 496388 b15c0747d702c0f883099ec1a7e1b5402f621fc5
push id36652
push userbtara@mozilla.com
push dateSat, 05 Oct 2019 09:47:12 +0000
treeherdermozilla-central@30154d163aca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs158968
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 158968 - Implement kiosk mode. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D41848
browser/base/content/browser-fullScreenAndPointerLock.js
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/components/BrowserContentHandler.jsm
browser/components/enterprisepolicies/docs/index.rst
browser/components/enterprisepolicies/moz.build
browser/components/nsIBrowserHandler.idl
browser/docs/index.rst
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -3,16 +3,27 @@
  * 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/. */
 
 // This file is loaded into the browser window scope.
 /* eslint-env mozilla/browser-window */
 
 ChromeUtils.import("resource:///modules/PermissionUI.jsm", this);
 
+var { XPCOMUtils } = ChromeUtils.import(
+  "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+XPCOMUtils.defineLazyServiceGetter(
+  this,
+  "BrowserHandler",
+  "@mozilla.org/browser/clh;1",
+  "nsIBrowserHandler"
+);
+
 var PointerlockFsWarning = {
   _element: null,
   _origin: null,
 
   /**
    * Timeout object for managing timeout request. If it is started when
    * the previous call hasn't finished, it would automatically cancelled
    * the previous one.
@@ -644,16 +655,19 @@ var FullScreen = {
       "browser.fullscreen.autohide",
       !Services.prefs.getBoolPref("browser.fullscreen.autohide")
     );
     // Try again to hide toolbar when we change the pref.
     FullScreen.hideNavToolbox(true);
   },
 
   showNavToolbox(trackMouse = true) {
+    if (BrowserHandler.kiosk) {
+      return;
+    }
     this._fullScrToggler.hidden = true;
     gNavToolbox.removeAttribute("fullscreenShouldAnimate");
     gNavToolbox.style.marginTop = "";
 
     if (!this._isChromeCollapsed) {
       return;
     }
 
@@ -685,21 +699,23 @@ var FullScreen = {
       return;
     }
     // On OS X Lion we don't want to hide toolbars.
     if (this.useLionFullScreen) {
       return;
     }
 
     // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
+    // unless we are kiosk mode
     let focused = document.commandDispatcher.focusedElement;
     if (
       focused &&
       focused.ownerDocument == document &&
-      focused.localName == "input"
+      focused.localName == "input" &&
+      !BrowserHandler.kiosk
     ) {
       // But try collapse the chrome again when anything happens which can make
       // it lose the focus. We cannot listen on "blur" event on focused here
       // because that event can be triggered by "mousedown", and hiding chrome
       // would cause the content to move. This combination may split a single
       // click into two actionless halves.
       let retryHideNavToolbox = () => {
         // Wait for at least a frame to give it a chance to be passed down to
@@ -720,17 +736,18 @@ var FullScreen = {
       window.addEventListener("click", retryHideNavToolbox);
       return;
     }
 
     this._fullScrToggler.hidden = false;
 
     if (
       aAnimate &&
-      Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")
+      Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") &&
+      !BrowserHandler.kiosk
     ) {
       gNavToolbox.setAttribute("fullscreenShouldAnimate", true);
       // Hide the fullscreen toggler until the transition ends.
       let listener = () => {
         gNavToolbox.removeEventListener("transitionend", listener, true);
         if (this._isChromeCollapsed) {
           this._fullScrToggler.hidden = false;
         }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -261,16 +261,17 @@ XPCOMUtils.defineLazyServiceGetters(this
   ],
   gDNSService: ["@mozilla.org/network/dns-service;1", "nsIDNSService"],
   gSerializationHelper: [
     "@mozilla.org/network/serialization-helper;1",
     "nsISerializationHelper",
   ],
   Marionette: ["@mozilla.org/remote/marionette;1", "nsIMarionette"],
   WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
+  BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
 });
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyServiceGetter(
     this,
     "gCrashReporter",
     "@mozilla.org/xre/app-info;1",
     "nsICrashReporter"
@@ -2173,16 +2174,25 @@ var gBrowserInit = {
 
     SessionStore.promiseAllWindowsRestored.then(() => {
       this._schedulePerWindowIdleTasks();
       document.documentElement.setAttribute("sessionrestored", "true");
     });
 
     Services.obs.notifyObservers(window, "browser-delayed-startup-finished");
     TelemetryTimestamps.add("delayedStartupFinished");
+
+    if (BrowserHandler.kiosk) {
+      // We don't modify popup windows for kiosk mode
+      if (!gURLBar.readOnly) {
+        // Don't show status tooltips in kiosk mode
+        document.getElementById("statuspanel").hidden = true;
+        window.fullScreen = true;
+      }
+    }
   },
 
   _setInitialFocus() {
     let initiallyFocusedElement = document.commandDispatcher.focusedElement;
 
     this._firstBrowserPaintDeferred = {};
     this._firstBrowserPaintDeferred.promise = new Promise(resolve => {
       this._firstBrowserPaintDeferred.resolve = resolve;
@@ -3848,17 +3858,17 @@ function getDefaultHomePage() {
   // If url is a pipe-delimited set of pages, just take the first one.
   if (url.includes("|")) {
     url = url.split("|")[0];
   }
   return url;
 }
 
 function BrowserFullScreen() {
-  window.fullScreen = !window.fullScreen;
+  window.fullScreen = !window.fullScreen || BrowserHandler.kiosk;
 }
 
 function getWebNavigation() {
   return gBrowser.webNavigation;
 }
 
 function BrowserReloadWithFlags(reloadFlags) {
   let unchangedRemoteness = [];
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -30,19 +30,30 @@ XPCOMUtils.defineLazyModuleGetters(this,
 XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
   Components.Constructor(
     "@mozilla.org/referrer-info;1",
     "nsIReferrerInfo",
     "init"
   )
 );
 
+XPCOMUtils.defineLazyServiceGetter(
+  this,
+  "BrowserHandler",
+  "@mozilla.org/browser/clh;1",
+  "nsIBrowserHandler"
+);
+
 var gContextMenuContentData = null;
 
 function openContextMenu(aMessage, aBrowser, aActor) {
+  if (BrowserHandler.kiosk) {
+    // Don't display context menus in kiosk mode
+    return;
+  }
   let data = aMessage.data;
   let browser = aBrowser;
   let actor = aActor;
   let spellInfo = data.spellInfo;
   let frameReferrerInfo = data.frameReferrerInfo;
   let linkReferrerInfo = data.linkReferrerInfo;
   let principal = data.principal;
   let storagePrincipal = data.storagePrincipal;
--- a/browser/components/BrowserContentHandler.jsm
+++ b/browser/components/BrowserContentHandler.jsm
@@ -88,16 +88,18 @@ function resolveURIInternal(aCmdLine, aA
     uri = uriFixup.createFixupURI(aArgument, 0);
   } catch (e) {
     Cu.reportError(e);
   }
 
   return uri;
 }
 
+let gKiosk = false;
+
 let gRemoteInstallPage = null;
 
 function getNewInstallPage() {
   if (!gRemoteInstallPage) {
     gRemoteInstallPage = new RemotePages(NEWINSTALL_PAGE);
   }
 
   return NEWINSTALL_PAGE;
@@ -388,16 +390,19 @@ nsBrowserContentHandler.prototype = {
     Ci.nsICommandLineHandler,
     Ci.nsIBrowserHandler,
     Ci.nsIContentHandler,
     Ci.nsICommandLineValidator,
   ]),
 
   /* nsICommandLineHandler */
   handle: function bch_handle(cmdLine) {
+    if (cmdLine.handleFlag("kiosk", false)) {
+      gKiosk = true;
+    }
     if (cmdLine.handleFlag("browser", false)) {
       openBrowserWindow(cmdLine, gSystemPrincipal);
       cmdLine.preventDefault = true;
     }
 
     // In the past, when an instance was not already running, the -remote
     // option returned an error code. Any script or application invoking the
     // -remote option is expected to be handling this case, otherwise they
@@ -601,16 +606,17 @@ nsBrowserContentHandler.prototype = {
       "  --screenshot [<path>] Save screenshot to <path> or in working directory.\n";
     info +=
       "  --window-size width[,height] Width and optionally height of screenshot.\n";
     info +=
       "  --search <term>    Search <term> with your default search engine.\n";
     info += "  --setDefaultBrowser Set this app as the default browser.\n";
     info +=
       "  --first-startup    Run post-install actions before opening a new window.\n";
+    info += "  --kiosk Start the browser in kiosk mode.\n";
     return info;
   },
 
   /* nsIBrowserHandler */
 
   get defaultArgs() {
     return this.getArgs();
   },
@@ -816,16 +822,20 @@ nsBrowserContentHandler.prototype = {
       ) {
         this.mFeatures += ",suppressanimation";
       }
     }
 
     return this.mFeatures;
   },
 
+  get kiosk() {
+    return gKiosk;
+  },
+
   /* nsIContentHandler */
 
   handleContent: function bch_handleContent(contentType, context, request) {
     const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
 
     try {
       var webNavInfo = Cc["@mozilla.org/webnavigation-info;1"].getService(
         Ci.nsIWebNavigationInfo
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/docs/index.rst
@@ -0,0 +1,18 @@
+Enterprise Policies
+===================
+
+Introduction
+------------
+
+Firefox provides policies to manage various aspects of Firefox. The best documentation is in `GitHub <https://github.com/mozilla/policy-templates/>`_.
+
+Kiosk Mode
+----------
+
+The kiosk mode provided by Firefox on the command line (--kiosk) is a very basic mode intended for kiosks where the content loaded in the browser is strictly limited by the owner of the kiosk and either there is no keyboard or keyboard access is limited (particularly Ctrl and Alt). It is their responsibility to ensure the content does not surprise/confuse users or break browser UI in this setup.
+
+It does three main things:
+
+1. Switch all main browser windows (not popup windows) into full screen mode.
+2. Don't show the context menu.
+3. Don't show status for URLs or pageloading.
--- a/browser/components/enterprisepolicies/moz.build
+++ b/browser/components/enterprisepolicies/moz.build
@@ -2,16 +2,18 @@
 # vim: set filetype=python:
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Enterprise Policies")
 
+SPHINX_TREES['docs'] = 'docs'
+
 DIRS += [
     'helpers',
     'schemas',
 ]
 
 TEST_DIRS += [
 	'tests'
 ]
--- a/browser/components/nsIBrowserHandler.idl
+++ b/browser/components/nsIBrowserHandler.idl
@@ -6,15 +6,16 @@
 
 interface nsICommandLine;
 
 [scriptable, uuid(8D3F5A9D-118D-4548-A137-CF7718679069)]
 interface nsIBrowserHandler : nsISupports
 {
   attribute AUTF8String startPage;
   attribute AUTF8String defaultArgs;
+  attribute boolean kiosk;
 
   /**
    * Extract the width and height specified on the command line, if present.
    * @return A feature string with a prepended comma, e.g. ",width=500,height=400"
    */
   AUTF8String getFeatures(in nsICommandLine aCmdLine);
 };
--- a/browser/docs/index.rst
+++ b/browser/docs/index.rst
@@ -4,14 +4,15 @@ Firefox
 
 This is the nascent documentation of the Firefox front-end code.
 
 .. toctree::
    :maxdepth: 2
 
    urlbar/index
    BrowserUsageTelemetry
+   components/enterprisepolicies/docs/index
    extensions/formautofill/docs/index
    components/newtab/docs/index
    installer/windows/installer/index
    base/sslerrorreport/index
    base/tabbrowser/index
    components/payments/docs/index