Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Mon, 01 Oct 2018 19:28:34 +0300
changeset 494754 c6ae59fd5fff3aff99383bd8caa2c84c7145008f
parent 494753 ba7f9ff7e4c528f35042ca72cc345353d655e560 (current diff)
parent 494705 890a773ea546b579acfe8f35a1b8bb197c7cf055 (diff)
child 494755 df4088486e03f86dfb135f66f5938971e3700e9e
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [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 mozilla-central to autoland. a=merge CLOSED TREE
testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html.ini
testing/web-platform/meta/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html.ini
testing/web-platform/meta/fetch/api/request/destination/fetch-destination.https.html.ini
testing/web-platform/meta/resource-timing/test_resource_timing.html.ini
testing/web-platform/meta/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html.ini
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -59,16 +59,17 @@
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/FullscreenChange.h"
 
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
+#include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/dom/FramingChecker.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/SVGUseElement.h"
 #include "nsGenericHTMLElement.h"
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "nsDOMString.h"
 #include "nsNodeUtils.h"
@@ -1922,16 +1923,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
 
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
@@ -2014,16 +2016,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy)
 
   tmp->mParentDocument = nullptr;
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
 
   tmp->ClearAllBoxObjects();
@@ -2774,16 +2777,20 @@ nsDocument::StartDocumentLoad(const char
   }
 
   // If this is not a data document, set CSP.
   if (!mLoadedAsData) {
     nsresult rv = InitCSP(aChannel);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Initialize FeaturePolicy
+  nsresult rv = InitFeaturePolicy(aChannel);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // XFO needs to be checked after CSP because it is ignored if
   // the CSP defines frame-ancestors.
   if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
     MOZ_LOG(gCspPRLog, LogLevel::Debug,
             ("XFO doesn't like frame's ancestry, not loading."));
     // stop!  ERROR page!
     aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
   }
@@ -2998,16 +3005,79 @@ nsIDocument::InitCSP(nsIChannel* aChanne
       // stop!  ERROR page!
       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
     }
   }
   ApplySettingsFromCSP(false);
   return NS_OK;
 }
 
+nsresult
+nsIDocument::InitFeaturePolicy(nsIChannel* aChannel)
+{
+  MOZ_ASSERT(!mFeaturePolicy, "we should only call init once");
+
+  // 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);
+
+  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();
+        }
+      }
+    }
+  }
+
+  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));
+  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 */);
+  }
+
+  return NS_OK;
+}
+
 void
 nsDocument::StopDocumentLoad()
 {
   if (mParser) {
     mParserAborted = true;
     mParser->Terminate();
   }
 }
@@ -10159,16 +10229,27 @@ nsIDocument::MaybeResolveReadyForIdle()
 {
   IgnoredErrorResult rv;
   Promise* readyPromise = GetDocumentReadyForIdle(rv);
   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.
+  MOZ_ASSERT(mFeaturePolicy);
+  return mFeaturePolicy;
+}
+
 nsIDOMXULCommandDispatcher*
 nsIDocument::GetCommandDispatcher()
 {
   // Only chrome documents are allowed to use command dispatcher.
   if (!nsContentUtils::IsChromeDoc(this)) {
     return nullptr;
   }
   if (!mCommandDispatcher) {
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -159,16 +159,17 @@ class DocumentTimeline;
 class DocumentType;
 class DOMImplementation;
 class DOMIntersectionObserver;
 class DOMStringList;
 class Element;
 struct ElementCreationOptions;
 class Event;
 class EventTarget;
+class FeaturePolicy;
 class FontFaceSet;
 class FrameRequestCallback;
 class ImageTracker;
 class HTMLBodyElement;
 class HTMLSharedElement;
 class HTMLImageElement;
 struct LifecycleCallbackArgs;
 class Link;
@@ -1435,16 +1436,18 @@ public:
   //
   already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
 
 protected:
   friend class nsUnblockOnloadEvent;
 
   nsresult InitCSP(nsIChannel* aChannel);
 
+  nsresult InitFeaturePolicy(nsIChannel* aChannel);
+
   void PostUnblockOnloadEvent();
 
   void DoUnblockOnload();
 
   void ClearAllBoxObjects();
 
   void MaybeEndOutermostXBLUpdate();
 
@@ -3768,16 +3771,19 @@ public:
     return mAllowPaymentRequest;
   }
 
   void SetAllowPaymentRequest(bool aAllowPaymentRequest)
   {
     mAllowPaymentRequest = aAllowPaymentRequest;
   }
 
+  mozilla::dom::FeaturePolicy*
+  Policy() const;
+
   bool IsShadowDOMEnabled() const
   {
     return mIsShadowDOMEnabled;
   }
 
   bool ModuleScriptsEnabled();
 
   /**
@@ -4081,16 +4087,18 @@ protected:
   // Last time this document or a one of its sub-documents was focused.  If
   // focus has never occurred then mLastFocusTime.IsNull() will be true.
   mozilla::TimeStamp mLastFocusTime;
 
   mozilla::EventStates mDocumentState;
 
   RefPtr<mozilla::dom::Promise> mReadyForIdle;
 
+  RefPtr<mozilla::dom::FeaturePolicy> mFeaturePolicy;
+
   // True if BIDI is enabled.
   bool mBidiEnabled : 1;
   // True if a MathML element has ever been owned by this document.
   bool mMathMLEnabled : 1;
 
   // True if this document is the initial document for a window.  This should
   // basically be true only for documents that exist in newly-opened windows or
   // documents created to satisfy a GetDocument() on a window when there's no
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -692,16 +692,20 @@ DOMInterfaces = {
 'PluginArray': {
     'nativeType': 'nsPluginArray',
 },
 
 'PluginTag': {
     'nativeType': 'nsIPluginTag',
 },
 
+'Policy': {
+    'nativeType': 'mozilla::dom::FeaturePolicy',
+},
+
 'Position': {
     'headerFile': 'nsGeoPosition.h'
 },
 
 'PromiseDebugging': {
     'concrete': False,
 },
 
--- a/dom/html/HTMLIFrameElement.cpp
+++ b/dom/html/HTMLIFrameElement.cpp
@@ -1,23 +1,26 @@
 /* -*- 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 "mozilla/dom/HTMLIFrameElement.h"
 #include "mozilla/dom/HTMLIFrameElementBinding.h"
+#include "mozilla/dom/FeaturePolicy.h"
 #include "mozilla/MappedDeclarations.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 {
 
 // static
 const DOMTokenListSupportedToken HTMLIFrameElement::sSupportedSandboxTokens[] = {
@@ -26,24 +29,43 @@ const DOMTokenListSupportedToken HTMLIFr
 #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);
+  }
 }
 
 HTMLIFrameElement::~HTMLIFrameElement()
 {
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLIFrameElement)
 
+nsresult
+HTMLIFrameElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent)
+{
+  nsresult rv = nsGenericHTMLFrameElement::BindToTree(aDocument, aParent,
+                                                      aBindingParent);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (StaticPrefs::dom_security_featurePolicy_enabled()) {
+    RefreshFeaturePolicy();
+  }
+  return NS_OK;
+}
+
 bool
 HTMLIFrameElement::ParseAttribute(int32_t aNamespaceID,
                                   nsAtom* aAttribute,
                                   const nsAString& aValue,
                                   nsIPrincipal* aMaybeScriptedPrincipal,
                                   nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
@@ -145,16 +167,22 @@ HTMLIFrameElement::AfterSetAttr(int32_t 
     if (aName == nsGkAtoms::sandbox) {
       if (mFrameLoader) {
         // 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::sandbox) &&
+        StaticPrefs::dom_security_featurePolicy_enabled()) {
+      RefreshFeaturePolicy();
+    }
   }
   return nsGenericHTMLFrameElement::AfterSetAttr(aNameSpaceID, aName,
                                                  aValue, aOldValue,
                                                  aMaybeScriptedPrincipal,
                                                  aNotify);
 }
 
 nsresult
@@ -178,26 +206,110 @@ HTMLIFrameElement::AfterMaybeChangeAttr(
       // Don't propagate errors from LoadSrc. The attribute was successfully
       // set/unset, that's what we should reflect.
       LoadSrc();
     }
   }
 }
 
 uint32_t
-HTMLIFrameElement::GetSandboxFlags()
+HTMLIFrameElement::GetSandboxFlags() const
 {
   const nsAttrValue* sandboxAttr = GetParsedAttr(nsGkAtoms::sandbox);
   // No sandbox attribute, no sandbox flags.
   if (!sandboxAttr) {
     return SANDBOXED_NONE;
   }
   return nsContentUtils::ParseSandboxAttributeToFlags(sandboxAttr);
 }
 
 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
+{
+  aDefaultOrigin.Truncate();
+
+  nsresult rv;
+  nsAutoString src;
+  GetURIAttr(nsGkAtoms::src, nullptr, src);
+
+  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 (!nodeURI) {
+    if (OwnerDoc()->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+      return NS_OK;
+    }
+
+    nodeURI = OwnerDoc()->GetDocumentURI();
+  }
+
+  nsAutoString origin;
+  rv = nsContentUtils::GetUTFOrigin(nodeURI, origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aDefaultOrigin.Assign(origin);
+  return NS_OK;
+}
+
+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;
+  }
+
+  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->InheritPolicy(OwnerDoc()->Policy());
+
+  // TODO: https://wicg.github.io/feature-policy/#process-feature-policy-attributes
+  // requires to check allowfullscreen, allowpaymentrequest and allowusermediarequest
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -28,27 +28,29 @@ public:
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override
   {
     return true;
   }
 
   // nsIContent
+  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+                              nsIContent* aBindingParent) override;
   virtual bool ParseAttribute(int32_t aNamespaceID,
                                 nsAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsIPrincipal* aMaybeScriptedPrincipal,
                                 nsAttrValue& aResult) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override;
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
 
-  uint32_t GetSandboxFlags();
+  uint32_t GetSandboxFlags() const;
 
   // Web IDL binding methods
   void GetSrc(nsString& aSrc) const
   {
     GetURIAttr(nsGkAtoms::src, nullptr, aSrc);
   }
   void SetSrc(const nsAString& aSrc, nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError)
   {
@@ -111,16 +113,24 @@ public:
   void GetAlign(DOMString& aAlign)
   {
     GetHTMLAttr(nsGkAtoms::align, aAlign);
   }
   void SetAlign(const nsAString& aAlign, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::align, aAlign, aError);
   }
+  void GetAllow(DOMString& aAllow)
+  {
+    GetHTMLAttr(nsGkAtoms::allow, aAllow);
+  }
+  void SetAllow(const nsAString& aAllow, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::allow, aAllow, aError);
+  }
   void GetScrolling(DOMString& aScrolling)
   {
     GetHTMLAttr(nsGkAtoms::scrolling, aScrolling);
   }
   void SetScrolling(const nsAString& aScrolling, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::scrolling, aScrolling, aError);
   }
@@ -183,16 +193,19 @@ public:
 
   // The fullscreen flag is set to true only when requestFullscreen is
   // explicitly called on this <iframe> element. In case this flag is
   // set, the fullscreen state of this element will not be reverted
   // automatically when its subdocument exits fullscreen.
   bool FullscreenFlag() const { return mFullscreenFlag; }
   void SetFullscreenFlag(bool aValue) { mFullscreenFlag = aValue; }
 
+  FeaturePolicy*
+  Policy() const;
+
 protected:
   virtual ~HTMLIFrameElement();
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
                                 const nsAttrValue* aValue,
                                 const nsAttrValue* aOldValue,
@@ -203,16 +216,21 @@ protected:
                                           bool aNotify) override;
 
 private:
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     MappedDeclarations&);
 
   static const DOMTokenListSupportedToken sSupportedSandboxTokens[];
 
+  void RefreshFeaturePolicy();
+
+  nsresult
+  GetFeaturePolicyDefaultOrigin(nsAString& aDefaultOrigin) 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.
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -30,27 +30,29 @@ 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,16 +5,17 @@
  * 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 {
@@ -122,16 +123,19 @@ 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/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -94,8 +94,14 @@ BlockSubresourceFTP=Loading FTP subresource within http(s) page not allowed (Blocked loading of: “%1$S”)
 
 # LOCALIZATION NOTE (BrowserUpgradeInsecureDisplayRequest):
 # %1$S is the browser name "brandShortName"; %2$S is the URL of the upgraded request; %1$S is the upgraded scheme.
 BrowserUpgradeInsecureDisplayRequest = %1$S is upgrading an insecure display request ‘%2$S’ to use ‘%3$S’
 # LOCALIZATION NOTE (RunningClearSiteDataValue):
 # %S is the URI of the resource whose data was cleaned up
 RunningClearSiteDataValue=Clear-Site-Data header forced the clean up of “%S” data.
 UnknownClearSiteDataValue=Clear-Site-Data header found. Unknown value “%S”.
+
+FeaturePolicyUnsupportedFeatureName=Feature Policy: Skipping unsupported feature name “%S”.
+# TODO: would be nice to add a link to the Feature-Policy MDN documentation here. See bug 1449501
+FeaturePolicyInvalidEmptyAllowValue= Feature Policy: Skipping empty allow list for feature: “%S”.
+# TODO: would be nice to add a link to the Feature-Policy MDN documentation here. See bug 1449501
+FeaturePolicyInvalidAllowValue=Feature Policy: Skipping unsupported allow value “%S”.
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/Feature.cpp
@@ -0,0 +1,98 @@
+/* -*- 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"
+
+using namespace mozilla::dom;
+
+void
+Feature::GetWhiteListedOrigins(nsTArray<nsString>& aList) const
+{
+  MOZ_ASSERT(mPolicy == eWhiteList);
+  aList.AppendElements(mWhiteListedOrigins);
+}
+
+bool
+Feature::Allows(const nsAString& aOrigin) const
+{
+  if (mPolicy == eNone) {
+    return false;
+  }
+
+  if (mPolicy == eAll) {
+    return true;
+  }
+
+  for (const nsString& whiteListedOrigin : mWhiteListedOrigins) {
+    if (whiteListedOrigin.Equals(aOrigin)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+Feature::Feature(const nsAString& aFeatureName)
+  : mFeatureName(aFeatureName)
+  , mPolicy(eWhiteList)
+{}
+
+Feature::~Feature() = default;
+
+const nsAString&
+Feature::Name() const
+{
+  return mFeatureName;
+}
+
+void
+Feature::SetAllowsNone()
+{
+  mPolicy = eNone;
+  mWhiteListedOrigins.Clear();
+}
+
+bool
+Feature::AllowsNone() const
+{
+  return mPolicy == eNone;
+}
+
+void
+Feature::SetAllowsAll()
+{
+  mPolicy = eAll;
+  mWhiteListedOrigins.Clear();
+}
+
+bool
+Feature::AllowsAll() const
+{
+  return mPolicy == eAll;
+}
+
+void
+Feature::AppendOriginToWhiteList(const nsAString& aOrigin)
+{
+  mPolicy = eWhiteList;
+  mWhiteListedOrigins.AppendElement(aOrigin);
+}
+
+bool
+Feature::WhiteListContains(const nsAString& aOrigin) const
+{
+  if (!IsWhiteList()) {
+    return false;
+  }
+
+  return mWhiteListedOrigins.Contains(aOrigin);
+}
+
+bool
+Feature::IsWhiteList() const
+{
+  return mPolicy == eWhiteList;
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/Feature.h
@@ -0,0 +1,76 @@
+/* -*- 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_dom_Feature_h
+#define mozilla_dom_Feature_h
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class Feature final
+{
+public:
+  explicit Feature(const nsAString& aFeatureName);
+
+  ~Feature();
+
+  const nsAString&
+  Name() const;
+
+  void
+  SetAllowsNone();
+
+  bool
+  AllowsNone() const;
+
+  void
+  SetAllowsAll();
+
+  bool
+  AllowsAll() const;
+
+  void
+  AppendOriginToWhiteList(const nsAString& aOrigin);
+
+  void
+  GetWhiteListedOrigins(nsTArray<nsString>& aList) const;
+
+  bool
+  WhiteListContains(const nsAString& aOrigin) const;
+
+  bool
+  IsWhiteList() const;
+
+  bool
+  Allows(const nsAString& aOrigin) 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,
+  };
+
+  Policy mPolicy;
+
+  nsTArray<nsString> mWhiteListedOrigins;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_Feature_h
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/FeaturePolicy.cpp
@@ -0,0 +1,184 @@
+/* -*- 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 "FeaturePolicy.h"
+#include "mozilla/dom/FeaturePolicyBinding.h"
+#include "mozilla/dom/FeaturePolicyParser.h"
+#include "mozilla/dom/FeaturePolicyUtils.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla::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)
+NS_INTERFACE_MAP_END
+
+FeaturePolicy::FeaturePolicy(nsINode* aNode)
+  : mParentNode(aNode)
+{}
+
+void
+FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy)
+{
+  MOZ_ASSERT(aParentPolicy);
+
+  mInheritedDeniedFeatureNames.Clear();
+
+  RefPtr<FeaturePolicy> dest = this;
+  RefPtr<FeaturePolicy> src = aParentPolicy;
+  nsString 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)) {
+      if (!dest->AllowsFeatureInternal(featureName, origin) ||
+          !src->AllowsFeatureInternal(featureName, origin)) {
+        dest->SetInheritedDeniedFeature(featureName);
+      }
+      return;
+    }
+
+    // If there was not a declared feature, we allow the feature if the parent
+    // FeaturePolicy allows the current origin.
+    if (!src->AllowsFeatureInternal(featureName, origin)) {
+      dest->SetInheritedDeniedFeature(featureName);
+    }
+  });
+}
+
+void
+FeaturePolicy::SetInheritedDeniedFeature(const nsAString& aFeatureName)
+{
+  MOZ_ASSERT(!HasInheritedDeniedFeature(aFeatureName));
+  mInheritedDeniedFeatureNames.AppendElement(aFeatureName);
+}
+
+bool
+FeaturePolicy::HasInheritedDeniedFeature(const nsAString& aFeatureName) const
+{
+  return mInheritedDeniedFeatureNames.Contains(aFeatureName);
+}
+
+bool
+FeaturePolicy::HasDeclaredFeature(const nsAString& aFeatureName) const
+{
+  for (const Feature& feature : mFeatures) {
+    if (feature.Name().Equals(aFeatureName)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void
+FeaturePolicy::SetDeclaredPolicy(nsIDocument* aDocument,
+                                 const nsAString& aPolicyString,
+                                 const nsAString& aSelfOrigin,
+                                 const nsAString& aSrcOrigin,
+                                 bool aSrcEnabled)
+{
+  ResetDeclaredPolicy();
+
+  Unused << NS_WARN_IF(!FeaturePolicyParser::ParseString(aPolicyString,
+                                                         aDocument,
+                                                         aSelfOrigin,
+                                                         aSrcOrigin,
+                                                         aSrcEnabled,
+                                                         mFeatures));
+}
+
+void
+FeaturePolicy::ResetDeclaredPolicy()
+{
+  mFeatures.Clear();
+}
+
+JSObject*
+FeaturePolicy::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  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);
+}
+
+bool
+FeaturePolicy::AllowsFeatureInternal(const nsAString& aFeatureName,
+                                     const nsAString& aOrigin) const
+{
+  // 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);
+}
+
+void
+FeaturePolicy::AllowedFeatures(nsTArray<nsString>& aAllowedFeatures)
+{
+  RefPtr<FeaturePolicy> self = this;
+  FeaturePolicyUtils::ForEachFeature([self, &aAllowedFeatures](const char* aFeatureName) {
+    nsString featureName;
+    featureName.AppendASCII(aFeatureName);
+
+    if (self->AllowsFeatureInternal(featureName, self->mDefaultOrigin)) {
+      aAllowedFeatures.AppendElement(featureName);
+    }
+  });
+}
+
+void
+FeaturePolicy::GetAllowlistForFeature(const nsAString& aFeatureName,
+                                      nsTArray<nsString>& aList) const
+{
+  if (!AllowsFeatureInternal(aFeatureName, mDefaultOrigin)) {
+    return;
+  }
+
+  for (const Feature& feature : mFeatures) {
+    if (feature.Name().Equals(aFeatureName)) {
+      if (feature.AllowsAll()) {
+        aList.AppendElement(NS_LITERAL_STRING("*"));
+        return;
+      }
+
+      feature.GetWhiteListedOrigins(aList);
+      return;
+    }
+  }
+
+  nsString defaultAllowList;
+  FeaturePolicyUtils::DefaultAllowListFeature(aFeatureName, mDefaultOrigin,
+                                              defaultAllowList);
+   if (!defaultAllowList.IsEmpty()) {
+    aList.AppendElement(defaultAllowList);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/FeaturePolicy.h
@@ -0,0 +1,166 @@
+/* -*- 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_dom_FeaturePolicy_h
+#define mozilla_dom_FeaturePolicy_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Feature.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+
+/**
+ * FeaturePolicy
+ * ~~~~~~~~~~~~~
+ *
+ * Each document and each HTMLIFrameElement have a FeaturePolicy object which is
+ * used to allow or deny features in their contexts. FeaturePolicy is active
+ * when pref dom.security.featurePolicy.enabled is set to true.
+ *
+ * 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.
+ *
+ * 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).
+ * 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
+ * current origin, it checks if the parent context denied that feature.
+ * If not, it checks if there is a Feature object for that
+ * feature named X and if the origin is allowed or not.
+ *
+ * From a C++ point of view, use FeaturePolicyUtils to obtain the list of
+ * features and to check if they are allowed in the current context.
+ **/
+
+class nsIDocument;
+class nsIHttpChannel;
+class nsINode;
+
+namespace mozilla {
+namespace dom {
+
+class FeaturePolicyUtils;
+
+class FeaturePolicy final : public nsISupports
+                          , public nsWrapperCache
+{
+  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.
+  // This method must be called before any other exposed WebIDL method or before
+  // checking if a feature is allowed.
+  void
+  SetDefaultOrigin(const nsAString& aOrigin)
+  {
+    // aOrigin can be an empty string if this is a opaque origin.
+    mDefaultOrigin = aOrigin;
+  }
+
+  const nsAString& 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);
+
+  // Clears all the declarative policy directives. This is needed when the
+  // 'allow' attribute or the 'src' attribute change for HTMLIFrameElement's
+  // policy.
+  void
+  ResetDeclaredPolicy();
+
+  // WebIDL internal methods.
+
+  JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  nsINode*
+  GetParentObject() const
+  {
+    return mParentNode;
+  }
+
+  // WebIDL explosed methods.
+
+  bool
+  AllowsFeature(const nsAString& aFeatureName,
+                const Optional<nsAString>& aOrigin) const;
+
+  void
+  AllowedFeatures(nsTArray<nsString>& aAllowedFeatures);
+
+  void
+  GetAllowlistForFeature(const nsAString& aFeatureName,
+                         nsTArray<nsString>& aList) const;
+
+private:
+  ~FeaturePolicy() = default;
+
+  bool
+  AllowsFeatureInternal(const nsAString& aFeatureName,
+                        const nsAString& aOrigin) const;
+
+  // Inherits a single denied feature from the parent context.
+  void
+  SetInheritedDeniedFeature(const nsAString& aFeatureName);
+
+  bool
+  HasInheritedDeniedFeature(const nsAString& aFeatureName) const;
+
+  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;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FeaturePolicy_h
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/FeaturePolicyParser.cpp
@@ -0,0 +1,162 @@
+/* -*- 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 "FeaturePolicyParser.h"
+
+#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 {
+
+void
+ReportToConsoleUnsupportedFeature(nsIDocument* aDocument,
+                                  const nsString& aFeatureName)
+{
+  const char16_t* params[] = { aFeatureName.get() };
+
+  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                  NS_LITERAL_CSTRING("Feature Policy"),
+                                  aDocument,
+                                  nsContentUtils::eSECURITY_PROPERTIES,
+                                  "FeaturePolicyUnsupportedFeatureName",
+                                  params, ArrayLength(params));
+}
+
+void
+ReportToConsoleInvalidEmptyAllowValue(nsIDocument* aDocument,
+                                      const nsString& aFeatureName)
+{
+  const char16_t* params[] = { aFeatureName.get() };
+
+  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                  NS_LITERAL_CSTRING("Feature Policy"),
+                                  aDocument,
+                                  nsContentUtils::eSECURITY_PROPERTIES,
+                                  "FeaturePolicyInvalidEmptyAllowValue",
+                                  params, ArrayLength(params));
+}
+
+void
+ReportToConsoleInvalidAllowValue(nsIDocument* aDocument,
+                                 const nsString& aValue)
+{
+  const char16_t* params[] = { aValue.get() };
+
+  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                  NS_LITERAL_CSTRING("Feature Policy"),
+                                  aDocument,
+                                  nsContentUtils::eSECURITY_PROPERTIES,
+                                  "FeaturePolicyInvalidAllowValue",
+                                  params, ArrayLength(params));
+}
+
+} // anonymous
+
+/* static */ bool
+FeaturePolicyParser::ParseString(const nsAString& aPolicy,
+                                 nsIDocument* aDocument,
+                                 const nsAString& aSelfOrigin,
+                                 const nsAString& aSrcOrigin,
+                                 bool aSrcEnabled,
+                                 nsTArray<Feature>& aParsedFeatures)
+{
+  nsTArray<nsTArray<nsString>> tokens;
+  PolicyTokenizer::tokenizePolicy(aPolicy, tokens);
+
+  nsTArray<Feature> parsedFeatures;
+
+  for (const nsTArray<nsString>& featureTokens : tokens) {
+    if (featureTokens.IsEmpty()) {
+      continue;
+    }
+
+    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);
+      } 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];
+        if (curVal.LowerCaseEqualsASCII("'none'")) {
+          feature.SetAllowsNone();
+          break;
+        }
+
+        if (curVal.EqualsLiteral("*")) {
+          feature.SetAllowsAll();
+          break;
+        }
+
+        if (curVal.LowerCaseEqualsASCII("'self'")) {
+          // Opaque origins are passed as empty string.
+          if (!aSelfOrigin.IsEmpty()) {
+            feature.AppendOriginToWhiteList(aSelfOrigin);
+          }
+          continue;
+        }
+
+        if (aSrcEnabled && curVal.LowerCaseEqualsASCII("'src'")) {
+          // Opaque origins are passed as empty string.
+          if (!aSrcOrigin.IsEmpty()) {
+            feature.AppendOriginToWhiteList(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))) {
+          ReportToConsoleInvalidAllowValue(aDocument, curVal);
+          continue;
+        }
+
+        feature.AppendOriginToWhiteList(origin);
+      }
+    }
+
+    // No duplicate!
+    bool found = false;
+    for (const Feature& parsedFeature : parsedFeatures) {
+      if (parsedFeature.Name() == feature.Name()) {
+        found = true;
+        break;
+      }
+    }
+
+    if (!found) {
+      parsedFeatures.AppendElement(feature);
+    }
+  }
+
+  aParsedFeatures.SwapElements(parsedFeatures);
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/FeaturePolicyParser.h
@@ -0,0 +1,37 @@
+/* -*- 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_dom_FeaturePolicyParser_h
+#define mozilla_dom_FeaturePolicyParser_h
+
+#include "nsString.h"
+
+class nsIDocument;
+class nsIURI;
+
+namespace mozilla {
+namespace dom {
+
+class Feature;
+
+class FeaturePolicyParser final
+{
+public:
+  // aSelfOrigin must not be empty. if aSrcOrigin is empty, 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,
+              nsTArray<Feature>& aParsedFeatures);
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FeaturePolicyParser_h
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.cpp
@@ -0,0 +1,148 @@
+/* -*- 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 "FeaturePolicyUtils.h"
+#include "mozilla/dom/FeaturePolicy.h"
+#include "mozilla/StaticPrefs.h"
+#include "nsIDocument.h"
+
+using namespace mozilla::dom;
+
+struct FeatureMap {
+  const char* mFeatureName;
+
+  enum {
+    eAll,
+    eSelf,
+  } mDefaultAllowList;
+};
+
+/*
+ * IMPORTANT: Do not change this list without review from a DOM peer _AND_ a
+ * DOM Security peer!
+ */
+static FeatureMap sSupportedFeatures[] = {
+  // TODO: not supported yet!!!
+  { "accelerometer", FeatureMap::eSelf },
+  // TODO: not supported yet!!!
+  { "ambient-light-sensor", FeatureMap::eSelf },
+  // TODO: not supported yet!!!
+  { "autoplay", FeatureMap::eSelf },
+  // TODO: not supported yet!!!
+  { "camera", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "encrypted-media", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "fullscreen", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "geolocation", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "gyroscope", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "magnetometer", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "microphone", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "midi", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "payment", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "picture-in-picture", FeatureMap::eAll  },
+  // TODO: not supported yet!!!
+  { "speaker", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "usb", FeatureMap::eSelf  },
+  // TODO: not supported yet!!!
+  { "vr", FeatureMap::eSelf  },
+};
+
+/* 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)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/* static */ void
+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)
+{
+  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 false;
+}
+
+/* static */ bool
+FeaturePolicyUtils::IsFeatureAllowed(nsIDocument* aDocument,
+                                     const nsAString& aFeatureName)
+{
+  MOZ_ASSERT(aDocument);
+
+  if (!StaticPrefs::dom_security_featurePolicy_enabled()) {
+    return true;
+  }
+
+  if (!aDocument->IsHTMLDocument()) {
+    return true;
+  }
+
+  FeaturePolicy* policy = aDocument->Policy();
+  MOZ_ASSERT(policy);
+
+  return policy->AllowsFeatureInternal(aFeatureName, policy->DefaultOrigin());
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/FeaturePolicyUtils.h
@@ -0,0 +1,45 @@
+/* -*- 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_dom_FeaturePolicyUtils_h
+#define mozilla_dom_FeaturePolicyUtils_h
+
+#include "nsString.h"
+#include <functional>
+
+class nsIDocument;
+
+namespace mozilla {
+namespace dom {
+
+class FeaturePolicyUtils final
+{
+public:
+  static bool
+  IsFeatureAllowed(nsIDocument* aDocument,
+                   const nsAString& aFeatureName);
+
+  static bool
+  IsSupportedFeature(const nsAString& aFeatureName);
+
+  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);
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_FeaturePolicyUtils_h
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/moz.build
@@ -0,0 +1,29 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ('Core', 'DOM: Security')
+
+TEST_DIRS += [ 'test/gtest' ]
+MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
+
+EXPORTS.mozilla.dom += [
+  'Feature.h',
+  'FeaturePolicy.h',
+  'FeaturePolicyParser.h',
+  'FeaturePolicyUtils.h',
+]
+
+UNIFIED_SOURCES += [
+  'Feature.cpp',
+  'FeaturePolicy.cpp',
+  'FeaturePolicyParser.cpp',
+  'FeaturePolicyUtils.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/test/gtest/TestFeaturePolicyParser.cpp
@@ -0,0 +1,162 @@
+/* -*- 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/dom/Feature.h"
+#include "mozilla/dom/FeaturePolicyParser.h"
+#include "nsNetUtil.h"
+#include "nsTArray.h"
+
+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")
+
+void
+CheckParser(const nsAString& aInput, bool aExpectedResults,
+            uint32_t aExpectedFeatures, nsTArray<Feature>& aParsedFeatures)
+{
+  nsTArray<Feature> parsedFeatures;
+  ASSERT_TRUE(FeaturePolicyParser::ParseString(aInput,
+                                               nullptr,
+                                               URL_SELF,
+                                               EmptyString(),
+                                               true, // 'src' enabled
+                                               parsedFeatures) == aExpectedResults);
+  ASSERT_TRUE(parsedFeatures.Length() == aExpectedFeatures);
+
+  parsedFeatures.SwapElements(aParsedFeatures);
+}
+
+TEST(FeaturePolicyParser, Basic)
+{
+  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());
+
+  // 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());
+
+  // 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());
+
+  // 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());
+
+  // 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[1].Name().Equals(NS_LITERAL_STRING("microphone")));
+  ASSERT_TRUE(parsedFeatures[1].IsWhiteList());
+
+  // 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[1].Name().Equals(NS_LITERAL_STRING("microphone")));
+  ASSERT_TRUE(parsedFeatures[1].IsWhiteList());
+
+  // 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[1].Name().Equals(NS_LITERAL_STRING("microphone")));
+  ASSERT_TRUE(parsedFeatures[1].IsWhiteList());
+
+  // 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));
+
+  // 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));
+
+  // 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));
+
+  // 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));
+
+  // 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.
+  CheckParser(NS_LITERAL_STRING("camera 'none' 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].AllowsNone());
+
+  // After a * we don't continue the parsing.
+  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));
+
+  // 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));
+
+  // 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));
+
+  // 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());
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/test/gtest/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES = [
+    'TestFeaturePolicyParser.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
+
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/test/mochitest/empty.html
@@ -0,0 +1,1 @@
+Nothing here
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/test/mochitest/mochitest.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+prefs =
+  dom.security.featurePolicy.enabled=true
+support-files =
+  empty.html
+  test_parser.html^headers^
+
+[test_parser.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/test/mochitest/test_parser.html
@@ -0,0 +1,220 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test feature policy - parsing</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe src="empty.html" id="ifr"></iframe>
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function test_document() {
+  info("Checking document.policy");
+  ok("policy" in document, "We have document.policy");
+
+  ok(!document.policy.allowsFeature("foobar"), "Random feature");
+  ok(!document.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
+
+  ok(document.policy.allowsFeature("camera"), "Camera is always enabled");
+  ok(document.policy.allowsFeature("camera", "http://foo.bar"), "Camera is always enabled");
+  let allowed = document.policy.getAllowlistForFeature("camera");
+  is(allowed.length, 1, "Only 1 entry in allowlist for camera");
+  is(allowed[0], "*", "allowlist is *");
+
+  ok(document.policy.allowsFeature("geolocation"), "Geolocation is enabled for self");
+  ok(document.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for self");
+  ok(!document.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is not enabled for anything else");
+  allowed = document.policy.getAllowlistForFeature("geolocation");
+  is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
+  is(allowed[0], location.origin, "allowlist is self");
+
+  ok(!document.policy.allowsFeature("microphone"), "Microphone is disabled for self");
+  ok(!document.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
+  ok(!document.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
+  ok(document.policy.allowsFeature("microphone", "http://example.com"), "Microphone is enabled for example.com");
+  ok(document.policy.allowsFeature("microphone", "http://example.org"), "Microphone is enabled for example.org");
+  allowed = document.policy.getAllowlistForFeature("microphone");
+  is(allowed.length, 0, "No allowlist for microphone");
+
+  ok(!document.policy.allowsFeature("vr"), "Vibrate is disabled for self");
+  ok(!document.policy.allowsFeature("vr", location.origin), "Vibrate is disabled for self");
+  ok(!document.policy.allowsFeature("vr", "http://foo.bar"), "Vibrate is disabled for foo.bar");
+  allowed = document.policy.getAllowlistForFeature("vr");
+  is(allowed.length, 0, "No allowlist for vr");
+
+  allowed = document.policy.allowedFeatures();
+  // microphone is disabled for this origin, vr is disabled everywhere.
+  let camera = false;
+  let geolocation = false;
+  allowed.forEach(a => {
+    if (a == "camera") camera = true;
+    if (a == "geolocation") geolocation = true;
+  });
+
+  ok(camera, "Camera is always allowed");
+  ok(geolocation, "Geolocation is allowed only for self");
+
+  next();
+}
+
+function test_iframe_without_allow() {
+  info("Checking HTMLIFrameElement.policy");
+  let ifr = document.getElementById("ifr");
+  ok("policy" in ifr, "HTMLIFrameElement.policy exists");
+
+  ok(!ifr.policy.allowsFeature("foobar"), "Random feature");
+  ok(!ifr.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
+
+  ok(ifr.policy.allowsFeature("camera"), "Camera is always enabled for self");
+  ok(ifr.policy.allowsFeature("camera", location.origin), "Camera is allowed for self");
+  ok(!ifr.policy.allowsFeature("camera", "http://foo.bar"), "Camera is not allowed for a random URL");
+  let allowed = ifr.policy.getAllowlistForFeature("camera");
+  is(allowed.length, 1, "Only 1 entry in allowlist for camera");
+  is(allowed[0], location.origin, "allowlist is 'self'");
+
+  ok(ifr.policy.allowsFeature("geolocation"), "Geolocation is enabled for self");
+  ok(ifr.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for self");
+  ok(!ifr.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is not enabled for anything else");
+  allowed = ifr.policy.getAllowlistForFeature("geolocation");
+  is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
+  is(allowed[0], location.origin, "allowlist is self");
+
+  ok(!ifr.policy.allowsFeature("microphone"), "Microphone is disabled for self");
+  ok(!ifr.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
+  ok(!ifr.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
+  ok(!ifr.policy.allowsFeature("microphone", "http://example.com"), "Microphone is disabled for example.com");
+  ok(!ifr.policy.allowsFeature("microphone", "http://example.org"), "Microphone is disabled for example.org");
+  allowed = ifr.policy.getAllowlistForFeature("microphone");
+  is(allowed.length, 0, "No allowlist for microphone");
+
+  ok(!ifr.policy.allowsFeature("vr"), "Vibrate is disabled for self");
+  ok(!ifr.policy.allowsFeature("vr", location.origin), "Vibrate is disabled for self");
+  ok(!ifr.policy.allowsFeature("vr", "http://foo.bar"), "Vibrate is disabled for foo.bar");
+  allowed = ifr.policy.getAllowlistForFeature("vr");
+  is(allowed.length, 0, "No allowlist for vr");
+
+  ok(ifr.policy.allowedFeatures().includes("camera"), "Camera is allowed");
+  ok(ifr.policy.allowedFeatures().includes("geolocation"), "Geolocation is allowed");
+  // microphone is disabled for this origin
+  ok(!ifr.policy.allowedFeatures().includes("microphone"), "microphone is not allowed");
+  // vr is disabled everywhere.
+  ok(!ifr.policy.allowedFeatures().includes("vr"), "VR is not allowed");
+
+  next();
+}
+
+function test_iframe_with_allow() {
+  info("Checking HTMLIFrameElement.policy");
+  let ifr = document.getElementById("ifr");
+  ok("policy" in ifr, "HTMLIFrameElement.policy exists");
+
+  ifr.setAttribute("allow", "camera 'none'");
+
+  ok(!ifr.policy.allowsFeature("foobar"), "Random feature");
+  ok(!ifr.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
+
+  ok(!ifr.policy.allowsFeature("camera"), "Camera is not enabled");
+  let allowed = ifr.policy.getAllowlistForFeature("camera");
+  is(allowed.length, 0, "Camera has an empty allowlist");
+
+  ok(ifr.policy.allowsFeature("geolocation"), "Geolocation is enabled for self");
+  ok(ifr.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for self");
+  ok(!ifr.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is not enabled for anything else");
+  allowed = ifr.policy.getAllowlistForFeature("geolocation");
+  is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
+  is(allowed[0], location.origin, "allowlist is self");
+
+  ok(!ifr.policy.allowsFeature("microphone"), "Microphone is disabled for self");
+  ok(!ifr.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
+  ok(!ifr.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
+  ok(!ifr.policy.allowsFeature("microphone", "http://example.com"), "Microphone is disabled for example.com");
+  ok(!ifr.policy.allowsFeature("microphone", "http://example.org"), "Microphone is disabled for example.org");
+  allowed = ifr.policy.getAllowlistForFeature("microphone");
+  is(allowed.length, 0, "No allowlist for microphone");
+
+  ok(!ifr.policy.allowsFeature("vr"), "Vibrate is disabled for self");
+  ok(!ifr.policy.allowsFeature("vr", location.origin), "Vibrate is disabled for self");
+  ok(!ifr.policy.allowsFeature("vr", "http://foo.bar"), "Vibrate is disabled for foo.bar");
+  allowed = ifr.policy.getAllowlistForFeature("vr");
+  is(allowed.length, 0, "No allowlist for vr");
+
+  ok(ifr.policy.allowedFeatures().includes("geolocation"), "Geolocation is allowed only for self");
+
+  next();
+}
+
+function test_iframe_contentDocument() {
+  info("Checking iframe document.policy");
+
+  let ifr = document.createElement("iframe");
+  ifr.setAttribute("src", "empty.html");
+  ifr.onload = function() {
+    ok("policy" in ifr.contentDocument, "We have ifr.contentDocument.policy");
+
+    ok(!ifr.contentDocument.policy.allowsFeature("foobar"), "Random feature");
+    ok(!ifr.contentDocument.policy.allowsFeature("foobar", "http://www.something.net"), "Random feature");
+
+    ok(ifr.contentDocument.policy.allowsFeature("camera"), "Camera is always enabled for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("camera", "http://foo.bar"), "Camera is not allowed for a random URL");
+    let allowed = ifr.contentDocument.policy.getAllowlistForFeature("camera");
+    is(allowed.length, 1, "Only 1 entry in allowlist for camera");
+    is(allowed[0], location.origin, "allowlist is self");
+
+    ok(ifr.contentDocument.policy.allowsFeature("geolocation"), "Geolocation is enabled for self");
+    ok(ifr.contentDocument.policy.allowsFeature("geolocation", location.origin), "Geolocation is enabled for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("geolocation", "http://foo.bar"), "Geolocation is not enabled for anything else");
+    allowed = ifr.contentDocument.policy.getAllowlistForFeature("geolocation");
+    is(allowed.length, 1, "Only 1 entry in allowlist for geolocation");
+    is(allowed[0], location.origin, "allowlist is self");
+
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone"), "Microphone is disabled for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone", location.origin), "Microphone is disabled for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://foo.bar"), "Microphone is disabled for foo.bar");
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://example.com"), "Microphone is enabled for example.com");
+    ok(!ifr.contentDocument.policy.allowsFeature("microphone", "http://example.org"), "Microphone is enabled for example.org");
+    allowed = ifr.contentDocument.policy.getAllowlistForFeature("microphone");
+    is(allowed.length, 0, "No allowlist for microphone");
+
+    ok(!ifr.contentDocument.policy.allowsFeature("vr"), "Vibrate is disabled for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("vr", location.origin), "Vibrate is disabled for self");
+    ok(!ifr.contentDocument.policy.allowsFeature("vr", "http://foo.bar"), "Vibrate is disabled for foo.bar");
+    allowed = ifr.contentDocument.policy.getAllowlistForFeature("vr");
+    is(allowed.length, 0, "No allowlist for vr");
+
+    ok(ifr.contentDocument.policy.allowedFeatures().includes("camera"), "Camera is allowed");
+    ok(ifr.contentDocument.policy.allowedFeatures().includes("geolocation"), "Geolocation is allowed");
+    // microphone is disabled for this origin
+    ok(!ifr.contentDocument.policy.allowedFeatures().includes("microphone"), "Microphone is not allowed");
+    // vr is disabled everywhere.
+    ok(!ifr.contentDocument.policy.allowedFeatures().includes("vr"), "VR is not allowed");
+
+    next();
+  };
+  document.body.appendChild(ifr);
+}
+
+var tests = [
+  test_document,
+  test_iframe_without_allow,
+  test_iframe_with_allow,
+  test_iframe_contentDocument,
+];
+
+function next() {
+  if (tests.length == 0) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+next();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/featurepolicy/test/mochitest/test_parser.html^headers^
@@ -0,0 +1,1 @@
+Feature-Policy: camera *; geolocation 'self'; microphone http://example.com http://example.org; vr 'none'
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -4,16 +4,18 @@
 # 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/.
 
 with Files('*'):
     BUG_COMPONENT = ('Core', 'DOM: Security')
 
 TEST_DIRS += ['test']
 
+DIRS += [ 'featurepolicy' ]
+
 EXPORTS.mozilla.dom += [
     'ContentVerifier.h',
     'CSPEvalChecker.h',
     'FramingChecker.h',
     'nsContentSecurityManager.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -7,16 +7,17 @@
  * https://html.spec.whatwg.org/multipage/dom.html#the-document-object
  * https://html.spec.whatwg.org/multipage/obsolete.html#other-elements%2C-attributes-and-apis
  * https://fullscreen.spec.whatwg.org/#api
  * https://w3c.github.io/pointerlock/#extensions-to-the-document-interface
  * https://w3c.github.io/pointerlock/#extensions-to-the-documentorshadowroot-mixin
  * https://w3c.github.io/page-visibility/#extensions-to-the-document-interface
  * https://drafts.csswg.org/cssom/#extensions-to-the-document-interface
  * https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface
+ * https://wicg.github.io/feature-policy/#policy
  */
 
 interface WindowProxy;
 interface nsISupports;
 interface URI;
 interface nsIDocShell;
 interface nsILoadGroup;
 
@@ -541,8 +542,14 @@ Document implements XPathEvaluator;
 Document implements GlobalEventHandlers;
 Document implements DocumentAndElementEventHandlers;
 Document implements TouchEventHandlers;
 Document implements ParentNode;
 Document implements OnErrorEventHandlerForNodes;
 Document implements GeometryUtils;
 Document implements FontFaceSource;
 Document implements DocumentOrShadowRoot;
+
+// https://wicg.github.io/feature-policy/#policy
+partial interface Document {
+    [SameObject, Pref="dom.security.featurePolicy.enabled"]
+    readonly attribute Policy policy;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/FeaturePolicy.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For more information on this interface, please see
+ * https://wicg.github.io/feature-policy/#policy
+ */
+
+[NoInterfaceObject]
+interface Policy {
+  boolean allowsFeature(DOMString feature, optional DOMString origin);
+  sequence<DOMString> allowedFeatures();
+  sequence<DOMString> getAllowlistForFeature(DOMString feature);
+};
--- a/dom/webidl/HTMLIFrameElement.webidl
+++ b/dom/webidl/HTMLIFrameElement.webidl
@@ -1,16 +1,18 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-iframe-element
  * http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
+ * https://wicg.github.io/feature-policy/#policy
+ *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 [HTMLConstructor]
 interface HTMLIFrameElement : HTMLElement {
   [CEReactions, SetterNeedsSubjectPrincipal=NonSystem, SetterThrows, Pure]
@@ -62,8 +64,17 @@ partial interface HTMLIFrameElement {
 partial interface HTMLIFrameElement {
   // nsIDOMMozBrowserFrame
   [ChromeOnly,SetterThrows]
            attribute boolean mozbrowser;
 };
 
 HTMLIFrameElement implements MozFrameLoaderOwner;
 HTMLIFrameElement implements BrowserElement;
+
+// https://wicg.github.io/feature-policy/#policy
+partial interface HTMLIFrameElement {
+  [SameObject, Pref="dom.security.featurePolicy.enabled"]
+  readonly attribute Policy policy;
+
+  [CEReactions, SetterThrows, Pure, Pref="dom.security.featurePolicy.enabled"]
+           attribute DOMString allow;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -95,16 +95,19 @@ with Files("DelayNode.webidl"):
     BUG_COMPONENT = ("Core", "Web Audio")
 
 with Files("DynamicsCompressorNode.webidl"):
     BUG_COMPONENT = ("Core", "Web Audio")
 
 with Files("FakePluginTagInit.webidl"):
     BUG_COMPONENT = ("Core", "Plug-ins")
 
+with Files("FeaturePolicy.webidl"):
+    BUG_COMPONENT = ("Core", "DOM: Security")
+
 with Files("Flex.webidl"):
     BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
 
 with Files("FocusEvent.webidl"):
     BUG_COMPONENT = ("Core", "DOM: Events")
 
 with Files("Font*"):
     BUG_COMPONENT = ("Core", "CSS Parsing and Computation")
@@ -492,16 +495,17 @@ WEBIDL_FILES = [
     'Event.webidl',
     'EventHandler.webidl',
     'EventListener.webidl',
     'EventSource.webidl',
     'EventTarget.webidl',
     'ExtendableEvent.webidl',
     'ExtendableMessageEvent.webidl',
     'FakePluginTagInit.webidl',
+    'FeaturePolicy.webidl',
     'Fetch.webidl',
     'FetchEvent.webidl',
     'FetchObserver.webidl',
     'File.webidl',
     'FileList.webidl',
     'FileMode.webidl',
     'FileReader.webidl',
     'FileReaderSync.webidl',
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -2065,18 +2065,25 @@ BacktrackingAllocator::resolveControlFlo
 
                 LAllocation* input = phi->getOperand(k);
                 LiveRange* from = vreg(input).rangeFor(exitOf(predecessor), /* preferRegister = */ true);
                 MOZ_ASSERT(from);
 
                 if (!alloc().ensureBallast()) {
                     return false;
                 }
-                if (!moveAtExit(predecessor, from, to, def->type())) {
-                    return false;
+                if (mSuccessor->numPredecessors() > 1) {
+                    MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1);
+                    if (!moveAtExit(predecessor, from, to, def->type())) {
+                        return false;
+                    }
+                } else {
+                    if (!moveAtEntry(successor, from, to, def->type())) {
+                        return false;
+                    }
                 }
             }
         }
     }
 
     // Add moves to resolve graph edges with different allocations at their
     // source and target.
     for (size_t i = 1; i < graph.numVirtualRegisters(); i++) {
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -812,16 +812,22 @@ IonBuilder::InliningResult
 IonBuilder::inlineArrayPush(CallInfo& callInfo)
 {
     const uint32_t inlineArgsLimit = 10;
     if (callInfo.argc() < 1 || callInfo.argc() > inlineArgsLimit || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
+    // XXX bug 1493903.
+    if (callInfo.argc() != 1) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
     for (uint32_t i = 0; i < callInfo.argc(); i++) {
         MDefinition* value = callInfo.getArg(i);
         if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                           &obj, nullptr, &value, /* canModify = */ false))
         {
             trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
             return InliningStatus_NotInlined;
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -2181,23 +2181,26 @@ CompareExchange(MacroAssembler& masm, co
         default:
             MOZ_CRASH();
     }
 
     Label again, end;
 
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
-    // FIXME: emit signal handling information if access != nullptr.
 
     if (nbytes == 4) {
 
         masm.memoryBarrierBefore(sync);
         masm.bind(&again);
 
+        if (access) {
+            masm.append(*access, masm.size());
+        }
+
         masm.as_ll(output, SecondScratchReg, 0);
         masm.ma_b(output, oldval, &end, Assembler::NotEqual, ShortJump);
         masm.ma_move(ScratchRegister, newval);
         masm.as_sc(ScratchRegister, SecondScratchReg, 0);
         masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero, ShortJump);
 
         masm.memoryBarrierAfter(sync);
         masm.bind(&end);
@@ -2214,16 +2217,20 @@ CompareExchange(MacroAssembler& masm, co
     masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
     masm.as_sllv(maskTemp, maskTemp, offsetTemp);
     masm.as_nor(maskTemp, zero, maskTemp);
 
     masm.memoryBarrierBefore(sync);
 
     masm.bind(&again);
 
+    if (access) {
+        masm.append(*access, masm.size());
+    }
+
     masm.as_ll(ScratchRegister, SecondScratchReg, 0);
 
     masm.as_srlv(output, ScratchRegister, offsetTemp);
 
     switch (nbytes) {
         case 1:
             if (signExtend) {
                 masm.ma_seb(valueTemp, oldval);
@@ -2320,23 +2327,25 @@ AtomicExchange(MacroAssembler& masm, con
         default:
             MOZ_CRASH();
     }
 
     Label again;
 
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
-    // FIXME: emit signal handling information if access != nullptr.
-
     if (nbytes == 4) {
 
         masm.memoryBarrierBefore(sync);
         masm.bind(&again);
 
+        if (access) {
+            masm.append(*access, masm.size());
+        }
+
         masm.as_ll(output, SecondScratchReg, 0);
         masm.ma_move(ScratchRegister, value);
         masm.as_sc(ScratchRegister, SecondScratchReg, 0);
         masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero, ShortJump);
 
         masm.memoryBarrierAfter(sync);
 
         return;
@@ -2360,16 +2369,20 @@ AtomicExchange(MacroAssembler& masm, con
             break;
     }
     masm.as_sllv(valueTemp, valueTemp, offsetTemp);
 
     masm.memoryBarrierBefore(sync);
 
     masm.bind(&again);
 
+    if (access) {
+        masm.append(*access, masm.size());
+    }
+
     masm.as_ll(output, SecondScratchReg, 0);
     masm.as_and(ScratchRegister, output, maskTemp);
     masm.as_or(ScratchRegister, ScratchRegister, valueTemp);
 
     masm.as_sc(ScratchRegister, SecondScratchReg, 0);
 
     masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::Zero, ShortJump);
 
@@ -2452,23 +2465,25 @@ AtomicFetchOp(MacroAssembler& masm, cons
         default:
             MOZ_CRASH();
     }
 
     Label again;
 
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
-    // FIXME: emit signal handling information if access != nullptr.
-
     if (nbytes == 4) {
 
         masm.memoryBarrierBefore(sync);
         masm.bind(&again);
 
+        if (access) {
+            masm.append(*access, masm.size());
+        }
+
         masm.as_ll(output, SecondScratchReg, 0);
 
         switch (op) {
         case AtomicFetchAddOp:
             masm.as_addu(ScratchRegister, output, value);
             break;
         case AtomicFetchSubOp:
             masm.as_subu(ScratchRegister, output, value);
@@ -2504,16 +2519,20 @@ AtomicFetchOp(MacroAssembler& masm, cons
     masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
     masm.as_sllv(maskTemp, maskTemp, offsetTemp);
     masm.as_nor(maskTemp, zero, maskTemp);
 
     masm.memoryBarrierBefore(sync);
 
     masm.bind(&again);
 
+    if (access) {
+        masm.append(*access, masm.size());
+    }
+
     masm.as_ll(ScratchRegister, SecondScratchReg, 0);
     masm.as_srlv(output, ScratchRegister, offsetTemp);
 
     switch (op) {
         case AtomicFetchAddOp:
             masm.as_addu(valueTemp, output, value);
             break;
         case AtomicFetchSubOp:
@@ -2624,23 +2643,25 @@ AtomicEffectOp(MacroAssembler& masm, con
         default:
             MOZ_CRASH();
     }
 
     Label again;
 
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
-    // FIXME: emit signal handling information if access != nullptr.
-
     if (nbytes == 4) {
 
         masm.memoryBarrierBefore(sync);
         masm.bind(&again);
 
+        if (access) {
+            masm.append(*access, masm.size());
+        }
+
         masm.as_ll(ScratchRegister, SecondScratchReg, 0);
 
         switch (op) {
         case AtomicFetchAddOp:
             masm.as_addu(ScratchRegister, ScratchRegister, value);
             break;
         case AtomicFetchSubOp:
             masm.as_subu(ScratchRegister, ScratchRegister, value);
@@ -2675,16 +2696,20 @@ AtomicEffectOp(MacroAssembler& masm, con
     masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8)));
     masm.as_sllv(maskTemp, maskTemp, offsetTemp);
     masm.as_nor(maskTemp, zero, maskTemp);
 
     masm.memoryBarrierBefore(sync);
 
     masm.bind(&again);
 
+    if (access) {
+        masm.append(*access, masm.size());
+    }
+
     masm.as_ll(ScratchRegister, SecondScratchReg, 0);
     masm.as_srlv(valueTemp, ScratchRegister, offsetTemp);
 
     switch (op) {
         case AtomicFetchAddOp:
             masm.as_addu(valueTemp, valueTemp, value);
             break;
         case AtomicFetchSubOp:
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -2688,19 +2688,22 @@ MacroAssemblerMIPSCompat::wasmStoreI64Im
                           SizeWord);
         asMasm().append(access, asMasm().size() - 4);
         asMasm().ma_store(value.low, address, SizeWord);
     }
     asMasm().memoryBarrierAfter(access.sync());
 }
 
 static void
-EnterAtomic64Region(MacroAssembler& masm, Register addr, Register spinlock, Register scratch)
+EnterAtomic64Region(MacroAssembler& masm, const wasm::MemoryAccessDesc& access, Register addr,
+                    Register spinlock, Register scratch)
 {
     masm.movePtr(wasm::SymbolicAddress::js_jit_gAtomic64Lock, spinlock);
+
+    masm.append(access, masm.size());
     masm.as_lbu(zero, addr, 7); // Force memory trap on invalid access before we enter the spinlock.
 
     Label tryLock;
 
     masm.memoryBarrier(MembarFull);
 
     masm.bind(&tryLock);
 
@@ -2725,51 +2728,47 @@ template <typename T>
 static void
 AtomicLoad64(MacroAssembler& masm, const wasm::MemoryAccessDesc& access, const T& mem,
              Register64 temp, Register64 output)
 {
     MOZ_ASSERT(temp.low == InvalidReg && temp.high == InvalidReg);
 
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
-    EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
+    EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
                         /* scratch= */ output.low);
 
-    // FIXME: Emit signal handling information.
-
     masm.load64(Address(SecondScratchReg, 0), output);
 
     ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
 }
 
 void
-MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, const Address& mem, Register64 temp,
-                                 Register64 output)
+MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, const Address& mem,
+                                 Register64 temp, Register64 output)
 {
     AtomicLoad64(*this, access, mem, temp, output);
 }
 
 void
-MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, const BaseIndex& mem, Register64 temp,
-                                 Register64 output)
+MacroAssembler::wasmAtomicLoad64(const wasm::MemoryAccessDesc& access, const BaseIndex& mem,
+                                 Register64 temp, Register64 output)
 {
     AtomicLoad64(*this, access, mem, temp, output);
 }
 
 template<typename T>
 void
 MacroAssemblerMIPSCompat::wasmAtomicStore64(const wasm::MemoryAccessDesc& access, const T& mem,
                                             Register temp, Register64 value)
 {
     computeEffectiveAddress(mem, SecondScratchReg);
 
-    EnterAtomic64Region(asMasm(), /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
-                        /* scratch= */ temp);
-
-    // FIXME: Emit signal handling information.
+    EnterAtomic64Region(asMasm(), access, /* addr= */ SecondScratchReg,
+                       /* spinlock= */ ScratchRegister, /* scratch= */ temp);
 
     store64(value, Address(SecondScratchReg, 0));
 
     ExitAtomic64Region(asMasm(), /* spinlock= */ ScratchRegister);
 }
 
 template void
 MacroAssemblerMIPSCompat::wasmAtomicStore64(const wasm::MemoryAccessDesc& access, const Address& mem,
@@ -2786,21 +2785,19 @@ WasmCompareExchange64(MacroAssembler& ma
     MOZ_ASSERT(output != expect);
     MOZ_ASSERT(output != replace);
 
     Label exit;
 
     masm.computeEffectiveAddress(mem, SecondScratchReg);
     Address addr(SecondScratchReg, 0);
 
-    EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
+    EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
                         /* scratch= */ output.low);
 
-    // FIXME: emit signal handling information
-
     masm.load64(addr, output);
 
     masm.ma_b(output.low, expect.low, &exit, Assembler::NotEqual, ShortJump);
     masm.ma_b(output.high, expect.high, &exit, Assembler::NotEqual, ShortJump);
     masm.store64(replace, addr);
     masm.bind(&exit);
     ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
 }
@@ -2824,53 +2821,49 @@ MacroAssembler::wasmCompareExchange64(co
 template <typename T>
 static void
 WasmAtomicExchange64(MacroAssembler& masm, const wasm::MemoryAccessDesc& access,
                      const T& mem, Register64 src, Register64 output)
 {
     masm.computeEffectiveAddress(mem, SecondScratchReg);
     Address addr(SecondScratchReg, 0);
 
-    EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
+    EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
                         /* scratch= */ output.low);
 
-    // FIXME: emit signal handling information
-
     masm.load64(addr, output);
     masm.store64(src, addr);
 
     ExitAtomic64Region(masm, /* spinlock= */ ScratchRegister);
 }
 
 void
-MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, const Address& mem, Register64 src,
-                                     Register64 output)
+MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, const Address& mem,
+                                     Register64 src, Register64 output)
 {
     WasmAtomicExchange64(*this, access, mem, src, output);
 }
 
 void
-MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, const BaseIndex& mem, Register64 src,
-                                     Register64 output)
+MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, const BaseIndex& mem,
+                                     Register64 src, Register64 output)
 {
     WasmAtomicExchange64(*this, access, mem, src, output);
 }
 
 template<typename T>
 static void
 AtomicFetchOp64(MacroAssembler& masm, const wasm::MemoryAccessDesc& access, AtomicOp op,
                 Register64 value, const T& mem, Register64 temp, Register64 output)
 {
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
-    EnterAtomic64Region(masm, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
+    EnterAtomic64Region(masm, access, /* addr= */ SecondScratchReg, /* spinlock= */ ScratchRegister,
                         /* scratch= */ output.low);
 
-    // FIXME: Emit signal handling information.
-
     masm.load64(Address(SecondScratchReg, 0), output);
 
     switch(op) {
       case AtomicFetchAddOp:
         masm.as_addu(temp.low, output.low, value.low);
         masm.as_sltu(temp.high, temp.low, output.low);
         masm.as_addu(temp.high, temp.high, output.high);
         masm.as_addu(temp.high, temp.high, value.high);
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -2575,19 +2575,19 @@ WasmCompareExchange64(MacroAssembler& ma
 
     Label tryAgain;
     Label exit;
 
     masm.memoryBarrierBefore(access.sync());
 
     masm.bind(&tryAgain);
 
-    // FIXME: emit signal handling information
-
+    masm.append(access, masm.size());
     masm.as_lld(output.reg, SecondScratchReg, 0);
+
     masm.ma_b(output.reg, expect.reg, &exit, Assembler::NotEqual, ShortJump);
     masm.movePtr(replace.reg, ScratchRegister);
     masm.as_scd(ScratchRegister, SecondScratchReg, 0);
     masm.ma_b(ScratchRegister, ScratchRegister, &tryAgain, Assembler::Zero, ShortJump);
 
     masm.memoryBarrierAfter(access.sync());
 
     masm.bind(&exit);
@@ -2615,19 +2615,19 @@ AtomicExchange64(MacroAssembler& masm, c
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
     Label tryAgain;
 
     masm.memoryBarrierBefore(access.sync());
 
     masm.bind(&tryAgain);
 
-    // FIXME: emit signal handling information
-
+    masm.append(access, masm.size());
     masm.as_lld(output.reg, SecondScratchReg, 0);
+
     masm.movePtr(src.reg, ScratchRegister);
     masm.as_scd(ScratchRegister, SecondScratchReg, 0);
     masm.ma_b(ScratchRegister, ScratchRegister, &tryAgain, Assembler::Zero, ShortJump);
 
     masm.memoryBarrierAfter(access.sync());
 }
 
 void
@@ -2652,18 +2652,17 @@ AtomicFetchOp64(MacroAssembler& masm, co
     masm.computeEffectiveAddress(mem, SecondScratchReg);
 
     Label tryAgain;
 
     masm.memoryBarrierBefore(access.sync());
 
     masm.bind(&tryAgain);
 
-    // FIXME: Emit signal handling information.
-
+    masm.append(access, masm.size());
     masm.as_lld(output.reg, SecondScratchReg, 0);
 
     switch(op) {
       case AtomicFetchAddOp:
         masm.as_daddu(temp.reg, output.reg, value.reg);
         break;
       case AtomicFetchSubOp:
         masm.as_dsubu(temp.reg, output.reg, value.reg);
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -165,44 +165,26 @@ nsImageRenderer::PrepareImage()
       mPrepareResult = ImgDrawResult::SUCCESS;
       break;
     }
     case eStyleImageType_Gradient:
       mGradientData = mImage->GetGradientData();
       mPrepareResult = ImgDrawResult::SUCCESS;
       break;
     case eStyleImageType_Element: {
-      nsAutoString elementId =
-        NS_LITERAL_STRING("#") + nsDependentAtomString(mImage->GetElementId());
-      nsCOMPtr<nsIURI> targetURI;
-      nsCOMPtr<nsIURI> base = mForFrame->GetContent()->GetBaseURI();
-      nsContentUtils::NewURIWithDocumentCharset(
-        getter_AddRefs(targetURI),
-        elementId,
-        mForFrame->GetContent()->GetUncomposedDoc(),
-        base);
-      RefPtr<URLAndReferrerInfo> url = new URLAndReferrerInfo(
-        targetURI,
-        mForFrame->GetContent()->OwnerDoc()->GetDocumentURI(),
-        mForFrame->GetContent()->OwnerDoc()->GetReferrerPolicy());
-
-      nsSVGPaintingProperty* property = SVGObserverUtils::GetPaintingPropertyForURI(
-          url, mForFrame->FirstContinuation(),
-          SVGObserverUtils::BackgroundImageProperty());
-      if (!property) {
-        mPrepareResult = ImgDrawResult::BAD_IMAGE;
-        return false;
-      }
-
+      Element* paintElement = // may be null
+        SVGObserverUtils::GetAndObserveBackgroundImage(
+          mForFrame->FirstContinuation(), mImage->GetElementId());
       // If the referenced element is an <img>, <canvas>, or <video> element,
       // prefer SurfaceFromElement as it's more reliable.
-      mImageElementSurface = nsLayoutUtils::SurfaceFromElement(
-                               property->GetAndObserveReferencedElement());
+      mImageElementSurface = nsLayoutUtils::SurfaceFromElement(paintElement);
+
       if (!mImageElementSurface.GetSourceSurface()) {
-        nsIFrame* paintServerFrame = property->GetAndObserveReferencedFrame();
+        nsIFrame* paintServerFrame =
+          paintElement ? paintElement->GetPrimaryFrame() : nullptr;
         // If there's no referenced frame, or the referenced frame is
         // non-displayable SVG, then we have nothing valid to paint.
         if (!paintServerFrame ||
             (paintServerFrame->IsFrameOfType(nsIFrame::eSVG) &&
              !paintServerFrame->IsFrameOfType(nsIFrame::eSVGPaintServer) &&
              !static_cast<nsSVGDisplayableFrame*>(
                do_QueryFrame(paintServerFrame)))) {
           mPrepareResult = ImgDrawResult::BAD_IMAGE;
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -615,16 +615,23 @@ GetEffectProperty(URLAndReferrerInfo* aU
   if (prop)
     return prop;
   prop = new T(aURI, aFrame, false);
   NS_ADDREF(prop);
   aFrame->SetProperty(aProperty, prop);
   return prop;
 }
 
+static nsSVGPaintingProperty*
+GetPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
+  const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty)
+{
+  return GetEffectProperty(aURI, aFrame, aProperty);
+}
+
 bool
 SVGObserverUtils::GetMarkerFrames(nsIFrame* aMarkedFrame,
                                   nsSVGMarkerFrame*(*aFrames)[3])
 {
   MOZ_ASSERT(!aMarkedFrame->GetPrevContinuation() &&
              aMarkedFrame->IsSVGGeometryFrame() &&
              static_cast<SVGGeometryElement*>(aMarkedFrame->GetContent())->IsMarkable(),
              "Bad frame");
@@ -748,18 +755,18 @@ GetOrCreateClipPathObserver(nsIFrame* aC
   MOZ_ASSERT(!aClippedFrame->GetPrevContinuation(), "Require first continuation");
 
   const nsStyleSVGReset* svgStyleReset = aClippedFrame->StyleSVGReset();
   if (svgStyleReset->mClipPath.GetType() != StyleShapeSourceType::URL) {
     return nullptr;
   }
   css::URLValue* url = svgStyleReset->mClipPath.GetURL();
   RefPtr<URLAndReferrerInfo> pathURI = ResolveURLUsingLocalRef(aClippedFrame, url);
-  return SVGObserverUtils::GetPaintingProperty(pathURI, aClippedFrame,
-                                         SVGObserverUtils::ClipPathProperty());
+  return GetPaintingProperty(pathURI, aClippedFrame,
+                             SVGObserverUtils::ClipPathProperty());
 }
 
 SVGObserverUtils::ReferenceState
 SVGObserverUtils::GetAndObserveClipPath(nsIFrame* aClippedFrame,
                                         nsSVGClipPathFrame** aClipPathFrame)
 {
   if (aClipPathFrame) {
     *aClipPathFrame = nullptr;
@@ -938,45 +945,50 @@ SVGObserverUtils::GetTemplateFrame(nsIFr
 }
 
 void
 SVGObserverUtils::RemoveTemplateObserver(nsIFrame* aFrame)
 {
   aFrame->DeleteProperty(SVGObserverUtils::HrefToTemplateProperty());
 }
 
-nsSVGPaintingProperty*
-SVGObserverUtils::GetPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
-  const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty)
+Element*
+SVGObserverUtils::GetAndObserveBackgroundImage(nsIFrame* aFrame,
+                                               const nsAtom* aHref)
 {
-  return GetEffectProperty(aURI, aFrame, aProperty);
-}
-
-nsSVGPaintingProperty*
-SVGObserverUtils::GetPaintingPropertyForURI(URLAndReferrerInfo* aURI,
-  nsIFrame* aFrame,
-  URIObserverHashtablePropertyDescriptor aProperty)
-{
-  if (!aURI)
-    return nullptr;
-
   SVGObserverUtils::URIObserverHashtable *hashtable =
-    aFrame->GetProperty(aProperty);
+    aFrame->GetProperty(BackgroundImageProperty());
   if (!hashtable) {
     hashtable = new SVGObserverUtils::URIObserverHashtable();
-    aFrame->SetProperty(aProperty, hashtable);
+    aFrame->SetProperty(BackgroundImageProperty(), hashtable);
   }
-  nsSVGPaintingProperty* prop =
-    static_cast<nsSVGPaintingProperty*>(hashtable->GetWeak(aURI));
-  if (!prop) {
-    bool watchImage = aProperty == SVGObserverUtils::BackgroundImageProperty();
-    prop = new nsSVGPaintingProperty(aURI, aFrame, watchImage);
-    hashtable->Put(aURI, prop);
+
+  nsAutoString elementId =
+    NS_LITERAL_STRING("#") + nsDependentAtomString(aHref);
+  nsCOMPtr<nsIURI> targetURI;
+  nsCOMPtr<nsIURI> base = aFrame->GetContent()->GetBaseURI();
+  nsContentUtils::NewURIWithDocumentCharset(
+    getter_AddRefs(targetURI),
+    elementId,
+    aFrame->GetContent()->GetUncomposedDoc(),
+    base);
+  RefPtr<URLAndReferrerInfo> url = new URLAndReferrerInfo(
+    targetURI,
+    aFrame->GetContent()->OwnerDoc()->GetDocumentURI(),
+    aFrame->GetContent()->OwnerDoc()->GetReferrerPolicy());
+
+  // XXXjwatt: this is broken - we're using the address of a new
+  // URLAndReferrerInfo as the hash key every time!
+  nsSVGPaintingProperty* observer =
+    static_cast<nsSVGPaintingProperty*>(hashtable->GetWeak(url));
+  if (!observer) {
+    observer = new nsSVGPaintingProperty(url, aFrame, /* aWatchImage */ true);
+    hashtable->Put(url, observer);
   }
-  return prop;
+  return observer->GetAndObserveReferencedElement();
 }
 
 nsSVGPaintServerFrame *
 SVGObserverUtils::GetPaintServer(nsIFrame* aTargetFrame,
                                  nsStyleSVGPaint nsStyleSVG::* aPaint)
 {
   // If we're looking at a frame within SVG text, then we need to look up
   // to find the right frame to get the painting property off.  We should at
@@ -997,17 +1009,17 @@ SVGObserverUtils::GetPaintServer(nsIFram
 
   RefPtr<URLAndReferrerInfo> paintServerURL =
     SVGObserverUtils::GetPaintURI(frame, aPaint);
   MOZ_ASSERT(aPaint == &nsStyleSVG::mFill || aPaint == &nsStyleSVG::mStroke);
   PaintingPropertyDescriptor propDesc = (aPaint == &nsStyleSVG::mFill) ?
                                         SVGObserverUtils::FillProperty() :
                                         SVGObserverUtils::StrokeProperty();
   nsSVGPaintingProperty *property =
-    SVGObserverUtils::GetPaintingProperty(paintServerURL, frame, propDesc);
+    GetPaintingProperty(paintServerURL, frame, propDesc);
   if (!property)
     return nullptr;
   nsIFrame* result = property->GetAndObserveReferencedFrame();
   if (!result)
     return nullptr;
 
   LayoutFrameType type = result->Type();
   if (type != LayoutFrameType::SVGLinearGradient &&
--- a/layout/svg/SVGObserverUtils.h
+++ b/layout/svg/SVGObserverUtils.h
@@ -800,30 +800,19 @@ public:
    * used as a callback to lazily get the href value, if necessary.
    */
   static nsIFrame*
   GetTemplateFrame(nsIFrame* aFrame, HrefToTemplateCallback aGetHref);
 
   static void
   RemoveTemplateObserver(nsIFrame* aFrame);
 
-  /**
-   * Get an nsSVGPaintingProperty for the frame, creating a fresh one if necessary
-   */
-  static nsSVGPaintingProperty*
-  GetPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
-      const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty);
-  /**
-   * Get an nsSVGPaintingProperty for the frame for that URI, creating a fresh
-   * one if necessary
-   */
-  static nsSVGPaintingProperty*
-  GetPaintingPropertyForURI(URLAndReferrerInfo* aURI,
-                            nsIFrame* aFrame,
-                            URIObserverHashtablePropertyDescriptor aProp);
+  static Element*
+  GetAndObserveBackgroundImage(nsIFrame* aFrame,
+                               const nsAtom* aHref);
 
   /**
    * A helper function to resolve marker's URL.
    */
   static already_AddRefed<URLAndReferrerInfo>
   GetMarkerURI(nsIFrame* aFrame,
                RefPtr<mozilla::css::URLValue> nsStyleSVG::* aMarker);
 
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -1715,12 +1715,22 @@ VARCACHE_PREF(
 
 VARCACHE_PREF(
   "devtools.enabled",
    devtools_enabled,
   RelaxedAtomicBool, false
 )
 
 //---------------------------------------------------------------------------
+// Feature-Policy prefs
+//---------------------------------------------------------------------------
+
+VARCACHE_PREF(
+  "dom.security.featurePolicy.enabled",
+   dom_security_featurePolicy_enabled,
+  bool, false
+)
+
+//---------------------------------------------------------------------------
 // End of prefs
 //---------------------------------------------------------------------------
 
 // clang-format on
--- a/testing/web-platform/meta/css/vendor-imports/mozilla/mozilla-central-reftests/images3/object-position-svg-002e.html.ini
+++ b/testing/web-platform/meta/css/vendor-imports/mozilla/mozilla-central-reftests/images3/object-position-svg-002e.html.ini
@@ -1,4 +1,2 @@
 [object-position-svg-002e.html]
-  expected:
-    if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): PASS
-    FAIL
+  expected: FAIL
--- a/testing/web-platform/meta/feature-policy/__dir__.ini
+++ b/testing/web-platform/meta/feature-policy/__dir__.ini
@@ -1,1 +1,2 @@
+prefs: [dom.security.featurePolicy.enabled:true]
 lsan-allowed: []
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-all.https.sub.html.ini
+++ /dev/null
@@ -1,127 +0,0 @@
-[feature-policy-frame-policy-allowed-for-all.https.sub.html]
-  [Test frame policy on same origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [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 *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [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 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same 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 = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" 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/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
@@ -1,139 +1,34 @@
 [feature-policy-frame-policy-allowed-for-self.https.sub.html]
-  [Test frame policy on same origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*".]
+  [Test frame policy on sandboxed iframe with no allow attribute.]
     expected: FAIL
 
   [Test frame policy on cross origin iframe with allow = "*".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
   [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
   [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
   [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
   [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
   [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 *;".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
   [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 'self';".]
     expected: FAIL
 
-  [Test frame policy on same 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 = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
   [Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" 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
 
-  [Test frame policy on sandboxed iframe with no allow attribute.]
-    expected: FAIL
-
-  [Test frame policy on sandboxed iframe with allow="fullscreen".]
-    expected: FAIL
-
-  [Test frame policy on sandboxed iframe with allow="fullscreen 'src'".]
-    expected: FAIL
-
-  [Test frame policy on sandboxed iframe with allow="fullscreen https://www.web-platform.test:8443".]
-    expected: FAIL
-
--- a/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.ini
@@ -1,190 +1,28 @@
 [feature-policy-frame-policy-allowed-for-some.https.sub.html]
-  [Test frame policy on same origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
   [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
   [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
   [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
+  [Test frame policy on another cross origin iframe with allow = "*" and allowfullscreen.]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
+  [Test frame policy on another cross origin iframe with allow = "*".]
     expected: FAIL
 
-  [Test frame policy on another cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [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 *;".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
+  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [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 'self';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
+  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
     expected: FAIL
 
-  [Test frame policy on same 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 = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on another 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 same origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
+  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
     expected: FAIL
 
   [Test frame policy on another cross origin iframe with allow = "*" and allowfullscreen.]
     expected: FAIL
 
-  [Test frame policy on same origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" 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
-
-  [Test frame policy on another cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and allowfullscreen.]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-disallowed-for-all.https.sub.html.ini
+++ /dev/null
@@ -1,127 +0,0 @@
-[feature-policy-frame-policy-disallowed-for-all.https.sub.html]
-  [Test frame policy on same origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe inherit from header policy.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [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 *;".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [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 'self';".]
-    expected: FAIL
-
-  [Test frame policy on same 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 = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'none'" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on same origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" 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
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-header-policy-allowed-for-all.https.sub.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[feature-policy-header-policy-allowed-for-all.https.sub.html]
-  [Feature-Policy: fullscreen *NaN]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen * -- test fullscreen is allowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen * -- test fullscreen is allowed on cross-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen *, iframe.allow = fullscreen 'self'; -- test fullscreen is allowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen *, iframe.allow = fullscreen 'self'; -- test fullscreen is disallowed on cross-origin subframe]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-header-policy-allowed-for-self.https.sub.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[feature-policy-header-policy-allowed-for-self.https.sub.html]
-  [Feature-Policy: fullscreen 'self' -- test allowlist is [same_origin\]]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' -- test fullscreen is allowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' -- test fullscreen is disallowed on cross-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self', iframe.allow = fullscreen 'src'; -- test fullscreen is allowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self', iframe.allow = fullscreen 'src'; -- test fullscreen is allowed on cross-origin subframe]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-header-policy-allowed-for-some.https.sub.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[feature-policy-header-policy-allowed-for-some.https.sub.html]
-  [Feature-Policy: fullscreen 'self' https://www.web-platform.test:8443 https://www.example.com; -- test allowlist is [same_origin, cross_origin, https://www.example.com\]]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' https://www.web-platform.test:8443 https://www.example.com; -- test fullscreen is allowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' https://www.web-platform.test:8443 https://www.example.com; -- test fullscreen is allowed on cross-origin https://www.web-platform.test:8443/feature-policy/resources/feature-policy-allowedfeatures.html subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' https://www.web-platform.test:8443 https://www.example.com; -- test fullscreen is disallowed on cross-origin https://www1.web-platform.test:8443/feature-policy/resources/feature-policy-allowedfeatures.html subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' https://www.web-platform.test:8443 https://www.example.com;, iframe.allow = fullscreen 'none'; -- test fullscreen is disallowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'self' https://www.web-platform.test:8443 https://www.example.com;iframe.allow = fullscreen 'none'; -- test fullscreen is disallowed on cross-origin subframe]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-header-policy-disallowed-for-all.https.sub.html.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[feature-policy-header-policy-disallowed-for-all.https.sub.html]
-  [Feature-Policy: fullscreen 'none' -- test allowlist is [\]]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'none' -- test fullscreen is disallowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'none' -- test fullscreen is disallowed on cross-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'none', iframe.allow = fullscreen 'src'; -- test fullscreen is disallowed on same-origin subframe]
-    expected: FAIL
-
-  [Feature-Policy: fullscreen 'none', iframe.allow = fullscreen 'src'; -- test fullscreen is disallowed on cross-origin subframe]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-nested-header-policy-allowed-for-all.https.sub.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[feature-policy-nested-header-policy-allowed-for-all.https.sub.html]
-  [Test nested header policy with local iframe on policy "fullscreen *"]
-    expected: FAIL
-
-  [Test nested header policy with local iframe on policy "fullscreen 'self'"]
-    expected: FAIL
-
-  [Test nested header policy with local iframe on policy "fullscreen 'none'"]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen *"]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen 'self'"]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen 'none'"]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-nested-header-policy-allowed-for-self.https.sub.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[feature-policy-nested-header-policy-allowed-for-self.https.sub.html]
-  [Test nested header policy with local iframe on policy "fullscreen *"]
-    expected: FAIL
-
-  [Test nested header policy with local iframe on policy "fullscreen 'self'"]
-    expected: FAIL
-
-  [Test nested header policy with local iframe on policy "fullscreen 'none'"]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen *"]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen 'self'"]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen 'none'"]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[feature-policy-nested-header-policy-disallowed-for-all.https.sub.html]
-  [Test nested header policy with local iframe on policy "fullscreen *".]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen *".]
-    expected: FAIL
-
-  [Test nested header policy with local iframe on policy "fullscreen 'self'".]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen 'self'".]
-    expected: FAIL
-
-  [Test nested header policy with local iframe on policy "fullscreen 'none'".]
-    expected: FAIL
-
-  [Test nested header policy with remote iframe on policy "fullscreen 'none'".]
-    expected: FAIL
-
--- a/testing/web-platform/meta/feature-policy/idlharness.window.js.ini
+++ b/testing/web-platform/meta/feature-policy/idlharness.window.js.ini
@@ -1,64 +1,37 @@
 [idlharness.window.html]
   [FeaturePolicyViolationReportBody interface: attribute columnNumber]
     expected: FAIL
 
-  [HTMLIFrameElement interface: attribute policy]
-    expected: FAIL
-
-  [Stringification of document.policy]
-    expected: FAIL
-
   [FeaturePolicyViolationReportBody interface: attribute sourceFile]
     expected: FAIL
 
-  [Policy interface: document.policy must inherit property "getAllowlistForFeature(DOMString)" with the proper type]
-    expected: FAIL
-
   [FeaturePolicyViolationReportBody interface object name]
     expected: FAIL
 
   [FeaturePolicyViolationReportBody interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
   [FeaturePolicyViolationReportBody interface object length]
     expected: FAIL
 
   [FeaturePolicyViolationReportBody interface: attribute featureId]
     expected: FAIL
 
   [FeaturePolicyViolationReportBody interface: existence and properties of interface prototype object's "constructor" property]
     expected: FAIL
 
-  [Policy interface: document.policy must inherit property "allowedFeatures()" with the proper type]
-    expected: FAIL
-
   [FeaturePolicyViolationReportBody interface: attribute lineNumber]
     expected: FAIL
 
   [FeaturePolicyViolationReportBody interface: existence and properties of interface prototype object]
     expected: FAIL
 
-  [Policy interface: calling allowsFeature(DOMString, DOMString) on document.policy with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Policy interface: document.policy must inherit property "allowsFeature(DOMString, DOMString)" with the proper type]
-    expected: FAIL
-
   [FeaturePolicyViolationReportBody interface: attribute disposition]
     expected: FAIL
 
   [FeaturePolicyViolationReportBody interface: existence and properties of interface object]
     expected: FAIL
 
-  [Document interface: document must inherit property "policy" with the proper type]
-    expected: FAIL
-
-  [Policy interface: calling getAllowlistForFeature(DOMString) on document.policy with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: attribute policy]
-    expected: FAIL
-
   [FeaturePolicyViolationReportBody interface: attribute message]
     expected: FAIL
 
--- a/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
@@ -9,17 +9,11 @@
     expected: FAIL
 
   [Feature-Policy allow="payment" allowpaymentrequest=true disallows cross-origin relocation.]
     expected: FAIL
 
   [Feature-Policy allow="payment" allows same-origin navigation in an iframe.]
     expected: FAIL
 
-  [Feature-Policy allow="payment" disallows cross-origin navigation in an iframe.]
-    expected: FAIL
-
   [Feature-Policy allow="payment" allowpaymentrequest=true allows same-origin navigation in an iframe.]
     expected: FAIL
 
-  [Feature-Policy allow="payment" allowpaymentrequest=true disallows cross-origin navigation in an iframe.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
@@ -1,10 +1,7 @@
 [picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
   [Feature-Policy allow="picture-in-picture" allows same-origin navigation in an iframe.]
     expected: FAIL
 
-  [Feature-Policy allow="picture-in-picture" disallows cross-origin navigation in an iframe.]
-    expected: FAIL
-
   [Untitled]
     expected: FAIL
 
deleted file mode 100644
--- a/testing/web-platform/meta/fetch/api/request/destination/fetch-destination.https.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[fetch-destination.https.html]
-  [HTMLLinkElement with rel=preload and as=image fetches with a "image" Request.destination]
-    expected:
-      if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/resource-timing/test_resource_timing.html.ini
+++ /dev/null
@@ -1,21 +0,0 @@
-[test_resource_timing.html]
-  [PerformanceEntry has correct protocol attribute (img)]
-    expected:
-      if debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PerformanceEntry has correct network transfer attributes (img)]
-    expected:
-      if debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [window.performance.getEntriesByName() and window.performance.getEntriesByNameType() return same data (img)]
-    expected:
-      if debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PerformanceEntry has correct order of timing attributes (img)]
-    expected:
-      if debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PerformanceEntry has correct name, initiatorType, startTime, and duration (img)]
-    expected:
-      if debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[update.https.html]
-  [Update a registration on ServiceWorkerGlobalScope]
-    expected:
-      if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -58,16 +58,17 @@ STATIC_ATOMS = [
     Atom("action", "action"),
     Atom("active", "active"),
     Atom("activateontab", "activateontab"),
     Atom("actuate", "actuate"),
     Atom("address", "address"),
     Atom("after", "after"),
     Atom("align", "align"),
     Atom("alink", "alink"),
+    Atom("allow", "allow"),
     Atom("allowdirs", "allowdirs"),
     Atom("allowevents", "allowevents"),
     Atom("allowforms", "allow-forms"),
     Atom("allowfullscreen", "allowfullscreen"),
     Atom("allowmodals", "allow-modals"),
     Atom("alloworientationlock", "allow-orientation-lock"),
     Atom("allowpaymentrequest", "allowpaymentrequest"),
     Atom("allowpointerlock", "allow-pointer-lock"),