Bug 1206581 - Implement notifyChannel() on AudioChannel API. r=kanru, r=baku.

This commit is contained in:
Alastor Wu 2015-11-06 00:32:04 +08:00
parent 18a2335fef
commit 41fb613ded
14 changed files with 320 additions and 10 deletions

View File

@ -7,15 +7,21 @@
#include "mozilla/Services.h"
#include "mozilla/dom/BrowserElementAudioChannelBinding.h"
#include "mozilla/dom/DOMRequest.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/ToJSValue.h"
#include "AudioChannelService.h"
#include "nsIBrowserElementAPI.h"
#include "nsIDocShell.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDOMRequest.h"
#include "nsIObserverService.h"
#include "nsISupportsPrimitives.h"
#include "nsISystemMessagesInternal.h"
#include "nsITabParent.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
namespace {
@ -50,10 +56,12 @@ BrowserElementAudioChannel::Create(nsPIDOMWindow* aWindow,
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
AudioChannel aAudioChannel,
const nsAString& aManifestURL,
ErrorResult& aRv)
{
RefPtr<BrowserElementAudioChannel> ac =
new BrowserElementAudioChannel(aWindow, aFrameLoader, aAPI, aAudioChannel);
new BrowserElementAudioChannel(aWindow, aFrameLoader, aAPI,
aAudioChannel, aManifestURL);
aRv = ac->Initialize();
if (NS_WARN_IF(aRv.Failed())) {
@ -64,14 +72,16 @@ BrowserElementAudioChannel::Create(nsPIDOMWindow* aWindow,
}
BrowserElementAudioChannel::BrowserElementAudioChannel(
nsPIDOMWindow* aWindow,
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
AudioChannel aAudioChannel)
nsPIDOMWindow* aWindow,
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
AudioChannel aAudioChannel,
const nsAString& aManifestURL)
: DOMEventTargetHelper(aWindow)
, mFrameLoader(aFrameLoader)
, mBrowserElementAPI(aAPI)
, mAudioChannel(aAudioChannel)
, mManifestURL(aManifestURL)
, mState(eStateUnknown)
{
MOZ_ASSERT(NS_IsMainThread());
@ -302,6 +312,43 @@ protected:
}
};
class RespondSuccessHandler final : public PromiseNativeHandler
{
public:
NS_DECL_ISUPPORTS
explicit RespondSuccessHandler(DOMRequest* aRequest)
: mDomRequest(aRequest)
{};
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
private:
~RespondSuccessHandler() {};
RefPtr<DOMRequest> mDomRequest;
};
NS_IMPL_ISUPPORTS0(RespondSuccessHandler);
void
RespondSuccessHandler::ResolvedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
JS::Rooted<JS::Value> value(aCx);
mDomRequest->FireSuccess(value);
}
void
RespondSuccessHandler::RejectedCallback(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
mDomRequest->FireError(NS_ERROR_FAILURE);
}
} // anonymous namespace
already_AddRefed<dom::DOMRequest>
@ -459,6 +506,62 @@ BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
return domRequest.forget();
}
already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::NotifyChannel(const nsAString& aEvent,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsParentProcess());
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
aRv = mBrowserElementAPI->NotifyChannel(aEvent, mManifestURL,
(uint32_t)mAudioChannel,
getter_AddRefs(request));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return request.forget().downcast<DOMRequest>();
}
nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
do_GetService("@mozilla.org/system-message-internal;1");
MOZ_ASSERT(systemMessenger);
AutoJSAPI jsAPI;
if (!jsAPI.Init(GetOwner())) {
return nullptr;
}
JS::Rooted<JS::Value> value(jsAPI.cx());
value.setInt32((uint32_t)mAudioChannel);
nsCOMPtr<nsIURI> manifestURI;
nsresult rv = NS_NewURI(getter_AddRefs(manifestURI), mManifestURL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
// Since the pageURI of the app has been registered to the system messager,
// when the app was installed. The system messager can only use the manifest
// to send the message to correct page.
nsCOMPtr<nsISupports> promise;
rv = systemMessenger->SendMessage(aEvent, value, nullptr, manifestURI,
JS::UndefinedHandleValue,
getter_AddRefs(promise));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
RefPtr<Promise> promiseIns = static_cast<Promise*>(promise.get());
RefPtr<DOMRequest> request = new DOMRequest(GetOwner());
RefPtr<RespondSuccessHandler> handler = new RespondSuccessHandler(request);
promiseIns->AppendNativeHandler(handler);
return request.forget();
}
NS_IMETHODIMP
BrowserElementAudioChannel::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)

View File

@ -40,6 +40,7 @@ public:
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
AudioChannel aAudioChannel,
const nsAString& aManifestURL,
ErrorResult& aRv);
// WebIDL methods
@ -57,13 +58,17 @@ public:
already_AddRefed<dom::DOMRequest> IsActive(ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> NotifyChannel(const nsAString& aEvent,
ErrorResult& aRv);
IMPL_EVENT_HANDLER(activestatechanged);
private:
BrowserElementAudioChannel(nsPIDOMWindow* aWindow,
nsIFrameLoader* aFrameLoader,
nsIBrowserElementAPI* aAPI,
AudioChannel aAudioChannel);
AudioChannel aAudioChannel,
const nsAString& aManifestURL);
~BrowserElementAudioChannel();
@ -76,6 +81,7 @@ private:
nsCOMPtr<nsITabParent> mTabParent;
nsCOMPtr<nsPIDOMWindow> mFrameWindow;
AudioChannel mAudioChannel;
nsString mManifestURL;
enum {
eStateActive,

View File

@ -23,6 +23,10 @@ XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function () {
return DOMApplicationRegistry;
});
XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger",
"@mozilla.org/system-message-internal;1",
"nsISystemMessagesInternal");
function debug(msg) {
//dump("BrowserElementParent - " + msg + "\n");
}
@ -1209,6 +1213,26 @@ BrowserElementParent.prototype = {
{audioChannel: aAudioChannel});
},
notifyChannel: function(aEvent, aManifest, aAudioChannel) {
var self = this;
var req = Services.DOMRequest.createRequest(self._window);
// Since the pageURI of the app has been registered to the system messager,
// when the app was installed. The system messager can only use the manifest
// to send the message to correct page.
let manifestURL = Services.io.newURI(aManifest, null, null);
systemMessenger.sendMessage(aEvent, aAudioChannel, null, manifestURL)
.then(function() {
Services.DOMRequest.fireSuccess(req,
Cu.cloneInto(true, self._window));
}, function() {
debug("Error : NotifyChannel fail.");
Services.DOMRequest.fireErrorAsync(req,
Cu.cloneInto("NotifyChannel fail.", self._window));
});
return req;
},
getStructuredData: defineDOMRequestMethod('get-structured-data'),
getWebManifest: defineDOMRequestMethod('get-web-manifest'),

View File

@ -0,0 +1,118 @@
"use strict";
SimpleTest.waitForExplicitFinish();
const { classes: Cc, interfaces: Ci } = Components;
const systemMessenger = Cc["@mozilla.org/system-message-internal;1"]
.getService(Ci.nsISystemMessagesInternal);
const ioService = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
var tests = [false /* INPROC */, true /* OOP */];
var rootURI = "http://test/chrome/dom/browser-element/mochitest/";
var manifestURI = rootURI + "manifest.webapp";
var srcURI = rootURI + "file_browserElement_NotifyChannel.html";
var generator = runTests();
var app = null;
addLoadEvent(() => {
SpecialPowers.pushPermissions(
[{ "type": "webapps-manage", "allow": 1, "context": document },
{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-apps", "allow": 1, "context": document }],
function() {
SpecialPowers.pushPrefEnv(
{'set': [["dom.mozBrowserFramesEnabled", true],
["dom.sysmsg.enabled", true]]},
() => { generator.next(); })
});
});
function error(message) {
ok(false, message);
SimpleTest.finish();
}
function continueTest() {
try {
generator.next();
} catch (e if e instanceof StopIteration) {
error("Stop test because of exception!");
}
}
function registerPage(aEvent) {
systemMessenger.registerPage(aEvent,
ioService.newURI(srcURI, null, null),
ioService.newURI(manifestURI, null, null));
}
function runTest(aEnable) {
var request = navigator.mozApps.install(manifestURI, {});
request.onerror = () => {
error("Install app failed!");
};
request.onsuccess = () => {
app = request.result;
ok(app, "App is installed. remote = " + aEnable);
is(app.manifestURL, manifestURI, "App manifest url is correct.");
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
iframe.setAttribute('remote', aEnable);
iframe.setAttribute('mozapp', manifestURI);
iframe.src = srcURI;
document.body.appendChild(iframe);
iframe.addEventListener('mozbrowserloadend', () => {
var channels = iframe.allowedAudioChannels;
is(channels.length, 1, "1 audio channel by default");
var ac = channels[0];
ok(ac instanceof BrowserElementAudioChannel, "Correct class");
ok("notifyChannel" in ac, "ac.notifyChannel exists");
var message = "audiochannel-interruption-begin";
registerPage(message);
ac.notifyChannel(message);
iframe.addEventListener("mozbrowsershowmodalprompt", function (e) {
is(e.detail.message, message,
"App got audiochannel-interruption-begin.");
if (app) {
request = navigator.mozApps.mgmt.uninstall(app);
app = null;
request.onerror = () => {
error("Uninstall app failed!");
};
request.onsuccess = () => {
is(request.result, manifestURI, "App uninstalled.");
runNextTest();
}
}
});
});
};
}
function runNextTest() {
if (tests.length) {
var isEnabledOOP = tests.shift();
runTest(isEnabledOOP);
} else {
SimpleTest.finish();
}
}
function runTests() {
SpecialPowers.setAllAppsLaunchable(true);
SpecialPowers.autoConfirmAppInstall(continueTest);
yield undefined;
SpecialPowers.autoConfirmAppUninstall(continueTest);
yield undefined;
runNextTest();
yield undefined;
}

View File

@ -0,0 +1,10 @@
[DEFAULT]
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
support-files =
browserElement_NotifyChannel.js
file_browserElement_NotifyChannel.html
manifest.webapp
manifest.webapp^headers^
[test_browserElement_NotifyChannel.html]

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of browser element audio channel method, notifyChannel</title>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
navigator.mozSetMessageHandler('audiochannel-interruption-begin',
function (e) {
alert("audiochannel-interruption-begin");
});
</script>
</body>
</html>

View File

@ -0,0 +1,7 @@
{
"name": "NotifyChannel Test",
"launch_path": "/index.html",
"messages": [
{ "audiochannel-interruption-begin": "./file_browserElement_NotifyChannel.html" }
]
}

View File

@ -0,0 +1 @@
Content-Type: application/manifest+json

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test BrowserElementAudioChannel function : notifyChannel().</title>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_NotifyChannel.js">
</script>
</body>
</html>

View File

@ -54,6 +54,7 @@ MOCHITEST_MANIFESTS += [
'mochitest/mochitest.ini',
'mochitest/priority/mochitest.ini',
]
MOCHITEST_CHROME_MANIFESTS += ['mochitest/chrome.ini']
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wshadow']

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(1e098c3a-7d65-452d-a2b2-9ffd1b6e04bb)]
[scriptable, uuid(57758c10-6036-11e5-a837-0800200c9a66)]
interface nsIBrowserElementAPI : nsISupports
{
const long FIND_CASE_SENSITIVE = 0;
@ -97,6 +97,10 @@ interface nsIBrowserElementAPI : nsISupports
nsIDOMDOMRequest isAudioChannelActive(in uint32_t audioChannel);
nsIDOMDOMRequest notifyChannel(in DOMString event,
in DOMString manifest,
in uint32_t audioChannel);
void setNFCFocus(in boolean isFocus);
nsIDOMDOMRequest executeScript(in DOMString script, in jsval options);

View File

@ -603,7 +603,8 @@ nsBrowserElement::GenerateAllowedAudioChannels(
RefPtr<BrowserElementAudioChannel> ac =
BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
AudioChannel::Normal, aRv);
AudioChannel::Normal,
aManifestURL, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@ -630,7 +631,7 @@ nsBrowserElement::GenerateAllowedAudioChannels(
RefPtr<BrowserElementAudioChannel> ac =
BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
(AudioChannel)audioChannelTable[i].value,
aRv);
aManifestURL, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}

View File

@ -136,7 +136,9 @@ this.SystemMessagePermissionsTable = {
},
"first-run-with-sim": {
"settings": ["read", "write"]
}
},
"audiochannel-interruption-begin" : {},
"audiochannel-interruption-ended" : {}
};

View File

@ -27,6 +27,9 @@ interface BrowserElementAudioChannel : EventTarget {
[Throws]
DOMRequest isActive();
[Throws]
DOMRequest notifyChannel(DOMString aEvent);
};
partial interface BrowserElementPrivileged {