Files
palemoon27/dom/apps/AppsServiceChild.jsm
T
roytam1 ae2cd1d073 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1165982 - part 1 - provide fallible initialization of nsDOMIterator from an nsRange; r=ehsan (e8f817ee7)
- Bug 1165982 - part 2 - validate an nsRange::SelectNode call in nsHTMLTMLEditRules::Init; r=ehsan (182e07bf6)
- Bug 1180118 - Part 1: Add a method to match a single nsCSSSelector (without pseudo-elements) against an Element. r=bzbarsky (26f9fe8c3)
- Partial port of Bug 1151829 - Unbreak build on non-SPS platforms after bug 1093934 (a9414f6b3)
- Bug 1180118 - Part 2: Add eRestyle_SomeDescendants restyle hint and pass associated restyle hint data into restyle methods. r=bzbarsky (7a6fc4bd2)
- Bug 1180118 - Part 3: Convert eRestyle_SomeDescendants into eRestyle_Self for elements that match selectors. r=bzbarsky (d4581f146)
- Bug 1180118 - Part 4: Store pointer to the rightmost selector for class, ID and attribute selectors in the rule cascade. r=bzbarsky (aca8878f8)
- Bug 1164366 - Need to fix an issue on unified build in dom/base. r=ehsan (8fcfb182f)
- Bug 1121760 (part 2) - Remove PL_DHashTableAdd(). r=poiru. (e9d140a4b)
- Bug 1121760 (part 3) - Remove PL_DHashTableRemove(). r=poiru. (588aeeaae)
- Bug 1181445 (part 1) - Implement iterators for nsTHashtable and nsBaseHashtable. r=froydnj. (55a19598d)
- Bug 1154053 - Limit concurrency of e10s memory reporting. r=erahm (88530ebb2)
- Bug 1181445 (part 2) - Use nsTHashTable::Iterator in nsMemoryReporterManager. r=froydnj. (70df14b26)
- Bug 1150916 - Be sure we aren't iterating over a hashtable while it is modified in nsObserverService::UnmarkGrayStrongObservers(). r=froydnj (572b31ed2)
- Bug 1153373 - Move AppendStrongObservers() function definition behind #ifndef MOZILLA_XPCOMRT_API. r=froydnj (cebafb6b7)
- Bug 1181445 (part 3) - Use nsTHashTable::Iterator in nsObserverService. r=froydnj. (1018c3526)
- Bug 1181445 (part 4) - Use nsTHashTable::Iterator in nsCategoryManager. r=froydnj. (a04fcf96b)
- Bug 1181445 (part 5) - Use nsTHashTable::Iterator in nsTHashtable.h. r=mccr8. (51bf669a0)
- Bug 1181445 (part 6) - Use nsBaseHashTable::Iterator in xpcom/components/. r=froydnj. (e2e75de6c)
- Bug 1167378 - Mark ArrayAndPrefix as MOZ_STACK_CLASS and array member as MOZ_NON_OWNING_REF. r=froydnj (e2d727c94)
- Bug 1181445 (part 7) - Use nsBaseHashTable::Iterator in xpcom/reflect/. r=froydnj. (f6b9290a0)
- Bug 1181445 (part 8) - Use nsBaseHashTable::Iterator in SystemMemoryReporter. r=froydnj. (69b81c209)
- Bug 1181445 (part 9) - Use nsBaseHashTable::Iterator in nsConsoleService. r=froydnj. (ce4183330)
- Bug 1154403 - Reduce incremental finalize slice time to 5ms. r=smaug (edbfd02e0)
- Bug 1181445 (part 10) - Use nsBaseHashTable::Iterator in CycleCollectedJSRuntime. r=mccr8. (35d9be72e)
- Bug 1181445 (part 11) - Use nsBaseHashTable::Iterator in xpcom/glue/. r=froydnj. (a08b703fe)
- Backout change 39e167bbd14c, a80140872ea5, adae9be2294d and b71ccef9c674 (bug 970307). (9ce474689)
- Bug 1158320 - rename nsThread::mRunningEvent to mNestedEventLoopDepth; r=bsmedberg (06d59a7e2)
- Bug 1181445 (part 12) - Use nsBaseHashTable::Iterator in xpcom/threads/. r=froydnj. (48edb8226)
- Bug 1181445 (part 13) - Use nsBaseHashTable::Iterator in xpcom/ds/. r=froydnj. (6d16d7670)
- Bug 1173634 - Report pending async shutdowns in shutdown hangs. r=cpearce (c806e4b3f)
- Bug 1183433 - Implement centralized crash report annotations, so that all async-shutdown hangs use only one key, and keeping track of all states each plugin has been through. r=cpearce (1d0284c70)
- Bug 1184432 - Use nsClassHashtable::ConstIter in GeckoMediaPluginServiceParent::AsyncShutdownPluginStates. r=cpearce (7c92e3363)
- Bug 1182318 - Part 1: Remove Closure::mCb. r=smaug (f28311ccd)
- Bug 1182318 - Part 2: Remove Closure from CycleCollectedJSRuntime. r=smaug (454f3b85b)
- Bug 1185470: Remove 'Get' prefixes from hashtable iterator methods. r (8ce896a26)
2021-03-16 09:20:28 +08:00

422 lines
12 KiB
JavaScript

/* 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/. */
"use strict";
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
// This module exposes a subset of the functionalities of the parent DOM
// Registry to content processes, to be used from the AppsService component.
this.EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "WrappedManifestCache"];
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function debug(s) {
//dump("-*- AppsServiceChild.jsm: " + s + "\n");
}
const APPS_IPC_MSG_NAMES = [
"Webapps:AddApp",
"Webapps:RemoveApp",
"Webapps:UpdateApp",
"Webapps:CheckForUpdate:Return:KO",
"Webapps:FireEvent",
"Webapps:UpdateState"
];
// A simple cache for the wrapped manifests.
this.WrappedManifestCache = {
_cache: { },
// Gets an entry from the cache, and populates the cache if needed.
get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) {
if (!aManifest) {
return;
}
if (!(aManifestURL in this._cache)) {
this._cache[aManifestURL] = { };
}
let winObjs = this._cache[aManifestURL];
if (!(aInnerWindowID in winObjs)) {
winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow);
}
return winObjs[aInnerWindowID];
},
// Invalidates an entry in the cache.
evict: function mcache_evict(aManifestURL, aInnerWindowID) {
debug("Evicting manifest " + aManifestURL + " window ID " +
aInnerWindowID);
if (aManifestURL in this._cache) {
let winObjs = this._cache[aManifestURL];
if (aInnerWindowID in winObjs) {
delete winObjs[aInnerWindowID];
}
if (Object.keys(winObjs).length == 0) {
delete this._cache[aManifestURL];
}
}
},
observe: function(aSubject, aTopic, aData) {
// Clear the cache on memory pressure.
this._cache = { };
Cu.forceGC();
},
init: function() {
Services.obs.addObserver(this, "memory-pressure", false);
}
};
this.WrappedManifestCache.init();
// DOMApplicationRegistry keeps a cache containing a list of apps in the device.
// This information is updated with the data received from the main process and
// it is queried by the DOM objects to set their state.
// This module handle all the messages broadcasted from the parent process,
// including DOM events, which are dispatched to the corresponding DOM objects.
this.DOMApplicationRegistry = {
// DOMApps will hold a list of arrays of weak references to
// mozIDOMApplication objects indexed by manifest URL.
DOMApps: {},
ready: false,
webapps: null,
init: function init() {
this.cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
APPS_IPC_MSG_NAMES.forEach((function(aMsgName) {
this.cpmm.addMessageListener(aMsgName, this);
}).bind(this));
this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
messages: APPS_IPC_MSG_NAMES
});
// We need to prime the cache with the list of apps.
let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
this.webapps = list ? list.webapps : { };
// We need a fast mapping from localId -> app, so we add an index.
// We also add the manifest to the app object.
this.localIdIndex = { };
for (let id in this.webapps) {
let app = this.webapps[id];
this.localIdIndex[app.localId] = app;
app.manifest = list.manifests[id];
}
Services.obs.addObserver(this, "xpcom-shutdown", false);
},
observe: function(aSubject, aTopic, aData) {
// cpmm.addMessageListener causes the DOMApplicationRegistry object to
// live forever if we don't clean up properly.
this.webapps = null;
this.DOMApps = null;
APPS_IPC_MSG_NAMES.forEach((aMsgName) => {
this.cpmm.removeMessageListener(aMsgName, this);
});
},
receiveMessage: function receiveMessage(aMessage) {
debug("Received " + aMessage.name + " message.");
let msg = aMessage.data;
switch (aMessage.name) {
case "Webapps:AddApp":
this.webapps[msg.id] = msg.app;
this.localIdIndex[msg.app.localId] = msg.app;
if (msg.manifest) {
this.webapps[msg.id].manifest = msg.manifest;
}
break;
case "Webapps:RemoveApp":
delete this.DOMApps[this.webapps[msg.id].manifestURL];
delete this.localIdIndex[this.webapps[msg.id].localId];
delete this.webapps[msg.id];
break;
case "Webapps:UpdateApp":
let app = this.webapps[msg.oldId];
if (!app) {
return;
}
if (msg.app) {
for (let prop in msg.app) {
app[prop] = msg.app[prop];
}
}
this.webapps[msg.newId] = app;
this.localIdIndex[app.localId] = app;
delete this.webapps[msg.oldId];
let apps = this.DOMApps[msg.app.manifestURL];
if (!apps) {
return;
}
for (let i = 0; i < apps.length; i++) {
let domApp = apps[i].get();
if (!domApp || domApp._window === null) {
apps.splice(i, 1);
continue;
}
domApp._proxy = new Proxy(domApp, {
get: function(target, prop) {
if (!DOMApplicationRegistry.webapps[msg.newId]) {
return;
}
return DOMApplicationRegistry.webapps[msg.newId][prop];
},
set: function(target, prop, val) {
if (!DOMApplicationRegistry.webapps[msg.newId]) {
return;
}
DOMApplicationRegistry.webapps[msg.newId][prop] = val;
return;
},
});
}
break;
case "Webapps:FireEvent":
this._fireEvent(aMessage);
break;
case "Webapps:UpdateState":
this._updateState(msg);
break;
case "Webapps:CheckForUpdate:Return:KO":
let DOMApps = this.DOMApps[msg.manifestURL];
if (!DOMApps || !msg.requestID) {
return;
}
DOMApps.forEach((DOMApp) => {
let domApp = DOMApp.get();
if (domApp && msg.requestID) {
domApp._fireRequestResult(aMessage, true /* aIsError */);
}
});
break;
}
},
/**
* mozIDOMApplication management
*/
// Every time a DOM app is created, we save a weak reference to it that will
// be used to dispatch events and fire request results.
addDOMApp: function(aApp, aManifestURL, aId) {
let weakRef = Cu.getWeakReference(aApp);
if (!this.DOMApps[aManifestURL]) {
this.DOMApps[aManifestURL] = [];
}
let apps = this.DOMApps[aManifestURL];
// Get rid of dead weak references.
for (let i = 0; i < apps.length; i++) {
let app = apps[i].get();
if (!app || app._window === null) {
apps.splice(i, 1);
}
}
apps.push(weakRef);
// Each DOM app contains a proxy object used to build their state. We
// return the handler for this proxy object with traps to get and set
// app properties kept in the DOMApplicationRegistry app cache.
return {
get: function(target, prop) {
if (!DOMApplicationRegistry.webapps[aId]) {
return;
}
if (prop in DOMApplicationRegistry.webapps[aId]) {
return DOMApplicationRegistry.webapps[aId][prop];
}
return null;
},
set: function(target, prop, val) {
if (!DOMApplicationRegistry.webapps[aId]) {
return;
}
DOMApplicationRegistry.webapps[aId][prop] = val;
return;
},
};
},
_fireEvent: function(aMessage) {
let msg = aMessage.data;
debug("_fireEvent " + JSON.stringify(msg));
if (!this.DOMApps || !msg.manifestURL || !msg.eventType) {
return;
}
let DOMApps = this.DOMApps[msg.manifestURL];
if (!DOMApps) {
return;
}
// The parent might ask childs to trigger more than one event in one
// shot, so in order to avoid needless IPC we allow an array for the
// 'eventType' IPC message field.
if (!Array.isArray(msg.eventType)) {
msg.eventType = [msg.eventType];
}
DOMApps.forEach((DOMApp) => {
let domApp = DOMApp.get();
if (!domApp) {
return;
}
msg.eventType.forEach((aEventType) => {
if ('on' + aEventType in domApp) {
domApp._fireEvent(aEventType);
}
});
if (msg.requestID) {
aMessage.data.result = msg.manifestURL;
domApp._fireRequestResult(aMessage);
}
});
},
_updateState: function(aMessage) {
if (!this.DOMApps || !aMessage.id) {
return;
}
let app = this.webapps[aMessage.id];
if (!app) {
return;
}
if (aMessage.app) {
for (let prop in aMessage.app) {
app[prop] = aMessage.app[prop];
}
}
if ("error" in aMessage) {
app.downloadError = aMessage.error;
}
if (aMessage.manifest) {
app.manifest = aMessage.manifest;
// Evict the wrapped manifest cache for all the affected DOM objects.
let DOMApps = this.DOMApps[app.manifestURL];
if (!DOMApps) {
return;
}
DOMApps.forEach((DOMApp) => {
let domApp = DOMApp.get();
if (!domApp) {
return;
}
WrappedManifestCache.evict(app.manifestURL, domApp.innerWindowID);
});
}
},
getAll: function(aCallback) {
debug("getAll()\n");
if (!aCallback || typeof aCallback !== "function") {
return;
}
let res = [];
for (let id in this.webapps) {
res.push(this.webapps[id]);
}
aCallback(res);
},
getAdditionalLanguages: function(aManifestURL) {
for (let id in this.webapps) {
if (this.webapps[id].manifestURL == aManifestURL) {
return this.webapps[id].additionalLanguages || {};
}
}
return {};
},
/**
* nsIAppsService API
*/
getAppByManifestURL: function getAppByManifestURL(aManifestURL) {
debug("getAppByManifestURL " + aManifestURL);
return AppsUtils.getAppByManifestURL(this.webapps, aManifestURL);
},
getAppLocalIdByManifestURL: function getAppLocalIdByManifestURL(aManifestURL) {
debug("getAppLocalIdByManifestURL " + aManifestURL);
return AppsUtils.getAppLocalIdByManifestURL(this.webapps, aManifestURL);
},
getManifestCSPByLocalId: function(aLocalId) {
debug("getManifestCSPByLocalId:" + aLocalId);
return AppsUtils.getManifestCSPByLocalId(this.webapps, aLocalId);
},
getDefaultCSPByLocalId: function(aLocalId) {
debug("getDefaultCSPByLocalId:" + aLocalId);
return AppsUtils.getDefaultCSPByLocalId(this.webapps, aLocalId);
},
getAppLocalIdByStoreId: function(aStoreId) {
debug("getAppLocalIdByStoreId:" + aStoreId);
return AppsUtils.getAppLocalIdByStoreId(this.webapps, aStoreId);
},
getAppByLocalId: function getAppByLocalId(aLocalId) {
debug("getAppByLocalId " + aLocalId + " - ready: " + this.ready);
let app = this.localIdIndex[aLocalId];
if (!app) {
debug("Ouch, No app!");
return null;
}
return new mozIApplication(app);
},
getManifestURLByLocalId: function getManifestURLByLocalId(aLocalId) {
debug("getManifestURLByLocalId " + aLocalId);
return AppsUtils.getManifestURLByLocalId(this.webapps, aLocalId);
},
getCoreAppsBasePath: function getCoreAppsBasePath() {
debug("getCoreAppsBasePath() not yet supported on child!");
return null;
},
getWebAppsBasePath: function getWebAppsBasePath() {
debug("getWebAppsBasePath() not yet supported on child!");
return null;
},
getAppInfo: function getAppInfo(aAppId) {
return AppsUtils.getAppInfo(this.webapps, aAppId);
}
}
DOMApplicationRegistry.init();