Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 12 Oct 2018 19:56:32 +0300
changeset 499320 ede21c2f2f993c7bb00d37b1e52b62c9f0c4c671
parent 499306 7fd59dc00149b33ede6efb0ed848eef227cae540 (current diff)
parent 499319 fbf50f6e40718aa2c8e1e1116e858ac5a3d3e26e (diff)
child 499321 f2d7836b93f9ad88ef27388df27be1e6510bcdb8
child 499353 f1d7d48f099d67b42484b568ea7e67983cca1914
child 499527 ba1ab0b667800182fbcb56a5ffc7b0933fe50433
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.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
Merge inbound to mozilla-central. a=merge
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3019,61 +3019,52 @@ nsIDocument::InitFeaturePolicy(nsIChanne
   // we need to create a policy here so getting the policy within
   // ::Policy() can *always* return a non null policy
   mFeaturePolicy = new FeaturePolicy(this);
 
   if (!StaticPrefs::dom_security_featurePolicy_enabled()) {
     return NS_OK;
   }
 
-  nsAutoString origin;
-  nsresult rv = nsContentUtils::GetUTFOrigin(NodePrincipal(), origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  mFeaturePolicy->SetDefaultOrigin(origin);
+  mFeaturePolicy->SetDefaultOrigin(NodePrincipal());
 
   RefPtr<FeaturePolicy> parentPolicy = nullptr;
   if (mDocumentContainer) {
     nsPIDOMWindowOuter* containerWindow = mDocumentContainer->GetWindow();
     if (containerWindow) {
       nsCOMPtr<nsINode> node = containerWindow->GetFrameElementInternal();
-      if (node) {
-        HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(node);
-        if (iframe) {
-          parentPolicy = iframe->Policy();
-        }
+      HTMLIFrameElement* iframe = HTMLIFrameElement::FromNodeOrNull(node);
+      if (iframe) {
+        parentPolicy = iframe->Policy();
       }
     }
   }
 
   if (parentPolicy) {
     // Let's inherit the policy from the parent HTMLIFrameElement if it exists.
     mFeaturePolicy->InheritPolicy(parentPolicy);
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel;
-  rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
+  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!httpChannel) {
     return NS_OK;
   }
 
   // query the policy from the header
   nsAutoCString value;
   rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Feature-Policy"),
                                       value);
   if (NS_SUCCEEDED(rv)) {
     mFeaturePolicy->SetDeclaredPolicy(this, NS_ConvertUTF8toUTF16(value),
-                                      origin, EmptyString(),
-                                      false /* 'src' enabled */);
+                                      NodePrincipal(), nullptr);
   }
 
   return NS_OK;
 }
 
 void
 nsDocument::StopDocumentLoad()
 {
@@ -10223,20 +10214,19 @@ nsIDocument::MaybeResolveReadyForIdle()
   if (readyPromise) {
     readyPromise->MaybeResolve(this);
   }
 }
 
 FeaturePolicy*
 nsIDocument::Policy() const
 {
-  MOZ_ASSERT(StaticPrefs::dom_security_featurePolicy_enabled());
-
   // The policy is created when the document is initialized. We _must_ have a
-  // policy here.
+  // policy here even if the featurePolicy pref is off. If this assertion fails,
+  // it means that ::Policy() is called before ::StartDocumentLoad().
   MOZ_ASSERT(mFeaturePolicy);
   return mFeaturePolicy;
 }
 
 nsIDOMXULCommandDispatcher*
 nsIDocument::GetCommandDispatcher()
 {
   // Only chrome documents are allowed to use command dispatcher.
--- a/dom/html/HTMLIFrameElement.cpp
+++ b/dom/html/HTMLIFrameElement.cpp
@@ -3,45 +3,63 @@
 /* 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 "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/HTMLIFrameElementBinding.h"
 #include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/MappedDeclarations.h"
+#include "mozilla/NullPrincipal.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsMappedAttributes.h"
 #include "nsAttrValueInlines.h"
 #include "nsError.h"
 #include "nsStyleConsts.h"
 #include "nsContentUtils.h"
 #include "nsSandboxFlags.h"
 #include "nsNetUtil.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame)
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLIFrameElement)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLIFrameElement,
+                                                  nsGenericHTMLFrameElement)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLIFrameElement,
+                                                nsGenericHTMLFrameElement)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_ADDREF_INHERITED(HTMLIFrameElement, nsGenericHTMLFrameElement)
+NS_IMPL_RELEASE_INHERITED(HTMLIFrameElement, nsGenericHTMLFrameElement)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLIFrameElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLFrameElement)
+
 // static
 const DOMTokenListSupportedToken HTMLIFrameElement::sSupportedSandboxTokens[] = {
 #define SANDBOX_KEYWORD(string, atom, flags) string,
 #include "IframeSandboxKeywordList.h"
 #undef SANDBOX_KEYWORD
   nullptr
 };
 
 HTMLIFrameElement::HTMLIFrameElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                                      FromParser aFromParser)
   : nsGenericHTMLFrameElement(std::move(aNodeInfo), aFromParser)
 {
-  if (StaticPrefs::dom_security_featurePolicy_enabled()) {
-    mFeaturePolicy = new FeaturePolicy(this);
-  }
+  // We always need a featurePolicy, even if not exposed.
+  mFeaturePolicy = new FeaturePolicy(this);
 }
 
 HTMLIFrameElement::~HTMLIFrameElement()
 {
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement)
 
@@ -169,16 +187,17 @@ HTMLIFrameElement::AfterSetAttr(int32_t 
         // If we have an nsFrameLoader, apply the new sandbox flags.
         // Since this is called after the setter, the sandbox flags have
         // alreay been updated.
         mFrameLoader->ApplySandboxFlags(GetSandboxFlags());
       }
     }
     if ((aName == nsGkAtoms::allow ||
          aName == nsGkAtoms::src ||
+         aName == nsGkAtoms::srcdoc ||
          aName == nsGkAtoms::sandbox ||
          aName == nsGkAtoms::allowpaymentrequest) &&
         StaticPrefs::dom_security_featurePolicy_enabled()) {
       RefreshFeaturePolicy();
     }
   }
   return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName,
                                                  aValue, aOldValue,
@@ -226,89 +245,62 @@ JSObject*
 HTMLIFrameElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLIFrameElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 FeaturePolicy*
 HTMLIFrameElement::Policy() const
 {
-  MOZ_ASSERT(StaticPrefs::dom_security_featurePolicy_enabled());
   return mFeaturePolicy;
 }
 
-nsresult
-HTMLIFrameElement::GetFeaturePolicyDefaultOrigin(nsAString& aDefaultOrigin) const
+already_AddRefed<nsIPrincipal>
+HTMLIFrameElement::GetFeaturePolicyDefaultOrigin() const
 {
-  aDefaultOrigin.Truncate();
+  nsCOMPtr<nsIPrincipal> principal;
 
-  nsresult rv;
-  nsAutoString src;
-  GetURIAttr(nsGkAtoms::src, nullptr, src);
+  if (HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc)) {
+    principal = NodePrincipal();
+    return principal.forget();
+  }
 
   nsCOMPtr<nsIURI> nodeURI;
-  if (!src.IsEmpty()) {
-    nsCOMPtr<nsIURI> baseURI = OwnerDoc()->GetBaseURI();
-
-    rv = NS_NewURI(getter_AddRefs(nodeURI), src,
-                   OwnerDoc()->GetDocumentCharacterSet(),
-                   baseURI);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      nodeURI = nullptr;
-    }
+  if (GetURIAttr(nsGkAtoms::src, nullptr, getter_AddRefs(nodeURI)) &&
+      nodeURI) {
+    principal =
+      BasePrincipal::CreateCodebasePrincipal(nodeURI,
+                                             BasePrincipal::Cast(NodePrincipal())->OriginAttributesRef());
   }
 
-  if (!nodeURI) {
-    if (OwnerDoc()->GetSandboxFlags() & SANDBOXED_ORIGIN) {
-      return NS_OK;
-    }
-
-    nodeURI = OwnerDoc()->GetDocumentURI();
+  if (!principal) {
+    principal = NodePrincipal();
   }
 
-  nsAutoString origin;
-  rv = nsContentUtils::GetUTFOrigin(nodeURI, origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  aDefaultOrigin.Assign(origin);
-  return NS_OK;
+  return principal.forget();
 }
 
 void
 HTMLIFrameElement::RefreshFeaturePolicy()
 {
   MOZ_ASSERT(StaticPrefs::dom_security_featurePolicy_enabled());
   mFeaturePolicy->ResetDeclaredPolicy();
 
-  // The origin can change if 'src' attribute changes.
-  nsAutoString origin;
-  nsresult rv = GetFeaturePolicyDefaultOrigin(origin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
+  // The origin can change if 'src' and 'srcdoc' attributes change.
+  nsCOMPtr<nsIPrincipal> origin = GetFeaturePolicyDefaultOrigin();
+  MOZ_ASSERT(origin);
   mFeaturePolicy->SetDefaultOrigin(origin);
 
   nsAutoString allow;
   GetAttr(nsGkAtoms::allow, allow);
 
   if (!allow.IsEmpty()) {
-    nsAutoString documentOrigin;
-    if (OwnerDoc()->GetSandboxFlags() ^ SANDBOXED_ORIGIN) {
-      nsresult rv = nsContentUtils::GetUTFOrigin(NodePrincipal(), documentOrigin);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-      }
-    }
-
     // Set or reset the FeaturePolicy directives.
-    mFeaturePolicy->SetDeclaredPolicy(OwnerDoc(), allow, documentOrigin,
-                                      origin, true /* 'src' enabled */);
+    mFeaturePolicy->SetDeclaredPolicy(OwnerDoc(), allow, NodePrincipal(),
+                                      origin);
   }
 
   mFeaturePolicy->InheritPolicy(OwnerDoc()->Policy());
 
   if (AllowPaymentRequest()) {
     mFeaturePolicy->MaybeSetAllowedPolicy(NS_LITERAL_STRING("payment"));
   }
 
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -3,33 +3,35 @@
 /* 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/. */
 
 #ifndef mozilla_dom_HTMLIFrameElement_h
 #define mozilla_dom_HTMLIFrameElement_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/FeaturePolicy.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "nsDOMTokenList.h"
 
 namespace mozilla {
 namespace dom {
 
 class HTMLIFrameElement final : public nsGenericHTMLFrameElement
 {
 public:
   explicit HTMLIFrameElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                              FromParser aFromParser = NOT_FROM_PARSER);
 
   NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLIFrameElement, iframe)
 
   // nsISupports
-  NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLIFrameElement,
-                                       nsGenericHTMLFrameElement)
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLIFrameElement,
+                                           nsGenericHTMLFrameElement)
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override
   {
     return true;
   }
 
   // nsIContent
@@ -218,27 +220,33 @@ protected:
 private:
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     MappedDeclarations&);
 
   static const DOMTokenListSupportedToken sSupportedSandboxTokens[];
 
   void RefreshFeaturePolicy();
 
-  nsresult
-  GetFeaturePolicyDefaultOrigin(nsAString& aDefaultOrigin) const;
+  // If this iframe has a 'srcdoc' attribute, the document's origin will be
+  // returned. Otherwise, if this iframe has a 'src' attribute, the origin will
+  // be the parsing of its value as URL. If the URL is invalid, or 'src'
+  // attribute doesn't exist, the origin will be the document's origin.
+  already_AddRefed<nsIPrincipal>
+  GetFeaturePolicyDefaultOrigin() const;
 
   /**
    * This function is called by AfterSetAttr and OnAttrSetButNotChanged.
    * This function will be called by AfterSetAttr whether the attribute is being
    * set or unset.
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
    * @param aNotify Whether we plan to notify document observers.
    */
   void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName, bool aNotify);
+
+  RefPtr<mozilla::dom::FeaturePolicy> mFeaturePolicy;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -30,29 +30,27 @@ using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement,
                                                 nsGenericHTMLElement)
   if (tmp->mFrameLoader) {
     tmp->mFrameLoader->Destroy();
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(nsGenericHTMLFrameElement,
                                              nsGenericHTMLElement,
                                              nsIFrameLoaderOwner,
                                              nsIDOMMozBrowserFrame,
                                              nsIMozBrowserFrame,
                                              nsGenericHTMLFrameElement)
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -5,17 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsGenericHTMLFrameElement_h
 #define nsGenericHTMLFrameElement_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/nsBrowserElement.h"
-#include "mozilla/dom/FeaturePolicy.h"
 
 #include "nsFrameLoader.h"
 #include "nsGenericHTMLElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIFrameLoaderOwner.h"
 #include "nsIMozBrowserFrame.h"
 
 namespace mozilla {
@@ -123,19 +122,16 @@ protected:
                                           const nsAttrValueOrString& aValue,
                                           bool aNotify) override;
 
   RefPtr<nsFrameLoader> mFrameLoader;
   nsCOMPtr<nsPIDOMWindowOuter> mOpenerWindow;
 
   nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal;
 
-  // Used by <iframe> only.
-  RefPtr<mozilla::dom::FeaturePolicy> mFeaturePolicy;
-
   /**
    * True if we have already loaded the frame's original src
    */
   bool mSrcLoadHappened;
 
   /**
    * True when the element is created by the parser using the
    * NS_FROM_PARSER_NETWORK flag.
--- a/dom/security/featurepolicy/Feature.cpp
+++ b/dom/security/featurepolicy/Feature.cpp
@@ -1,98 +1,108 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "Feature.h"
+#include "mozilla/BasePrincipal.h"
 
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
 
 void
-Feature::GetWhiteListedOrigins(nsTArray<nsString>& aList) const
+Feature::GetAllowList(nsTArray<nsCOMPtr<nsIPrincipal>>& aList) const
 {
-  MOZ_ASSERT(mPolicy == eWhiteList);
-  aList.AppendElements(mWhiteListedOrigins);
+  MOZ_ASSERT(mPolicy == eAllowList);
+  aList.AppendElements(mAllowList);
 }
 
 bool
-Feature::Allows(const nsAString& aOrigin) const
+Feature::Allows(nsIPrincipal* aPrincipal) const
 {
   if (mPolicy == eNone) {
     return false;
   }
 
   if (mPolicy == eAll) {
     return true;
   }
 
-  for (const nsString& whiteListedOrigin : mWhiteListedOrigins) {
-    if (whiteListedOrigin.Equals(aOrigin)) {
-      return true;
-    }
-  }
-
-  return false;
+  return AllowListContains(aPrincipal);
 }
 
 Feature::Feature(const nsAString& aFeatureName)
   : mFeatureName(aFeatureName)
-  , mPolicy(eWhiteList)
+  , mPolicy(eAllowList)
 {}
 
 Feature::~Feature() = default;
 
 const nsAString&
 Feature::Name() const
 {
   return mFeatureName;
 }
 
 void
 Feature::SetAllowsNone()
 {
   mPolicy = eNone;
-  mWhiteListedOrigins.Clear();
+  mAllowList.Clear();
 }
 
 bool
 Feature::AllowsNone() const
 {
   return mPolicy == eNone;
 }
 
 void
 Feature::SetAllowsAll()
 {
   mPolicy = eAll;
-  mWhiteListedOrigins.Clear();
+  mAllowList.Clear();
 }
 
 bool
 Feature::AllowsAll() const
 {
   return mPolicy == eAll;
 }
 
 void
-Feature::AppendOriginToWhiteList(const nsAString& aOrigin)
+Feature::AppendToAllowList(nsIPrincipal* aPrincipal)
 {
-  mPolicy = eWhiteList;
-  mWhiteListedOrigins.AppendElement(aOrigin);
+  MOZ_ASSERT(aPrincipal);
+
+  mPolicy = eAllowList;
+  mAllowList.AppendElement(aPrincipal);
 }
 
 bool
-Feature::WhiteListContains(const nsAString& aOrigin) const
+Feature::AllowListContains(nsIPrincipal* aPrincipal) const
 {
-  if (!IsWhiteList()) {
+  MOZ_ASSERT(aPrincipal);
+
+  if (!HasAllowList()) {
     return false;
   }
 
-  return mWhiteListedOrigins.Contains(aOrigin);
+  for (nsIPrincipal* principal : mAllowList) {
+    if (BasePrincipal::Cast(principal)->Subsumes(aPrincipal,
+                                                 BasePrincipal::ConsiderDocumentDomain)) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 bool
-Feature::IsWhiteList() const
+Feature::HasAllowList() const
 {
-  return mPolicy == eWhiteList;
+  return mPolicy == eAllowList;
 }
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/security/featurepolicy/Feature.h
+++ b/dom/security/featurepolicy/Feature.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_dom_Feature_h
 #define mozilla_dom_Feature_h
 
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsCOMPtr.h"
 
+class nsIPrincipal;
+
 namespace mozilla {
 namespace dom {
 
 class Feature final
 {
 public:
   explicit Feature(const nsAString& aFeatureName);
 
@@ -32,45 +34,45 @@ public:
 
   void
   SetAllowsAll();
 
   bool
   AllowsAll() const;
 
   void
-  AppendOriginToWhiteList(const nsAString& aOrigin);
+  AppendToAllowList(nsIPrincipal* aPrincipal);
 
   void
-  GetWhiteListedOrigins(nsTArray<nsString>& aList) const;
+  GetAllowList(nsTArray<nsCOMPtr<nsIPrincipal>>& aList) const;
 
   bool
-  WhiteListContains(const nsAString& aOrigin) const;
+  AllowListContains(nsIPrincipal* aPrincipal) const;
 
   bool
-  IsWhiteList() const;
+  HasAllowList() const;
 
   bool
-  Allows(const nsAString& aOrigin) const;
+  Allows(nsIPrincipal* aPrincipal) const;
 
 private:
   nsString mFeatureName;
 
   enum Policy {
     // denotes a policy of "feature 'none'"
     eNone,
 
     // denotes a policy of "feature *"
     eAll,
 
     // denotes a policy of "feature bar.com foo.com"
-    eWhiteList,
+    eAllowList,
   };
 
   Policy mPolicy;
 
-  nsTArray<nsString> mWhiteListedOrigins;
+  nsTArray<nsCOMPtr<nsIPrincipal>> mAllowList;
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_Feature_h
--- a/dom/security/featurepolicy/FeaturePolicy.cpp
+++ b/dom/security/featurepolicy/FeaturePolicy.cpp
@@ -4,18 +4,20 @@
  * 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 "FeaturePolicy.h"
 #include "mozilla/dom/FeaturePolicyBinding.h"
 #include "mozilla/dom/FeaturePolicyParser.h"
 #include "mozilla/dom/FeaturePolicyUtils.h"
 #include "nsContentUtils.h"
+#include "nsNetUtil.h"
 
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FeaturePolicy, mParentNode)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(FeaturePolicy)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(FeaturePolicy)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FeaturePolicy)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
@@ -29,17 +31,17 @@ void
 FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy)
 {
   MOZ_ASSERT(aParentPolicy);
 
   mInheritedDeniedFeatureNames.Clear();
 
   RefPtr<FeaturePolicy> dest = this;
   RefPtr<FeaturePolicy> src = aParentPolicy;
-  nsString origin = mDefaultOrigin;
+  nsCOMPtr<nsIPrincipal> origin = mDefaultOrigin;
   FeaturePolicyUtils::ForEachFeature([dest, src, origin](const char* aFeatureName) {
     nsString featureName;
     featureName.AppendASCII(aFeatureName);
 
     // If the destination has a declared feature (via the HTTP header or 'allow'
     // attribute) we allow the feature only if both parent FeaturePolicy and this
     // one allow the current origin.
     if (dest->HasDeclaredFeature(featureName)) {
@@ -81,27 +83,25 @@ FeaturePolicy::HasDeclaredFeature(const 
   }
 
   return false;
 }
 
 void
 FeaturePolicy::SetDeclaredPolicy(nsIDocument* aDocument,
                                  const nsAString& aPolicyString,
-                                 const nsAString& aSelfOrigin,
-                                 const nsAString& aSrcOrigin,
-                                 bool aSrcEnabled)
+                                 nsIPrincipal* aSelfOrigin,
+                                 nsIPrincipal* aSrcOrigin)
 {
   ResetDeclaredPolicy();
 
   Unused << NS_WARN_IF(!FeaturePolicyParser::ParseString(aPolicyString,
                                                          aDocument,
                                                          aSelfOrigin,
                                                          aSrcOrigin,
-                                                         aSrcEnabled,
                                                          mFeatures));
 }
 
 void
 FeaturePolicy::ResetDeclaredPolicy()
 {
   mFeatures.Clear();
 }
@@ -111,39 +111,69 @@ FeaturePolicy::WrapObject(JSContext* aCx
 {
   return Policy_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 bool
 FeaturePolicy::AllowsFeature(const nsAString& aFeatureName,
                              const Optional<nsAString>& aOrigin) const
 {
-  return AllowsFeatureInternal(aFeatureName,
-                               aOrigin.WasPassed()
-                                 ? aOrigin.Value()
-                                 : mDefaultOrigin);
+  nsCOMPtr<nsIPrincipal> origin;
+  if (aOrigin.WasPassed()) {
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), aOrigin.Value());
+    if (NS_FAILED(rv)) {
+      return false;
+    }
+    origin = BasePrincipal::CreateCodebasePrincipal(uri,
+                                                    BasePrincipal::Cast(mDefaultOrigin)->OriginAttributesRef());
+  } else {
+    origin = mDefaultOrigin;
+  }
+
+  if (NS_WARN_IF(!origin)) {
+    return false;
+  }
+
+  return AllowsFeatureInternal(aFeatureName, origin);
 }
 
 bool
 FeaturePolicy::AllowsFeatureInternal(const nsAString& aFeatureName,
-                                     const nsAString& aOrigin) const
+                                     nsIPrincipal* aOrigin) const
 {
+  MOZ_ASSERT(aOrigin);
+
   // Let's see if have to disable this feature because inherited policy.
   if (HasInheritedDeniedFeature(aFeatureName)) {
     return false;
   }
 
   for (const Feature& feature : mFeatures) {
     if (feature.Name().Equals(aFeatureName)) {
       return feature.Allows(aOrigin);
     }
   }
 
-  return FeaturePolicyUtils::AllowDefaultFeature(aFeatureName, mDefaultOrigin,
-                                                 aOrigin);
+  switch (FeaturePolicyUtils::DefaultAllowListFeature(aFeatureName)) {
+    case FeaturePolicyUtils::FeaturePolicyValue::eAll:
+      return true;
+
+    case FeaturePolicyUtils::FeaturePolicyValue::eSelf:
+      return BasePrincipal::Cast(mDefaultOrigin)->Subsumes(aOrigin,
+                                                           BasePrincipal::ConsiderDocumentDomain);
+
+    case FeaturePolicyUtils::FeaturePolicyValue::eNone:
+      return false;
+
+    default:
+      MOZ_CRASH("Unknown default value");
+  }
+
+  return false;
 }
 
 void
 FeaturePolicy::AllowedFeatures(nsTArray<nsString>& aAllowedFeatures)
 {
   RefPtr<FeaturePolicy> self = this;
   FeaturePolicyUtils::ForEachFeature([self, &aAllowedFeatures](const char* aFeatureName) {
     nsString featureName;
@@ -165,26 +195,54 @@ FeaturePolicy::GetAllowlistForFeature(co
 
   for (const Feature& feature : mFeatures) {
     if (feature.Name().Equals(aFeatureName)) {
       if (feature.AllowsAll()) {
         aList.AppendElement(NS_LITERAL_STRING("*"));
         return;
       }
 
-      feature.GetWhiteListedOrigins(aList);
+      nsTArray<nsCOMPtr<nsIPrincipal>> list;
+      feature.GetAllowList(list);
+
+      for (nsIPrincipal* principal : list) {
+        nsAutoCString originNoSuffix;
+        nsresult rv = principal->GetOriginNoSuffix(originNoSuffix);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return;
+        }
+
+        aList.AppendElement(NS_ConvertUTF8toUTF16(originNoSuffix));
+      }
       return;
     }
   }
 
-  nsString defaultAllowList;
-  FeaturePolicyUtils::DefaultAllowListFeature(aFeatureName, mDefaultOrigin,
-                                              defaultAllowList);
-   if (!defaultAllowList.IsEmpty()) {
-    aList.AppendElement(defaultAllowList);
+  switch (FeaturePolicyUtils::DefaultAllowListFeature(aFeatureName)) {
+    case FeaturePolicyUtils::FeaturePolicyValue::eAll:
+      aList.AppendElement(NS_LITERAL_STRING("*"));
+      return;
+
+    case FeaturePolicyUtils::FeaturePolicyValue::eSelf:
+    {
+      nsAutoCString originNoSuffix;
+      nsresult rv = mDefaultOrigin->GetOriginNoSuffix(originNoSuffix);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+
+      aList.AppendElement(NS_ConvertUTF8toUTF16(originNoSuffix));
+      return;
+    }
+
+    case FeaturePolicyUtils::FeaturePolicyValue::eNone:
+      return;
+
+    default:
+      MOZ_CRASH("Unknown default value");
   }
 }
 
 void
 FeaturePolicy::MaybeSetAllowedPolicy(const nsAString& aFeatureName)
 {
   MOZ_ASSERT(FeaturePolicyUtils::IsSupportedFeature(aFeatureName));
 
@@ -192,8 +250,11 @@ FeaturePolicy::MaybeSetAllowedPolicy(con
     return;
   }
 
   Feature feature(aFeatureName);
   feature.SetAllowsAll();
 
   mFeatures.AppendElement(feature);
 }
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/security/featurepolicy/FeaturePolicy.h
+++ b/dom/security/featurepolicy/FeaturePolicy.h
@@ -25,22 +25,22 @@
  *
  * FeaturePolicy is composed by a set of directives configured by the
  * 'Feature-Policy' HTTP Header and the 'allow' attribute in HTMLIFrameElements.
  * Both header and attribute are parsed by FeaturePolicyParser which returns an
  * array of Feature objects. Each Feature object has a feature name and one of
  * these policies:
  * - eNone - the feature is fully disabled.
  * - eAll - the feature is allowed.
- * - eWhitelist - the feature is allowed for a list of origins.
+ * - eAllowList - the feature is allowed for a list of origins.
  *
  * An interesting element of FeaturePolicy is the inheritance: each context
  * inherits the feature-policy directives from the parent context, if it exists.
  * When a context inherits a policy for feature X, it only knows if that feature
- * is allowed or denied (it ignores the list of whitelist origins for instance).
+ * is allowed or denied (it ignores the list of allowed origins for instance).
  * This information is stored in an array of inherited feature strings because
  * we care only to know when they are denied.
  *
  * FeaturePolicy can be reset if the 'allow' or 'src' attributes change in
  * HTMLIFrameElements. 'src' attribute is important to compute correcly
  * the features via FeaturePolicy 'src' keyword.
  *
  * When FeaturePolicy must decide if feature X is allowed or denied for the
@@ -67,44 +67,41 @@ class FeaturePolicy final : public nsISu
   friend class FeaturePolicyUtils;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FeaturePolicy)
 
   explicit FeaturePolicy(nsINode* aNode);
 
-  // A FeaturePolicy must have a default origin, if not in a sandboxed context.
+  // A FeaturePolicy must have a default origin.
   // This method must be called before any other exposed WebIDL method or before
   // checking if a feature is allowed.
   void
-  SetDefaultOrigin(const nsAString& aOrigin)
+  SetDefaultOrigin(nsIPrincipal* aPrincipal)
   {
-    // aOrigin can be an empty string if this is a opaque origin.
-    mDefaultOrigin = aOrigin;
+    mDefaultOrigin = aPrincipal;
   }
 
-  const nsAString& DefaultOrigin() const
+  nsIPrincipal* DefaultOrigin() const
   {
-    // Returns an empty string if this is an opaque origin.
     return mDefaultOrigin;
   }
 
   // Inherits the policy from the 'parent' context if it exists.
   void
   InheritPolicy(FeaturePolicy* aParentFeaturePolicy);
 
   // Sets the declarative part of the policy. This can be from the HTTP header
   // or for the 'allow' HTML attribute.
   void
   SetDeclaredPolicy(nsIDocument* aDocument,
                     const nsAString& aPolicyString,
-                    const nsAString& aSelfOrigin,
-                    const nsAString& aSrcOrigin,
-                    bool aSrcEnabled);
+                    nsIPrincipal* aSelfOrigin,
+                    nsIPrincipal* aSrcOrigin);
 
   // This method creates a policy for aFeatureName allowing it to '*' if it
   // doesn't exist yet. It's used by HTMLIFrameElement to enable features by
   // attributes.
   void
   MaybeSetAllowedPolicy(const nsAString& aFeatureName);
 
   // Clears all the declarative policy directives. This is needed when the
@@ -135,38 +132,42 @@ public:
 
   void
   GetAllowlistForFeature(const nsAString& aFeatureName,
                          nsTArray<nsString>& aList) const;
 
 private:
   ~FeaturePolicy() = default;
 
+  // This method returns true if the aFeatureName is allowed for aOrigin,
+  // following the feature-policy directives. See the comment at the top of this
+  // file.
   bool
   AllowsFeatureInternal(const nsAString& aFeatureName,
-                        const nsAString& aOrigin) const;
+                        nsIPrincipal* aOrigin) const;
 
   // Inherits a single denied feature from the parent context.
   void
   SetInheritedDeniedFeature(const nsAString& aFeatureName);
 
   bool
   HasInheritedDeniedFeature(const nsAString& aFeatureName) const;
 
+  // This returns true if we have a declared feature policy for aFeatureName.
   bool
   HasDeclaredFeature(const nsAString& aFeatureName) const;
 
   nsCOMPtr<nsINode> mParentNode;
 
   // This is set in sub-contexts when the parent blocks some feature for the
   // current context.
   nsTArray<nsString> mInheritedDeniedFeatureNames;
 
   // Feature policy for the current context.
   nsTArray<Feature> mFeatures;
 
-  nsString mDefaultOrigin;
+  nsCOMPtr<nsIPrincipal> mDefaultOrigin;
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_FeaturePolicy_h
--- a/dom/security/featurepolicy/FeaturePolicyParser.cpp
+++ b/dom/security/featurepolicy/FeaturePolicyParser.cpp
@@ -8,18 +8,18 @@
 
 #include "mozilla/dom/Feature.h"
 #include "mozilla/dom/FeaturePolicyUtils.h"
 #include "mozilla/dom/PolicyTokenizer.h"
 #include "nsIScriptError.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
 
 namespace {
 
 void
 ReportToConsoleUnsupportedFeature(nsIDocument* aDocument,
                                   const nsString& aFeatureName)
 {
   const char16_t* params[] = { aFeatureName.get() };
@@ -60,21 +60,22 @@ ReportToConsoleInvalidAllowValue(nsIDocu
                                   params, ArrayLength(params));
 }
 
 } // anonymous
 
 /* static */ bool
 FeaturePolicyParser::ParseString(const nsAString& aPolicy,
                                  nsIDocument* aDocument,
-                                 const nsAString& aSelfOrigin,
-                                 const nsAString& aSrcOrigin,
-                                 bool aSrcEnabled,
+                                 nsIPrincipal* aSelfOrigin,
+                                 nsIPrincipal* aSrcOrigin,
                                  nsTArray<Feature>& aParsedFeatures)
 {
+  MOZ_ASSERT(aSelfOrigin);
+
   nsTArray<nsTArray<nsString>> tokens;
   PolicyTokenizer::tokenizePolicy(aPolicy, tokens);
 
   nsTArray<Feature> parsedFeatures;
 
   for (const nsTArray<nsString>& featureTokens : tokens) {
     if (featureTokens.IsEmpty()) {
       continue;
@@ -83,19 +84,18 @@ FeaturePolicyParser::ParseString(const n
     if (!FeaturePolicyUtils::IsSupportedFeature(featureTokens[0])) {
       ReportToConsoleUnsupportedFeature(aDocument, featureTokens[0]);
       continue;
     }
 
     Feature feature(featureTokens[0]);
 
     if (featureTokens.Length() == 1) {
-      if (aSrcEnabled) {
-        // Note that this src origin can be empty if opaque.
-        feature.AppendOriginToWhiteList(aSrcOrigin);
+      if (aSrcOrigin) {
+        feature.AppendToAllowList(aSrcOrigin);
       } else {
         ReportToConsoleInvalidEmptyAllowValue(aDocument, featureTokens[0]);
         continue;
       }
     } else {
       // we gotta start at 1 here
       for (uint32_t i = 1; i < featureTokens.Length(); ++i) {
         const nsString& curVal = featureTokens[i];
@@ -105,46 +105,41 @@ FeaturePolicyParser::ParseString(const n
         }
 
         if (curVal.EqualsLiteral("*")) {
           feature.SetAllowsAll();
           break;
         }
 
         if (curVal.LowerCaseEqualsASCII("'self'")) {
-          // Opaque origins are passed as empty string.
-          if (!aSelfOrigin.IsEmpty()) {
-            feature.AppendOriginToWhiteList(aSelfOrigin);
-          }
+          feature.AppendToAllowList(aSelfOrigin);
           continue;
         }
 
-        if (aSrcEnabled && curVal.LowerCaseEqualsASCII("'src'")) {
-          // Opaque origins are passed as empty string.
-          if (!aSrcOrigin.IsEmpty()) {
-            feature.AppendOriginToWhiteList(aSrcOrigin);
-          }
+        if (aSrcOrigin && curVal.LowerCaseEqualsASCII("'src'")) {
+          feature.AppendToAllowList(aSrcOrigin);
           continue;
         }
 
         nsCOMPtr<nsIURI> uri;
         nsresult rv = NS_NewURI(getter_AddRefs(uri), curVal);
         if (NS_FAILED(rv)) {
           ReportToConsoleInvalidAllowValue(aDocument, curVal);
           continue;
         }
 
-        nsAutoString origin;
-        rv = nsContentUtils::GetUTFOrigin(uri, origin);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
+        nsCOMPtr<nsIPrincipal> origin =
+          BasePrincipal::CreateCodebasePrincipal(uri,
+                                                 BasePrincipal::Cast(aSelfOrigin)->OriginAttributesRef());
+        if (NS_WARN_IF(!origin)) {
           ReportToConsoleInvalidAllowValue(aDocument, curVal);
           continue;
         }
 
-        feature.AppendOriginToWhiteList(origin);
+        feature.AppendToAllowList(origin);
       }
     }
 
     // No duplicate!
     bool found = false;
     for (const Feature& parsedFeature : parsedFeatures) {
       if (parsedFeature.Name() == feature.Name()) {
         found = true;
@@ -155,8 +150,11 @@ FeaturePolicyParser::ParseString(const n
     if (!found) {
       parsedFeatures.AppendElement(feature);
     }
   }
 
   aParsedFeatures.SwapElements(parsedFeatures);
   return true;
 }
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/security/featurepolicy/FeaturePolicyParser.h
+++ b/dom/security/featurepolicy/FeaturePolicyParser.h
@@ -5,33 +5,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FeaturePolicyParser_h
 #define mozilla_dom_FeaturePolicyParser_h
 
 #include "nsString.h"
 
 class nsIDocument;
-class nsIURI;
+class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
 class Feature;
 
 class FeaturePolicyParser final
 {
 public:
-  // aSelfOrigin must not be empty. if aSrcOrigin is empty, the parsing will not
+  // aSelfOrigin must not be null. if aSrcOrigin is null, the parsing will not
   // support 'src' as valid allow directive value.
   static bool
   ParseString(const nsAString& aPolicy,
               nsIDocument* aDocument,
-              const nsAString& aSelfOrigin,
-              const nsAString& aSrcOrigin,
-              bool aSrcEnabled,
+              nsIPrincipal* aSelfOrigin,
+              nsIPrincipal* aSrcOrigin,
               nsTArray<Feature>& aParsedFeatures);
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_FeaturePolicyParser_h
--- a/dom/security/featurepolicy/FeaturePolicyUtils.cpp
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.cpp
@@ -4,43 +4,40 @@
  * 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 "FeaturePolicyUtils.h"
 #include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsIDocument.h"
 
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
 
 struct FeatureMap {
   const char* mFeatureName;
-
-  enum {
-    eAll,
-    eSelf,
-  } mDefaultAllowList;
+  FeaturePolicyUtils::FeaturePolicyValue mDefaultAllowList;
 };
 
 /*
  * IMPORTANT: Do not change this list without review from a DOM peer _AND_ a
  * DOM Security peer!
  */
 static FeatureMap sSupportedFeatures[] = {
-  { "autoplay", FeatureMap::eAll },
-  { "camera", FeatureMap::eAll  },
-  { "encrypted-media", FeatureMap::eAll  },
-  { "fullscreen", FeatureMap::eAll  },
-  { "geolocation", FeatureMap::eAll  },
-  { "microphone", FeatureMap::eAll  },
-  { "midi", FeatureMap::eAll  },
-  { "payment", FeatureMap::eAll  },
+  { "autoplay", FeaturePolicyUtils::FeaturePolicyValue::eAll },
+  { "camera", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "geolocation", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "midi", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "payment", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
   // TODO: not supported yet!!!
-  { "speaker", FeatureMap::eAll  },
-  { "vr", FeatureMap::eAll  },
+  { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
+  { "vr", FeaturePolicyUtils::FeaturePolicyValue::eAll  },
 };
 
 /* static */ bool
 FeaturePolicyUtils::IsSupportedFeature(const nsAString& aFeatureName)
 {
   uint32_t numFeatures = (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
   for (uint32_t i = 0; i < numFeatures; ++i) {
     if (aFeatureName.LowerCaseEqualsASCII(sSupportedFeatures[i].mFeatureName)) {
@@ -54,61 +51,27 @@ FeaturePolicyUtils::IsSupportedFeature(c
 FeaturePolicyUtils::ForEachFeature(const std::function<void(const char*)>& aCallback)
 {
   uint32_t numFeatures = (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
   for (uint32_t i = 0; i < numFeatures; ++i) {
     aCallback(sSupportedFeatures[i].mFeatureName);
   }
 }
 
-/* static */ void
-FeaturePolicyUtils::DefaultAllowListFeature(const nsAString& aFeatureName,
-                                            const nsAString& aDefaultOrigin,
-                                            nsAString& aDefaultAllowList)
+/* static */ FeaturePolicyUtils::FeaturePolicyValue
+FeaturePolicyUtils::DefaultAllowListFeature(const nsAString& aFeatureName)
 {
   uint32_t numFeatures = (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
   for (uint32_t i = 0; i < numFeatures; ++i) {
     if (aFeatureName.LowerCaseEqualsASCII(sSupportedFeatures[i].mFeatureName)) {
-      switch (sSupportedFeatures[i].mDefaultAllowList) {
-        case FeatureMap::eAll:
-          aDefaultAllowList.AppendASCII("*");
-          return;
-
-        case FeatureMap::eSelf:
-          aDefaultAllowList = aDefaultOrigin;
-          return;
-
-        default:
-          MOZ_CRASH("Unknown default value");
-      }
-    }
-  }
-}
-
-/* static */ bool
-FeaturePolicyUtils::AllowDefaultFeature(const nsAString& aFeatureName,
-                                        const nsAString& aDefaultOrigin,
-                                        const nsAString& aOrigin)
-{
-  uint32_t numFeatures = (sizeof(sSupportedFeatures) / sizeof(sSupportedFeatures[0]));
-  for (uint32_t i = 0; i < numFeatures; ++i) {
-    if (aFeatureName.LowerCaseEqualsASCII(sSupportedFeatures[i].mFeatureName)) {
-      switch (sSupportedFeatures[i].mDefaultAllowList) {
-        case FeatureMap::eAll:
-          return true;
-        case FeatureMap::eSelf:
-          return aDefaultOrigin == aOrigin;
-        default:
-          MOZ_CRASH("Unknown default value");
-      }
-      return true;
+      return sSupportedFeatures[i].mDefaultAllowList;
     }
   }
 
-  return false;
+  return FeaturePolicyValue::eNone;
 }
 
 /* static */ bool
 FeaturePolicyUtils::IsFeatureAllowed(nsIDocument* aDocument,
                                      const nsAString& aFeatureName)
 {
   MOZ_ASSERT(aDocument);
 
@@ -120,8 +83,11 @@ FeaturePolicyUtils::IsFeatureAllowed(nsI
     return true;
   }
 
   FeaturePolicy* policy = aDocument->Policy();
   MOZ_ASSERT(policy);
 
   return policy->AllowsFeatureInternal(aFeatureName, policy->DefaultOrigin());
 }
+
+} // dom namespace
+} // mozilla namespace
--- a/dom/security/featurepolicy/FeaturePolicyUtils.h
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.h
@@ -13,33 +13,44 @@
 class nsIDocument;
 
 namespace mozilla {
 namespace dom {
 
 class FeaturePolicyUtils final
 {
 public:
+  enum FeaturePolicyValue
+  {
+    // Feature always allowed.
+    eAll,
+
+    // Feature allowed for documents that are same-origin with this one.
+    eSelf,
+
+    // Feature denied.
+    eNone,
+  };
+
+  // This method returns true if aFeatureName is allowed for aDocument.
+  // Use this method everywhere you need to check feature-policy directives.
   static bool
   IsFeatureAllowed(nsIDocument* aDocument,
                    const nsAString& aFeatureName);
 
+  // Returns true if aFeatureName is a known feature policy name.
   static bool
   IsSupportedFeature(const nsAString& aFeatureName);
 
+  // Runs aCallback for each known feature policy, with the feature name as
+  // argument.
   static void
   ForEachFeature(const std::function<void(const char*)>& aCallback);
 
-  static void
-  DefaultAllowListFeature(const nsAString& aFeatureName,
-                          const nsAString& aDefaultOrigin,
-                          nsAString& aDefaultAllowList);
-
-  static bool
-  AllowDefaultFeature(const nsAString& aFeatureName,
-                      const nsAString& aDefaultOrigin,
-                      const nsAString& aOrigin);
+  // Returns the default policy value for aFeatureName.
+  static FeaturePolicyValue
+  DefaultAllowListFeature(const nsAString& aFeatureName);
 };
 
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_FeaturePolicyUtils_h
--- a/dom/security/featurepolicy/test/gtest/TestFeaturePolicyParser.cpp
+++ b/dom/security/featurepolicy/test/gtest/TestFeaturePolicyParser.cpp
@@ -1,124 +1,134 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/dom/Feature.h"
 #include "mozilla/dom/FeaturePolicyParser.h"
 #include "nsNetUtil.h"
 #include "nsTArray.h"
 
+using namespace mozilla;
 using namespace mozilla::dom;
 
-#define URL_SELF NS_LITERAL_STRING("https://example.com")
-#define URL_EXAMPLE_COM NS_LITERAL_STRING("http://example.com")
-#define URL_EXAMPLE_NET NS_LITERAL_STRING("http://example.net")
+#define URL_SELF NS_LITERAL_CSTRING("https://example.com")
+#define URL_EXAMPLE_COM NS_LITERAL_CSTRING("http://example.com")
+#define URL_EXAMPLE_NET NS_LITERAL_CSTRING("http://example.net")
 
 void
 CheckParser(const nsAString& aInput, bool aExpectedResults,
             uint32_t aExpectedFeatures, nsTArray<Feature>& aParsedFeatures)
 {
+  nsCOMPtr<nsIPrincipal> principal =
+    mozilla::BasePrincipal::CreateCodebasePrincipal(URL_SELF);
   nsTArray<Feature> parsedFeatures;
   ASSERT_TRUE(FeaturePolicyParser::ParseString(aInput,
                                                nullptr,
-                                               URL_SELF,
-                                               EmptyString(),
-                                               true, // 'src' enabled
+                                               principal,
+                                               principal,
                                                parsedFeatures) == aExpectedResults);
   ASSERT_TRUE(parsedFeatures.Length() == aExpectedFeatures);
 
   parsedFeatures.SwapElements(aParsedFeatures);
 }
 
 TEST(FeaturePolicyParser, Basic)
 {
+  nsCOMPtr<nsIPrincipal> selfPrincipal =
+    mozilla::BasePrincipal::CreateCodebasePrincipal(URL_SELF);
+  nsCOMPtr<nsIPrincipal> exampleComPrincipal =
+    mozilla::BasePrincipal::CreateCodebasePrincipal(URL_EXAMPLE_COM);
+  nsCOMPtr<nsIPrincipal> exampleNetPrincipal =
+    mozilla::BasePrincipal::CreateCodebasePrincipal(URL_EXAMPLE_NET);
+
   nsTArray<Feature> parsedFeatures;
 
   // Empty string is a valid policy.
   CheckParser(EmptyString(), true, 0, parsedFeatures);
 
   // Empty string with spaces is still valid.
   CheckParser(NS_LITERAL_STRING("   "), true, 0, parsedFeatures);
 
   // Non-Existing features with no allowed values
   CheckParser(NS_LITERAL_STRING("non-existing-feature"), true, 0, parsedFeatures);
   CheckParser(NS_LITERAL_STRING("non-existing-feature;another-feature"), true,
               0, parsedFeatures);
 
   // Existing feature with no allowed values
   CheckParser(NS_LITERAL_STRING("camera"), true, 1, parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
 
   // Some spaces.
   CheckParser(NS_LITERAL_STRING(" camera "), true, 1, parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
 
   // A random ;
   CheckParser(NS_LITERAL_STRING("camera;"), true, 1, parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
 
   // Another random ;
   CheckParser(NS_LITERAL_STRING(";camera;"), true, 1, parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
 
   // 2 features
   CheckParser(NS_LITERAL_STRING("camera;microphone"), true, 2, parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
   ASSERT_TRUE(parsedFeatures[1].Name().Equals(NS_LITERAL_STRING("microphone")));
-  ASSERT_TRUE(parsedFeatures[1].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[1].HasAllowList());
 
   // 2 features with spaces
   CheckParser(NS_LITERAL_STRING(" camera ; microphone "), true, 2,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
   ASSERT_TRUE(parsedFeatures[1].Name().Equals(NS_LITERAL_STRING("microphone")));
-  ASSERT_TRUE(parsedFeatures[1].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[1].HasAllowList());
 
   // 3 features, but only 2 exist.
   CheckParser(NS_LITERAL_STRING("camera;microphone;foobar"), true, 2,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
   ASSERT_TRUE(parsedFeatures[1].Name().Equals(NS_LITERAL_STRING("microphone")));
-  ASSERT_TRUE(parsedFeatures[1].IsWhiteList());
+  ASSERT_TRUE(parsedFeatures[1].HasAllowList());
 
   // Multiple spaces around the value
   CheckParser(NS_LITERAL_STRING("camera      'self'"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_SELF));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal));
 
   // Multiple spaces around the value
   CheckParser(NS_LITERAL_STRING("camera      'self'    "), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_SELF));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal));
 
   // No final '
   CheckParser(NS_LITERAL_STRING("camera      'self"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].IsWhiteList());
-  ASSERT_TRUE(!parsedFeatures[0].WhiteListContains(URL_SELF));
+  ASSERT_TRUE(parsedFeatures[0].HasAllowList());
+  ASSERT_TRUE(!parsedFeatures[0].AllowListContains(selfPrincipal));
 
   // Lowercase/Uppercase
   CheckParser(NS_LITERAL_STRING("camera      'selF'"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_SELF));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal));
 
   // Lowercase/Uppercase
   CheckParser(NS_LITERAL_STRING("camera * 'self' none' a.com 123"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
   ASSERT_TRUE(parsedFeatures[0].AllowsAll());
 
   // After a 'none' we don't continue the parsing.
@@ -131,32 +141,32 @@ TEST(FeaturePolicyParser, Basic)
   CheckParser(NS_LITERAL_STRING("camera * a.com b.org c.net d.co.uk"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
   ASSERT_TRUE(parsedFeatures[0].AllowsAll());
 
   // 'self'
   CheckParser(NS_LITERAL_STRING("camera 'self'"), true, 1, parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_SELF));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal));
 
   // A couple of URLs
   CheckParser(NS_LITERAL_STRING("camera http://example.com http://example.net"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(!parsedFeatures[0].WhiteListContains(URL_SELF));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_EXAMPLE_COM));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_EXAMPLE_NET));
+  ASSERT_TRUE(!parsedFeatures[0].AllowListContains(selfPrincipal));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleComPrincipal));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleNetPrincipal));
 
   // A couple of URLs + self
   CheckParser(NS_LITERAL_STRING("camera http://example.com 'self' http://example.net"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_SELF));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_EXAMPLE_COM));
-  ASSERT_TRUE(parsedFeatures[0].WhiteListContains(URL_EXAMPLE_NET));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(selfPrincipal));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleComPrincipal));
+  ASSERT_TRUE(parsedFeatures[0].AllowListContains(exampleNetPrincipal));
 
   // A couple of URLs but then *
   CheckParser(NS_LITERAL_STRING("camera http://example.com 'self' http://example.net *"), true, 1,
               parsedFeatures);
   ASSERT_TRUE(parsedFeatures[0].Name().Equals(NS_LITERAL_STRING("camera")));
   ASSERT_TRUE(parsedFeatures[0].AllowsAll());
 }
--- a/dom/serviceworkers/test/browser.ini
+++ b/dom/serviceworkers/test/browser.ini
@@ -6,20 +6,23 @@ support-files =
   download/worker.js
   download_canceled/page_download_canceled.html
   download_canceled/server-stream-download.sjs
   download_canceled/sw_download_canceled.js
   fetch.js
   file_userContextId_openWindow.js
   force_refresh_browser_worker.js
   empty.html
+  empty_with_utils.html
   empty.js
+  page_post_controlled.html
   storage_recovery_worker.sjs
   utils.js
 
+[browser_antitracking.js]
 [browser_devtools_serviceworker_interception.js]
 skip-if = serviceworker_e10s
 [browser_force_refresh.js]
 [browser_download.js]
 [browser_download_canceled.js]
 skip-if = verify
 [browser_storage_permission.js]
 skip-if = (verify && debug && (os == 'win' || os == 'mac'))
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/browser_antitracking.js
@@ -0,0 +1,103 @@
+
+const BEHAVIOR_ACCEPT         = Ci.nsICookieService.BEHAVIOR_ACCEPT;
+const BEHAVIOR_REJECT_TRACKER = Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+
+let {UrlClassifierTestUtils} =
+  ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
+
+const TOP_DOMAIN = "http://mochi.test:8888/";
+const SW_DOMAIN = "https://tracking.example.org/";
+
+const TOP_TEST_ROOT = getRootDirectory(gTestPath)
+  .replace("chrome://mochitests/content/", TOP_DOMAIN);
+const SW_TEST_ROOT = getRootDirectory(gTestPath)
+  .replace("chrome://mochitests/content/", SW_DOMAIN);
+
+const TOP_EMPTY_PAGE = `${TOP_TEST_ROOT}empty_with_utils.html`;
+const SW_REGISTER_PAGE = `${SW_TEST_ROOT}empty_with_utils.html`;
+const SW_IFRAME_PAGE = `${SW_TEST_ROOT}page_post_controlled.html`;
+// An empty script suffices for our SW needs; it's by definition no-fetch.
+const SW_REL_SW_SCRIPT = "empty.js";
+
+/**
+ * Set up a no-fetch-optimized ServiceWorker on a domain that will be covered by
+ * tracking protection (but is not yet).  Once the SW is installed, activate TP
+ * and create a tab that embeds that tracking-site in an iframe.
+ */
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({'set': [
+    ['dom.serviceWorkers.enabled', true],
+    ['dom.serviceWorkers.exemptFromPerDomainMax', true],
+    ['dom.serviceWorkers.testing.enabled', true],
+    ['network.cookie.cookieBehavior', BEHAVIOR_ACCEPT],
+  ]});
+
+  // ## Install SW
+  info("Installing SW");
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: SW_REGISTER_PAGE
+    },
+    async function(linkedBrowser) {
+      await ContentTask.spawn(
+        linkedBrowser,
+        { sw: SW_REL_SW_SCRIPT },
+        async function({ sw }) {
+          // Waive the xray to use the content utils.js script functions.
+          await content.wrappedJSObject.registerAndWaitForActive(sw);
+        }
+      );
+    }
+  );
+
+  // Enable Anti-tracking.
+  await SpecialPowers.pushPrefEnv({'set': [
+    ['privacy.trackingprotection.enabled', false],
+    ["privacy.trackingprotection.pbmode.enabled", false],
+    ["privacy.trackingprotection.annotate_channels", true],
+    ['network.cookie.cookieBehavior', BEHAVIOR_REJECT_TRACKER],
+  ]});
+  await UrlClassifierTestUtils.addTestTrackers();
+
+  // Open the top-level page.
+  info("Open top-level page");
+  let topTab = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    opening: TOP_EMPTY_PAGE
+  });
+
+  // Create Iframe in the top-level page and verify its state.
+  let { controlled } = await ContentTask.spawn(
+    topTab.linkedBrowser,
+    { url: SW_IFRAME_PAGE },
+    async function ({ url }) {
+      const payload =
+        await content.wrappedJSObject.createIframeAndWaitForMessage(url);
+      return payload;
+    }
+  );
+
+  ok(!controlled, "Should not be controlled!");
+
+  // ## Cleanup
+  // Close the testing tab.
+  BrowserTestUtils.removeTab(topTab);
+  // Unregister the SW we registered for the tracking protection origin.
+  await BrowserTestUtils.withNewTab(
+    {
+      gBrowser,
+      url: SW_REGISTER_PAGE
+    },
+    async function(linkedBrowser) {
+      await ContentTask.spawn(
+        linkedBrowser,
+        {},
+        async function() {
+          // Waive the xray to use the content utils.js script functions.
+          await content.wrappedJSObject.unregisterAll();
+        }
+      );
+    }
+  );
+});
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/empty_with_utils.html
@@ -0,0 +1,13 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <script src="utils.js" type="text/javascript"></script>
+</head>
+<body>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/page_post_controlled.html
@@ -0,0 +1,17 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+<script type="text/javascript">
+  window.parent.postMessage({
+    controlled: !!navigator.serviceWorker.controller
+  }, "*");
+</script>
+</body>
+</html>
--- a/dom/serviceworkers/test/utils.js
+++ b/dom/serviceworkers/test/utils.js
@@ -18,8 +18,52 @@ function waitForControlled(win) {
     if (win.navigator.serviceWorker.controller) {
       return resolve();
     }
 
     win.navigator.serviceWorker.addEventListener('controllerchange', resolve,
                                                  { once: true });
   });
 }
+
+/**
+ * Helper for browser tests to issue register calls from the content global and
+ * wait for the SW to progress to the active state, as most tests desire.
+ * From the ContentTask.spawn, use via
+ * `content.wrappedJSObject.registerAndWaitForActive`.
+ */
+async function registerAndWaitForActive(...args) {
+  console.log('...calling register');
+  const reg = await navigator.serviceWorker.register(...args);
+  // Unless registration resurrection happens, the SW should be in the
+  // installing slot.
+  console.log('...waiting for activation');
+  await waitForState(reg.installing, 'activated', reg);
+  console.log("...activated!");
+  return reg;
+}
+
+/**
+ * Helper to create an iframe with the given URL and return the first
+ * postMessage payload received.  This is intended to be used when creating
+ * cross-origin iframes.
+ *
+ * A promise will be returned that resolves with the payload of the postMessage
+ * call.
+ */
+function createIframeAndWaitForMessage(url) {
+  const iframe = document.createElement('iframe');
+  document.body.appendChild(iframe);
+  return new Promise((resolve) => {
+    window.addEventListener(
+      'message',
+      (event) => { resolve(event.data); },
+      { once: true });
+    iframe.src = url;
+  });
+}
+
+async function unregisterAll() {
+  const registrations = await navigator.serviceWorker.getRegistrations();
+  for (const reg of registrations) {
+    await reg.unregister();
+  }
+}
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was b832dae6e48d3a95d1e6d977d0b7c53a873fd246 (2018-09-17 21:35:14 +1200)
+The git commit ID used was 455981555e37a0153e5dd4d4517f073262946519 (2018-10-11 18:48:08 +0300)
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -582,17 +582,17 @@ audiounit_output_callback(void * user_pt
   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
 
   ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %lu.",
         stm,
         (unsigned int) outBufferList->mNumberBuffers,
         (unsigned int) outBufferList->mBuffers[0].mDataByteSize,
         (unsigned int) outBufferList->mBuffers[0].mNumberChannels,
         (unsigned int) output_frames,
-        stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
+        has_input(stm) ? stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame : 0);
 
   long input_frames = 0;
   void * output_buffer = NULL, * input_buffer = NULL;
 
   if (stm->shutdown) {
     ALOG("(%p) output shutdown.", stm);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
@@ -3391,41 +3391,50 @@ audiounit_get_devices_of_type(cubeb_devi
 {
   UInt32 size = 0;
   OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
                                                 &DEVICES_PROPERTY_ADDRESS, 0,
                                                 NULL, &size);
   if (ret != noErr) {
     return vector<AudioObjectID>();
   }
-  /* Total number of input and output devices. */
-  uint32_t count = (uint32_t)(size / sizeof(AudioObjectID));
-
-  vector<AudioObjectID> devices(count);
+  vector<AudioObjectID> devices(size / sizeof(AudioObjectID));
   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                    &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size,
                                    devices.data());
   if (ret != noErr) {
     return vector<AudioObjectID>();
   }
+
+  // Remove the aggregate device from the list of devices (if any).
+  for (auto it = devices.begin(); it != devices.end();) {
+    CFStringRef name = get_device_name(*it);
+    if (CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location !=
+        kCFNotFound) {
+      it = devices.erase(it);
+    } else {
+      it++;
+    }
+  }
+
   /* Expected sorted but did not find anything in the docs. */
   sort(devices.begin(), devices.end(), [](AudioObjectID a, AudioObjectID b) {
       return a < b;
     });
 
   if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
     return devices;
   }
 
   AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) ?
                                          kAudioDevicePropertyScopeInput :
                                          kAudioDevicePropertyScopeOutput;
 
   vector<AudioObjectID> devices_in_scope;
-  for (uint32_t i = 0; i < count; ++i) {
+  for (uint32_t i = 0; i < devices.size(); ++i) {
     /* For device in the given scope channel must be > 0. */
     if (audiounit_get_channel_count(devices[i], scope) > 0) {
       devices_in_scope.push_back(devices[i]);
     }
   }
 
   return devices_in_scope;
 }
@@ -3441,57 +3450,27 @@ audiounit_collection_changed_callback(Au
   // This can be called from inside an AudioUnit function, dispatch to another queue.
   dispatch_async(context->serial_queue, ^() {
     auto_lock lock(context->mutex);
     if (context->collection_changed_callback == NULL) {
       /* Listener removed while waiting in mutex, abort. */
       return;
     }
 
-    /* Differentiate input from output changes. */
-    if (context->collection_changed_devtype == CUBEB_DEVICE_TYPE_INPUT ||
-        context->collection_changed_devtype == CUBEB_DEVICE_TYPE_OUTPUT) {
-      vector<AudioObjectID> devices = audiounit_get_devices_of_type(context->collection_changed_devtype);
-      /* When count is the same examine the devid for the case of coalescing. */
-      if (context->devtype_device_array == devices) {
-        /* Device changed for the other scope, ignore. */
-        return;
-      } else {
-        /* Also don't trigger the user callback if the new added device is private
-         * aggregate device: compute the set of new devices, and remove those
-         * with the name of our private aggregate devices. */
-        set<AudioObjectID> current_devices(devices.begin(), devices.end());
-        set<AudioObjectID> previous_devices(context->devtype_device_array.begin(),
-                                            context->devtype_device_array.end());
-        set<AudioObjectID> new_devices;
-        set_difference(current_devices.begin(), current_devices.end(),
-                       previous_devices.begin(), previous_devices.end(),
-                       inserter(new_devices, new_devices.begin()));
-
-        for (auto it = new_devices.begin(); it != new_devices.end();) {
-          CFStringRef name = get_device_name(*it);
-          if (CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location !=
-              kCFNotFound) {
-            it = new_devices.erase(it);
-          } else {
-            it++;
-          }
-        }
-
-        // If this set of new devices is empty, it means this was triggerd
-        // solely by creating an aggregate device, no need to trigger the user
-        // callback.
-        if (new_devices.empty()) {
-          return;
-        }
-      }
-      /* Device on desired scope changed. */
-      context->devtype_device_array = devices;
+    assert(context->collection_changed_devtype &
+           (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT));
+
+    vector<AudioObjectID> devices = audiounit_get_devices_of_type(context->collection_changed_devtype);
+    /* The elements in the vector are sorted. */
+    if (context->devtype_device_array == devices) {
+      /* Device changed for the other scope, ignore. */
+      return;
     }
-
+    /* Device on desired scope has changed. */
+    context->devtype_device_array = devices;
     context->collection_changed_callback(context, context->collection_changed_user_ptr);
   });
   return noErr;
 }
 
 static OSStatus
 audiounit_add_device_listener(cubeb * context,
                               cubeb_device_type devtype,
@@ -3506,21 +3485,19 @@ audiounit_add_device_listener(cubeb * co
                                                 &DEVICES_PROPERTY_ADDRESS,
                                                 audiounit_collection_changed_callback,
                                                 context);
   if (ret == noErr) {
     /* Expected empty after unregister. */
     assert(context->devtype_device_array.empty());
     /* Listener works for input and output.
      * When requested one of them we need to differentiate. */
-    if (devtype == CUBEB_DEVICE_TYPE_INPUT ||
-        devtype == CUBEB_DEVICE_TYPE_OUTPUT) {
-      /* Used to differentiate input from output device changes. */
-      context->devtype_device_array = audiounit_get_devices_of_type(devtype);
-    }
+    assert(devtype &
+           (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT));
+    context->devtype_device_array = audiounit_get_devices_of_type(devtype);
     context->collection_changed_devtype = devtype;
     context->collection_changed_callback = collection_changed_callback;
     context->collection_changed_user_ptr = user_ptr;
   }
   return ret;
 }
 
 static OSStatus
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -37,16 +37,17 @@
 #include "nsIUrlClassifierDBService.h"
 #include "nsIURLFormatter.h"
 
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/net/HttpBaseChannel.h"
+#include "mozilla/net/TrackingDummyChannel.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace net {
 
@@ -324,16 +325,21 @@ SetIsTrackingResourceHelper(nsIChannel* 
     // request. We should notify the child process as well.
     parentChannel->NotifyTrackingResource(aIsThirdParty);
   }
 
   RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
   if (httpChannel) {
     httpChannel->SetIsTrackingResource(aIsThirdParty);
   }
+
+  RefPtr<TrackingDummyChannel> dummyChannel = do_QueryObject(aChannel);
+  if (dummyChannel) {
+    dummyChannel->SetIsTrackingResource();
+  }
 }
 
 static void
 LowerPriorityHelper(nsIChannel* aChannel)
 {
   MOZ_ASSERT(aChannel);
 
   bool isBlockingResource = false;
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/net/WebSocketEventListenerChild.h"
 #include "mozilla/net/DNSRequestChild.h"
 #include "mozilla/net/ChannelDiverterChild.h"
 #include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
 #include "mozilla/dom/network/TCPServerSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "mozilla/net/AltDataOutputStreamChild.h"
+#include "mozilla/net/TrackingDummyChannelChild.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/net/StunAddrsRequestChild.h"
 #endif
 
 #include "SerializedLoadContext.h"
 #include "nsGlobalWindow.h"
 #include "nsIOService.h"
 #include "nsINetworkPredictor.h"
@@ -529,10 +530,24 @@ NeckoChild::RecvNetworkChangeNotificatio
   nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
   if (obsService) {
     obsService->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC,
                                 NS_ConvertUTF8toUTF16(type).get());
   }
   return IPC_OK();
 }
 
+PTrackingDummyChannelChild*
+NeckoChild::AllocPTrackingDummyChannelChild(nsIURI* aURI,
+                                            const OptionalLoadInfoArgs& aLoadInfo)
+{
+  return new TrackingDummyChannelChild();
+}
+
+bool
+NeckoChild::DeallocPTrackingDummyChannelChild(PTrackingDummyChannelChild* aActor)
+{
+  delete static_cast<TrackingDummyChannelChild*>(aActor);
+  return true;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -100,16 +100,23 @@ protected:
   virtual mozilla::ipc::IPCResult RecvCrossProcessRedirect(
                                     const uint32_t& aRegistrarId,
                                     nsIURI* aURI,
                                     const uint32_t& aNewLoadFlags,
                                     const OptionalLoadInfoArgs& aLoadInfoForwarder,
                                     const uint64_t& aChannelId,
                                     nsIURI* aOriginalURI,
                                     const uint64_t& aIdentifier) override;
+
+  virtual PTrackingDummyChannelChild*
+    AllocPTrackingDummyChannelChild(nsIURI* aURI,
+                                    const OptionalLoadInfoArgs& aLoadInfo) override;
+
+  virtual bool
+    DeallocPTrackingDummyChannelChild(PTrackingDummyChannelChild* aChannel) override;
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
 extern PNeckoChild *gNeckoChild;
 
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/net/DataChannelParent.h"
 #include "mozilla/net/SimpleChannelParent.h"
 #include "mozilla/net/AltDataOutputStreamParent.h"
 #include "mozilla/Unused.h"
 #include "mozilla/net/FileChannelParent.h"
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
+#include "mozilla/net/TrackingDummyChannelParent.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/net/StunAddrsRequestParent.h"
 #endif
 #include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/network/TCPSocketParent.h"
@@ -1009,10 +1010,49 @@ NeckoParent::RecvGetExtensionFD(const UR
   if (result.isErr()) {
     FileDescriptor invalidFD;
     aResolve(invalidFD);
   }
 
   return IPC_OK();
 }
 
+PTrackingDummyChannelParent*
+NeckoParent::AllocPTrackingDummyChannelParent(nsIURI* aURI,
+                                              const OptionalLoadInfoArgs& aLoadInfo)
+{
+  RefPtr<TrackingDummyChannelParent> c = new TrackingDummyChannelParent();
+  return c.forget().take();
+}
+
+mozilla::ipc::IPCResult
+NeckoParent::RecvPTrackingDummyChannelConstructor(PTrackingDummyChannelParent* aActor,
+                                                  nsIURI* aURI,
+                                                  const OptionalLoadInfoArgs& aLoadInfo)
+{
+  TrackingDummyChannelParent* p =
+    static_cast<TrackingDummyChannelParent*>(aActor);
+
+  if (NS_WARN_IF(!aURI)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  nsresult rv = LoadInfoArgsToLoadInfo(aLoadInfo, getter_AddRefs(loadInfo));
+  if (NS_WARN_IF(NS_FAILED(rv)) || !loadInfo) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  p->Init(aURI, loadInfo);
+  return IPC_OK();
+}
+
+bool
+NeckoParent::DeallocPTrackingDummyChannelParent(PTrackingDummyChannelParent* aActor)
+{
+  RefPtr<TrackingDummyChannelParent> c =
+    dont_AddRef(static_cast<TrackingDummyChannelParent*>(aActor));
+  MOZ_ASSERT(c);
+  return true;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -235,14 +235,26 @@ protected:
   /* WebExtensions */
   virtual mozilla::ipc::IPCResult
     RecvGetExtensionStream(const URIParams& aURI,
                            GetExtensionStreamResolver&& aResolve) override;
 
   virtual mozilla::ipc::IPCResult
     RecvGetExtensionFD(const URIParams& aURI,
                        GetExtensionFDResolver&& aResolve) override;
+
+  virtual PTrackingDummyChannelParent*
+    AllocPTrackingDummyChannelParent(nsIURI* aURI,
+                                     const OptionalLoadInfoArgs& aLoadInfo) override;
+
+  virtual bool
+    DeallocPTrackingDummyChannelParent(PTrackingDummyChannelParent* aChild) override;
+
+  virtual mozilla::ipc::IPCResult
+    RecvPTrackingDummyChannelConstructor(PTrackingDummyChannelParent* aActor,
+                                         nsIURI* aURI,
+                                         const OptionalLoadInfoArgs& aLoadInfo) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -21,16 +21,17 @@ include protocol PChannelDiverter;
 include protocol PFileDescriptorSet;
 include protocol PDataChannel;
 include protocol PSimpleChannel;
 include protocol PTransportProvider;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 include protocol PStunAddrsRequest;
 include protocol PFileChannel;
+include protocol PTrackingDummyChannel;
 
 include IPCStream;
 include URIParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 include protocol PAltDataOutputStream;
 
 using class IPC::SerializedLoadContext from "SerializedLoadContext.h";
@@ -58,16 +59,17 @@ nested(upto inside_cpow) sync protocol P
   manages PDNSRequest;
   manages PDataChannel;
   manages PSimpleChannel;
   manages PFileChannel;
   manages PChannelDiverter;
   manages PTransportProvider;
   manages PAltDataOutputStream;
   manages PStunAddrsRequest;
+  manages PTrackingDummyChannel;
 
 parent:
   async __delete__();
 
   nested(inside_cpow) async PCookieService();
   async PHttpChannel(PBrowserOrId browser,
                      SerializedLoadContext loadContext,
                      HttpChannelCreationArgs args);
@@ -105,16 +107,18 @@ parent:
    * the parent and the child when we're redirecting to a data: URI.
    */
   async PDataChannel(uint32_t channelId);
   async PSimpleChannel(uint32_t channelId);
   async PFileChannel(uint32_t channelId);
 
   async PChannelDiverter(ChannelDiverterArgs channel);
 
+  async PTrackingDummyChannel(nsIURI uri, OptionalLoadInfoArgs loadInfo);
+
   /**
    * These are called from the child with the results of the auth prompt.
    * callbackId is the id that was passed in PBrowser::AsyncAuthPrompt,
    * corresponding to an nsIAuthPromptCallback
    */
   async OnAuthAvailable(uint64_t callbackId, nsString user,
                         nsString password, nsString domain);
   async OnAuthCancelled(uint64_t callbackId, bool userCancel);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -54,25 +54,28 @@
 #include "nsIDOMWindowUtils.h"
 #include "nsIEventTarget.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsSocketTransportService2.h"
 #include "nsStreamUtils.h"
 #include "nsThreadUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsApplicationCache.h"
+#include "TrackingDummyChannel.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerMarkerPayload.h"
 #endif
 
+#include <functional>
+
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ISUPPORTS(InterceptStreamListener,
                   nsIStreamListener,
@@ -2693,28 +2696,56 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   // Set user agent override from docshell
   HttpBaseChannel::SetDocshellUserAgentOverride();
 
   MOZ_ASSERT_IF(mPostRedirectChannelShouldUpgrade,
                 mPostRedirectChannelShouldIntercept);
   bool shouldUpgrade = mPostRedirectChannelShouldUpgrade;
   if (mPostRedirectChannelShouldIntercept ||
       ShouldInterceptURI(mURI, shouldUpgrade)) {
-    mResponseCouldBeSynthesized = true;
-
-    nsCOMPtr<nsINetworkInterceptController> controller;
-    GetCallback(controller);
-
-    mInterceptListener = new InterceptStreamListener(this, mListenerContext);
-
-    RefPtr<InterceptedChannelContent> intercepted =
-        new InterceptedChannelContent(this, controller,
-                                      mInterceptListener, shouldUpgrade);
-    intercepted->NotifyController();
-    return NS_OK;
+    RefPtr<HttpChannelChild> self = this;
+
+    std::function<void(bool)> callback = [self, shouldUpgrade](bool aStorageAllowed) {
+      if (!aStorageAllowed) {
+        nsresult rv = self->ContinueAsyncOpen();
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          Unused << self->AsyncAbort(rv);
+        }
+        return;
+      }
+
+      self->mResponseCouldBeSynthesized = true;
+
+      nsCOMPtr<nsINetworkInterceptController> controller;
+      self->GetCallback(controller);
+
+      self->mInterceptListener =
+        new InterceptStreamListener(self, self->mListenerContext);
+
+      RefPtr<InterceptedChannelContent> intercepted =
+          new InterceptedChannelContent(self, controller,
+                                        self->mInterceptListener,
+                                        shouldUpgrade);
+      intercepted->NotifyController();
+    };
+
+    TrackingDummyChannel::StorageAllowedState state =
+      TrackingDummyChannel::StorageAllowed(this, callback);
+    if (state == TrackingDummyChannel::eStorageGranted) {
+      callback(true);
+      return NS_OK;
+    }
+
+    if (state == TrackingDummyChannel::eAsyncNeeded) {
+      // The async callback will be executed eventually.
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(state == TrackingDummyChannel::eStorageDenied);
+    // Fall through
   }
 
   return ContinueAsyncOpen();
 }
 
 NS_IMETHODIMP
 HttpChannelChild::AsyncOpen2(nsIStreamListener *aListener)
 {
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PTrackingDummyChannel.ipdl
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+
+namespace mozilla {
+namespace net {
+
+// This protocol provides a mechanism for the "child intercept" mode of
+// ServiceWorker operation to work correctly with Tracking Protection
+// annotations.  ServiceWorkers should not be allowed for third-party iframes
+// which are annotated as tracking origins.
+//
+// In child intercept mode, the decision to intercept a channel is made in the
+// child process without consulting the parent process.  The decision is based
+// on whether there is a ServiceWorker with a scope covering the URL in question
+// and whether storage is allowed for the origin/URL.  When the
+// "network.cookie.cookieBehavior" preference is set to BEHAVIOR_REJECT_TRACKER,
+// annotated channels are denied storage which means that the ServiceWorker
+// should not intercept the channel.  However, the decision for tracking
+// protection to annotate a channel only occurs in the parent process.  The
+// dummy channel is a hack to allow the intercept decision process to ask the
+// parent process if the channel should be annotated.  Because this round-trip
+// to the parent has overhead, the dummy channel is only created 1) if the
+// ServiceWorker initially determines that the channel should be intercepted and
+// 2) it's a navigation request.
+//
+// This hack can be removed once Bug 1231208's new "parent intercept" mechanism
+// fully lands, the pref is enabled by default it stays enabled for long enough
+// to be confident we will never need/want to turn it off.  Then as part of bug
+// 1496997 we can remove this implementation.  Bug 1498259 covers removing this
+// hack in particular.
+protocol PTrackingDummyChannel
+{
+  manager PNecko;
+
+child:
+  async __delete__(bool aTrackingResource);
+};
+
+} // namespace net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannel.cpp
@@ -0,0 +1,356 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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 "TrackingDummyChannel.h"
+
+#include "mozilla/AntiTrackingCommon.h"
+#include "mozilla/net/TrackingDummyChannelChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Preferences.h"
+#include "nsIChannel.h"
+#include "nsIURI.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+// We need TrackingDummyChannel any time
+// privacy.trackingprotection.annotate_channels prefs is set to true.
+bool
+ChannelNeedsToBeAnnotated()
+{
+  static bool sChannelAnnotationNeededInited = false;
+  static bool sChannelAnnotationNeeded = false;
+
+  if (!sChannelAnnotationNeededInited) {
+    sChannelAnnotationNeededInited = true;
+    Preferences::AddBoolVarCache(&sChannelAnnotationNeeded,
+                                 "privacy.trackingprotection.annotate_channels");
+  }
+
+  return sChannelAnnotationNeeded;
+}
+
+} // anonymous
+
+/* static */ TrackingDummyChannel::StorageAllowedState
+TrackingDummyChannel::StorageAllowed(nsIChannel* aChannel,
+                                     const std::function<void(bool)>& aCallback)
+{
+  MOZ_ASSERT(!XRE_IsParentProcess());
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+  if (!httpChannel) {
+    // Any non-http channel is allowed.
+    return eStorageGranted;
+  }
+
+  if (!nsContentUtils::IsNonSubresourceRequest(aChannel)) {
+    // If this is a sub-resource, we don't need to check if the channel is
+    // annotated as tracker because:
+    // - if the SW doesn't respond, or it sends us back to the network, the
+    //   channel will be annotated on the parent process.
+    // - if the SW answers, the content is taken from the cache, which is
+    //   considered trusted.
+    return eStorageGranted;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  aChannel->GetURI(getter_AddRefs(uri));
+
+  if (ChannelNeedsToBeAnnotated()) {
+    ContentChild* cc = static_cast<ContentChild*>(gNeckoChild->Manager());
+    if (cc->IsShuttingDown()) {
+      return eStorageDenied;
+    }
+
+    if (!TrackingDummyChannelChild::Create(httpChannel, uri, aCallback)) {
+      return eStorageDenied;
+    }
+
+    return eAsyncNeeded;
+  }
+
+  if (AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel, uri,
+                                                              nullptr)) {
+    return eStorageGranted;
+  }
+
+  return eStorageDenied;
+}
+
+NS_IMPL_ADDREF(TrackingDummyChannel)
+NS_IMPL_RELEASE(TrackingDummyChannel)
+
+NS_INTERFACE_MAP_BEGIN(TrackingDummyChannel)
+  NS_INTERFACE_MAP_ENTRY(nsIRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIChannel)
+  NS_INTERFACE_MAP_ENTRY_CONCRETE(TrackingDummyChannel)
+NS_INTERFACE_MAP_END
+
+TrackingDummyChannel::TrackingDummyChannel(nsIURI* aURI,
+                                           nsILoadInfo* aLoadInfo)
+  : mIsTrackingResource(false)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  SetOriginalURI(aURI);
+  SetLoadInfo(aLoadInfo);
+}
+
+TrackingDummyChannel::~TrackingDummyChannel() = default;
+
+bool
+TrackingDummyChannel::IsTrackingResource() const
+{
+  return mIsTrackingResource;
+}
+
+void
+TrackingDummyChannel::SetIsTrackingResource()
+{
+  mIsTrackingResource = true;
+}
+
+//-----------------------------------------------------------------------------
+// TrackingDummyChannel::nsIChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetOriginalURI(nsIURI** aOriginalURI)
+{
+  NS_IF_ADDREF(*aOriginalURI = mURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetOriginalURI(nsIURI* aOriginalURI)
+{
+  mURI = aOriginalURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetURI(nsIURI** aURI)
+{
+  NS_IF_ADDREF(*aURI = mURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetOwner(nsISupports** aOwner)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetOwner(nsISupports* aOwner)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aNotificationCallbacks)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetSecurityInfo(nsISupports** aSecurityInfo)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentType(nsACString& aContentType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentType(const nsACString& aContentType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentCharset(nsACString& aContentCharset)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentCharset(const nsACString& aContentCharset)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentLength(int64_t* aContentLength)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentLength(int64_t aContentLength)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Open(nsIInputStream** aRetval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Open2(nsIInputStream** aStream)
+{
+  nsCOMPtr<nsIStreamListener> listener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return Open(aStream);
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+  nsCOMPtr<nsIStreamListener> listener = aListener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return AsyncOpen(listener, nullptr);
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentDisposition(uint32_t* aContentDisposition)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentDisposition(uint32_t aContentDisposition)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentDispositionFilename(nsAString& aContentDispositionFilename)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetContentDispositionHeader(nsACString& aContentDispositionHeader)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
+{
+  NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
+{
+  mLoadInfo = aLoadInfo;
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// TrackingDummyChannel::nsIRequest
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetName(nsACString& aName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::IsPending(bool* aRetval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetStatus(nsresult* aStatus)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Cancel(nsresult aStatus)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Suspend()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::Resume()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetLoadGroup(nsILoadGroup** aLoadGroup)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetLoadFlags(nsLoadFlags* aLoadFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TrackingDummyChannel::GetIsDocument(bool* aIsDocument)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // net namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannel.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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/. */
+
+#ifndef mozilla_net_TrackingDummyChannel_h
+#define mozilla_net_TrackingDummyChannel_h
+
+#include "nsIChannel.h"
+#include <functional>
+
+#define TRACKING_DUMMY_CHANNEL_IID \
+  { 0x70ceb97d, 0xbfa6, 0x4255, \
+    { 0xb7, 0x08, 0xe1, 0xb4, 0x4a, 0x1e, 0x0e, 0x9a } }
+
+class nsIChannel;
+
+namespace mozilla {
+namespace net {
+
+/**
+ * In child intercept mode, the decision to intercept a channel is made in the
+ * child process without consulting the parent process.  The decision is based
+ * on whether there is a ServiceWorker with a scope covering the URL in question
+ * and whether storage is allowed for the origin/URL.  When the
+ * "network.cookie.cookieBehavior" preference is set to BEHAVIOR_REJECT_TRACKER,
+ * annotated channels are denied storage which means that the ServiceWorker
+ * should not intercept the channel.  However, the decision for tracking
+ * protection to annotate a channel only occurs in the parent process.  The
+ * dummy channel is a hack to allow the intercept decision process to ask the
+ * parent process if the channel should be annotated.  Because this round-trip
+ * to the parent has overhead, the dummy channel is only created 1) if the
+ * ServiceWorker initially determines that the channel should be intercepted and
+ * 2) it's a navigation request.
+ *
+ * This hack can be removed once Bug 1231208's new "parent intercept" mechanism
+ * fully lands, the pref is enabled by default it stays enabled for long enough
+ * to be confident we will never need/want to turn it off.  Then as part of bug
+ * 1496997 we can remove this implementation.  Bug 1498259 covers removing this
+ * hack in particular.
+ */
+class TrackingDummyChannel final : public nsIChannel
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(TRACKING_DUMMY_CHANNEL_IID)
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIREQUEST
+  NS_DECL_NSICHANNEL
+
+  enum StorageAllowedState
+  {
+    eStorageGranted,
+    eStorageDenied,
+    eAsyncNeeded,
+  };
+
+  static StorageAllowedState
+  StorageAllowed(nsIChannel* aChannel,
+                 const std::function<void(bool)>& aCallback);
+
+  TrackingDummyChannel(nsIURI* aURI,
+                       nsILoadInfo* aLoadInfo);
+
+  bool
+  IsTrackingResource() const;
+
+  void
+  SetIsTrackingResource();
+
+private:
+  ~TrackingDummyChannel();
+
+  nsCOMPtr<nsILoadInfo> mLoadInfo;
+  nsCOMPtr<nsIURI> mURI;
+
+  bool mIsTrackingResource;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(TrackingDummyChannel, TRACKING_DUMMY_CHANNEL_IID)
+
+} // net namespace
+} // mozilla namespace
+
+#endif // mozilla_net_TrackingDummyChannel_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelChild.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "TrackingDummyChannelChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "nsIChannel.h"
+#include "nsIURI.h"
+
+namespace mozilla {
+namespace net {
+
+/* static */ bool
+TrackingDummyChannelChild::Create(nsIHttpChannel* aChannel, nsIURI* aURI,
+                                  const std::function<void(bool)>& aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(aChannel);
+  MOZ_ASSERT(aURI);
+
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (!loadInfo) {
+    return false;
+  }
+
+  OptionalLoadInfoArgs loadInfoArgs;
+  mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs);
+
+  PTrackingDummyChannelChild* actor =
+    gNeckoChild->SendPTrackingDummyChannelConstructor(aURI, loadInfoArgs);
+  if (!actor) {
+    return false;
+  }
+
+  bool isThirdParty =
+    nsContentUtils::IsThirdPartyWindowOrChannel(nullptr, aChannel, aURI);
+
+  static_cast<TrackingDummyChannelChild*>(actor)->Initialize(aChannel, aURI,
+                                                             isThirdParty,
+                                                             aCallback);
+  return true;
+}
+
+TrackingDummyChannelChild::TrackingDummyChannelChild()
+  : mIsThirdParty(false)
+{}
+
+TrackingDummyChannelChild::~TrackingDummyChannelChild() = default;
+
+void
+TrackingDummyChannelChild::Initialize(nsIHttpChannel* aChannel,
+                                      nsIURI* aURI,
+                                      bool aIsThirdParty,
+                                      const std::function<void(bool)>& aCallback)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mChannel = aChannel;
+  mURI = aURI;
+  mIsThirdParty = aIsThirdParty;
+  mCallback = aCallback;
+}
+
+mozilla::ipc::IPCResult
+TrackingDummyChannelChild::Recv__delete__(const bool& aTrackingResource)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mChannel) {
+    return IPC_OK();
+  }
+
+  nsCOMPtr<nsIHttpChannel> channel = std::move(mChannel);
+
+  RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(channel);
+  httpChannel->SetIsTrackingResource(mIsThirdParty);
+
+  bool storageGranted =
+    AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel, mURI,
+                                                            nullptr);
+  mCallback(storageGranted);
+  return IPC_OK();
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_net_TrackingDummyChannelChild_h
+#define mozilla_net_TrackingDummyChannelChild_h
+
+#include "mozilla/net/PTrackingDummyChannelChild.h"
+#include "nsCOMPtr.h"
+
+#include <functional>
+
+class nsIHttpChannel;
+class nsIURI;
+
+namespace mozilla {
+namespace net {
+
+class TrackingDummyChannelChild final : public PTrackingDummyChannelChild
+{
+public:
+  static bool
+  Create(nsIHttpChannel* aChannel, nsIURI* aURI,
+         const std::function<void(bool)>& aCallback);
+
+  // Used by PNeckoChild only!
+  TrackingDummyChannelChild();
+  ~TrackingDummyChannelChild();
+
+private:
+  void
+  Initialize(nsIHttpChannel* aChannel,
+             nsIURI* aURI,
+             bool aIsThirdParty,
+             const std::function<void(bool)>& aCallback);
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const bool& aTrackingResource) override;
+
+  nsCOMPtr<nsIHttpChannel> mChannel;
+  nsCOMPtr<nsIURI> mURI;
+  std::function<void(bool)> mCallback;
+  bool mIsThirdParty;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TrackingDummyChannelChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelParent.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "TrackingDummyChannelParent.h"
+#include "mozilla/Unused.h"
+#include "nsChannelClassifier.h"
+#include "nsIChannel.h"
+#include "nsIPrincipal.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+namespace net {
+
+TrackingDummyChannelParent::TrackingDummyChannelParent()
+  : mIPCActive(true)
+{}
+
+TrackingDummyChannelParent::~TrackingDummyChannelParent() = default;
+
+void
+TrackingDummyChannelParent::Init(nsIURI* aURI,
+                                 nsILoadInfo* aLoadInfo)
+{
+  MOZ_ASSERT(mIPCActive);
+
+  RefPtr<TrackingDummyChannelParent> self = this;
+  auto onExit = MakeScopeExit([self] {
+    Unused << Send__delete__(self, false);
+  });
+
+  if (!aURI) {
+    return;
+  }
+
+  RefPtr<TrackingDummyChannel> channel =
+    new TrackingDummyChannel(aURI, aLoadInfo);
+
+  RefPtr<nsChannelClassifier> channelClassifier =
+    new nsChannelClassifier(channel);
+
+  bool willCallback =
+    NS_SUCCEEDED(channelClassifier->CheckIsTrackerWithLocalTable(
+      [self = std::move(self), channel]() {
+        if (self->mIPCActive) {
+          Unused << Send__delete__(self, channel->IsTrackingResource());
+        }
+      }));
+
+  if (willCallback) {
+    onExit.release();
+  }
+}
+
+void
+TrackingDummyChannelParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mIPCActive = false;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TrackingDummyChannelParent.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_net_TrackingDummyChannelParent_h
+#define mozilla_net_TrackingDummyChannelParent_h
+
+#include "mozilla/net/PTrackingDummyChannelParent.h"
+#include "nsISupportsImpl.h"
+
+class nsILoadInfo;
+class nsIURI;
+
+namespace mozilla {
+namespace net {
+
+class TrackingDummyChannelParent final : public PTrackingDummyChannelParent
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(TrackingDummyChannelParent)
+
+  TrackingDummyChannelParent();
+
+  void
+  Init(nsIURI* aURI,
+       nsILoadInfo* aLoadInfo);
+
+private:
+  ~TrackingDummyChannelParent();
+
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  bool mIPCActive;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_TrackingDummyChannelParent_h
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -47,16 +47,19 @@ EXPORTS.mozilla.net += [
     'HttpChannelChild.h',
     'HttpChannelParent.h',
     'HttpInfo.h',
     'nsServerTiming.h',
     'NullHttpChannel.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
     'TimingStruct.h',
+    'TrackingDummyChannel.h',
+    'TrackingDummyChannelChild.h',
+    'TrackingDummyChannelParent.h',
 ]
 
 SOURCES += [
     'nsHttpChannelAuthProvider.cpp', # redefines GetAuthType
 ]
 
 UNIFIED_SOURCES += [
     'AltDataOutputStreamChild.cpp',
@@ -95,28 +98,32 @@ UNIFIED_SOURCES += [
     'nsHttpHeaderArray.cpp',
     'nsHttpNTLMAuth.cpp',
     'nsHttpRequestHead.cpp',
     'nsHttpResponseHead.cpp',
     'nsHttpTransaction.cpp',
     'nsServerTiming.cpp',
     'NullHttpChannel.cpp',
     'NullHttpTransaction.cpp',
+    'TrackingDummyChannel.cpp',
+    'TrackingDummyChannelChild.cpp',
+    'TrackingDummyChannelParent.cpp',
     'TunnelUtils.cpp',
 ]
 
 # These files cannot be built in unified mode because of OS X headers.
 SOURCES += [
     'nsHttpHandler.cpp',
 ]
 
 IPDL_SOURCES += [
     'PAltDataOutputStream.ipdl',
     'PHttpBackgroundChannel.ipdl',
     'PHttpChannel.ipdl',
+    'PTrackingDummyChannel.ipdl',
 ]
 
 EXTRA_JS_MODULES += [
     'UserAgentOverrides.jsm',
     'UserAgentUpdates.jsm',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -104,35 +104,42 @@
 #include "mozilla/extensions/StreamFilterParent.h"
 #include "mozilla/net/Predictor.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/NullPrincipal.h"
 #include "CacheControlParser.h"
 #include "nsMixedContentBlocker.h"
 #include "CacheStorageService.h"
 #include "HttpChannelParent.h"
+#include "HttpChannelParentListener.h"
 #include "InterceptedHttpChannel.h"
 #include "nsIBufferedStreams.h"
 #include "nsIFileStreams.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "../../cache2/CacheFileUtils.h"
 #include "nsINetworkLinkService.h"
+#include "nsIRedirectProcessChooser.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ServiceWorkerUtils.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 #ifdef MOZ_GECKO_PROFILER
 #include "ProfilerMarkerPayload.h"
 #endif
 
-namespace mozilla { namespace net {
+namespace mozilla {
+
+using namespace dom;
+
+namespace net {
 
 namespace {
 
 static bool sRCWNEnabled = false;
 static uint32_t sRCWNQueueSizeNormal = 50;
 static uint32_t sRCWNQueueSizePriority = 10;
 static uint32_t sRCWNSmallResourceSizeKB = 256;
 static uint32_t sRCWNMinWaitMs = 0;
@@ -6770,17 +6777,17 @@ nsHttpChannel::ProcessId()
   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
   if (httpParent) {
     return httpParent->OtherPid();
   }
   return base::GetCurrentProcId();
 }
 
 bool
-nsHttpChannel::AttachStreamFilter(ipc::Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
+nsHttpChannel::AttachStreamFilter(mozilla::ipc::Endpoint<extensions::PStreamFilterParent>&& aEndpoint)
 
 {
   nsCOMPtr<nsIParentChannel> parentChannel;
   NS_QueryNotificationCallbacks(this, parentChannel);
   RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
   if (httpParent) {
     return httpParent->SendAttachStreamFilter(std::move(aEndpoint));
   }
--- a/security/manager/locales/en-US/chrome/pippki/certManager.dtd
+++ b/security/manager/locales/en-US/chrome/pippki/certManager.dtd
@@ -74,17 +74,15 @@
 <!ENTITY certmgr.hierarchy.accesskey2         "H">
 <!ENTITY certmgr.addException.label           "Add Exception…">
 <!ENTITY certmgr.addException.accesskey       "x">
 
 <!ENTITY exceptionMgr.title                   "Add Security Exception">
 <!ENTITY exceptionMgr.exceptionButton.label   "Confirm Security Exception">
 <!ENTITY exceptionMgr.exceptionButton.accesskey "C">
 <!ENTITY exceptionMgr.supplementalWarning     "Legitimate banks, stores, and other public sites will not ask you to do this.">
-<!ENTITY exceptionMgr.certlocation.caption2   "Server">
 <!ENTITY exceptionMgr.certlocation.url        "Location:">
 <!ENTITY exceptionMgr.certlocation.download   "Get Certificate">
 <!ENTITY exceptionMgr.certlocation.accesskey  "G">
-<!ENTITY exceptionMgr.certstatus.caption      "Certificate Status">
 <!ENTITY exceptionMgr.certstatus.viewCert     "View…">
 <!ENTITY exceptionMgr.certstatus.accesskey    "V">
 <!ENTITY exceptionMgr.permanent.label         "Permanently store this exception">
 <!ENTITY exceptionMgr.permanent.accesskey     "P">
--- a/security/manager/pki/resources/content/changepassword.xul
+++ b/security/manager/pki/resources/content/changepassword.xul
@@ -20,17 +20,16 @@
 
 <hbox align="center">
   <label value="&setPassword.tokenName.label;: "/>
   <label id="tokenName" />
 </hbox>
 
 <separator/>
 
-<groupbox>
 <grid>
  <columns>
    <column/>
    <column/> 
  </columns>
  <rows>
    <row>
      <label value="&setPassword.oldPassword.label;"/> 
@@ -50,17 +49,13 @@
        oninput="setPasswordStrength(); checkPasswords();"/> 
    </row>
    <row>
      <label value="&setPassword.reenterPassword.label;"/> 
      <textbox id="pw2" type="password" oninput="checkPasswords();"/>  
    </row>
  </rows>
 </grid>
-</groupbox>
 
-<groupbox>
- <caption label="&setPassword.meter.label;"/>
- <progressmeter id="pwmeter" mode="determined" 
-                  value="0"/>
-</groupbox>
+<label control="pwmeter" value="&setPassword.meter.label;"/>
+<progressmeter id="pwmeter" mode="determined" value="0"/>
 
 </dialog>
--- a/security/manager/pki/resources/content/choosetoken.xul
+++ b/security/manager/pki/resources/content/choosetoken.xul
@@ -19,16 +19,14 @@
   ondialogcancel="return doCancel();"
   onload="onLoad();">
 
 <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
 <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 <script type="application/javascript" src="chrome://pippki/content/choosetoken.js"/>
 
-  <groupbox>
-    <description>&chooseToken.message1;</description>
-    <menulist id="tokens">
-      <menupopup/>
-    </menulist>
-  </groupbox>
+<description>&chooseToken.message1;</description>
+<menulist id="tokens">
+  <menupopup/>
+</menulist>
 
 </dialog>
--- a/security/manager/pki/resources/content/clientauthask.xul
+++ b/security/manager/pki/resources/content/clientauthask.xul
@@ -20,29 +20,26 @@
 
 <stringbundleset id="stringbundleset">
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 </stringbundleset>
 
 <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 <script type="application/javascript" src="chrome://pippki/content/clientauthask.js"/>
 
-  <groupbox>
-    <description style="font-weight: bold;">&clientAuthAsk.message1;</description>
-    <description id="hostname"/>
-    <description id="organization"/>
-    <description id="issuer"/>
-    </groupbox>
-    <groupbox>
-    <description style="font-weight: bold;">&clientAuthAsk.message2;</description>
-    <!-- The items in this menulist must never be sorted,
-         but remain in the order filled by the application
-    -->
-    <menulist id="nicknames" oncommand="onCertSelected();">
-        <menupopup/>
-    </menulist>
-    <description>&clientAuthAsk.message3;</description>
-    <textbox readonly="true" id="details" multiline="true"
-      style="height: 11em;"/>
-    <checkbox id="rememberBox" checked="true"/>
-  </groupbox>
+<description style="font-weight: bold;">&clientAuthAsk.message1;</description>
+<description id="hostname"/>
+<description id="organization"/>
+<description id="issuer"/>
+
+<description style="font-weight: bold;">&clientAuthAsk.message2;</description>
+<!-- The items in this menulist must never be sorted,
+     but remain in the order filled by the application
+-->
+<menulist id="nicknames" oncommand="onCertSelected();">
+    <menupopup/>
+</menulist>
+<description>&clientAuthAsk.message3;</description>
+<textbox readonly="true" id="details" multiline="true"
+  style="height: 11em;"/>
+<checkbox id="rememberBox" checked="true"/>
 
 </dialog>
--- a/security/manager/pki/resources/content/exceptionDialog.xul
+++ b/security/manager/pki/resources/content/exceptionDialog.xul
@@ -42,46 +42,38 @@
       <description id="warningText"
                    style="white-space: pre-wrap"/>
       <description id="warningSupplemental"
                    style="font-weight: bold; white-space: pre-wrap;"
                    >&exceptionMgr.supplementalWarning;</description>
     </vbox>
   </hbox>
 
-  <groupbox id="locationGroupBox">
-    <caption label="&exceptionMgr.certlocation.caption2;"/>
-    <hbox align="center">
-      <label control="locationTextBox" value="&exceptionMgr.certlocation.url;"/>
-      <textbox id="locationTextBox" flex="1" oninput="handleTextChange();"
-               value="https://" class="uri-element"/>
-      <button id="checkCertButton" disabled="true" dlgtype="extra2"
-              accesskey="&exceptionMgr.certlocation.accesskey;"
-              label="&exceptionMgr.certlocation.download;"/>
-    </hbox>
-  </groupbox>
+  <hbox align="center">
+    <label control="locationTextBox" value="&exceptionMgr.certlocation.url;"/>
+    <textbox id="locationTextBox" flex="1" oninput="handleTextChange();"
+             value="https://" class="uri-element"/>
+    <button id="checkCertButton" disabled="true" dlgtype="extra2"
+            accesskey="&exceptionMgr.certlocation.accesskey;"
+            label="&exceptionMgr.certlocation.download;"/>
+  </hbox>
 
-  <groupbox id="certStatusGroupBox" flex="1">
-    <caption label="&exceptionMgr.certstatus.caption;"/>
-    <hbox>
-      <description id="headerDescription" style="white-space: pre-wrap;"
-                   flex="1"/>
-      <vbox>
-        <button id="viewCertButton" label="&exceptionMgr.certstatus.viewCert;"
-                accesskey="&exceptionMgr.certstatus.accesskey;"
-                disabled="true" oncommand="viewCertButtonClick();"/>
-      </vbox>
-    </hbox>
-    <description id="statusDescription"
-                 style="font-weight: bold; padding-bottom: 1em;"/>
-    <description id="statusLongDescription" style="white-space: pre-wrap;"/>
-    <description id="status2Description"
-                 style="font-weight: bold; padding-bottom: 1em;"/>
-    <description id="status2LongDescription" style="white-space: pre-wrap;"/>
-    <description id="status3Description"
-                 style="font-weight: bold; padding-bottom: 1em;"/>
-    <description id="status3LongDescription" style="white-space: pre-wrap;"/>
-    <spacer flex="1"/>
-    <checkbox id="permanent" disabled="true"
-              label="&exceptionMgr.permanent.label;"
-              accesskey="&exceptionMgr.permanent.accesskey;"/>
-  </groupbox>
+  <hbox align="center">
+    <description id="headerDescription" style="white-space: pre-wrap;"
+                 flex="1"/>
+    <button id="viewCertButton" label="&exceptionMgr.certstatus.viewCert;"
+            accesskey="&exceptionMgr.certstatus.accesskey;"
+            disabled="true" oncommand="viewCertButtonClick();"/>
+  </hbox>
+  <description id="statusDescription"
+               style="font-weight: bold; padding-bottom: 1em;"/>
+  <description id="statusLongDescription" style="white-space: pre-wrap;"/>
+  <description id="status2Description"
+               style="font-weight: bold; padding-bottom: 1em;"/>
+  <description id="status2LongDescription" style="white-space: pre-wrap;"/>
+  <description id="status3Description"
+               style="font-weight: bold; padding-bottom: 1em;"/>
+  <description id="status3LongDescription" style="white-space: pre-wrap;"/>
+  <spacer flex="1"/>
+  <checkbox id="permanent" disabled="true"
+            label="&exceptionMgr.permanent.label;"
+            accesskey="&exceptionMgr.permanent.accesskey;"/>
 </dialog>
--- a/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.ini
@@ -26,9 +26,8 @@
   [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
     expected: FAIL
 
   [Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
     expected: FAIL
 
   [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and allowfullscreen.]
     expected: FAIL
-
--- a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html
+++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html
@@ -5,80 +5,141 @@
   <script src=/feature-policy/resources/featurepolicy.js></script>
   <!-- Feature-Policy: fullscreen *; -->
   <script>
   'use strict';
   var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}';
   var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}';
   var same_origin_src = '/feature-policy/resources/feature-policy-allowedfeatures.html';
   var cross_origin_src = cross_origin + same_origin_src;
+  var data_src = 'data:text/html,<h1>data: URL</h1>';
   var policies = [
-    {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true},
-    {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false},
-    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false},
-    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true}];
+    {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: true},
+    {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: false}];
   var pipe_front = '?pipe=sub|header(Feature-Policy,fullscreen ';
   var pipe_end = ';)';
   var header_policies = ["*", "'self'", "'none'"];
 
   // Test that frame.policy inherits from parent's header policy when allow
   // attribute is not specified.
   test(function() {
-    test_frame_policy('fullscreen', same_origin_src, true);
+    test_frame_policy('fullscreen', same_origin_src, undefined, true);
   }, 'Test frame policy on same origin iframe inherit from header policy.');
   test(function() {
-    test_frame_policy('fullscreen', cross_origin_src, true);
+    test_frame_policy('fullscreen', cross_origin_src, undefined, true);
   }, 'Test frame policy on cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', undefined, true, true);
+  }, 'Test frame policy on srcdoc iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', same_origin_src, true, true);
+  }, 'Test frame policy on srcdoc+ same origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', cross_origin_src, true, true);
+  }, 'Test frame policy on srcdoc+ cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', data_src, undefined, true);
+  }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.');
 
   // Test frame policy with allow attribute set to be one of the policies above.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".');
   }
 
   // Test that the header policy of the iframe document does not change the
   // frame policy.
   for (var i = 0; i < policies.length; i++) {
     for (var j = 0; j < header_policies.length; j++) {
       test(function() {
         test_frame_policy(
           'fullscreen',
           same_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].sameOriginTestExpect,
+          undefined, policies[i].sameOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
       test(function() {
         test_frame_policy(
           'fullscreen',
           cross_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].crossOriginTestExpect,
+          undefined, policies[i].crossOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
     }
   }
 
   // Test that the allow attribute overrides allowfullscreen.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
   }
   </script>
 </body>
--- a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html
+++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html
@@ -5,98 +5,175 @@
   <script src=/feature-policy/resources/featurepolicy.js></script>
   <!-- Feature-Policy: fullscreen 'self'; -->
   <script>
   'use strict';
   var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}';
   var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}';
   var same_origin_src = '/feature-policy/resources/feature-policy-allowedfeatures.html';
   var cross_origin_src = cross_origin + same_origin_src;
+  var data_src = 'data:text/html,<h1>data: URL</h1>';
   var policies = [
-    {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true},
-    {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false},
-    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false},
-    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true}];
+    {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: false},
+    {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, dataOriginTestExpect: false}];
   var pipe_front = '?pipe=sub|header(Feature-Policy,fullscreen ';
   var pipe_end = ';)';
   var header_policies = ["*", "'self'", "'none'"];
 
   // Test that frame.policy inherits from parent's header policy when allow
   // attribute is not specified.
   test(function() {
-    test_frame_policy('fullscreen', same_origin_src, true);
+    test_frame_policy('fullscreen', same_origin_src, undefined, true);
   }, 'Test frame policy on same origin iframe inherit from header policy.');
   test(function() {
-    test_frame_policy('fullscreen', cross_origin_src, false);
+    test_frame_policy('fullscreen', cross_origin_src, undefined, false);
   }, 'Test frame policy on cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', undefined, true, true);
+  }, 'Test frame policy on srcdoc iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', same_origin_src, true, true);
+  }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', cross_origin_src, true, true);
+  }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', data_src, undefined, false);
+  }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.');
 
   // Test that frame policy can be used for sandboxed frames
   test(function() {
     test_frame_policy(
-      'fullscreen', same_origin_src, false, undefined, false, true);
+      'fullscreen', same_origin_src, undefined, false, undefined, false, true);
     }, 'Test frame policy on sandboxed iframe with no allow attribute.');
   test(function() {
     test_frame_policy(
-      'fullscreen', same_origin_src, true, 'fullscreen', false, true);
+      'fullscreen', same_origin_src, undefined, true, 'fullscreen', false, true);
     }, 'Test frame policy on sandboxed iframe with allow="fullscreen".');
   test(function() {
     test_frame_policy(
-      'fullscreen', same_origin_src, true, 'fullscreen \'src\'', false, true);
+      'fullscreen', same_origin_src, undefined, true, 'fullscreen \'src\'', false, true);
     }, 'Test frame policy on sandboxed iframe with allow="fullscreen \'src\'".');
   test(function() {
     test_frame_policy(
-      'fullscreen', cross_origin_src, false, 'fullscreen ' + cross_origin, false, true);
+      'fullscreen', cross_origin_src, undefined, false, 'fullscreen ' + cross_origin, false, true);
     }, 'Test frame policy on sandboxed iframe with allow="fullscreen ' + cross_origin + '".');
+  test(function() {
+    test_frame_policy(
+      'fullscreen', undefined, true, true, 'fullscreen', false, true);
+    }, 'Test frame policy on srcdoc sandboxed iframe with allow="fullscreen".');
+  test(function() {
+    test_frame_policy(
+      'fullscreen', same_origin_src, true, true, 'fullscreen', false, true);
+    }, 'Test frame policy on srcdoc + same origin sandboxed iframe with allow="fullscreen".');
+  test(function() {
+    test_frame_policy(
+      'fullscreen', cross_origin_src, true, true, 'fullscreen', false, true);
+    }, 'Test frame policy on srcdoc + cross origin sandboxed iframe with allow="fullscreen".');
+  test(function() {
+    test_frame_policy(
+      'fullscreen', data_src, undefined, false, 'fullscreen ' + cross_origin, false, true);
+    }, 'Test frame policy on sandboxed srcdoc iframe with allow="fullscreen ' + cross_origin + '".');
 
   // Test frame policy with allow attribute set to be one of the policies above.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on data: URL origin iframe with allow = "' + policies[i].allow + '".');
   }
 
   // Test that the header policy of the iframe document does not change the
   // frame policy.
   for (var i = 0; i < policies.length; i++) {
     for (var j = 0; j < header_policies.length; j++) {
       test(function() {
         test_frame_policy(
           'fullscreen',
           same_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].sameOriginTestExpect,
+          undefined, policies[i].sameOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
       test(function() {
         test_frame_policy(
           'fullscreen',
           cross_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].crossOriginTestExpect,
+          undefined, policies[i].crossOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
     }
   }
 
   // Test that the allow attribute overrides allowfullscreen.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on data: URL origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
   }
   </script>
 </body>
--- a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html
+++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html
@@ -7,103 +7,180 @@
   <script>
   'use strict';
   var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}';
   var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}';
   var cross_origin1 = 'https://{{domains[www1]}}:{{ports[https][0]}}';
   var same_origin_src = '/feature-policy/resources/feature-policy-allowedfeatures.html';
   var cross_origin_src = cross_origin + same_origin_src;
   var cross_origin_src1 = cross_origin1 + same_origin_src;
+  var data_src = 'data:text/html,<h1>data: URL</h1>';
   // Test feature policy with same_origin_src and cross_origin_src.
   var policies = [
-    {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: true},
-    {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, crossOrigin1TestExpect: false},
-    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, crossOrigin1TestExpect: false},
-    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: false}];
+    {allow: "*", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: true, dataOriginTestExpect: false},
+    {allow: "'self'", sameOriginTestExpect: true, crossOriginTestExpect: false, crossOrigin1TestExpect: false, dataOriginTestExpect: false},
+    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, crossOrigin1TestExpect: false, dataOriginTestExpect: false},
+    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: true, crossOriginTestExpect: true, crossOrigin1TestExpect: false, dataOriginTestExpect: false}];
   var pipe_front = '?pipe=sub|header(Feature-Policy,fullscreen ';
   var pipe_end = ';)';
   var header_policies = ["*", "'self'", "'none'"];
 
   // Test that frame.policy inherits from parent's header policy when allow
   // attribute is not specified.
   test(function() {
-    test_frame_policy('fullscreen', same_origin_src, true);
+    test_frame_policy('fullscreen', same_origin_src, undefined, true);
   }, 'Test frame policy on same origin iframe inherit from header policy.');
   test(function() {
-    test_frame_policy('fullscreen', cross_origin_src, true);
+    test_frame_policy('fullscreen', cross_origin_src, undefined, true);
   }, 'Test frame policy on cross origin iframe inherit from header policy.');
   test(function() {
-    test_frame_policy('fullscreen', cross_origin_src1, false);
+    test_frame_policy('fullscreen', cross_origin_src1, undefined, false);
   }, 'Test frame policy on another cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', undefined, true, true);
+  }, 'Test frame policy on srcdoc iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', same_origin_src, true, true);
+  }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', cross_origin_src, true, true);
+  }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', cross_origin_src1, true, true);
+  }, 'Test frame policy on srcdoc + another cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', data_src, undefined, false);
+  }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.');
 
   // Test frame policy with allow attribute set to be one of the policies above.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src1, policies[i].crossOrigin1TestExpect,
+        'fullscreen', cross_origin_src1, undefined,
+        policies[i].crossOrigin1TestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src1, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + another cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".');
   }
 
   // Test that the header policy of the iframe document does not change the
   // frame policy.
   for (var i = 0; i < policies.length; i++) {
     for (var j = 0; j < header_policies.length; j++) {
       test(function() {
         test_frame_policy(
           'fullscreen',
           same_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].sameOriginTestExpect,
+          undefined, policies[i].sameOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
       test(function() {
         test_frame_policy(
           'fullscreen',
           cross_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].crossOriginTestExpect,
+          undefined, policies[i].crossOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
       test(function() {
         test_frame_policy(
           'fullscreen',
           cross_origin_src1 + pipe_front + header_policies[j] + pipe_end,
-          policies[i].crossOrigin1TestExpect,
+          undefined, policies[i].crossOrigin1TestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
     }
   }
 
   // Test that the allow attribute overrides allowfullscreen.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src1, policies[i].crossOrigin1TestExpect,
+        'fullscreen', cross_origin_src1, undefined,
+        policies[i].crossOrigin1TestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on another cross origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src1, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + another cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
   }
   </script>
 </body>
--- a/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html
+++ b/testing/web-platform/tests/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html
@@ -5,80 +5,141 @@
   <script src=/feature-policy/resources/featurepolicy.js></script>
   <!-- Feature-Policy: fullscreen 'none'; -->
   <script>
   'use strict';
   var same_origin = 'https://{{domains[]}}:{{ports[https][0]}}';
   var cross_origin = 'https://{{domains[www]}}:{{ports[https][0]}}';
   var same_origin_src = '/feature-policy/resources/feature-policy-allowedfeatures.html';
   var cross_origin_src = cross_origin + same_origin_src;
+  var data_src = 'data:text/html,<h1>data: URL</h1>';
   var policies = [
-    {allow: "*", sameOriginTestExpect: false, crossOriginTestExpect: false},
-    {allow: "'self'", sameOriginTestExpect: false, crossOriginTestExpect: false},
-    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false},
-    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: false, crossOriginTestExpect: false}];
+    {allow: "*", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'self'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'none'", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false},
+    {allow: "'self' " + cross_origin + " https://www.example.com", sameOriginTestExpect: false, crossOriginTestExpect: false, dataOriginTestExpect: false}];
   var pipe_front = '?pipe=sub|header(Feature-Policy,fullscreen ';
   var pipe_end = ';)';
   var header_policies = ["*", "'self'", "'none'"];
 
   // Test that frame.policy inherits from parent's header policy when allow
   // attribute is not specified.
   test(function() {
-    test_frame_policy('fullscreen', same_origin_src, false);
+    test_frame_policy('fullscreen', same_origin_src, undefined, false);
   }, 'Test frame policy on same origin iframe inherit from header policy.');
   test(function() {
-    test_frame_policy('fullscreen', cross_origin_src, false);
+    test_frame_policy('fullscreen', cross_origin_src, undefined, false);
   }, 'Test frame policy on cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', undefined, true, false);
+  }, 'Test frame policy on srcdoc iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', same_origin_src, true, false);
+  }, 'Test frame policy on srcdoc + same origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', cross_origin_src, true, false);
+  }, 'Test frame policy on srcdoc + cross origin iframe inherit from header policy.');
+  test(function() {
+    test_frame_policy('fullscreen', data_src, undefined, false);
+  }, 'Test frame policy on data: URL cross origin iframe inherit from header policy.');
 
   // Test frame policy with allow attribute set to be one of the policies above.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow + '".');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';');
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow + '".');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';');
+    }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow + '".');
   }
 
   // Test that the header policy of the iframe document does not change the
   // frame policy.
   for (var i = 0; i < policies.length; i++) {
     for (var j = 0; j < header_policies.length; j++) {
       test(function() {
         test_frame_policy(
           'fullscreen',
           same_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].sameOriginTestExpect,
+          undefined, policies[i].sameOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
       test(function() {
         test_frame_policy(
           'fullscreen',
           cross_origin_src + pipe_front + header_policies[j] + pipe_end,
-          policies[i].crossOriginTestExpect,
+          undefined, policies[i].crossOriginTestExpect,
           'fullscreen ' + policies[i].allow + ';');
       }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
          '" and header policy = "Feature-Policy: fullscreen ' + header_policies[j] + ';".');
     }
   }
 
   // Test that the allow attribute overrides allowfullscreen.
   for (var i = 0; i < policies.length; i++) {
     test(function() {
       test_frame_policy(
-        'fullscreen', same_origin_src, policies[i].sameOriginTestExpect,
+        'fullscreen', same_origin_src, undefined,
+        policies[i].sameOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on same origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
     test(function() {
       test_frame_policy(
-        'fullscreen', cross_origin_src, policies[i].crossOriginTestExpect,
+        'fullscreen', cross_origin_src, undefined,
+        policies[i].crossOriginTestExpect,
         'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
     }, 'Test frame policy on cross origin iframe with allow = "' + policies[i].allow +
        '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', undefined, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', same_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + same origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', cross_origin_src, true, policies[i].sameOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on srcdoc + cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
+    test(function() {
+      test_frame_policy(
+        'fullscreen', data_src, undefined, policies[i].dataOriginTestExpect,
+        'fullscreen ' + policies[i].allow + ';', /*allowfullscreen*/true);
+    }, 'Test frame policy on data: URL cross origin iframe with allow = "' + policies[i].allow +
+       '" and allowfullscreen.');
   }
   </script>
 </body>
--- a/testing/web-platform/tests/feature-policy/resources/featurepolicy.js
+++ b/testing/web-platform/tests/feature-policy/resources/featurepolicy.js
@@ -391,37 +391,43 @@ function test_subframe_header_policy(
   }, test_name);
 }
 
 // This function tests that frame policy allows a given feature correctly. A
 // feature is allowed in a frame either through inherited policy or specified
 // by iframe allow attribute.
 // Arguments:
 //     feature: feature name.
-//     src: the URL to load in the frame.
+//     src: the URL to load in the frame. If undefined, the iframe will have a
+//         srcdoc="" attribute
 //     test_expect: boolean value of whether the feature should be allowed.
 //     allow: optional, the allow attribute (container policy) of the iframe.
 //     allowfullscreen: optional, boolean value of allowfullscreen attribute.
 //     sandbox: optional boolean. If true, the frame will be sandboxed (with
 //         allow-scripts, so that tests can run in it.)
 function test_frame_policy(
-    feature, src, test_expect, allow, allowfullscreen, sandbox) {
+    feature, src, srcdoc, test_expect, allow, allowfullscreen, sandbox) {
   let frame = document.createElement('iframe');
   document.body.appendChild(frame);
   // frame_policy should be dynamically updated as allow and allowfullscreen is
   // updated.
   var frame_policy = frame.policy;
   if (typeof allow !== 'undefined') {
     frame.setAttribute('allow', allow);
   }
   if (!!allowfullscreen) {
     frame.setAttribute('allowfullscreen', true);
   }
   if (!!sandbox) {
     frame.setAttribute('sandbox', 'allow-scripts');
   }
-  frame.src = src;
+  if (!!src) {
+    frame.src = src;
+  }
+  if (!!srcdoc) {
+    frame.srcdoc = "<h1>Hello world!</h1>";
+  }
   if (test_expect) {
     assert_true(frame_policy.allowedFeatures().includes(feature));
   } else {
     assert_false(frame_policy.allowedFeatures().includes(feature));
   }
 }