1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
9 this.EXPORTED_SYMBOLS = ["InterAppCommService"];
11 Cu.import("resource://gre/modules/Services.jsm");
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
13 Cu.import("resource://gre/modules/AppsUtils.jsm");
16 function debug(aMsg) {
17 dump("-- InterAppCommService: " + Date.now() + ": " + aMsg + "\n");
20 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
21 "@mozilla.org/AppsService;1",
24 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
25 "@mozilla.org/parentprocessmessagemanager;1",
26 "nsIMessageBroadcaster");
28 XPCOMUtils.defineLazyServiceGetter(this, "UUIDGenerator",
29 "@mozilla.org/uuid-generator;1",
32 XPCOMUtils.defineLazyServiceGetter(this, "messenger",
33 "@mozilla.org/system-message-internal;1",
34 "nsISystemMessagesInternal");
36 const kMessages =["Webapps:Connect",
37 "Webapps:GetConnections",
38 "InterAppConnection:Cancel",
39 "InterAppMessagePort:PostMessage",
40 "InterAppMessagePort:Register",
41 "InterAppMessagePort:Unregister",
42 "child-process-shutdown"];
45 * This module contains helpers for Inter-App Communication API [1] related
46 * purposes, which plays the role of the central service receiving messages
47 * from and interacting with the content processes.
49 * [1] https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
52 this.InterAppCommService = {
54 Services.obs.addObserver(this, "xpcom-shutdown", false);
55 Services.obs.addObserver(this, "webapps-clear-data", false);
57 kMessages.forEach(function(aMsg) {
58 ppmm.addMessageListener(aMsg, this);
61 // This matrix is used for saving the inter-app connection info registered in
62 // the app manifest. The object literal is defined as below:
66 // "subAppManifestURL1": {
67 // /* subscribed info */
69 // "subAppManifestURL2": {
70 // /* subscribed info */
75 // "subAppManifestURL3": {
76 // /* subscribed info */
87 // "app://subApp1.gaiamobile.org/manifest.webapp": {
88 // pageURL: "app://subApp1.gaiamobile.org/handler.html",
89 // description: "blah blah",
92 // "app://subApp2.gaiamobile.org/manifest.webapp": {
93 // pageURL: "app://subApp2.gaiamobile.org/handler.html",
94 // description: "blah blah",
99 // "app://subApp3.gaiamobile.org/manifest.webapp": {
100 // pageURL: "app://subApp3.gaiamobile.org/handler.html",
101 // description: "blah blah",
107 // TODO Bug 908999 - Update registered connections when app gets uninstalled.
108 this._registeredConnections = {};
110 // This matrix is used for saving the permitted connections, which allows
111 // the messaging between publishers and subscribers. The object literal is
116 // "pubAppManifestURL1": [
117 // "subAppManifestURL1",
118 // "subAppManifestURL2",
121 // "pubAppManifestURL2": [
122 // "subAppManifestURL3",
123 // "subAppManifestURL4",
129 // "pubAppManifestURL3": [
130 // "subAppManifestURL5",
142 // "app://pubApp1.gaiamobile.org/manifest.webapp": [
143 // "app://subApp1.gaiamobile.org/manifest.webapp",
144 // "app://subApp2.gaiamobile.org/manifest.webapp"
146 // "app://pubApp2.gaiamobile.org/manifest.webapp": [
147 // "app://subApp3.gaiamobile.org/manifest.webapp",
148 // "app://subApp4.gaiamobile.org/manifest.webapp"
152 // "app://pubApp3.gaiamobile.org/manifest.webapp": [
153 // "app://subApp5.gaiamobile.org/manifest.webapp",
158 // TODO Bug 908999 - Update allowed connections when app gets uninstalled.
159 this._allowedConnections = {};
161 // This matrix is used for saving the caller info from the content process,
162 // which is indexed by a random UUID, to know where to return the promise
163 // resolvser's callback when the prompt UI for allowing connections returns.
164 // An example of the object literal is shown as below:
168 // outerWindowID: 12,
170 // target: pubAppTarget1
173 // outerWindowID: 56,
175 // target: pubAppTarget2
179 // where |outerWindowID| is the ID of the window requesting the connection,
180 // |requestID| is the ID specifying the promise resolver to return,
181 // |target| is the target of the process requesting the connection.
182 this._promptUICallers = {};
184 // This matrix is used for saving the pair of message ports, which is indexed
185 // by a random UUID, so that each port can know whom it should talk to.
186 // An example of the object literal is shown as below:
190 // keyword: "keyword1",
192 // manifestURL: "app://pubApp1.gaiamobile.org/manifest.webapp",
193 // target: pubAppTarget1,
194 // pageURL: "app://pubApp1.gaiamobile.org/caller.html",
195 // messageQueue: [...]
198 // manifestURL: "app://subApp1.gaiamobile.org/manifest.webapp",
199 // target: subAppTarget1,
200 // pageURL: "app://pubApp1.gaiamobile.org/handler.html",
201 // messageQueue: [...]
205 // keyword: "keyword2",
207 // manifestURL: "app://pubApp2.gaiamobile.org/manifest.webapp",
208 // target: pubAppTarget2,
209 // pageURL: "app://pubApp2.gaiamobile.org/caller.html",
210 // messageQueue: [...]
213 // manifestURL: "app://subApp2.gaiamobile.org/manifest.webapp",
214 // target: subAppTarget2,
215 // pageURL: "app://pubApp2.gaiamobile.org/handler.html",
216 // messageQueue: [...]
220 this._messagePortPairs = {};
224 * Registration of a page that wants to be connected to other apps through
225 * the Inter-App Communication API.
227 * @param aKeyword The connection's keyword.
228 * @param aHandlerPageURI The URI of the handler's page.
229 * @param aManifestURI The webapp's manifest URI.
230 * @param aDescription The connection's description.
231 * @param aRules The connection's rules.
233 registerConnection: function(aKeyword, aHandlerPageURI, aManifestURI,
234 aDescription, aRules) {
235 let manifestURL = aManifestURI.spec;
236 let pageURL = aHandlerPageURI.spec;
239 debug("registerConnection: aKeyword: " + aKeyword +
240 " manifestURL: " + manifestURL + " pageURL: " + pageURL +
241 " aDescription: " + aDescription +
242 " aRules.minimumAccessLevel: " + aRules.minimumAccessLevel +
243 " aRules.manifestURLs: " + aRules.manifestURLs +
244 " aRules.installOrigins: " + aRules.installOrigins);
247 let subAppManifestURLs = this._registeredConnections[aKeyword];
248 if (!subAppManifestURLs) {
249 subAppManifestURLs = this._registeredConnections[aKeyword] = {};
252 subAppManifestURLs[manifestURL] = {
254 description: aDescription,
256 manifestURL: manifestURL
260 _matchMinimumAccessLevel: function(aRules, aAppStatus) {
261 if (!aRules || !aRules.minimumAccessLevel) {
263 debug("rules.minimumAccessLevel is not available. No need to match.");
268 let minAccessLevel = aRules.minimumAccessLevel;
269 switch (minAccessLevel) {
271 if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_INSTALLED ||
272 aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
273 aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
278 if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED ||
279 aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
284 if (aAppStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
291 debug("rules.minimumAccessLevel is not matched!" +
292 " minAccessLevel: " + minAccessLevel +
293 " aAppStatus : " + aAppStatus);
298 _matchManifestURLs: function(aRules, aManifestURL) {
299 if (!aRules || !Array.isArray(aRules.manifestURLs)) {
301 debug("rules.manifestURLs is not available. No need to match.");
306 let manifestURLs = aRules.manifestURLs;
307 if (manifestURLs.indexOf(aManifestURL) != -1) {
312 debug("rules.manifestURLs is not matched!" +
313 " manifestURLs: " + manifestURLs +
314 " aManifestURL : " + aManifestURL);
319 _matchInstallOrigins: function(aRules, aInstallOrigin) {
320 if (!aRules || !Array.isArray(aRules.installOrigins)) {
322 debug("rules.installOrigins is not available. No need to match.");
327 let installOrigins = aRules.installOrigins;
328 if (installOrigins.indexOf(aInstallOrigin) != -1) {
333 debug("rules.installOrigins is not matched!" +
334 " installOrigins: " + installOrigins +
335 " installOrigin : " + aInstallOrigin);
340 _matchRules: function(aPubAppManifestURL, aPubRules,
341 aSubAppManifestURL, aSubRules) {
342 let pubApp = appsService.getAppByManifestURL(aPubAppManifestURL);
343 let subApp = appsService.getAppByManifestURL(aSubAppManifestURL);
345 let isPubAppCertified =
346 (pubApp.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED);
348 let isSubAppCertified =
349 (subApp.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED);
351 // TODO Bug 907068 In the initiative step, we only expose this API to
352 // certified apps to meet the time line. Eventually, we need to make
353 // it available for the non-certified apps as well. For now, only the
354 // certified apps can match the rules.
355 if (!isPubAppCertified || !isSubAppCertified) {
357 debug("Only certified apps are allowed to do connections.");
362 if (!aPubRules && !aSubRules) {
364 debug("No rules for publisher and subscriber. No need to match.");
369 // Check minimumAccessLevel.
370 if (!this._matchMinimumAccessLevel(aPubRules, subApp.appStatus) ||
371 !this._matchMinimumAccessLevel(aSubRules, pubApp.appStatus)) {
375 // Check manifestURLs.
376 if (!this._matchManifestURLs(aPubRules, aSubAppManifestURL) ||
377 !this._matchManifestURLs(aSubRules, aPubAppManifestURL)) {
381 // Check installOrigins. Note that we only check the install origin for the
382 // non-certified app, because the certified app doesn't have install origin.
383 if ((!isSubAppCertified &&
384 !this._matchInstallOrigins(aPubRules, subApp.installOrigin)) ||
385 (!isPubAppCertified &&
386 !this._matchInstallOrigins(aSubRules, pubApp.installOrigin))) {
390 if (DEBUG) debug("All rules are matched.");
394 _dispatchMessagePorts: function(aKeyword, aPubAppManifestURL,
395 aAllowedSubAppManifestURLs,
396 aTarget, aOuterWindowID, aRequestID) {
398 debug("_dispatchMessagePorts: aKeyword: " + aKeyword +
399 " aPubAppManifestURL: " + aPubAppManifestURL +
400 " aAllowedSubAppManifestURLs: " + aAllowedSubAppManifestURLs);
403 if (aAllowedSubAppManifestURLs.length == 0) {
404 if (DEBUG) debug("No apps are allowed to connect. Returning.");
405 aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
406 { oid: aOuterWindowID, requestID: aRequestID });
410 let subAppManifestURLs = this._registeredConnections[aKeyword];
411 if (!subAppManifestURLs) {
412 if (DEBUG) debug("No apps are subscribed to connect. Returning.");
413 aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
414 { oid: aOuterWindowID, requestID: aRequestID });
418 let messagePortIDs = [];
419 aAllowedSubAppManifestURLs.forEach(function(aAllowedSubAppManifestURL) {
420 let subscribedInfo = subAppManifestURLs[aAllowedSubAppManifestURL];
421 if (!subscribedInfo) {
423 debug("The sunscribed info is not available. Skipping: " +
424 aAllowedSubAppManifestURL);
429 // The message port ID is aimed for identifying the coupling targets
430 // to deliver messages with each other. This ID is centrally generated
431 // by the parent and dispatched to both the sender and receiver ends
432 // for creating their own message ports respectively.
433 let messagePortID = UUIDGenerator.generateUUID().toString();
434 this._messagePortPairs[messagePortID] = {
437 manifestURL: aPubAppManifestURL
440 manifestURL: aAllowedSubAppManifestURL
444 // Fire system message to deliver the message port to the subscriber.
445 messenger.sendMessage("connection",
447 messagePortID: messagePortID },
448 Services.io.newURI(subscribedInfo.pageURL, null, null),
449 Services.io.newURI(subscribedInfo.manifestURL, null, null));
451 messagePortIDs.push(messagePortID);
454 if (messagePortIDs.length == 0) {
455 if (DEBUG) debug("No apps are subscribed to connect. Returning.");
456 aTarget.sendAsyncMessage("Webapps:Connect:Return:KO",
457 { oid: aOuterWindowID, requestID: aRequestID });
461 // Return the message port IDs to open the message ports for the publisher.
462 if (DEBUG) debug("messagePortIDs: " + messagePortIDs);
463 aTarget.sendAsyncMessage("Webapps:Connect:Return:OK",
465 messagePortIDs: messagePortIDs,
466 oid: aOuterWindowID, requestID: aRequestID });
470 * Fetch the subscribers that are currently allowed to connect.
472 * @param aKeyword The connection's keyword.
473 * @param aPubAppManifestURL The manifest URL of the publisher.
475 * @param return an array of manifest URLs of the subscribers.
477 _getAllowedSubAppManifestURLs: function(aKeyword, aPubAppManifestURL) {
478 let allowedPubAppManifestURLs = this._allowedConnections[aKeyword];
479 if (!allowedPubAppManifestURLs) {
483 let allowedSubAppManifestURLs =
484 allowedPubAppManifestURLs[aPubAppManifestURL];
485 if (!allowedSubAppManifestURLs) {
489 return allowedSubAppManifestURLs;
493 * Add the newly selected apps into the allowed connections and return the
494 * aggregated allowed connections.
496 * @param aKeyword The connection's keyword.
497 * @param aPubAppManifestURL The manifest URL of the publisher.
498 * @param aSelectedApps An array of the subscribers' information.
500 * @param return an array of manifest URLs of the subscribers.
502 _addSelectedApps: function(aKeyword, aPubAppManifestURL, aSelectedApps) {
503 let allowedPubAppManifestURLs = this._allowedConnections[aKeyword];
505 // Add a new entry for |aKeyword|.
506 if (!allowedPubAppManifestURLs) {
507 allowedPubAppManifestURLs = this._allowedConnections[aKeyword] = {};
510 let allowedSubAppManifestURLs =
511 allowedPubAppManifestURLs[aPubAppManifestURL];
513 // Add a new entry for |aPubAppManifestURL|.
514 if (!allowedSubAppManifestURLs) {
515 allowedSubAppManifestURLs =
516 allowedPubAppManifestURLs[aPubAppManifestURL] = [];
519 // Add the selected apps into the existing set of allowed connections.
520 aSelectedApps.forEach(function(aSelectedApp) {
521 let allowedSubAppManifestURL = aSelectedApp.manifestURL;
522 if (allowedSubAppManifestURLs.indexOf(allowedSubAppManifestURL) == -1) {
523 allowedSubAppManifestURLs.push(allowedSubAppManifestURL);
527 return allowedSubAppManifestURLs;
530 _connect: function(aMessage, aTarget) {
531 let keyword = aMessage.keyword;
532 let pubRules = aMessage.rules;
533 let pubAppManifestURL = aMessage.manifestURL;
534 let outerWindowID = aMessage.outerWindowID;
535 let requestID = aMessage.requestID;
537 let subAppManifestURLs = this._registeredConnections[keyword];
538 if (!subAppManifestURLs) {
540 debug("No apps are subscribed for this connection. Returning.");
542 this._dispatchMessagePorts(keyword, pubAppManifestURL, [],
543 aTarget, outerWindowID, requestID);
547 // Fetch the apps that are currently allowed to connect, so that users
548 // don't need to select/allow them again, which means we only pop up the
549 // prompt UI for the *new* connections.
550 let allowedSubAppManifestURLs =
551 this._getAllowedSubAppManifestURLs(keyword, pubAppManifestURL);
553 // Check rules to see if a subscribed app is allowed to connect.
554 let appsToSelect = [];
555 for (let subAppManifestURL in subAppManifestURLs) {
556 if (allowedSubAppManifestURLs.indexOf(subAppManifestURL) != -1) {
558 debug("Don't need to select again. Skipping: " + subAppManifestURL);
563 // Only rule-matched publishers/subscribers are allowed to connect.
564 let subscribedInfo = subAppManifestURLs[subAppManifestURL];
565 let subRules = subscribedInfo.rules;
568 this._matchRules(pubAppManifestURL, pubRules,
569 subAppManifestURL, subRules);
572 debug("Rules are not matched. Skipping: " + subAppManifestURL);
578 manifestURL: subAppManifestURL,
579 description: subscribedInfo.description
583 if (appsToSelect.length == 0) {
585 debug("No additional apps need to be selected for this connection. " +
586 "Just dispatch message ports for the existing connections.");
589 this._dispatchMessagePorts(keyword, pubAppManifestURL,
590 allowedSubAppManifestURLs,
591 aTarget, outerWindowID, requestID);
595 // Remember the caller info with an UUID so that we can know where to
596 // return the promise resolver's callback when the prompt UI returns.
597 let callerID = UUIDGenerator.generateUUID().toString();
598 this._promptUICallers[callerID] = {
599 outerWindowID: outerWindowID,
600 requestID: requestID,
604 let glue = Cc["@mozilla.org/dom/apps/inter-app-comm-ui-glue;1"]
605 .createInstance(Ci.nsIInterAppCommUIGlue);
607 glue.selectApps(callerID, pubAppManifestURL, keyword, appsToSelect).then(
609 this._handleSelectedApps(aData);
613 debug("Error occurred in the UI glue component. " + aError)
616 // Resolve the caller as if there were no selected apps.
617 this._handleSelectedApps({ callerID: callerID,
619 manifestURL: pubAppManifestURL,
625 debug("Error! The UI glue component is not implemented.")
628 // Resolve the caller as if there were no selected apps.
629 this._handleSelectedApps({ callerID: callerID,
631 manifestURL: pubAppManifestURL,
636 _getConnections: function(aMessage, aTarget) {
637 let outerWindowID = aMessage.outerWindowID;
638 let requestID = aMessage.requestID;
640 let connections = [];
641 for (let keyword in this._allowedConnections) {
642 let allowedPubAppManifestURLs = this._allowedConnections[keyword];
643 for (let allowedPubAppManifestURL in allowedPubAppManifestURLs) {
644 let allowedSubAppManifestURLs =
645 allowedPubAppManifestURLs[allowedPubAppManifestURL];
646 allowedSubAppManifestURLs.forEach(function(allowedSubAppManifestURL) {
647 connections.push({ keyword: keyword,
648 pubAppManifestURL: allowedPubAppManifestURL,
649 subAppManifestURL: allowedSubAppManifestURL });
654 aTarget.sendAsyncMessage("Webapps:GetConnections:Return:OK",
655 { connections: connections,
656 oid: outerWindowID, requestID: requestID });
659 _cancelConnection: function(aMessage) {
660 let keyword = aMessage.keyword;
661 let pubAppManifestURL = aMessage.pubAppManifestURL;
662 let subAppManifestURL = aMessage.subAppManifestURL;
664 let allowedPubAppManifestURLs = this._allowedConnections[keyword];
665 if (!allowedPubAppManifestURLs) {
666 if (DEBUG) debug("keyword is not found: " + keyword);
670 let allowedSubAppManifestURLs =
671 allowedPubAppManifestURLs[pubAppManifestURL];
672 if (!allowedSubAppManifestURLs) {
673 if (DEBUG) debug("publisher is not found: " + pubAppManifestURL);
677 let index = allowedSubAppManifestURLs.indexOf(subAppManifestURL);
679 if (DEBUG) debug("subscriber is not found: " + subAppManifestURL);
683 if (DEBUG) debug("Cancelling the connection.");
684 allowedSubAppManifestURLs.splice(index, 1);
686 // Clean up the parent entries if needed.
687 if (allowedSubAppManifestURLs.length == 0) {
688 delete allowedPubAppManifestURLs[pubAppManifestURL];
689 if (Object.keys(allowedPubAppManifestURLs).length == 0) {
690 delete this._allowedConnections[keyword];
694 if (DEBUG) debug("Unregistering message ports based on this connection.");
695 let messagePortIDs = [];
696 for (let messagePortID in this._messagePortPairs) {
697 let pair = this._messagePortPairs[messagePortID];
698 if (pair.keyword == keyword &&
699 pair.publisher.manifestURL == pubAppManifestURL &&
700 pair.subscriber.manifestURL == subAppManifestURL) {
701 messagePortIDs.push(messagePortID);
704 messagePortIDs.forEach(function(aMessagePortID) {
705 delete this._messagePortPairs[aMessagePortID];
709 _identifyMessagePort: function(aMessagePortID, aManifestURL) {
710 let pair = this._messagePortPairs[aMessagePortID];
713 debug("Error! The message port ID is invalid: " + aMessagePortID +
714 ", which should have been generated by parent.");
719 // Check it the message port is for publisher.
720 if (pair.publisher.manifestURL == aManifestURL) {
721 return { pair: pair, isPublisher: true };
724 // Check it the message port is for subscriber.
725 if (pair.subscriber.manifestURL == aManifestURL) {
726 return { pair: pair, isPublisher: false };
730 debug("Error! The manifest URL is invalid: " + aManifestURL +
731 ", which might be a hacked app.");
736 _registerMessagePort: function(aMessage, aTarget) {
737 let messagePortID = aMessage.messagePortID;
738 let manifestURL = aMessage.manifestURL;
739 let pageURL = aMessage.pageURL;
741 let identity = this._identifyMessagePort(messagePortID, manifestURL);
744 debug("Cannot identify the message port. Failed to register.");
749 if (DEBUG) debug("Registering message port for " + manifestURL);
750 let pair = identity.pair;
751 let isPublisher = identity.isPublisher;
753 let sender = isPublisher ? pair.publisher : pair.subscriber;
754 sender.target = aTarget;
755 sender.pageURL = pageURL;
756 sender.messageQueue = [];
758 // Check if the other port has queued messages. Deliver them if needed.
760 debug("Checking if the other port used to send messages but queued.");
762 let receiver = isPublisher ? pair.subscriber : pair.publisher;
763 if (receiver.messageQueue) {
764 while (receiver.messageQueue.length) {
765 let message = receiver.messageQueue.shift();
766 if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
767 sender.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
769 manifestURL: sender.manifestURL,
770 pageURL: sender.pageURL,
771 messagePortID: messagePortID });
776 _unregisterMessagePort: function(aMessage) {
777 let messagePortID = aMessage.messagePortID;
778 let manifestURL = aMessage.manifestURL;
780 let identity = this._identifyMessagePort(messagePortID, manifestURL);
783 debug("Cannot identify the message port. Failed to unregister.");
789 debug("Unregistering message port for " + manifestURL);
791 delete this._messagePortPairs[messagePortID];
794 _removeTarget: function(aTarget) {
796 if (DEBUG) debug("Error! aTarget cannot be null/undefined in any way.");
800 if (DEBUG) debug("Unregistering message ports based on this target.");
801 let messagePortIDs = [];
802 for (let messagePortID in this._messagePortPairs) {
803 let pair = this._messagePortPairs[messagePortID];
804 if (pair.publisher.target === aTarget ||
805 pair.subscriber.target === aTarget) {
806 messagePortIDs.push(messagePortID);
807 // Send a shutdown message to the part of the pair that is still alive.
808 let actor = pair.publisher.target === aTarget ? pair.subscriber
810 actor.target.sendAsyncMessage("InterAppMessagePort:Shutdown",
811 { manifestURL: actor.manifestURL,
812 pageURL: actor.pageURL,
813 messagePortID: messagePortID });
816 messagePortIDs.forEach(function(aMessagePortID) {
817 delete this._messagePortPairs[aMessagePortID];
821 _postMessage: function(aMessage) {
822 let messagePortID = aMessage.messagePortID;
823 let manifestURL = aMessage.manifestURL;
824 let message = aMessage.message;
826 let identity = this._identifyMessagePort(messagePortID, manifestURL);
828 if (DEBUG) debug("Cannot identify the message port. Failed to post.");
832 let pair = identity.pair;
833 let isPublisher = identity.isPublisher;
835 let receiver = isPublisher ? pair.subscriber : pair.publisher;
836 if (!receiver.target) {
838 debug("The receiver's target is not ready yet. Queuing the message.");
840 let sender = isPublisher ? pair.publisher : pair.subscriber;
841 sender.messageQueue.push(message);
845 if (DEBUG) debug("Delivering message: " + JSON.stringify(message));
846 receiver.target.sendAsyncMessage("InterAppMessagePort:OnMessage",
847 { manifestURL: receiver.manifestURL,
848 pageURL: receiver.pageURL,
849 messagePortID: messagePortID,
853 _handleSelectedApps: function(aData) {
854 let callerID = aData.callerID;
855 let caller = this._promptUICallers[callerID];
857 if (DEBUG) debug("Error! Cannot find the caller.");
861 delete this._promptUICallers[callerID];
863 let outerWindowID = caller.outerWindowID;
864 let requestID = caller.requestID;
865 let target = caller.target;
867 let pubAppManifestURL = aData.manifestURL;
868 let keyword = aData.keyword;
869 let selectedApps = aData.selectedApps;
871 let allowedSubAppManifestURLs;
872 if (selectedApps.length == 0) {
873 // Only do the connections for the existing allowed subscribers because
874 // no new apps are selected to connect.
875 if (DEBUG) debug("No new apps are selected to connect.")
877 allowedSubAppManifestURLs =
878 this._getAllowedSubAppManifestURLs(keyword, pubAppManifestURL);
880 // Do connections for for the existing allowed subscribers and the newly
881 // selected subscribers.
882 if (DEBUG) debug("Some new apps are selected to connect.")
884 allowedSubAppManifestURLs =
885 this._addSelectedApps(keyword, pubAppManifestURL, selectedApps);
888 // Finally, dispatch the message ports for the allowed connections,
889 // including the old connections and the newly selected connection.
890 this._dispatchMessagePorts(keyword, pubAppManifestURL,
891 allowedSubAppManifestURLs,
892 target, outerWindowID, requestID);
895 receiveMessage: function(aMessage) {
896 if (DEBUG) debug("receiveMessage: name: " + aMessage.name);
897 let message = aMessage.json;
898 let target = aMessage.target;
900 // To prevent the hacked child process from sending commands to parent
901 // to do illegal connections, we need to check its manifest URL.
902 if (aMessage.name !== "child-process-shutdown" &&
903 // TODO: fix bug 988142 to re-enable "InterAppMessagePort:Unregister".
904 aMessage.name !== "InterAppMessagePort:Unregister" &&
905 kMessages.indexOf(aMessage.name) != -1) {
906 if (!target.assertContainApp(message.manifestURL)) {
908 debug("Got message from a process carrying illegal manifest URL.");
914 switch (aMessage.name) {
915 case "Webapps:Connect":
916 this._connect(message, target);
918 case "Webapps:GetConnections":
919 this._getConnections(message, target);
921 case "InterAppConnection:Cancel":
922 this._cancelConnection(message);
924 case "InterAppMessagePort:PostMessage":
925 this._postMessage(message);
927 case "InterAppMessagePort:Register":
928 this._registerMessagePort(message, target);
930 case "InterAppMessagePort:Unregister":
931 this._unregisterMessagePort(message);
933 case "child-process-shutdown":
934 this._removeTarget(target);
939 observe: function(aSubject, aTopic, aData) {
941 case "xpcom-shutdown":
942 Services.obs.removeObserver(this, "xpcom-shutdown");
943 Services.obs.removeObserver(this, "webapps-clear-data");
944 kMessages.forEach(function(aMsg) {
945 ppmm.removeMessageListener(aMsg, this);
949 case "webapps-clear-data":
951 aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
954 debug("Error updating registered/allowed connections for an " +
960 // Only update registered/allowed connections for apps.
961 if (params.browserOnly) {
963 debug("Only update registered/allowed connections for apps.");
968 let manifestURL = appsService.getManifestURLByLocalId(params.appId);
971 debug("Error updating registered/allowed connections for an " +
977 // Update registered connections.
978 for (let keyword in this._registeredConnections) {
979 let subAppManifestURLs = this._registeredConnections[keyword];
980 if (subAppManifestURLs[manifestURL]) {
981 delete subAppManifestURLs[manifestURL];
983 debug("Remove " + manifestURL + " from registered connections " +
984 "due to app uninstallation.");
989 // Update allowed connections.
990 for (let keyword in this._allowedConnections) {
991 let allowedPubAppManifestURLs = this._allowedConnections[keyword];
992 if (allowedPubAppManifestURLs[manifestURL]) {
993 delete allowedPubAppManifestURLs[manifestURL];
995 debug("Remove " + manifestURL + " (as a pub app) from allowed " +
996 "connections due to app uninstallation.");
1000 for (let pubAppManifestURL in allowedPubAppManifestURLs) {
1001 let subAppManifestURLs = allowedPubAppManifestURLs[pubAppManifestURL];
1002 for (let i = subAppManifestURLs.length - 1; i >= 0; i--) {
1003 if (subAppManifestURLs[i] === manifestURL) {
1004 subAppManifestURLs.splice(i, 1);
1006 debug("Remove " + manifestURL + " (as a sub app to pub " +
1007 pubAppManifestURL + ") from allowed connections " +
1008 "due to app uninstallation.");
1014 debug("Finish updating registered/allowed connections for an " +
1015 "uninstalled app.");
1021 InterAppCommService.init();