Bug 933136 - Part 2: Add support to notify 'onpeerready' and 'onpeerlost' events from Chrome process. r=yoshi

Add two new P2P IPC messages 'CheckP2PRegistration' and 'NotifyUserAcceptedP2P'
to establish User Communication on P2P UI from content process to chrome process. These IPC messages are needed to support basic PeerToPeer use cases.

--HG--
extra : rebase_source : 0c0815e9ee873fe017bb6bbc91f3505891533769
This commit is contained in:
Siddartha Pothapragada 2013-12-03 13:22:21 -08:00
parent a47318a090
commit f1b8e1f1d2
4 changed files with 304 additions and 6 deletions

View File

@ -51,6 +51,13 @@ const NFC_IPC_MSG_NAMES = [
"NFC:Close" "NFC:Close"
]; ];
const NFC_IPC_PEER_MSG_NAMES = [
"NFC:RegisterPeerTarget",
"NFC:UnregisterPeerTarget",
"NFC:CheckP2PRegistration",
"NFC:NotifyUserAcceptedP2P"
];
XPCOMUtils.defineLazyServiceGetter(this, "ppmm", XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1", "@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageBroadcaster"); "nsIMessageBroadcaster");
@ -78,6 +85,10 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
targetsBySessionTokens: {}, targetsBySessionTokens: {},
sessionTokens: [], sessionTokens: [],
// Manage registered Peer Targets
peerTargetsMap: {},
currentPeerAppId: null,
init: function init(nfc) { init: function init(nfc) {
this.nfc = nfc; this.nfc = nfc;
@ -97,6 +108,10 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
for (let msgname of NFC_IPC_MSG_NAMES) { for (let msgname of NFC_IPC_MSG_NAMES) {
ppmm.addMessageListener(msgname, this); ppmm.addMessageListener(msgname, this);
} }
for (let msgname of NFC_IPC_PEER_MSG_NAMES) {
ppmm.addMessageListener(msgname, this);
}
}, },
_unregisterMessageListeners: function _unregisterMessageListeners() { _unregisterMessageListeners: function _unregisterMessageListeners() {
@ -104,6 +119,10 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
for (let msgname of NFC_IPC_MSG_NAMES) { for (let msgname of NFC_IPC_MSG_NAMES) {
ppmm.removeMessageListener(msgname, this); ppmm.removeMessageListener(msgname, this);
} }
for (let msgname of NFC_IPC_PEER_MSG_NAMES) {
ppmm.removeMessageListener(msgname, this);
}
ppmm = null; ppmm = null;
}, },
@ -171,6 +190,82 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
} }
}, },
registerPeerTarget: function registerPeerTarget(msg) {
let appInfo = msg.json;
// Sanity check on PeerEvent
if (!this.isValidPeerEvent(appInfo.event)) {
return;
}
let targets = this.peerTargetsMap;
let targetInfo = targets[appInfo.appId];
// If the application Id is already registered
if (targetInfo) {
// If the event is not registered
if (targetInfo.event !== appInfo.event) {
// Update the event field ONLY
targetInfo.event |= appInfo.event;
}
// Otherwise event is already registered, return!
return;
}
// Target not registered yet! Add to the target map
// Registered targetInfo target consists of 2 fields (values)
// target : Target to notify the right content for peer notifications
// event : NFC_PEER_EVENT_READY (0x01) Or NFC_PEER_EVENT_LOST (0x02)
let newTargetInfo = { target : msg.target,
event : appInfo.event };
targets[appInfo.appId] = newTargetInfo;
},
unregisterPeerTarget: function unregisterPeerTarget(msg) {
let appInfo = msg.json;
// Sanity check on PeerEvent
if (!this.isValidPeerEvent(appInfo.event)) {
return;
}
let targets = this.peerTargetsMap;
let targetInfo = targets[appInfo.appId];
if (targetInfo) {
// Application Id registered and the event exactly matches.
if (targetInfo.event === appInfo.event) {
// Remove the target from the list of registered targets
delete targets[appInfo.appId]
}
else {
// Otherwise, update the event field ONLY, by removing the event flag
targetInfo.event &= ~appInfo.event;
}
}
},
isRegisteredP2PTarget: function isRegisteredP2PTarget(appId, event) {
let targetInfo = this.peerTargetsMap[appId];
// Check if it is a registered target for the 'event'
return ((targetInfo != null) && (targetInfo.event & event !== 0));
},
notifyPeerEvent: function notifyPeerEvent(appId, event) {
let targetInfo = this.peerTargetsMap[appId];
// Check if the application id is a registeredP2PTarget
if (this.isRegisteredP2PTarget(appId, event)) {
targetInfo.target.sendAsyncMessage("NFC:PeerEvent", {
event: event,
sessionToken: this.nfc.sessionTokenMap[this.nfc._currentSessionId]
});
return;
}
debug("Application ID : " + appId + " is not a registered target" +
"for the event " + event + " notification");
},
isValidPeerEvent: function isValidPeerEvent(event) {
// Valid values : 0x01, 0x02 Or 0x03
return ((event === NFC.NFC_PEER_EVENT_READY) ||
(event === NFC.NFC_PEER_EVENT_LOST) ||
(event === (NFC.NFC_PEER_EVENT_READY | NFC.NFC_PEER_EVENT_LOST)));
},
/** /**
* nsIMessageListener interface methods. * nsIMessageListener interface methods.
*/ */
@ -191,6 +286,24 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
" from a content process with no 'nfc-read' privileges."); " from a content process with no 'nfc-read' privileges.");
return null; return null;
} }
} else if (NFC_IPC_PEER_MSG_NAMES.indexOf(msg.name) != -1) {
if (!msg.target.assertPermission("nfc-write")) {
debug("Nfc Peer message " + msg.name +
" from a content process with no 'nfc-write' privileges.");
return null;
}
// Add extra permission check for two IPC Peer events:
// 'NFC:CheckP2PRegistration' , 'NFC:NotifyUserAcceptedP2P'
if ((msg.name == "NFC:CheckP2PRegistration") ||
(msg.name == "NFC:NotifyUserAcceptedP2P")) {
// ONLY privileged Content can send these two events
if (!msg.target.assertPermission("nfc-manager")) {
debug("NFC message " + message.name +
" from a content process with no 'nfc-manager' privileges.");
return null;
}
}
} else { } else {
debug("Ignoring unknown message type: " + msg.name); debug("Ignoring unknown message type: " + msg.name);
return null; return null;
@ -201,9 +314,33 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
this._registerMessageTarget(this.nfc.sessionTokenMap[this.nfc._currentSessionId], msg.target); this._registerMessageTarget(this.nfc.sessionTokenMap[this.nfc._currentSessionId], msg.target);
debug("Registering target for this SessionToken : " + debug("Registering target for this SessionToken : " +
this.nfc.sessionTokenMap[this.nfc._currentSessionId]); this.nfc.sessionTokenMap[this.nfc._currentSessionId]);
return null; break;
case "NFC:RegisterPeerTarget":
this.registerPeerTarget(msg);
break;
case "NFC:UnregisterPeerTarget":
this.unregisterPeerTarget(msg);
break;
case "NFC:CheckP2PRegistration":
// Check if the application id is a valid registered target.
// (It should have registered for NFC_PEER_EVENT_READY).
let isRegistered = this.isRegisteredP2PTarget(msg.json.appId,
NFC.NFC_PEER_EVENT_READY);
// Remember the current AppId if registered.
this.currentPeerAppId = (isRegistered) ? msg.json.appId : null;
let status = (isRegistered) ? NFC.GECKO_NFC_ERROR_SUCCESS :
NFC.GECKO_NFC_ERROR_GENERIC_FAILURE;
// Notify the content process immediately of the status
msg.target.sendAsyncMessage(msg.name + "Response", {
status: status,
requestId: msg.json.requestId
});
break;
case "NFC:NotifyUserAcceptedP2P":
// Notify the 'NFC_PEER_EVENT_READY' since user has acknowledged
this.notifyPeerEvent(msg.json.appId, NFC.NFC_PEER_EVENT_READY);
break;
} }
return null; return null;
}, },
@ -311,6 +448,7 @@ Nfc.prototype = {
switch (message.type) { switch (message.type) {
case "techDiscovered": case "techDiscovered":
this._currentSessionId = message.sessionId; this._currentSessionId = message.sessionId;
// Check if the session token already exists. If exists, continue to use the same one. // Check if the session token already exists. If exists, continue to use the same one.
// If not, generate a new token. // If not, generate a new token.
if (!this.sessionTokenMap[this._currentSessionId]) { if (!this.sessionTokenMap[this._currentSessionId]) {
@ -320,17 +458,23 @@ Nfc.prototype = {
message.sessionToken = this.sessionTokenMap[this._currentSessionId]; message.sessionToken = this.sessionTokenMap[this._currentSessionId];
// Do not expose the actual session to the content // Do not expose the actual session to the content
delete message.sessionId; delete message.sessionId;
gSystemMessenger.broadcastMessage("nfc-manager-tech-discovered", message); gSystemMessenger.broadcastMessage("nfc-manager-tech-discovered", message);
break; break;
case "techLost": case "techLost":
gMessageManager._unregisterMessageTarget(this.sessionTokenMap[this._currentSessionId], null); gMessageManager._unregisterMessageTarget(this.sessionTokenMap[this._currentSessionId], null);
// Update the upper layers with a session token (alias) // Update the upper layers with a session token (alias)
message.sessionToken = this.sessionTokenMap[this._currentSessionId]; message.sessionToken = this.sessionTokenMap[this._currentSessionId];
// Do not expose the actual session to the content // Do not expose the actual session to the content
delete message.sessionId; delete message.sessionId;
gSystemMessenger.broadcastMessage("nfc-manager-tech-lost", message); gSystemMessenger.broadcastMessage("nfc-manager-tech-lost", message);
// Notify 'PeerLost' to appropriate registered target, if any
gMessageManager.notifyPeerEvent(this.currentPeerAppId, NFC.NFC_PEER_EVENT_LOST);
delete this.sessionTokenMap[this._currentSessionId]; delete this.sessionTokenMap[this._currentSessionId];
this._currentSessionId = null; this._currentSessionId = null;
this.currentPeerAppId = null;
break; break;
case "ConfigResponse": case "ConfigResponse":
gSystemMessenger.broadcastMessage("nfc-powerlevel-change", message); gSystemMessenger.broadcastMessage("nfc-powerlevel-change", message);

View File

@ -47,7 +47,9 @@ const NFC_IPC_MSG_NAMES = [
"NFC:GetDetailsNDEFResponse", "NFC:GetDetailsNDEFResponse",
"NFC:MakeReadOnlyNDEFResponse", "NFC:MakeReadOnlyNDEFResponse",
"NFC:ConnectResponse", "NFC:ConnectResponse",
"NFC:CloseResponse" "NFC:CloseResponse",
"NFC:CheckP2PRegistrationResponse",
"NFC:PeerEvent"
]; ];
XPCOMUtils.defineLazyServiceGetter(this, "cpmm", XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
@ -59,6 +61,10 @@ function NfcContentHelper() {
Services.obs.addObserver(this, "xpcom-shutdown", false); Services.obs.addObserver(this, "xpcom-shutdown", false);
this._requestMap = []; this._requestMap = [];
// Maintains an array of PeerEvent related callbacks, mainly
// one for 'peerReady' and another for 'peerLost'.
this.peerEventsCallbackMap = {};
} }
NfcContentHelper.prototype = { NfcContentHelper.prototype = {
@ -75,6 +81,7 @@ NfcContentHelper.prototype = {
}), }),
_requestMap: null, _requestMap: null,
peerEventsCallbackMap: null,
/* TODO: Bug 815526: This is a limitation when a DOMString is used in sequences of Moz DOM Objects. /* TODO: Bug 815526: This is a limitation when a DOMString is used in sequences of Moz DOM Objects.
* Strings such as 'type', 'id' 'payload' will not be acccessible to NfcWorker. * Strings such as 'type', 'id' 'payload' will not be acccessible to NfcWorker.
@ -145,13 +152,11 @@ NfcContentHelper.prototype = {
throw Components.Exception("Can't get window object", throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED); Cr.NS_ERROR_UNEXPECTED);
} }
let request = Services.DOMRequest.createRequest(window); let request = Services.DOMRequest.createRequest(window);
let requestId = btoa(this.getRequestId(request)); let requestId = btoa(this.getRequestId(request));
this._requestMap[requestId] = window; this._requestMap[requestId] = window;
let encodedRecords = this.encodeNdefRecords(records); let encodedRecords = this.encodeNdefRecords(records);
cpmm.sendAsyncMessage("NFC:WriteNDEF", { cpmm.sendAsyncMessage("NFC:WriteNDEF", {
requestId: requestId, requestId: requestId,
sessionToken: sessionToken, sessionToken: sessionToken,
@ -210,6 +215,63 @@ NfcContentHelper.prototype = {
return request; return request;
}, },
registerTargetForPeerEvent: function registerTargetForPeerEvent(window,
appId, event, callback) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
this.peerEventsCallbackMap[event] = callback;
cpmm.sendAsyncMessage("NFC:RegisterPeerTarget", {
appId: appId,
event: event
});
},
unregisterTargetForPeerEvent: function unregisterTargetForPeerEvent(window,
appId, event) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
let callback = this.peerEventsCallbackMap[event];
if (callback != null) {
delete this.peerEventsCallbackMap[event];
}
cpmm.sendAsyncMessage("NFC:UnregisterPeerTarget", {
appId: appId,
event: event
});
},
checkP2PRegistration: function checkP2PRegistration(window, appId) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = btoa(this.getRequestId(request));
this._requestMap[requestId] = window;
cpmm.sendAsyncMessage("NFC:CheckP2PRegistration", {
appId: appId,
requestId: requestId
});
return request;
},
notifyUserAcceptedP2P: function notifyUserAcceptedP2P(window, appId) {
if (window == null) {
throw Components.Exception("Can't get window object",
Cr.NS_ERROR_UNEXPECTED);
}
cpmm.sendAsyncMessage("NFC:NotifyUserAcceptedP2P", {
appId: appId
});
},
// nsIObserver // nsIObserver
observe: function observe(subject, topic, data) { observe: function observe(subject, topic, data) {
@ -259,8 +321,19 @@ NfcContentHelper.prototype = {
case "NFC:WriteNDEFResponse": case "NFC:WriteNDEFResponse":
case "NFC:MakeReadOnlyNDEFResponse": case "NFC:MakeReadOnlyNDEFResponse":
case "NFC:GetDetailsNDEFResponse": case "NFC:GetDetailsNDEFResponse":
case "NFC:CheckP2PRegistrationResponse":
this.handleResponse(message.json); this.handleResponse(message.json);
break; break;
case "NFC:PeerEvent":
let callback = this.peerEventsCallbackMap[message.json.event];
if (callback) {
callback.peerNotification(message.json.event,
message.json.sessionToken);
} else {
debug("PeerEvent: No valid callback registered for the event " +
message.json.event);
}
break;
} }
}, },

View File

@ -67,5 +67,8 @@ this.TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
this.SETTING_NFC_ENABLED = "nfc.enabled"; this.SETTING_NFC_ENABLED = "nfc.enabled";
this.SETTING_NFC_POWER_LEVEL = "nfc.powerlevel"; this.SETTING_NFC_POWER_LEVEL = "nfc.powerlevel";
this.NFC_PEER_EVENT_READY = 0x01;
this.NFC_PEER_EVENT_LOST = 0x02;
// Allow this file to be imported via Components.utils.import(). // Allow this file to be imported via Components.utils.import().
this.EXPORTED_SYMBOLS = Object.keys(this); this.EXPORTED_SYMBOLS = Object.keys(this);

View File

@ -7,9 +7,29 @@
interface nsIVariant; interface nsIVariant;
[scriptable, uuid(28c8f240-da8c-11e1-9b23-0800200c9a66)] [scriptable, function, uuid(271f48b0-c884-4f0b-a348-e29824c95168)]
interface nsINfcPeerCallback : nsISupports
{
/**
* Callback function used to notify NFC peer events.
*
* @param event
* An event indicating 'PeerReady' or 'PeerLost'
* One of NFC_EVENT_PEER_XXXX
*
* @param sessionToken
* SessionToken received from Chrome process
*/
void peerNotification(in unsigned long event,
in DOMString sessionToken);
};
[scriptable, uuid(91c2760a-f41c-4174-ad68-614840d4e201)]
interface nsINfcContentHelper : nsISupports interface nsINfcContentHelper : nsISupports
{ {
const long NFC_EVENT_PEER_READY = 0x01;
const long NFC_EVENT_PEER_LOST = 0x02;
void setSessionToken(in DOMString sessionToken); void setSessionToken(in DOMString sessionToken);
nsIDOMDOMRequest getDetailsNDEF(in nsIDOMWindow window, in DOMString sessionToken); nsIDOMDOMRequest getDetailsNDEF(in nsIDOMWindow window, in DOMString sessionToken);
@ -19,4 +39,62 @@ interface nsINfcContentHelper : nsISupports
nsIDOMDOMRequest connect(in nsIDOMWindow window, in unsigned long techType, in DOMString sessionToken); nsIDOMDOMRequest connect(in nsIDOMWindow window, in unsigned long techType, in DOMString sessionToken);
nsIDOMDOMRequest close(in nsIDOMWindow window, in DOMString sessionToken); nsIDOMDOMRequest close(in nsIDOMWindow window, in DOMString sessionToken);
/**
* Register the given application id with Chrome process
*
* @param window
* Current window
*
* @param appId
* Application ID to be registered
*
* @param event
* Event to be registered. Either NFC_EVENT_PEER_READY or NFC_EVENT_PEER_LOST
*
* @param callback
* Callback that is used to notify upper layers whenever PeerEvents happen.
*/
void registerTargetForPeerEvent(in nsIDOMWindow window,
in unsigned long appId,
in octet event,
in nsINfcPeerCallback callback);
/**
* Unregister the given application id with Chrome process
*
* @param window
* Current window
*
* @param appId
* Application ID to be registered
*
* @param event
* Event to be unregistered. Either NFC_EVENT_PEER_READY or NFC_EVENT_PEER_LOST
*/
void unregisterTargetForPeerEvent(in nsIDOMWindow window,
in unsigned long appId,
in octet event);
/**
* Checks if the given application's id is a registered peer target (with the Chrome process)
*
* @param window
* Current window
*
* @param appId
* Application ID to be updated with Chrome process
*
* Returns DOMRequest, if appId is registered then 'onsuccess' is called else 'onerror'
*/
nsIDOMDOMRequest checkP2PRegistration(in nsIDOMWindow window, in unsigned long appId);
/**
* Notify the Chrome process that user has accepted to share nfc message on P2P UI
*
* @param window
* Current window
*
* @param appId
* Application ID that is capable of handling NFC_EVENT_PEER_READY event
*/
void notifyUserAcceptedP2P(in nsIDOMWindow window, in unsigned long appId);
}; };