From 08bc7dd59ddbaa66b54e505c723c047b32e899d2 Mon Sep 17 00:00:00 2001 From: Stephanie Ouillon Date: Wed, 26 Aug 2015 13:12:13 +0200 Subject: [PATCH] Bug 1178533 - Add nsIInstallPackagedWebapp for registering permissions when navigating to signed packages r=bholley,fabrice,valentin --- b2g/installer/package-manifest.in | 5 + .../preferences/in-content/advanced.js | 3 +- browser/components/preferences/translation.js | 5 +- .../sessionstore/SessionStorage.jsm | 3 +- browser/installer/package-manifest.in | 5 + caps/BasePrincipal.cpp | 22 ++++ caps/BasePrincipal.h | 1 + caps/nsIScriptSecurityManager.idl | 9 +- caps/nsScriptSecurityManager.cpp | 18 +++ caps/tests/unit/test_origin.js | 5 +- dom/apps/PermissionsInstaller.jsm | 9 +- dom/moz.build | 1 + dom/newapps/InstallPackagedWebapp.js | 63 +++++++++ dom/newapps/InstallPackagedWebapp.manifest | 2 + dom/newapps/interfaces/moz.build | 11 ++ .../interfaces/nsIInstallPackagedWebapp.idl | 13 ++ dom/newapps/moz.build | 14 ++ dom/newapps/test/xpcshell/test_install.js | 123 ++++++++++++++++++ dom/newapps/test/xpcshell/xpcshell.ini | 5 + dom/permission/PermissionSettings.jsm | 52 +++++--- dom/push/PushRecord.jsm | 4 +- dom/requestsync/RequestSyncService.jsm | 3 +- .../b2gdroid/installer/package-manifest.in | 4 + mobile/android/installer/package-manifest.in | 4 + netwerk/protocol/http/PackagedAppService.cpp | 43 +++++- netwerk/protocol/http/PackagedAppService.h | 13 +- toolkit/modules/BrowserUtils.jsm | 24 ---- toolkit/modules/PermissionsUtils.jsm | 2 +- 28 files changed, 401 insertions(+), 65 deletions(-) create mode 100644 dom/newapps/InstallPackagedWebapp.js create mode 100644 dom/newapps/InstallPackagedWebapp.manifest create mode 100644 dom/newapps/interfaces/moz.build create mode 100644 dom/newapps/interfaces/nsIInstallPackagedWebapp.idl create mode 100644 dom/newapps/moz.build create mode 100644 dom/newapps/test/xpcshell/test_install.js create mode 100644 dom/newapps/test/xpcshell/xpcshell.ini diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index a8c6c3ad3e6..d125c22875a 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -185,6 +185,7 @@ @RESPATH@/components/dom.xpt @RESPATH@/components/dom_activities.xpt @RESPATH@/components/dom_apps.xpt +@RESPATH@/components/dom_newapps.xpt @RESPATH@/components/dom_audiochannel.xpt @RESPATH@/components/dom_base.xpt @RESPATH@/components/dom_system.xpt @@ -729,6 +730,10 @@ @RESPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@ #endif +; Signed Packaged Content +@RESPATH@/components/InstallPackagedWebapp.manifest +@RESPATH@/components/InstallPackagedWebapp.js + ; ANGLE on Win32 #ifdef XP_WIN32 #ifndef HAVE_64BIT_BUILD diff --git a/browser/components/preferences/in-content/advanced.js b/browser/components/preferences/in-content/advanced.js index 7ec64ba4e6d..90949210aee 100644 --- a/browser/components/preferences/in-content/advanced.js +++ b/browser/components/preferences/in-content/advanced.js @@ -6,7 +6,6 @@ Components.utils.import("resource://gre/modules/DownloadUtils.jsm"); Components.utils.import("resource://gre/modules/LoadContextInfo.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); var gAdvancedPane = { _inited: false, @@ -572,7 +571,7 @@ var gAdvancedPane = { var list = document.getElementById("offlineAppsList"); var item = list.selectedItem; var origin = item.getAttribute("origin"); - var principal = BrowserUtils.principalFromOrigin(origin); + var principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); diff --git a/browser/components/preferences/translation.js b/browser/components/preferences/translation.js index cb977fc394f..c13001c3dcd 100644 --- a/browser/components/preferences/translation.js +++ b/browser/components/preferences/translation.js @@ -9,7 +9,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/BrowserUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "gLangBundle", () => Services.strings.createBundle("chrome://global/locale/languageNames.properties")); @@ -214,7 +213,7 @@ var gTranslationExceptions = { onSiteDeleted: function() { let removedSites = this._siteTree.getSelectedItems(); for (let origin of removedSites) { - let principal = BrowserUtils.principalFromOrigin(origin); + let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); Services.perms.removeFromPrincipal(principal, kPermissionType); } }, @@ -227,7 +226,7 @@ var gTranslationExceptions = { this._siteTree.boxObject.rowCountChanged(0, -removedSites.length); for (let origin of removedSites) { - let principal = BrowserUtils.principalFromOrigin(origin); + let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); Services.perms.removeFromPrincipal(principal, kPermissionType); } diff --git a/browser/components/sessionstore/SessionStorage.jsm b/browser/components/sessionstore/SessionStorage.jsm index 5ed1dbdfa86..a2322e0181f 100644 --- a/browser/components/sessionstore/SessionStorage.jsm +++ b/browser/components/sessionstore/SessionStorage.jsm @@ -9,7 +9,6 @@ this.EXPORTED_SYMBOLS = ["SessionStorage"]; const Cu = Components.utils; const Ci = Components.interfaces; -Cu.import("resource://gre/modules/BrowserUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -105,7 +104,7 @@ var SessionStorageInternal = { restore: function (aDocShell, aStorageData) { for (let origin of Object.keys(aStorageData)) { let data = aStorageData[origin]; - let principal = BrowserUtils.principalFromOrigin(origin); + let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager); let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index f006c903dcb..f68595c74a9 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -193,6 +193,7 @@ @RESPATH@/components/dom_messages.xpt #endif @RESPATH@/components/dom_apps.xpt +@RESPATH@/components/dom_newapps.xpt @RESPATH@/components/dom_base.xpt @RESPATH@/components/dom_system.xpt #ifdef MOZ_B2G_BT @@ -638,6 +639,10 @@ @RESPATH@/components/PrivateBrowsing.manifest @RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js +; Signed Packaged Content +@RESPATH@/components/InstallPackagedWebapp.manifest +@RESPATH@/components/InstallPackagedWebapp.js + ; ANGLE GLES-on-D3D rendering library #ifdef MOZ_ANGLE_RENDERER @BINPATH@/libEGL.dll diff --git a/caps/BasePrincipal.cpp b/caps/BasePrincipal.cpp index 23e2f3fef2e..b5c480e7047 100644 --- a/caps/BasePrincipal.cpp +++ b/caps/BasePrincipal.cpp @@ -426,6 +426,28 @@ BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs) return codebase.forget(); } +already_AddRefed +BasePrincipal::CreateCodebasePrincipal(const nsACString& aOrigin) +{ + MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("[")), + "CreateCodebasePrincipal does not support System and Expanded principals"); + + MOZ_ASSERT(!StringBeginsWith(aOrigin, NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":")), + "CreateCodebasePrincipal does not support nsNullPrincipal"); + + nsAutoCString originNoSuffix; + mozilla::OriginAttributes attrs; + if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) { + return nullptr; + } + + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); + NS_ENSURE_SUCCESS(rv, nullptr); + + return BasePrincipal::CreateCodebasePrincipal(uri, attrs); +} + bool BasePrincipal::AddonAllowsLoad(nsIURI* aURI) { diff --git a/caps/BasePrincipal.h b/caps/BasePrincipal.h index e65d04058ef..7a6e107afeb 100644 --- a/caps/BasePrincipal.h +++ b/caps/BasePrincipal.h @@ -160,6 +160,7 @@ public: static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast(aPrin); } static already_AddRefed CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs); + static already_AddRefed CreateCodebasePrincipal(const nsACString& aOrigin); const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; } uint32_t AppId() const { return mOriginAttributes.mAppId; } diff --git a/caps/nsIScriptSecurityManager.idl b/caps/nsIScriptSecurityManager.idl index 916563d8587..cac4beec6a7 100644 --- a/caps/nsIScriptSecurityManager.idl +++ b/caps/nsIScriptSecurityManager.idl @@ -26,7 +26,7 @@ class DomainPolicyClone; [ptr] native JSObjectPtr(JSObject); [ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone); -[scriptable, uuid(6e8a4d1e-d9c6-4d86-bf53-d73f58f36148)] +[scriptable, uuid(b7ae2310-576e-11e5-a837-0800200c9a66)] interface nsIScriptSecurityManager : nsISupports { /** @@ -197,6 +197,13 @@ interface nsIScriptSecurityManager : nsISupports [implicit_jscontext] nsIPrincipal createCodebasePrincipal(in nsIURI uri, in jsval originAttributes); + /** + * Returns a principal whose origin is the one we pass in. + * See nsIPrincipal.idl for a description of origin attributes, and + * ChromeUtils.webidl for a list of origin attributes and their defaults. + */ + nsIPrincipal createCodebasePrincipalFromOrigin(in ACString origin); + /** * Returns a unique nonce principal with |originAttributes|. * See nsIPrincipal.idl for a description of origin attributes, and diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index a2309cb29c2..2352177464a 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -67,6 +67,7 @@ #include "nsContentUtils.h" #include "nsJSUtils.h" #include "nsILoadInfo.h" +#include "nsXPCOMStrings.h" // This should be probably defined on some other place... but I couldn't find it #define WEBAPPS_PERM_NAME "webapps-manage" @@ -1043,6 +1044,23 @@ nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, JS::Handle prin = BasePrincipal::CreateCodebasePrincipal(aOrigin); + prin.forget(aPrincipal); + return *aPrincipal ? NS_OK : NS_ERROR_FAILURE; +} + NS_IMETHODIMP nsScriptSecurityManager::CreateNullPrincipal(JS::Handle aOriginAttributes, JSContext* aCx, nsIPrincipal** aPrincipal) diff --git a/caps/tests/unit/test_origin.js b/caps/tests/unit/test_origin.js index 8a84102d2d8..ff2d2449ab5 100644 --- a/caps/tests/unit/test_origin.js +++ b/caps/tests/unit/test_origin.js @@ -3,7 +3,6 @@ const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/BrowserUtils.jsm"); var ssm = Services.scriptSecurityManager; function makeURI(uri) { return Services.io.newURI(uri, null, null); } @@ -28,9 +27,9 @@ function checkOriginAttributes(prin, attrs, suffix) { do_check_eq(prin.originAttributes.inBrowser, attrs.inBrowser || false); do_check_eq(prin.originSuffix, suffix || ''); if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) { - do_check_true(BrowserUtils.principalFromOrigin(prin.origin).equals(prin)); + do_check_true(ssm.createCodebasePrincipalFromOrigin(prin.origin).equals(prin)); } else { - checkThrows(() => BrowserUtils.principalFromOrigin(prin.origin)); + checkThrows(() => ssm.createCodebasePrincipalFromOrigin(prin.origin)); } } diff --git a/dom/apps/PermissionsInstaller.jsm b/dom/apps/PermissionsInstaller.jsm index 208b681dfbf..e3ed3aca83d 100644 --- a/dom/apps/PermissionsInstaller.jsm +++ b/dom/apps/PermissionsInstaller.jsm @@ -164,7 +164,8 @@ this.PermissionsInstaller = { PermissionSettingsModule.getPermission(expandedPermNames[idx], aApp.manifestURL, aApp.origin, - false); + false, + aApp.isCachedPackage); if (permValue === "unknown") { permValue = PERM_TO_STRING[permission]; } @@ -192,7 +193,7 @@ this.PermissionsInstaller = { * The permission value. * @param object aApp * The just-installed app configuration. - * The properties used are manifestURL and origin. + * The properties used are manifestURL, origin, appId, isCachedPackage. * @returns void **/ _setPermission: function setPermission(aPermName, aPermValue, aApp) { @@ -201,7 +202,9 @@ this.PermissionsInstaller = { origin: aApp.origin, manifestURL: aApp.manifestURL, value: aPermValue, - browserFlag: false + browserFlag: false, + localId: aApp.localId, + isCachedPackage: aApp.isCachedPackage, }); } }; diff --git a/dom/moz.build b/dom/moz.build index 4279c8ff757..59c67682ceb 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -113,6 +113,7 @@ DIRS += [ 'resourcestats', 'manifest', 'vr', + 'newapps', ] if CONFIG['OS_ARCH'] == 'WINNT': diff --git a/dom/newapps/InstallPackagedWebapp.js b/dom/newapps/InstallPackagedWebapp.js new file mode 100644 index 00000000000..5577e23b0db --- /dev/null +++ b/dom/newapps/InstallPackagedWebapp.js @@ -0,0 +1,63 @@ +/* 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/. */ + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr, Constructor: CC } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/AppsUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PermissionsInstaller", + "resource://gre/modules/PermissionsInstaller.jsm"); + +function debug(aMsg) { + dump("-*-*- InstallPackagedWebapps.js : " + aMsg + "\n"); +} + +function InstallPackagedWebapp() { +} + +InstallPackagedWebapp.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIInstallPackagedWebapp]), + classID: Components.ID("{5cc6554a-5421-4a5e-b8c2-c62e8b7f4f3f}"), + + /** + * Install permissions for signed packaged web content + * @param string manifestContent + * The manifest content of the cached package. + * @param string aOrigin + * The package origin. + * @param string aManifestURL + * The manifest URL of the package. + * @returns boolean + **/ + + installPackagedWebapp: function(aManifestContent, aOrigin, aManifestURL) { + + try { + let isSuccess = true; + let manifest = JSON.parse(aManifestContent); + + PermissionsInstaller.installPermissions({ + manifest: manifest, + manifestURL: aManifestURL, + origin: aOrigin, + isPreinstalled: false, + isCachedPackage: true + }, false, function() { + Cu.reportError(ex); + }); + + // TODO Bug 1206058 - Register app handlers (system msg) on navigation + // to signed packages. + + return isSuccess; + } + catch(ex) { + Cu.reportError(ex); + return false; + } + }, +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InstallPackagedWebapp]); diff --git a/dom/newapps/InstallPackagedWebapp.manifest b/dom/newapps/InstallPackagedWebapp.manifest new file mode 100644 index 00000000000..62699dc434f --- /dev/null +++ b/dom/newapps/InstallPackagedWebapp.manifest @@ -0,0 +1,2 @@ +component {5cc6554a-5421-4a5e-b8c2-c62e8b7f4f3f} InstallPackagedWebapp.js +contract @mozilla.org/newapps/installpackagedwebapp;1 {5cc6554a-5421-4a5e-b8c2-c62e8b7f4f3f} diff --git a/dom/newapps/interfaces/moz.build b/dom/newapps/interfaces/moz.build new file mode 100644 index 00000000000..f9eb7a739b0 --- /dev/null +++ b/dom/newapps/interfaces/moz.build @@ -0,0 +1,11 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# 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/. + +XPIDL_SOURCES += [ + 'nsIInstallPackagedWebapp.idl' +] + +XPIDL_MODULE = 'dom_newapps' diff --git a/dom/newapps/interfaces/nsIInstallPackagedWebapp.idl b/dom/newapps/interfaces/nsIInstallPackagedWebapp.idl new file mode 100644 index 00000000000..b61ae1ed9ef --- /dev/null +++ b/dom/newapps/interfaces/nsIInstallPackagedWebapp.idl @@ -0,0 +1,13 @@ +/* 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/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(3b4b69a0-56dc-11e5-a837-0800200c9a66)] +interface nsIInstallPackagedWebapp : nsISupports +{ + boolean installPackagedWebapp(in string aManifestContent, + in string aOrigin, + in string aManifestURL); +}; diff --git a/dom/newapps/moz.build b/dom/newapps/moz.build new file mode 100644 index 00000000000..f213894e296 --- /dev/null +++ b/dom/newapps/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# 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/. + +DIRS += ['interfaces'] + +EXTRA_COMPONENTS += [ + 'InstallPackagedWebapp.js', + 'InstallPackagedWebapp.manifest', +] + +XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini'] diff --git a/dom/newapps/test/xpcshell/test_install.js b/dom/newapps/test/xpcshell/test_install.js new file mode 100644 index 00000000000..f288597b8bc --- /dev/null +++ b/dom/newapps/test/xpcshell/test_install.js @@ -0,0 +1,123 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); +Cu.import("resource://gre/modules/PermissionSettings.jsm"); +Cu.import("resource://gre/modules/PermissionsTable.jsm"); +Cu.import("resource://gre/modules/AppsUtils.jsm"); + +const mod = Cc['@mozilla.org/newapps/installpackagedwebapp;1'] + .getService(Ci.nsIInstallPackagedWebapp); + +XPCOMUtils.defineLazyServiceGetter(this, + "appsService", + "@mozilla.org/AppsService;1", + "nsIAppsService"); + +function run_test() { + + do_get_profile(); + + let manifestWithPerms = { + name: "Test App", + launch_path: "/index.html", + type: "privileged", + permissions: { + "alarms": { }, + "wifi-manage": { }, + "tcp-socket": { }, + "desktop-notification": { }, + "geolocation": { }, + }, + }; + + let manifestNoPerms = { + name: "Test App", + launch_path: "/index.html", + type: "privileged", + }; + + let appStatus = "privileged"; + + // Triggering error due to bad manifest + let origin = ""; + let manifestURL = ""; + let manifestString = "boum"; + + let res = mod.installPackagedWebapp(manifestString, origin, manifestURL); + equal(res, false); + + // Install a package with permissions + origin = "http://test.com^appId=1019&inBrowser=1"; + manifestURL = "http://test.com/manifest.json"; + manifestString = JSON.stringify(manifestWithPerms); + let manifestHelper = new ManifestHelper(manifestWithPerms, origin, manifestURL); + + cleanDB(manifestHelper, origin, manifestURL); + + res = mod.installPackagedWebapp(manifestString, origin, manifestURL); + equal(res, true); + checkPermissions(manifestHelper, origin, manifestURL, appStatus); + + // Install a package with permissions + origin = "http://test.com"; + manifestHelper = new ManifestHelper(manifestWithPerms, origin, manifestURL); + + cleanDB(manifestHelper, origin, manifestURL); + + res = mod.installPackagedWebapp(manifestString, origin, manifestURL); + equal(res, true); + checkPermissions(manifestHelper, origin, manifestURL, appStatus); + + + // Install a package with no permission + origin = "http://bar.com^appId=1337&inBrowser=1"; + manifestURL = "http://bar.com/manifest.json"; + manifestString = JSON.stringify(manifestNoPerms); + manifestHelper = new ManifestHelper(manifestNoPerms, origin, manifestURL); + + cleanDB(manifestHelper, origin, manifestURL); + + res = mod.installPackagedWebapp(manifestString, origin, manifestURL); + equal(res, true); + checkPermissions(manifestHelper, origin, manifestURL, appStatus); +} + +// Cleaning permissions database before running a test +function cleanDB(manifestHelper, origin, manifestURL) { + for (let permName in manifestHelper.permissions) { + PermissionSettingsModule.removePermission(permName, manifestURL, origin, "", true); + } +} + +// Check permissions are correctly set in the database +function checkPermissions(manifestHelper, origin, manifestURL, appStatus) { + let perm; + for (let permName in manifestHelper.permissions) { + let permValue = PermissionSettingsModule.getPermission( + permName, manifestURL, origin, "", true); + switch (PermissionsTable[permName][appStatus]) { + case Ci.nsIPermissionManager.UNKNOWN_ACTION: + perm = "unknown"; + break; + case Ci.nsIPermissionManager.ALLOW_ACTION: + perm = "allow"; + break; + case Ci.nsIPermissionManager.DENY_ACTION: + perm = "deny"; + break; + case Ci.nsIPermissionManager.PROMPT_ACTION: + perm = "prompt"; + break; + default: + break; + } + equal(permValue, perm); + } +} diff --git a/dom/newapps/test/xpcshell/xpcshell.ini b/dom/newapps/test/xpcshell/xpcshell.ini new file mode 100644 index 00000000000..5c1329e6e8b --- /dev/null +++ b/dom/newapps/test/xpcshell/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = +tail = + +[test_install.js] diff --git a/dom/permission/PermissionSettings.jsm b/dom/permission/PermissionSettings.jsm index 8cda5e09a04..ddd3a19c45d 100644 --- a/dom/permission/PermissionSettings.jsm +++ b/dom/permission/PermissionSettings.jsm @@ -67,13 +67,23 @@ this.PermissionSettingsModule = { _internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) { - // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm - let uri = Services.io.newURI(aData.origin, null, null); - let app = appsService.getAppByManifestURL(aData.manifestURL); - let principal = - Services.scriptSecurityManager.createCodebasePrincipal(uri, - {appId: app.localId, - inBrowser: aData.browserFlag}); + // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm. + let app; + let principal; + // Test if app is cached (signed streamable package) or installed via DOMApplicationRegistry + if (aData.isCachedPackage) { + // If the app is from packaged web app, the origin includes origin attributes already. + principal = + Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(aData.origin); + app = {localId: principal.appId}; + } else { + app = appsService.getAppByManifestURL(aData.manifestURL); + let uri = Services.io.newURI(aData.origin, null, null); + principal = + Services.scriptSecurityManager.createCodebasePrincipal(uri, + {appId: app.localId, + inBrowser: aData.browserFlag}); + } let action; switch (aData.value) @@ -106,17 +116,24 @@ this.PermissionSettingsModule = { } }, - getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) { + getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag, aIsCachedPackage) { // TODO: Bug 1196644 - Add signPKg parameter into PermissionSettings.jsm debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin); - let uri = Services.io.newURI(aOrigin, null, null); - let appID = appsService.getAppLocalIdByManifestURL(aManifestURL); - let principal = - Services.scriptSecurityManager.createCodebasePrincipal(uri, - {appId: appID, - inBrowser: aBrowserFlag}); + let principal; + // Test if app is cached (signed streamable package) or installed via DOMApplicationRegistry + if (aIsCachedPackage) { + // If the app is from packaged web app, the origin includes origin attributes already. + principal = + Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(aOrigin); + } else { + let uri = Services.io.newURI(aOrigin, null, null); + let appID = appsService.getAppLocalIdByManifestURL(aManifestURL); + principal = + Services.scriptSecurityManager.createCodebasePrincipal(uri, + {appId: appID, + inBrowser: aBrowserFlag}); + } let result = Services.perms.testExactPermissionFromPrincipal(principal, aPermName); - switch (result) { case Ci.nsIPermissionManager.UNKNOWN_ACTION: @@ -133,13 +150,14 @@ this.PermissionSettingsModule = { } }, - removePermission: function removePermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) { + removePermission: function removePermission(aPermName, aManifestURL, aOrigin, aBrowserFlag, aIsCachedPackage) { let data = { type: aPermName, origin: aOrigin, manifestURL: aManifestURL, value: "unknown", - browserFlag: aBrowserFlag + browserFlag: aBrowserFlag, + isCachedPackage: aIsCachedPackage }; this._internalAddPermission(data, true); }, diff --git a/dom/push/PushRecord.jsm b/dom/push/PushRecord.jsm index a468ddd39cd..b2e3c0e2415 100644 --- a/dom/push/PushRecord.jsm +++ b/dom/push/PushRecord.jsm @@ -19,8 +19,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", - "resource://gre/modules/BrowserUtils.jsm"); this.EXPORTED_SYMBOLS = ["PushRecord"]; @@ -218,7 +216,7 @@ Object.defineProperties(PushRecord.prototype, { // Allow tests to omit origin attributes. url += this.originAttributes; } - principal = BrowserUtils.principalFromOrigin(url); + principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(url); principals.set(this, principal); } return principal; diff --git a/dom/requestsync/RequestSyncService.jsm b/dom/requestsync/RequestSyncService.jsm index c771f6438e1..4e6bdda0697 100644 --- a/dom/requestsync/RequestSyncService.jsm +++ b/dom/requestsync/RequestSyncService.jsm @@ -20,7 +20,6 @@ const RSYNC_STATE_ENABLED = "enabled"; const RSYNC_STATE_DISABLED = "disabled"; const RSYNC_STATE_WIFIONLY = "wifiOnly"; -Cu.import("resource://gre/modules/BrowserUtils.jsm"); Cu.import('resource://gre/modules/IndexedDBHelper.jsm'); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -175,7 +174,7 @@ this.RequestSyncService = { let dbKeys = []; for (let key in this._registrations) { - let prin = BrowserUtils.principalFromOrigin(key); + let prin = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(key); if (!ChromeUtils.originAttributesMatchPattern(prin.originAttributes, pattern)) { continue; } diff --git a/mobile/android/b2gdroid/installer/package-manifest.in b/mobile/android/b2gdroid/installer/package-manifest.in index c38e5b8f910..696b5f4411d 100644 --- a/mobile/android/b2gdroid/installer/package-manifest.in +++ b/mobile/android/b2gdroid/installer/package-manifest.in @@ -138,6 +138,7 @@ @BINPATH@/components/dom.xpt @BINPATH@/components/dom_activities.xpt @BINPATH@/components/dom_apps.xpt +@BINPATH@/components/dom_newapps.xpt @BINPATH@/components/dom_base.xpt @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_core.xpt @@ -677,6 +678,9 @@ bin/libfreebl_32int64_3.so @BINPATH@/components/SystemMessageCache.js @BINPATH@/components/SystemMessageManager.manifest +@BINPATH@/components/InstallPackagedWebapp.manifest +@BINPATH@/components/InstallPackagedWebapp.js + @BINPATH@/components/B2GComponents.manifest @BINPATH@/components/AlertsService.js @BINPATH@/components/ContentPermissionPrompt.js diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index 68065601ae2..6c2af32f2dd 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -138,6 +138,7 @@ @BINPATH@/components/dom.xpt @BINPATH@/components/dom_activities.xpt @BINPATH@/components/dom_apps.xpt +@BINPATH@/components/dom_newapps.xpt @BINPATH@/components/dom_base.xpt @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_core.xpt @@ -427,6 +428,9 @@ @BINPATH@/components/SystemMessageCache.js @BINPATH@/components/SystemMessageManager.manifest +@BINPATH@/components/InstallPackagedWebapp.manifest +@BINPATH@/components/InstallPackagedWebapp.js + #ifdef MOZ_WEBRTC @BINPATH@/components/PeerConnection.js @BINPATH@/components/PeerConnection.manifest diff --git a/netwerk/protocol/http/PackagedAppService.cpp b/netwerk/protocol/http/PackagedAppService.cpp index 0e7be513d5c..bd9cc60dbf5 100644 --- a/netwerk/protocol/http/PackagedAppService.cpp +++ b/netwerk/protocol/http/PackagedAppService.cpp @@ -17,6 +17,7 @@ #include "mozilla/DebugOnly.h" #include "nsIHttpHeaderVisitor.h" #include "mozilla/LoadContext.h" +#include "nsIInstallPackagedWebapp.h" namespace mozilla { namespace net { @@ -427,6 +428,7 @@ PackagedAppService::PackagedAppDownloader::Init(nsILoadContextInfo* aInfo, mPackageKey = aKey; mPackageOrigin = aPackageOrigin; + mProcessingFirstRequest = true; return NS_OK; } @@ -620,6 +622,8 @@ PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest, LOG(("[%p] PackagedAppDownloader::OnStopRequest > status:%X multiChannel:%p\n", this, aStatusCode, multiChannel.get())); + mProcessingFirstRequest = false; + // lastPart will be true if this is the last part in the package, // or if aRequest isn't a multipart channel bool lastPart = true; @@ -700,6 +704,11 @@ PackagedAppService::PackagedAppDownloader::ConsumeData(nsIInputStream *aStream, self->mWriter->ConsumeData(aFromRawSegment, aCount, aWriteCount); + if (self->mProcessingFirstRequest) { + // mProcessingFirstRequest will be set to false on the first OnStopRequest. + self->mManifestContent.Append(aFromRawSegment, aCount); + } + nsCOMPtr stream = CreateSharedStringStream(aFromRawSegment, aCount); return self->mVerifier->OnDataAvailable(nullptr, nullptr, stream, 0, aCount); } @@ -855,11 +864,39 @@ PackagedAppService::PackagedAppDownloader::NotifyOnStartSignedPackageRequest(con LOG(("Notifying the signed package is ready to load.")); } -void PackagedAppService::PackagedAppDownloader::InstallSignedPackagedApp() +void PackagedAppService::PackagedAppDownloader::InstallSignedPackagedApp(const ResourceCacheInfo* aInfo) { // TODO: Bug 1178533 to register permissions, system messages etc on navigation to // signed packages. LOG(("Install this packaged app.")); + bool isSuccess = false; + + nsCOMPtr installer = + do_GetService("@mozilla.org/newapps/installpackagedwebapp;1"); + + if (!installer) { + LOG(("InstallSignedPackagedApp: fail to get InstallPackagedWebapp service")); + return OnError(ERROR_GET_INSTALLER_FAILED); + } + + nsCString manifestURL; + aInfo->mURI->GetAsciiSpec(manifestURL); + + // Use the origin stored in the verifier since the signed packaged app would + // have a specifi package identifer defined in the manifest file. + nsCString packageOrigin; + mVerifier->GetPackageOrigin(packageOrigin); + + installer->InstallPackagedWebapp(mManifestContent.get(), + packageOrigin.get(), + manifestURL.get(), + &isSuccess); + if (!isSuccess) { + LOG(("InstallSignedPackagedApp: failed to install permissions")); + return OnError(ERROR_INSTALL_RESOURCE_FAILED); + } + + LOG(("InstallSignedPackagedApp: success.")); } //------------------------------------------------------------------ @@ -917,7 +954,7 @@ PackagedAppService::PackagedAppDownloader::OnManifestVerified(const ResourceCach nsCString packageOrigin; mVerifier->GetPackageOrigin(packageOrigin); NotifyOnStartSignedPackageRequest(packageOrigin); - InstallSignedPackagedApp(); + InstallSignedPackagedApp(aInfo); } void @@ -1084,7 +1121,7 @@ PackagedAppService::GetResource(nsIChannel *aChannel, downloader = new PackagedAppDownloader(); nsCString packageOrigin; - principal->GetOriginNoSuffix(packageOrigin); + principal->GetOrigin(packageOrigin); rv = downloader->Init(loadContextInfo, key, packageOrigin); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/netwerk/protocol/http/PackagedAppService.h b/netwerk/protocol/http/PackagedAppService.h index 4dd04dd0080..e1ae1b01d25 100644 --- a/netwerk/protocol/http/PackagedAppService.h +++ b/netwerk/protocol/http/PackagedAppService.h @@ -106,6 +106,8 @@ private: enum EErrorType { ERROR_MANIFEST_VERIFIED_FAILED, ERROR_RESOURCE_VERIFIED_FAILED, + ERROR_GET_INSTALLER_FAILED, + ERROR_INSTALL_RESOURCE_FAILED, }; public: @@ -172,7 +174,7 @@ private: // Handle all tasks about app installation like permission and system message // registration. - void InstallSignedPackagedApp(); + void InstallSignedPackagedApp(const ResourceCacheInfo* aInfo); // Calls all the callbacks registered for the given URI. // aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath @@ -211,6 +213,15 @@ private: // If you need the origin with the signity taken into account, use // PackagedAppVerifier::GetPackageOrigin(). nsCString mPackageOrigin; + + //The app id of the package loaded from the LoadContextInfo + uint32_t mAppId; + + // A flag to indicate if we are processing the first request. + bool mProcessingFirstRequest; + + // A in-memory copy of the manifest content. + nsCString mManifestContent; }; // Intercepts OnStartRequest, OnDataAvailable*, OnStopRequest method calls diff --git a/toolkit/modules/BrowserUtils.jsm b/toolkit/modules/BrowserUtils.jsm index bdeacf91840..920c18ade00 100644 --- a/toolkit/modules/BrowserUtils.jsm +++ b/toolkit/modules/BrowserUtils.jsm @@ -100,30 +100,6 @@ this.BrowserUtils = { return Services.io.newURI(aCPOWURI.spec, aCPOWURI.originCharset, null); }, - // Creates a codebase principal from a canonical origin string. This is - // the inverse operation of .origin on a codebase principal. - principalFromOrigin: function(aOriginString) { - if (aOriginString.startsWith('[')) { - throw new Error("principalFromOrigin does not support System and Expanded principals"); - } - - if (aOriginString.startsWith("moz-nullprincipal:")) { - throw new Error("principalFromOrigin does not support nsNullPrincipal"); - } - - var parts = aOriginString.split('^'); - if (parts.length > 2) { - throw new Error("bad origin string: " + aOriginString); - } - - var uri = Services.io.newURI(parts[0], null, null); - var attrs = {}; - // Parse the parameters string into a dictionary. - (parts[1] || "").split("&").map((x) => x.split('=')).forEach((x) => attrs[x[0]] = x[1]); - - return Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs); - }, - /** * For a given DOM element, returns its position in "screen" * coordinates. In a content process, the coordinates returned will diff --git a/toolkit/modules/PermissionsUtils.jsm b/toolkit/modules/PermissionsUtils.jsm index 3f95437fb24..3f731a3fd94 100644 --- a/toolkit/modules/PermissionsUtils.jsm +++ b/toolkit/modules/PermissionsUtils.jsm @@ -29,7 +29,7 @@ function importPrefBranch(aPrefBranch, aPermission, aAction) { for (let origin of origins) { let principals = []; try { - principals = [ BrowserUtils.principalFromOrigin(origin) ]; + principals = [ Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin) ]; } catch (e) { // This preference used to contain a list of hosts. For back-compat // reasons, we convert these hosts into http:// and https:// permissions