From 6fa70576bdfa82f8666eac098db294707ab5b4f3 Mon Sep 17 00:00:00 2001 From: Alastor Wu Date: Wed, 4 Nov 2015 10:33:27 +0800 Subject: [PATCH] Bug 1206581 - Implement notifyChannel() on AudioChannel API. r=kanru, r=baku --HG-- extra : transplant_source : %25%253%AFA%06%18%7Bz%00%0EZ%E5%E5%D3%3E%0B%EA%DE%DA --- .../BrowserElementAudioChannel.cpp | 113 ++++++++++++++++- .../BrowserElementAudioChannel.h | 8 +- dom/browser-element/BrowserElementParent.js | 24 ++++ .../mochitest/browserElement_NotifyChannel.js | 118 ++++++++++++++++++ dom/browser-element/mochitest/chrome.ini | 10 ++ .../file_browserElement_NotifyChannel.html | 15 +++ dom/browser-element/mochitest/manifest.webapp | 7 ++ .../mochitest/manifest.webapp^headers^ | 1 + .../test_browserElement_NotifyChannel.html | 15 +++ dom/browser-element/moz.build | 1 + dom/browser-element/nsIBrowserElementAPI.idl | 6 +- dom/html/nsBrowserElement.cpp | 5 +- .../SystemMessagePermissionsChecker.jsm | 4 +- dom/webidl/BrowserElementAudioChannel.webidl | 3 + 14 files changed, 320 insertions(+), 10 deletions(-) create mode 100644 dom/browser-element/mochitest/browserElement_NotifyChannel.js create mode 100644 dom/browser-element/mochitest/chrome.ini create mode 100644 dom/browser-element/mochitest/file_browserElement_NotifyChannel.html create mode 100644 dom/browser-element/mochitest/manifest.webapp create mode 100644 dom/browser-element/mochitest/manifest.webapp^headers^ create mode 100644 dom/browser-element/mochitest/test_browserElement_NotifyChannel.html diff --git a/dom/browser-element/BrowserElementAudioChannel.cpp b/dom/browser-element/BrowserElementAudioChannel.cpp index 8aa5f73183ea..c522b0a10a6a 100644 --- a/dom/browser-element/BrowserElementAudioChannel.cpp +++ b/dom/browser-element/BrowserElementAudioChannel.cpp @@ -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 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 aValue) override; + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle aValue) override; + +private: + ~RespondSuccessHandler() {}; + + RefPtr mDomRequest; +}; +NS_IMPL_ISUPPORTS0(RespondSuccessHandler); + +void +RespondSuccessHandler::ResolvedCallback(JSContext* aCx, + JS::Handle aValue) +{ + JS::Rooted value(aCx); + mDomRequest->FireSuccess(value); +} + +void +RespondSuccessHandler::RejectedCallback(JSContext* aCx, + JS::Handle aValue) +{ + mDomRequest->FireError(NS_ERROR_FAILURE); +} + } // anonymous namespace already_AddRefed @@ -459,6 +506,62 @@ BrowserElementAudioChannel::IsActive(ErrorResult& aRv) return domRequest.forget(); } +already_AddRefed +BrowserElementAudioChannel::NotifyChannel(const nsAString& aEvent, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess()); + + if (!mFrameWindow) { + nsCOMPtr request; + aRv = mBrowserElementAPI->NotifyChannel(aEvent, mManifestURL, + (uint32_t)mAudioChannel, + getter_AddRefs(request)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return request.forget().downcast(); + } + + nsCOMPtr systemMessenger = + do_GetService("@mozilla.org/system-message-internal;1"); + MOZ_ASSERT(systemMessenger); + + AutoJSAPI jsAPI; + if (!jsAPI.Init(GetOwner())) { + return nullptr; + } + + JS::Rooted value(jsAPI.cx()); + value.setInt32((uint32_t)mAudioChannel); + + nsCOMPtr 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 promise; + rv = systemMessenger->SendMessage(aEvent, value, nullptr, manifestURI, + JS::UndefinedHandleValue, + getter_AddRefs(promise)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + RefPtr promiseIns = static_cast(promise.get()); + RefPtr request = new DOMRequest(GetOwner()); + RefPtr handler = new RespondSuccessHandler(request); + promiseIns->AppendNativeHandler(handler); + + return request.forget(); +} + NS_IMETHODIMP BrowserElementAudioChannel::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) diff --git a/dom/browser-element/BrowserElementAudioChannel.h b/dom/browser-element/BrowserElementAudioChannel.h index 8ec2020f7bd9..69ff3fa6ae43 100644 --- a/dom/browser-element/BrowserElementAudioChannel.h +++ b/dom/browser-element/BrowserElementAudioChannel.h @@ -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 IsActive(ErrorResult& aRv); + already_AddRefed 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 mTabParent; nsCOMPtr mFrameWindow; AudioChannel mAudioChannel; + nsString mManifestURL; enum { eStateActive, diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index d2fe5e026eca..92a0e31a2e28 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -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"); } @@ -1208,6 +1212,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'), /** diff --git a/dom/browser-element/mochitest/browserElement_NotifyChannel.js b/dom/browser-element/mochitest/browserElement_NotifyChannel.js new file mode 100644 index 000000000000..564e719eb280 --- /dev/null +++ b/dom/browser-element/mochitest/browserElement_NotifyChannel.js @@ -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; +} diff --git a/dom/browser-element/mochitest/chrome.ini b/dom/browser-element/mochitest/chrome.ini new file mode 100644 index 000000000000..15e694812bb6 --- /dev/null +++ b/dom/browser-element/mochitest/chrome.ini @@ -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] \ No newline at end of file diff --git a/dom/browser-element/mochitest/file_browserElement_NotifyChannel.html b/dom/browser-element/mochitest/file_browserElement_NotifyChannel.html new file mode 100644 index 000000000000..b6a80dcb3940 --- /dev/null +++ b/dom/browser-element/mochitest/file_browserElement_NotifyChannel.html @@ -0,0 +1,15 @@ + + + + Test of browser element audio channel method, notifyChannel + + + + + diff --git a/dom/browser-element/mochitest/manifest.webapp b/dom/browser-element/mochitest/manifest.webapp new file mode 100644 index 000000000000..1e57eeb200d0 --- /dev/null +++ b/dom/browser-element/mochitest/manifest.webapp @@ -0,0 +1,7 @@ +{ + "name": "NotifyChannel Test", + "launch_path": "/index.html", + "messages": [ + { "audiochannel-interruption-begin": "./file_browserElement_NotifyChannel.html" } + ] +} diff --git a/dom/browser-element/mochitest/manifest.webapp^headers^ b/dom/browser-element/mochitest/manifest.webapp^headers^ new file mode 100644 index 000000000000..3cea33fec8b6 --- /dev/null +++ b/dom/browser-element/mochitest/manifest.webapp^headers^ @@ -0,0 +1 @@ +Content-Type: application/manifest+json \ No newline at end of file diff --git a/dom/browser-element/mochitest/test_browserElement_NotifyChannel.html b/dom/browser-element/mochitest/test_browserElement_NotifyChannel.html new file mode 100644 index 000000000000..6111dcc729ce --- /dev/null +++ b/dom/browser-element/mochitest/test_browserElement_NotifyChannel.html @@ -0,0 +1,15 @@ + + + + Test BrowserElementAudioChannel function : notifyChannel(). + + + + + + + + diff --git a/dom/browser-element/moz.build b/dom/browser-element/moz.build index d65f2b53dfa8..5bce01e4e8a1 100644 --- a/dom/browser-element/moz.build +++ b/dom/browser-element/moz.build @@ -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'] diff --git a/dom/browser-element/nsIBrowserElementAPI.idl b/dom/browser-element/nsIBrowserElementAPI.idl index f95215067f99..ba2e514776ad 100644 --- a/dom/browser-element/nsIBrowserElementAPI.idl +++ b/dom/browser-element/nsIBrowserElementAPI.idl @@ -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(9946695c-1ed3-4abb-bc60-6f8947fd5641)] +[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); diff --git a/dom/html/nsBrowserElement.cpp b/dom/html/nsBrowserElement.cpp index 628141a8b997..28121e7f0694 100644 --- a/dom/html/nsBrowserElement.cpp +++ b/dom/html/nsBrowserElement.cpp @@ -603,7 +603,8 @@ nsBrowserElement::GenerateAllowedAudioChannels( RefPtr 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 ac = BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI, (AudioChannel)audioChannelTable[i].value, - aRv); + aManifestURL, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index 742f58e718e4..30ec8b742da3 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -136,7 +136,9 @@ this.SystemMessagePermissionsTable = { }, "first-run-with-sim": { "settings": ["read", "write"] - } + }, + "audiochannel-interruption-begin" : {}, + "audiochannel-interruption-ended" : {} }; diff --git a/dom/webidl/BrowserElementAudioChannel.webidl b/dom/webidl/BrowserElementAudioChannel.webidl index 8075dac6b28b..ccb6ad2c15a7 100644 --- a/dom/webidl/BrowserElementAudioChannel.webidl +++ b/dom/webidl/BrowserElementAudioChannel.webidl @@ -27,6 +27,9 @@ interface BrowserElementAudioChannel : EventTarget { [Throws] DOMRequest isActive(); + + [Throws] + DOMRequest notifyChannel(DOMString aEvent); }; partial interface BrowserElementPrivileged {