mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:23:07 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1200445 - Expose android native apps trough the navigator.mozApps api r=snorp,ferjm (5f2e5df93a)
- Bug 1199844 - limit webapp debug logging to webapp tests; r=fabrice (59bf73deb2)
- Bug 1199295 - Pass right loadingPrincipal to uriloader/prefetch - dom/apps (r=fabrice) (8bdab29703)
- Bug 1213170 - followup r=bustage on a CLOSED TREE (9ac5f0a677)
- Bug 1194243 - Keep the message manager alive in doUninstall() to be able to signal the result back; r=myk (3093189372)
- Bug 1154864 - Fix getScopeByLocalId use in PushService.jsm r=nsm (0f67e9de60)
- Bug 1196963 - Make extensions work without being in developer mode r=jduell (e120108628)
- Bug 1200851 - DataStoreService should update its permission map when the homescreen changes, r=fabrice (1582ac0320)
- Bug 1200851 - DataStoreService should update its permission map when the homescreen changes, r=fabrice (7949555b43)
- Bug 1186805 - Replace nsBaseHashtable::EnumerateRead() calls in dom/datastore/ with iterators, r=njn (0c6f234576)
- Bug 1208355 - Fix -Wshadow warnings in dom/datastore. r=baku (8b617685d4)
- Bug 1214092 - Part 1 of 1: Exposed WebSpeech API SpeechRecognition Interface to privileged apps. r=smaug (e33c92d4e8)
- Bug 1218337 - Part 1 of 1: Introduced permission 'speech-recognition' and used it in place of the app-check. r=smaug (1cdf67a55f)
- Bug 1051604 - Adapt VAD strategy on SpeechRecognition to be less strict on some devices with poor mics. r=smaug (911258b56e)
- Bug 1111135 - Part 1: Add audio-capture:3gpp perimission. r=fabrice (c98bee92a6)
- bit of 1196988 (a334242521)
- Bug 1193183 - Correctly implement SpeechRecognitionAlternative::confidence using ps_get_prob(). r=anatal (14a881b44f)
- Bug 1197455 - Call ps_set_search() only after successful grammar compilation. r=anatal (55d37ea0fb)
- Bug 1156560 - Prefer old CDMs on update if they are in use - r=cpearce (b763f1044a)
- Bug 1228215 - Add helper to do dir enumeration in GMPServiceParent. r=jwwang (1d3bc1eef0)
- Bug 1228215 - Store each GMP's storage and nodeId salt in separate directories. r=jwwang (20fb2b7a18)
- Bug 1172396 - Update GMP trial creation pref from chrome process - r=cpearce (123d97d03a)
- Bug 1228215 - Add a 'gmpName' parameter to GMPService::GetNodeId(), so each GMP can see a different nodeId for the same origin. r=jwwang (fceaef0c11)
- fixes for no EME (b70879a799)
- Bug 1228215 - Migrate existing GMP storage from post-42 pre-45 location to 45 location. r=jwwang (9da581744d)
- missing crash stuff (b537d416b3)
- Bug 1187193 - Use UserData() instead of Data() in ConstIter loops that used to be EnumerateRead's. r=njn (2a4c297f36)
- Bug 1211337 - Added crash report annotations tracking sync shutdown (60b3004394)
- Bug 1173195 - Don't assert success until successful in GMPSharedMemManager. r=edwin (3844ba6e20)
- Bug 1208289 - Log outstanding frames in GMP DrainComplete() and detect dropped ResetComplete. r=jwwang (eccf4dbecc)
- Bug 1224442: null-check GMP Parent Shmem messages from the Child to handle messages after shutdown r=cpearce (d12b9c57c2)
- add some defines (3c4fc2d5b9)
- Bug 1220929 - RemotePageManager should let us get all ports for a browser. r=Mossop (402fc2a536)
- Bug 1220929 - RemotePageManager should use documentURI and allow special URLs with query params. r=Mossop (9fc73b228e)
- Bug 1144422 - fix lightweight theme code to deal with invalid CSS so we don't mistakenly keep text colors, r=jaws (8b47394d6c)
- Bug 1229519: Fix download managers to pass eslint checks. r=mak (e4a684db58)
- Bug 1180113 - Introducing g2p algorithm inside pocketsphinx to allow out of dictionary words to be added to grammars. r=smaug (b3a23daf56)
- Bug 1202989 - Added check for 0 length phones string in addition to NULL. r=andrenatal (387faeb88c)
- Bug 1171082 - Now _WIN32_WINNT is defined to 0x0400 only if it is not defined, 0x0400 is the minimal version. Also modified update.sh to do this. r=smaug (bb7dd37c00)
- bits of Bug 1165518 - Part 2: Replace prlog.h with Logging.h. (fc0ca3ca20)
- Bug 1188970: Fix usage of forward slash in constructing webrtc trace file path. r=rjesup (8518b84be1)
- Bug 1225682 - Don't use nsAuto{,C}String as class member variables in docshell/. r=bz (839a57580e)
- Bug 1220916 - Remove "WARNING: TimelineConsumers could not be initialized" when running gtests, r=fitzgen (c89330afcc)
- Bug 1217836 - Add a readme file to our timeline backend, r=jsantell (df0ea6b198)
- enable shadow (dcad5bdb7a)
This commit is contained in:
@@ -65,6 +65,6 @@ private:
|
||||
bool mFixupCreatedAlternateURI;
|
||||
nsString mKeywordProviderName;
|
||||
nsString mKeywordAsSent;
|
||||
nsAutoCString mOriginalInput;
|
||||
nsCString mOriginalInput;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoString mRestyleHint;
|
||||
nsString mRestyleHint;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -60,7 +60,6 @@ TimelineConsumers::Get()
|
||||
if (sInstance->Init()) {
|
||||
ClearOnShutdown(&sInstance);
|
||||
} else {
|
||||
NS_WARNING("TimelineConsumers could not be initialized.");
|
||||
sInstance->RemoveObservers();
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
@@ -38,3 +38,6 @@ FINAL_LIBRARY = 'xul'
|
||||
LOCAL_INCLUDES += [
|
||||
'/docshell/base'
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
|
||||
@@ -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<AbstractTimelineMarker>&&)`
|
||||
|
||||
* Any thread
|
||||
`AddMarkerForAllObservedDocShells(const char*, MarkerTracingType)`
|
||||
`AddMarkerForAllObservedDocShells(const char*, const TimeStamp&, MarkerTracingType)`
|
||||
`AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>&)`
|
||||
|
||||
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");`
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
}
|
||||
+28
-5
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-5
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+128
-55
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
function prepareEnv(cb) {
|
||||
SpecialPowers.pushPrefEnv({"set":[["dom.mozApps.debug", true]]}, cb);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1042881
|
||||
<title>Test for Bug 923897 - Test apps as addons</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="application/javascript;version=1.7">
|
||||
/**
|
||||
@@ -178,7 +179,7 @@ function runTest() {
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={1XXXXXX}
|
||||
<title>Test for Bug {1072090}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -121,7 +122,7 @@ function runTest() {
|
||||
is(request.result.length, initialAppsCount, "All apps are uninstalled.");
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=826058
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 826058</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
@@ -278,7 +279,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=826058
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=826058">Mozilla Bug 826058</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=863337">Mozilla Bug 863337</a>
|
||||
<p id="display"></p>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1168300
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1168300</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
@@ -111,7 +112,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1168300
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1168300">Mozilla Bug 1168300</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=795164
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 795164</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
@@ -96,7 +97,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=795164
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=795164">Mozilla Bug 795164</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=945152
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 945152</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
@@ -162,7 +163,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=945152
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=945152">Mozilla Bug 945152</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id={1191516}
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug {1191516}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={1191516}">Mozilla Bug {1191516}</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
|
||||
var gManifestURL = "http://mochi.test:8888/tests/dom/apps/tests/file_app.sjs?apptype=hosted&getmanifest=true";
|
||||
var gGenerator = runTest();
|
||||
|
||||
function debug(aMsg) {
|
||||
//dump("== Tests debug == " + aMsg + "\n");
|
||||
}
|
||||
|
||||
function go() {
|
||||
SpecialPowers.pushPermissions(
|
||||
[{ "type": "webapps-manage", "allow": 1, "context": document }],
|
||||
function() { gGenerator.next() });
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
try {
|
||||
gGenerator.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
function finish() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function cbError(aError) {
|
||||
ok(false, "Error callback invoked " + aError.target.error.name);
|
||||
finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
|
||||
SpecialPowers.autoConfirmAppInstall(continueTest);
|
||||
yield undefined;
|
||||
|
||||
SpecialPowers.autoConfirmAppUninstall(continueTest);
|
||||
yield undefined;
|
||||
|
||||
var request = navigator.mozApps.install(gManifestURL);
|
||||
request.onerror = cbError;
|
||||
request.onsuccess = continueTest;
|
||||
yield undefined;
|
||||
|
||||
var installedApp = request.result;
|
||||
ok(installedApp, "installedApp is non-null");
|
||||
ok(installedApp.manifestURL === gManifestURL, "installedApp's manifestURL is correct");
|
||||
|
||||
var app;
|
||||
request = navigator.mozApps.checkInstalled(gManifestURL);
|
||||
request.onsuccess = function() {
|
||||
app = request.result;
|
||||
continueTest();
|
||||
};
|
||||
request.onerror = cbError;
|
||||
yield undefined;
|
||||
|
||||
ok(app, "app is non-null");
|
||||
ok(app.manifestURL === gManifestURL, "app's manifestURL is correct");
|
||||
|
||||
request = app.launch();
|
||||
request.onsuccess = continueTest;
|
||||
request.onerror = cbError;
|
||||
yield undefined;
|
||||
|
||||
request = navigator.mozApps.mgmt.uninstall(app);
|
||||
request.onerror = cbError;
|
||||
request.onsuccess = continueTest;
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={982874}
|
||||
<title>Test for Bug {982874}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@@ -322,7 +323,7 @@ function runTest() {
|
||||
is(request.result.length, initialAppsCount, "All apps are uninstalled.");
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={1111961}
|
||||
<title>Test for Bug {1111961}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -114,7 +115,7 @@ function runTest() {
|
||||
is(request.result.length, initialAppsCount, "All apps are uninstalled.");
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={778277}
|
||||
<title>Test for Bug {778277}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -116,7 +117,7 @@ function runTest() {
|
||||
is(request.result.length, initialAppsCount, "All apps are uninstalled.");
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={960837}
|
||||
<title>Test for Bug {960837}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -206,7 +207,7 @@ function runTest() {
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1108096
|
||||
<title>Test for Bug 1108096 - Langpack support</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="application/javascript;version=1.7">
|
||||
/**
|
||||
@@ -305,7 +306,7 @@ function runTest() {
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=989806
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -341,7 +342,7 @@ PackagedTestHelper.setSteps([
|
||||
}
|
||||
]);
|
||||
|
||||
addLoadEvent(PackagedTestHelper.start);
|
||||
addLoadEvent(() => prepareEnv(() => PackagedTestHelper.start()));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -212,6 +212,9 @@ function checkAppState(aApp,
|
||||
}
|
||||
|
||||
var steps = [
|
||||
function() {
|
||||
prepareEnv(next);
|
||||
},
|
||||
function() {
|
||||
setupDataDirs(next);
|
||||
ok(true, "Data directory set up to " + singlevariantDir);
|
||||
|
||||
@@ -8,6 +8,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=893800
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
@@ -10,6 +10,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=997886
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/chrome-harness.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
@@ -229,7 +230,7 @@ function runTest() {
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={821589}
|
||||
<title>Test for Bug {821589} Packaged apps installation and update</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@@ -347,7 +348,7 @@ var steps = [
|
||||
|
||||
PackagedTestHelper.setSteps(steps);
|
||||
|
||||
addLoadEvent(PackagedTestHelper.start);
|
||||
addLoadEvent(() => prepareEnv(() => PackagedTestHelper.start()));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={900533}
|
||||
<title>Test for Bug {900533} Packaged app update tests</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@@ -335,7 +336,7 @@ PackagedTestHelper.setSteps(steps);
|
||||
// install tests
|
||||
miniManifestURL = PackagedTestHelper.gSJS + "?getManifest=true&appToUpdate";
|
||||
|
||||
addLoadEvent(PackagedTestHelper.start);
|
||||
addLoadEvent(() => prepareEnv(() => PackagedTestHelper.start()));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={757226}
|
||||
<title>Test for Bug {757226} Implement mozApps app.replaceReceipt</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -231,7 +232,7 @@ function runTest() {
|
||||
ok(true, "App uninstalled");
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -8,6 +8,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=880043
|
||||
<title>Test for Bug 880043 Packaged apps installation and update</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@@ -269,7 +270,7 @@ var steps = [
|
||||
PackagedTestHelper.setSteps(steps);
|
||||
PackagedTestHelper.gSJSPath = gSJSPath;
|
||||
|
||||
addLoadEvent(PackagedTestHelper.start);
|
||||
addLoadEvent(() => prepareEnv(() => PackagedTestHelper.start()));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={1011738}
|
||||
<title>Test for Bug {1011738}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<script type="text/javascript" src="test_packaged_app_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
@@ -102,7 +103,7 @@ function runTest() {
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={1097468}
|
||||
<title>Test for Bug {1097468}</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -195,7 +196,7 @@ function runTest() {
|
||||
navigator.mozApps.mgmt.onuninstall = null;
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -8,6 +8,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=830258
|
||||
<title>Test for Bug 830258</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<script type="application/javascript;version=1.7">
|
||||
|
||||
/** Test for Bug 830258 **/
|
||||
@@ -96,7 +97,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=830258
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<body onload="prepareEnv(go)">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=830258">Mozilla Bug 830258</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
@@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={1075716}
|
||||
<title>Install web app from manifest with application/manifest+json MIME type</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -165,7 +166,7 @@ function runTest() {
|
||||
is(request.result.length, initialAppsCount, "Correct number of apps.");
|
||||
}
|
||||
|
||||
addLoadEvent(go);
|
||||
addLoadEvent(() => prepareEnv(go));
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>Test for Widget</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="file_test_widget.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,7 +13,7 @@
|
||||
<script type="application/javascript;version=1.7">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
gHasBrowserPermission = false;
|
||||
runTest();
|
||||
prepareEnv(runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<title>Test for DataStore - basic operation on a readonly db</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="file_test_widget.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
@@ -12,7 +13,7 @@
|
||||
<script type="application/javascript;version=1.7">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
gHasBrowserPermission = true;
|
||||
runTest();
|
||||
prepareEnv(runTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
Cu.import("resource:///modules/Services.jsm");
|
||||
var dom_mozApps_debug = Services.prefs.getBoolPref("dom.mozApps.debug");
|
||||
Services.prefs.setBoolPref("dom.mozApps.debug", true);
|
||||
@@ -0,0 +1 @@
|
||||
Services.prefs.setBoolPref("dom.mozApps.debug", dom_mozApps_debug);
|
||||
@@ -1,10 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource:///modules/AppsUtils.jsm");
|
||||
Cu.import("resource:///modules/Services.jsm");
|
||||
|
||||
add_test(function test_has_widget_criterion() {
|
||||
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/InterAppCommService.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
let UUIDGenerator = Cc["@mozilla.org/uuid-generator;1"]
|
||||
var UUIDGenerator = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator);
|
||||
|
||||
const MESSAGE_PORT_ID = UUIDGenerator.generateUUID().toString();
|
||||
const FAKE_MESSAGE_PORT_ID = UUIDGenerator.generateUUID().toString();
|
||||
const OUTER_WINDOW_ID = UUIDGenerator.generateUUID().toString();
|
||||
const TOP_WINDOW_ID = UUIDGenerator.generateUUID().toString();
|
||||
const REQUEST_ID = UUIDGenerator.generateUUID().toString();
|
||||
|
||||
const PUB_APP_MANIFEST_URL = "app://pubApp.gaiamobile.org/manifest.webapp";
|
||||
const SUB_APP_MANIFEST_URL = "app://subApp.gaiamobile.org/manifest.webapp";
|
||||
const PUB_APP_MANIFEST_URL = "app://pubapp.gaiamobile.org/manifest.webapp";
|
||||
const PUB_APP_MANIFEST_URL_WRONG =
|
||||
"app://pubappnotaccepted.gaiamobile.org/manifest.webapp";
|
||||
const SUB_APP_MANIFEST_URL = "app://subapp.gaiamobile.org/manifest.webapp";
|
||||
const SUB_APP_MANIFEST_URL_WRONG =
|
||||
"app://subappnotaccepted.gaiamobile.org/manifest.webapp";
|
||||
|
||||
const PUB_APP_PAGE_URL = "app://pubApp.gaiamobile.org/handler.html";
|
||||
const SUB_APP_PAGE_URL = "app://subApp.gaiamobile.org/handler.html";
|
||||
const PUB_APP_PAGE_URL = "app://pubapp.gaiamobile.org/handler.html";
|
||||
const PUB_APP_PAGE_URL_WRONG = "app://pubapp.gaiamobile.org/notAccepted.html";
|
||||
const SUB_APP_PAGE_URL = "app://subapp.gaiamobile.org/handler.html";
|
||||
const SUB_APP_PAGE_URL_WORNG = "app://subapp.gaiamobile.org/notAccepted.html";
|
||||
|
||||
const PAGE_URL_REG_EXP = "^app://.*\\.gaiamobile\\.org/handler.html";
|
||||
|
||||
const KEYWORD = "test";
|
||||
const CONNECT_KEYWORD = "connect-test";
|
||||
|
||||
function create_message_port_pair(aMessagePortId,
|
||||
aKeyword,
|
||||
@@ -340,7 +349,7 @@ add_test(function test_postMessage() {
|
||||
SUB_APP_MANIFEST_URL,
|
||||
{ text: "sub app says world again" });
|
||||
} else {
|
||||
do_throw("sub app receives an unexpected message")
|
||||
do_throw("sub app receives an unexpected message");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -386,7 +395,7 @@ add_test(function test_registerMessagePort_with_queued_messages() {
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
} else {
|
||||
do_throw("sub app receives an unexpected message")
|
||||
do_throw("sub app receives an unexpected message");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -450,6 +459,629 @@ add_test(function test_cancelConnection() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
function registerConnection(aKeyword, aSubAppPageURL, aSubAppManifestURL,
|
||||
aDescription, aRules) {
|
||||
var subAppPageUrl = Services.io.newURI(aSubAppPageURL, null, null);
|
||||
var subAppManifestUrl = Services.io.newURI(aSubAppManifestURL, null, null);
|
||||
InterAppCommService.registerConnection(aKeyword, subAppPageUrl,
|
||||
subAppManifestUrl, aDescription,
|
||||
aRules);
|
||||
}
|
||||
|
||||
add_test(function test_registerConnection() {
|
||||
InterAppCommService._registeredConnections = {};
|
||||
var description = "A test connection";
|
||||
|
||||
// Rules can have (ATM):
|
||||
// * minimumAccessLevel
|
||||
// * manifestURLs
|
||||
// * pageURLs
|
||||
// * installOrigins
|
||||
var sampleRules = {
|
||||
minimumAccessLevel: "certified",
|
||||
pageURLs: ["http://a.server.com/a/page.html"]
|
||||
};
|
||||
|
||||
registerConnection(CONNECT_KEYWORD, SUB_APP_PAGE_URL, SUB_APP_MANIFEST_URL,
|
||||
description, sampleRules);
|
||||
|
||||
var regConn = InterAppCommService._registeredConnections[CONNECT_KEYWORD];
|
||||
do_check_true(regConn !== undefined);
|
||||
var regEntry = regConn[SUB_APP_MANIFEST_URL];
|
||||
do_check_true(regEntry !== undefined);
|
||||
do_check_eq(regEntry.pageURL, SUB_APP_PAGE_URL);
|
||||
do_check_eq(regEntry.description, description);
|
||||
do_check_eq(regEntry.rules, sampleRules);
|
||||
do_check_eq(regEntry.manifestURL, SUB_APP_MANIFEST_URL);
|
||||
|
||||
InterAppCommService._registeredConnections = {};
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
// Simulates mozApps connect
|
||||
function connect(publisher, aTargetSendAsyncMessage) {
|
||||
let message = {
|
||||
name: "Webapps:Connect",
|
||||
json: {
|
||||
keyword: publisher.connectKw,
|
||||
rules: publisher.rules,
|
||||
manifestURL: publisher.manifestURL,
|
||||
pubPageURL: publisher.pageURL,
|
||||
outerWindowID: OUTER_WINDOW_ID,
|
||||
topWindowID: TOP_WINDOW_ID,
|
||||
requestID: REQUEST_ID
|
||||
},
|
||||
target: {
|
||||
sendAsyncMessage: function(aName, aData) {
|
||||
if (aTargetSendAsyncMessage) {
|
||||
aTargetSendAsyncMessage(aName, aData);
|
||||
}
|
||||
},
|
||||
assertContainApp: function(_manifestURL) {
|
||||
return (publisher.manifestURL == _manifestURL);
|
||||
}
|
||||
}
|
||||
};
|
||||
InterAppCommService.receiveMessage(message);
|
||||
};
|
||||
|
||||
function registerComponent(aObject, aDescription, aContract) {
|
||||
var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator);
|
||||
var cid = uuidGenerator.generateUUID();
|
||||
|
||||
var componentManager =
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
componentManager.registerFactory(cid, aDescription, aContract, aObject);
|
||||
|
||||
// Keep the id on the object so we can unregister later.
|
||||
aObject.cid = cid;
|
||||
}
|
||||
|
||||
function unregisterComponent(aObject) {
|
||||
var componentManager =
|
||||
Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
componentManager.unregisterFactory(aObject.cid, aObject);
|
||||
}
|
||||
|
||||
// The fun thing about this mock is that it actually does the same than the
|
||||
// current actual implementation does.
|
||||
var mockUIGlue = {
|
||||
// nsISupports implementation.
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsIFactory) ||
|
||||
iid.equals(Ci.nsIInterAppCommUIGlue)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
// nsIFactory implementation.
|
||||
createInstance: function(outer, iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
// nsIActivityUIGlue implementation.
|
||||
selectApps: function(aCallerID, aPubAppManifestURL, aKeyword,
|
||||
aAppsToSelect) {
|
||||
return Promise.resolve({
|
||||
callerID: aCallerID,
|
||||
keyword: aKeyword,
|
||||
manifestURL: aPubAppManifestURL,
|
||||
selectedApps: aAppsToSelect
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Used to keep a fake table of installed apps (needed by mockAppsService)
|
||||
var webappsTable = {
|
||||
};
|
||||
|
||||
var mockAppsService = {
|
||||
// We use the status and the installOrigin here only.
|
||||
getAppByManifestURL: function (aPubAppManifestURL) {
|
||||
return webappsTable[aPubAppManifestURL];
|
||||
},
|
||||
|
||||
// And so far so well this should not be used by tests at all.
|
||||
getManifestURLByLocalId: function(appId) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Template test for connect
|
||||
function simpleConnectTestTemplate(aTestCase) {
|
||||
dump("TEST: " + aTestCase.subscriber.description + "\n");
|
||||
|
||||
// First, add some mocking....
|
||||
|
||||
// System Messenger
|
||||
var resolveMessenger, rejectMessenger;
|
||||
var messengerPromise = new Promise((resolve, reject) => {
|
||||
resolveMessenger = resolve;
|
||||
rejectMessenger = reject;
|
||||
});
|
||||
var mockMessenger = {
|
||||
sendMessage: function(aName, aData, aDestURL, aDestManURL) {
|
||||
do_check_eq(aName, "connection");
|
||||
resolveMessenger(aData);
|
||||
}
|
||||
};
|
||||
InterAppCommService.messenger = mockMessenger;
|
||||
|
||||
// AppsService
|
||||
InterAppCommService.appsService = mockAppsService;
|
||||
|
||||
// Set the initial state:
|
||||
// First, setup our fake webappsTable:
|
||||
var subs = aTestCase.subscriber;
|
||||
var pub = aTestCase.publisher;
|
||||
webappsTable[subs.manifestURL] = subs.webappEntry;
|
||||
webappsTable[pub.manifestURL] = pub.webappEntry;
|
||||
|
||||
InterAppCommService._registeredConnections = {};
|
||||
clear_allowed_connections();
|
||||
clear_message_port_pairs();
|
||||
|
||||
registerConnection(subs.connectKw, subs.pageURL, subs.manifestURL,
|
||||
subs.description, subs.rules);
|
||||
|
||||
// And now we can try connecting...
|
||||
connect(pub, function(aName, aData) {
|
||||
|
||||
var expectedName = "Webapps:Connect:Return:OK";
|
||||
var expectedLength = 1;
|
||||
if (!aTestCase.expectedSuccess) {
|
||||
expectedName = "Webapps:Connect:Return:KO";
|
||||
expectedLength = 0;
|
||||
}
|
||||
|
||||
do_check_eq(aName, expectedName);
|
||||
var numPortIDs =
|
||||
(aData.messagePortIDs && aData.messagePortIDs.length ) || 0;
|
||||
do_check_eq(numPortIDs, expectedLength);
|
||||
if (expectedLength) {
|
||||
var portPair =
|
||||
InterAppCommService._messagePortPairs[aData.messagePortIDs[0]];
|
||||
do_check_eq(portPair.publisher.manifestURL,pub.manifestURL);
|
||||
do_check_eq(portPair.subscriber.manifestURL, subs.manifestURL);
|
||||
} else {
|
||||
run_next_test();
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to wait for the message to be "received" on the publisher also
|
||||
messengerPromise.then(messageData => {
|
||||
do_check_eq(messageData.keyword, subs.connectKw);
|
||||
do_check_eq(messageData.pubPageURL, pub.pageURL);
|
||||
do_check_eq(messageData.messagePortID, aData.messagePortIDs[0]);
|
||||
// Cleanup
|
||||
InterAppCommService.registeredConnections = {};
|
||||
clear_allowed_connections();
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const CERTIFIED = Ci.nsIPrincipal.APP_STATUS_CERTIFIED;
|
||||
const PRIVILEGED = Ci.nsIPrincipal.APP_STATUS_PRIVILEGED;
|
||||
const INSTALLED = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
|
||||
const NOT_INSTALLED = Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED;
|
||||
|
||||
var connectTestCases = [
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "Trivial case [empty rules]. Successful test",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "not certified SUB status and not PUB rules",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: PRIVILEGED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "not certified PUB status and not SUB rules",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: INSTALLED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchMinimumAccessLvl --> Sub INSTALLED PubRul web",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: INSTALLED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {
|
||||
minimumAccessLevel:"web"
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchMinimumAccessLvl --> Sub NOT INSTALLED PubRul web",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: NOT_INSTALLED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {
|
||||
minimumAccessLevel:"web"
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchMinimumAccessLvl --> Pub CERTIFIED SubRul certified",
|
||||
rules: {
|
||||
minimumAccessLevel:"certified"
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchMinimumAccessLvl --> Pub PRIVILEGED SubRul certified",
|
||||
rules: {
|
||||
minimumAccessLevel:"certified"
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: PRIVILEGED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchManifest --> Pub manifest1 SubRules:{ manifest1 }",
|
||||
rules: {
|
||||
manifestURLs: [PUB_APP_MANIFEST_URL]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchManifest --> Pub manifest2 SubRules:{ manifest1 }",
|
||||
rules: {
|
||||
manifestURLs: [PUB_APP_MANIFEST_URL]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL_WRONG,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchManifest --> Sub manifest1 PubRules:{ manifest1 }",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {
|
||||
manifestURLs: [SUB_APP_MANIFEST_URL]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL_WRONG,
|
||||
description: "matchManifest --> Sub manifest2 PubRules:{ manifest1 }",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {
|
||||
manifestURLs: [SUB_APP_MANIFEST_URL]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL_WRONG,
|
||||
description: "matchPage --> Pub page1 SubRules:{ page1 }",
|
||||
rules: {
|
||||
pageURLs: [PAGE_URL_REG_EXP]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchPage --> Pub page2 SubRules:{ page1 }",
|
||||
rules: {
|
||||
pageURLs: [PAGE_URL_REG_EXP]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL_WRONG,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchPage --> Sub page1 PubRules:{ page1 }",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {
|
||||
pageURLs: [PAGE_URL_REG_EXP]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: true
|
||||
},
|
||||
{
|
||||
subscriber: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: SUB_APP_PAGE_URL_WORNG,
|
||||
manifestURL: SUB_APP_MANIFEST_URL,
|
||||
description: "matchPage --> Sub page2 PubRules:{ page1 }",
|
||||
rules: {},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
publisher: {
|
||||
connectKw: CONNECT_KEYWORD,
|
||||
pageURL: PUB_APP_PAGE_URL,
|
||||
manifestURL: PUB_APP_MANIFEST_URL,
|
||||
rules: {
|
||||
pageURLs: [PAGE_URL_REG_EXP]
|
||||
},
|
||||
webappEntry: {
|
||||
appStatus: CERTIFIED,
|
||||
installOrigin: "app://system.gaiamobile.org"
|
||||
}
|
||||
},
|
||||
expectedSuccess: false
|
||||
}
|
||||
];
|
||||
|
||||
// Only run these test cases if we're on a nightly build. Otherwise they
|
||||
// don't make much sense.
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
registerComponent(mockUIGlue,
|
||||
"Mock InterApp UI Glue",
|
||||
"@mozilla.org/dom/apps/inter-app-comm-ui-glue;1");
|
||||
|
||||
do_register_cleanup(function () {
|
||||
// Cleanup the mocks
|
||||
InterAppCommService.messenger = undefined;
|
||||
InterAppCommService.appsService = undefined;
|
||||
unregisterComponent(mockUIGlue);
|
||||
});
|
||||
connectTestCases.forEach(
|
||||
aTestCase =>
|
||||
add_test(simpleConnectTestTemplate.bind(undefined, aTestCase))
|
||||
);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource:///modules/AppsUtils.jsm");
|
||||
|
||||
add_test(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
head = head.js
|
||||
tail = tail.js
|
||||
|
||||
[test_has_widget_criterion.js]
|
||||
[test_inter_app_comm_service.js]
|
||||
|
||||
+230
-168
@@ -125,6 +125,7 @@ namespace {
|
||||
|
||||
// Singleton for DataStoreService.
|
||||
StaticRefPtr<DataStoreService> gDataStoreService;
|
||||
nsString gHomeScreenManifestURL;
|
||||
static uint64_t gCounterID = 0;
|
||||
|
||||
typedef nsClassHashtable<nsUint32HashKey, DataStoreInfo> HashApp;
|
||||
@@ -295,152 +296,16 @@ ResetPermission(uint32_t aAppId, const nsAString& aOriginURL,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class MOZ_STACK_CLASS GetDataStoreInfosData
|
||||
{
|
||||
public:
|
||||
GetDataStoreInfosData(nsClassHashtable<nsStringHashKey, HashApp>& aAccessStores,
|
||||
const nsAString& aName, const nsAString& aManifestURL,
|
||||
uint32_t aAppId, nsTArray<DataStoreInfo>& aStores)
|
||||
: mAccessStores(aAccessStores)
|
||||
, mName(aName)
|
||||
, mManifestURL(aManifestURL)
|
||||
, mAppId(aAppId)
|
||||
, mStores(aStores)
|
||||
{}
|
||||
|
||||
nsClassHashtable<nsStringHashKey, HashApp>& mAccessStores;
|
||||
nsString mName;
|
||||
nsString mManifestURL;
|
||||
uint32_t mAppId;
|
||||
nsTArray<DataStoreInfo>& mStores;
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
GetDataStoreInfosEnumerator(const uint32_t& aAppId,
|
||||
DataStoreInfo* aInfo,
|
||||
void* aUserData)
|
||||
void
|
||||
HomeScreenPrefCallback(const char* aPrefName, void* /* aClosure */)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
||||
|
||||
auto* data = static_cast<GetDataStoreInfosData*>(aUserData);
|
||||
if (aAppId == data->mAppId) {
|
||||
return PL_DHASH_NEXT;
|
||||
RefPtr<DataStoreService> service = DataStoreService::Get();
|
||||
if (!service) {
|
||||
return;
|
||||
}
|
||||
|
||||
HashApp* apps;
|
||||
if (!data->mAccessStores.Get(data->mName, &apps)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
if (!data->mManifestURL.IsEmpty() &&
|
||||
!data->mManifestURL.Equals(aInfo->mManifestURL)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
DataStoreInfo* accessInfo = nullptr;
|
||||
if (!apps->Get(data->mAppId, &accessInfo)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
bool readOnly = aInfo->mReadOnly || accessInfo->mReadOnly;
|
||||
DataStoreInfo* accessStore = data->mStores.AppendElement();
|
||||
accessStore->Init(aInfo->mName, aInfo->mOriginURL,
|
||||
aInfo->mManifestURL, readOnly,
|
||||
aInfo->mEnabled);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
GetAppManifestURLsEnumerator(const uint32_t& aAppId,
|
||||
DataStoreInfo* aInfo,
|
||||
void* aUserData)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
||||
|
||||
auto* manifestURLs = static_cast<nsIMutableArray*>(aUserData);
|
||||
nsCOMPtr<nsISupportsString> manifestURL(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
|
||||
if (manifestURL) {
|
||||
manifestURL->SetData(aInfo->mManifestURL);
|
||||
manifestURLs->AppendElement(manifestURL, false);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// This class is useful to enumerate the add permissions for each app.
|
||||
class MOZ_STACK_CLASS AddPermissionsData
|
||||
{
|
||||
public:
|
||||
AddPermissionsData(const nsAString& aPermission, bool aReadOnly)
|
||||
: mPermission(aPermission)
|
||||
, mReadOnly(aReadOnly)
|
||||
, mResult(NS_OK)
|
||||
{}
|
||||
|
||||
nsString mPermission;
|
||||
bool mReadOnly;
|
||||
nsresult mResult;
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
AddPermissionsEnumerator(const uint32_t& aAppId,
|
||||
DataStoreInfo* aInfo,
|
||||
void* userData)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
||||
|
||||
auto* data = static_cast<AddPermissionsData*>(userData);
|
||||
|
||||
// ReadOnly is decided by the owner first.
|
||||
bool readOnly = data->mReadOnly || aInfo->mReadOnly;
|
||||
|
||||
data->mResult = ResetPermission(aAppId, aInfo->mOriginURL,
|
||||
aInfo->mManifestURL,
|
||||
data->mPermission,
|
||||
readOnly);
|
||||
return NS_FAILED(data->mResult) ? PL_DHASH_STOP : PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// This class is useful to enumerate the add permissions for each app.
|
||||
class MOZ_STACK_CLASS AddAccessPermissionsData
|
||||
{
|
||||
public:
|
||||
AddAccessPermissionsData(uint32_t aAppId, const nsAString& aName,
|
||||
const nsAString& aOriginURL, bool aReadOnly)
|
||||
: mAppId(aAppId)
|
||||
, mName(aName)
|
||||
, mOriginURL(aOriginURL)
|
||||
, mReadOnly(aReadOnly)
|
||||
, mResult(NS_OK)
|
||||
{}
|
||||
|
||||
uint32_t mAppId;
|
||||
nsString mName;
|
||||
nsString mOriginURL;
|
||||
bool mReadOnly;
|
||||
nsresult mResult;
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
AddAccessPermissionsEnumerator(const uint32_t& aAppId,
|
||||
DataStoreInfo* aInfo,
|
||||
void* userData)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
||||
|
||||
auto* data = static_cast<AddAccessPermissionsData*>(userData);
|
||||
|
||||
nsString permission;
|
||||
GeneratePermissionName(permission, data->mName, aInfo->mManifestURL);
|
||||
|
||||
// ReadOnly is decided by the owner first.
|
||||
bool readOnly = aInfo->mReadOnly || data->mReadOnly;
|
||||
|
||||
data->mResult = ResetPermission(data->mAppId, data->mOriginURL,
|
||||
aInfo->mManifestURL,
|
||||
permission, readOnly);
|
||||
return NS_FAILED(data->mResult) ? PL_DHASH_STOP : PL_DHASH_NEXT;
|
||||
service->HomeScreenPrefChanged();
|
||||
}
|
||||
|
||||
} /* anonymous namespace */
|
||||
@@ -799,6 +664,12 @@ DataStoreService::Shutdown()
|
||||
if (obs) {
|
||||
obs->RemoveObserver(gDataStoreService, "webapps-clear-data");
|
||||
}
|
||||
|
||||
nsresult rv =
|
||||
Preferences::UnregisterCallback(HomeScreenPrefCallback,
|
||||
"dom.mozApps.homescreenURL",
|
||||
nullptr);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
gDataStoreService = nullptr;
|
||||
@@ -841,6 +712,13 @@ DataStoreService::Init()
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Preferences::RegisterCallback(HomeScreenPrefCallback,
|
||||
"dom.mozApps.homescreenURL",
|
||||
nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -939,22 +817,20 @@ DataStoreService::GetDataStores(nsIDOMWindow* aWindow,
|
||||
// window, so we can skip the ipc communication.
|
||||
if (XRE_IsParentProcess()) {
|
||||
uint32_t appId;
|
||||
nsresult rv = principal->GetAppId(&appId);
|
||||
if (NS_FAILED(rv)) {
|
||||
RejectPromise(window, promise, rv);
|
||||
rv = principal->GetAppId(&appId);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
RejectPromise(window, promise, rv.StealNSResult());
|
||||
promise.forget(aDataStores);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = GetDataStoreInfos(aName, aOwner, appId, principal, stores);
|
||||
if (NS_FAILED(rv)) {
|
||||
RejectPromise(window, promise, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
RejectPromise(window, promise, rv.StealNSResult());
|
||||
promise.forget(aDataStores);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
// This method can be called in the child so we need to send a request
|
||||
// to the parent and create DataStore object here.
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
@@ -1087,7 +963,7 @@ DataStoreService::GetDataStoresResolve(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
}
|
||||
|
||||
// Thie method populates 'aStores' with the list of DataStores with 'aName' as
|
||||
// This method populates 'aStores' with the list of DataStores with 'aName' as
|
||||
// name and available for this 'aAppId'.
|
||||
nsresult
|
||||
DataStoreService::GetDataStoreInfos(const nsAString& aName,
|
||||
@@ -1125,16 +1001,44 @@ DataStoreService::GetDataStoreInfos(const nsAString& aName,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DataStoreInfo* info = nullptr;
|
||||
if (apps->Get(aAppId, &info) &&
|
||||
(aOwner.IsEmpty() || aOwner.Equals(info->mManifestURL))) {
|
||||
DataStoreInfo* appsInfo = nullptr;
|
||||
if (apps->Get(aAppId, &appsInfo) &&
|
||||
(aOwner.IsEmpty() || aOwner.Equals(appsInfo->mManifestURL))) {
|
||||
DataStoreInfo* owned = aStores.AppendElement();
|
||||
owned->Init(info->mName, info->mOriginURL, info->mManifestURL, false,
|
||||
info->mEnabled);
|
||||
owned->Init(appsInfo->mName, appsInfo->mOriginURL, appsInfo->mManifestURL,
|
||||
false, appsInfo->mEnabled);
|
||||
}
|
||||
|
||||
for (auto iter = apps->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.Key() == aAppId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DataStoreInfo* appInfo = iter.UserData();
|
||||
MOZ_ASSERT(appInfo);
|
||||
|
||||
HashApp* accessApp;
|
||||
if (!mAccessStores.Get(aName, &accessApp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!aOwner.IsEmpty() &&
|
||||
!aOwner.Equals(appInfo->mManifestURL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DataStoreInfo* accessInfo = nullptr;
|
||||
if (!accessApp->Get(aAppId, &accessInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool readOnly = appInfo->mReadOnly || accessInfo->mReadOnly;
|
||||
DataStoreInfo* accessStore = aStores.AppendElement();
|
||||
accessStore->Init(aName, appInfo->mOriginURL,
|
||||
appInfo->mManifestURL, readOnly,
|
||||
appInfo->mEnabled);
|
||||
}
|
||||
|
||||
GetDataStoreInfosData data(mAccessStores, aName, aOwner, aAppId, aStores);
|
||||
apps->EnumerateRead(GetDataStoreInfosEnumerator, &data);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -1152,10 +1056,22 @@ DataStoreService::GetAppManifestURLsForDataStore(const nsAString& aName,
|
||||
|
||||
HashApp* apps = nullptr;
|
||||
if (mStores.Get(aName, &apps)) {
|
||||
apps->EnumerateRead(GetAppManifestURLsEnumerator, manifestURLs.get());
|
||||
for (auto iter = apps->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
nsCOMPtr<nsISupportsString> manifestURL(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
|
||||
if (manifestURL) {
|
||||
manifestURL->SetData(iter.UserData()->mManifestURL);
|
||||
manifestURLs->AppendElement(manifestURL, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mAccessStores.Get(aName, &apps)) {
|
||||
apps->EnumerateRead(GetAppManifestURLsEnumerator, manifestURLs.get());
|
||||
for (auto iter = apps->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
nsCOMPtr<nsISupportsString> manifestURL(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
|
||||
if (manifestURL) {
|
||||
manifestURL->SetData(iter.UserData()->mManifestURL);
|
||||
manifestURLs->AppendElement(manifestURL, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
manifestURLs.forget(aManifestURLs);
|
||||
@@ -1186,8 +1102,41 @@ DataStoreService::CheckPermission(nsIPrincipal* aPrincipal)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only support DataStore API for certified apps for now.
|
||||
return status == nsIPrincipal::APP_STATUS_CERTIFIED;
|
||||
// Certified apps are always allowed.
|
||||
if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (status != nsIPrincipal::APP_STATUS_PRIVILEGED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Privileged apps are allowed if they are the homescreen.
|
||||
nsAdoptingString homescreen =
|
||||
Preferences::GetString("dom.mozApps.homescreenURL");
|
||||
if (!homescreen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t appId;
|
||||
nsresult rv = aPrincipal->GetAppId(&appId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService =
|
||||
do_GetService("@mozilla.org/AppsService;1");
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString manifestURL;
|
||||
rv = appsService->GetManifestURLByLocalId(appId, manifestURL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return manifestURL.Equals(homescreen);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -1276,9 +1225,21 @@ DataStoreService::AddPermissions(uint32_t aAppId,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AddPermissionsData data(permission, aReadOnly);
|
||||
apps->EnumerateRead(AddPermissionsEnumerator, &data);
|
||||
return data.mResult;
|
||||
for (auto iter = apps->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
DataStoreInfo* info = iter.UserData();
|
||||
MOZ_ASSERT(info);
|
||||
|
||||
bool readOnly = aReadOnly || info->mReadOnly;
|
||||
|
||||
rv = ResetPermission(iter.Key(), info->mOriginURL,
|
||||
info->mManifestURL,
|
||||
permission, readOnly);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -1296,9 +1257,25 @@ DataStoreService::AddAccessPermissions(uint32_t aAppId, const nsAString& aName,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AddAccessPermissionsData data(aAppId, aName, aOriginURL, aReadOnly);
|
||||
apps->EnumerateRead(AddAccessPermissionsEnumerator, &data);
|
||||
return data.mResult;
|
||||
for (auto iter = apps->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
DataStoreInfo* info = iter.UserData();
|
||||
MOZ_ASSERT(info);
|
||||
|
||||
nsAutoString permission;
|
||||
GeneratePermissionName(permission, aName, info->mManifestURL);
|
||||
|
||||
// ReadOnly is decided by the owner first.
|
||||
bool readOnly = info->mReadOnly || aReadOnly;
|
||||
|
||||
nsresult rv = ResetPermission(aAppId, aOriginURL,
|
||||
info->mManifestURL,
|
||||
permission, readOnly);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This method starts the operation to create the first revision for a DataStore
|
||||
@@ -1456,5 +1433,90 @@ DataStoreService::GenerateUUID(nsAString& aID)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DataStoreService::HomeScreenPrefChanged()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
|
||||
|
||||
nsAdoptingString homescreen =
|
||||
Preferences::GetString("dom.mozApps.homescreenURL");
|
||||
if (homescreen == gHomeScreenManifestURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove datastores of the old homescreen.
|
||||
if (!gHomeScreenManifestURL.IsEmpty()) {
|
||||
DeleteDataStoresIfNotAllowed(gHomeScreenManifestURL);
|
||||
}
|
||||
|
||||
gHomeScreenManifestURL = homescreen;
|
||||
if (gHomeScreenManifestURL.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add datastores for the new homescreen.
|
||||
AddDataStoresIfAllowed(gHomeScreenManifestURL);
|
||||
}
|
||||
|
||||
void
|
||||
DataStoreService::DeleteDataStoresIfNotAllowed(const nsAString& aManifestURL)
|
||||
{
|
||||
nsCOMPtr<nsIAppsService> appsService =
|
||||
do_GetService("@mozilla.org/AppsService;1");
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
nsresult rv = appsService->GetAppByManifestURL(aManifestURL,
|
||||
getter_AddRefs(app));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !app) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t localId;
|
||||
rv = app->GetLocalId(&localId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = app->GetPrincipal(getter_AddRefs(principal));
|
||||
|
||||
// We delete all the dataStores for this app here.
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !principal ||
|
||||
!CheckPermission(principal)) {
|
||||
DeleteDataStores(localId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DataStoreService::AddDataStoresIfAllowed(const nsAString& aManifestURL)
|
||||
{
|
||||
nsCOMPtr<nsIAppsService> appsService =
|
||||
do_GetService("@mozilla.org/AppsService;1");
|
||||
if (NS_WARN_IF(!appsService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
nsresult rv = appsService->GetAppByManifestURL(aManifestURL,
|
||||
getter_AddRefs(app));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !app) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t localId;
|
||||
rv = app->GetLocalId(&localId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = appsService->UpdateDataStoreEntriesFromLocalId(localId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
nsIPrincipal* aPrincipal,
|
||||
nsTArray<DataStoreSetting>* aValue);
|
||||
|
||||
void HomeScreenPrefChanged();
|
||||
|
||||
private:
|
||||
DataStoreService();
|
||||
~DataStoreService();
|
||||
@@ -97,6 +99,9 @@ private:
|
||||
|
||||
void RemoveCounter(uint32_t aId);
|
||||
|
||||
void DeleteDataStoresIfNotAllowed(const nsAString& aManifestURL);
|
||||
void AddDataStoresIfAllowed(const nsAString& aManifestURL);
|
||||
|
||||
nsClassHashtable<nsStringHashKey, HashApp> mStores;
|
||||
nsClassHashtable<nsStringHashKey, HashApp> mAccessStores;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ interface nsIURI;
|
||||
* This service allows accessing some DOMApplicationRegistry methods from
|
||||
* non-javascript code.
|
||||
*/
|
||||
[scriptable, uuid(76ced447-6f92-48fb-b4e6-690e4859bc7f)]
|
||||
[scriptable, uuid(711cfab6-7b72-4aa2-a60c-17952ea05661)]
|
||||
interface nsIAppsService : nsISupports
|
||||
{
|
||||
mozIApplication getAppByManifestURL(in DOMString manifestURL);
|
||||
@@ -78,4 +78,21 @@ interface nsIAppsService : nsISupports
|
||||
* Returns the localId if the app was installed from a store
|
||||
*/
|
||||
DOMString getAppLocalIdByStoreId(in DOMString storeID);
|
||||
|
||||
/**
|
||||
* Returns the scope for app to use with service workers.
|
||||
*/
|
||||
DOMString getScopeByLocalId(in unsigned long localId);
|
||||
|
||||
/**
|
||||
* Returns true if this uri is a script or css resource loaded
|
||||
* from an extension.
|
||||
* Available only in the parent process.
|
||||
*/
|
||||
bool isExtensionResource(in nsIURI uri);
|
||||
|
||||
/**
|
||||
* Reads the manifest file for this app and update the DataStore map
|
||||
*/
|
||||
void updateDataStoreEntriesFromLocalId(in unsigned long localId);
|
||||
};
|
||||
|
||||
@@ -135,7 +135,7 @@ private:
|
||||
uint32_t mLength;
|
||||
uint32_t mOffset;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsAutoCString mContentType;
|
||||
const nsCString mContentType;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -308,7 +308,7 @@ ParseXing(const char *aBuffer)
|
||||
}
|
||||
|
||||
static int64_t
|
||||
FindNumVBRFrames(const nsAutoCString& aFrame)
|
||||
FindNumVBRFrames(const nsCString& aFrame)
|
||||
{
|
||||
const char *buffer = aFrame.get();
|
||||
const char *bufferEnd = aFrame.get() + aFrame.Length();
|
||||
|
||||
@@ -198,7 +198,7 @@ private:
|
||||
|
||||
// If the MP3 has a variable bitrate, then there *should* be metadata about
|
||||
// the encoding in the first frame. We buffer the first frame here.
|
||||
nsAutoCString mFirstFrame;
|
||||
nsCString mFirstFrame;
|
||||
|
||||
// While we are reading the first frame, this is the stream offset of the
|
||||
// last byte of that frame. -1 at all other times.
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
TrackID mTrackId;
|
||||
|
||||
nsAutoCString mMimeType;
|
||||
nsCString mMimeType;
|
||||
int64_t mDuration;
|
||||
int64_t mMediaTime;
|
||||
CryptoTrack mCrypto;
|
||||
@@ -481,7 +481,7 @@ private:
|
||||
uint32_t mStreamSourceID;
|
||||
|
||||
public:
|
||||
const nsAutoCString& mMimeType;
|
||||
const nsCString& mMimeType;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -451,10 +451,10 @@ protected:
|
||||
// Content-Type of the channel. This is copied from the nsIChannel when the
|
||||
// MediaResource is created. This is constant, so accessing from any thread
|
||||
// is safe.
|
||||
const nsAutoCString mContentType;
|
||||
const nsCString mContentType;
|
||||
|
||||
// Copy of the url of the channel resource.
|
||||
nsAutoCString mContentURL;
|
||||
nsCString mContentURL;
|
||||
|
||||
// True if SetLoadInBackground() has been called with
|
||||
// aLoadInBackground = true, i.e. when the document load event is not
|
||||
|
||||
@@ -435,7 +435,7 @@ void
|
||||
LogToBrowserConsole(const nsAString& aMsg)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
nsAutoString msg(aMsg);
|
||||
nsString msg(aMsg);
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NS_NewRunnableFunction([msg]() { LogToBrowserConsole(msg); });
|
||||
NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL);
|
||||
|
||||
@@ -340,6 +340,9 @@ IsAACContentType(const nsAString& aContentType);
|
||||
bool
|
||||
IsAACCodecString(const nsAString& aCodec);
|
||||
|
||||
void
|
||||
LogToBrowserConsole(const nsAString& aMsg);
|
||||
|
||||
} // end namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -79,7 +79,7 @@ GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendSetSessionId,
|
||||
aCreateSessionToken, nsAutoCString(aSessionId, aSessionIdLength));
|
||||
aCreateSessionToken, nsCString(aSessionId, aSessionIdLength));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -102,7 +102,7 @@ GMPDecryptorChild::RejectPromise(uint32_t aPromiseId,
|
||||
uint32_t aMessageLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendRejectPromise,
|
||||
aPromiseId, aException, nsAutoCString(aMessage, aMessageLength));
|
||||
aPromiseId, aException, nsCString(aMessage, aMessageLength));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -115,7 +115,7 @@ GMPDecryptorChild::SessionMessage(const char* aSessionId,
|
||||
nsTArray<uint8_t> msg;
|
||||
msg.AppendElements(aMessage, aMessageLength);
|
||||
CALL_ON_GMP_THREAD(SendSessionMessage,
|
||||
nsAutoCString(aSessionId, aSessionIdLength),
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
aMessageType, Move(msg));
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ GMPDecryptorChild::ExpirationChange(const char* aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendExpirationChange,
|
||||
nsAutoCString(aSessionId, aSessionIdLength), aExpiryTime);
|
||||
nsCString(aSessionId, aSessionIdLength), aExpiryTime);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -133,7 +133,7 @@ GMPDecryptorChild::SessionClosed(const char* aSessionId,
|
||||
uint32_t aSessionIdLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendSessionClosed,
|
||||
nsAutoCString(aSessionId, aSessionIdLength));
|
||||
nsCString(aSessionId, aSessionIdLength));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -145,9 +145,9 @@ GMPDecryptorChild::SessionError(const char* aSessionId,
|
||||
uint32_t aMessageLength)
|
||||
{
|
||||
CALL_ON_GMP_THREAD(SendSessionError,
|
||||
nsAutoCString(aSessionId, aSessionIdLength),
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
aException, aSystemCode,
|
||||
nsAutoCString(aMessage, aMessageLength));
|
||||
nsCString(aMessage, aMessageLength));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -160,7 +160,7 @@ GMPDecryptorChild::KeyStatusChanged(const char* aSessionId,
|
||||
nsAutoTArray<uint8_t, 16> kid;
|
||||
kid.AppendElements(aKeyId, aKeyIdLength);
|
||||
CALL_ON_GMP_THREAD(SendKeyStatusChanged,
|
||||
nsAutoCString(aSessionId, aSessionIdLength), kid,
|
||||
nsCString(aSessionId, aSessionIdLength), kid,
|
||||
aStatus);
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ private:
|
||||
nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
|
||||
// NodeId the plugin is assigned to, or empty if the the plugin is not
|
||||
// assigned to a NodeId.
|
||||
nsAutoCString mNodeId;
|
||||
nsCString mNodeId;
|
||||
// This is used for GMP content in the parent, there may be more of these in
|
||||
// the content processes.
|
||||
RefPtr<GMPContentParent> mGMPContentParent;
|
||||
|
||||
@@ -137,12 +137,12 @@ class GetNodeIdDone : public GetServiceChildCallback
|
||||
{
|
||||
public:
|
||||
GetNodeIdDone(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
|
||||
bool aInPrivateBrowsing, const nsACString& aVersion,
|
||||
UniquePtr<GetNodeIdCallback>&& aCallback)
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsing, UniquePtr<GetNodeIdCallback>&& aCallback)
|
||||
: mOrigin(aOrigin),
|
||||
mTopLevelOrigin(aTopLevelOrigin),
|
||||
mGMPName(aGMPName),
|
||||
mInPrivateBrowsing(aInPrivateBrowsing),
|
||||
mVersion(aVersion),
|
||||
mCallback(Move(aCallback))
|
||||
{
|
||||
}
|
||||
@@ -156,8 +156,8 @@ public:
|
||||
|
||||
nsCString outId;
|
||||
if (!aGMPServiceChild->SendGetGMPNodeId(mOrigin, mTopLevelOrigin,
|
||||
mInPrivateBrowsing, mVersion,
|
||||
&outId)) {
|
||||
mGMPName,
|
||||
mInPrivateBrowsing, &outId)) {
|
||||
mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
|
||||
return;
|
||||
}
|
||||
@@ -168,21 +168,54 @@ public:
|
||||
private:
|
||||
nsString mOrigin;
|
||||
nsString mTopLevelOrigin;
|
||||
nsString mGMPName;
|
||||
bool mInPrivateBrowsing;
|
||||
nsCString mVersion;
|
||||
UniquePtr<GetNodeIdCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsing,
|
||||
const nsACString& aVersion,
|
||||
UniquePtr<GetNodeIdCallback>&& aCallback)
|
||||
{
|
||||
UniquePtr<GetServiceChildCallback> callback(
|
||||
new GetNodeIdDone(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, aVersion,
|
||||
Move(aCallback)));
|
||||
new GetNodeIdDone(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, Move(aCallback)));
|
||||
GetServiceChild(Move(callback));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginServiceChild::UpdateTrialCreateState(const nsAString& aKeySystem,
|
||||
uint32_t aState)
|
||||
{
|
||||
if (NS_GetCurrentThread() != mGMPThread) {
|
||||
mGMPThread->Dispatch(NS_NewRunnableMethodWithArgs<nsString, uint32_t>(
|
||||
this, &GeckoMediaPluginServiceChild::UpdateTrialCreateState,
|
||||
aKeySystem, aState), NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class Callback : public GetServiceChildCallback
|
||||
{
|
||||
public:
|
||||
Callback(const nsAString& aKeySystem, uint32_t aState)
|
||||
: mKeySystem(aKeySystem)
|
||||
, mState(aState)
|
||||
{ }
|
||||
|
||||
virtual void Done(GMPServiceChild* aService) override
|
||||
{
|
||||
aService->SendUpdateGMPTrialCreateState(mKeySystem, mState);
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mKeySystem;
|
||||
uint32_t mState;
|
||||
};
|
||||
|
||||
UniquePtr<GetServiceChildCallback> callback(new Callback(aKeySystem, aState));
|
||||
GetServiceChild(Move(callback));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -47,9 +47,11 @@ public:
|
||||
nsACString& aOutVersion) override;
|
||||
NS_IMETHOD GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsingMode,
|
||||
const nsACString& aVersion,
|
||||
UniquePtr<GetNodeIdCallback>&& aCallback) override;
|
||||
NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
|
||||
uint32_t aState) override;
|
||||
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
|
||||
+200
-125
@@ -9,6 +9,9 @@
|
||||
#include "mozilla/Logging.h"
|
||||
#include "GMPParent.h"
|
||||
#include "GMPVideoDecoderParent.h"
|
||||
#ifdef MOZ_EME
|
||||
#include "mozilla/dom/GMPVideoDecoderTrialCreator.h"
|
||||
#endif
|
||||
#include "nsIObserverService.h"
|
||||
#include "GeckoChildProcessHost.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
@@ -86,6 +89,9 @@ static bool sHaveSetGMPServiceParentPrefCaches = false;
|
||||
|
||||
GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
|
||||
: mShuttingDown(false)
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
, mAsyncShutdownPluginStatesMutex("GeckoMediaPluginService::mAsyncShutdownPluginStatesMutex")
|
||||
#endif
|
||||
, mScannedPluginOnDisk(false)
|
||||
, mWaitingForPluginsSyncShutdown(false)
|
||||
{
|
||||
@@ -160,13 +166,13 @@ CloneAndAppend(nsIFile* aFile, const nsAString& aDir)
|
||||
}
|
||||
|
||||
static void
|
||||
MoveAndOverwrite(nsIFile* aOldStorageDir,
|
||||
nsIFile* aNewStorageDir,
|
||||
MoveAndOverwrite(nsIFile* aOldParentDir,
|
||||
nsIFile* aNewParentDir,
|
||||
const nsAString& aSubDir)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldStorageDir, aSubDir));
|
||||
nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldParentDir, aSubDir));
|
||||
if (NS_WARN_IF(!srcDir)) {
|
||||
return;
|
||||
}
|
||||
@@ -176,7 +182,13 @@ MoveAndOverwrite(nsIFile* aOldStorageDir,
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewStorageDir, aSubDir));
|
||||
// Ensure destination parent directory exists.
|
||||
rv = aNewParentDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
||||
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewParentDir, aSubDir));
|
||||
if (FileExists(dstDir)) {
|
||||
// We must have migrated before already, and then ran an old version
|
||||
// of Gecko again which created storage at the old location. Overwrite
|
||||
@@ -188,7 +200,7 @@ MoveAndOverwrite(nsIFile* aOldStorageDir,
|
||||
}
|
||||
}
|
||||
|
||||
rv = srcDir->MoveTo(aNewStorageDir, EmptyString());
|
||||
rv = srcDir->MoveTo(aNewParentDir, EmptyString());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
@@ -202,6 +214,21 @@ MigratePreGecko42StorageDir(nsIFile* aOldStorageDir,
|
||||
MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("storage"));
|
||||
}
|
||||
|
||||
static void
|
||||
MigratePreGecko45StorageDir(nsIFile* aStorageDirBase)
|
||||
{
|
||||
nsCOMPtr<nsIFile> adobeStorageDir(CloneAndAppend(aStorageDirBase, NS_LITERAL_STRING("gmp-eme-adobe")));
|
||||
if (NS_WARN_IF(!adobeStorageDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The base storage dir in pre-45 contained "id" and "storage" subdirs.
|
||||
// We assume all storage in the base storage dir that aren't known to GMP
|
||||
// storage are records for the Adobe GMP.
|
||||
MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("id"));
|
||||
MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("storage"));
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GMPPlatformString(nsAString& aOutPlatform)
|
||||
{
|
||||
@@ -288,11 +315,19 @@ GeckoMediaPluginServiceParent::InitStorage()
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Prior to 42, GMP storage was stored in $profile/gmp/. After 42, it's
|
||||
// stored in $profile/gmp/$platform/. So we must migrate any old records
|
||||
// Prior to 42, GMP storage was stored in $profileDir/gmp/. After 42, it's
|
||||
// stored in $profileDir/gmp/$platform/. So we must migrate any old records
|
||||
// from the old location to the new location, for forwards compatibility.
|
||||
MigratePreGecko42StorageDir(gmpDirWithoutPlatform, mStorageBaseDir);
|
||||
|
||||
// Prior to 45, GMP storage was not separated by plugin. In 45 and after,
|
||||
// it's stored in $profile/gmp/$platform/$gmpName. So we must migrate old
|
||||
// records from the old location to the new location, for forwards
|
||||
// compatibility. We assume all directories in the base storage dir that
|
||||
// aren't known to GMP storage are records for the Adobe GMP, since it
|
||||
// was first.
|
||||
MigratePreGecko45StorageDir(mStorageBaseDir);
|
||||
|
||||
return GeckoMediaPluginService::Init();
|
||||
}
|
||||
|
||||
@@ -377,16 +412,30 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
|
||||
}
|
||||
|
||||
if (gmpThread) {
|
||||
LOGD(("%s::%s Starting to unload plugins, waiting for first sync shutdown..."
|
||||
, __CLASS__, __FUNCTION__));
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(nullptr, '0',
|
||||
NS_LITERAL_CSTRING("Dispatching UnloadPlugins"));
|
||||
#endif
|
||||
gmpThread->Dispatch(
|
||||
NS_NewRunnableMethod(this,
|
||||
&GeckoMediaPluginServiceParent::UnloadPlugins),
|
||||
NS_DISPATCH_NORMAL);
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(nullptr, '1',
|
||||
NS_LITERAL_CSTRING("Waiting for sync shutdown"));
|
||||
#endif
|
||||
// Wait for UnloadPlugins() to do initial sync shutdown...
|
||||
while (mWaitingForPluginsSyncShutdown) {
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(nullptr, '4',
|
||||
NS_LITERAL_CSTRING("Waiting for async shutdown"));
|
||||
#endif
|
||||
// Wait for other plugins (if any) to do async shutdown...
|
||||
auto syncShutdownPluginsRemaining =
|
||||
std::numeric_limits<decltype(mAsyncShutdownPlugins.Length())>::max();
|
||||
@@ -421,7 +470,13 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
|
||||
}
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
}
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(nullptr, '5',
|
||||
NS_LITERAL_CSTRING("Async shutdown complete"));
|
||||
#endif
|
||||
} else {
|
||||
// GMP thread has already shutdown.
|
||||
MOZ_ASSERT(mPlugins.IsEmpty());
|
||||
mWaitingForPluginsSyncShutdown = false;
|
||||
}
|
||||
|
||||
@@ -520,7 +575,14 @@ GeckoMediaPluginServiceParent::SetAsyncShutdownPluginState(GMPParent* aGMPParent
|
||||
char aId,
|
||||
const nsCString& aState)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MutexAutoLock lock(mAsyncShutdownPluginStatesMutex);
|
||||
if (!aGMPParent) {
|
||||
mAsyncShutdownPluginStates.Update(NS_LITERAL_CSTRING("-"),
|
||||
NS_LITERAL_CSTRING("-"),
|
||||
aId,
|
||||
aState);
|
||||
return;
|
||||
}
|
||||
mAsyncShutdownPluginStates.Update(aGMPParent->GetDisplayName(),
|
||||
nsPrintfCString("%p", aGMPParent),
|
||||
aId,
|
||||
@@ -547,13 +609,13 @@ GeckoMediaPluginServiceParent::AsyncShutdownPluginStates::Update(const nsCString
|
||||
note += pluginIt.Key();
|
||||
note += ":{";
|
||||
bool firstInstance = true;
|
||||
for (auto instanceIt = pluginIt.Data()->ConstIter(); !instanceIt.Done(); instanceIt.Next()) {
|
||||
for (auto instanceIt = pluginIt.UserData()->ConstIter(); !instanceIt.Done(); instanceIt.Next()) {
|
||||
if (!firstInstance) { note += ','; } else { firstInstance = false; }
|
||||
note += instanceIt.Key();
|
||||
note += ":\"";
|
||||
note += instanceIt.Data()->mStateSequence;
|
||||
note += instanceIt.UserData()->mStateSequence;
|
||||
note += '=';
|
||||
note += instanceIt.Data()->mLastStateDescription;
|
||||
note += instanceIt.UserData()->mLastStateDescription;
|
||||
note += '"';
|
||||
}
|
||||
note += '}';
|
||||
@@ -587,6 +649,10 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
MOZ_ASSERT(!mShuttingDownOnGMPThread);
|
||||
mShuttingDownOnGMPThread = true;
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(nullptr, '2',
|
||||
NS_LITERAL_CSTRING("Starting to unload plugins"));
|
||||
#endif
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
@@ -605,11 +671,19 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
|
||||
// Note: CloseActive may be async; it could actually finish
|
||||
// shutting down when all the plugins have unloaded.
|
||||
for (size_t i = 0; i < mPlugins.Length(); i++) {
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(mPlugins[i], 'S',
|
||||
NS_LITERAL_CSTRING("CloseActive"));
|
||||
#endif
|
||||
mPlugins[i]->CloseActive(true);
|
||||
}
|
||||
mPlugins.Clear();
|
||||
}
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SetAsyncShutdownPluginState(nullptr, '3',
|
||||
NS_LITERAL_CSTRING("Dispatching sync-shutdown-complete"));
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete));
|
||||
NS_DispatchToMainThread(task);
|
||||
@@ -845,9 +919,17 @@ GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
|
||||
return gmp;
|
||||
}
|
||||
|
||||
// This GMP has the correct type but has the wrong nodeId; hold on to it
|
||||
// in case we need to clone it.
|
||||
gmpToClone = gmp;
|
||||
if (!gmpToClone ||
|
||||
(gmpToClone->IsMarkedForDeletion() && !gmp->IsMarkedForDeletion())) {
|
||||
// This GMP has the correct type but has the wrong nodeId; hold on to it
|
||||
// in case we need to clone it.
|
||||
// Prefer GMPs in-use for the case where an upgraded plugin version is
|
||||
// waiting for the old one to die. If the old plugin is in use, we
|
||||
// should continue using it so that any persistent state remains
|
||||
// consistent. Otherwise, just check that the plugin isn't scheduled
|
||||
// for deletion.
|
||||
gmpToClone = gmp;
|
||||
}
|
||||
// Loop around and try the next plugin; it may be usable from aNodeId.
|
||||
index++;
|
||||
}
|
||||
@@ -1141,8 +1223,8 @@ GeckoMediaPluginServiceParent::IsPersistentStorageAllowed(const nsACString& aNod
|
||||
nsresult
|
||||
GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsing,
|
||||
const nsACString& aVersion,
|
||||
nsACString& aOutId)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
@@ -1171,15 +1253,15 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
}
|
||||
|
||||
const uint32_t hash = AddToHash(HashString(aOrigin),
|
||||
HashString(aTopLevelOrigin),
|
||||
HashString(aVersion));
|
||||
HashString(aTopLevelOrigin));
|
||||
|
||||
if (aInPrivateBrowsing) {
|
||||
// For PB mode, we store the node id, indexed by the origin pair,
|
||||
// so that if the same origin pair is opened in this session, it gets
|
||||
// the same node id.
|
||||
// For PB mode, we store the node id, indexed by the origin pair and GMP name,
|
||||
// so that if the same origin pair is opened for the same GMP in this session,
|
||||
// it gets the same node id.
|
||||
const uint32_t pbHash = AddToHash(HashString(aGMPName), hash);
|
||||
nsCString* salt = nullptr;
|
||||
if (!(salt = mTempNodeIds.Get(hash))) {
|
||||
if (!(salt = mTempNodeIds.Get(pbHash))) {
|
||||
// No salt stored, generate and temporarily store some for this id.
|
||||
nsAutoCString newSalt;
|
||||
rv = GenerateRandomPathName(newSalt, NodeIdSaltLength);
|
||||
@@ -1187,7 +1269,7 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
return rv;
|
||||
}
|
||||
salt = new nsCString(newSalt);
|
||||
mTempNodeIds.Put(hash, salt);
|
||||
mTempNodeIds.Put(pbHash, salt);
|
||||
mPersistentStorageAllowed.Put(*salt, false);
|
||||
}
|
||||
aOutId = *salt;
|
||||
@@ -1196,18 +1278,29 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
|
||||
// Otherwise, try to see if we've previously generated and stored salt
|
||||
// for this origin pair.
|
||||
nsCOMPtr<nsIFile> path; // $profileDir/gmp/
|
||||
nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
|
||||
rv = GetStorageDir(getter_AddRefs(path));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = path->Append(aGMPName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/$platform/$gmpName/
|
||||
rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
||||
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/id/
|
||||
// $profileDir/gmp/$platform/$gmpName/id/
|
||||
rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
||||
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
@@ -1216,7 +1309,7 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
nsAutoCString hashStr;
|
||||
hashStr.AppendInt((int64_t)hash);
|
||||
|
||||
// $profileDir/gmp/id/$hash
|
||||
// $profileDir/gmp/$platform/$gmpName/id/$hash
|
||||
rv = path->AppendNative(hashStr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
@@ -1253,13 +1346,13 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
}
|
||||
MOZ_ASSERT(salt.Length() == NodeIdSaltLength);
|
||||
|
||||
// $profileDir/gmp/id/$hash/salt
|
||||
// $profileDir/gmp/$platform/$gmpName/id/$hash/salt
|
||||
rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/id/$hash/origin
|
||||
// $profileDir/gmp/$platform/$gmpName/id/$hash/origin
|
||||
rv = WriteToFile(path,
|
||||
NS_LITERAL_CSTRING("origin"),
|
||||
NS_ConvertUTF16toUTF8(aOrigin));
|
||||
@@ -1267,7 +1360,7 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
return rv;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/id/$hash/topLevelOrigin
|
||||
// $profileDir/gmp/$platform/$gmpName/id/$hash/topLevelOrigin
|
||||
rv = WriteToFile(path,
|
||||
NS_LITERAL_CSTRING("topLevelOrigin"),
|
||||
NS_ConvertUTF16toUTF8(aTopLevelOrigin));
|
||||
@@ -1291,16 +1384,32 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsing,
|
||||
const nsACString& aVersion,
|
||||
UniquePtr<GetNodeIdCallback>&& aCallback)
|
||||
{
|
||||
nsCString nodeId;
|
||||
nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, aVersion, nodeId);
|
||||
nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, nodeId);
|
||||
aCallback->Done(rv, nodeId);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginServiceParent::UpdateTrialCreateState(const nsAString& aKeySystem,
|
||||
uint32_t aState)
|
||||
{
|
||||
#ifdef MOZ_EME
|
||||
nsString keySystem(aKeySystem);
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction([keySystem, aState] {
|
||||
mozilla::dom::GMPVideoDecoderTrialCreator::UpdateTrialCreateState(keySystem, aState);
|
||||
}));
|
||||
|
||||
return NS_OK;
|
||||
#else
|
||||
return NS_ERROR_FAILURE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
ExtractHostName(const nsACString& aOrigin, nsACString& aOutData)
|
||||
{
|
||||
@@ -1397,54 +1506,40 @@ private:
|
||||
void
|
||||
GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
|
||||
{
|
||||
nsresult rv;
|
||||
// $profileDir/gmp/$platform/
|
||||
nsCOMPtr<nsIFile> path;
|
||||
|
||||
// $profileDir/gmp/
|
||||
rv = GetStorageDir(getter_AddRefs(path));
|
||||
nsresult rv = GetStorageDir(getter_AddRefs(path));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/id/
|
||||
rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
|
||||
if (NS_FAILED(rv)) {
|
||||
// Iterate all sub-folders of $profileDir/gmp/$platform/, i.e. the dirs in which
|
||||
// specific GMPs store their data.
|
||||
DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
|
||||
for (nsCOMPtr<nsIFile> pluginDir; (pluginDir = iter.Next()) != nullptr;) {
|
||||
ClearNodeIdAndPlugin(pluginDir, aFilter);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
|
||||
DirectoryFilter& aFilter)
|
||||
{
|
||||
// $profileDir/gmp/$platform/$gmpName/id/
|
||||
nsCOMPtr<nsIFile> path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("id"));
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate all sub-folders of $profileDir/gmp/id/
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
rv = path->GetDirectoryEntries(getter_AddRefs(iter));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasMore = false;
|
||||
// Iterate all sub-folders of $profileDir/gmp/$platform/$gmpName/id/
|
||||
nsTArray<nsCString> nodeIDsToClear;
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
rv = iter->GetNext(getter_AddRefs(supports));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/id/$hash
|
||||
nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip non-directory files.
|
||||
bool isDirectory = false;
|
||||
rv = dirEntry->IsDirectory(&isDirectory);
|
||||
if (NS_FAILED(rv) || !isDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
|
||||
for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
|
||||
// dirEntry is the hash of origins, i.e.:
|
||||
// $profileDir/gmp/$platform/$gmpName/id/$originHash/
|
||||
if (!aFilter(dirEntry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString salt;
|
||||
if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) {
|
||||
// Keep node IDs to clear data/plugins associated with them later.
|
||||
@@ -1458,28 +1553,23 @@ GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
|
||||
}
|
||||
}
|
||||
|
||||
// Kill plugins that have node IDs to be cleared.
|
||||
// Kill plugin instances that have node IDs being cleared.
|
||||
KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
|
||||
|
||||
// Clear all matching $profileDir/gmp/storage/$nodeId/
|
||||
rv = GetStorageDir(getter_AddRefs(path));
|
||||
if (NS_FAILED(rv)) {
|
||||
// Clear all storage in $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
|
||||
path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("storage"));
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = path->AppendNative(NS_LITERAL_CSTRING("storage"));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nodeIDsToClear.Length(); i++) {
|
||||
for (const nsCString& nodeId : nodeIDsToClear) {
|
||||
nsCOMPtr<nsIFile> dirEntry;
|
||||
rv = path->Clone(getter_AddRefs(dirEntry));
|
||||
nsresult rv = path->Clone(getter_AddRefs(dirEntry));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rv = dirEntry->AppendNative(nodeIDsToClear[i]);
|
||||
rv = dirEntry->AppendNative(nodeId);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1514,16 +1604,9 @@ GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince));
|
||||
|
||||
nsCOMPtr<nsIFile> storagePath;
|
||||
nsCOMPtr<nsIFile> temp;
|
||||
if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) &&
|
||||
NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) {
|
||||
storagePath = temp.forget();
|
||||
}
|
||||
|
||||
struct MTimeFilter : public DirectoryFilter {
|
||||
explicit MTimeFilter(PRTime aSince, already_AddRefed<nsIFile> aPath)
|
||||
: mSince(aSince), mStoragePath(aPath) {}
|
||||
explicit MTimeFilter(PRTime aSince)
|
||||
: mSince(aSince) {}
|
||||
|
||||
// Return true if any files under aPath is modified after |mSince|.
|
||||
bool IsModifiedAfter(nsIFile* aPath) {
|
||||
@@ -1532,63 +1615,47 @@ GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
|
||||
if (NS_SUCCEEDED(rv) && lastModified >= mSince) {
|
||||
return true;
|
||||
}
|
||||
// Check sub-directories recursively
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
rv = aPath->GetDirectoryEntries(getter_AddRefs(iter));
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasMore = false;
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
rv = iter->GetNext(getter_AddRefs(supports));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsModifiedAfter(path)) {
|
||||
DirectoryEnumerator iter(aPath, DirectoryEnumerator::FilesAndDirs);
|
||||
for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
|
||||
if (IsModifiedAfter(dirEntry)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// |aPath| is $profileDir/gmp/id/$hash
|
||||
// |aPath| is $profileDir/gmp/$platform/$gmpName/id/$originHash/
|
||||
virtual bool operator()(nsIFile* aPath) {
|
||||
if (IsModifiedAfter(aPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAutoCString salt;
|
||||
nsresult rv = ReadSalt(aPath, salt);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (NS_FAILED(ReadSalt(aPath, salt))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/storage/
|
||||
if (!mStoragePath) {
|
||||
// $profileDir/gmp/$platform/$gmpName/id/
|
||||
nsCOMPtr<nsIFile> idDir;
|
||||
if (NS_FAILED(aPath->GetParent(getter_AddRefs(idDir)))) {
|
||||
return false;
|
||||
}
|
||||
// $profileDir/gmp/storage/$nodeId/
|
||||
nsCOMPtr<nsIFile> path;
|
||||
rv = mStoragePath->Clone(getter_AddRefs(path));
|
||||
if (NS_FAILED(rv)) {
|
||||
// $profileDir/gmp/$platform/$gmpName/
|
||||
nsCOMPtr<nsIFile> temp;
|
||||
if (NS_FAILED(idDir->GetParent(getter_AddRefs(temp)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rv = path->AppendNative(salt);
|
||||
return NS_SUCCEEDED(rv) && IsModifiedAfter(path);
|
||||
// $profileDir/gmp/$platform/$gmpName/storage/
|
||||
if (NS_FAILED(temp->Append(NS_LITERAL_STRING("storage")))) {
|
||||
return false;
|
||||
}
|
||||
// $profileDir/gmp/$platform/$gmpName/storage/$originSalt
|
||||
return NS_SUCCEEDED(temp->AppendNative(salt)) && IsModifiedAfter(temp);
|
||||
}
|
||||
private:
|
||||
const PRTime mSince;
|
||||
const nsCOMPtr<nsIFile> mStoragePath;
|
||||
} filter(aSince, storagePath.forget());
|
||||
} filter(aSince);
|
||||
|
||||
ClearNodeIdAndPlugin(filter);
|
||||
|
||||
@@ -1617,7 +1684,7 @@ GeckoMediaPluginServiceParent::ClearStorage()
|
||||
// Kill plugins with valid nodeIDs.
|
||||
KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
|
||||
|
||||
nsCOMPtr<nsIFile> path; // $profileDir/gmp/
|
||||
nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
|
||||
nsresult rv = GetStorageDir(getter_AddRefs(path));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
@@ -1662,15 +1729,23 @@ GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId,
|
||||
bool
|
||||
GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
|
||||
const nsString& aTopLevelOrigin,
|
||||
const nsString& aGMPName,
|
||||
const bool& aInPrivateBrowsing,
|
||||
const nsCString& aVersion,
|
||||
nsCString* aID)
|
||||
{
|
||||
nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin,
|
||||
aInPrivateBrowsing, aVersion, *aID);
|
||||
nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName,
|
||||
aInPrivateBrowsing, *aID);
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
bool
|
||||
GMPServiceParent::RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
|
||||
const uint32_t& aState)
|
||||
{
|
||||
mService->UpdateTrialCreateState(aKeySystem, aState);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
GMPServiceParent::RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
|
||||
|
||||
@@ -41,9 +41,11 @@ public:
|
||||
nsACString& aOutVersion) override;
|
||||
NS_IMETHOD GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsingMode,
|
||||
const nsACString& aVersion,
|
||||
UniquePtr<GetNodeIdCallback>&& aCallback) override;
|
||||
NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
|
||||
uint32_t aState) override;
|
||||
|
||||
NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
@@ -73,8 +75,8 @@ private:
|
||||
size_t* aOutPluginIndex);
|
||||
|
||||
nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
|
||||
bool aInPrivateBrowsing, const nsACString& aVersion,
|
||||
nsACString& aOutId);
|
||||
const nsAString& aGMPName,
|
||||
bool aInPrivateBrowsing, nsACString& aOutId);
|
||||
|
||||
void UnloadPlugins();
|
||||
void CrashPlugins();
|
||||
@@ -96,7 +98,8 @@ private:
|
||||
~DirectoryFilter() {}
|
||||
};
|
||||
void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
|
||||
|
||||
void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
|
||||
DirectoryFilter& aFilter);
|
||||
void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
|
||||
void ClearRecentHistoryOnGMPThread(PRTime aSince);
|
||||
|
||||
@@ -152,7 +155,7 @@ private:
|
||||
void Update(const nsCString& aPlugin, const nsCString& aInstance,
|
||||
char aId, const nsCString& aState);
|
||||
private:
|
||||
struct State { nsAutoCString mStateSequence; nsCString mLastStateDescription; };
|
||||
struct State { nsCString mStateSequence; nsCString mLastStateDescription; };
|
||||
typedef nsClassHashtable<nsCStringHashKey, State> StatesByInstance;
|
||||
typedef nsClassHashtable<nsCStringHashKey, StatesByInstance> StateInstancesByPlugin;
|
||||
StateInstancesByPlugin mStates;
|
||||
@@ -214,13 +217,15 @@ public:
|
||||
uint32_t* aPluginId) override;
|
||||
virtual bool RecvGetGMPNodeId(const nsString& aOrigin,
|
||||
const nsString& aTopLevelOrigin,
|
||||
const nsString& aGMPName,
|
||||
const bool& aInPrivateBrowsing,
|
||||
const nsCString& aVersion,
|
||||
nsCString* aID) override;
|
||||
static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
|
||||
nsTArray<nsCString>&& aTags,
|
||||
bool* aHasPlugin,
|
||||
nsCString* aVersion);
|
||||
virtual bool RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
|
||||
const uint32_t& aState) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ GMPSharedMemManager::MgrAllocShmem(GMPSharedMem::GMPMemoryClasses aClass, size_t
|
||||
size_t pagesize = ipc::SharedMemory::SystemPageSize();
|
||||
aSize = (aSize + (pagesize-1)) & ~(pagesize-1); // round up to page size
|
||||
bool retval = Alloc(aSize, aType, aMem);
|
||||
// The allocator (or NeedsShmem call) should never return less than we ask for...
|
||||
MOZ_ASSERT(aMem->Size<uint8_t>() >= aSize);
|
||||
if (retval) {
|
||||
// The allocator (or NeedsShmem call) should never return less than we ask for...
|
||||
MOZ_ASSERT(aMem->Size<uint8_t>() >= aSize);
|
||||
mData->mGmpAllocated[aClass]++;
|
||||
}
|
||||
return retval;
|
||||
|
||||
@@ -32,10 +32,12 @@ extern LogModule* GetGMPLog();
|
||||
|
||||
namespace gmp {
|
||||
|
||||
// We store the records in files in the profile dir.
|
||||
// $profileDir/gmp/storage/$nodeId/
|
||||
// We store the records for a given GMP as files in the profile dir.
|
||||
// $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
|
||||
static nsresult
|
||||
GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
|
||||
GetGMPStorageDir(nsIFile** aTempDir,
|
||||
const nsString& aGMPName,
|
||||
const nsCString& aNodeId)
|
||||
{
|
||||
if (NS_WARN_IF(!aTempDir)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
@@ -53,6 +55,16 @@ GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = tmpFile->Append(aGMPName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
|
||||
if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage"));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
@@ -87,8 +99,10 @@ GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
|
||||
// record bytes (entire remainder of file)
|
||||
class GMPDiskStorage : public GMPStorage {
|
||||
public:
|
||||
explicit GMPDiskStorage(const nsCString& aNodeId)
|
||||
explicit GMPDiskStorage(const nsCString& aNodeId,
|
||||
const nsString& aGMPName)
|
||||
: mNodeId(aNodeId)
|
||||
, mGMPName(aGMPName)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -106,29 +120,13 @@ public:
|
||||
nsresult Init() {
|
||||
// Build our index of records on disk.
|
||||
nsCOMPtr<nsIFile> storageDir;
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
rv = storageDir->GetDirectoryEntries(getter_AddRefs(iter));
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool hasMore;
|
||||
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
rv = iter->GetNext(getter_AddRefs(supports));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DirectoryEnumerator iter(storageDir, DirectoryEnumerator::FilesAndDirs);
|
||||
for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
|
||||
PRFileDesc* fd = nullptr;
|
||||
if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
|
||||
continue;
|
||||
@@ -334,7 +332,7 @@ private:
|
||||
nsString& aOutFilename)
|
||||
{
|
||||
nsCOMPtr<nsIFile> storageDir;
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@@ -379,7 +377,7 @@ private:
|
||||
MOZ_ASSERT(aOutFD);
|
||||
|
||||
nsCOMPtr<nsIFile> f;
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@@ -456,7 +454,7 @@ private:
|
||||
nsresult RemoveStorageFile(const nsString& aFilename)
|
||||
{
|
||||
nsCOMPtr<nsIFile> f;
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
|
||||
nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@@ -484,7 +482,8 @@ private:
|
||||
|
||||
// Hash record name to record data.
|
||||
nsClassHashtable<nsCStringHashKey, Record> mRecords;
|
||||
const nsAutoCString mNodeId;
|
||||
const nsCString mNodeId;
|
||||
const nsString mGMPName;
|
||||
};
|
||||
|
||||
class GMPMemoryStorage : public GMPStorage {
|
||||
@@ -592,7 +591,8 @@ GMPStorageParent::Init()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (persistent) {
|
||||
UniquePtr<GMPDiskStorage> storage = MakeUnique<GMPDiskStorage>(mNodeId);
|
||||
UniquePtr<GMPDiskStorage> storage =
|
||||
MakeUnique<GMPDiskStorage>(mNodeId, mPlugin->GetPluginBaseName());
|
||||
if (NS_FAILED(storage->Init())) {
|
||||
NS_WARNING("Failed to initialize on disk GMP storage");
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "nsLiteralString.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -71,4 +72,41 @@ FileExists(nsIFile* aFile)
|
||||
return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
|
||||
}
|
||||
|
||||
DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode)
|
||||
: mMode(aMode)
|
||||
{
|
||||
aPath->GetDirectoryEntries(getter_AddRefs(mIter));
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
DirectoryEnumerator::Next()
|
||||
{
|
||||
if (!mIter) {
|
||||
return nullptr;
|
||||
}
|
||||
bool hasMore = false;
|
||||
while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> supports;
|
||||
nsresult rv = mIter->GetNext(getter_AddRefs(supports));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mMode == DirsOnly) {
|
||||
bool isDirectory = false;
|
||||
rv = path->IsDirectory(&isDirectory);
|
||||
if (NS_FAILED(rv) || !isDirectory) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return path.forget();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsIFile;
|
||||
class nsCString;
|
||||
class nsISimpleEnumerator;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -40,6 +42,24 @@ ToBase64(const nsTArray<uint8_t>& aBytes);
|
||||
bool
|
||||
FileExists(nsIFile* aFile);
|
||||
|
||||
// Enumerate directory entries for a specified path.
|
||||
class DirectoryEnumerator {
|
||||
public:
|
||||
|
||||
enum Mode {
|
||||
DirsOnly, // Enumeration only includes directories.
|
||||
FilesAndDirs // Enumeration includes directories and non-directory files.
|
||||
};
|
||||
|
||||
DirectoryEnumerator(nsIFile* aPath, Mode aMode);
|
||||
|
||||
already_AddRefed<nsIFile> Next();
|
||||
|
||||
private:
|
||||
Mode mMode;
|
||||
nsCOMPtr<nsISimpleEnumerator> mIter;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,7 @@ extern LogModule* GetGMPLog();
|
||||
|
||||
#define LOGV(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Verbose, msg)
|
||||
#define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
|
||||
#define LOGE(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Error, msg)
|
||||
#define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
|
||||
|
||||
namespace gmp {
|
||||
@@ -50,6 +51,7 @@ GMPVideoDecoderParent::GMPVideoDecoderParent(GMPContentParent* aPlugin)
|
||||
, mCallback(nullptr)
|
||||
, mVideoHost(this)
|
||||
, mPluginId(aPlugin->GetPluginId())
|
||||
, mFrameCount(0)
|
||||
{
|
||||
MOZ_ASSERT(mPlugin);
|
||||
}
|
||||
@@ -131,6 +133,7 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame,
|
||||
aInputFrame->FrameType() == kGMPKeyFrame));
|
||||
|
||||
if (!mIsOpen) {
|
||||
LOGE(("GMPVideoDecoderParent[%p]::Decode() ERROR; dead GMPVideoDecoder", this));
|
||||
NS_WARNING("Trying to use an dead GMP video decoder");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -145,6 +148,8 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame,
|
||||
// 3* is because we're using 3 buffers per frame for i420 data for now.
|
||||
if ((NumInUse(GMPSharedMem::kGMPFrameData) > 3*GMPSharedMem::kGMPBufLimit) ||
|
||||
(NumInUse(GMPSharedMem::kGMPEncodedData) > GMPSharedMem::kGMPBufLimit)) {
|
||||
LOGE(("GMPVideoDecoderParent[%p]::Decode() ERROR; shmem buffer limit hit frame=%d encoded=%d",
|
||||
this, NumInUse(GMPSharedMem::kGMPFrameData), NumInUse(GMPSharedMem::kGMPEncodedData)));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@@ -155,8 +160,10 @@ GMPVideoDecoderParent::Decode(GMPUniquePtr<GMPVideoEncodedFrame> aInputFrame,
|
||||
aMissingFrames,
|
||||
aCodecSpecificInfo,
|
||||
aRenderTimeMs)) {
|
||||
LOGE(("GMPVideoDecoderParent[%p]::Decode() ERROR; SendDecode() failure.", this));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mFrameCount++;
|
||||
|
||||
// Async IPC, we don't have access to a return value.
|
||||
return NS_OK;
|
||||
@@ -180,14 +187,33 @@ GMPVideoDecoderParent::Reset()
|
||||
|
||||
mIsAwaitingResetComplete = true;
|
||||
|
||||
RefPtr<GMPVideoDecoderParent> self(this);
|
||||
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([self]() -> void
|
||||
{
|
||||
LOGD(("GMPVideoDecoderParent[%p]::ResetCompleteTimeout() timed out waiting for ResetComplete", self.get()));
|
||||
self->mResetCompleteTimeout = nullptr;
|
||||
LogToBrowserConsole(NS_LITERAL_STRING("GMPVideoDecoderParent timed out waiting for ResetComplete()"));
|
||||
});
|
||||
CancelResetCompleteTimeout();
|
||||
mResetCompleteTimeout = SimpleTimer::Create(task, 5000, mPlugin->GMPThread());
|
||||
|
||||
// Async IPC, we don't have access to a return value.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GMPVideoDecoderParent::CancelResetCompleteTimeout()
|
||||
{
|
||||
if (mResetCompleteTimeout) {
|
||||
mResetCompleteTimeout->Cancel();
|
||||
mResetCompleteTimeout = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoderParent::Drain()
|
||||
{
|
||||
LOGD(("GMPVideoDecoderParent[%p]::Drain()", this));
|
||||
LOGD(("GMPVideoDecoderParent[%p]::Drain() frameCount=%d", this, mFrameCount));
|
||||
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use an dead GMP video decoder");
|
||||
@@ -257,7 +283,6 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
|
||||
mIsOpen = false;
|
||||
mActorDestroyed = true;
|
||||
mVideoHost.DoneWithAPI();
|
||||
|
||||
// Ensure if we've received a destroy while waiting for a ResetComplete
|
||||
// or DrainComplete notification, we'll unblock the caller before processing
|
||||
@@ -280,16 +305,16 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
bool
|
||||
GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
|
||||
{
|
||||
LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld",
|
||||
this, aDecodedFrame.mTimestamp()));
|
||||
--mFrameCount;
|
||||
LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld frameCount=%d",
|
||||
this, aDecodedFrame.mTimestamp(), mFrameCount));
|
||||
|
||||
if (!mCallback) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GMPVideoi420FrameImpl::CheckFrameData(aDecodedFrame)) {
|
||||
LOG(LogLevel::Error,
|
||||
("GMPVideoDecoderParent[%p]::RecvDecoded() "
|
||||
LOGE(("GMPVideoDecoderParent[%p]::RecvDecoded() "
|
||||
"timestamp=%lld decoded frame corrupt, ignoring"));
|
||||
return false;
|
||||
}
|
||||
@@ -345,8 +370,11 @@ GMPVideoDecoderParent::RecvInputDataExhausted()
|
||||
bool
|
||||
GMPVideoDecoderParent::RecvDrainComplete()
|
||||
{
|
||||
LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete()", this));
|
||||
|
||||
LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete() frameCount=%d", this, mFrameCount));
|
||||
nsAutoString msg;
|
||||
msg.AppendLiteral("GMPVideoDecoderParent::RecvDrainComplete() outstanding frames=");
|
||||
msg.AppendInt(mFrameCount);
|
||||
LogToBrowserConsole(msg);
|
||||
if (!mCallback) {
|
||||
return false;
|
||||
}
|
||||
@@ -367,6 +395,8 @@ GMPVideoDecoderParent::RecvResetComplete()
|
||||
{
|
||||
LOGD(("GMPVideoDecoderParent[%p]::RecvResetComplete()", this));
|
||||
|
||||
CancelResetCompleteTimeout();
|
||||
|
||||
if (!mCallback) {
|
||||
return false;
|
||||
}
|
||||
@@ -375,6 +405,7 @@ GMPVideoDecoderParent::RecvResetComplete()
|
||||
return true;
|
||||
}
|
||||
mIsAwaitingResetComplete = false;
|
||||
mFrameCount = 0;
|
||||
|
||||
// Ignore any return code. It is OK for this to fail without killing the process.
|
||||
mCallback->ResetComplete();
|
||||
@@ -431,8 +462,8 @@ GMPVideoDecoderParent::AnswerNeedShmem(const uint32_t& aFrameBufferSize,
|
||||
aFrameBufferSize,
|
||||
ipc::SharedMemory::TYPE_BASIC, &mem))
|
||||
{
|
||||
LOG(LogLevel::Error, ("%s: Failed to get a shared mem buffer for Child! size %u",
|
||||
__FUNCTION__, aFrameBufferSize));
|
||||
LOGE(("%s: Failed to get a shared mem buffer for Child! size %u",
|
||||
__FUNCTION__, aFrameBufferSize));
|
||||
return false;
|
||||
}
|
||||
*aMem = mem;
|
||||
@@ -457,7 +488,9 @@ GMPVideoDecoderParent::Recv__delete__()
|
||||
void
|
||||
GMPVideoDecoderParent::UnblockResetAndDrain()
|
||||
{
|
||||
LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain()", this));
|
||||
LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain() "
|
||||
"awaitingResetComplete=%d awaitingDrainComplete=%d",
|
||||
this, mIsAwaitingResetComplete, mIsAwaitingDrainComplete));
|
||||
|
||||
if (!mCallback) {
|
||||
MOZ_ASSERT(!mIsAwaitingResetComplete);
|
||||
@@ -472,6 +505,7 @@ GMPVideoDecoderParent::UnblockResetAndDrain()
|
||||
mIsAwaitingDrainComplete = false;
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
CancelResetCompleteTimeout();
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "GMPUtils.h"
|
||||
#include "GMPVideoHost.h"
|
||||
#include "GMPVideoDecoderProxy.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
@@ -80,6 +81,7 @@ private:
|
||||
virtual bool Recv__delete__() override;
|
||||
|
||||
void UnblockResetAndDrain();
|
||||
void CancelResetCompleteTimeout();
|
||||
|
||||
bool mIsOpen;
|
||||
bool mShuttingDown;
|
||||
@@ -90,6 +92,8 @@ private:
|
||||
GMPVideoDecoderCallbackProxy* mCallback;
|
||||
GMPVideoHostImpl mVideoHost;
|
||||
const uint32_t mPluginId;
|
||||
int32_t mFrameCount;
|
||||
RefPtr<SimpleTimer> mResetCompleteTimeout;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
||||
@@ -229,7 +229,6 @@ GMPVideoEncoderParent::Shutdown()
|
||||
mCallback->Terminated();
|
||||
mCallback = nullptr;
|
||||
}
|
||||
mVideoHost.DoneWithAPI();
|
||||
|
||||
mIsOpen = false;
|
||||
if (!mActorDestroyed) {
|
||||
@@ -269,7 +268,7 @@ GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
mPlugin->VideoEncoderDestroyed(this);
|
||||
mPlugin = nullptr;
|
||||
}
|
||||
mVideoHost.ActorDestroyed();
|
||||
mVideoHost.ActorDestroyed(); // same as DoneWithAPI
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -331,8 +330,15 @@ bool
|
||||
GMPVideoEncoderParent::RecvParentShmemForPool(Shmem&& aFrameBuffer)
|
||||
{
|
||||
if (aFrameBuffer.IsWritable()) {
|
||||
mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData,
|
||||
aFrameBuffer);
|
||||
// This test may be paranoia now that we don't shut down the VideoHost
|
||||
// in ::Shutdown, but doesn't hurt
|
||||
if (mVideoHost.SharedMemMgr()) {
|
||||
mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMem::kGMPFrameData,
|
||||
aFrameBuffer);
|
||||
} else {
|
||||
LOGD(("%s::%s: %p Called in shutdown, ignoring and freeing directly", __CLASS__, __FUNCTION__, this));
|
||||
DeallocShmem(aFrameBuffer);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -343,7 +349,10 @@ GMPVideoEncoderParent::AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
|
||||
{
|
||||
ipc::Shmem mem;
|
||||
|
||||
if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMem::kGMPEncodedData,
|
||||
// This test may be paranoia now that we don't shut down the VideoHost
|
||||
// in ::Shutdown, but doesn't hurt
|
||||
if (!mVideoHost.SharedMemMgr() ||
|
||||
!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMem::kGMPEncodedData,
|
||||
aEncodedBufferSize,
|
||||
ipc::SharedMemory::TYPE_BASIC, &mem))
|
||||
{
|
||||
|
||||
@@ -71,8 +71,15 @@ GMPVideoHostImpl::SharedMemMgr()
|
||||
return mSharedMemMgr;
|
||||
}
|
||||
|
||||
// XXX This should merge with ActorDestroyed
|
||||
void
|
||||
GMPVideoHostImpl::DoneWithAPI()
|
||||
{
|
||||
ActorDestroyed();
|
||||
}
|
||||
|
||||
void
|
||||
GMPVideoHostImpl::ActorDestroyed()
|
||||
{
|
||||
for (uint32_t i = mPlanes.Length(); i > 0; i--) {
|
||||
mPlanes[i - 1]->DoneWithAPI();
|
||||
@@ -85,20 +92,6 @@ GMPVideoHostImpl::DoneWithAPI()
|
||||
mSharedMemMgr = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GMPVideoHostImpl::ActorDestroyed()
|
||||
{
|
||||
for (uint32_t i = mPlanes.Length(); i > 0; i--) {
|
||||
mPlanes[i - 1]->ActorDestroyed();
|
||||
mPlanes.RemoveElementAt(i - 1);
|
||||
}
|
||||
for (uint32_t i = mEncodedFrames.Length(); i > 0; i--) {
|
||||
mEncodedFrames[i - 1]->ActorDestroyed();
|
||||
mEncodedFrames.RemoveElementAt(i - 1);
|
||||
}
|
||||
mSharedMemMgr = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
GMPVideoHostImpl::PlaneCreated(GMPPlaneImpl* aPlane)
|
||||
{
|
||||
|
||||
@@ -19,8 +19,11 @@ parent:
|
||||
ProcessId[] alreadyBridgedTo)
|
||||
returns (ProcessId id, nsCString displayName, uint32_t pluginId);
|
||||
sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
|
||||
bool inPrivateBrowsing, nsCString version)
|
||||
nsString gmpName,
|
||||
bool inPrivateBrowsing)
|
||||
returns (nsCString id);
|
||||
|
||||
async UpdateGMPTrialCreateState(nsString keySystem, uint32_t status);
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
||||
@@ -52,7 +52,7 @@ native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>
|
||||
native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
|
||||
native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
|
||||
|
||||
[scriptable, uuid(6e3023f4-d9a2-46b4-8f50-70d4c918471d)]
|
||||
[scriptable, uuid(b5492915-2f0e-4973-9f91-a6fe61ac4749)]
|
||||
interface mozIGeckoMediaPluginService : nsISupports
|
||||
{
|
||||
|
||||
@@ -145,7 +145,13 @@ interface mozIGeckoMediaPluginService : nsISupports
|
||||
[noscript]
|
||||
void getNodeId(in AString origin,
|
||||
in AString topLevelOrigin,
|
||||
in AString gmpName,
|
||||
in bool inPrivateBrowsingMode,
|
||||
in ACString version,
|
||||
in GetNodeIdCallback callback);
|
||||
|
||||
/**
|
||||
* Stores the result of trying to create a decoder for the given keysystem.
|
||||
*/
|
||||
[noscript]
|
||||
void updateTrialCreateState(in AString keySystem, in uint32_t status);
|
||||
};
|
||||
|
||||
@@ -273,7 +273,7 @@ EnumerateDir(nsIFile* aPath, T&& aDirIter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate files under $profileDir/gmp/$aDir/ (non-recursive).
|
||||
* Enumerate files under $profileDir/gmp/$platform/gmp-fake/$aDir/ (non-recursive).
|
||||
*/
|
||||
template<typename T>
|
||||
static nsresult
|
||||
@@ -283,14 +283,21 @@ EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter)
|
||||
GeckoMediaPluginServiceParent::GetSingleton();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
// $profileDir/gmp/
|
||||
// $profileDir/gmp/$platform/
|
||||
nsCOMPtr<nsIFile> path;
|
||||
nsresult rv = service->GetStorageDir(getter_AddRefs(path));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/$aDir/
|
||||
|
||||
// $profileDir/gmp/$platform/gmp-fake/
|
||||
rv = path->Append(NS_LITERAL_STRING("gmp-fake"));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// $profileDir/gmp/$platform/gmp-fake/$aDir/
|
||||
rv = path->AppendNative(aDir);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
@@ -469,8 +476,8 @@ GetNodeId(const nsAString& aOrigin,
|
||||
// GeckoMediaPluginServiceParent is synchronous.
|
||||
nsresult rv = service->GetNodeId(aOrigin,
|
||||
aTopLevelOrigin,
|
||||
NS_LITERAL_STRING("gmp-fake"),
|
||||
aInPBMode,
|
||||
NS_LITERAL_CSTRING(""),
|
||||
Move(callback));
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv) && NS_SUCCEEDED(result));
|
||||
return nodeId;
|
||||
@@ -821,10 +828,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
|
||||
/**
|
||||
* 1. Generate some storage data.
|
||||
* 2. Find the max mtime |t| in $profileDir/gmp/id/.
|
||||
* 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/id/.
|
||||
* 3. Pass |t| to clear recent history.
|
||||
* 4. Check if all directories in $profileDir/gmp/id/ and
|
||||
* $profileDir/gmp/storage are removed.
|
||||
* 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
|
||||
* $profileDir/gmp/$platform/gmp-fake/storage are removed.
|
||||
*/
|
||||
void TestClearRecentHistory1() {
|
||||
AssertIsOnGMPThread();
|
||||
@@ -843,10 +850,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
|
||||
/**
|
||||
* 1. Generate some storage data.
|
||||
* 2. Find the max mtime |t| in $profileDir/gmp/storage/.
|
||||
* 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/.
|
||||
* 3. Pass |t| to clear recent history.
|
||||
* 4. Check if all directories in $profileDir/gmp/id/ and
|
||||
* $profileDir/gmp/storage are removed.
|
||||
* 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
|
||||
* $profileDir/gmp/$platform/gmp-fake/storage are removed.
|
||||
*/
|
||||
void TestClearRecentHistory2() {
|
||||
AssertIsOnGMPThread();
|
||||
@@ -865,10 +872,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
|
||||
/**
|
||||
* 1. Generate some storage data.
|
||||
* 2. Find the max mtime |t| in $profileDir/gmp/storage/.
|
||||
* 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/.
|
||||
* 3. Pass |t+1| to clear recent history.
|
||||
* 4. Check if all directories in $profileDir/gmp/id/ and
|
||||
* $profileDir/gmp/storage remain unchanged.
|
||||
* 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
|
||||
* $profileDir/gmp/$platform/gmp-fake/storage remain unchanged.
|
||||
*/
|
||||
void TestClearRecentHistory3() {
|
||||
AssertIsOnGMPThread();
|
||||
@@ -949,13 +956,13 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
FileCounter c1;
|
||||
nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
// There should be no files under $profileDir/gmp/id/
|
||||
// There should be no files under $profileDir/gmp/$platform/gmp-fake/id/
|
||||
EXPECT_EQ(c1.GetCount(), 0);
|
||||
|
||||
FileCounter c2;
|
||||
rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
// There should be no files under $profileDir/gmp/storage/
|
||||
// There should be no files under $profileDir/gmp/$platform/gmp-fake/storage/
|
||||
EXPECT_EQ(c2.GetCount(), 0);
|
||||
|
||||
SetFinished();
|
||||
@@ -965,13 +972,13 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
|
||||
FileCounter c1;
|
||||
nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
// There should be one directory under $profileDir/gmp/id/
|
||||
// There should be one directory under $profileDir/gmp/$platform/gmp-fake/id/
|
||||
EXPECT_EQ(c1.GetCount(), 1);
|
||||
|
||||
FileCounter c2;
|
||||
rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
// There should be one directory under $profileDir/gmp/storage/
|
||||
// There should be one directory under $profileDir/gmp/$platform/gmp-fake/storage/
|
||||
EXPECT_EQ(c2.GetCount(), 1);
|
||||
|
||||
SetFinished();
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
extern "C" {
|
||||
#include "pocketsphinx/pocketsphinx.h"
|
||||
#include "sphinxbase/logmath.h"
|
||||
#include "sphinxbase/sphinx_config.h"
|
||||
#include "sphinxbase/jsgf.h"
|
||||
}
|
||||
@@ -33,8 +34,10 @@ class DecodeResultTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DecodeResultTask(const nsString& hypstring,
|
||||
float64 confidence,
|
||||
WeakPtr<dom::SpeechRecognition> recognition)
|
||||
: mResult(hypstring),
|
||||
mConfidence(confidence),
|
||||
mRecognition(recognition),
|
||||
mWorkerThread(do_GetCurrentThread())
|
||||
{
|
||||
@@ -59,7 +62,7 @@ public:
|
||||
new SpeechRecognitionAlternative(mRecognition);
|
||||
|
||||
alternative->mTranscript = mResult;
|
||||
alternative->mConfidence = 100;
|
||||
alternative->mConfidence = mConfidence;
|
||||
|
||||
result->mItems.AppendElement(alternative);
|
||||
}
|
||||
@@ -77,6 +80,7 @@ public:
|
||||
|
||||
private:
|
||||
nsString mResult;
|
||||
float64 mConfidence;
|
||||
WeakPtr<dom::SpeechRecognition> mRecognition;
|
||||
nsCOMPtr<nsIThread> mWorkerThread;
|
||||
};
|
||||
@@ -95,7 +99,9 @@ public:
|
||||
{
|
||||
char const* hyp;
|
||||
int rv;
|
||||
int32 score;
|
||||
int32 final;
|
||||
int32 logprob;
|
||||
float64 confidence;
|
||||
nsAutoCString hypoValue;
|
||||
|
||||
rv = ps_start_utt(mPs);
|
||||
@@ -103,15 +109,18 @@ public:
|
||||
FALSE);
|
||||
|
||||
rv = ps_end_utt(mPs);
|
||||
confidence = 0;
|
||||
if (rv >= 0) {
|
||||
hyp = ps_get_hyp(mPs, &score);
|
||||
if (hyp) {
|
||||
hyp = ps_get_hyp_final(mPs, &final);
|
||||
if (hyp && final) {
|
||||
logprob = ps_get_prob(mPs);
|
||||
confidence = logmath_exp(ps_get_logmath(mPs), logprob);
|
||||
hypoValue.Assign(hyp);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> resultrunnable =
|
||||
new DecodeResultTask(NS_ConvertUTF8toUTF16(hypoValue), mRecognition);
|
||||
new DecodeResultTask(NS_ConvertUTF8toUTF16(hypoValue), confidence, mRecognition);
|
||||
return NS_DispatchToMainThread(resultrunnable);
|
||||
}
|
||||
|
||||
@@ -154,7 +163,7 @@ PocketSphinxSpeechRecognitionService::PocketSphinxSpeechRecognitionService()
|
||||
|
||||
// FOR B2G PATHS HARDCODED (APPEND /DATA ON THE BEGINING, FOR DESKTOP, ONLY
|
||||
// MODELS/ RELATIVE TO ROOT
|
||||
mPSConfig = cmd_ln_init(nullptr, ps_args(), TRUE, "-hmm",
|
||||
mPSConfig = cmd_ln_init(nullptr, ps_args(), TRUE, "-bestpath", "yes", "-hmm",
|
||||
ToNewUTF8String(aStringAMPath), // acoustic model
|
||||
"-dict", ToNewUTF8String(aStringDictPath), nullptr);
|
||||
if (mPSConfig == nullptr) {
|
||||
@@ -268,11 +277,11 @@ PocketSphinxSpeechRecognitionService::ValidateAndSetGrammarList(
|
||||
int result = ps_set_jsgf_string(mPSHandle, "name",
|
||||
NS_ConvertUTF16toUTF8(grammar).get());
|
||||
|
||||
ps_set_search(mPSHandle, "name");
|
||||
|
||||
if (result != 0) {
|
||||
ISGrammarCompiled = false;
|
||||
} else {
|
||||
ps_set_search(mPSHandle, "name");
|
||||
|
||||
ISGrammarCompiled = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
#include "endpointer.h"
|
||||
|
||||
#include "mozilla/dom/SpeechRecognitionEvent.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsQueryObject.h"
|
||||
@@ -125,9 +128,9 @@ SpeechRecognition::SpeechRecognition(nsPIDOMWindow* aOwnerWindow)
|
||||
}
|
||||
|
||||
mEndpointer.set_speech_input_complete_silence_length(
|
||||
Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 500000));
|
||||
Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 1250000));
|
||||
mEndpointer.set_long_speech_input_complete_silence_length(
|
||||
Preferences::GetInt(PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH, 1000000));
|
||||
Preferences::GetInt(PREFERENCE_ENDPOINTER_LONG_SILENCE_LENGTH, 2500000));
|
||||
mEndpointer.set_long_speech_length(
|
||||
Preferences::GetInt(PREFERENCE_ENDPOINTER_SILENCE_LENGTH, 3 * 1000000));
|
||||
Reset();
|
||||
@@ -156,11 +159,26 @@ SpeechRecognition::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
bool
|
||||
SpeechRecognition::IsAuthorized(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
bool inCertifiedApp = IsInCertifiedApp(aCx, aGlobal);
|
||||
nsCOMPtr<nsIPrincipal> principal = nsContentUtils::ObjectPrincipal(aGlobal);
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPermissionManager> mgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t speechRecognition = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
rv = mgr->TestExactPermissionFromPrincipal(principal, "speech-recognition", &speechRecognition);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasPermission = (speechRecognition == nsIPermissionManager::ALLOW_ACTION);
|
||||
|
||||
bool enableTests = Preferences::GetBool(TEST_PREFERENCE_ENABLE);
|
||||
bool enableRecognitionEnable = Preferences::GetBool(TEST_PREFERENCE_RECOGNITION_ENABLE);
|
||||
bool enableRecognitionForceEnable = Preferences::GetBool(TEST_PREFERENCE_RECOGNITION_FORCE_ENABLE);
|
||||
return (inCertifiedApp || enableRecognitionForceEnable || enableTests) && enableRecognitionEnable;
|
||||
return (hasPermission || enableRecognitionForceEnable || enableTests) && enableRecognitionEnable;
|
||||
}
|
||||
|
||||
already_AddRefed<SpeechRecognition>
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
/* System headers. */
|
||||
#include <string.h>
|
||||
#include <limits.h> // We need this for LONG_MIN
|
||||
|
||||
/* SphinxBase headers. */
|
||||
#include <sphinxbase/pio.h>
|
||||
@@ -249,14 +250,14 @@ dict_write(dict_t *dict, char const *filename, char const *format)
|
||||
|
||||
|
||||
dict_t *
|
||||
dict_init(cmd_ln_t *config, bin_mdef_t * mdef)
|
||||
dict_init(cmd_ln_t *config, bin_mdef_t * mdef, logmath_t *logmath)
|
||||
{
|
||||
FILE *fp, *fp2;
|
||||
int32 n;
|
||||
lineiter_t *li;
|
||||
dict_t *d;
|
||||
s3cipid_t sil;
|
||||
char const *dictfile = NULL, *fillerfile = NULL;
|
||||
char const *dictfile = NULL, *fillerfile = NULL, *arpafile = NULL;
|
||||
|
||||
if (config) {
|
||||
dictfile = cmd_ln_str_r(config, "-dict");
|
||||
@@ -303,6 +304,19 @@ dict_init(cmd_ln_t *config, bin_mdef_t * mdef)
|
||||
* Also check for type size restrictions.
|
||||
*/
|
||||
d = (dict_t *) ckd_calloc(1, sizeof(dict_t)); /* freed in dict_free() */
|
||||
if (config){
|
||||
arpafile = string_join(dictfile, ".dmp", NULL);
|
||||
}
|
||||
if (arpafile) {
|
||||
ngram_model_t *ngram_g2p_model = ngram_model_read(NULL,arpafile,NGRAM_AUTO,logmath);
|
||||
ckd_free(arpafile);
|
||||
if (!ngram_g2p_model) {
|
||||
E_ERROR("No arpa model found \n");
|
||||
return NULL;
|
||||
}
|
||||
d->ngram_g2p_model = ngram_g2p_model;
|
||||
}
|
||||
|
||||
d->refcnt = 1;
|
||||
d->max_words =
|
||||
(n + S3DICT_INC_SZ < MAX_S3WID) ? n + S3DICT_INC_SZ : MAX_S3WID;
|
||||
@@ -474,6 +488,8 @@ dict_free(dict_t * d)
|
||||
hash_table_free(d->ht);
|
||||
if (d->mdef)
|
||||
bin_mdef_free(d->mdef);
|
||||
if (d->ngram_g2p_model)
|
||||
ngram_model_free(d->ngram_g2p_model);
|
||||
ckd_free((void *) d);
|
||||
|
||||
return 0;
|
||||
@@ -487,3 +503,237 @@ dict_report(dict_t * d)
|
||||
E_INFO_NOFN("No of word: %d\n", d->n_word);
|
||||
E_INFO_NOFN("\n");
|
||||
}
|
||||
|
||||
// This function returns if a string (str) starts with the passed prefix (*pre)
|
||||
int
|
||||
dict_starts_with(const char *pre, const char *str)
|
||||
{
|
||||
size_t lenpre = strlen(pre), lenstr = strlen(str);
|
||||
return lenstr < lenpre ? 0 : strncmp(pre, str, lenpre) == 0;
|
||||
}
|
||||
|
||||
// Helper function to clear unigram
|
||||
void
|
||||
free_unigram_t(unigram_t *unigram)
|
||||
{
|
||||
ckd_free(unigram->word);
|
||||
ckd_free(unigram->phone);
|
||||
}
|
||||
|
||||
// This function splits an unigram received (in format e|w}UW) and return a structure
|
||||
// containing two fields: the grapheme (before }) in unigram.word and the phoneme (after }) unigram.phone
|
||||
unigram_t
|
||||
dict_split_unigram(const char * word)
|
||||
{
|
||||
size_t total_graphemes = 0;
|
||||
size_t total_phone = 0;
|
||||
int token_pos = 0;
|
||||
int w ;
|
||||
char *phone;
|
||||
char *letter;
|
||||
size_t lenword = 0;
|
||||
char unigram_letter;
|
||||
int add;
|
||||
|
||||
lenword = strlen(word);
|
||||
for (w = 0; w < lenword; w++) {
|
||||
unigram_letter = word[w];
|
||||
if (unigram_letter == '}') {
|
||||
token_pos = w;
|
||||
continue;
|
||||
}
|
||||
if (!token_pos)
|
||||
total_graphemes++;
|
||||
else
|
||||
total_phone++;
|
||||
}
|
||||
|
||||
letter = ckd_calloc(1, total_graphemes+1);
|
||||
add = 0;
|
||||
for (w = 0; w < total_graphemes; w++) {
|
||||
if (word[w] == '|')
|
||||
{
|
||||
add++;
|
||||
continue;
|
||||
}
|
||||
letter[w - add] = word[w];
|
||||
}
|
||||
|
||||
phone = ckd_calloc(1, total_phone+1);
|
||||
for (w = 0; w < total_phone; w++) {
|
||||
if (word[w + 1 + total_graphemes] == '|') {
|
||||
phone[w] = ' ';
|
||||
} else {
|
||||
phone[w] = word[w + 1 + total_graphemes];
|
||||
}
|
||||
}
|
||||
|
||||
unigram_t unigram = { letter , phone};
|
||||
|
||||
return unigram;
|
||||
};
|
||||
|
||||
// This function calculates the most likely unigram to appear in the current position at the word
|
||||
// based on the three latest chosen/winners unigrams (history) and return a structure containing
|
||||
// the word id (wid), and lengths of the phoneme and the word
|
||||
struct winner_t
|
||||
dict_get_winner_wid(ngram_model_t *model, const char * word_grapheme, glist_t history_list, int word_offset)
|
||||
{
|
||||
long current_prob = LONG_MIN;
|
||||
struct winner_t winner;
|
||||
int32 i = 0, j = 0;
|
||||
int nused;
|
||||
int32 ngram_order = ngram_model_get_size(model);
|
||||
int32 *history = ckd_calloc((size_t)ngram_order, sizeof(int32));
|
||||
gnode_t *gn;
|
||||
const char *vocab;
|
||||
const char *sub;
|
||||
int32 prob;
|
||||
unigram_t unigram;
|
||||
const int32 *total_unigrams = ngram_model_get_counts(model);
|
||||
|
||||
for (gn = history_list; gn; gn = gnode_next(gn)) {
|
||||
// we need to build history from last to first because glist returns itens from last to first
|
||||
history[ngram_order - j - 1] = gnode_int32(gn);
|
||||
j++;
|
||||
if (j >= ngram_order)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < *total_unigrams; i++) {
|
||||
vocab = ngram_word(model, i);
|
||||
unigram = dict_split_unigram(vocab);
|
||||
sub = word_grapheme + word_offset;
|
||||
if (dict_starts_with(unigram.word, sub)) {
|
||||
prob = ngram_ng_prob(model, i, history, j, &nused);
|
||||
if (current_prob < prob) {
|
||||
current_prob = prob;
|
||||
winner.winner_wid = i;
|
||||
winner.length_match = strlen(unigram.word);
|
||||
winner.len_phoneme = strlen(unigram.phone);
|
||||
}
|
||||
}
|
||||
|
||||
free_unigram_t(&unigram);
|
||||
}
|
||||
|
||||
if (history)
|
||||
ckd_free(history);
|
||||
|
||||
return winner;
|
||||
}
|
||||
|
||||
// This function manages the winner unigrams and builds the history of winners to properly generate the final phoneme. In the first part,
|
||||
// it gets the most likely unigrams which graphemes compose the word and build a history of wids that is used in this search. In second part, the we
|
||||
// use the history of wids to get each correspondent unigram, and on third part, we build the final phoneme word from this history.
|
||||
char *
|
||||
dict_g2p(char const *word_grapheme, ngram_model_t *ngram_g2p_model)
|
||||
{
|
||||
char *final_phone = NULL;
|
||||
int totalh = 0;
|
||||
size_t increment = 1;
|
||||
int word_offset = 0;
|
||||
int j;
|
||||
size_t grapheme_len = 0, final_phoneme_len = 0;
|
||||
glist_t history_list = NULL;
|
||||
gnode_t *gn;
|
||||
int first = 0;
|
||||
struct winner_t winner;
|
||||
const char *word;
|
||||
unigram_t unigram;
|
||||
|
||||
int32 wid_sentence = ngram_wid(ngram_g2p_model,"<s>"); // start with sentence
|
||||
history_list = glist_add_int32(history_list, wid_sentence);
|
||||
grapheme_len = strlen(word_grapheme);
|
||||
for (j = 0 ; j < grapheme_len ; j += increment) {
|
||||
winner = dict_get_winner_wid(ngram_g2p_model, word_grapheme, history_list, word_offset);
|
||||
increment = winner.length_match;
|
||||
if (increment == 0) {
|
||||
E_ERROR("Error trying to find matching phoneme (%s) Exiting.. \n" , word_grapheme);
|
||||
ckd_free(history_list);
|
||||
return NULL;
|
||||
}
|
||||
history_list = glist_add_int32(history_list, winner.winner_wid);
|
||||
totalh = j + 1;
|
||||
word_offset += winner.length_match;
|
||||
final_phoneme_len += winner.len_phoneme;
|
||||
}
|
||||
|
||||
history_list = glist_reverse(history_list);
|
||||
final_phone = ckd_calloc(1, (final_phoneme_len * 2)+1);
|
||||
for (gn = history_list; gn; gn = gnode_next(gn)) {
|
||||
if (!first) {
|
||||
first = 1;
|
||||
continue;
|
||||
}
|
||||
word = ngram_word(ngram_g2p_model, gnode_int32(gn));
|
||||
|
||||
if (!word)
|
||||
continue;
|
||||
|
||||
unigram = dict_split_unigram(word);
|
||||
|
||||
if (strcmp(unigram.phone, "_") == 0) {
|
||||
free_unigram_t(&unigram);
|
||||
continue;
|
||||
}
|
||||
strcat(final_phone, unigram.phone);
|
||||
strcat(final_phone, " ");
|
||||
|
||||
free_unigram_t(&unigram);
|
||||
}
|
||||
|
||||
if (history_list)
|
||||
glist_free(history_list);
|
||||
|
||||
return final_phone;
|
||||
}
|
||||
|
||||
// This function just receives the dict lacking word from fsg_search, call the main function dict_g2p, and then adds the word to the memory dict.
|
||||
// The second part of this function is the same as pocketsphinx.c: https://github.com/cmusphinx/pocketsphinx/blob/ba6bd21b3601339646d2db6d2297d02a8a6b7029/src/libpocketsphinx/pocketsphinx.c#L816
|
||||
int
|
||||
dict_add_g2p_word(dict_t *dict, char const *word)
|
||||
{
|
||||
int32 wid = 0;
|
||||
s3cipid_t *pron;
|
||||
char **phonestr, *tmp;
|
||||
int np, i;
|
||||
char *phones;
|
||||
|
||||
phones = dict_g2p(word, dict->ngram_g2p_model);
|
||||
if (phones == NULL)
|
||||
return 0;
|
||||
if (0 == strlen(phones)) {
|
||||
ckd_free(phones);
|
||||
return 0;
|
||||
}
|
||||
|
||||
E_INFO("Adding phone '%s' for word '%s' \n", phones, word);
|
||||
tmp = ckd_salloc(phones);
|
||||
np = str2words(tmp, NULL, 0);
|
||||
phonestr = ckd_calloc(np, sizeof(*phonestr));
|
||||
str2words(tmp, phonestr, np);
|
||||
pron = ckd_calloc(np, sizeof(*pron));
|
||||
for (i = 0; i < np; ++i) {
|
||||
pron[i] = bin_mdef_ciphone_id(dict->mdef, phonestr[i]);
|
||||
if (pron[i] == -1) {
|
||||
E_ERROR("Unknown phone %s in phone string %s\n",
|
||||
phonestr[i], tmp);
|
||||
ckd_free(phonestr);
|
||||
ckd_free(tmp);
|
||||
ckd_free(pron);
|
||||
ckd_free(phones);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ckd_free(phonestr);
|
||||
ckd_free(tmp);
|
||||
ckd_free(phones);
|
||||
if ((wid = dict_add_word(dict, word, pron, np)) == -1) {
|
||||
ckd_free(pron);
|
||||
return -1;
|
||||
}
|
||||
ckd_free(pron);
|
||||
|
||||
return wid;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
|
||||
/* SphinxBase headers. */
|
||||
#include <sphinxbase/hash_table.h>
|
||||
#include <sphinxbase/ngram_model.h>
|
||||
|
||||
/* Local headers. */
|
||||
#include "s3types.h"
|
||||
@@ -86,8 +87,21 @@ typedef struct {
|
||||
s3wid_t finishwid; /**< FOR INTERNAL-USE ONLY */
|
||||
s3wid_t silwid; /**< FOR INTERNAL-USE ONLY */
|
||||
int nocase;
|
||||
ngram_model_t *ngram_g2p_model;
|
||||
} dict_t;
|
||||
|
||||
struct winner_t
|
||||
{
|
||||
size_t length_match;
|
||||
int winner_wid;
|
||||
size_t len_phoneme;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *word;
|
||||
char *phone;
|
||||
} unigram_t;
|
||||
|
||||
/**
|
||||
* Initialize a new dictionary.
|
||||
@@ -101,7 +115,8 @@ typedef struct {
|
||||
* Return ptr to dict_t if successful, NULL otherwise.
|
||||
*/
|
||||
dict_t *dict_init(cmd_ln_t *config, /**< Configuration (-dict, -fdict, -dictcase) or NULL */
|
||||
bin_mdef_t *mdef /**< For looking up CI phone IDs (or NULL) */
|
||||
bin_mdef_t *mdef, /**< For looking up CI phone IDs (or NULL) */
|
||||
logmath_t *logmath // To load ngram_model for g2p load. logmath must be retained with logmath_retain() if it is to be used elsewhere.
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -203,6 +218,9 @@ int dict_free(dict_t *d);
|
||||
void dict_report(dict_t *d /**< A dictionary structure */
|
||||
);
|
||||
|
||||
// g2p functions
|
||||
int dict_add_g2p_word(dict_t * dict, char const *word);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
#include "fsg_search_internal.h"
|
||||
#include "fsg_history.h"
|
||||
#include "fsg_lextree.h"
|
||||
#include "dict.h"
|
||||
|
||||
/* Turn this on for detailed debugging dump */
|
||||
#define __FSG_DBG__ 0
|
||||
@@ -139,9 +140,21 @@ fsg_search_check_dict(fsg_search_t *fsgs, fsg_model_t *fsg)
|
||||
word = fsg_model_word_str(fsg, i);
|
||||
wid = dict_wordid(dict, word);
|
||||
if (wid == BAD_S3WID) {
|
||||
E_ERROR("The word '%s' is missing in the dictionary\n", word);
|
||||
return FALSE;
|
||||
}
|
||||
E_WARN("The word '%s' is missing in the dictionary. Trying to create new phoneme \n", word);
|
||||
if (!dict->ngram_g2p_model) {
|
||||
E_ERROR("NO dict->ngram_g2p_model. Aborting..");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int new_wid = dict_add_g2p_word(dict, word);
|
||||
if (new_wid > 0){
|
||||
/* Now we also have to add it to dict2pid. */
|
||||
dict2pid_add_word(ps_search_dict2pid(fsgs), new_wid);
|
||||
} else {
|
||||
E_ERROR("Exiting... \n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
@@ -69,7 +69,8 @@ typedef struct fsg_search_s {
|
||||
ps_search_t base;
|
||||
|
||||
hmm_context_t *hmmctx; /**< HMM context. */
|
||||
|
||||
char const *arpafile;
|
||||
cmd_ln_t *config;
|
||||
fsg_model_t *fsg; /**< FSG model */
|
||||
struct fsg_lextree_s *lextree;/**< Lextree structure for the currently
|
||||
active FSG */
|
||||
|
||||
@@ -278,7 +278,7 @@ ps_reinit(ps_decoder_t *ps, cmd_ln_t *config)
|
||||
|
||||
/* Dictionary and triphone mappings (depends on acmod). */
|
||||
/* FIXME: pass config, change arguments, implement LTS, etc. */
|
||||
if ((ps->dict = dict_init(ps->config, ps->acmod->mdef)) == NULL)
|
||||
if ((ps->dict = dict_init(ps->config, ps->acmod->mdef, ps->acmod->lmath)) == NULL)
|
||||
return -1;
|
||||
if ((ps->d2p = dict2pid_build(ps->acmod->mdef, ps->dict)) == NULL)
|
||||
return -1;
|
||||
@@ -720,7 +720,7 @@ ps_load_dict(ps_decoder_t *ps, char const *dictfile,
|
||||
cmd_ln_str_r(ps->config, "-fdict"));
|
||||
|
||||
/* Try to load it. */
|
||||
if ((dict = dict_init(newconfig, ps->acmod->mdef)) == NULL) {
|
||||
if ((dict = dict_init(newconfig, ps->acmod->mdef, ps->acmod->lmath)) == NULL) {
|
||||
cmd_ln_free_r(newconfig);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -404,10 +404,11 @@ ps_lattice_read(ps_decoder_t *ps,
|
||||
dag->search = ps->search;
|
||||
dag->dict = dict_retain(ps->dict);
|
||||
dag->lmath = logmath_retain(ps->lmath);
|
||||
dag->dict = dict_init(NULL, NULL, dag->lmath);
|
||||
dag->frate = cmd_ln_int32_r(dag->search->config, "-frate");
|
||||
}
|
||||
else {
|
||||
dag->dict = dict_init(NULL, NULL);
|
||||
dag->dict = dict_init(NULL, NULL, dag->lmath);
|
||||
dag->lmath = logmath_init(1.0001, 0, FALSE);
|
||||
dag->frate = 100;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
diff --git b/src/libsphinxbase/util/sbthread.c a/src/libsphinxbase/util/sbthread.c
|
||||
index 14207cb..2f82c76 100644
|
||||
--- b/src/libsphinxbase/util/sbthread.c
|
||||
+++ a/src/libsphinxbase/util/sbthread.c
|
||||
@@ -51,7 +51,9 @@
|
||||
* Platform-specific parts: threads, mutexes, and signals.
|
||||
*/
|
||||
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(__SYMBIAN32__)
|
||||
+#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0400
|
||||
+#endif /* not _WIN32_WINNT */
|
||||
#include <windows.h>
|
||||
|
||||
struct sbthread_s {
|
||||
@@ -51,7 +51,9 @@
|
||||
* Platform-specific parts: threads, mutexes, and signals.
|
||||
*/
|
||||
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(__SYMBIAN32__)
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0400
|
||||
#endif /* not _WIN32_WINNT */
|
||||
#include <windows.h>
|
||||
|
||||
struct sbthread_s {
|
||||
|
||||
@@ -36,3 +36,6 @@ cp $1/src/libsphinxbase/feat/*.c src/libsphinxbase/feat/
|
||||
cp $1/src/libsphinxbase/lm/*.c src/libsphinxbase/lm/
|
||||
cp $1/src/libsphinxbase/lm/*.h src/libsphinxbase/lm/
|
||||
cp $1/src/libsphinxbase/util/*.c src/libsphinxbase/util/
|
||||
|
||||
# Apply any patches against upstream here.
|
||||
patch -l -p1 < sbthread.patch
|
||||
|
||||
@@ -465,4 +465,4 @@ static unsigned char *DIGITS[] = {
|
||||
*ptr = sLumaMax;
|
||||
}
|
||||
|
||||
} // Namespace mozilla.
|
||||
} // namespace mozilla.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "WebRtcLog.h"
|
||||
|
||||
#include "prlog.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "prenv.h"
|
||||
#include "webrtc/system_wrappers/interface/trace.h"
|
||||
|
||||
@@ -19,12 +19,20 @@
|
||||
#include "nsStringAPI.h"
|
||||
#endif
|
||||
|
||||
#include "nsIFile.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
||||
using mozilla::LogLevel;
|
||||
|
||||
static int gWebRtcTraceLoggingOn = 0;
|
||||
|
||||
#ifndef ANDROID
|
||||
static const char *default_log = "WebRTC.log";
|
||||
|
||||
#if defined(ANDROID)
|
||||
static const char *default_tmp_dir = "/dev/null";
|
||||
static const char *default_log_name = "nspr";
|
||||
#else // Assume a POSIX environment
|
||||
NS_NAMED_LITERAL_CSTRING(default_log_name, "WebRTC.log");
|
||||
#endif
|
||||
|
||||
static PRLogModuleInfo* GetWebRtcTraceLog()
|
||||
@@ -106,39 +114,34 @@ void ConfigWebRtcLog(uint32_t trace_mask, nsCString &aLogFile, nsCString &aAECLo
|
||||
return;
|
||||
}
|
||||
|
||||
nsCString logFile;
|
||||
nsCString aecLogDir;
|
||||
#if defined(XP_WIN)
|
||||
// Use the Windows TEMP environment variable as part of the default location.
|
||||
const char *temp_dir = PR_GetEnv("TEMP");
|
||||
if (!temp_dir) {
|
||||
logFile.Assign(default_log);
|
||||
} else {
|
||||
logFile.Assign(temp_dir);
|
||||
logFile.Append('/');
|
||||
aecLogDir = logFile;
|
||||
logFile.Append(default_log);
|
||||
}
|
||||
#elif defined(ANDROID)
|
||||
#if defined(ANDROID)
|
||||
// Special case: use callback to pipe to NSPR logging.
|
||||
logFile.Assign("nspr");
|
||||
// for AEC, force the user to specify a directory
|
||||
aecLogDir.Assign("/dev/null");
|
||||
#else
|
||||
// UNIX-like place for the others
|
||||
logFile.Assign("/tmp/");
|
||||
aecLogDir = logFile;
|
||||
logFile.Append(default_log);
|
||||
#endif
|
||||
if (aLogFile.IsEmpty()) {
|
||||
aLogFile = logFile;
|
||||
}
|
||||
aLogFile.Assign(default_log_name);
|
||||
// For AEC, do not use a default value: force the user to specify a directory.
|
||||
if (aAECLogDir.IsEmpty()) {
|
||||
aAECLogDir = aecLogDir;
|
||||
aAECLogDir.Assign(default_tmp_dir);
|
||||
}
|
||||
#else
|
||||
if (aLogFile.IsEmpty() || aAECLogDir.IsEmpty()) {
|
||||
nsCOMPtr<nsIFile> tempDir;
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempDir));
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (aAECLogDir.IsEmpty()) {
|
||||
tempDir->GetNativePath(aAECLogDir);
|
||||
}
|
||||
|
||||
if (aLogFile.IsEmpty()) {
|
||||
tempDir->AppendNative(default_log_name);
|
||||
tempDir->GetNativePath(aLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
webrtc::Trace::set_level_filter(trace_mask);
|
||||
webrtc::Trace::set_aec_debug_filename(aAECLogDir.get());
|
||||
|
||||
if (trace_mask != 0) {
|
||||
if (aLogFile.EqualsLiteral("nspr")) {
|
||||
webrtc::Trace::SetTraceCallback(&gWebRtcCallback);
|
||||
|
||||
@@ -598,7 +598,17 @@ NeckoParent::AllocPRemoteOpenFileParent(const SerializedLoadContext& aSerialized
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> appUri = DeserializeURI(aAppURI);
|
||||
|
||||
if (!haveValidBrowser) {
|
||||
// Extension loads come from chrome and have no valid browser, so we check
|
||||
// for these early on.
|
||||
bool fromExtension = false;
|
||||
if (NS_SUCCEEDED(appsService->IsExtensionResource(appUri, &fromExtension)) &&
|
||||
fromExtension) {
|
||||
RemoteOpenFileParent* parent = new RemoteOpenFileParent(fileURL);
|
||||
return parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -609,7 +619,6 @@ NeckoParent::AllocPRemoteOpenFileParent(const SerializedLoadContext& aSerialized
|
||||
// Check if we load the whitelisted app uri for the neterror page.
|
||||
bool netErrorWhiteList = false;
|
||||
|
||||
nsCOMPtr<nsIURI> appUri = DeserializeURI(aAppURI);
|
||||
if (appUri) {
|
||||
nsAdoptingString netErrorURI;
|
||||
netErrorURI = Preferences::GetString("b2g.neterror.url");
|
||||
|
||||
@@ -210,10 +210,6 @@ RemoteOpenFileChild::AsyncRemoteFileOpen(int32_t aFlags,
|
||||
|
||||
mTabChild = static_cast<TabChild*>(aTabChild);
|
||||
|
||||
if (MissingRequiredTabChild(mTabChild, "remoteopenfile")) {
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
|
||||
// Windows/OSX desktop builds skip remoting, and just open file in child
|
||||
// process when asked for NSPR handle
|
||||
|
||||
@@ -247,8 +247,6 @@ user_pref("browser.translation.bing.translateArrayURL", "http://%(server)s/brows
|
||||
// Make sure we don't try to load snippets from the network.
|
||||
user_pref("browser.aboutHomeSnippets.updateUrl", "nonexistent://test");
|
||||
|
||||
// Enable debug logging in the mozApps implementation.
|
||||
user_pref("dom.mozApps.debug", true);
|
||||
// Enable apps customizations
|
||||
user_pref("dom.apps.customization.enabled", true);
|
||||
|
||||
|
||||
@@ -71,4 +71,4 @@ function run_test()
|
||||
stmt.finalize();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Termination functions common to all tests
|
||||
|
||||
add_task(function test_common_terminate()
|
||||
add_task(function* test_common_terminate()
|
||||
{
|
||||
// Stop the HTTP server. We must do this inside a task in "tail.js" until the
|
||||
// xpcshell testing framework supports asynchronous termination functions.
|
||||
|
||||
@@ -159,7 +159,9 @@ add_test(function test_nullCallback() {
|
||||
fileSize: 12,
|
||||
}, null);
|
||||
do_throw("Callback cannot be null");
|
||||
} catch (ex if ex.result == Cr.NS_ERROR_INVALID_POINTER) {
|
||||
} catch (ex) {
|
||||
if (ex.result != Cr.NS_ERROR_INVALID_POINTER)
|
||||
throw ex;
|
||||
// We don't even increment the count here, because there's no callback.
|
||||
check_telemetry(counts.total, counts.shouldBlock, counts.listCounts);
|
||||
run_next_test();
|
||||
|
||||
@@ -210,13 +210,13 @@ function promiseQueryReputation(query, expectedShouldBlock) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function()
|
||||
add_task(function* ()
|
||||
{
|
||||
// Wait for Safebrowsing local list updates to complete.
|
||||
yield waitForUpdates();
|
||||
});
|
||||
|
||||
add_task(function test_blocked_binary()
|
||||
add_task(function* test_blocked_binary()
|
||||
{
|
||||
// We should reach the remote server for a verdict.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -229,7 +229,7 @@ add_task(function test_blocked_binary()
|
||||
fileSize: 12}, true);
|
||||
});
|
||||
|
||||
add_task(function test_non_binary()
|
||||
add_task(function* test_non_binary()
|
||||
{
|
||||
// We should not reach the remote server for a verdict for non-binary files.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -241,7 +241,7 @@ add_task(function test_non_binary()
|
||||
fileSize: 12}, false);
|
||||
});
|
||||
|
||||
add_task(function test_good_binary()
|
||||
add_task(function* test_good_binary()
|
||||
{
|
||||
// We should reach the remote server for a verdict.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -254,7 +254,7 @@ add_task(function test_good_binary()
|
||||
fileSize: 12}, false);
|
||||
});
|
||||
|
||||
add_task(function test_disabled()
|
||||
add_task(function* test_disabled()
|
||||
{
|
||||
// Explicitly disable remote checks
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -276,7 +276,7 @@ add_task(function test_disabled()
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function test_disabled_through_lists()
|
||||
add_task(function* test_disabled_through_lists()
|
||||
{
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
false);
|
||||
@@ -297,7 +297,7 @@ add_task(function test_disabled_through_lists()
|
||||
);
|
||||
yield deferred.promise;
|
||||
});
|
||||
add_task(function test_teardown()
|
||||
add_task(function* test_teardown()
|
||||
{
|
||||
gStillRunning = false;
|
||||
});
|
||||
|
||||
@@ -165,7 +165,7 @@ function run_test()
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function test_setup()
|
||||
add_task(function* test_setup()
|
||||
{
|
||||
// Wait 10 minutes, that is half of the external xpcshell timeout.
|
||||
do_timeout(10 * 60 * 1000, function() {
|
||||
@@ -310,13 +310,13 @@ function promiseQueryReputation(query, expectedShouldBlock) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function()
|
||||
add_task(function* ()
|
||||
{
|
||||
// Wait for Safebrowsing local list updates to complete.
|
||||
yield waitForUpdates();
|
||||
});
|
||||
|
||||
add_task(function test_signature_whitelists()
|
||||
add_task(function* test_signature_whitelists()
|
||||
{
|
||||
// We should never get to the remote server.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -347,7 +347,7 @@ add_task(function test_signature_whitelists()
|
||||
fileSize: 12}, false);
|
||||
});
|
||||
|
||||
add_task(function test_blocked_binary()
|
||||
add_task(function* test_blocked_binary()
|
||||
{
|
||||
// We should reach the remote server for a verdict.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -360,7 +360,7 @@ add_task(function test_blocked_binary()
|
||||
fileSize: 12}, true);
|
||||
});
|
||||
|
||||
add_task(function test_non_binary()
|
||||
add_task(function* test_non_binary()
|
||||
{
|
||||
// We should not reach the remote server for a verdict for non-binary files.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -372,7 +372,7 @@ add_task(function test_non_binary()
|
||||
fileSize: 12}, false);
|
||||
});
|
||||
|
||||
add_task(function test_good_binary()
|
||||
add_task(function* test_good_binary()
|
||||
{
|
||||
// We should reach the remote server for a verdict.
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -385,7 +385,7 @@ add_task(function test_good_binary()
|
||||
fileSize: 12}, false);
|
||||
});
|
||||
|
||||
add_task(function test_disabled()
|
||||
add_task(function* test_disabled()
|
||||
{
|
||||
// Explicitly disable remote checks
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
@@ -407,7 +407,7 @@ add_task(function test_disabled()
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function test_disabled_through_lists()
|
||||
add_task(function* test_disabled_through_lists()
|
||||
{
|
||||
Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.enabled",
|
||||
false);
|
||||
@@ -428,7 +428,7 @@ add_task(function test_disabled_through_lists()
|
||||
);
|
||||
yield deferred.promise;
|
||||
});
|
||||
add_task(function test_teardown()
|
||||
add_task(function* test_teardown()
|
||||
{
|
||||
gStillRunning = false;
|
||||
});
|
||||
|
||||
@@ -99,7 +99,9 @@ function runNextTest()
|
||||
} catch (ex) {
|
||||
try {
|
||||
do_report_unexpected_exception(ex, "while removing " + file.path);
|
||||
} catch (ex if ex == Components.results.NS_ERROR_ABORT) {
|
||||
} catch (ex) {
|
||||
if (ex != Components.results.NS_ERROR_ABORT)
|
||||
throw ex;
|
||||
/* swallow */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ function run_test()
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function test_execute()
|
||||
add_task(function* test_execute()
|
||||
{
|
||||
// Like the code, we check to see if nav-history-service exists
|
||||
// (i.e MOZ_PLACES is enabled), so that we don't run this test if it doesn't.
|
||||
|
||||
@@ -433,7 +433,7 @@ this.Download.prototype = {
|
||||
|
||||
// Now that we stored the promise in the download object, we can start the
|
||||
// task that will actually execute the download.
|
||||
deferAttempt.resolve(Task.spawn(function task_D_start() {
|
||||
deferAttempt.resolve(Task.spawn(function* task_D_start() {
|
||||
// Wait upon any pending operation before restarting.
|
||||
if (this._promiseCanceled) {
|
||||
yield this._promiseCanceled;
|
||||
@@ -845,7 +845,7 @@ this.Download.prototype = {
|
||||
this._promiseRemovePartialData = promiseRemovePartialData;
|
||||
|
||||
deferRemovePartialData.resolve(
|
||||
Task.spawn(function task_D_removePartialData() {
|
||||
Task.spawn(function* task_D_removePartialData() {
|
||||
try {
|
||||
// Wait upon any pending cancellation request.
|
||||
if (this._promiseCanceled) {
|
||||
@@ -908,7 +908,7 @@ this.Download.prototype = {
|
||||
*/
|
||||
refresh: function ()
|
||||
{
|
||||
return Task.spawn(function () {
|
||||
return Task.spawn(function* () {
|
||||
if (!this.stopped || this._finalized) {
|
||||
return;
|
||||
}
|
||||
@@ -942,7 +942,10 @@ this.Download.prototype = {
|
||||
this.progress = Math.floor(this.currentBytes /
|
||||
this.totalBytes * 100);
|
||||
}
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error) || !ex.becauseNoSuchFile) {
|
||||
throw ex;
|
||||
}
|
||||
// Ignore the result if the state has changed meanwhile.
|
||||
if (!this.stopped || this._finalized) {
|
||||
return;
|
||||
@@ -1709,8 +1712,11 @@ this.DownloadSaver.prototype = {
|
||||
gDownloadHistory.addDownload(sourceUri, referrerUri, startPRTime,
|
||||
targetUri);
|
||||
}
|
||||
catch(ex if ex instanceof Components.Exception &&
|
||||
ex.result == Cr.NS_ERROR_NOT_AVAILABLE) {
|
||||
catch(ex) {
|
||||
if (!(ex instanceof Components.Exception) ||
|
||||
ex.result != Cr.NS_ERROR_NOT_AVAILABLE) {
|
||||
throw ex;
|
||||
}
|
||||
//
|
||||
// Under normal operation the download history service may not
|
||||
// be available. We don't want all downloads that are public to fail
|
||||
@@ -1840,7 +1846,7 @@ this.DownloadCopySaver.prototype = {
|
||||
let partFilePath = download.target.partFilePath;
|
||||
let keepPartialData = download.tryToKeepPartialData;
|
||||
|
||||
return Task.spawn(function task_DCS_execute() {
|
||||
return Task.spawn(function* task_DCS_execute() {
|
||||
// Add the download to history the first time it is started in this
|
||||
// session. If the download is restarted in a different session, a new
|
||||
// history visit will be added. We do this just to avoid the complexity
|
||||
@@ -1860,7 +1866,10 @@ this.DownloadCopySaver.prototype = {
|
||||
// If the file already exists, don't delete its contents yet.
|
||||
let file = yield OS.File.open(targetPath, { write: true });
|
||||
yield file.close();
|
||||
} catch (ex if ex instanceof OS.File.Error) {
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error)) {
|
||||
throw ex;
|
||||
}
|
||||
// Throw a DownloadError indicating that the operation failed because of
|
||||
// the target file. We cannot translate this into a specific result
|
||||
// code, but we preserve the original message using the toString method.
|
||||
@@ -1930,8 +1939,11 @@ this.DownloadCopySaver.prototype = {
|
||||
channel.resumeAt(stat.size, this.entityID);
|
||||
resumeAttempted = true;
|
||||
resumeFromBytes = stat.size;
|
||||
} catch (ex if ex instanceof OS.File.Error &&
|
||||
ex.becauseNoSuchFile) { }
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error) || !ex.becauseNoSuchFile) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
channel.notificationCallbacks = {
|
||||
@@ -2000,8 +2012,11 @@ this.DownloadCopySaver.prototype = {
|
||||
try {
|
||||
// If reading the ID succeeds, the source is resumable.
|
||||
this.entityID = aRequest.entityID;
|
||||
} catch (ex if ex instanceof Components.Exception &&
|
||||
ex.result == Cr.NS_ERROR_NOT_RESUMABLE) {
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof Components.Exception) ||
|
||||
ex.result != Cr.NS_ERROR_NOT_RESUMABLE) {
|
||||
throw ex;
|
||||
}
|
||||
keepPartialData = false;
|
||||
}
|
||||
} else {
|
||||
@@ -2152,11 +2167,15 @@ this.DownloadCopySaver.prototype = {
|
||||
*/
|
||||
removePartialData: function ()
|
||||
{
|
||||
return Task.spawn(function task_DCS_removePartialData() {
|
||||
return Task.spawn(function* task_DCS_removePartialData() {
|
||||
if (this.download.target.partFilePath) {
|
||||
try {
|
||||
yield OS.File.remove(this.download.target.partFilePath);
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) { }
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error) || !ex.becauseNoSuchFile) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
@@ -2334,8 +2353,12 @@ this.DownloadLegacySaver.prototype = {
|
||||
try {
|
||||
// If reading the ID succeeds, the source is resumable.
|
||||
this.entityID = aRequest.entityID;
|
||||
} catch (ex if ex instanceof Components.Exception &&
|
||||
ex.result == Cr.NS_ERROR_NOT_RESUMABLE) { }
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof Components.Exception) ||
|
||||
ex.result != Cr.NS_ERROR_NOT_RESUMABLE) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For legacy downloads, we must update the referrer at this time.
|
||||
@@ -2411,7 +2434,7 @@ this.DownloadLegacySaver.prototype = {
|
||||
|
||||
this.setProgressBytesFn = aSetProgressBytesFn;
|
||||
|
||||
return Task.spawn(function task_DLS_execute() {
|
||||
return Task.spawn(function* task_DLS_execute() {
|
||||
try {
|
||||
// Wait for the component that executes the download to finish.
|
||||
yield this.deferExecuted.promise;
|
||||
@@ -2441,7 +2464,11 @@ this.DownloadLegacySaver.prototype = {
|
||||
let file = yield OS.File.open(this.download.target.path,
|
||||
{ create: true });
|
||||
yield file.close();
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseExists) { }
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof OS.File.Error) || !ex.becauseExists) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield this._checkReputationAndMove();
|
||||
@@ -2623,7 +2650,7 @@ this.DownloadPDFSaver.prototype = {
|
||||
*/
|
||||
execute: function (aSetProgressBytesFn, aSetPropertiesFn)
|
||||
{
|
||||
return Task.spawn(function task_DCS_execute() {
|
||||
return Task.spawn(function* task_DCS_execute() {
|
||||
if (!this.download.source.windowRef) {
|
||||
throw new DownloadError({
|
||||
message: "PDF saver must be passed an open window, and cannot be restarted.",
|
||||
|
||||
@@ -71,7 +71,7 @@ this.DownloadImport.prototype = {
|
||||
* the DownloadList)
|
||||
*/
|
||||
import: function () {
|
||||
return Task.spawn(function task_DI_import() {
|
||||
return Task.spawn(function* task_DI_import() {
|
||||
let connection = yield Sqlite.openConnection({ path: this.path });
|
||||
|
||||
try {
|
||||
|
||||
@@ -233,7 +233,7 @@ this.DownloadList.prototype = {
|
||||
* additional filter.
|
||||
*/
|
||||
removeFinished: function DL_removeFinished(aFilterFn) {
|
||||
Task.spawn(function() {
|
||||
Task.spawn(function* () {
|
||||
let list = yield this.getAll();
|
||||
for (let download of list) {
|
||||
// Remove downloads that have been canceled, even if the cancellation
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user