Bug 1105666 - [BrowserAPI] Add an API to indicate this iframe could receive NFC event. r=smaug, kanru, yoshi

This commit is contained in:
Dimi Lee 2015-01-22 17:40:17 +08:00
parent 2098468ab7
commit 1b2902b630
13 changed files with 180 additions and 26 deletions

View File

@ -834,6 +834,35 @@ BrowserElementParent.prototype = {
{isActive: isActive});
},
setNFCFocus: function(isFocus) {
if (!this._isAlive()) {
throw Components.Exception("Dead content process",
Cr.NS_ERROR_DOM_INVALID_STATE_ERR);
}
// For now, we use tab id as an identifier to let NFC module know
// which app is in foreground. But this approach will not work in
// in-process mode because tab id doesn't exist. Fix bug 1116449
// if we are going to support in-process mode.
try {
var tabId = this._frameLoader.QueryInterface(Ci.nsIFrameLoader)
.tabParent
.tabId;
} catch(e) {
debug("SetNFCFocus for in-process mode is not yet supported");
throw Components.Exception("SetNFCFocus for in-process mode is not yet supported",
Cr.NS_ERROR_NOT_IMPLEMENTED);
}
try {
let nfcContentHelper =
Cc["@mozilla.org/nfc/content-helper;1"].getService(Ci.nsINfcBrowserAPI);
nfcContentHelper.setFocusApp(tabId, isFocus);
} catch(e) {
// Not all platforms support NFC
}
},
/**
* Called when the visibility of the window which owns this iframe changes.
*/

View File

@ -26,7 +26,7 @@ interface nsIBrowserElementNextPaintListener : nsISupports
* Interface to the BrowserElementParent implementation. All methods
* but setFrameLoader throw when the remote process is dead.
*/
[scriptable, uuid(abae4fb1-7d6f-4e3f-b435-6501f1d4c659)]
[scriptable, uuid(3811446f-90bb-42c1-b2b6-aae3603b61e1)]
interface nsIBrowserElementAPI : nsISupports
{
void setFrameLoader(in nsIFrameLoader frameLoader);
@ -71,4 +71,6 @@ interface nsIBrowserElementAPI : nsISupports
void removeNextPaintListener(in nsIBrowserElementNextPaintListener listener);
nsIDOMDOMRequest setInputMethodActive(in boolean isActive);
void setNFCFocus(in boolean isFocus);
};

View File

@ -542,4 +542,36 @@ nsBrowserElement::SetInputMethodActive(bool aIsActive,
return req.forget().downcast<DOMRequest>();
}
void
nsBrowserElement::SetNFCFocus(bool aIsFocus,
ErrorResult& aRv)
{
NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (!frameLoader) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIDOMElement> ownerElement;
nsresult rv = frameLoader->GetOwnerElement(getter_AddRefs(ownerElement));
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
nsCOMPtr<nsINode> node = do_QueryInterface(ownerElement);
nsCOMPtr<nsIPrincipal> principal = node->NodePrincipal();
if (!nsContentUtils::IsExactSitePermAllow(principal, "nfc-manager")) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return;
}
rv = mBrowserElementAPI->SetNFCFocus(aIsFocus);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
}
}
} // namespace mozilla

View File

@ -89,6 +89,9 @@ public:
already_AddRefed<dom::DOMRequest> SetInputMethodActive(bool isActive,
ErrorResult& aRv);
void SetNFCFocus(bool isFocus,
ErrorResult& aRv);
protected:
NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;

View File

@ -10,7 +10,7 @@ interface nsIWebBrowserChrome3;
native CommandsArray(nsTArray<nsCString>);
[ref] native CommandsArrayRef(nsTArray<nsCString>);
[scriptable, uuid(7227bac4-b6fe-4090-aeb4-bc288b790925)]
[scriptable, uuid(1fb79c27-e760-4088-b19c-1ce3673ec24e)]
interface nsITabChild : nsISupports
{
readonly attribute nsIContentFrameMessageManager messageManager;
@ -22,5 +22,7 @@ interface nsITabChild : nsISupports
[noscript, notxpcom] void enableDisableCommands(in AString action,
in CommandsArrayRef enabledCommands,
in CommandsArrayRef disabledCommands);
readonly attribute uint64_t tabId;
};

View File

@ -5,7 +5,7 @@
#include "domstubs.idl"
[scriptable, uuid(33e8571d-5e5d-4000-81cf-e01f5f85d424)]
[scriptable, uuid(30361a5b-a3b8-4dbc-b464-e08761abb123)]
interface nsITabParent : nsISupports
{
void injectTouchEvent(in AString aType,
@ -22,4 +22,6 @@ interface nsITabParent : nsISupports
readonly attribute boolean useAsyncPanZoom;
void setIsDocShellActive(in bool aIsActive);
readonly attribute uint64_t tabId;
};

View File

@ -3372,6 +3372,14 @@ TabChild::EnableDisableCommands(const nsAString& aAction,
aEnabledCommands, aDisabledCommands);
}
NS_IMETHODIMP
TabChild::GetTabId(uint64_t* aId)
{
*aId = GetTabId();
return NS_OK;
}
bool
TabChild::DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,

View File

@ -2490,6 +2490,13 @@ TabParent::SetIsDocShellActive(bool isActive)
return NS_OK;
}
NS_IMETHODIMP
TabParent::GetTabId(uint64_t* aId)
{
*aId = GetTabId();
return NS_OK;
}
bool
TabParent::RecvRemotePaintIsReady()
{

View File

@ -79,18 +79,21 @@ NfcContentHelper.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcContentHelper,
Ci.nsINfcBrowserAPI,
Ci.nsISupportsWeakReference,
Ci.nsIObserver]),
classID: NFCCONTENTHELPER_CID,
classInfo: XPCOMUtils.generateCI({
classID: NFCCONTENTHELPER_CID,
classDescription: "NfcContentHelper",
interfaces: [Ci.nsINfcContentHelper]
interfaces: [Ci.nsINfcContentHelper,
Ci.nsINfcBrowserAPI]
}),
_window: null,
_requestMap: null,
_rfState: null,
_tabId: null,
eventListener: null,
init: function init(aWindow) {
@ -112,6 +115,31 @@ NfcContentHelper.prototype = {
let info = cpmm.sendSyncMessage("NFC:QueryInfo")[0];
this._rfState = info.rfState;
// For now, we assume app will run in oop mode so we can get
// tab id for each app. Fix bug 1116449 if we are going to
// support in-process mode.
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
try {
this._tabId = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsITabChild)
.tabId;
} catch(e) {
// Only parent process does not have tab id, so in this case
// NfcContentHelper is used by system app. Use -1(tabId) to
// indicate its system app.
let inParent = Cc["@mozilla.org/xre/app-info;1"]
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if (inParent) {
this._tabId = -1;
} else {
throw Components.Exception("Can't get tab id in child process",
Cr.NS_ERROR_UNEXPECTED);
}
}
},
queryRFState: function queryRFState() {
@ -132,6 +160,13 @@ NfcContentHelper.prototype = {
return encodedRecords;
},
setFocusApp: function setFocusApp(tabId, isFocus) {
cpmm.sendAsyncMessage("NFC:SetFocusApp", {
tabId: tabId,
isFocus: isFocus
});
},
// NFCTag interface
readNDEF: function readNDEF(sessionToken, callback) {
let requestId = callback.getCallbackId();
@ -207,7 +242,7 @@ NfcContentHelper.prototype = {
addEventListener: function addEventListener(listener) {
this.eventListener = listener;
cpmm.sendAsyncMessage("NFC:AddEventListener");
cpmm.sendAsyncMessage("NFC:AddEventListener", { tabId: this._tabId });
},
registerTargetForPeerReady: function registerTargetForPeerReady(appId) {

View File

@ -75,7 +75,8 @@ const NFC_IPC_MSG_ENTRIES = [
messages: ["NFC:CheckP2PRegistration",
"NFC:NotifyUserAcceptedP2P",
"NFC:NotifySendFileStatus",
"NFC:ChangeRFState"] }
"NFC:ChangeRFState",
"NFC:SetFocusApp"] }
];
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
@ -97,7 +98,9 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
// Manage registered Peer Targets
peerTargets: {},
eventListeners: [],
eventListeners: {},
focusApp: null,
init: function init(nfc) {
this.nfc = nfc;
@ -172,18 +175,30 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
target.sendAsyncMessage("NFC:DOMEvent", options);
},
addEventListener: function addEventListener(target) {
if (this.eventListeners.indexOf(target) != -1) {
setFocusApp: function setFocusApp(id, isFocus) {
if (isFocus) {
// Now we only support one focus app.
this.focusApp = id;
} else if (this.focusApp == id){
// Set focusApp to null means currently there is no foreground app.
this.focusApp = null;
}
},
addEventListener: function addEventListener(target, id) {
if (this.eventListeners[id] !== undefined) {
return;
}
this.eventListeners.push(target);
this.eventListeners[id] = target;
},
removeEventListener: function removeEventListener(target) {
let index = this.eventListeners.indexOf(target);
if (index !== -1) {
this.eventListeners.splice(index, 1);
for (let id in this.eventListeners) {
if (target == this.eventListeners[id]) {
delete this.eventListeners[id];
break;
}
}
},
@ -214,30 +229,33 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
onTagFound: function onTagFound(message) {
message.event = NFC.TAG_EVENT_FOUND;
for (let target of this.eventListeners) {
this.notifyDOMEvent(target, message);
for (let id in this.eventListeners) {
this.notifyDOMEvent(this.eventListeners[id], message);
}
delete message.event;
},
onTagLost: function onTagLost(sessionToken) {
for (let target of this.eventListeners) {
this.notifyDOMEvent(target, {event: NFC.TAG_EVENT_LOST,
sessionToken: sessionToken});
for (let id in this.eventListeners) {
this.notifyDOMEvent(this.eventListeners[id],
{ event: NFC.TAG_EVENT_LOST,
sessionToken: sessionToken });
}
},
onPeerEvent: function onPeerEvent(eventType, sessionToken) {
for (let target of this.eventListeners) {
this.notifyDOMEvent(target, { event: eventType,
sessionToken: sessionToken });
for (let id in this.eventListeners) {
this.notifyDOMEvent(this.eventListeners[id],
{ event: eventType,
sessionToken: sessionToken });
}
},
onRFStateChange: function onRFStateChange(rfState) {
for (let target of this.eventListeners) {
this.notifyDOMEvent(target, { event: NFC.RF_EVENT_STATE_CHANGE,
rfState: rfState});
for (let id in this.eventListeners) {
this.notifyDOMEvent(this.eventListeners[id],
{ event: NFC.RF_EVENT_STATE_CHANGE,
rfState: rfState });
}
},
@ -268,8 +286,11 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
}
switch (message.name) {
case "NFC:SetFocusApp":
this.setFocusApp(message.data.tabId, message.data.isFocus);
return null;
case "NFC:AddEventListener":
this.addEventListener(message.target);
this.addEventListener(message.target, message.data.tabId);
return null;
case "NFC:RegisterPeerReadyTarget":
this.registerPeerReadyTarget(message.target, message.data.appId);

View File

@ -102,6 +102,13 @@ interface nsINfcRequestCallback : nsISupports
void notifyError(in DOMString errorMsg);
};
[scriptable, uuid(7ae46728-bc42-44bd-8093-bc7563abf52d)]
interface nsINfcBrowserAPI : nsISupports
{
void setFocusApp(in uint64_t tabId,
in boolean isFocus);
};
[scriptable, uuid(b5194ae8-d5d5-482f-a73f-dd0d755a1972)]
interface nsINfcContentHelper : nsISupports
{

View File

@ -331,7 +331,6 @@ function MozNFCImpl() {
this.eventService = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
this._nfcContentHelper.addEventListener(this);
}
MozNFCImpl.prototype = {
_nfcContentHelper: null,
@ -353,6 +352,7 @@ MozNFCImpl.prototype = {
if (this._nfcContentHelper) {
this._nfcContentHelper.init(aWindow);
this._nfcContentHelper.addEventListener(this);
this._rfState = this._nfcContentHelper.queryRFState();
}
},

View File

@ -139,4 +139,10 @@ interface BrowserElementPrivileged {
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
DOMRequest setInputMethodActive(boolean isActive);
// Additional |nfc-manager| permission is required for setNFCFocus API
[Throws,
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
void setNFCFocus(boolean isFocus);
};