diff --git a/docshell/base/nsDefaultURIFixup.h b/docshell/base/nsDefaultURIFixup.h index 5e19ee1e76..4a37438e93 100644 --- a/docshell/base/nsDefaultURIFixup.h +++ b/docshell/base/nsDefaultURIFixup.h @@ -65,6 +65,6 @@ private: bool mFixupCreatedAlternateURI; nsString mKeywordProviderName; nsString mKeywordAsSent; - nsAutoCString mOriginalInput; + nsCString mOriginalInput; }; #endif diff --git a/docshell/base/timeline/RestyleTimelineMarker.h b/docshell/base/timeline/RestyleTimelineMarker.h index b5d5a02ef2..fcc37b05d8 100644 --- a/docshell/base/timeline/RestyleTimelineMarker.h +++ b/docshell/base/timeline/RestyleTimelineMarker.h @@ -34,7 +34,7 @@ public: } private: - nsAutoString mRestyleHint; + nsString mRestyleHint; }; } // namespace mozilla diff --git a/docshell/base/timeline/TimelineConsumers.cpp b/docshell/base/timeline/TimelineConsumers.cpp index 788139a512..04da39d3c6 100644 --- a/docshell/base/timeline/TimelineConsumers.cpp +++ b/docshell/base/timeline/TimelineConsumers.cpp @@ -60,7 +60,6 @@ TimelineConsumers::Get() if (sInstance->Init()) { ClearOnShutdown(&sInstance); } else { - NS_WARNING("TimelineConsumers could not be initialized."); sInstance->RemoveObservers(); sInstance = nullptr; } diff --git a/docshell/base/timeline/moz.build b/docshell/base/timeline/moz.build index c88e24021f..69f126596e 100644 --- a/docshell/base/timeline/moz.build +++ b/docshell/base/timeline/moz.build @@ -38,3 +38,6 @@ FINAL_LIBRARY = 'xul' LOCAL_INCLUDES += [ '/docshell/base' ] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/docshell/base/timeline/readme.md b/docshell/base/timeline/readme.md new file mode 100644 index 0000000000..2af9ed3a27 --- /dev/null +++ b/docshell/base/timeline/readme.md @@ -0,0 +1,97 @@ + +#Timeline + +The files in this directory are concerned with providing the backend platform features required for the developer tools interested in tracking down operations done in Gecko. The mechanism we use to define these operations are `markers`. + +Examples of traced operations include: + +* Style Recalculation +* Layout +* Painting +* JavaScript run-to-completion +* HTML parsing +* etc. + +The traced operations are displayed in the DevTools Performance tool's timeline. + +This is an overview of how everything works and can be extended. + +##MarkersStorage +A `MarkersStorage` is an abstract class defining a place where timeline markers may be held. It defines an interface with pure virtual functions to highlight how this storage can be interacted with: + +- `AddMarker`: adding a marker, from the main thread only +- `AddOTMTMarker`: adding a marker off the main thread only +- `ClearMarkers`: clearing all accumulated markers (both from the main thread and off it) +- `PopMarkers`: popping all accumulated markers (both from the main thread and off it). + +Note on why we handle on/off the main thread markers separately: since most of our markers will come from the main thread, we can be a little more efficient and avoid dealing with multithreading scenarios until all the markers are actually cleared or popped in `ClearMarkers` or `PopMarkers`. Main thread markers may only be added via `AddMarker`, while off the main thread markers may only be added via `AddOTMTMarker`. Clearing and popping markers will yield until all operations involving off the main thread markers finish. When popping, the markers accumulated off the main thread will be moved over. We expect popping to be fairly infrequent (every few hundred milliseconds, currently we schedule this to happen every 200ms). + +##ObservedDocShell +The only implementation of a MarkersStorage we have right now is an `ObservedDocShell`. + +Instances of `ObservedDocShell` accumulate markers that are *mostly* about a particular docshell. At a high level, for example, an `ObservedDocshell` would be created when a timeline tool is opened on a page. It is reasonable to assume that most operations which are interesting for that particular page happen on the main thread. However certain operations may happen outside of it, yet are interesting for its developers, for which markers can be created as well (e.g. web audio stuff, service workers etc.). It is also reasonable to assume that a docshell may sometimes not be easily accessible from certain parts of the platform code, but for which markers still need to be created. + +Therefore, the following scenarios arise: + +- a). creating a marker on the main thread about a particular dochsell + +- b). creating a marker on the main thread without pinpointing to an affected docshell (unlikely, but allowed; in this case such a marker would have to be stored in all currently existing `ObservedDocShell` instances) + +- c). creating a marker off the main thread about a particular dochsell (impossible; docshells can't be referenced outside the main thread, in which case some other type of identification mechanism needs to be put in place). + +- d). creating a marker off the main thread without pinpointing to a particular docshell (same path as c. here, such a marker would have to be stored in all currently existing `ObservedDocShell` instances). + +An observed docshell (in other words, "a docshell for which a timeline tool was opened") can thus receive both main thread and off the main thread markers. + +Cross-process markers are unnecessary at the moment, but tracked in bug 1200120. + +##TimelineConsumers +A `TimelineConsumer` is a singleton that facilitates access to `ObservedDocShell` instances. This is where a docshell can register/unregister itself as being observed via the `AddConsumer` and `RemoveConsumer` methods. + +All markers may only be stored via this singleton. Certain helper methods are available: + +* Main thread only +`AddMarkerForDocShell(nsDocShell*, const char*, MarkerTracingType)` +`AddMarkerForDocShell(nsDocShell*, const char*, const TimeStamp&, MarkerTracingType)` +`AddMarkerForDocShell(nsDocShell*, UniquePtr&&)` + +* Any thread +`AddMarkerForAllObservedDocShells(const char*, MarkerTracingType)` +`AddMarkerForAllObservedDocShells(const char*, const TimeStamp&, MarkerTracingType)` +`AddMarkerForAllObservedDocShells(UniquePtr&)` + +The "main thread only" methods deal with point a). described above. The "any thread" methods deal with points b). and d). + +##AbstractTimelineMarker + +All markers inherit from this abstract class, providing a simple thread-safe extendable blueprint. + +Markers are readonly after instantiation, and will always be identified by a name, a timestamp and their tracing type (`START`, `END`, `TIMESTAMP`). It *should not* make sense to modify their data after their creation. + +There are only two accessible constructors: +`AbstractTimelineMarker(const char*, MarkerTracingType)` +`AbstractTimelineMarker(const char*, const TimeStamp&, MarkerTracingType)` +which create a marker with a name and a tracing type. If unspecified, the corresponding timestamp will be the current instantiation time. Instantiating a marker *much later* after a particular operation is possible, but be careful providing the correct timestamp. + +The `AddDetails` virtual method should be implemented by subclasses when creating WebIDL versions of these markers, which will be sent over to a JavaScript frontend. + +##TimelineMarker +A `TimelineMarker` is the main `AbstractTimelineMarker` implementation. They allow attaching a JavaScript stack on `START` and `TIMESTAMP` markers. + +These markers will be created when using the `TimelineConsumers` helper methods which take in a string, a tracing type and (optionally) a timestamp. For more complex markers, subclasses are encouraged. See `EventTimelineMarker` or `ConsoleTimelineMarker` for some examples. + +##RAII + +### mozilla::AutoTimelineMarker + +The easiest way to trace Gecko events/tasks with start and end timeline markers is to use the `mozilla::AutoTimelineMarker` RAII class. It automatically adds the start marker on construction, and adds the end marker on destruction. Don't worry too much about potential performance impact! It only actually adds the markers when the given docshell is being observed by a timeline consumer, so essentially nothing will happen if a tool to inspect those markers isn't specifically open. + +This class may only be used on the main thread, and pointer to a docshell is necessary. If the docshell is a nullptr, nothing happens and this operation fails silently. + +Example: `AutoTimelineMarker marker(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML");` + +### mozilla::AutoGlobalTimelineMarker` + +Similar to the previous RAII class, but doesn't expect a specific docshell, and the marker will be visible in all timeline consumers. This is useful for generic operations that don't involve a particular dochsell, or where a docshell isn't accessible. May also only be used on the main thread. + +Example: `AutoGlobalTimelineMarker marker("Some global operation");` diff --git a/dom/apps/AndroidUtils.jsm b/dom/apps/AndroidUtils.jsm new file mode 100644 index 0000000000..53fb4002cf --- /dev/null +++ b/dom/apps/AndroidUtils.jsm @@ -0,0 +1,123 @@ +/* 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 { interfaces: Ci, utils: Cu } = Components; + +this.EXPORTED_SYMBOLS = ["AndroidUtils"]; + +Cu.import("resource://gre/modules/AppsUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Messaging", + "resource://gre/modules/Messaging.jsm"); + +let appsRegistry = null; + +function debug() { + //dump("-*- AndroidUtils " + Array.slice(arguments) + "\n"); +} + +// Helper functions to manage Android native apps. We keep them in the +// registry with a `kind` equals to "android-native" and we also store +// the package name and class name in the registry. +// Communication with the android side happens through json messages. + +this.AndroidUtils = { + init: function(aRegistry) { + appsRegistry = aRegistry; + Services.obs.addObserver(this, "Android:Apps:Installed", false); + Services.obs.addObserver(this, "Android:Apps:Uninstalled", false); + }, + + uninit: function() { + Services.obs.removeObserver(this, "Android:Apps:Installed"); + Services.obs.removeObserver(this, "Android:Apps:Uninstalled"); + }, + + getOriginAndManifestURL: function(aPackageName) { + let origin = "android://" + aPackageName.toLowerCase(); + let manifestURL = origin + "/manifest.webapp"; + return [origin, manifestURL]; + }, + + getPackageAndClassFromManifestURL: function(aManifestURL) { + debug("getPackageAndClassFromManifestURL " + aManifestURL); + let app = appsRegistry.getAppByManifestURL(aManifestURL); + if (!app) { + debug("No app for " + aManifestURL); + return []; + } + return [app.android_packagename, app.android_classname]; + }, + + buildAndroidAppData: function(aApp) { + // Use the package and class name to get a unique origin. + // We put the version with the normal case as part of the manifest url. + let [origin, manifestURL] = + this.getOriginAndManifestURL(aApp.packagename); + // TODO: Bug 1204557 to improve the icons support. + let manifest = { + name: aApp.name, + icons: { "96": aApp.icon } + } + debug("Origin is " + origin); + let appData = { + app: { + installOrigin: origin, + origin: origin, + manifest: manifest, + manifestURL: manifestURL, + manifestHash: AppsUtils.computeHash(JSON.stringify(manifest)), + appStatus: Ci.nsIPrincipal.APP_STATUS_INSTALLED, + removable: aApp.removable, + android_packagename: aApp.packagename, + android_classname: aApp.classname + }, + isBrowser: false, + isPackage: false + }; + + return appData; + }, + + installAndroidApps: function() { + return Messaging.sendRequestForResult({ type: "Apps:GetList" }).then( + aApps => { + debug("Got " + aApps.apps.length + " android apps."); + let promises = []; + aApps.apps.forEach(app => { + debug("App is " + app.name + " removable? " + app.removable); + let p = new Promise((aResolveInstall, aRejectInstall) => { + let appData = this.buildAndroidAppData(app); + appsRegistry.confirmInstall(appData, null, aResolveInstall); + }); + promises.push(p); + }); + + // Wait for all apps to be installed. + return Promise.all(promises); + } + ).then(appsRegistry._saveApps.bind(appsRegistry)); + }, + + observe: function(aSubject, aTopic, aData) { + let data; + try { + data = JSON.parse(aData); + } catch(e) { + debug(e); + return; + } + + if (aTopic == "Android:Apps:Installed") { + let appData = this.buildAndroidAppData(data); + appsRegistry.confirmInstall(appData); + } else if (aTopic == "Android:Apps:Uninstalled") { + let [origin, manifestURL] = + this.getOriginAndManifestURL(data.packagename); + appsRegistry.uninstall(manifestURL); + } + }, +} diff --git a/dom/apps/AppsService.js b/dom/apps/AppsService.js index 63483a58be..1f15c78992 100644 --- a/dom/apps/AppsService.js +++ b/dom/apps/AppsService.js @@ -11,15 +11,14 @@ function debug(s) { const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; +const Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); -try { - if (Services.prefs.getBoolPref("dom.apps.customization.enabled")) { - Cu.import("resource://gre/modules/UserCustomizations.jsm"); - } -} catch(e) {} + +XPCOMUtils.defineLazyModuleGetter(this, "UserCustomizations", + "resource://gre/modules/UserCustomizations.jsm"); const APPS_SERVICE_CID = Components.ID("{05072afa-92fe-45bf-ae22-39b69c117058}"); @@ -131,6 +130,30 @@ AppsService.prototype = { return null; }, + getScopeByLocalId: function(aLocalId) { + debug("getScopeByLocalId( " + aLocalId + " )"); + if (this.isInvalidId(aLocalId)) { + return null; + } + // TODO : implement properly! + // We just return null for now to not break PushService.jsm + return null; + }, + + isExtensionResource: function(aURI) { + // This is only expected to be used by NeckoParent, and will not work + // properly in child processes. + if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) { + throw Cr.NS_ERROR_FAILURE; + } + + return UserCustomizations.isFromExtension(aURI); + }, + + updateDataStoreEntriesFromLocalId: function(aLocalId) { + return DOMApplicationRegistry.updateDataStoreEntriesFromLocalId(aLocalId); + }, + classID : APPS_SERVICE_CID, QueryInterface : XPCOMUtils.generateQI([Ci.nsIAppsService]) } diff --git a/dom/apps/AppsServiceChild.jsm b/dom/apps/AppsServiceChild.jsm index d72193dd8d..2618dc89f7 100644 --- a/dom/apps/AppsServiceChild.jsm +++ b/dom/apps/AppsServiceChild.jsm @@ -415,6 +415,10 @@ this.DOMApplicationRegistry = { getAppInfo: function getAppInfo(aAppId) { return AppsUtils.getAppInfo(this.webapps, aAppId); + }, + + updateDataStoreEntriesFromLocalId: function(aLocalId) { + debug("updateDataStoreEntriesFromLocalId() not yet supported on child!"); } } diff --git a/dom/apps/Langpacks.jsm b/dom/apps/Langpacks.jsm index b2e1769163..bf26a67e9a 100644 --- a/dom/apps/Langpacks.jsm +++ b/dom/apps/Langpacks.jsm @@ -18,11 +18,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm", this.EXPORTED_SYMBOLS = ["Langpacks"]; -let debug = Services.prefs.getBoolPref("dom.mozApps.debug") - ? (aMsg) => { - dump("-*-*- Langpacks: " + aMsg + "\n"); - } - : (aMsg) => {}; +let debug; +function debugPrefObserver() { + debug = Services.prefs.getBoolPref("dom.mozApps.debug") + ? (aMsg) => { + dump("-*-*- Langpacks: " + aMsg + "\n"); + } + : (aMsg) => {}; +} +debugPrefObserver(); +Services.prefs.addObserver("dom.mozApps.debug", debugPrefObserver, false); /** * Langpack support diff --git a/dom/apps/PermissionsTable.jsm b/dom/apps/PermissionsTable.jsm index d7c9047bef..835a7c382c 100644 --- a/dom/apps/PermissionsTable.jsm +++ b/dom/apps/PermissionsTable.jsm @@ -116,6 +116,11 @@ this.PermissionsTable = { geolocation: { privileged: DENY_ACTION, certified: ALLOW_ACTION }, + "speech-recognition": { + app: DENY_ACTION, + privileged: ALLOW_ACTION, + certified: ALLOW_ACTION + }, telephony: { app: DENY_ACTION, privileged: DENY_ACTION, @@ -365,6 +370,11 @@ this.PermissionsTable = { geolocation: { privileged: ALLOW_ACTION, certified: ALLOW_ACTION }, + "audio-capture:3gpp2": { + app: DENY_ACTION, + privileged: ALLOW_ACTION, + certified: ALLOW_ACTION + }, "nfc": { app: DENY_ACTION, privileged: ALLOW_ACTION, diff --git a/dom/apps/Webapps.jsm b/dom/apps/Webapps.jsm index 523768f89f..d89f687b73 100755 --- a/dom/apps/Webapps.jsm +++ b/dom/apps/Webapps.jsm @@ -88,6 +88,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ImportExport", XPCOMUtils.defineLazyModuleGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Messaging", + "resource://gre/modules/Messaging.jsm"); + #ifdef MOZ_WIDGET_GONK XPCOMUtils.defineLazyGetter(this, "libcutils", function() { Cu.import("resource://gre/modules/systemlibs.js"); @@ -100,14 +103,18 @@ XPCOMUtils.defineLazyGetter(this, "libcutils", function() { // from the AndroidLog module so it gets the "debug" priority and a log tag. // We always report debug messages on Android because it's unnecessary // to restrict reporting, per bug 1003469. -let debug = Cu.import("resource://gre/modules/AndroidLog.jsm", {}) +var debug = Cu.import("resource://gre/modules/AndroidLog.jsm", {}) .AndroidLog.d.bind(null, "Webapps"); #else // Elsewhere, report debug messages only if dom.mozApps.debug is set to true. -// The pref is only checked once, on startup, so restart after changing it. -let debug = Services.prefs.getBoolPref("dom.mozApps.debug") - ? (aMsg) => dump("-*- Webapps.jsm : " + aMsg + "\n") - : (aMsg) => {}; +var debug; +function debugPrefObserver() { + debug = Services.prefs.getBoolPref("dom.mozApps.debug") + ? (aMsg) => dump("-*- Webapps.jsm : " + aMsg + "\n") + : (aMsg) => {}; +} +debugPrefObserver(); +Services.prefs.addObserver("dom.mozApps.debug", debugPrefObserver, false); #endif function getNSPRErrorCode(err) { @@ -125,7 +132,9 @@ function supportSystemMessages() { // Minimum delay between two progress events while downloading, in ms. const MIN_PROGRESS_EVENT_DELAY = 1500; -const chromeWindowType = "navigator:browser"; +const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org"; + +const chromeWindowType = WEBAPP_RUNTIME ? "webapprt:webapp" : "navigator:browser"; XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", @@ -168,8 +177,10 @@ XPCOMUtils.defineLazyGetter(this, "permMgr", function() { #elifdef ANDROID const DIRECTORY_NAME = "webappsDir"; #else - // Mulet, B2G Desktop, etc. - const DIRECTORY_NAME = "ProfD"; + // If we're executing in the context of the webapp runtime, the data files + // are in a different directory (currently the Firefox profile that installed + // the webapp); otherwise, they're in the current profile. + const DIRECTORY_NAME = WEBAPP_RUNTIME ? "WebappRegD" : "ProfD"; #endif // We'll use this to identify privileged apps that have been preinstalled @@ -181,9 +192,10 @@ const STORE_ID_PENDING_PREFIX = "#unknownID#"; this.DOMApplicationRegistry = { // pseudo-constants for the different application kinds. - get kPackaged() "packaged", - get kHosted() "hosted", - get kHostedAppcache() "hosted-appcache", + get kPackaged() { return "packaged"; }, + get kHosted() { return "hosted"; }, + get kHostedAppcache() { return "hosted-appcache"; }, + get kAndroid() { return "android-native"; }, // Path to the webapps.json file where we store the registry data. appsFile: null, @@ -248,6 +260,11 @@ this.DOMApplicationRegistry = { this.getFullAppByManifestURL.bind(this)); MessageBroadcaster.init(this.getAppByManifestURL); + + if (AppConstants.MOZ_B2GDROID) { + Cu.import("resource://gre/modules/AndroidUtils.jsm"); + AndroidUtils.init(this); + } }, // loads the current registry, that could be empty on first run. @@ -453,7 +470,9 @@ this.DOMApplicationRegistry = { }), appKind: function(aApp, aManifest) { - if (aApp.origin.startsWith("app://")) { + if (aApp.origin.startsWith("android://")) { + return this.kAndroid; + } if (aApp.origin.startsWith("app://")) { return this.kPackaged; } else { // Hosted apps, can be appcached or not. @@ -511,7 +530,10 @@ this.DOMApplicationRegistry = { // Installs a 3rd party app. installPreinstalledApp: function installPreinstalledApp(aId) { -#ifdef MOZ_WIDGET_GONK + if (!AppConstants.MOZ_B2GDROID && AppConstants.platform !== "gonk") { + return false; + } + // In some cases, the app might be already installed under a different ID but // with the same manifestURL. In that case, the only content of the webapp will // be the id of the old version, which is the one we'll keep. @@ -614,7 +636,6 @@ this.DOMApplicationRegistry = { zipReader.close(); } return isPreinstalled; -#endif }, // For hosted apps, uninstall an app served from http:// if we have @@ -782,6 +803,10 @@ this.DOMApplicationRegistry = { yield this.installSystemApps(); } + if (AppConstants.MOZ_B2GDROID) { + yield AndroidUtils.installAndroidApps(); + } + // At first run, install preloaded apps and set up their permissions. for (let id in this.webapps) { let isPreinstalled = this.installPreinstalledApp(id); @@ -794,7 +819,7 @@ this.DOMApplicationRegistry = { } // Need to update the persisted list of apps since // installPreinstalledApp() removes the ones failing to install. - this._saveApps(); + yield this._saveApps(); Services.prefs.setBoolPref("dom.apps.reset-permissions", true); } @@ -1294,22 +1319,22 @@ this.DOMApplicationRegistry = { this.registryReady.then( () => { switch (aMessage.name) { case "Webapps:Install": { -#ifdef MOZ_WIDGET_ANDROID - Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg)); -#else - this.doInstall(msg, mm); -#endif + if (AppConstants.platform == "android" && !AppConstants.MOZ_B2GDROID) { + Services.obs.notifyObservers(mm, "webapps-runtime-install", JSON.stringify(msg)); + } else { + this.doInstall(msg, mm); + } break; } case "Webapps:GetSelf": this.getSelf(msg, mm); break; case "Webapps:Uninstall": -#ifdef MOZ_WIDGET_ANDROID - Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg)); -#else - this.doUninstall(msg, mm); -#endif + if (AppConstants.platform == "android" && !AppConstants.MOZ_B2GDROID) { + Services.obs.notifyObservers(mm, "webapps-runtime-uninstall", JSON.stringify(msg)); + } else { + this.doUninstall(msg, mm); + } break; case "Webapps:Launch": this.doLaunch(msg, mm); @@ -1327,11 +1352,11 @@ this.DOMApplicationRegistry = { this.getNotInstalled(msg, mm); break; case "Webapps:InstallPackage": { -#ifdef MOZ_WIDGET_ANDROID - Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg)); -#else - this.doInstallPackage(msg, mm); -#endif + if (AppConstants.platform == "android" && !AppConstants.MOZ_B2GDROID) { + Services.obs.notifyObservers(mm, "webapps-runtime-install-package", JSON.stringify(msg)); + } else { + this.doInstallPackage(msg, mm); + } break; } case "Webapps:Download": @@ -1592,6 +1617,19 @@ this.DOMApplicationRegistry = { return; } + // Delegate native android apps launch. + if (this.kAndroid == app.kind) { + debug("Launching android app " + app.origin); + let [packageName, className] = + AndroidUtils.getPackageAndClassFromManifestURL(aManifestURL); + debug(" " + packageName + " " + className); + Messaging.sendRequest({ type: "Apps:Launch", + packagename: packageName, + classname: className }); + aOnSuccess(); + return; + } + // We have to clone the app object as nsIDOMApplication objects are // stringified as an empty object. (see bug 830376) let appClone = AppsUtils.cloneAppObject(app); @@ -1946,8 +1984,12 @@ this.DOMApplicationRegistry = { }, id: aApp.id }); + let appURI = NetUtil.newURI(aApp.origin, null, null); + let principal = + Services.scriptSecurityManager.createCodebasePrincipal(appURI, + {appId: aApp.localId}); let cacheUpdate = updateSvc.scheduleAppUpdate( - appcacheURI, docURI, aApp.localId, false, aProfileDir); + appcacheURI, docURI, principal, aApp.localId, false, aProfileDir); // We save the download details for potential further usage like // cancelling it. @@ -2036,8 +2078,9 @@ this.DOMApplicationRegistry = { } // If the app is packaged and its manifestURL has an app:// scheme, - // then we can't have an update. - if (app.kind == this.kPackaged && app.manifestURL.startsWith("app://")) { + // or if it's a native Android app then we can't have an update. + if (app.kind == this.kAndroid || + (app.kind == this.kPackaged && app.manifestURL.startsWith("app://"))) { sendError("NOT_UPDATABLE"); return; } @@ -2095,8 +2138,12 @@ this.DOMApplicationRegistry = { new ManifestHelper(manifest, aData.origin, aData.manifestURL); debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " + helper.fullAppcachePath()); + let appURI = NetUtil.newURI(aApp.origin, null, null); + let principal = + Services.scriptSecurityManager.createCodebasePrincipal(appURI, + {appId: aApp.localId}); updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null), - app.localId, false, updateObserver); + principal, app.localId, false, updateObserver); }); return; } @@ -2365,9 +2412,13 @@ this.DOMApplicationRegistry = { manifest.fullAppcachePath()); let updateDeferred = Promise.defer(); + let appURI = NetUtil.newURI(aApp.origin, null, null); + let principal = + Services.scriptSecurityManager.createCodebasePrincipal(appURI, + {appId: aApp.localId}); updateSvc.checkForUpdate(Services.io.newURI(manifest.fullAppcachePath(), null, null), - aApp.localId, false, + principal, aApp.localId, false, (aSubject, aTopic, aData) => updateDeferred.resolve(aTopic)); let topic = yield updateDeferred.promise; @@ -2769,8 +2820,10 @@ this.DOMApplicationRegistry = { _setupApp: function(aData, aId) { let app = aData.app; - // app can be uninstalled - app.removable = true; + // app can be uninstalled by default. + if (app.removable === undefined) { + app.removable = true; + } if (aData.isPackage) { // Override the origin with the correct id. @@ -2803,7 +2856,8 @@ this.DOMApplicationRegistry = { appObject.downloading = true; appObject.downloadSize = aLocaleManifest.size; appObject.readyToApplyDownload = false; - } else if (appObject.kind == this.kHosted) { + } else if (appObject.kind == this.kHosted || + appObject.kind == this.kAndroid) { appObject.installState = "installed"; appObject.downloadAvailable = false; appObject.downloading = false; @@ -2859,8 +2913,6 @@ this.DOMApplicationRegistry = { app.appStatus = AppsUtils.getAppManifestStatus(aManifest); - app.removable = true; - // Reuse the app ID if the scheme is "app". let uri = Services.io.newURI(app.origin, null, null); if (uri.scheme == "app") { @@ -2950,6 +3002,7 @@ this.DOMApplicationRegistry = { let appObject = this._cloneApp(aData, app, manifest, jsonManifest, id, localId); this.webapps[id] = appObject; + this._manifestCache[id] = jsonManifest; // For package apps, the permissions are not in the mini-manifest, so // don't update the permissions yet. @@ -3676,16 +3729,24 @@ this.DOMApplicationRegistry = { // true. if (useReviewerCerts) { let manifestPath = Services.io.newURI(aManifestURL, null, null).path; + let isReviewer = false; + // There are different reviewer paths for apps & addons so we keep + // them in a comma separated preference. + try { + let reviewerPaths = + Services.prefs.getCharPref("dom.apps.reviewer_paths").split(","); + isReviewer = reviewerPaths.some(path => { return manifestPath.startsWith(path); }); + } catch(e) {} switch (aInstallOrigin) { case "https://marketplace.firefox.com": - root = manifestPath.startsWith("/reviewers/") + root = isReviewer ? Ci.nsIX509CertDB.AppMarketplaceProdReviewersRoot : Ci.nsIX509CertDB.AppMarketplaceProdPublicRoot; break; case "https://marketplace-dev.allizom.org": - root = manifestPath.startsWith("/reviewers/") + root = isReviewer ? Ci.nsIX509CertDB.AppMarketplaceDevReviewersRoot : Ci.nsIX509CertDB.AppMarketplaceDevPublicRoot; break; @@ -4023,31 +4084,36 @@ this.DOMApplicationRegistry = { }, doUninstall: Task.async(function*(aData, aMm) { - // The yields here could get stuck forever, so we only hold - // a weak reference to the message manager while yielding, to avoid - // leaking the whole page associationed with the message manager. - aMm = Cu.getWeakReference(aMm); - let response = "Webapps:Uninstall:Return:OK"; try { aData.app = yield this._getAppWithManifest(aData.manifestURL); - let prefName = "dom.mozApps.auto_confirm_uninstall"; - if (Services.prefs.prefHasUserValue(prefName) && - Services.prefs.getBoolPref(prefName)) { - yield this._uninstallApp(aData.app); + if (this.kAndroid == aData.app.kind) { + debug("Uninstalling android app " + aData.app.origin); + let [packageName, className] = + AndroidUtils.getPackageAndClassFromManifestURL(aData.manifestURL); + Messaging.sendRequest({ type: "Apps:Uninstall", + packagename: packageName, + classname: className }); + // We have to wait for Android's uninstall before sending the + // uninstall event, so fake an error here. + response = "Webapps:Uninstall:Return:KO"; } else { - yield this._promptForUninstall(aData); + let prefName = "dom.mozApps.auto_confirm_uninstall"; + if (Services.prefs.prefHasUserValue(prefName) && + Services.prefs.getBoolPref(prefName)) { + yield this._uninstallApp(aData.app); + } else { + yield this._promptForUninstall(aData); + } } } catch (error) { aData.error = error; response = "Webapps:Uninstall:Return:KO"; } - if ((aMm = aMm.get())) { - aMm.sendAsyncMessage(response, this.formatMessage(aData)); - } + aMm.sendAsyncMessage(response, this.formatMessage(aData)); }), uninstall: function(aManifestURL) { @@ -4616,6 +4682,13 @@ this.DOMApplicationRegistry = { return OS.Path.dirname(this.appsFile); }, + updateDataStoreEntriesFromLocalId: function(aLocalId) { + let app = appsService.getAppByLocalId(aLocalId); + if (app) { + this.updateDataStoreForApp(app.id); + } + }, + _isLaunchable: function(aApp) { return true; }, diff --git a/dom/apps/moz.build b/dom/apps/moz.build index 5b730f8d46..419ad9a45f 100644 --- a/dom/apps/moz.build +++ b/dom/apps/moz.build @@ -42,6 +42,11 @@ EXTRA_JS_MODULES += [ 'UserCustomizations.jsm', ] +if CONFIG['MOZ_B2GDROID']: + EXTRA_JS_MODULES += [ + 'AndroidUtils.jsm', + ] + EXTRA_PP_JS_MODULES += [ 'AppsUtils.jsm', 'ImportExport.jsm', diff --git a/dom/apps/tests/chrome.ini b/dom/apps/tests/chrome.ini index 8391284338..3348cb6db4 100644 --- a/dom/apps/tests/chrome.ini +++ b/dom/apps/tests/chrome.ini @@ -1,7 +1,8 @@ [DEFAULT] -skip-if = buildapp == 'b2g' +skip-if = buildapp == 'b2g' || os == 'android' support-files = asmjs/* + common.js file_bug_945152.html file_bug_945152.sjs @@ -12,5 +13,5 @@ skip-if = os != 'linux' [test_operator_app_install.js] [test_operator_app_install.xul] # bug 928262 - skip-if = os == "win" + skip-if = os == 'win' [test_packaged_app_asmjs.html] diff --git a/dom/apps/tests/common.js b/dom/apps/tests/common.js new file mode 100644 index 0000000000..371073ede6 --- /dev/null +++ b/dom/apps/tests/common.js @@ -0,0 +1,3 @@ +function prepareEnv(cb) { + SpecialPowers.pushPrefEnv({"set":[["dom.mozApps.debug", true]]}, cb); +} diff --git a/dom/apps/tests/mochitest.ini b/dom/apps/tests/mochitest.ini index a3303033ad..8828904211 100644 --- a/dom/apps/tests/mochitest.ini +++ b/dom/apps/tests/mochitest.ini @@ -6,6 +6,7 @@ support-files = addons/update.webapp^headers^ addons/index.html chromeAddCert.js + common.js file_app.sjs file_app.template.html file_script.template.js diff --git a/dom/apps/tests/test_app_addons.html b/dom/apps/tests/test_app_addons.html index 13a1311188..2d666bf519 100644 --- a/dom/apps/tests/test_app_addons.html +++ b/dom/apps/tests/test_app_addons.html @@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1042881 Test for Bug 923897 - Test apps as addons + - +

diff --git a/dom/apps/tests/test_app_enabled.html b/dom/apps/tests/test_app_enabled.html index 4ed48fc09c..3e82f2c6a9 100644 --- a/dom/apps/tests/test_app_enabled.html +++ b/dom/apps/tests/test_app_enabled.html @@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={1XXXXXX} Test for Bug {1072090} + @@ -121,7 +122,7 @@ function runTest() { is(request.result.length, initialAppsCount, "All apps are uninstalled."); } -addLoadEvent(go); +addLoadEvent(() => prepareEnv(go)); diff --git a/dom/apps/tests/test_app_update.html b/dom/apps/tests/test_app_update.html index cdff6856df..55421b2cd7 100644 --- a/dom/apps/tests/test_app_update.html +++ b/dom/apps/tests/test_app_update.html @@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=826058 Test for Bug 826058 + - + Mozilla Bug 826058 Mozilla Bug 863337

diff --git a/dom/apps/tests/test_bug_1168300.html b/dom/apps/tests/test_bug_1168300.html index ea7c2443fd..eb79b11215 100644 --- a/dom/apps/tests/test_bug_1168300.html +++ b/dom/apps/tests/test_bug_1168300.html @@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1168300 Test for Bug 1168300 + - + Mozilla Bug 1168300