Bug 1058150 - Use restricted profiles for guest mode. r=mfinkle
authorWes Johnston <wjohnston@mozilla.com>
Tue, 09 Sep 2014 15:08:37 -0700
changeset 227816 bf52063ed95f132867e32cbd1fc45b62f42549f1
parent 227815 a0b9b573cbb655f7b4b542c5248a53030294649a
child 227817 4e2bb1833c0f1920a45122349ec5ad4fb9af7751
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs1058150
milestone35.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 1058150 - Use restricted profiles for guest mode. r=mfinkle
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/RestrictedProfiles.java
mobile/android/base/moz.build
mobile/android/base/tests/testRestrictedProfiles.js
mobile/android/chrome/content/SelectionHandler.js
mobile/android/chrome/content/browser.js
mobile/android/chrome/content/downloads.js
mobile/android/components/BrowserCLH.js
mobile/android/locales/en-US/chrome/webapp.properties
mobile/android/modules/WebappManager.jsm
toolkit/components/parentalcontrols/nsIParentalControlsService.idl
toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm
toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp
toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -2551,22 +2551,22 @@ public class BrowserApp extends GeckoApp
         if (AboutPages.isAboutReader(url)) {
             String urlFromReader = ReaderModeUtils.getUrlFromAboutReader(url);
             if (urlFromReader != null) {
                 url = urlFromReader;
             }
         }
 
         // Disable share menuitem for about:, chrome:, file:, and resource: URIs
-        final boolean inGuestMode = GeckoProfile.get(this).inGuestMode();
-        share.setVisible(!inGuestMode);
-        share.setEnabled(StringUtils.isShareableUrl(url) && !inGuestMode);
-        MenuUtils.safeSetEnabled(aMenu, R.id.apps, !inGuestMode);
-        MenuUtils.safeSetEnabled(aMenu, R.id.addons, !inGuestMode);
-        MenuUtils.safeSetEnabled(aMenu, R.id.downloads, !inGuestMode);
+        final boolean shareEnabled = RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_SHARE);
+        share.setVisible(shareEnabled);
+        share.setEnabled(StringUtils.isShareableUrl(url) && shareEnabled);
+        MenuUtils.safeSetEnabled(aMenu, R.id.apps, RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_INSTALL_APPS));
+        MenuUtils.safeSetEnabled(aMenu, R.id.addons, RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_INSTALL_EXTENSIONS));
+        MenuUtils.safeSetEnabled(aMenu, R.id.downloads, RestrictedProfiles.isAllowed(RestrictedProfiles.Restriction.DISALLOW_DOWNLOADS));
 
         // NOTE: Use MenuUtils.safeSetEnabled because some actions might
         // be on the BrowserToolbar context menu.
         if (Versions.feature11Plus) {
             MenuUtils.safeSetEnabled(aMenu, R.id.page, !isAboutHome(tab));
         }
         MenuUtils.safeSetEnabled(aMenu, R.id.subscribe, tab.hasFeeds());
         MenuUtils.safeSetEnabled(aMenu, R.id.add_search_engine, tab.hasOpenSearch());
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -99,17 +99,16 @@ import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.SystemClock;
-import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -2549,49 +2548,16 @@ public class GeckoAppShell
                 return "PROXY " + proxy.address().toString();
             case SOCKS:
                 return "SOCKS " + proxy.address().toString();
         }
 
         return "DIRECT";
     }
 
-    @WrapElementForJNI
-    public static boolean isUserRestricted() {
-        if (Versions.preJBMR2) {
-            return false;
-        }
-
-        UserManager mgr = (UserManager)getContext().getSystemService(Context.USER_SERVICE);
-        Bundle restrictions = mgr.getUserRestrictions();
-
-        return !restrictions.isEmpty();
-    }
-
-    @WrapElementForJNI
-    public static String getUserRestrictions() {
-        if (Versions.preJBMR2) {
-            return "{}";
-        }
-
-        JSONObject json = new JSONObject();
-        UserManager mgr = (UserManager)getContext().getSystemService(Context.USER_SERVICE);
-        Bundle restrictions = mgr.getUserRestrictions();
-
-        Set<String> keys = restrictions.keySet();
-        for (String key : keys) {
-            try {
-                json.put(key, restrictions.get(key));
-            } catch (JSONException e) {
-            }
-        }
-
-        return json.toString();
-    }
-
     /* Downloads the uri pointed to by a share intent, and alters the intent to point to the locally stored file.
      */
     public static void downloadImageForIntent(final Intent intent) {
         final String src = intent.getStringExtra(Intent.EXTRA_TEXT);
         final File dir = GeckoApp.getTempDirectory();
 
         if (dir == null) {
             showImageShareFailureToast();
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/RestrictedProfiles.java
@@ -0,0 +1,132 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.lang.StringBuilder;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
+
+
+public class RestrictedProfiles {
+    private static final String LOGTAG = "GeckoRestrictedProfiles";
+
+    // These constants should be in sync with the ones from toolkit/components/parentalcontrols/nsIParentalControlServices.java
+    public static enum Restriction {
+        DISALLOW_DOWNLOADS(1, "no_download_files"),
+        DISALLOW_INSTALL_EXTENSIONS(2, "no_install_extensions"),
+        DISALLOW_INSTALL_APPS(3, UserManager.DISALLOW_INSTALL_APPS),
+        DISALLOW_BROWSE_FILES(4, "no_browse_files"),
+        DISALLOW_SHARE(5, "no_share"),
+        DISALLOW_BOOKMARK(6, "no_bookmark"),
+        DISALLOW_ADD_CONTACTS(7, "no_add_contacts"),
+        DISALLOW_SET_IMAGE(8, "no_set_image");
+
+        public final int id;
+        public final String name;
+
+        private Restriction(final int id, final String name) {
+            this.id = id;
+            this.name = name;
+        }
+    }
+
+    private static String geckoActionToRestrction(int action) {
+        for (Restriction rest : Restriction.values()) {
+            if (rest.id == action) {
+                return rest.name;
+            }
+        }
+
+        throw new IllegalArgumentException("Unknown action " + action);
+    }
+
+    private static Bundle getRestrctions() {
+        final UserManager mgr = (UserManager) GeckoAppShell.getContext().getSystemService(Context.USER_SERVICE);
+        return mgr.getUserRestrictions();
+    }
+
+    @WrapElementForJNI
+    public static boolean isUserRestricted() {
+        // Guest mode is supported in all Android versions
+        if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
+            return true;
+        }
+
+        if (Versions.preJBMR2) {
+            return false;
+        }
+
+        return !getRestrctions().isEmpty();
+    }
+
+    public static boolean isAllowed(Restriction action) {
+        return isAllowed(action.id, null);
+    }
+
+    @WrapElementForJNI
+    public static boolean isAllowed(int action, String url) {
+        // ALl actions are blocked in Guest mode
+        if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
+            return false;
+        }
+
+        if (Versions.preJBMR2) {
+            return true;
+        }
+
+        try {
+            final String restriction = geckoActionToRestrction(action);
+            return !getRestrctions().getBoolean(restriction, false);
+        } catch(IllegalArgumentException ex) {
+            Log.i(LOGTAG, "Invalid action", ex);
+        }
+
+        return true;
+    }
+
+    @WrapElementForJNI
+    public static String getUserRestrictions() {
+        // Guest mode is supported in all Android versions
+        if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
+            StringBuilder builder = new StringBuilder("{ ");
+
+            for (Restriction restriction : Restriction.values()) {
+                builder.append("\"" + restriction.name + "\": true, ");
+            }
+
+            builder.append(" }");
+            return builder.toString();
+        }
+
+        if (Versions.preJBMR2) {
+            return "{}";
+        }
+
+        final JSONObject json = new JSONObject();
+        final Bundle restrictions = getRestrctions();
+        final Set<String> keys = restrictions.keySet();
+
+        for (String key : keys) {
+            try {
+                json.put(key, restrictions.get(key));
+            } catch (JSONException e) {
+            }
+        }
+
+        return json.toString();
+    }
+}
\ No newline at end of file
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -364,16 +364,17 @@ gbjar.sources += [
     'prompts/PromptInput.java',
     'prompts/PromptListAdapter.java',
     'prompts/PromptListItem.java',
     'prompts/PromptService.java',
     'prompts/TabInput.java',
     'ReaderModeUtils.java',
     'RemoteTabsExpandableListAdapter.java',
     'Restarter.java',
+    'RestrictedProfiles.java',
     'ScrollAnimator.java',
     'ServiceNotificationClient.java',
     'SessionParser.java',
     'SharedPreferencesHelper.java',
     'SiteIdentity.java',
     'SmsManager.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
--- a/mobile/android/base/tests/testRestrictedProfiles.js
+++ b/mobile/android/base/tests/testRestrictedProfiles.js
@@ -13,33 +13,43 @@ add_task(function test_isUserRestricted(
   do_check_true("@mozilla.org/parental-controls-service;1" in Cc);
   
   let pc = Cc["@mozilla.org/parental-controls-service;1"].createInstance(Ci.nsIParentalControlsService);
   
   // In an admin profile, like the tests: enabled = false
   // In a restricted profile: enabled = true
   do_check_false(pc.parentalControlsEnabled);
 
-  //run_next_test();
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.DOWNLOAD));
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.INSTALL_EXTENSION));
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.INSTALL_APP));
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.VISIT_FILE_URLS));
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.SHARE));
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.BOOKMARK));
+  do_check_true(pc.isAllowed(Ci.nsIParentalControlsService.INSTALL_EXTENSION));
+
+  run_next_test();
 });
-/*
-// NOTE: JNI.jsm has no way to call a string method yet
+
 add_task(function test_getUserRestrictions() {
   // In an admin profile, like the tests: {}
   // In a restricted profile: {"no_modify_accounts":true,"no_share_location":true}
   let restrictions = "{}";
 
-  let jni = null;
+  var jenv = null;
   try {
-    jni = new JNI();
-    let cls = jni.findClass("org/mozilla/gecko/GeckoAppShell");
-    let method = jni.getStaticMethodID(cls, "getUserRestrictions", "()Ljava/lang/String;");
-    restrictions = jni.callStaticStringMethod(cls, method);
+    jenv = JNI.GetForThread();
+    var geckoAppShell = JNI.LoadClass(jenv, "org.mozilla.gecko.RestrictedProfile", {
+      static_methods: [
+        { name: "getUserRestrictions", sig: "()Ljava/lang/String;" },
+      ],
+    });
+    restrictions = JNI.ReadString(jenv, geckoAppShell.getUserRestrictions());
   } finally {
-    if (jni != null) {
-      jni.close();
+    if (jenv) {
+      JNI.UnloadClasses(jenv);
     }
   }
 
   do_check_eq(restrictions, "{}");
 });
-*/
+
 run_next_test();
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -647,16 +647,20 @@ var SelectionHandler = {
       id: "share_action",
       icon: "drawable://ic_menu_share",
       action: function() {
         SelectionHandler.shareSelection();
         UITelemetry.addEvent("action.1", "actionbar", null, "share");
       },
       selector: {
         matches: function() {
+          if (!ParentalControls.isAllowed(ParentalControls.SHARE)) {
+            return false;
+          }
+
           return SelectionHandler.isSelectionActive();
         }
       }
     },
 
     SEARCH: {
       label: function() {
         return Strings.browser.formatStringFromName("contextmenu.search", [Services.search.defaultEngine.name], 1);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -159,16 +159,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   notifications.forEach(notification => {
     Services.obs.addObserver(observer, notification, false);
   });
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "Haptic",
   "@mozilla.org/widget/hapticfeedback;1", "nsIHapticFeedback");
 
+XPCOMUtils.defineLazyServiceGetter(this, "ParentalControls",
+  "@mozilla.org/parental-controls-service;1", "nsIParentalControlsService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "DOMUtils",
   "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
 
 XPCOMUtils.defineLazyServiceGetter(window, "URIFixup",
   "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
 
 #ifdef MOZ_WEBRTC
 XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
@@ -282,17 +285,16 @@ var Strings = {};
 const kFormHelperModeDisabled = 0;
 const kFormHelperModeEnabled = 1;
 const kFormHelperModeDynamic = 2;   // disabled on tablets
 
 var BrowserApp = {
   _tabs: [],
   _selectedTab: null,
   _prefObservers: [],
-  isGuest: false,
 
   get isTablet() {
     let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
     delete this.isTablet;
     return this.isTablet = sysInfo.get("tablet");
   },
 
   get isOnLowMemoryPlatform() {
@@ -429,18 +431,16 @@ var BrowserApp = {
       if (window.arguments[0])
         url = window.arguments[0];
       if (window.arguments[1])
         gScreenWidth = window.arguments[1];
       if (window.arguments[2])
         gScreenHeight = window.arguments[2];
       if (window.arguments[3])
         pinned = window.arguments[3];
-      if (window.arguments[4])
-        this.isGuest = window.arguments[4];
     }
 
     if (pinned) {
       this._initRuntime(this._startupStatus, url, aUrl => this.addTab(aUrl));
     } else {
       SearchEngines.init();
       this.initContextMenu();
     }
@@ -454,17 +454,17 @@ var BrowserApp = {
     // Broadcast a UIReady message so add-ons know we are finished with startup
     let event = document.createEvent("Events");
     event.initEvent("UIReady", true, false);
     window.dispatchEvent(event);
 
     if (this._startupStatus)
       this.onAppUpdated();
 
-    if (this.isGuest) {
+    if (!ParentalControls.isAllowed(ParentalControls.INSTALL_EXTENSIONS)) {
       // Disable extension installs
       Services.prefs.setIntPref("extensions.enabledScopes", 1);
       Services.prefs.setIntPref("extensions.autoDisableScopes", 1);
       Services.prefs.setBoolPref("xpinstall.enabled", false);
     }
 
     // notify java that gecko has loaded
     Messaging.sendRequest({ type: "Gecko:Ready" });
@@ -576,33 +576,33 @@ var BrowserApp = {
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
         NativeWindow.contextmenus._copyStringToDefaultClipboard(phoneNumber);
       });
 
     NativeWindow.contextmenus.add({
       label: Strings.browser.GetStringFromName("contextmenu.shareLink"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1, // Show above HTML5 menu items
-      selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.linkShareableContext),
+      selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.linkShareableContext),
       showAsActions: function(aElement) {
         return {
           title: aElement.textContent.trim() || aElement.title.trim(),
           uri: NativeWindow.contextmenus._getLinkURL(aElement),
         };
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_link");
       }
     });
 
     NativeWindow.contextmenus.add({
       label: Strings.browser.GetStringFromName("contextmenu.shareEmailAddress"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
-      selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.emailLinkContext),
+      selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.emailLinkContext),
       showAsActions: function(aElement) {
         let url = NativeWindow.contextmenus._getLinkURL(aElement);
         let emailAddr = NativeWindow.contextmenus._stripScheme(url);
         let title = aElement.textContent || aElement.title;
         return {
           title: title,
           uri: emailAddr,
         };
@@ -611,58 +611,58 @@ var BrowserApp = {
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_email");
       }
     });
 
     NativeWindow.contextmenus.add({
       label: Strings.browser.GetStringFromName("contextmenu.sharePhoneNumber"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
-      selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.phoneNumberLinkContext),
+      selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.phoneNumberLinkContext),
       showAsActions: function(aElement) {
         let url = NativeWindow.contextmenus._getLinkURL(aElement);
         let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
         let title = aElement.textContent || aElement.title;
         return {
           title: title,
           uri: phoneNumber,
         };
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_phone");
       }
     });
 
     NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.addToContacts"),
-      NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.emailLinkContext),
+      NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.emailLinkContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         Messaging.sendRequest({
           type: "Contact:Add",
           email: url
         });
       });
 
     NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.addToContacts"),
-      NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.phoneNumberLinkContext),
+      NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.phoneNumberLinkContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         Messaging.sendRequest({
           type: "Contact:Add",
           phone: url
         });
       });
 
     NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.bookmarkLink"),
-      NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.linkBookmarkableContext),
+      NativeWindow.contextmenus._disableRestricted("BOOKMARK", NativeWindow.contextmenus.linkBookmarkableContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_bookmark");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         let title = aTarget.textContent || aTarget.title || url;
         Messaging.sendRequest({
           type: "Bookmark:Insert",
           url: url,
@@ -689,17 +689,17 @@ var BrowserApp = {
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_controls_media");
         aTarget.setAttribute("controls", true);
       });
 
     NativeWindow.contextmenus.add({
       label: Strings.browser.GetStringFromName("contextmenu.shareMedia"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
-      selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.SelectorContext("video")),
+      selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.SelectorContext("video")),
       showAsActions: function(aElement) {
         let url = (aElement.currentSrc || aElement.src);
         let title = aElement.textContent || aElement.title;
         return {
           title: title,
           uri: url,
           type: "video/*",
         };
@@ -737,17 +737,17 @@ var BrowserApp = {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_image");
 
         let url = aTarget.src;
         NativeWindow.contextmenus._copyStringToDefaultClipboard(url);
       });
 
     NativeWindow.contextmenus.add({
       label: Strings.browser.GetStringFromName("contextmenu.shareImage"),
-      selector: NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.imageSaveableContext),
+      selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.imageSaveableContext),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1, // Show above HTML5 menu items
       showAsActions: function(aTarget) {
         let doc = aTarget.ownerDocument;
         let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                                                          .getImgCacheForDocument(doc);
         let props = imageCache.findEntryProperties(aTarget.currentURI, doc.characterSet);
         let src = aTarget.src;
         return {
@@ -769,17 +769,17 @@ var BrowserApp = {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_image");
 
         ContentAreaUtils.saveImageURL(aTarget.currentURI.spec, null, "SaveImageTitle",
                                       false, true, aTarget.ownerDocument.documentURIObject,
                                       aTarget.ownerDocument);
       });
 
     NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.setImageAs"),
-      NativeWindow.contextmenus._disableInGuest(NativeWindow.contextmenus.imageSaveableContext),
+      NativeWindow.contextmenus._disableRestricted("SET_IMAGE", NativeWindow.contextmenus.imageSaveableContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image");
 
         let src = aTarget.src;
         Messaging.sendRequest({
           type: "Image:SetAs",
           url: src
         });
@@ -2675,21 +2675,23 @@ var NativeWindow = {
         try {
           let url = this._getLinkURL(aElement);
           return Services.io.newURI(url, null, null);
         } catch (e) {}
       }
       return null;
     },
 
-    _disableInGuest: function _disableInGuest(selector) {
+    _disableRestricted: function _disableRestricted(restriction, selector) {
       return {
-        matches: function _disableInGuestMatches(aElement, aX, aY) {
-          if (BrowserApp.isGuest)
+        matches: function _disableRestrictedMatches(aElement, aX, aY) {
+          if (!ParentalControls.isAllowed(ParentalControls[restriction])) {
             return false;
+          }
+
           return selector.matches(aElement, aX, aY);
         }
       };
     },
 
     _getLinkURL: function ch_getLinkURL(aLink) {
       let href = aLink.href;
       if (href)
@@ -4197,18 +4199,18 @@ Tab.prototype = {
 
     this._hostChanged = true;
 
     let fixedURI = aLocationURI;
     try {
       fixedURI = URIFixup.createExposableURI(aLocationURI);
     } catch (ex) { }
 
-    // In guest sessions, we refuse to let you open any file urls.
-    if (BrowserApp.isGuest) {
+    // In restricted profiles, we refuse to let you open any file urls.
+    if (!ParentalControls.isAllowed(ParentalControls.VISIT_FILE_URLS)) {
       let bannedSchemes = ["file", "chrome", "resource", "jar", "wyciwyg"];
 
       if (bannedSchemes.indexOf(fixedURI.scheme) > -1) {
         aRequest.cancel(Cr.NS_BINDING_ABORTED);
 
         aRequest = this.browser.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, fixedURI, null);
         if (aRequest) {
           fixedURI = aRequest.URI;
--- a/mobile/android/chrome/content/downloads.js
+++ b/mobile/android/chrome/content/downloads.js
@@ -251,21 +251,22 @@ AlertDownloadProgressListener.prototype 
 
     Downloads.updateNotification(aDownload, new DownloadProgressNotifOptions(aDownload, [PAUSE_BUTTON, CANCEL_BUTTON]));
   },
 
   onDownloadStateChange: function(aState, aDownload) {
     let state = aDownload.state;
     switch (state) {
       case Ci.nsIDownloadManager.DOWNLOAD_QUEUED: {
-        if (BrowserApp.isGuest) {
+        if (!ParentalControls.isAllowed(ParentalControls.DOWNLOADS)) {
           aDownload.cancel();
           NativeWindow.toast.show(Strings.browser.GetStringFromName("downloads.disabledInGuest"), "long");
           return;
         }
+
         NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long");
         Downloads.createNotification(aDownload, new DownloadNotifOptions(aDownload,
                                                                          Strings.browser.GetStringFromName("alertDownloadsStart2"),
                                                                          aDownload.displayName));
         break;
       }
       case Ci.nsIDownloadManager.DOWNLOAD_PAUSED: {
         Downloads.updateNotification(aDownload, new DownloadProgressNotifOptions(aDownload, [RESUME_BUTTON, CANCEL_BUTTON]));
--- a/mobile/android/components/BrowserCLH.js
+++ b/mobile/android/components/BrowserCLH.js
@@ -13,34 +13,31 @@ Cu.import("resource://gre/modules/Servic
 function dump(a) {
   Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
 }
 
 function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) {
   let argsArray = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
   let urlString = null;
   let pinnedBool = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
-  let guestBool = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
   let widthInt = Cc["@mozilla.org/supports-PRInt32;1"].createInstance(Ci.nsISupportsPRInt32);
   let heightInt = Cc["@mozilla.org/supports-PRInt32;1"].createInstance(Ci.nsISupportsPRInt32);
 
   if ("url" in aArgs) {
     urlString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
     urlString.data = aArgs.url;
   }
   widthInt.data = "width" in aArgs ? aArgs.width : 1;
   heightInt.data = "height" in aArgs ? aArgs.height : 1;
   pinnedBool.data = "pinned" in aArgs ? aArgs.pinned : false;
-  guestBool.data = "guest" in aArgs ? aArgs["guest"] : false;
 
   argsArray.AppendElement(urlString, false);
   argsArray.AppendElement(widthInt, false);
   argsArray.AppendElement(heightInt, false);
   argsArray.AppendElement(pinnedBool, false);
-  argsArray.AppendElement(guestBool, false);
   return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argsArray);
 }
 
 
 function resolveURIInternal(aCmdLine, aArgument) {
   let uri = aCmdLine.resolveURI(aArgument);
   if (uri)
     return uri;
@@ -56,30 +53,26 @@ function resolveURIInternal(aCmdLine, aA
 }
 
 function BrowserCLH() {}
 
 BrowserCLH.prototype = {
   handle: function fs_handle(aCmdLine) {
     let openURL = "about:home";
     let pinned = false;
-    let guest = false;
 
     let width = 1;
     let height = 1;
 
     try {
       openURL = aCmdLine.handleFlagWithParam("url", false);
     } catch (e) { /* Optional */ }
     try {
       pinned = aCmdLine.handleFlag("webapp", false);
     } catch (e) { /* Optional */ }
-    try {
-      guest = aCmdLine.handleFlag("guest", false);
-    } catch (e) { /* Optional */ }
 
     try {
       width = aCmdLine.handleFlagWithParam("width", false);
     } catch (e) { /* Optional */ }
     try {
       height = aCmdLine.handleFlagWithParam("height", false);
     } catch (e) { /* Optional */ }
 
@@ -97,17 +90,16 @@ BrowserCLH.prototype = {
           browserWin.browserDOMWindow.openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
         }
       } else {
         let args = {
           url: openURL,
           pinned: pinned,
           width: width,
           height: height,
-          guest: guest
         };
 
         // Make sure webapps do not have: locationbar, personalbar, menubar, statusbar, and toolbar
         let flags = "chrome,dialog=no";
         if (!pinned)
           flags += ",all";
 
         browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", flags, args);
--- a/mobile/android/locales/en-US/chrome/webapp.properties
+++ b/mobile/android/locales/en-US/chrome/webapp.properties
@@ -55,9 +55,9 @@ installUpdateMessage2=Touch to install u
 retrievalFailedTitle=#1 update failed;#1 updates failed
 
 # LOCALIZATION NOTE (retrievalFailedMessage): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # %1$S is a comma-separated list of apps for which retrieval failed.
 # example: Failed to retrieve updates for Foo, Bar, Baz
 retrievalFailedMessage=Failed to retrieve update for %1$S;Failed to retrieve updates for %1$S
 
-webappsDisabledInGuest=Installing apps is disabled in guest sessions
+webappsDisabled=Installing apps is disabled
--- a/mobile/android/modules/WebappManager.jsm
+++ b/mobile/android/modules/WebappManager.jsm
@@ -24,16 +24,19 @@ Cu.import("resource://gre/modules/Task.j
 XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "Strings", function() {
   return Services.strings.createBundle("chrome://browser/locale/webapp.properties");
 });
 
+XPCOMUtils.defineLazyServiceGetter(this, "ParentalControls",
+  "@mozilla.org/parental-controls-service;1", "nsIParentalControlsService");
+
 /**
  * Get the formatted plural form of a string.  Escapes semicolons in arguments
  * to provide to the formatter before formatting the string, then unescapes them
  * after getting its plural form, to avoid tripping up the plural form getter
  * with a semicolon in one of the formatter's arguments, since the plural forms
  * of localized strings are delimited by semicolons.
  *
  * Ideally, we'd get the plural form first and then format the string,
@@ -84,18 +87,18 @@ this.WebappManager = {
       DOMApplicationRegistry.doInstallPackage(aMessage, aMessageManager);
       return;
     }
 
     this._installApk(aMessage, aMessageManager);
   },
 
   _installApk: function(aMessage, aMessageManager) { return Task.spawn((function*() {
-    if (this.inGuestSession()) {
-      aMessage.error = Strings.GetStringFromName("webappsDisabledInGuest"),
+    if (!ParentalControls.isAllowed(ParentalControls.INSTALL_APPS)) {
+      aMessage.error = Strings.GetStringFromName("webappsDisabled"),
       aMessageManager.sendAsyncMessage("Webapps:Install:Return:KO", aMessage);
       return;
     }
 
     let filePath;
 
 
     let appName = aMessage.app.manifest ? aMessage.app.manifest.name
@@ -272,20 +275,16 @@ this.WebappManager = {
       // to ensure the user can always remove an app from the registry (and thus
       // about:apps) even if it's out of sync with installed APKs.
       debug("APK not installed; proceeding directly to removal from registry");
       DOMApplicationRegistry.doUninstall(aData, aMessageManager);
     }
 
   }),
 
-  inGuestSession: function() {
-    return Services.wm.getMostRecentWindow("navigator:browser").BrowserApp.isGuest;
-  },
-
   autoInstall: function(aData) {
     debug("autoInstall " + aData.manifestURL);
 
     // If the app is already installed, update the existing installation.
     // We should be able to use DOMApplicationRegistry.getAppByManifestURL,
     // but it returns a mozIApplication, while _autoUpdate needs the original
     // object from DOMApplicationRegistry.webapps in order to modify it.
     for (let [ , app] in Iterator(DOMApplicationRegistry.webapps)) {
--- a/toolkit/components/parentalcontrols/nsIParentalControlsService.idl
+++ b/toolkit/components/parentalcontrols/nsIParentalControlsService.idl
@@ -6,32 +6,53 @@
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFile;
 interface nsIInterfaceRequestor;
 interface nsIArray;
 
-[scriptable, uuid(871cf229-2b21-4f04-b24d-e08061f14815)]
+[scriptable, uuid(b3585b2a-b4b3-4aa7-be92-b8ddaa6aec5f)]
 interface nsIParentalControlsService : nsISupports
 {
   /**
+   * Action types that can be blocked for users.
+   */
+  const short DOWNLOAD = 1; // Downloading files
+  const short INSTALL_EXTENSION = 2; // Installing extensions
+  const short INSTALL_APP = 3; // Installing webapps
+  const short VISIT_FILE_URLS = 4; // Opening file:/// urls
+  const short SHARE = 5; // Sharing
+  const short BOOKMARK = 6; // Creating bookmarks
+  const short ADD_CONTACT = 7; // Add contacts to the system database
+  const short SET_IMAGE = 8; // Setting images as wall paper
+
+  /**
    * @returns true if the current user account has parental controls
    * restrictions enabled.
    */ 
   readonly attribute boolean parentalControlsEnabled;
 
   /**
    * @returns true if the current user account parental controls
    * restrictions include the blocking of all file downloads.
    */ 
   readonly attribute boolean blockFileDownloadsEnabled;
 
   /**
+   * Check if the user can do the prescibed action for this uri.
+   *
+   * @param aAction             Action being performed
+   * @param aUri                The uri requesting this action
+   * @param aWindow             The window generating this event.
+   */
+   boolean isAllowed(in short aAction, [optional] in nsIURI aUri);
+
+  /**
    * Request that blocked URI(s) be allowed through parental
    * control filters. Returns true if the URI was successfully
    * overriden. Note, may block while native UI is shown.
    *
    * @param aTarget(s)          URI to be overridden. In the case of
    *                            multiple URI, the first URI in the array
    *                            should be the root URI of the site.
    * @param window              Window that generates the event.
--- a/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp
@@ -9,17 +9,17 @@
 #include "nsIFile.h"
 
 NS_IMPL_ISUPPORTS(nsParentalControlsService, nsIParentalControlsService)
 
 nsParentalControlsService::nsParentalControlsService() :
   mEnabled(false)
 {
   if (mozilla::AndroidBridge::HasEnv()) {
-    mEnabled = mozilla::widget::android::GeckoAppShell::IsUserRestricted();
+    mEnabled = mozilla::widget::android::RestrictedProfiles::IsUserRestricted();
   }
 }
 
 nsParentalControlsService::~nsParentalControlsService()
 {
 }
 
 NS_IMETHODIMP
@@ -27,17 +27,21 @@ nsParentalControlsService::GetParentalCo
 {
   *aResult = mEnabled;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsParentalControlsService::GetBlockFileDownloadsEnabled(bool *aResult)
 {
-  return NS_ERROR_NOT_AVAILABLE;
+  bool res;
+  IsAllowed(nsIParentalControlsService::DOWNLOAD, NULL, &res);
+  *aResult = res;
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsParentalControlsService::GetLoggingEnabled(bool *aResult)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
@@ -60,8 +64,35 @@ nsParentalControlsService::RequestURIOve
 
 NS_IMETHODIMP
 nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets,
                                                nsIInterfaceRequestor *aWindowContext,
                                                bool *_retval)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
+
+NS_IMETHODIMP
+nsParentalControlsService::IsAllowed(int16_t aAction,
+                                     nsIURI *aUri,
+                                     bool *_retval)
+{
+  nsresult rv = NS_OK;
+  *_retval = true;
+
+  if (!mEnabled) {
+    return rv;
+  }
+
+  if (mozilla::AndroidBridge::HasEnv()) {
+    nsAutoCString url;
+    if (aUri) {
+      rv = aUri->GetSpec(url);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    *_retval = mozilla::widget::android::RestrictedProfiles::IsAllowed(aAction,
+                                                    NS_ConvertUTF8toUTF16(url));
+    return rv;
+  }
+
+  return NS_ERROR_NOT_AVAILABLE;
+}
--- a/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm
@@ -60,8 +60,17 @@ nsParentalControlsService::RequestURIOve
 
 NS_IMETHODIMP
 nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets,
                                                nsIInterfaceRequestor *aWindowContext,
                                                bool *_retval)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
+
+NS_IMETHODIMP
+nsParentalControlsService::IsAllowed(int16_t aAction,
+                                     nsIURI *aUri,
+                                     bool *_retval)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
--- a/toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp
@@ -55,8 +55,16 @@ nsParentalControlsService::RequestURIOve
 
 NS_IMETHODIMP
 nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets,
                                                nsIInterfaceRequestor *aWindowContext,
                                                bool *_retval)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
+
+NS_IMETHODIMP
+nsParentalControlsService::IsAllowed(int16_t aAction,
+                                     nsIURI *aUri,
+                                     bool *_retval)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
--- a/toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp
@@ -326,8 +326,15 @@ nsParentalControlsService::LogFileDownlo
   }
   else {
     EventDataDescCreate(&eventData[WPC_ARGS_FILEDOWNLOADEVENT_PATH], (const void*)fill, sizeof(fill));
   }
 
   gEventWrite(mProvider, &WPCEVENT_WEB_FILEDOWNLOAD, ARRAYSIZE(eventData), eventData);
 }
 
+NS_IMETHODIMP
+nsParentalControlsService::IsAllowed(int16_t aAction,
+                                     nsIURI *aUri,
+                                     bool *_retval)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -50,25 +50,23 @@ jmethodID GeckoAppShell::jGetIconForExte
 jmethodID GeckoAppShell::jGetMessageWrapper = 0;
 jmethodID GeckoAppShell::jGetMimeTypeFromExtensionsWrapper = 0;
 jmethodID GeckoAppShell::jGetNextMessageInListWrapper = 0;
 jmethodID GeckoAppShell::jGetProxyForURIWrapper = 0;
 jmethodID GeckoAppShell::jGetScreenDepthWrapper = 0;
 jmethodID GeckoAppShell::jGetScreenOrientationWrapper = 0;
 jmethodID GeckoAppShell::jGetShowPasswordSetting = 0;
 jmethodID GeckoAppShell::jGetSystemColoursWrapper = 0;
-jmethodID GeckoAppShell::jGetUserRestrictions = 0;
 jmethodID GeckoAppShell::jHandleGeckoMessageWrapper = 0;
 jmethodID GeckoAppShell::jHandleUncaughtException = 0;
 jmethodID GeckoAppShell::jHideProgressDialog = 0;
 jmethodID GeckoAppShell::jInitCameraWrapper = 0;
 jmethodID GeckoAppShell::jIsNetworkLinkKnown = 0;
 jmethodID GeckoAppShell::jIsNetworkLinkUp = 0;
 jmethodID GeckoAppShell::jIsTablet = 0;
-jmethodID GeckoAppShell::jIsUserRestricted = 0;
 jmethodID GeckoAppShell::jKillAnyZombies = 0;
 jmethodID GeckoAppShell::jLoadPluginClass = 0;
 jmethodID GeckoAppShell::jLockScreenOrientation = 0;
 jmethodID GeckoAppShell::jMarkURIVisited = 0;
 jmethodID GeckoAppShell::jMoveTaskToBack = 0;
 jmethodID GeckoAppShell::jNetworkLinkType = 0;
 jmethodID GeckoAppShell::jNotifyDefaultPrevented = 0;
 jmethodID GeckoAppShell::jNotifyIME = 0;
@@ -139,25 +137,23 @@ void GeckoAppShell::InitStubs(JNIEnv *jE
     jGetMessageWrapper = getStaticMethod("getMessage", "(II)V");
     jGetMimeTypeFromExtensionsWrapper = getStaticMethod("getMimeTypeFromExtensions", "(Ljava/lang/String;)Ljava/lang/String;");
     jGetNextMessageInListWrapper = getStaticMethod("getNextMessageInList", "(II)V");
     jGetProxyForURIWrapper = getStaticMethod("getProxyForURI", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;");
     jGetScreenDepthWrapper = getStaticMethod("getScreenDepth", "()I");
     jGetScreenOrientationWrapper = getStaticMethod("getScreenOrientation", "()S");
     jGetShowPasswordSetting = getStaticMethod("getShowPasswordSetting", "()Z");
     jGetSystemColoursWrapper = getStaticMethod("getSystemColors", "()[I");
-    jGetUserRestrictions = getStaticMethod("getUserRestrictions", "()Ljava/lang/String;");
     jHandleGeckoMessageWrapper = getStaticMethod("handleGeckoMessage", "(Lorg/mozilla/gecko/util/NativeJSContainer;)V");
     jHandleUncaughtException = getStaticMethod("handleUncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
     jHideProgressDialog = getStaticMethod("hideProgressDialog", "()V");
     jInitCameraWrapper = getStaticMethod("initCamera", "(Ljava/lang/String;III)[I");
     jIsNetworkLinkKnown = getStaticMethod("isNetworkLinkKnown", "()Z");
     jIsNetworkLinkUp = getStaticMethod("isNetworkLinkUp", "()Z");
     jIsTablet = getStaticMethod("isTablet", "()Z");
-    jIsUserRestricted = getStaticMethod("isUserRestricted", "()Z");
     jKillAnyZombies = getStaticMethod("killAnyZombies", "()V");
     jLoadPluginClass = getStaticMethod("loadPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
     jLockScreenOrientation = getStaticMethod("lockScreenOrientation", "(I)V");
     jMarkURIVisited = getStaticMethod("markUriVisited", "(Ljava/lang/String;)V");
     jMoveTaskToBack = getStaticMethod("moveTaskToBack", "()V");
     jNetworkLinkType = getStaticMethod("networkLinkType", "()I");
     jNotifyDefaultPrevented = getStaticMethod("notifyDefaultPrevented", "(Z)V");
     jNotifyIME = getStaticMethod("notifyIME", "(I)V");
@@ -782,29 +778,16 @@ jintArray GeckoAppShell::GetSystemColour
     }
 
     jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetSystemColoursWrapper);
     AndroidBridge::HandleUncaughtException(env);
     jintArray ret = static_cast<jintArray>(env->PopLocalFrame(temp));
     return ret;
 }
 
-jstring GeckoAppShell::GetUserRestrictions() {
-    JNIEnv *env = AndroidBridge::GetJNIEnv();
-    if (env->PushLocalFrame(1) != 0) {
-        AndroidBridge::HandleUncaughtException(env);
-        MOZ_CRASH("Exception should have caused crash.");
-    }
-
-    jobject temp = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetUserRestrictions);
-    AndroidBridge::HandleUncaughtException(env);
-    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
-    return ret;
-}
-
 void GeckoAppShell::HandleGeckoMessageWrapper(jobject a0) {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(1) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jHandleGeckoMessageWrapper, a0);
@@ -887,29 +870,16 @@ bool GeckoAppShell::IsTablet() {
     }
 
     bool temp = env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
     return temp;
 }
 
-bool GeckoAppShell::IsUserRestricted() {
-    JNIEnv *env = AndroidBridge::GetJNIEnv();
-    if (env->PushLocalFrame(0) != 0) {
-        AndroidBridge::HandleUncaughtException(env);
-        MOZ_CRASH("Exception should have caused crash.");
-    }
-
-    bool temp = env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsUserRestricted);
-    AndroidBridge::HandleUncaughtException(env);
-    env->PopLocalFrame(nullptr);
-    return temp;
-}
-
 void GeckoAppShell::KillAnyZombies() {
     JNIEnv *env = AndroidBridge::GetJNIEnv();
     if (env->PushLocalFrame(0) != 0) {
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoAppShellClass, jKillAnyZombies);
@@ -1559,16 +1529,76 @@ void GeckoJavaSampler::UnpauseJavaProfil
         AndroidBridge::HandleUncaughtException(env);
         MOZ_CRASH("Exception should have caused crash.");
     }
 
     env->CallStaticVoidMethod(mGeckoJavaSamplerClass, jUnpauseJavaProfiling);
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
+jclass RestrictedProfiles::mRestrictedProfilesClass = 0;
+jmethodID RestrictedProfiles::jGetUserRestrictions = 0;
+jmethodID RestrictedProfiles::jIsAllowed = 0;
+jmethodID RestrictedProfiles::jIsUserRestricted = 0;
+void RestrictedProfiles::InitStubs(JNIEnv *jEnv) {
+    initInit();
+
+    mRestrictedProfilesClass = getClassGlobalRef("org/mozilla/gecko/RestrictedProfiles");
+    jGetUserRestrictions = getStaticMethod("getUserRestrictions", "()Ljava/lang/String;");
+    jIsAllowed = getStaticMethod("isAllowed", "(ILjava/lang/String;)Z");
+    jIsUserRestricted = getStaticMethod("isUserRestricted", "()Z");
+}
+
+RestrictedProfiles* RestrictedProfiles::Wrap(jobject obj) {
+    JNIEnv *env = GetJNIForThread();
+    RestrictedProfiles* ret = new RestrictedProfiles(obj, env);
+    env->DeleteLocalRef(obj);
+    return ret;
+}
+
+jstring RestrictedProfiles::GetUserRestrictions() {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jobject temp = env->CallStaticObjectMethod(mRestrictedProfilesClass, jGetUserRestrictions);
+    AndroidBridge::HandleUncaughtException(env);
+    jstring ret = static_cast<jstring>(env->PopLocalFrame(temp));
+    return ret;
+}
+
+bool RestrictedProfiles::IsAllowed(int32_t a0, const nsAString& a1) {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (env->PushLocalFrame(1) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    jstring j1 = AndroidBridge::NewJavaString(env, a1);
+
+    bool temp = env->CallStaticBooleanMethod(mRestrictedProfilesClass, jIsAllowed, a0, j1);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
+
+bool RestrictedProfiles::IsUserRestricted() {
+    JNIEnv *env = AndroidBridge::GetJNIEnv();
+    if (env->PushLocalFrame(0) != 0) {
+        AndroidBridge::HandleUncaughtException(env);
+        MOZ_CRASH("Exception should have caused crash.");
+    }
+
+    bool temp = env->CallStaticBooleanMethod(mRestrictedProfilesClass, jIsUserRestricted);
+    AndroidBridge::HandleUncaughtException(env);
+    env->PopLocalFrame(nullptr);
+    return temp;
+}
 jclass SurfaceBits::mSurfaceBitsClass = 0;
 jmethodID SurfaceBits::jSurfaceBits = 0;
 jfieldID SurfaceBits::jbuffer = 0;
 jfieldID SurfaceBits::jformat = 0;
 jfieldID SurfaceBits::jheight = 0;
 jfieldID SurfaceBits::jwidth = 0;
 void SurfaceBits::InitStubs(JNIEnv *jEnv) {
     initInit();
@@ -2540,16 +2570,17 @@ void Clipboard::SetClipboardText(const n
     AndroidBridge::HandleUncaughtException(env);
     env->PopLocalFrame(nullptr);
 }
 
 void InitStubs(JNIEnv *jEnv) {
     GeckoAppShell::InitStubs(jEnv);
     JavaDomKeyLocation::InitStubs(jEnv);
     GeckoJavaSampler::InitStubs(jEnv);
+    RestrictedProfiles::InitStubs(jEnv);
     SurfaceBits::InitStubs(jEnv);
     ThumbnailHelper::InitStubs(jEnv);
     DisplayPortMetrics::InitStubs(jEnv);
     GLController::InitStubs(jEnv);
     GeckoLayerClient::InitStubs(jEnv);
     ImmutableViewportMetrics::InitStubs(jEnv);
     LayerView::InitStubs(jEnv);
     NativePanZoomController::InitStubs(jEnv);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -57,25 +57,23 @@ public:
     static void GetMessageWrapper(int32_t a0, int32_t a1);
     static jstring GetMimeTypeFromExtensionsWrapper(const nsAString& a0);
     static void GetNextMessageInListWrapper(int32_t a0, int32_t a1);
     static jstring GetProxyForURIWrapper(const nsAString& a0, const nsAString& a1, const nsAString& a2, int32_t a3);
     static int32_t GetScreenDepthWrapper();
     static int16_t GetScreenOrientationWrapper();
     static bool GetShowPasswordSetting();
     static jintArray GetSystemColoursWrapper();
-    static jstring GetUserRestrictions();
     static void HandleGeckoMessageWrapper(jobject a0);
     static void HandleUncaughtException(jobject a0, jthrowable a1);
     static void HideProgressDialog();
     static jintArray InitCameraWrapper(const nsAString& a0, int32_t a1, int32_t a2, int32_t a3);
     static bool IsNetworkLinkKnown();
     static bool IsNetworkLinkUp();
     static bool IsTablet();
-    static bool IsUserRestricted();
     static void KillAnyZombies();
     static jclass LoadPluginClass(const nsAString& a0, const nsAString& a1);
     static void LockScreenOrientation(int32_t a0);
     static void MarkURIVisited(const nsAString& a0);
     static void MoveTaskToBack();
     static int32_t NetworkLinkType();
     static void NotifyDefaultPrevented(bool a0);
     static void NotifyIME(int32_t a0);
@@ -145,25 +143,23 @@ protected:
     static jmethodID jGetMessageWrapper;
     static jmethodID jGetMimeTypeFromExtensionsWrapper;
     static jmethodID jGetNextMessageInListWrapper;
     static jmethodID jGetProxyForURIWrapper;
     static jmethodID jGetScreenDepthWrapper;
     static jmethodID jGetScreenOrientationWrapper;
     static jmethodID jGetShowPasswordSetting;
     static jmethodID jGetSystemColoursWrapper;
-    static jmethodID jGetUserRestrictions;
     static jmethodID jHandleGeckoMessageWrapper;
     static jmethodID jHandleUncaughtException;
     static jmethodID jHideProgressDialog;
     static jmethodID jInitCameraWrapper;
     static jmethodID jIsNetworkLinkKnown;
     static jmethodID jIsNetworkLinkUp;
     static jmethodID jIsTablet;
-    static jmethodID jIsUserRestricted;
     static jmethodID jKillAnyZombies;
     static jmethodID jLoadPluginClass;
     static jmethodID jLockScreenOrientation;
     static jmethodID jMarkURIVisited;
     static jmethodID jMoveTaskToBack;
     static jmethodID jNetworkLinkType;
     static jmethodID jNotifyDefaultPrevented;
     static jmethodID jNotifyIME;
@@ -241,16 +237,32 @@ protected:
     static jmethodID jGetSampleTimeJavaProfiling;
     static jmethodID jGetThreadNameJavaProfilingWrapper;
     static jmethodID jPauseJavaProfiling;
     static jmethodID jStartJavaProfiling;
     static jmethodID jStopJavaProfiling;
     static jmethodID jUnpauseJavaProfiling;
 };
 
+class RestrictedProfiles : public AutoGlobalWrappedJavaObject {
+public:
+    static void InitStubs(JNIEnv *jEnv);
+    static RestrictedProfiles* Wrap(jobject obj);
+    RestrictedProfiles(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
+    static jstring GetUserRestrictions();
+    static bool IsAllowed(int32_t a0, const nsAString& a1);
+    static bool IsUserRestricted();
+    RestrictedProfiles() : AutoGlobalWrappedJavaObject() {};
+protected:
+    static jclass mRestrictedProfilesClass;
+    static jmethodID jGetUserRestrictions;
+    static jmethodID jIsAllowed;
+    static jmethodID jIsUserRestricted;
+};
+
 class SurfaceBits : public AutoGlobalWrappedJavaObject {
 public:
     static void InitStubs(JNIEnv *jEnv);
     static SurfaceBits* Wrap(jobject obj);
     SurfaceBits(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};
     SurfaceBits();
     jobject getbuffer();
     void setbuffer(jobject a0);