Bug 1214148 - patch 1 - propagation from the nested iframe back to the toplevel iframe, r=alwu
☠☠ backed out by 306790649f34 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 11 Dec 2015 11:17:33 -0500
changeset 310349 afe3d65b74b61081d78dc3602c84690682108e0c
parent 310348 ee132b90ad4b11b03c4dcc5b21bc636ef11bbe66
child 310350 debd1ce280de9a629509c8178b31ad1c48ea8d88
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersalwu
bugs1214148
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1214148 - patch 1 - propagation from the nested iframe back to the toplevel iframe, r=alwu
dom/base/nsIFrameLoader.idl
dom/base/nsObjectLoadingContent.cpp
dom/browser-element/BrowserElementAudioChannel.cpp
dom/browser-element/mochitest/browserElement_AudioChannel_nested.js
dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html
dom/browser-element/mochitest/mochitest-oop.ini
dom/browser-element/mochitest/mochitest.ini
dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel_nested.html
dom/browser-element/mochitest/test_browserElement_oop_AudioChannel_nested.html
dom/html/nsBrowserElement.cpp
dom/html/nsBrowserElement.h
dom/html/nsGenericHTMLFrameElement.cpp
dom/interfaces/base/nsITabParent.idl
dom/system/gonk/AudioChannelManager.cpp
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -1,15 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "nsISupports.idl"
 
+interface mozIApplication;
 interface nsFrameLoader;
 interface nsIDocShell;
 interface nsIURI;
 interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
@@ -209,26 +210,32 @@ interface nsIFrameLoader : nsISupports
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
-[scriptable, uuid(c4abebcf-55f3-47d4-af15-151311971255)]
+[scriptable, uuid(adc1b3ba-8deb-4943-8045-e6de0044f2ce)]
 interface nsIFrameLoaderOwner : nsISupports
 {
   /**
    * The frame loader owned by this nsIFrameLoaderOwner
    */
   readonly attribute nsIFrameLoader frameLoader;
   [noscript, notxpcom] alreadyAddRefed_nsFrameLoader GetFrameLoader();
 
   /**
+   * The principal of parent mozIApplication in case of nested mozbrowser
+   * iframes.
+   */
+  readonly attribute mozIApplication parentApplication;
+
+  /**
    * Puts the FrameLoaderOwner in prerendering mode.
    */
   void setIsPrerendered();
 
   /**
    * Swap frame loaders with the given nsIFrameLoaderOwner.  This may
    * only be posible in a very limited range of circumstances, or
    * never, depending on the object implementing this interface.
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1211,16 +1211,27 @@ nsObjectLoadingContent::GetFrameLoader(n
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsObjectLoadingContent::GetFrameLoader()
 {
   RefPtr<nsFrameLoader> loader = mFrameLoader;
   return loader.forget();
 }
 
 NS_IMETHODIMP
+nsObjectLoadingContent::GetParentApplication(mozIApplication** aApplication)
+{
+  if (!aApplication) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aApplication = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsObjectLoadingContent::SetIsPrerendered()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
 {
--- a/dom/browser-element/BrowserElementAudioChannel.cpp
+++ b/dom/browser-element/BrowserElementAudioChannel.cpp
@@ -2,42 +2,34 @@
  * 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/Element.h"
+#include "mozilla/dom/TabParent.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);
-}
-
-} // anonymous namespace
-
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ADDREF_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
@@ -84,17 +76,16 @@ BrowserElementAudioChannel::BrowserEleme
   : 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;
     AudioChannelService::GetAudioChannelString(aAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
@@ -102,17 +93,16 @@ BrowserElementAudioChannel::BrowserEleme
 
     obs->AddObserver(this, topic.get(), true);
   }
 }
 
 BrowserElementAudioChannel::~BrowserElementAudioChannel()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsAutoString name;
     AudioChannelService::GetAudioChannelString(mAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
@@ -168,18 +158,16 @@ BrowserElementAudioChannel::WrapObject(J
 {
   return BrowserElementAudioChannelBinding::Wrap(aCx, this, aGivenProto);
 }
 
 AudioChannel
 BrowserElementAudioChannel::Name() const
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
-
   return mAudioChannel;
 }
 
 namespace {
 
 class BaseRunnable : public nsRunnable
 {
 protected:
@@ -356,17 +344,16 @@ RespondSuccessHandler::RejectedCallback(
 }
 
 } // anonymous namespace
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->GetAudioChannelVolume((uint32_t)mAudioChannel,
                                                     getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
@@ -382,17 +369,16 @@ BrowserElementAudioChannel::GetVolume(Er
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->SetAudioChannelVolume((uint32_t)mAudioChannel,
                                                     aVolume,
                                                     getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
@@ -415,17 +401,16 @@ BrowserElementAudioChannel::SetVolume(fl
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::GetMuted(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->GetAudioChannelMuted((uint32_t)mAudioChannel,
                                                    getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
@@ -441,17 +426,16 @@ BrowserElementAudioChannel::GetMuted(Err
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->SetAudioChannelMuted((uint32_t)mAudioChannel,
                                                    aMuted,
                                                    getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
@@ -474,17 +458,16 @@ BrowserElementAudioChannel::SetMuted(boo
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (mState != eStateUnknown) {
     RefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
 
     nsCOMPtr<nsIRunnable> runnable =
       new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel,
                            mState == eStateActive);
     NS_DispatchToMainThread(runnable);
@@ -588,18 +571,39 @@ BrowserElementAudioChannel::Observe(nsIS
     if (mTabParent == aSubject) {
       ProcessStateChanged(aData);
     }
 
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
-  if (NS_WARN_IF(!wrapper)) {
-    return NS_ERROR_FAILURE;
+  // This can be a nested iframe.
+  if (!wrapper) {
+    nsCOMPtr<nsITabParent> iTabParent = do_QueryInterface(aSubject);
+    if (!iTabParent) {
+      return NS_ERROR_FAILURE;
+    }
+
+    RefPtr<TabParent> tabParent = TabParent::GetFrom(iTabParent);
+    if (!tabParent) {
+      return NS_ERROR_FAILURE;
+    }
+
+    Element* element = tabParent->GetOwnerElement();
+    if (!element) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsPIDOMWindow> window = element->OwnerDoc()->GetWindow();
+    if (window == mFrameWindow) {
+      ProcessStateChanged(aData);
+    }
+
+    return NS_OK;
   }
 
   uint64_t windowID;
   nsresult rv = wrapper->GetData(&windowID);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_AudioChannel_nested.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1113086 - tests for AudioChannel API into BrowserElement
+
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+function runTests() {
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+  iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+
+  var listener = function(e) {
+    var message = e.detail.message;
+    if (/^OK/.exec(message)) {
+      ok(true, "Message from app: " + message);
+    } else if (/^KO/.exec(message)) {
+      ok(false, "Message from app: " + message);
+    } else if (/DONE/.exec(message)) {
+      ok(true, "Messaging from app complete");
+      iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
+    }
+  }
+
+  function audio_loadend() {
+    ok("mute" in iframe, "iframe.mute exists");
+    ok("unmute" in iframe, "iframe.unmute exists");
+    ok("getMuted" in iframe, "iframe.getMuted exists");
+    ok("getVolume" in iframe, "iframe.getVolume exists");
+    ok("setVolume" in iframe, "iframe.setVolume exists");
+
+    ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
+    var channels = iframe.allowedAudioChannels;
+    is(channels.length, 1, "1 audio channel by default");
+
+    var ac = channels[0];
+
+    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+    ok("getVolume" in ac, "ac.getVolume exists");
+    ok("setVolume" in ac, "ac.setVolume exists");
+    ok("getMuted" in ac, "ac.getMuted exists");
+    ok("setMuted" in ac, "ac.setMuted exists");
+    ok("isActive" in ac, "ac.isActive exists");
+
+    info("Setting the volume...");
+    ac.setVolume(0.5);
+
+    ac.onactivestatechanged = function() {
+      ok(true, "activestatechanged event received.");
+      ac.onactivestatechanged = null;
+      SimpleTest.finish();
+    }
+  }
+
+  iframe.addEventListener('mozbrowserloadend', audio_loadend);
+  iframe.addEventListener('mozbrowsershowmodalprompt', listener, false);
+  document.body.appendChild(iframe);
+
+  var context = { 'url': 'http://example.org',
+                  'appId': SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
+                  'isInBrowserElement': true };
+  SpecialPowers.pushPermissions([
+    {'type': 'browser', 'allow': 1, 'context': context},
+    {'type': 'embed-apps', 'allow': 1, 'context': context}
+  ], function() {
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html';
+  });
+}
+
+addEventListener('testready', function() {
+  SimpleTest.executeSoon(runTests);
+});
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html
@@ -0,0 +1,63 @@
+<html>
+<head>
+<script type="text/javascript">
+
+  function ok(a, msg) {
+    alert((!!a ? "OK" : "KO") + " " + msg);
+  }
+
+  function is(a, b, msg) {
+    ok(a === b, msg);
+  }
+
+  function finish(a, b, msg) {
+    alert("DONE");
+  }
+
+  addEventListener('load', function(e) {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('mozbrowser', 'true');
+    // set 'remote' to true here will make the the iframe remote in _inproc_
+    // test and in-process in _oop_  test.
+    iframe.setAttribute('remote', 'true');
+    iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+
+    iframe.addEventListener('mozbrowserloadend', function(e) {
+      ok("mute" in iframe, "iframe.mute exists");
+      ok("unmute" in iframe, "iframe.unmute exists");
+      ok("getMuted" in iframe, "iframe.getMuted exists");
+      ok("getVolume" in iframe, "iframe.getVolume exists");
+      ok("setVolume" in iframe, "iframe.setVolume exists");
+
+      ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
+      var channels = iframe.allowedAudioChannels;
+      is(channels.length, 1, "1 audio channel by default");
+
+      var ac = channels[0];
+
+      ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+      ok("getVolume" in ac, "ac.getVolume exists");
+      ok("setVolume" in ac, "ac.setVolume exists");
+      ok("getMuted" in ac, "ac.getMuted exists");
+      ok("setMuted" in ac, "ac.setMuted exists");
+      ok("isActive" in ac, "ac.isActive exists");
+
+      ac.onactivestatechanged = function() {
+        ok("activestatechanged event received.");
+
+        ac.getVolume().onsuccess = function(e) {
+          ok(e.target.result, 1, "Default volume is 1");
+        };
+
+        finish();
+      }
+    });
+
+    document.body.appendChild(iframe);
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_audio.html';
+  });
+</script>
+</head>
+<body>
+</body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -116,12 +116,13 @@ disabled = bug 930449
 [test_browserElement_oop_CloseFromOpener.html]
 disabled = bug 924771
 [test_browserElement_oop_CloseApp.html]
 disabled = bug 924771
 [test_browserElement_oop_ExposableURI.html]
 disabled = bug 924771
 [test_browserElement_oop_GetContentDimensions.html]
 [test_browserElement_oop_AudioChannel.html]
+[test_browserElement_oop_AudioChannel_nested.html]
 [test_browserElement_oop_SetNFCFocus.html]
 [test_browserElement_oop_getWebManifest.html]
 [test_browserElement_oop_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -78,21 +78,23 @@ support-files =
   browserElement_VisibilityChange.js
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
   browserElement_AudioChannel.js
+  browserElement_AudioChannel_nested.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
+  file_browserElement_AudioChannel_nested.html
   file_browserElement_Viewmode.html
   file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
   file_browserElement_DisallowEmbedAppsInOOP.html
   file_browserElement_ExecuteScript.html
@@ -245,12 +247,13 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_XFrameOptionsSameOrigin.html]
 [test_browserElement_oop_NextPaint.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 [test_browserElement_inproc_Reload.html]
 disabled = bug 774100
 [test_browserElement_inproc_GetContentDimensions.html]
 [test_browserElement_inproc_AudioChannel.html]
+[test_browserElement_inproc_AudioChannel_nested.html]
 [test_browserElement_inproc_SetNFCFocus.html]
 [test_browserElement_inproc_getStructuredData.html]
 [test_browserElement_inproc_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel_nested.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test of browser element audioChannel in nested mozbrowser iframes.</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_AudioChannel_nested.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test of browser element audioChannel.</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
+</script>
+</body>
+</html>
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -558,36 +558,43 @@ nsBrowserElement::GetAllowedAudioChannel
     }
 
     nsAutoString manifestURL;
     aRv = mozBrowserFrame->GetAppManifestURL(manifestURL);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
+    nsCOMPtr<mozIApplication> parentApp;
+    aRv = GetParentApplication(getter_AddRefs(parentApp));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
             ("nsBrowserElement, GetAllowedAudioChannels, this = %p\n", this));
 
     GenerateAllowedAudioChannels(window, frameLoader, mBrowserElementAPI,
-                                 manifestURL, mBrowserElementAudioChannels,
-                                 aRv);
+                                 manifestURL, parentApp,
+                                 mBrowserElementAudioChannels, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   }
 
   aAudioChannels.AppendElements(mBrowserElementAudioChannels);
 }
 
 /* static */ void
 nsBrowserElement::GenerateAllowedAudioChannels(
                  nsPIDOMWindow* aWindow,
                  nsIFrameLoader* aFrameLoader,
                  nsIBrowserElementAPI* aAPI,
                  const nsAString& aManifestURL,
+                 mozIApplication* aParentApp,
                  nsTArray<RefPtr<BrowserElementAudioChannel>>& aAudioChannels,
                  ErrorResult& aRv)
 {
   MOZ_ASSERT(aAudioChannels.IsEmpty());
 
   nsCOMPtr<nsIAppsService> appsService =
     do_GetService("@mozilla.org/AppsService;1");
   if (NS_WARN_IF(!appsService)) {
@@ -620,16 +627,29 @@ nsBrowserElement::GenerateAllowedAudioCh
 
     bool allowed;
     nsAutoCString permissionName;
 
     for (uint32_t i = 0; audioChannelTable && audioChannelTable[i].tag; ++i) {
       permissionName.AssignASCII("audio-channel-");
       permissionName.AppendASCII(audioChannelTable[i].tag);
 
+      // In case of nested iframes we want to check if the parent has the
+      // permission to use this AudioChannel.
+      if (aParentApp) {
+        aRv = aParentApp->HasPermission(permissionName.get(), &allowed);
+        if (NS_WARN_IF(aRv.Failed())) {
+          return;
+        }
+
+        if (!allowed) {
+          continue;
+        }
+      }
+
       aRv = app->HasPermission(permissionName.get(), &allowed);
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
 
       if (allowed) {
         RefPtr<BrowserElementAudioChannel> ac =
           BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -120,21 +120,24 @@ public:
                    ErrorResult& aRv);
 
   // Helper
   static void GenerateAllowedAudioChannels(
                  nsPIDOMWindow* aWindow,
                  nsIFrameLoader* aFrameLoader,
                  nsIBrowserElementAPI* aAPI,
                  const nsAString& aManifestURL,
+                 mozIApplication* aParentApp,
                  nsTArray<RefPtr<dom::BrowserElementAudioChannel>>& aAudioChannels,
                  ErrorResult& aRv);
 
 protected:
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
+  NS_IMETHOD GetParentApplication(mozIApplication** aApplication) = 0;
+
   void InitBrowserElementAPI();
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
   nsTArray<RefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;
 
 private:
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
   bool IsNotWidgetOrThrow(ErrorResult& aRv);
   bool mOwnerIsWidget;
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -187,16 +187,45 @@ nsGenericHTMLFrameElement::GetFrameLoade
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsGenericHTMLFrameElement::GetFrameLoader()
 {
   RefPtr<nsFrameLoader> loader = mFrameLoader;
   return loader.forget();
 }
 
 NS_IMETHODIMP
+nsGenericHTMLFrameElement::GetParentApplication(mozIApplication** aApplication)
+{
+  if (!aApplication) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aApplication = nullptr;
+
+  uint32_t appId;
+  nsIPrincipal *principal = NodePrincipal();
+  nsresult rv = principal->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!appsService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = appsService->GetAppByLocalId(appId, aApplication);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
 {
   // We don't support this yet
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::SetIsPrerendered()
--- a/dom/interfaces/base/nsITabParent.idl
+++ b/dom/interfaces/base/nsITabParent.idl
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "domstubs.idl"
 
-[scriptable, uuid(7615408c-1fb3-4128-8dd5-a3e2f3fa8842)]
+[builtinclass, scriptable, uuid(8e49f7b0-1f98-4939-bf91-e9c39cd56434)]
 interface nsITabParent : nsISupports
 {
   void injectTouchEvent(in AString aType,
                         [array, size_is(count)] in uint32_t aIdentifiers,
                         [array, size_is(count)] in int32_t aXs,
                         [array, size_is(count)] in int32_t aYs,
                         [array, size_is(count)] in uint32_t aRxs,
                         [array, size_is(count)] in uint32_t aRys,
--- a/dom/system/gonk/AudioChannelManager.cpp
+++ b/dom/system/gonk/AudioChannelManager.cpp
@@ -208,16 +208,16 @@ AudioChannelManager::GetAllowedAudioChan
 
   nsAutoString manifestURL;
   aRv = appsService->GetManifestURLByLocalId(appId, manifestURL);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsBrowserElement::GenerateAllowedAudioChannels(window, nullptr, nullptr,
-                                                 manifestURL, aAudioChannels,
-                                                 aRv);
+                                                 manifestURL, nullptr,
+                                                 aAudioChannels, aRv);
   NS_WARN_IF(aRv.Failed());
 }
 
 } // namespace system
 } // namespace dom
 } // namespace mozilla
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1612,16 +1612,27 @@ nsXULElement::GetFrameLoader()
     if (!slots)
         return nullptr;
 
     RefPtr<nsFrameLoader> loader = slots->mFrameLoader;
     return loader.forget();
 }
 
 nsresult
+nsXULElement::GetParentApplication(mozIApplication** aApplication)
+{
+    if (!aApplication) {
+        return NS_ERROR_FAILURE;
+    }
+
+    *aApplication = nullptr;
+    return NS_OK;
+}
+
+nsresult
 nsXULElement::SetIsPrerendered()
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr,
                  NS_LITERAL_STRING("true"), true);
 }
 
 nsresult
 nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -407,16 +407,17 @@ public:
 
     // nsIDOMXULElement
     NS_DECL_NSIDOMXULELEMENT
 
     virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
     virtual mozilla::EventStates IntrinsicState() const override;
 
     nsresult GetFrameLoader(nsIFrameLoader** aFrameLoader);
+    nsresult GetParentApplication(mozIApplication** aApplication);
     nsresult SetIsPrerendered();
     nsresult SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner);
 
     virtual void RecompileScriptEventListeners() override;
 
     // This function should ONLY be used by BindToTree implementations.
     // The function exists solely because XUL elements store the binding
     // parent as a member instead of in the slots, as Element does.