From 03414226cbd8627cbad3bf55c7e75b4d2dd0d807 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Tue, 9 Sep 2014 15:08:37 -0700 Subject: [PATCH] Bug 1058150 - Use restricted profiles for guest mode. r=mfinkle --- mobile/android/base/BrowserApp.java | 12 +- mobile/android/base/GeckoAppShell.java | 34 ----- mobile/android/base/RestrictedProfiles.java | 132 ++++++++++++++++++ mobile/android/base/moz.build | 1 + .../base/tests/testRestrictedProfiles.js | 32 +++-- .../chrome/content/SelectionHandler.js | 4 + mobile/android/chrome/content/browser.js | 38 ++--- mobile/android/chrome/content/downloads.js | 3 +- mobile/android/components/BrowserCLH.js | 8 -- .../locales/en-US/chrome/webapp.properties | 2 +- mobile/android/modules/WebappManager.jsm | 11 +- .../nsIParentalControlsService.idl | 23 ++- .../nsParentalControlsServiceAndroid.cpp | 35 ++++- .../nsParentalControlsServiceCocoa.mm | 9 ++ .../nsParentalControlsServiceDefault.cpp | 8 ++ .../nsParentalControlsServiceWin.cpp | 7 + widget/android/GeneratedJNIWrappers.cpp | 91 ++++++++---- widget/android/GeneratedJNIWrappers.h | 20 ++- 18 files changed, 348 insertions(+), 122 deletions(-) create mode 100644 mobile/android/base/RestrictedProfiles.java diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index bf71ab8769e7..9d69e5c915c9 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -2556,12 +2556,12 @@ public class BrowserApp extends GeckoApp } // 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. diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index a324fce751f1..44c292cb3381 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -104,7 +104,6 @@ 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; @@ -2554,39 +2553,6 @@ public class GeckoAppShell 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 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) { diff --git a/mobile/android/base/RestrictedProfiles.java b/mobile/android/base/RestrictedProfiles.java new file mode 100644 index 000000000000..b49c1cd6e28c --- /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 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 diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index 801a4badba5d..ea98e9e281aa 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -369,6 +369,7 @@ gbjar.sources += [ 'ReaderModeUtils.java', 'RemoteTabsExpandableListAdapter.java', 'Restarter.java', + 'RestrictedProfiles.java', 'ScrollAnimator.java', 'ServiceNotificationClient.java', 'SessionParser.java', diff --git a/mobile/android/base/tests/testRestrictedProfiles.js b/mobile/android/base/tests/testRestrictedProfiles.js index cb66f917740f..e6268d4631b0 100644 --- a/mobile/android/base/tests/testRestrictedProfiles.js +++ b/mobile/android/base/tests/testRestrictedProfiles.js @@ -18,28 +18,38 @@ add_task(function test_isUserRestricted() { // 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(); diff --git a/mobile/android/chrome/content/SelectionHandler.js b/mobile/android/chrome/content/SelectionHandler.js index 3b380fd41b2d..82dc7a949f4c 100644 --- a/mobile/android/chrome/content/SelectionHandler.js +++ b/mobile/android/chrome/content/SelectionHandler.js @@ -652,6 +652,10 @@ var SelectionHandler = { }, selector: { matches: function() { + if (!ParentalControls.isAllowed(ParentalControls.SHARE)) { + return false; + } + return SelectionHandler.isSelectionActive(); } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index b1eaf3e4222d..3b37230e7f97 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -164,6 +164,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetErrorHelper", 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"); @@ -287,7 +290,6 @@ var BrowserApp = { _tabs: [], _selectedTab: null, _prefObservers: [], - isGuest: false, get isTablet() { let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); @@ -434,8 +436,6 @@ var BrowserApp = { gScreenHeight = window.arguments[2]; if (window.arguments[3]) pinned = window.arguments[3]; - if (window.arguments[4]) - this.isGuest = window.arguments[4]; } if (pinned) { @@ -459,7 +459,7 @@ var BrowserApp = { 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); @@ -581,7 +581,7 @@ var BrowserApp = { 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(), @@ -597,7 +597,7 @@ var BrowserApp = { 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); @@ -616,7 +616,7 @@ var BrowserApp = { 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); @@ -633,7 +633,7 @@ var BrowserApp = { }); 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"); @@ -645,7 +645,7 @@ var BrowserApp = { }); 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"); @@ -657,7 +657,7 @@ var BrowserApp = { }); 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"); @@ -694,7 +694,7 @@ var BrowserApp = { 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; @@ -742,7 +742,7 @@ var BrowserApp = { 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; @@ -774,7 +774,7 @@ var BrowserApp = { }); 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"); @@ -2680,11 +2680,13 @@ var NativeWindow = { 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); } }; @@ -4202,8 +4204,8 @@ Tab.prototype = { 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) { diff --git a/mobile/android/chrome/content/downloads.js b/mobile/android/chrome/content/downloads.js index 4c5cf6e48fce..04c656e68970 100644 --- a/mobile/android/chrome/content/downloads.js +++ b/mobile/android/chrome/content/downloads.js @@ -256,11 +256,12 @@ AlertDownloadProgressListener.prototype = { 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"), diff --git a/mobile/android/components/BrowserCLH.js b/mobile/android/components/BrowserCLH.js index a51d4f09c45d..e6325b2f248b 100644 --- a/mobile/android/components/BrowserCLH.js +++ b/mobile/android/components/BrowserCLH.js @@ -18,7 +18,6 @@ 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); @@ -29,13 +28,11 @@ function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) { 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); } @@ -61,7 +58,6 @@ BrowserCLH.prototype = { handle: function fs_handle(aCmdLine) { let openURL = "about:home"; let pinned = false; - let guest = false; let width = 1; let height = 1; @@ -72,9 +68,6 @@ BrowserCLH.prototype = { try { pinned = aCmdLine.handleFlag("webapp", false); } catch (e) { /* Optional */ } - try { - guest = aCmdLine.handleFlag("guest", false); - } catch (e) { /* Optional */ } try { width = aCmdLine.handleFlagWithParam("width", false); @@ -102,7 +95,6 @@ BrowserCLH.prototype = { pinned: pinned, width: width, height: height, - guest: guest }; // Make sure webapps do not have: locationbar, personalbar, menubar, statusbar, and toolbar diff --git a/mobile/android/locales/en-US/chrome/webapp.properties b/mobile/android/locales/en-US/chrome/webapp.properties index e926cc4f26f9..ff4a666d6e77 100644 --- a/mobile/android/locales/en-US/chrome/webapp.properties +++ b/mobile/android/locales/en-US/chrome/webapp.properties @@ -60,4 +60,4 @@ retrievalFailedTitle=#1 update failed;#1 updates 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 diff --git a/mobile/android/modules/WebappManager.jsm b/mobile/android/modules/WebappManager.jsm index 60e7704bdd61..036ab597ad3a 100644 --- a/mobile/android/modules/WebappManager.jsm +++ b/mobile/android/modules/WebappManager.jsm @@ -29,6 +29,9 @@ 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 @@ -89,8 +92,8 @@ this.WebappManager = { }, _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; } @@ -277,10 +280,6 @@ this.WebappManager = { }), - inGuestSession: function() { - return Services.wm.getMostRecentWindow("navigator:browser").BrowserApp.isGuest; - }, - autoInstall: function(aData) { debug("autoInstall " + aData.manifestURL); diff --git a/toolkit/components/parentalcontrols/nsIParentalControlsService.idl b/toolkit/components/parentalcontrols/nsIParentalControlsService.idl index 82701687d7d4..3ae5c2508e4c 100644 --- a/toolkit/components/parentalcontrols/nsIParentalControlsService.idl +++ b/toolkit/components/parentalcontrols/nsIParentalControlsService.idl @@ -11,9 +11,21 @@ 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. @@ -26,6 +38,15 @@ interface nsIParentalControlsService : nsISupports */ 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 diff --git a/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp index dc05165881d6..be64cf35198f 100644 --- a/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp +++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceAndroid.cpp @@ -14,7 +14,7 @@ nsParentalControlsService::nsParentalControlsService() : mEnabled(false) { if (mozilla::AndroidBridge::HasEnv()) { - mEnabled = mozilla::widget::android::GeckoAppShell::IsUserRestricted(); + mEnabled = mozilla::widget::android::RestrictedProfiles::IsUserRestricted(); } } @@ -32,7 +32,11 @@ nsParentalControlsService::GetParentalControlsEnabled(bool *aResult) 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 @@ -65,3 +69,30 @@ nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets, { 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; +} diff --git a/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm b/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm index 313d77828416..09da2c89d47f 100644 --- a/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm +++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm @@ -65,3 +65,12 @@ nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets, { return NS_ERROR_NOT_AVAILABLE; } + +NS_IMETHODIMP +nsParentalControlsService::IsAllowed(int16_t aAction, + nsIURI *aUri, + bool *_retval) +{ + return NS_ERROR_NOT_AVAILABLE; +} + diff --git a/toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp b/toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp index 9db29e1098ae..aa2900c1db26 100644 --- a/toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp +++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceDefault.cpp @@ -60,3 +60,11 @@ nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets, { return NS_ERROR_NOT_AVAILABLE; } + +NS_IMETHODIMP +nsParentalControlsService::IsAllowed(int16_t aAction, + nsIURI *aUri, + bool *_retval) +{ + return NS_ERROR_NOT_AVAILABLE; +} diff --git a/toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp b/toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp index a64ef4f9ccb4..2aac69c33085 100644 --- a/toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp +++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceWin.cpp @@ -331,3 +331,10 @@ nsParentalControlsService::LogFileDownload(bool blocked, nsIURI *aSource, nsIFil gEventWrite(mProvider, &WPCEVENT_WEB_FILEDOWNLOAD, ARRAYSIZE(eventData), eventData); } +NS_IMETHODIMP +nsParentalControlsService::IsAllowed(int16_t aAction, + nsIURI *aUri, + bool *_retval) +{ + return NS_ERROR_NOT_AVAILABLE; +} diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index 68d444fc735f..bcc3561f204a 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -55,7 +55,6 @@ 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; @@ -63,7 +62,6 @@ 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; @@ -144,7 +142,6 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) { 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"); @@ -152,7 +149,6 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) { 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"); @@ -787,19 +783,6 @@ jintArray GeckoAppShell::GetSystemColoursWrapper() { 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(env->PopLocalFrame(temp)); - return ret; -} - void GeckoAppShell::HandleGeckoMessageWrapper(jobject a0) { JNIEnv *env = AndroidBridge::GetJNIEnv(); if (env->PushLocalFrame(1) != 0) { @@ -892,19 +875,6 @@ bool GeckoAppShell::IsTablet() { 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) { @@ -1564,6 +1534,66 @@ void GeckoJavaSampler::UnpauseJavaProfiling() { 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(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; @@ -2545,6 +2575,7 @@ 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); diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 643b1470922c..fee648eddcca 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -62,7 +62,6 @@ public: 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(); @@ -70,7 +69,6 @@ public: 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); @@ -150,7 +148,6 @@ protected: static jmethodID jGetScreenOrientationWrapper; static jmethodID jGetShowPasswordSetting; static jmethodID jGetSystemColoursWrapper; - static jmethodID jGetUserRestrictions; static jmethodID jHandleGeckoMessageWrapper; static jmethodID jHandleUncaughtException; static jmethodID jHideProgressDialog; @@ -158,7 +155,6 @@ protected: static jmethodID jIsNetworkLinkKnown; static jmethodID jIsNetworkLinkUp; static jmethodID jIsTablet; - static jmethodID jIsUserRestricted; static jmethodID jKillAnyZombies; static jmethodID jLoadPluginClass; static jmethodID jLockScreenOrientation; @@ -246,6 +242,22 @@ protected: 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);