Bug 1206581 - Implement notifyChannel() on AudioChannel API. r=kanru, r=baku
☠☠ backed out by 8f42cacc508e ☠ ☠
authorAlastor Wu <alwu@mozilla.com>
Wed, 04 Nov 2015 10:33:27 +0800
changeset 306850 0fcb7abf3523a32868dc0b66b5ef02432a304801
parent 306849 9875f804994f1521d9d60bc3c34c0ac4fa4c667f
child 306851 0f15d62411bfabd985586bb7e75d33702af0a7a3
push id7204
push usercku@mozilla.com
push dateThu, 05 Nov 2015 15:38:26 +0000
reviewerskanru, baku
bugs1206581
milestone45.0a1
Bug 1206581 - Implement notifyChannel() on AudioChannel API. r=kanru, r=baku
dom/browser-element/BrowserElementAudioChannel.cpp
dom/browser-element/BrowserElementAudioChannel.h
dom/browser-element/BrowserElementParent.js
dom/browser-element/mochitest/browserElement_NotifyChannel.js
dom/browser-element/mochitest/chrome.ini
dom/browser-element/mochitest/file_browserElement_NotifyChannel.html
dom/browser-element/mochitest/manifest.webapp
dom/browser-element/mochitest/manifest.webapp^headers^
dom/browser-element/mochitest/test_browserElement_NotifyChannel.html
dom/browser-element/moz.build
dom/browser-element/nsIBrowserElementAPI.idl
dom/html/nsBrowserElement.cpp
dom/messages/SystemMessagePermissionsChecker.jsm
dom/webidl/BrowserElementAudioChannel.webidl
--- a/dom/browser-element/BrowserElementAudioChannel.cpp
+++ b/dom/browser-element/BrowserElementAudioChannel.cpp
@@ -2,25 +2,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BrowserElementAudioChannel.h"
 
 #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 {
 
 void
 AssertIsInMainProcess()
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 }
@@ -45,38 +51,42 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Brows
                                    mTabParent,
                                    mBrowserElementAPI)
 
 /* static */ already_AddRefed<BrowserElementAudioChannel>
 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())) {
     return nullptr;
   }
 
   return ac.forget();
 }
 
 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());
   AssertIsInMainProcess();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsAutoString name;
@@ -297,16 +307,53 @@ public:
 protected:
   virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override
   {
     JS::Rooted<JS::Value> value(aCx);
     mRequest->FireSuccess(value);
   }
 };
 
+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>
 BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   AssertIsInMainProcess();
 
@@ -454,16 +501,72 @@ BrowserElementAudioChannel::IsActive(Err
 
   nsCOMPtr<nsIRunnable> runnable =
     new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel);
   NS_DispatchToMainThread(runnable);
 
   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)
 {
   nsAutoString name;
   AudioChannelService::GetAudioChannelString(mAudioChannel, name);
 
   nsAutoCString topic;
--- a/dom/browser-element/BrowserElementAudioChannel.h
+++ b/dom/browser-element/BrowserElementAudioChannel.h
@@ -35,16 +35,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BrowserElementAudioChannel,
                                            DOMEventTargetHelper)
 
   static already_AddRefed<BrowserElementAudioChannel>
   Create(nsPIDOMWindow* aWindow,
          nsIFrameLoader* aFrameLoader,
          nsIBrowserElementAPI* aAPI,
          AudioChannel aAudioChannel,
+         const nsAString& aManifestURL,
          ErrorResult& aRv);
 
   // WebIDL methods
 
   virtual JSObject* WrapObject(JSContext *aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   AudioChannel Name() const;
@@ -52,35 +53,40 @@ public:
   already_AddRefed<dom::DOMRequest> GetVolume(ErrorResult& aRv);
   already_AddRefed<dom::DOMRequest> SetVolume(float aVolume, ErrorResult& aRv);
 
   already_AddRefed<dom::DOMRequest> GetMuted(ErrorResult& aRv);
   already_AddRefed<dom::DOMRequest> SetMuted(bool aMuted, ErrorResult& aRv);
 
   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();
 
   nsresult Initialize();
 
   void ProcessStateChanged(const char16_t* aData);
 
   nsCOMPtr<nsIFrameLoader> mFrameLoader;
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
   nsCOMPtr<nsITabParent> mTabParent;
   nsCOMPtr<nsPIDOMWindow> mFrameWindow;
   AudioChannel mAudioChannel;
+  nsString mManifestURL;
 
   enum {
     eStateActive,
     eStateInactive,
     eStateUnknown
   } mState;
 };
 
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -18,16 +18,20 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function () {
   Cu.import("resource://gre/modules/Webapps.jsm");
   return DOMApplicationRegistry;
 });
 
+XPCOMUtils.defineLazyServiceGetter(this, "systemMessenger",
+                                   "@mozilla.org/system-message-internal;1",
+                                   "nsISystemMessagesInternal");
+
 function debug(msg) {
   //dump("BrowserElementParent - " + msg + "\n");
 }
 
 function getIntPref(prefName, def) {
   try {
     return Services.prefs.getIntPref(prefName);
   }
@@ -1203,16 +1207,36 @@ BrowserElementParent.prototype = {
                                  muted: aMuted});
   },
 
   isAudioChannelActive: function(aAudioChannel) {
     return this._sendDOMRequest('get-is-audio-channel-active',
                                 {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'),
 
   /**
    * Called when the visibility of the window which owns this iframe changes.
    */
   _ownerVisibilityChange: function() {
     this._sendAsyncMsg('owner-visibility-change',
                        {visible: !this._window.document.hidden});
new file mode 100644
--- /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;
+}
new file mode 100644
--- /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
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_NotifyChannel.html
@@ -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>
new file mode 100644
--- /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" }
+  ]
+}
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/manifest.webapp^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/manifest+json
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_NotifyChannel.html
@@ -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>
--- a/dom/browser-element/moz.build
+++ b/dom/browser-element/moz.build
@@ -49,11 +49,12 @@ LOCAL_INCLUDES += [
     '/dom/ipc',
 ]
 
 MOCHITEST_MANIFESTS += [
     'mochitest/mochitest-oop.ini',
     'mochitest/mochitest.ini',
     'mochitest/priority/mochitest.ini',
 ]
+MOCHITEST_CHROME_MANIFESTS += ['mochitest/chrome.ini']
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
--- a/dom/browser-element/nsIBrowserElementAPI.idl
+++ b/dom/browser-element/nsIBrowserElementAPI.idl
@@ -21,17 +21,17 @@ interface nsIBrowserElementNextPaintList
     { 0x651db7e3, 0x1734, 0x4536,                               \
       { 0xb1, 0x5a, 0x5b, 0x3a, 0xe6, 0x44, 0x13, 0x4c } }
 %}
 
 /**
  * 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;
   const long FIND_CASE_INSENSITIVE = 1;
 
   const long FIND_FORWARD = 0;
   const long FIND_BACKWARD = 1;
 
@@ -92,16 +92,20 @@ interface nsIBrowserElementAPI : nsISupp
   nsIDOMDOMRequest getAudioChannelVolume(in uint32_t audioChannel);
   nsIDOMDOMRequest setAudioChannelVolume(in uint32_t audioChannel, in float volume);
 
   nsIDOMDOMRequest getAudioChannelMuted(in uint32_t audioChannel);
   nsIDOMDOMRequest setAudioChannelMuted(in uint32_t audioChannel, in bool muted);
 
   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);
 
   /**
    * Returns a JSON string representing Microdata objects on the page.
    * Format is described at:
    *   https://html.spec.whatwg.org/multipage/microdata.html#json
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -598,17 +598,18 @@ nsBrowserElement::GenerateAllowedAudioCh
     return;
   }
 
   // Normal is always allowed.
   nsTArray<RefPtr<BrowserElementAudioChannel>> channels;
 
   RefPtr<BrowserElementAudioChannel> ac =
     BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
-                                       AudioChannel::Normal, aRv);
+                                       AudioChannel::Normal,
+                                       aManifestURL, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   channels.AppendElement(ac);
 
   if (app) {
     const nsAttrValue::EnumTable* audioChannelTable =
@@ -625,17 +626,17 @@ nsBrowserElement::GenerateAllowedAudioCh
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
 
       if (allowed) {
         RefPtr<BrowserElementAudioChannel> ac =
           BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
                                              (AudioChannel)audioChannelTable[i].value,
-                                             aRv);
+                                             aManifestURL, aRv);
         if (NS_WARN_IF(aRv.Failed())) {
           return;
         }
 
         channels.AppendElement(ac);
       }
     }
   }
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -131,17 +131,19 @@ this.SystemMessagePermissionsTable = {
   "nfc-manager-send-file": {
     "nfc-manager": []
   },
   "wifip2p-pairing-request": {
     "wifi-manage": []
   },
   "first-run-with-sim": {
     "settings": ["read", "write"]
-  }
+  },
+  "audiochannel-interruption-begin" : {},
+  "audiochannel-interruption-ended" : {}
 };
 
 
 this.SystemMessagePermissionsChecker = {
   /**
    * Return all the needed permission names for the given system message.
    * @param string aSysMsgName
    *        The system messsage name.
--- a/dom/webidl/BrowserElementAudioChannel.webidl
+++ b/dom/webidl/BrowserElementAudioChannel.webidl
@@ -22,16 +22,19 @@ interface BrowserElementAudioChannel : E
   [Throws]
   DOMRequest getMuted();
 
   [Throws]
   DOMRequest setMuted(boolean aMuted);
 
   [Throws]
   DOMRequest isActive();
+
+  [Throws]
+  DOMRequest notifyChannel(DOMString aEvent);
 };
 
 partial interface BrowserElementPrivileged {
   [Pure, Cached, Throws,
    Pref="dom.mozBrowserFramesEnabled",
    CheckAnyPermissions="browser"]
   readonly attribute sequence<BrowserElementAudioChannel> allowedAudioChannels;