mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge b2g-inbound to mozilla-central
This commit is contained in:
commit
fed6f5b097
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0292e64ef8451df104dcf9ac3b2c6749b81684dd"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
@ -128,7 +128,7 @@
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="09485b73629856b21b2ed6073e327ab0e69a1189"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9a00b3db898a83ef0766baa93e935e525572899e"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bb11a0417efa7e6a08ed1cb2d5d6a13a3100abd3"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="72e3a520e3c700839f07ba0113fd527b923c3330"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="baaf899afb158b9530690002f3656e958e3eb047"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0292e64ef8451df104dcf9ac3b2c6749b81684dd"/>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "9f480d1c52a46cac8e9a0be04bdb0d73810bc596",
|
||||
"revision": "2eae294604eb70e0e6eaee76b17e155ffd031107",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d25852a189c9707b144eb5f82d08384eb066c0fd"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="6a2eb4b3664fb3e6f5c87db2af8485b7424f9ecb"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -6,869 +6,23 @@
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
Cu.import("resource://gre/modules/InterAppCommService.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(aMsg) {
|
||||
dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
|
||||
function InterAppCommServiceProxy() {
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "messenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
const kMessages =["Webapps:Connect",
|
||||
"Webapps:GetConnections",
|
||||
"InterAppConnection:Cancel",
|
||||
"InterAppMessagePort:PostMessage",
|
||||
"InterAppMessagePort:Register",
|
||||
"InterAppMessagePort:Unregister",
|
||||
"child-process-shutdown"];
|
||||
|
||||
function InterAppCommService() {
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, "inter-app-comm-select-app-result", false);
|
||||
|
||||
kMessages.forEach(function(aMsg) {
|
||||
ppmm.addMessageListener(aMsg, this);
|
||||
}, this);
|
||||
|
||||
// This matrix is used for saving the inter-app connection info registered in
|
||||
// the app manifest. The object literal is defined as below:
|
||||
//
|
||||
// {
|
||||
// "keyword1": {
|
||||
// "subAppManifestURL1": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// "subAppManifestURL2": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// "keyword2": {
|
||||
// "subAppManifestURL3": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// {
|
||||
// "foo": {
|
||||
// "app://subApp1.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp1.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// rules: { ... }
|
||||
// },
|
||||
// "app://subApp2.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp2.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// rules: { ... }
|
||||
// }
|
||||
// },
|
||||
// "bar": {
|
||||
// "app://subApp3.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp3.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// rules: { ... }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TODO Bug 908999 - Update registered connections when app gets uninstalled.
|
||||
this._registeredConnections = {};
|
||||
|
||||
// This matrix is used for saving the permitted connections, which allows
|
||||
// the messaging between publishers and subscribers. The object literal is
|
||||
// defined as below:
|
||||
//
|
||||
// {
|
||||
// "keyword1": {
|
||||
// "pubAppManifestURL1": [
|
||||
// "subAppManifestURL1",
|
||||
// "subAppManifestURL2",
|
||||
// ...
|
||||
// ],
|
||||
// "pubAppManifestURL2": [
|
||||
// "subAppManifestURL3",
|
||||
// "subAppManifestURL4",
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// },
|
||||
// "keyword2": {
|
||||
// "pubAppManifestURL3": [
|
||||
// "subAppManifestURL5",
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// {
|
||||
// "foo": {
|
||||
// "app://pubApp1.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp1.gaiamobile.org/manifest.webapp",
|
||||
// "app://subApp2.gaiamobile.org/manifest.webapp"
|
||||
// ],
|
||||
// "app://pubApp2.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp3.gaiamobile.org/manifest.webapp",
|
||||
// "app://subApp4.gaiamobile.org/manifest.webapp"
|
||||
// ]
|
||||
// },
|
||||
// "bar": {
|
||||
// "app://pubApp3.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp5.gaiamobile.org/manifest.webapp",
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TODO Bug 908999 - Update allowed connections when app gets uninstalled.
|
||||
this._allowedConnections = {};
|
||||
|
||||
// This matrix is used for saving the caller info from the content process,
|
||||
// which is indexed by a random UUID, to know where to return the promise
|
||||
// resolvser's callback when the prompt UI for allowing connections returns.
|
||||
// An example of the object literal is shown as below:
|
||||
//
|
||||
// {
|
||||
// "fooID": {
|
||||
// outerWindowID: 12,
|
||||
// requestID: 34,
|
||||
// target: pubAppTarget1
|
||||
// },
|
||||
// "barID": {
|
||||
// outerWindowID: 56,
|
||||
// requestID: 78,
|
||||
// target: pubAppTarget2
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// where |outerWindowID| is the ID of the window requesting the connection,
|
||||
// |requestID| is the ID specifying the promise resolver to return,
|
||||
// |target| is the target of the process requesting the connection.
|
||||
this._promptUICallers = {};
|
||||
|
||||
// This matrix is used for saving the pair of message ports, which is indexed
|
||||
// by a random UUID, so that each port can know whom it should talk to.
|
||||
// An example of the object literal is shown as below:
|
||||
//
|
||||
// {
|
||||
// "UUID1": {
|
||||
// keyword: "keyword1",
|
||||
// publisher: {
|
||||
// manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
|
||||
// target: pubAppTarget1,
|
||||
// pageURL: "app://pubApp1.gaiamobile.org/caller.html",
|
||||
// messageQueue: [...]
|
||||
// },
|
||||
// subscriber: {
|
||||
// manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
|
||||
// target: subAppTarget1,
|
||||
// pageURL: "app://pubApp1.gaiamobile.org/handler.html",
|
||||
// messageQueue: [...]
|
||||
// }
|
||||
// },
|
||||
// "UUID2": {
|
||||
// keyword: "keyword2",
|
||||
// publisher: {
|
||||
// manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
|
||||
// target: pubAppTarget2,
|
||||
// pageURL: "app://pubApp2.gaiamobile.org/caller.html",
|
||||
// messageQueue: [...]
|
||||
// },
|
||||
// subscriber: {
|
||||
// manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
|
||||
// target: subAppTarget2,
|
||||
// pageURL: "app://pubApp2.gaiamobile.org/handler.html",
|
||||
// messageQueue: [...]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
this._messagePortPairs = {};
|
||||
}
|
||||
|
||||
InterAppCommService.prototype = {
|
||||
InterAppCommServiceProxy.prototype = {
|
||||
registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
|
||||
aDescription, aRules) {
|
||||
let manifestURL = aManifestURI.spec;
|
||||
let pageURL = aHandlerPageURI.spec;
|
||||
|
||||
if (DEBUG) {
|
||||
debug("registerConnection: aKeyword: " + aKeyword +
|
||||
" manifestURL: " + manifestURL + " pageURL: " + pageURL +
|
||||
" aDescription: " + aDescription +
|
||||
" aRules.minimumAccessLevel: " + aRules.minimumAccessLevel +
|
||||
" aRules.manifestURLs: " + aRules.manifestURLs +
|
||||
" aRules.installOrigins: " + aRules.installOrigins);
|
||||
}
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[aKeyword];
|
||||
if (!subAppManifestURLs) {
|
||||
subAppManifestURLs = this._registeredConnections[aKeyword] = {};
|
||||
}
|
||||
|
||||
subAppManifestURLs[manifestURL] = {
|
||||
pageURL: pageURL,
|
||||
description: aDescription,
|
||||
rules: aRules,
|
||||
manifestURL: manifestURL
|
||||
};
|
||||
},
|
||||
|
||||
_matchMinimumAccessLevel: function(aRules, aAppStatus) {
|
||||
if (!aRules || !aRules.minimumAccessLevel) {
|
||||
if (DEBUG) {
|
||||
debug("rules.minimumAccessLevel is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let minAccessLevel = aRules.minimumAccessLevel;
|
||||
switch (minAccessLevel) {
|
||||
case "web":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_INSTALLED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "privileged":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "certified":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.minimumAccessLevel is not matched!" +
|
||||
" minAccessLevel: " + minAccessLevel +
|
||||
" aAppStatus : " + aAppStatus);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchManifestURLs: function(aRules, aManifestURL) {
|
||||
if (!aRules || !Array.isArray(aRules.manifestURLs)) {
|
||||
if (DEBUG) {
|
||||
debug("rules.manifestURLs is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let manifestURLs = aRules.manifestURLs;
|
||||
if (manifestURLs.indexOf(aManifestURL) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.manifestURLs is not matched!" +
|
||||
" manifestURLs: " + manifestURLs +
|
||||
" aManifestURL : " + aManifestURL);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchInstallOrigins: function(aRules, aInstallOrigin) {
|
||||
if (!aRules || !Array.isArray(aRules.installOrigins)) {
|
||||
if (DEBUG) {
|
||||
debug("rules.installOrigins is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let installOrigins = aRules.installOrigins;
|
||||
if (installOrigins.indexOf(aInstallOrigin) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.installOrigins is not matched!" +
|
||||
" installOrigins: " + installOrigins +
|
||||
" installOrigin : " + aInstallOrigin);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchRules: function(aPubAppManifestURL, aPubRules,
|
||||
aSubAppManifestURL, aSubRules) {
|
||||
let pubApp = appsService.getAppByManifestURL(aPubAppManifestURL);
|
||||
let subApp = appsService.getAppByManifestURL(aSubAppManifestURL);
|
||||
|
||||
// TODO Bug 907068 In the initiative step, we only expose this API to
|
||||
// certified apps to meet the time line. Eventually, we need to make
|
||||
// it available for the non-certified apps as well. For now, only the
|
||||
// certified apps can match the rules.
|
||||
if (pubApp.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED ||
|
||||
subApp.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
if (DEBUG) {
|
||||
debug("Only certified apps are allowed to do connections.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aPubRules && !aSubRules) {
|
||||
if (DEBUG) {
|
||||
debug("No rules for publisher and subscriber. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check minimumAccessLevel.
|
||||
if (!this._matchMinimumAccessLevel(aPubRules, subApp.appStatus) ||
|
||||
!this._matchMinimumAccessLevel(aSubRules, pubApp.appStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check manifestURLs.
|
||||
if (!this._matchManifestURLs(aPubRules, aSubAppManifestURL) ||
|
||||
!this._matchManifestURLs(aSubRules, aPubAppManifestURL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check installOrigins.
|
||||
if (!this._matchInstallOrigins(aPubRules, subApp.installOrigin) ||
|
||||
!this._matchInstallOrigins(aSubRules, pubApp.installOrigin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check developers.
|
||||
// TODO Do we really want to check this? This one seems naive.
|
||||
|
||||
if (DEBUG) debug("All rules are matched.");
|
||||
return true;
|
||||
},
|
||||
|
||||
_dispatchMessagePorts: function(aKeyword, aPubAppManifestURL,
|
||||
aAllowedSubAppManifestURLs,
|
||||
aTarget, aOuterWindowID, aRequestID) {
|
||||
if (DEBUG) {
|
||||
debug("_dispatchMessagePorts: aKeyword: " + aKeyword +
|
||||
" aPubAppManifestURL: " + aPubAppManifestURL +
|
||||
" aAllowedSubAppManifestURLs: " + aAllowedSubAppManifestURLs);
|
||||
}
|
||||
|
||||
if (aAllowedSubAppManifestURLs.length == 0) {
|
||||
if (DEBUG) debug("No apps are allowed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[aKeyword];
|
||||
if (!subAppManifestURLs) {
|
||||
if (DEBUG) debug("No apps are subscribed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
let messagePortIDs = [];
|
||||
aAllowedSubAppManifestURLs.forEach(function(aAllowedSubAppManifestURL) {
|
||||
let subscribedInfo = subAppManifestURLs[aAllowedSubAppManifestURL];
|
||||
if (!subscribedInfo) {
|
||||
if (DEBUG) {
|
||||
debug("The sunscribed info is not available. Skipping: " +
|
||||
aAllowedSubAppManifestURL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The message port ID is aimed for identifying the coupling targets
|
||||
// to deliver messages with each other. This ID is centrally generated
|
||||
// by the parent and dispatched to both the sender and receiver ends
|
||||
// for creating their own message ports respectively.
|
||||
let messagePortID = UUIDGenerator.generateUUID().toString();
|
||||
this._messagePortPairs[messagePortID] = {
|
||||
keyword: aKeyword,
|
||||
publisher: {
|
||||
manifestURL: aPubAppManifestURL
|
||||
},
|
||||
subscriber: {
|
||||
manifestURL: aAllowedSubAppManifestURL
|
||||
}
|
||||
};
|
||||
|
||||
// Fire system message to deliver the message port to the subscriber.
|
||||
messenger.sendMessage("connection",
|
||||
{ keyword: aKeyword,
|
||||
messagePortID: messagePortID },
|
||||
Services.io.newURI(subscribedInfo.pageURL, null, null),
|
||||
Services.io.newURI(subscribedInfo.manifestURL, null, null));
|
||||
|
||||
messagePortIDs.push(messagePortID);
|
||||
}, this);
|
||||
|
||||
if (messagePortIDs.length == 0) {
|
||||
if (DEBUG) debug("No apps are subscribed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the message port IDs to open the message ports for the publisher.
|
||||
if (DEBUG) debug("messagePortIDs: " + messagePortIDs);
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:OK",
|
||||
{ keyword: aKeyword,
|
||||
messagePortIDs: messagePortIDs,
|
||||
oid: aOuterWindowID, requestID: aRequestID });
|
||||
},
|
||||
|
||||
_connect: function(aMessage, aTarget) {
|
||||
let keyword = aMessage.keyword;
|
||||
let pubRules = aMessage.rules;
|
||||
let pubAppManifestURL = aMessage.manifestURL;
|
||||
let outerWindowID = aMessage.outerWindowID;
|
||||
let requestID = aMessage.requestID;
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[keyword];
|
||||
if (!subAppManifestURLs) {
|
||||
if (DEBUG) {
|
||||
debug("No apps are subscribed for this connection. Returning.");
|
||||
}
|
||||
this._dispatchMessagePorts(keyword, pubAppManifestURL, [],
|
||||
aTarget, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the apps that used to be allowed to connect before, so that
|
||||
// users don't need to select/allow them again. That is, we only pop up
|
||||
// the prompt UI for the *new* connections.
|
||||
let allowedSubAppManifestURLs = [];
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (allowedPubAppManifestURLs &&
|
||||
allowedPubAppManifestURLs[pubAppManifestURL]) {
|
||||
allowedSubAppManifestURLs = allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
}
|
||||
|
||||
// Check rules to see if a subscribed app is allowed to connect.
|
||||
let appsToSelect = [];
|
||||
for (let subAppManifestURL in subAppManifestURLs) {
|
||||
if (allowedSubAppManifestURLs.indexOf(subAppManifestURL) != -1) {
|
||||
if (DEBUG) {
|
||||
debug("Don't need to select again. Skipping: " + subAppManifestURL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only rule-matched publishers/subscribers are allowed to connect.
|
||||
let subscribedInfo = subAppManifestURLs[subAppManifestURL];
|
||||
let subRules = subscribedInfo.rules;
|
||||
|
||||
let matched =
|
||||
this._matchRules(pubAppManifestURL, pubRules,
|
||||
subAppManifestURL, subRules);
|
||||
if (!matched) {
|
||||
if (DEBUG) {
|
||||
debug("Rules are not matched. Skipping: " + subAppManifestURL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
appsToSelect.push({
|
||||
manifestURL: subAppManifestURL,
|
||||
description: subscribedInfo.description
|
||||
});
|
||||
}
|
||||
|
||||
if (appsToSelect.length == 0) {
|
||||
if (DEBUG) {
|
||||
debug("No additional apps need to be selected for this connection. " +
|
||||
"Just dispatch message ports for the existing connections.");
|
||||
}
|
||||
|
||||
this._dispatchMessagePorts(keyword, pubAppManifestURL,
|
||||
allowedSubAppManifestURLs,
|
||||
aTarget, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember the caller info with an UUID so that we can know where to
|
||||
// return the promise resolver's callback when the prompt UI returns.
|
||||
let callerID = UUIDGenerator.generateUUID().toString();
|
||||
this._promptUICallers[callerID] = {
|
||||
outerWindowID: outerWindowID,
|
||||
requestID: requestID,
|
||||
target: aTarget
|
||||
};
|
||||
|
||||
// TODO Bug 897169 Temporarily disable the notification for popping up
|
||||
// the prompt until the UX/UI for the prompt is confirmed.
|
||||
//
|
||||
// TODO Bug 908191 We need to change the way of interaction between API and
|
||||
// run-time prompt from observer notification to xpcom-interface caller.
|
||||
//
|
||||
/*
|
||||
if (DEBUG) debug("appsToSelect: " + appsToSelect);
|
||||
Services.obs.notifyObservers(null, "inter-app-comm-select-app",
|
||||
JSON.stringify({ callerID: callerID,
|
||||
manifestURL: pubAppManifestURL,
|
||||
keyword: keyword,
|
||||
appsToSelect: appsToSelect }));
|
||||
*/
|
||||
|
||||
// TODO Bug 897169 Simulate the return of the app-selected result by
|
||||
// the prompt, which always allows the connection. This dummy codes
|
||||
// will be removed when the UX/UI for the prompt is ready.
|
||||
if (DEBUG) debug("appsToSelect: " + appsToSelect);
|
||||
Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
|
||||
JSON.stringify({ callerID: callerID,
|
||||
manifestURL: pubAppManifestURL,
|
||||
keyword: keyword,
|
||||
selectedApps: appsToSelect }));
|
||||
},
|
||||
|
||||
_getConnections: function(aMessage, aTarget) {
|
||||
let outerWindowID = aMessage.outerWindowID;
|
||||
let requestID = aMessage.requestID;
|
||||
|
||||
let connections = [];
|
||||
for (let keyword in this._allowedConnections) {
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
for (let allowedPubAppManifestURL in allowedPubAppManifestURLs) {
|
||||
let allowedSubAppManifestURLs =
|
||||
allowedPubAppManifestURLs[allowedPubAppManifestURL];
|
||||
allowedSubAppManifestURLs.forEach(function(allowedSubAppManifestURL) {
|
||||
connections.push({ keyword: keyword,
|
||||
pubAppManifestURL: allowedPubAppManifestURL,
|
||||
subAppManifestURL: allowedSubAppManifestURL });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
aTarget.sendAsyncMessage("Webapps:GetConnections:Return:OK",
|
||||
{ connections: connections,
|
||||
oid: outerWindowID, requestID: requestID });
|
||||
},
|
||||
|
||||
_cancelConnection: function(aMessage) {
|
||||
let keyword = aMessage.keyword;
|
||||
let pubAppManifestURL = aMessage.pubAppManifestURL;
|
||||
let subAppManifestURL = aMessage.subAppManifestURL;
|
||||
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (!allowedPubAppManifestURLs) {
|
||||
if (DEBUG) debug("keyword is not found: " + keyword);
|
||||
return;
|
||||
}
|
||||
|
||||
let allowedSubAppManifestURLs =
|
||||
allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
if (!allowedSubAppManifestURLs) {
|
||||
if (DEBUG) debug("publisher is not found: " + pubAppManifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
let index = allowedSubAppManifestURLs.indexOf(subAppManifestURL);
|
||||
if (index == -1) {
|
||||
if (DEBUG) debug("subscriber is not found: " + subAppManifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Cancelling the connection.");
|
||||
allowedSubAppManifestURLs.splice(index, 1);
|
||||
|
||||
// Clean up the parent entries if needed.
|
||||
if (allowedSubAppManifestURLs.length == 0) {
|
||||
delete allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
if (Object.keys(allowedPubAppManifestURLs).length == 0) {
|
||||
delete this._allowedConnections[keyword];
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Unregistering message ports based on this connection.");
|
||||
let messagePortIDs = [];
|
||||
for (let messagePortID in this._messagePortPairs) {
|
||||
let pair = this._messagePortPairs[messagePortID];
|
||||
if (pair.keyword == keyword &&
|
||||
pair.publisher.manifestURL == pubAppManifestURL &&
|
||||
pair.subscriber.manifestURL == subAppManifestURL) {
|
||||
messagePortIDs.push(messagePortID);
|
||||
}
|
||||
}
|
||||
messagePortIDs.forEach(function(aMessagePortID) {
|
||||
delete this._messagePortPairs[aMessagePortID];
|
||||
}, this);
|
||||
},
|
||||
|
||||
_identifyMessagePort: function(aMessagePortID, aManifestURL) {
|
||||
let pair = this._messagePortPairs[aMessagePortID];
|
||||
if (!pair) {
|
||||
if (DEBUG) {
|
||||
debug("Error! The message port ID is invalid: " + aMessagePortID +
|
||||
", which should have been generated by parent.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check it the message port is for publisher.
|
||||
if (pair.publisher.manifestURL == aManifestURL) {
|
||||
return { pair: pair, isPublisher: true };
|
||||
}
|
||||
|
||||
// Check it the message port is for subscriber.
|
||||
if (pair.subscriber.manifestURL == aManifestURL) {
|
||||
return { pair: pair, isPublisher: false };
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Error! The manifest URL is invalid: " + aManifestURL +
|
||||
", which might be a hacked app.");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_registerMessagePort: function(aMessage, aTarget) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
let pageURL = aMessage.pageURL;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) {
|
||||
debug("Cannot identify the message port. Failed to register.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Registering message port for " + manifestURL);
|
||||
let pair = identity.pair;
|
||||
let isPublisher = identity.isPublisher;
|
||||
|
||||
let sender = isPublisher ? pair.publisher : pair.subscriber;
|
||||
sender.target = aTarget;
|
||||
sender.pageURL = pageURL;
|
||||
sender.messageQueue = [];
|
||||
|
||||
// Check if the other port has queued messages. Deliver them if needed.
|
||||
if (DEBUG) {
|
||||
debug("Checking if the other port used to send messages but queued.");
|
||||
}
|
||||
let receiver = isPublisher ? pair.subscriber : pair.publisher;
|
||||
if (receiver.messageQueue) {
|
||||
while (receiver.messageQueue.length) {
|
||||
let message = receiver.messageQueue.shift();
|
||||
if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
|
||||
sender.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
|
||||
{ message: message,
|
||||
manifestURL: sender.manifestURL,
|
||||
pageURL: sender.pageURL,
|
||||
messagePortID: messagePortID });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_unregisterMessagePort: function(aMessage) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) {
|
||||
debug("Cannot identify the message port. Failed to unregister.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Unregistering message port for " + manifestURL);
|
||||
}
|
||||
delete this._messagePortPairs[messagePortID];
|
||||
},
|
||||
|
||||
_removeTarget: function(aTarget) {
|
||||
if (!aTarget) {
|
||||
if (DEBUG) debug("Error! aTarget cannot be null/undefined in any way.");
|
||||
return
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Unregistering message ports based on this target.");
|
||||
let messagePortIDs = [];
|
||||
for (let messagePortID in this._messagePortPairs) {
|
||||
let pair = this._messagePortPairs[messagePortID];
|
||||
if (pair.publisher.target === aTarget ||
|
||||
pair.subscriber.target === aTarget) {
|
||||
messagePortIDs.push(messagePortID);
|
||||
}
|
||||
}
|
||||
messagePortIDs.forEach(function(aMessagePortID) {
|
||||
delete this._messagePortPairs[aMessagePortID];
|
||||
}, this);
|
||||
},
|
||||
|
||||
_postMessage: function(aMessage) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
let message = aMessage.message;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) debug("Cannot identify the message port. Failed to post.");
|
||||
return;
|
||||
}
|
||||
|
||||
let pair = identity.pair;
|
||||
let isPublisher = identity.isPublisher;
|
||||
|
||||
let receiver = isPublisher ? pair.subscriber : pair.publisher;
|
||||
if (!receiver.target) {
|
||||
if (DEBUG) {
|
||||
debug("The receiver's target is not ready yet. Queuing the message.");
|
||||
}
|
||||
let sender = isPublisher ? pair.publisher : pair.subscriber;
|
||||
sender.messageQueue.push(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
|
||||
receiver.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
|
||||
{ manifestURL: receiver.manifestURL,
|
||||
pageURL: receiver.pageURL,
|
||||
messagePortID: messagePortID,
|
||||
message: message });
|
||||
},
|
||||
|
||||
_handleSelectcedApps: function(aData) {
|
||||
let callerID = aData.callerID;
|
||||
let caller = this._promptUICallers[callerID];
|
||||
if (!caller) {
|
||||
if (DEBUG) debug("Error! Cannot find the caller.");
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._promptUICallers[callerID];
|
||||
|
||||
let outerWindowID = caller.outerWindowID;
|
||||
let requestID = caller.requestID;
|
||||
let target = caller.target;
|
||||
|
||||
let manifestURL = aData.manifestURL;
|
||||
let keyword = aData.keyword;
|
||||
let selectedApps = aData.selectedApps;
|
||||
|
||||
if (selectedApps.length == 0) {
|
||||
if (DEBUG) debug("No apps are selected to connect.")
|
||||
this._dispatchMessagePorts(keyword, manifestURL, [],
|
||||
target, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the entry of allowed connections to add the selected apps.
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (!allowedPubAppManifestURLs) {
|
||||
allowedPubAppManifestURLs = this._allowedConnections[keyword] = {};
|
||||
}
|
||||
let allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL];
|
||||
if (!allowedSubAppManifestURLs) {
|
||||
allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL] = [];
|
||||
}
|
||||
|
||||
// Add the selected app into the existing set of allowed connections.
|
||||
selectedApps.forEach(function(aSelectedApp) {
|
||||
let allowedSubAppManifestURL = aSelectedApp.manifestURL;
|
||||
if (allowedSubAppManifestURLs.indexOf(allowedSubAppManifestURL) == -1) {
|
||||
allowedSubAppManifestURLs.push(allowedSubAppManifestURL);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally, dispatch the message ports for the allowed connections,
|
||||
// including the old connections and the newly selected connection.
|
||||
this._dispatchMessagePorts(keyword, manifestURL, allowedSubAppManifestURLs,
|
||||
target, outerWindowID, requestID);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
|
||||
let message = aMessage.json;
|
||||
let target = aMessage.target;
|
||||
|
||||
// To prevent the hacked child process from sending commands to parent
|
||||
// to do illegal connections, we need to check its manifest URL.
|
||||
if (aMessage.name !== "child-process-shutdown" &&
|
||||
// TODO: fix bug 988142 to re-enable "InterAppMessagePort:Unregister".
|
||||
aMessage.name !== "InterAppMessagePort:Unregister" &&
|
||||
kMessages.indexOf(aMessage.name) != -1) {
|
||||
if (!target.assertContainApp(message.manifestURL)) {
|
||||
if (DEBUG) {
|
||||
debug("Got message from a process carrying illegal manifest URL.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Connect":
|
||||
this._connect(message, target);
|
||||
break;
|
||||
case "Webapps:GetConnections":
|
||||
this._getConnections(message, target);
|
||||
break;
|
||||
case "InterAppConnection:Cancel":
|
||||
this._cancelConnection(message);
|
||||
break;
|
||||
case "InterAppMessagePort:PostMessage":
|
||||
this._postMessage(message);
|
||||
break;
|
||||
case "InterAppMessagePort:Register":
|
||||
this._registerMessagePort(message, target);
|
||||
break;
|
||||
case "InterAppMessagePort:Unregister":
|
||||
this._unregisterMessagePort(message);
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
this._removeTarget(target);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "xpcom-shutdown":
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
Services.obs.removeObserver(this, "inter-app-comm-select-app-result");
|
||||
kMessages.forEach(function(aMsg) {
|
||||
ppmm.removeMessageListener(aMsg, this);
|
||||
}, this);
|
||||
ppmm = null;
|
||||
break;
|
||||
case "inter-app-comm-select-app-result":
|
||||
if (DEBUG) debug("inter-app-comm-select-app-result: " + aData);
|
||||
this._handleSelectcedApps(JSON.parse(aData));
|
||||
break;
|
||||
}
|
||||
InterAppCommService.
|
||||
registerConnection(aKeyword, aHandlerPageURI, aManifestURI,
|
||||
aDescription, aRules);
|
||||
},
|
||||
|
||||
classID: Components.ID("{3dd15ce6-e7be-11e2-82bc-77967e7a63e6}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService,
|
||||
Ci.nsIObserver])
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIInterAppCommService])
|
||||
}
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommService]);
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InterAppCommServiceProxy]);
|
||||
|
889
dom/apps/src/InterAppCommService.jsm
Normal file
889
dom/apps/src/InterAppCommService.jsm
Normal file
@ -0,0 +1,889 @@
|
||||
/* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["InterAppCommService"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
function debug(aMsg) {
|
||||
dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
"nsIAppsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageBroadcaster");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "messenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
|
||||
const kMessages =["Webapps:Connect",
|
||||
"Webapps:GetConnections",
|
||||
"InterAppConnection:Cancel",
|
||||
"InterAppMessagePort:PostMessage",
|
||||
"InterAppMessagePort:Register",
|
||||
"InterAppMessagePort:Unregister",
|
||||
"child-process-shutdown"];
|
||||
|
||||
/**
|
||||
* This module contains helpers for Inter-App Communication API [1] related
|
||||
* purposes, which plays the role of the central service receiving messages
|
||||
* from and interacting with the content processes.
|
||||
*
|
||||
* [1] https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
|
||||
*/
|
||||
|
||||
this.InterAppCommService = {
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
Services.obs.addObserver(this, "inter-app-comm-select-app-result", false);
|
||||
|
||||
kMessages.forEach(function(aMsg) {
|
||||
ppmm.addMessageListener(aMsg, this);
|
||||
}, this);
|
||||
|
||||
// This matrix is used for saving the inter-app connection info registered in
|
||||
// the app manifest. The object literal is defined as below:
|
||||
//
|
||||
// {
|
||||
// "keyword1": {
|
||||
// "subAppManifestURL1": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// "subAppManifestURL2": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// "keyword2": {
|
||||
// "subAppManifestURL3": {
|
||||
// /* subscribed info */
|
||||
// },
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// {
|
||||
// "foo": {
|
||||
// "app://subApp1.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp1.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// rules: { ... }
|
||||
// },
|
||||
// "app://subApp2.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp2.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// rules: { ... }
|
||||
// }
|
||||
// },
|
||||
// "bar": {
|
||||
// "app://subApp3.gaiamobile.org/manifest.webapp": {
|
||||
// pageURL: "app://subApp3.gaiamobile.org/handler.html",
|
||||
// description: "blah blah",
|
||||
// rules: { ... }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TODO Bug 908999 - Update registered connections when app gets uninstalled.
|
||||
this._registeredConnections = {};
|
||||
|
||||
// This matrix is used for saving the permitted connections, which allows
|
||||
// the messaging between publishers and subscribers. The object literal is
|
||||
// defined as below:
|
||||
//
|
||||
// {
|
||||
// "keyword1": {
|
||||
// "pubAppManifestURL1": [
|
||||
// "subAppManifestURL1",
|
||||
// "subAppManifestURL2",
|
||||
// ...
|
||||
// ],
|
||||
// "pubAppManifestURL2": [
|
||||
// "subAppManifestURL3",
|
||||
// "subAppManifestURL4",
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// },
|
||||
// "keyword2": {
|
||||
// "pubAppManifestURL3": [
|
||||
// "subAppManifestURL5",
|
||||
// ...
|
||||
// ],
|
||||
// ...
|
||||
// },
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// {
|
||||
// "foo": {
|
||||
// "app://pubApp1.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp1.gaiamobile.org/manifest.webapp",
|
||||
// "app://subApp2.gaiamobile.org/manifest.webapp"
|
||||
// ],
|
||||
// "app://pubApp2.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp3.gaiamobile.org/manifest.webapp",
|
||||
// "app://subApp4.gaiamobile.org/manifest.webapp"
|
||||
// ]
|
||||
// },
|
||||
// "bar": {
|
||||
// "app://pubApp3.gaiamobile.org/manifest.webapp": [
|
||||
// "app://subApp5.gaiamobile.org/manifest.webapp",
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TODO Bug 908999 - Update allowed connections when app gets uninstalled.
|
||||
this._allowedConnections = {};
|
||||
|
||||
// This matrix is used for saving the caller info from the content process,
|
||||
// which is indexed by a random UUID, to know where to return the promise
|
||||
// resolvser's callback when the prompt UI for allowing connections returns.
|
||||
// An example of the object literal is shown as below:
|
||||
//
|
||||
// {
|
||||
// "fooID": {
|
||||
// outerWindowID: 12,
|
||||
// requestID: 34,
|
||||
// target: pubAppTarget1
|
||||
// },
|
||||
// "barID": {
|
||||
// outerWindowID: 56,
|
||||
// requestID: 78,
|
||||
// target: pubAppTarget2
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// where |outerWindowID| is the ID of the window requesting the connection,
|
||||
// |requestID| is the ID specifying the promise resolver to return,
|
||||
// |target| is the target of the process requesting the connection.
|
||||
this._promptUICallers = {};
|
||||
|
||||
// This matrix is used for saving the pair of message ports, which is indexed
|
||||
// by a random UUID, so that each port can know whom it should talk to.
|
||||
// An example of the object literal is shown as below:
|
||||
//
|
||||
// {
|
||||
// "UUID1": {
|
||||
// keyword: "keyword1",
|
||||
// publisher: {
|
||||
// manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
|
||||
// target: pubAppTarget1,
|
||||
// pageURL: "app://pubApp1.gaiamobile.org/caller.html",
|
||||
// messageQueue: [...]
|
||||
// },
|
||||
// subscriber: {
|
||||
// manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
|
||||
// target: subAppTarget1,
|
||||
// pageURL: "app://pubApp1.gaiamobile.org/handler.html",
|
||||
// messageQueue: [...]
|
||||
// }
|
||||
// },
|
||||
// "UUID2": {
|
||||
// keyword: "keyword2",
|
||||
// publisher: {
|
||||
// manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
|
||||
// target: pubAppTarget2,
|
||||
// pageURL: "app://pubApp2.gaiamobile.org/caller.html",
|
||||
// messageQueue: [...]
|
||||
// },
|
||||
// subscriber: {
|
||||
// manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
|
||||
// target: subAppTarget2,
|
||||
// pageURL: "app://pubApp2.gaiamobile.org/handler.html",
|
||||
// messageQueue: [...]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
this._messagePortPairs = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Registration of a page that wants to be connected to other apps through
|
||||
* the Inter-App Communication API.
|
||||
*
|
||||
* @param aKeyword The connection's keyword.
|
||||
* @param aHandlerPageURI The URI of the handler's page.
|
||||
* @param aManifestURI The webapp's manifest URI.
|
||||
* @param aDescription The connection's description.
|
||||
* @param aRules The connection's rules.
|
||||
*/
|
||||
registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
|
||||
aDescription, aRules) {
|
||||
let manifestURL = aManifestURI.spec;
|
||||
let pageURL = aHandlerPageURI.spec;
|
||||
|
||||
if (DEBUG) {
|
||||
debug("registerConnection: aKeyword: " + aKeyword +
|
||||
" manifestURL: " + manifestURL + " pageURL: " + pageURL +
|
||||
" aDescription: " + aDescription +
|
||||
" aRules.minimumAccessLevel: " + aRules.minimumAccessLevel +
|
||||
" aRules.manifestURLs: " + aRules.manifestURLs +
|
||||
" aRules.installOrigins: " + aRules.installOrigins);
|
||||
}
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[aKeyword];
|
||||
if (!subAppManifestURLs) {
|
||||
subAppManifestURLs = this._registeredConnections[aKeyword] = {};
|
||||
}
|
||||
|
||||
subAppManifestURLs[manifestURL] = {
|
||||
pageURL: pageURL,
|
||||
description: aDescription,
|
||||
rules: aRules,
|
||||
manifestURL: manifestURL
|
||||
};
|
||||
},
|
||||
|
||||
_matchMinimumAccessLevel: function(aRules, aAppStatus) {
|
||||
if (!aRules || !aRules.minimumAccessLevel) {
|
||||
if (DEBUG) {
|
||||
debug("rules.minimumAccessLevel is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let minAccessLevel = aRules.minimumAccessLevel;
|
||||
switch (minAccessLevel) {
|
||||
case "web":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_INSTALLED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "privileged":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
|
||||
aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case "certified":
|
||||
if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.minimumAccessLevel is not matched!" +
|
||||
" minAccessLevel: " + minAccessLevel +
|
||||
" aAppStatus : " + aAppStatus);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchManifestURLs: function(aRules, aManifestURL) {
|
||||
if (!aRules || !Array.isArray(aRules.manifestURLs)) {
|
||||
if (DEBUG) {
|
||||
debug("rules.manifestURLs is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let manifestURLs = aRules.manifestURLs;
|
||||
if (manifestURLs.indexOf(aManifestURL) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.manifestURLs is not matched!" +
|
||||
" manifestURLs: " + manifestURLs +
|
||||
" aManifestURL : " + aManifestURL);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchInstallOrigins: function(aRules, aInstallOrigin) {
|
||||
if (!aRules || !Array.isArray(aRules.installOrigins)) {
|
||||
if (DEBUG) {
|
||||
debug("rules.installOrigins is not available. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let installOrigins = aRules.installOrigins;
|
||||
if (installOrigins.indexOf(aInstallOrigin) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("rules.installOrigins is not matched!" +
|
||||
" installOrigins: " + installOrigins +
|
||||
" installOrigin : " + aInstallOrigin);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_matchRules: function(aPubAppManifestURL, aPubRules,
|
||||
aSubAppManifestURL, aSubRules) {
|
||||
let pubApp = appsService.getAppByManifestURL(aPubAppManifestURL);
|
||||
let subApp = appsService.getAppByManifestURL(aSubAppManifestURL);
|
||||
|
||||
// TODO Bug 907068 In the initiative step, we only expose this API to
|
||||
// certified apps to meet the time line. Eventually, we need to make
|
||||
// it available for the non-certified apps as well. For now, only the
|
||||
// certified apps can match the rules.
|
||||
if (pubApp.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED ||
|
||||
subApp.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
if (DEBUG) {
|
||||
debug("Only certified apps are allowed to do connections.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aPubRules && !aSubRules) {
|
||||
if (DEBUG) {
|
||||
debug("No rules for publisher and subscriber. No need to match.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check minimumAccessLevel.
|
||||
if (!this._matchMinimumAccessLevel(aPubRules, subApp.appStatus) ||
|
||||
!this._matchMinimumAccessLevel(aSubRules, pubApp.appStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check manifestURLs.
|
||||
if (!this._matchManifestURLs(aPubRules, aSubAppManifestURL) ||
|
||||
!this._matchManifestURLs(aSubRules, aPubAppManifestURL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check installOrigins.
|
||||
if (!this._matchInstallOrigins(aPubRules, subApp.installOrigin) ||
|
||||
!this._matchInstallOrigins(aSubRules, pubApp.installOrigin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check developers.
|
||||
// TODO Do we really want to check this? This one seems naive.
|
||||
|
||||
if (DEBUG) debug("All rules are matched.");
|
||||
return true;
|
||||
},
|
||||
|
||||
_dispatchMessagePorts: function(aKeyword, aPubAppManifestURL,
|
||||
aAllowedSubAppManifestURLs,
|
||||
aTarget, aOuterWindowID, aRequestID) {
|
||||
if (DEBUG) {
|
||||
debug("_dispatchMessagePorts: aKeyword: " + aKeyword +
|
||||
" aPubAppManifestURL: " + aPubAppManifestURL +
|
||||
" aAllowedSubAppManifestURLs: " + aAllowedSubAppManifestURLs);
|
||||
}
|
||||
|
||||
if (aAllowedSubAppManifestURLs.length == 0) {
|
||||
if (DEBUG) debug("No apps are allowed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[aKeyword];
|
||||
if (!subAppManifestURLs) {
|
||||
if (DEBUG) debug("No apps are subscribed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
let messagePortIDs = [];
|
||||
aAllowedSubAppManifestURLs.forEach(function(aAllowedSubAppManifestURL) {
|
||||
let subscribedInfo = subAppManifestURLs[aAllowedSubAppManifestURL];
|
||||
if (!subscribedInfo) {
|
||||
if (DEBUG) {
|
||||
debug("The sunscribed info is not available. Skipping: " +
|
||||
aAllowedSubAppManifestURL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The message port ID is aimed for identifying the coupling targets
|
||||
// to deliver messages with each other. This ID is centrally generated
|
||||
// by the parent and dispatched to both the sender and receiver ends
|
||||
// for creating their own message ports respectively.
|
||||
let messagePortID = UUIDGenerator.generateUUID().toString();
|
||||
this._messagePortPairs[messagePortID] = {
|
||||
keyword: aKeyword,
|
||||
publisher: {
|
||||
manifestURL: aPubAppManifestURL
|
||||
},
|
||||
subscriber: {
|
||||
manifestURL: aAllowedSubAppManifestURL
|
||||
}
|
||||
};
|
||||
|
||||
// Fire system message to deliver the message port to the subscriber.
|
||||
messenger.sendMessage("connection",
|
||||
{ keyword: aKeyword,
|
||||
messagePortID: messagePortID },
|
||||
Services.io.newURI(subscribedInfo.pageURL, null, null),
|
||||
Services.io.newURI(subscribedInfo.manifestURL, null, null));
|
||||
|
||||
messagePortIDs.push(messagePortID);
|
||||
}, this);
|
||||
|
||||
if (messagePortIDs.length == 0) {
|
||||
if (DEBUG) debug("No apps are subscribed to connect. Returning.");
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
|
||||
{ oid: aOuterWindowID, requestID: aRequestID });
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the message port IDs to open the message ports for the publisher.
|
||||
if (DEBUG) debug("messagePortIDs: " + messagePortIDs);
|
||||
aTarget.sendAsyncMessage("Webapps:Connect:Return:OK",
|
||||
{ keyword: aKeyword,
|
||||
messagePortIDs: messagePortIDs,
|
||||
oid: aOuterWindowID, requestID: aRequestID });
|
||||
},
|
||||
|
||||
_connect: function(aMessage, aTarget) {
|
||||
let keyword = aMessage.keyword;
|
||||
let pubRules = aMessage.rules;
|
||||
let pubAppManifestURL = aMessage.manifestURL;
|
||||
let outerWindowID = aMessage.outerWindowID;
|
||||
let requestID = aMessage.requestID;
|
||||
|
||||
let subAppManifestURLs = this._registeredConnections[keyword];
|
||||
if (!subAppManifestURLs) {
|
||||
if (DEBUG) {
|
||||
debug("No apps are subscribed for this connection. Returning.");
|
||||
}
|
||||
this._dispatchMessagePorts(keyword, pubAppManifestURL, [],
|
||||
aTarget, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the apps that used to be allowed to connect before, so that
|
||||
// users don't need to select/allow them again. That is, we only pop up
|
||||
// the prompt UI for the *new* connections.
|
||||
let allowedSubAppManifestURLs = [];
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (allowedPubAppManifestURLs &&
|
||||
allowedPubAppManifestURLs[pubAppManifestURL]) {
|
||||
allowedSubAppManifestURLs = allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
}
|
||||
|
||||
// Check rules to see if a subscribed app is allowed to connect.
|
||||
let appsToSelect = [];
|
||||
for (let subAppManifestURL in subAppManifestURLs) {
|
||||
if (allowedSubAppManifestURLs.indexOf(subAppManifestURL) != -1) {
|
||||
if (DEBUG) {
|
||||
debug("Don't need to select again. Skipping: " + subAppManifestURL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only rule-matched publishers/subscribers are allowed to connect.
|
||||
let subscribedInfo = subAppManifestURLs[subAppManifestURL];
|
||||
let subRules = subscribedInfo.rules;
|
||||
|
||||
let matched =
|
||||
this._matchRules(pubAppManifestURL, pubRules,
|
||||
subAppManifestURL, subRules);
|
||||
if (!matched) {
|
||||
if (DEBUG) {
|
||||
debug("Rules are not matched. Skipping: " + subAppManifestURL);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
appsToSelect.push({
|
||||
manifestURL: subAppManifestURL,
|
||||
description: subscribedInfo.description
|
||||
});
|
||||
}
|
||||
|
||||
if (appsToSelect.length == 0) {
|
||||
if (DEBUG) {
|
||||
debug("No additional apps need to be selected for this connection. " +
|
||||
"Just dispatch message ports for the existing connections.");
|
||||
}
|
||||
|
||||
this._dispatchMessagePorts(keyword, pubAppManifestURL,
|
||||
allowedSubAppManifestURLs,
|
||||
aTarget, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember the caller info with an UUID so that we can know where to
|
||||
// return the promise resolver's callback when the prompt UI returns.
|
||||
let callerID = UUIDGenerator.generateUUID().toString();
|
||||
this._promptUICallers[callerID] = {
|
||||
outerWindowID: outerWindowID,
|
||||
requestID: requestID,
|
||||
target: aTarget
|
||||
};
|
||||
|
||||
// TODO Bug 897169 Temporarily disable the notification for popping up
|
||||
// the prompt until the UX/UI for the prompt is confirmed.
|
||||
//
|
||||
// TODO Bug 908191 We need to change the way of interaction between API and
|
||||
// run-time prompt from observer notification to xpcom-interface caller.
|
||||
//
|
||||
/*
|
||||
if (DEBUG) debug("appsToSelect: " + appsToSelect);
|
||||
Services.obs.notifyObservers(null, "inter-app-comm-select-app",
|
||||
JSON.stringify({ callerID: callerID,
|
||||
manifestURL: pubAppManifestURL,
|
||||
keyword: keyword,
|
||||
appsToSelect: appsToSelect }));
|
||||
*/
|
||||
|
||||
// TODO Bug 897169 Simulate the return of the app-selected result by
|
||||
// the prompt, which always allows the connection. This dummy codes
|
||||
// will be removed when the UX/UI for the prompt is ready.
|
||||
if (DEBUG) debug("appsToSelect: " + appsToSelect);
|
||||
Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
|
||||
JSON.stringify({ callerID: callerID,
|
||||
manifestURL: pubAppManifestURL,
|
||||
keyword: keyword,
|
||||
selectedApps: appsToSelect }));
|
||||
},
|
||||
|
||||
_getConnections: function(aMessage, aTarget) {
|
||||
let outerWindowID = aMessage.outerWindowID;
|
||||
let requestID = aMessage.requestID;
|
||||
|
||||
let connections = [];
|
||||
for (let keyword in this._allowedConnections) {
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
for (let allowedPubAppManifestURL in allowedPubAppManifestURLs) {
|
||||
let allowedSubAppManifestURLs =
|
||||
allowedPubAppManifestURLs[allowedPubAppManifestURL];
|
||||
allowedSubAppManifestURLs.forEach(function(allowedSubAppManifestURL) {
|
||||
connections.push({ keyword: keyword,
|
||||
pubAppManifestURL: allowedPubAppManifestURL,
|
||||
subAppManifestURL: allowedSubAppManifestURL });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
aTarget.sendAsyncMessage("Webapps:GetConnections:Return:OK",
|
||||
{ connections: connections,
|
||||
oid: outerWindowID, requestID: requestID });
|
||||
},
|
||||
|
||||
_cancelConnection: function(aMessage) {
|
||||
let keyword = aMessage.keyword;
|
||||
let pubAppManifestURL = aMessage.pubAppManifestURL;
|
||||
let subAppManifestURL = aMessage.subAppManifestURL;
|
||||
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (!allowedPubAppManifestURLs) {
|
||||
if (DEBUG) debug("keyword is not found: " + keyword);
|
||||
return;
|
||||
}
|
||||
|
||||
let allowedSubAppManifestURLs =
|
||||
allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
if (!allowedSubAppManifestURLs) {
|
||||
if (DEBUG) debug("publisher is not found: " + pubAppManifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
let index = allowedSubAppManifestURLs.indexOf(subAppManifestURL);
|
||||
if (index == -1) {
|
||||
if (DEBUG) debug("subscriber is not found: " + subAppManifestURL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Cancelling the connection.");
|
||||
allowedSubAppManifestURLs.splice(index, 1);
|
||||
|
||||
// Clean up the parent entries if needed.
|
||||
if (allowedSubAppManifestURLs.length == 0) {
|
||||
delete allowedPubAppManifestURLs[pubAppManifestURL];
|
||||
if (Object.keys(allowedPubAppManifestURLs).length == 0) {
|
||||
delete this._allowedConnections[keyword];
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Unregistering message ports based on this connection.");
|
||||
let messagePortIDs = [];
|
||||
for (let messagePortID in this._messagePortPairs) {
|
||||
let pair = this._messagePortPairs[messagePortID];
|
||||
if (pair.keyword == keyword &&
|
||||
pair.publisher.manifestURL == pubAppManifestURL &&
|
||||
pair.subscriber.manifestURL == subAppManifestURL) {
|
||||
messagePortIDs.push(messagePortID);
|
||||
}
|
||||
}
|
||||
messagePortIDs.forEach(function(aMessagePortID) {
|
||||
delete this._messagePortPairs[aMessagePortID];
|
||||
}, this);
|
||||
},
|
||||
|
||||
_identifyMessagePort: function(aMessagePortID, aManifestURL) {
|
||||
let pair = this._messagePortPairs[aMessagePortID];
|
||||
if (!pair) {
|
||||
if (DEBUG) {
|
||||
debug("Error! The message port ID is invalid: " + aMessagePortID +
|
||||
", which should have been generated by parent.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check it the message port is for publisher.
|
||||
if (pair.publisher.manifestURL == aManifestURL) {
|
||||
return { pair: pair, isPublisher: true };
|
||||
}
|
||||
|
||||
// Check it the message port is for subscriber.
|
||||
if (pair.subscriber.manifestURL == aManifestURL) {
|
||||
return { pair: pair, isPublisher: false };
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Error! The manifest URL is invalid: " + aManifestURL +
|
||||
", which might be a hacked app.");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_registerMessagePort: function(aMessage, aTarget) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
let pageURL = aMessage.pageURL;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) {
|
||||
debug("Cannot identify the message port. Failed to register.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Registering message port for " + manifestURL);
|
||||
let pair = identity.pair;
|
||||
let isPublisher = identity.isPublisher;
|
||||
|
||||
let sender = isPublisher ? pair.publisher : pair.subscriber;
|
||||
sender.target = aTarget;
|
||||
sender.pageURL = pageURL;
|
||||
sender.messageQueue = [];
|
||||
|
||||
// Check if the other port has queued messages. Deliver them if needed.
|
||||
if (DEBUG) {
|
||||
debug("Checking if the other port used to send messages but queued.");
|
||||
}
|
||||
let receiver = isPublisher ? pair.subscriber : pair.publisher;
|
||||
if (receiver.messageQueue) {
|
||||
while (receiver.messageQueue.length) {
|
||||
let message = receiver.messageQueue.shift();
|
||||
if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
|
||||
sender.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
|
||||
{ message: message,
|
||||
manifestURL: sender.manifestURL,
|
||||
pageURL: sender.pageURL,
|
||||
messagePortID: messagePortID });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_unregisterMessagePort: function(aMessage) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) {
|
||||
debug("Cannot identify the message port. Failed to unregister.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
debug("Unregistering message port for " + manifestURL);
|
||||
}
|
||||
delete this._messagePortPairs[messagePortID];
|
||||
},
|
||||
|
||||
_removeTarget: function(aTarget) {
|
||||
if (!aTarget) {
|
||||
if (DEBUG) debug("Error! aTarget cannot be null/undefined in any way.");
|
||||
return
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Unregistering message ports based on this target.");
|
||||
let messagePortIDs = [];
|
||||
for (let messagePortID in this._messagePortPairs) {
|
||||
let pair = this._messagePortPairs[messagePortID];
|
||||
if (pair.publisher.target === aTarget ||
|
||||
pair.subscriber.target === aTarget) {
|
||||
messagePortIDs.push(messagePortID);
|
||||
}
|
||||
}
|
||||
messagePortIDs.forEach(function(aMessagePortID) {
|
||||
delete this._messagePortPairs[aMessagePortID];
|
||||
}, this);
|
||||
},
|
||||
|
||||
_postMessage: function(aMessage) {
|
||||
let messagePortID = aMessage.messagePortID;
|
||||
let manifestURL = aMessage.manifestURL;
|
||||
let message = aMessage.message;
|
||||
|
||||
let identity = this._identifyMessagePort(messagePortID, manifestURL);
|
||||
if (!identity) {
|
||||
if (DEBUG) debug("Cannot identify the message port. Failed to post.");
|
||||
return;
|
||||
}
|
||||
|
||||
let pair = identity.pair;
|
||||
let isPublisher = identity.isPublisher;
|
||||
|
||||
let receiver = isPublisher ? pair.subscriber : pair.publisher;
|
||||
if (!receiver.target) {
|
||||
if (DEBUG) {
|
||||
debug("The receiver's target is not ready yet. Queuing the message.");
|
||||
}
|
||||
let sender = isPublisher ? pair.publisher : pair.subscriber;
|
||||
sender.messageQueue.push(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
|
||||
receiver.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
|
||||
{ manifestURL: receiver.manifestURL,
|
||||
pageURL: receiver.pageURL,
|
||||
messagePortID: messagePortID,
|
||||
message: message });
|
||||
},
|
||||
|
||||
_handleSelectcedApps: function(aData) {
|
||||
let callerID = aData.callerID;
|
||||
let caller = this._promptUICallers[callerID];
|
||||
if (!caller) {
|
||||
if (DEBUG) debug("Error! Cannot find the caller.");
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._promptUICallers[callerID];
|
||||
|
||||
let outerWindowID = caller.outerWindowID;
|
||||
let requestID = caller.requestID;
|
||||
let target = caller.target;
|
||||
|
||||
let manifestURL = aData.manifestURL;
|
||||
let keyword = aData.keyword;
|
||||
let selectedApps = aData.selectedApps;
|
||||
|
||||
if (selectedApps.length == 0) {
|
||||
if (DEBUG) debug("No apps are selected to connect.")
|
||||
this._dispatchMessagePorts(keyword, manifestURL, [],
|
||||
target, outerWindowID, requestID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the entry of allowed connections to add the selected apps.
|
||||
let allowedPubAppManifestURLs = this._allowedConnections[keyword];
|
||||
if (!allowedPubAppManifestURLs) {
|
||||
allowedPubAppManifestURLs = this._allowedConnections[keyword] = {};
|
||||
}
|
||||
let allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL];
|
||||
if (!allowedSubAppManifestURLs) {
|
||||
allowedSubAppManifestURLs = allowedPubAppManifestURLs[manifestURL] = [];
|
||||
}
|
||||
|
||||
// Add the selected app into the existing set of allowed connections.
|
||||
selectedApps.forEach(function(aSelectedApp) {
|
||||
let allowedSubAppManifestURL = aSelectedApp.manifestURL;
|
||||
if (allowedSubAppManifestURLs.indexOf(allowedSubAppManifestURL) == -1) {
|
||||
allowedSubAppManifestURLs.push(allowedSubAppManifestURL);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally, dispatch the message ports for the allowed connections,
|
||||
// including the old connections and the newly selected connection.
|
||||
this._dispatchMessagePorts(keyword, manifestURL, allowedSubAppManifestURLs,
|
||||
target, outerWindowID, requestID);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
|
||||
let message = aMessage.json;
|
||||
let target = aMessage.target;
|
||||
|
||||
// To prevent the hacked child process from sending commands to parent
|
||||
// to do illegal connections, we need to check its manifest URL.
|
||||
if (aMessage.name !== "child-process-shutdown" &&
|
||||
// TODO: fix bug 988142 to re-enable "InterAppMessagePort:Unregister".
|
||||
aMessage.name !== "InterAppMessagePort:Unregister" &&
|
||||
kMessages.indexOf(aMessage.name) != -1) {
|
||||
if (!target.assertContainApp(message.manifestURL)) {
|
||||
if (DEBUG) {
|
||||
debug("Got message from a process carrying illegal manifest URL.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Webapps:Connect":
|
||||
this._connect(message, target);
|
||||
break;
|
||||
case "Webapps:GetConnections":
|
||||
this._getConnections(message, target);
|
||||
break;
|
||||
case "InterAppConnection:Cancel":
|
||||
this._cancelConnection(message);
|
||||
break;
|
||||
case "InterAppMessagePort:PostMessage":
|
||||
this._postMessage(message);
|
||||
break;
|
||||
case "InterAppMessagePort:Register":
|
||||
this._registerMessagePort(message, target);
|
||||
break;
|
||||
case "InterAppMessagePort:Unregister":
|
||||
this._unregisterMessagePort(message);
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
this._removeTarget(target);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "xpcom-shutdown":
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
Services.obs.removeObserver(this, "inter-app-comm-select-app-result");
|
||||
kMessages.forEach(function(aMsg) {
|
||||
ppmm.removeMessageListener(aMsg, this);
|
||||
}, this);
|
||||
ppmm = null;
|
||||
break;
|
||||
case "inter-app-comm-select-app-result":
|
||||
if (DEBUG) debug("inter-app-comm-select-app-result: " + aData);
|
||||
this._handleSelectcedApps(JSON.parse(aData));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InterAppCommService.init();
|
@ -27,6 +27,7 @@ EXTRA_JS_MODULES += [
|
||||
'AppDownloadManager.jsm',
|
||||
'AppsServiceChild.jsm',
|
||||
'FreeSpaceWatcher.jsm',
|
||||
'InterAppCommService.jsm',
|
||||
'OfflineCacheInstaller.jsm',
|
||||
'PermissionsInstaller.jsm',
|
||||
'PermissionsTable.jsm',
|
||||
|
457
dom/apps/tests/unit/test_inter_app_comm_service.js
Normal file
457
dom/apps/tests/unit/test_inter_app_comm_service.js
Normal file
@ -0,0 +1,457 @@
|
||||
/* 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");
|
||||
|
||||
let 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 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_PAGE_URL = "app://pubApp.gaiamobile.org/handler.html";
|
||||
const SUB_APP_PAGE_URL = "app://subApp.gaiamobile.org/handler.html";
|
||||
|
||||
const KEYWORD = "test";
|
||||
|
||||
function create_message_port_pair(aMessagePortId,
|
||||
aKeyword,
|
||||
aPubManifestURL,
|
||||
aSubManifestURL) {
|
||||
InterAppCommService._messagePortPairs[aMessagePortId] = {
|
||||
keyword: aKeyword,
|
||||
publisher: {
|
||||
manifestURL: aPubManifestURL
|
||||
},
|
||||
subscriber: {
|
||||
manifestURL: aSubManifestURL
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function clear_message_port_pairs() {
|
||||
InterAppCommService._messagePortPairs = {};
|
||||
}
|
||||
|
||||
function register_message_port(aMessagePortId,
|
||||
aManifestURL,
|
||||
aPageURL,
|
||||
aTargetSendAsyncMessage) {
|
||||
let message = {
|
||||
name: "InterAppMessagePort:Register",
|
||||
json: {
|
||||
messagePortID: aMessagePortId,
|
||||
manifestURL: aManifestURL,
|
||||
pageURL: aPageURL
|
||||
},
|
||||
target: {
|
||||
sendAsyncMessage: function(aName, aData) {
|
||||
if (aTargetSendAsyncMessage) {
|
||||
aTargetSendAsyncMessage(aName, aData);
|
||||
}
|
||||
},
|
||||
assertContainApp: function(_manifestURL) {
|
||||
return (aManifestURL == _manifestURL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InterAppCommService.receiveMessage(message);
|
||||
|
||||
return message.target;
|
||||
}
|
||||
|
||||
function register_message_ports(aMessagePortId,
|
||||
aPubTargetSendAsyncMessage,
|
||||
aSubTargetSendAsyncMessage) {
|
||||
let pubTarget = register_message_port(aMessagePortId,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
PUB_APP_PAGE_URL,
|
||||
aPubTargetSendAsyncMessage);
|
||||
|
||||
let subTarget = register_message_port(aMessagePortId,
|
||||
SUB_APP_MANIFEST_URL,
|
||||
SUB_APP_PAGE_URL,
|
||||
aSubTargetSendAsyncMessage);
|
||||
|
||||
return { pubTarget: pubTarget, subTarget: subTarget };
|
||||
}
|
||||
|
||||
function unregister_message_port(aMessagePortId,
|
||||
aManifestURL) {
|
||||
let message = {
|
||||
name: "InterAppMessagePort:Unregister",
|
||||
json: {
|
||||
messagePortID: aMessagePortId,
|
||||
manifestURL: aManifestURL
|
||||
},
|
||||
target: {
|
||||
assertContainApp: function(_manifestURL) {
|
||||
return (aManifestURL == _manifestURL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InterAppCommService.receiveMessage(message);
|
||||
}
|
||||
|
||||
function remove_target(aTarget) {
|
||||
let message = {
|
||||
name: "child-process-shutdown",
|
||||
target: aTarget
|
||||
};
|
||||
|
||||
InterAppCommService.receiveMessage(message);
|
||||
}
|
||||
|
||||
function post_message(aMessagePortId,
|
||||
aManifestURL,
|
||||
aMessage) {
|
||||
let message = {
|
||||
name: "InterAppMessagePort:PostMessage",
|
||||
json: {
|
||||
messagePortID: aMessagePortId,
|
||||
manifestURL: aManifestURL,
|
||||
message: aMessage
|
||||
},
|
||||
target: {
|
||||
assertContainApp: function(_manifestURL) {
|
||||
return (aManifestURL == _manifestURL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InterAppCommService.receiveMessage(message);
|
||||
}
|
||||
|
||||
function create_allowed_connections(aKeyword,
|
||||
aPubManifestURL,
|
||||
aSubManifestURL) {
|
||||
let allowedPubAppManifestURLs =
|
||||
InterAppCommService._allowedConnections[aKeyword] = {};
|
||||
|
||||
allowedPubAppManifestURLs[aPubManifestURL] = [aSubManifestURL];
|
||||
}
|
||||
|
||||
function clear_allowed_connections() {
|
||||
InterAppCommService._allowedConnections = {};
|
||||
}
|
||||
|
||||
function get_connections(aManifestURL,
|
||||
aOuterWindowID,
|
||||
aRequestID,
|
||||
aTargetSendAsyncMessage) {
|
||||
let message = {
|
||||
name: "Webapps:GetConnections",
|
||||
json: {
|
||||
manifestURL: aManifestURL,
|
||||
outerWindowID: aOuterWindowID,
|
||||
requestID: aRequestID
|
||||
},
|
||||
target: {
|
||||
sendAsyncMessage: function(aName, aData) {
|
||||
if (aTargetSendAsyncMessage) {
|
||||
aTargetSendAsyncMessage(aName, aData);
|
||||
}
|
||||
},
|
||||
assertContainApp: function(_manifestURL) {
|
||||
return (aManifestURL == _manifestURL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InterAppCommService.receiveMessage(message);
|
||||
}
|
||||
|
||||
function cancel_connections(aManifestURL,
|
||||
aKeyword,
|
||||
aPubManifestURL,
|
||||
aSubManifestURL) {
|
||||
let message = {
|
||||
name: "InterAppConnection:Cancel",
|
||||
json: {
|
||||
manifestURL: aManifestURL,
|
||||
keyword: aKeyword,
|
||||
pubAppManifestURL: aPubManifestURL,
|
||||
subAppManifestURL: aSubManifestURL
|
||||
},
|
||||
target: {
|
||||
assertContainApp: function(_manifestURL) {
|
||||
return (aManifestURL == _manifestURL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
InterAppCommService.receiveMessage(message);
|
||||
}
|
||||
|
||||
add_test(function test_registerMessagePort() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
let targets = register_message_ports(MESSAGE_PORT_ID);
|
||||
|
||||
let messagePortPair = InterAppCommService._messagePortPairs[MESSAGE_PORT_ID];
|
||||
|
||||
do_check_eq(PUB_APP_PAGE_URL, messagePortPair.publisher.pageURL);
|
||||
do_check_eq(SUB_APP_PAGE_URL, messagePortPair.subscriber.pageURL);
|
||||
|
||||
do_check_true(targets.pubTarget === messagePortPair.publisher.target);
|
||||
do_check_true(targets.subTarget === messagePortPair.subscriber.target);
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_failToRegisterMessagePort() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
let targets = register_message_ports(FAKE_MESSAGE_PORT_ID);
|
||||
|
||||
let messagePortPair = InterAppCommService._messagePortPairs[MESSAGE_PORT_ID];
|
||||
|
||||
// Because it failed to register, the page URLs and targets don't exist.
|
||||
do_check_true(messagePortPair.publisher.pageURL === undefined);
|
||||
do_check_true(messagePortPair.subscriber.pageURL === undefined);
|
||||
|
||||
do_check_true(messagePortPair.publisher.target === undefined);
|
||||
do_check_true(messagePortPair.subscriber.target === undefined);
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_unregisterMessagePort() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
register_message_ports(MESSAGE_PORT_ID);
|
||||
|
||||
unregister_message_port(MESSAGE_PORT_ID, PUB_APP_MANIFEST_URL);
|
||||
|
||||
do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
|
||||
=== undefined);
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_failToUnregisterMessagePort() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
register_message_ports(MESSAGE_PORT_ID);
|
||||
|
||||
unregister_message_port(FAKE_MESSAGE_PORT_ID, PUB_APP_MANIFEST_URL);
|
||||
|
||||
// Because it failed to unregister, the entry still exists.
|
||||
do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
|
||||
!== undefined);
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_removeTarget() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
let targets = register_message_ports(MESSAGE_PORT_ID);
|
||||
|
||||
remove_target(targets.pubTarget);
|
||||
|
||||
do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
|
||||
=== undefined);
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_postMessage() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
let countPubAppOnMessage = 0;
|
||||
function pubAppOnMessage(aName, aData) {
|
||||
countPubAppOnMessage++;
|
||||
|
||||
do_check_eq(aName, "InterAppMessagePort:OnMessage");
|
||||
do_check_eq(aData.manifestURL, PUB_APP_MANIFEST_URL);
|
||||
do_check_eq(aData.pageURL, PUB_APP_PAGE_URL);
|
||||
do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
|
||||
|
||||
if (countPubAppOnMessage == 1) {
|
||||
do_check_eq(aData.message.text, "sub app says world");
|
||||
|
||||
post_message(MESSAGE_PORT_ID,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
{ text: "pub app says hello again" });
|
||||
|
||||
} else if (countPubAppOnMessage == 2) {
|
||||
do_check_eq(aData.message.text, "sub app says world again");
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
} else {
|
||||
do_throw("pub app receives an unexpected message")
|
||||
}
|
||||
};
|
||||
|
||||
let countSubAppOnMessage = 0;
|
||||
function subAppOnMessage(aName, aData) {
|
||||
countSubAppOnMessage++;
|
||||
|
||||
do_check_eq(aName, "InterAppMessagePort:OnMessage");
|
||||
do_check_eq(aData.manifestURL, SUB_APP_MANIFEST_URL);
|
||||
do_check_eq(aData.pageURL, SUB_APP_PAGE_URL);
|
||||
do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
|
||||
|
||||
if (countSubAppOnMessage == 1) {
|
||||
do_check_eq(aData.message.text, "pub app says hello");
|
||||
|
||||
post_message(MESSAGE_PORT_ID,
|
||||
SUB_APP_MANIFEST_URL,
|
||||
{ text: "sub app says world" });
|
||||
|
||||
} else if (countSubAppOnMessage == 2) {
|
||||
do_check_eq(aData.message.text, "pub app says hello again");
|
||||
|
||||
post_message(MESSAGE_PORT_ID,
|
||||
SUB_APP_MANIFEST_URL,
|
||||
{ text: "sub app says world again" });
|
||||
} else {
|
||||
do_throw("sub app receives an unexpected message")
|
||||
}
|
||||
};
|
||||
|
||||
register_message_ports(MESSAGE_PORT_ID, pubAppOnMessage, subAppOnMessage);
|
||||
|
||||
post_message(MESSAGE_PORT_ID,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
{ text: "pub app says hello" });
|
||||
});
|
||||
|
||||
add_test(function test_registerMessagePort_with_queued_messages() {
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
register_message_port(MESSAGE_PORT_ID,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
PUB_APP_PAGE_URL);
|
||||
|
||||
post_message(MESSAGE_PORT_ID,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
{ text: "pub app says hello" });
|
||||
|
||||
post_message(MESSAGE_PORT_ID,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
{ text: "pub app says hello again" });
|
||||
|
||||
let countSubAppOnMessage = 0;
|
||||
function subAppOnMessage(aName, aData) {
|
||||
countSubAppOnMessage++;
|
||||
|
||||
do_check_eq(aName, "InterAppMessagePort:OnMessage");
|
||||
do_check_eq(aData.manifestURL, SUB_APP_MANIFEST_URL);
|
||||
do_check_eq(aData.pageURL, SUB_APP_PAGE_URL);
|
||||
do_check_eq(aData.messagePortID, MESSAGE_PORT_ID);
|
||||
|
||||
if (countSubAppOnMessage == 1) {
|
||||
do_check_eq(aData.message.text, "pub app says hello");
|
||||
} else if (countSubAppOnMessage == 2) {
|
||||
do_check_eq(aData.message.text, "pub app says hello again");
|
||||
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
} else {
|
||||
do_throw("sub app receives an unexpected message")
|
||||
}
|
||||
};
|
||||
|
||||
register_message_port(MESSAGE_PORT_ID,
|
||||
SUB_APP_MANIFEST_URL,
|
||||
SUB_APP_PAGE_URL,
|
||||
subAppOnMessage);
|
||||
});
|
||||
|
||||
add_test(function test_getConnections() {
|
||||
create_allowed_connections(KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
function onGetConnections(aName, aData) {
|
||||
do_check_eq(aName, "Webapps:GetConnections:Return:OK");
|
||||
do_check_eq(aData.oid, OUTER_WINDOW_ID);
|
||||
do_check_eq(aData.requestID, REQUEST_ID);
|
||||
|
||||
let connections = aData.connections;
|
||||
do_check_eq(connections.length, 1);
|
||||
do_check_eq(connections[0].keyword, KEYWORD);
|
||||
do_check_eq(connections[0].pubAppManifestURL, PUB_APP_MANIFEST_URL);
|
||||
do_check_eq(connections[0].subAppManifestURL, SUB_APP_MANIFEST_URL);
|
||||
|
||||
clear_allowed_connections();
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
get_connections(PUB_APP_MANIFEST_URL,
|
||||
OUTER_WINDOW_ID,
|
||||
REQUEST_ID,
|
||||
onGetConnections);
|
||||
});
|
||||
|
||||
add_test(function test_cancelConnection() {
|
||||
create_allowed_connections(KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
create_message_port_pair(MESSAGE_PORT_ID,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
register_message_ports(MESSAGE_PORT_ID);
|
||||
|
||||
cancel_connections(PUB_APP_MANIFEST_URL,
|
||||
KEYWORD,
|
||||
PUB_APP_MANIFEST_URL,
|
||||
SUB_APP_MANIFEST_URL);
|
||||
|
||||
do_check_true(InterAppCommService._allowedConnections[KEYWORD]
|
||||
=== undefined);
|
||||
|
||||
do_check_true(InterAppCommService._messagePortPairs[MESSAGE_PORT_ID]
|
||||
=== undefined);
|
||||
|
||||
clear_allowed_connections();
|
||||
clear_message_port_pairs();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
|
||||
run_next_test();
|
||||
}
|
@ -2,4 +2,5 @@
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_inter_app_comm_service.js]
|
||||
[test_manifestSanitizer.js]
|
||||
|
@ -64,7 +64,8 @@ class B2GDesktopReftest(RefTest):
|
||||
|
||||
log.info("%s | Running tests: start.", os.path.basename(__file__))
|
||||
cmd, args = self.build_command_line(options.app,
|
||||
ignore_window_size=options.ignoreWindowSize)
|
||||
ignore_window_size=options.ignoreWindowSize,
|
||||
browser_arg=options.browser_arg)
|
||||
self.runner = FirefoxRunner(profile=self.profile,
|
||||
binary=cmd,
|
||||
cmdargs=args,
|
||||
@ -123,10 +124,14 @@ class B2GDesktopReftest(RefTest):
|
||||
profile.set_preferences(prefs)
|
||||
return profile
|
||||
|
||||
def build_command_line(self, app, ignore_window_size=False):
|
||||
def build_command_line(self, app, ignore_window_size=False,
|
||||
browser_arg=None):
|
||||
cmd = os.path.abspath(app)
|
||||
args = ['-marionette']
|
||||
|
||||
if browser_arg:
|
||||
args += [browser_arg]
|
||||
|
||||
if not ignore_window_size:
|
||||
args.extend(['--screen', '800x1000'])
|
||||
return cmd, args
|
||||
|
@ -31,6 +31,11 @@ class B2GOptions(ReftestOptions):
|
||||
|
||||
ReftestOptions.__init__(self, automation)
|
||||
|
||||
self.add_option("--browser-arg", action="store",
|
||||
type = "string", dest = "browser_arg",
|
||||
help = "Optional command-line arg to pass to the browser")
|
||||
defaults["browser_arg"] = None
|
||||
|
||||
self.add_option("--b2gpath", action="store",
|
||||
type = "string", dest = "b2gPath",
|
||||
help = "path to B2G repo or qemu dir")
|
||||
|
@ -8,12 +8,13 @@ config = {
|
||||
"--total-chunks=%(total_chunks)s", "--this-chunk=%(this_chunk)s",
|
||||
"--profile=%(gaia_profile)s", "--app=%(application)s", "--desktop",
|
||||
"--utility-path=%(utility_path)s", "--certificate-path=%(cert_path)s",
|
||||
"--symbols-path=%(symbols_path)s",
|
||||
"--symbols-path=%(symbols_path)s", "--browser-arg=%(browser_arg)s",
|
||||
"--quiet"
|
||||
],
|
||||
|
||||
"reftest_options": [
|
||||
"--desktop", "--profile=%(gaia_profile)s", "--appname=%(application)s",
|
||||
"--symbols-path=%(symbols_path)s", "%(test_manifest)s",
|
||||
"--browser-arg=%(browser_arg)s", "--symbols-path=%(symbols_path)s",
|
||||
"%(test_manifest)s"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user