Bug 1214148 - patch 1 - propagation from the nested iframe back to the toplevel iframe, r=alwu
☠☠ backed out by 87e625f2b847 ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 09 Dec 2015 16:46:25 -0500
changeset 297684 3a4865d7941676ebe9dfa79d6dd2e4e14de77b50
parent 297683 a22ade61487c77f74e09723224470ca3c8dac7e5
child 297685 5f693237c8c1b7c4e632d25dac02c0fb3f156c03
push id8824
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:18:56 +0000
treeherdermozilla-aurora@e2031358e2a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersalwu
bugs1214148
milestone45.0a1
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/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/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.