From 2d974555d270050ef7c47cb2bda0fb60e79aed02 Mon Sep 17 00:00:00 2001 From: Christoph Kerschbaumer Date: Tue, 22 Oct 2019 08:53:47 +0000 Subject: [PATCH] Bug 1584993: Make CSP frame-ancestors work with fission enabled. r=jkt,farre,valentin Differential Revision: https://phabricator.services.mozilla.com/D49147 --HG-- extra : moz-landing-system : lando --- dom/base/Document.cpp | 15 -- .../security/nsIContentSecurityPolicy.idl | 8 +- dom/security/DOMSecurityManager.cpp | 202 ++++++++++++++++++ dom/security/DOMSecurityManager.h | 31 +++ dom/security/moz.build | 2 + dom/security/nsCSPContext.cpp | 87 +++----- dom/security/test/csp/mochitest.ini | 2 - .../test/csp/test_frameancestors.html | 10 +- .../csp/test_frameancestors_userpass.html | 11 +- ...ort_uri_missing_in_report_only_header.html | 9 + layout/build/nsLayoutStatics.cpp | 3 + .../content/SpecialPowersChild.jsm | 19 ++ .../content/SpecialPowersParent.jsm | 19 +- ...ncestors-from-serviceworker.https.html.ini | 3 +- 14 files changed, 332 insertions(+), 89 deletions(-) create mode 100644 dom/security/DOMSecurityManager.cpp create mode 100644 dom/security/DOMSecurityManager.h diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 24eb8b28eb1d..0142cdb79bf9 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -3286,21 +3286,6 @@ nsresult Document::InitCSP(nsIChannel* aChannel) { SetPrincipals(principal, principal); } - // ----- Enforce frame-ancestor policy on any applied policies - nsCOMPtr docShell(mDocumentContainer); - if (docShell) { - bool safeAncestry = false; - - // PermitsAncestry sends violation reports when necessary - rv = mCSP->PermitsAncestry(docShell, &safeAncestry); - - if (NS_FAILED(rv) || !safeAncestry) { - MOZ_LOG(gCspPRLog, LogLevel::Debug, - ("CSP doesn't like frame's ancestry, not loading.")); - // stop! ERROR page! - aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); - } - } ApplySettingsFromCSP(false); return NS_OK; } diff --git a/dom/interfaces/security/nsIContentSecurityPolicy.idl b/dom/interfaces/security/nsIContentSecurityPolicy.idl index 557b906219f4..98441832bff2 100644 --- a/dom/interfaces/security/nsIContentSecurityPolicy.idl +++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl @@ -6,8 +6,8 @@ #include "nsIContentPolicy.idl" interface nsIURI; -interface nsIDocShell; interface nsIEventTarget; +interface nsILoadInfo; interface nsIPrincipal; interface nsICSPEventListener; @@ -275,14 +275,14 @@ interface nsIContentSecurityPolicy : nsISerializable * NOTE: Calls to this may trigger violation reports when queried, so this * value should not be cached. * - * @param docShell - * containing the protected resource + * @param aLoadInfo + * The loadinfo of the channel containing the protected resource * @return * true if the frame's ancestors are all allowed by policy (except for * report-only policies, which will send reports and then return true * here when violated). */ - boolean permitsAncestry(in nsIDocShell docShell); + boolean permitsAncestry(in nsILoadInfo aLoadInfo); /** diff --git a/dom/security/DOMSecurityManager.cpp b/dom/security/DOMSecurityManager.cpp new file mode 100644 index 000000000000..ec28752289a5 --- /dev/null +++ b/dom/security/DOMSecurityManager.cpp @@ -0,0 +1,202 @@ +/* -*- 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 "DOMSecurityManager.h" +#include "nsCSPContext.h" +#include "mozilla/dom/WindowGlobalParent.h" + +#include "nsIMultiPartChannel.h" +#include "nsIObserverService.h" +#include "nsIHttpProtocolHandler.h" + +using namespace mozilla; + +namespace { +StaticRefPtr gDOMSecurityManager; +} // namespace + +static nsresult GetHttpChannelHelper(nsIChannel* aChannel, + nsIHttpChannel** aHttpChannel) { + nsCOMPtr httpChannel = do_QueryInterface(aChannel); + if (httpChannel) { + httpChannel.forget(aHttpChannel); + return NS_OK; + } + + nsCOMPtr multipart = do_QueryInterface(aChannel); + if (!multipart) { + *aHttpChannel = nullptr; + return NS_OK; + } + + nsCOMPtr baseChannel; + nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + httpChannel = do_QueryInterface(baseChannel); + httpChannel.forget(aHttpChannel); + + return NS_OK; +} + +NS_INTERFACE_MAP_BEGIN(DOMSecurityManager) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsIObserver) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(DOMSecurityManager) +NS_IMPL_RELEASE(DOMSecurityManager) + +/* static */ +void DOMSecurityManager::Initialize() { + MOZ_ASSERT(!gDOMSecurityManager); + + MOZ_ASSERT(NS_IsMainThread()); + + if (!XRE_IsParentProcess()) { + return; + } + + RefPtr service = new DOMSecurityManager(); + + nsCOMPtr obs = services::GetObserverService(); + if (NS_WARN_IF(!obs)) { + return; + } + + obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false); + obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + gDOMSecurityManager = service.forget(); +} + +/* static */ +void DOMSecurityManager::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + + if (!gDOMSecurityManager) { + return; + } + + RefPtr service = gDOMSecurityManager.forget(); + + nsCOMPtr obs = services::GetObserverService(); + if (NS_WARN_IF(!obs)) { + return; + } + + obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC); + obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID); +} + +NS_IMETHODIMP +DOMSecurityManager::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + Shutdown(); + return NS_OK; + } + + MOZ_ASSERT(!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC)); + + nsCOMPtr channel = do_QueryInterface(aSubject); + if (NS_WARN_IF(!channel)) { + return NS_OK; + } + + nsresult rv = ParseCSPAndEnforceFrameAncestorCheck(channel); + if (NS_FAILED(rv)) { + return rv; + } + + return NS_OK; +} + +nsresult DOMSecurityManager::ParseCSPAndEnforceFrameAncestorCheck( + nsIChannel* aChannel) { + MOZ_ASSERT(aChannel); + + // CSP can only hang off an http channel, if this channel is not + // an http channel then there is nothing to do here. + nsCOMPtr httpChannel; + nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!httpChannel) { + return NS_OK; + } + + nsCOMPtr loadInfo = aChannel->LoadInfo(); + nsContentPolicyType contentType = loadInfo->GetExternalContentPolicyType(); + // frame-ancestor check only makes sense for subdocument loads, if this is + // not a load of such type, there is nothing to do here. + if (contentType != nsIContentPolicy::TYPE_SUBDOCUMENT) { + return NS_OK; + } + + nsAutoCString tCspHeaderValue, tCspROHeaderValue; + + Unused << httpChannel->GetResponseHeader( + NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue); + + Unused << httpChannel->GetResponseHeader( + NS_LITERAL_CSTRING("content-security-policy-report-only"), + tCspROHeaderValue); + + // if there are no CSP values, then there is nothing to do here. + if (tCspHeaderValue.IsEmpty() && tCspROHeaderValue.IsEmpty()) { + return NS_OK; + } + + NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue); + NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue); + + RefPtr csp = new nsCSPContext(); + nsCOMPtr resultPrincipal; + rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( + aChannel, getter_AddRefs(resultPrincipal)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr selfURI; + aChannel->GetURI(getter_AddRefs(selfURI)); + + nsCOMPtr referrerInfo = httpChannel->GetReferrerInfo(); + nsAutoString referrerSpec; + referrerInfo->GetComputedReferrerSpec(referrerSpec); + uint64_t innerWindowID = loadInfo->GetInnerWindowID(); + + rv = csp->SetRequestContextWithPrincipal(resultPrincipal, selfURI, + referrerSpec, innerWindowID); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // ----- if there's a full-strength CSP header, apply it. + if (!cspHeaderValue.IsEmpty()) { + rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false); + NS_ENSURE_SUCCESS(rv, rv); + } + + // ----- if there's a report-only CSP header, apply it. + if (!cspROHeaderValue.IsEmpty()) { + rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true); + NS_ENSURE_SUCCESS(rv, rv); + } + + // ----- Enforce frame-ancestor policy on any applied policies + bool safeAncestry = false; + // PermitsAncestry sends violation reports when necessary + rv = csp->PermitsAncestry(loadInfo, &safeAncestry); + + if (NS_FAILED(rv) || !safeAncestry) { + // stop! ERROR page! + aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); + } + + return NS_OK; +} diff --git a/dom/security/DOMSecurityManager.h b/dom/security/DOMSecurityManager.h new file mode 100644 index 000000000000..a61155764640 --- /dev/null +++ b/dom/security/DOMSecurityManager.h @@ -0,0 +1,31 @@ +/* -*- 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_DOMSecurityManager_h +#define mozilla_dom_DOMSecurityManager_h + +#include "nsIObserver.h" + +class DOMSecurityManager final : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + static void Initialize(); + + private: + DOMSecurityManager() = default; + ~DOMSecurityManager() = default; + + // Only enforces the frame-anecstor check which needs to happen in + // the parent because we can only access the window global in the + // parent. The actual CSP gets parsed and applied in content. + nsresult ParseCSPAndEnforceFrameAncestorCheck(nsIChannel* aChannel); + + static void Shutdown(); +}; + +#endif /* mozilla_dom_DOMSecurityManager_h */ diff --git a/dom/security/moz.build b/dom/security/moz.build index 1ba57800f45a..802dc92442a9 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -13,6 +13,7 @@ DIRS += [ 'featurepolicy' ] EXPORTS.mozilla.dom += [ 'CSPEvalChecker.h', + 'DOMSecurityManager.h', 'FramingChecker.h', 'nsContentSecurityManager.h', 'nsContentSecurityUtils.h', @@ -36,6 +37,7 @@ EXPORTS += [ UNIFIED_SOURCES += [ 'CSPEvalChecker.cpp', + 'DOMSecurityManager.cpp', 'FramingChecker.cpp', 'nsContentSecurityManager.cpp', 'nsContentSecurityUtils.cpp', diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 5c586b06ced1..ce4ee7f01460 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -16,8 +16,6 @@ #include "nsError.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "nsIClassInfoImpl.h" -#include "nsIDocShell.h" -#include "nsIDocShellTreeItem.h" #include "mozilla/dom/Document.h" #include "nsIHttpChannel.h" #include "nsIInterfaceRequestor.h" @@ -1522,79 +1520,52 @@ nsresult nsCSPContext::AsyncReportViolation( } /** - * Based on the given docshell, determines if this CSP context allows the + * Based on the given loadinfo, determines if this CSP context allows the * ancestry. * * In order to determine the URI of the parent document (one causing the load - * of this protected document), this function obtains the docShellTreeItem, - * then walks up the hierarchy until it finds a privileged (chrome) tree item. - * Getting the a tree item's URI looks like this in pseudocode: - * - * nsIDocShellTreeItem->GetDocument()->GetDocumentURI(); - * - * aDocShell is the docShell for the protected document. + * of this protected document), this function traverses all Browsing Contexts + * until it reaches the top level browsing context. */ NS_IMETHODIMP -nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell, +nsCSPContext::PermitsAncestry(nsILoadInfo* aLoadInfo, bool* outPermitsAncestry) { - nsresult rv; + MOZ_ASSERT(XRE_IsParentProcess(), "frame-ancestor check only in parent"); - // Can't check ancestry without a docShell. - if (aDocShell == nullptr) { - return NS_ERROR_FAILURE; - } + nsresult rv; *outPermitsAncestry = true; + RefPtr ctx; + aLoadInfo->GetBrowsingContext(getter_AddRefs(ctx)); + // extract the ancestry as an array nsCOMArray ancestorsArray; - - nsCOMPtr ir(do_QueryInterface(aDocShell)); - nsCOMPtr treeItem(do_GetInterface(ir)); - nsCOMPtr parentTreeItem; - nsCOMPtr currentURI; nsCOMPtr uriClone; - // iterate through each docShell parent item - while (NS_SUCCEEDED( - treeItem->GetInProcessParent(getter_AddRefs(parentTreeItem))) && - parentTreeItem != nullptr) { - // stop when reaching chrome - if (parentTreeItem->ItemType() == nsIDocShellTreeItem::typeChrome) { - break; - } + while (ctx) { + WindowGlobalParent* window = ctx->Canonical()->GetCurrentWindowGlobal(); + if (window) { + nsCOMPtr currentURI = window->GetDocumentURI(); + if (currentURI) { + nsAutoCString spec; + currentURI->GetSpec(spec); + // delete the userpass from the URI. + rv = NS_MutateURI(currentURI) + .SetRef(EmptyCString()) + .SetUserPass(EmptyCString()) + .Finalize(uriClone); - Document* doc = parentTreeItem->GetDocument(); - NS_ASSERTION(doc, - "Could not get Document from nsIDocShellTreeItem in " - "nsCSPContext::PermitsAncestry"); - NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); - - currentURI = doc->GetDocumentURI(); - - if (currentURI) { - // delete the userpass from the URI. - rv = NS_MutateURI(currentURI) - .SetRef(EmptyCString()) - .SetUserPass(EmptyCString()) - .Finalize(uriClone); - - // If setUserPass fails for some reason, just return a clone of the - // current URI - if (NS_FAILED(rv)) { - rv = NS_GetURIWithoutRef(currentURI, getter_AddRefs(uriClone)); - NS_ENSURE_SUCCESS(rv, rv); + // If setUserPass fails for some reason, just return a clone of the + // current URI + if (NS_FAILED(rv)) { + rv = NS_GetURIWithoutRef(currentURI, getter_AddRefs(uriClone)); + NS_ENSURE_SUCCESS(rv, rv); + } + ancestorsArray.AppendElement(uriClone); } - - if (CSPCONTEXTLOGENABLED()) { - CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, found ancestor: %s", - uriClone->GetSpecOrDefault().get())); - } - ancestorsArray.AppendElement(uriClone); } - - // next ancestor - treeItem = parentTreeItem; + ctx = ctx->GetParent(); } nsAutoString violatedDirective; diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index 6ac4b71d70dc..2329e322e2d5 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -257,9 +257,7 @@ skip-if = !debug [test_evalscript_blocked_by_strict_dynamic.html] [test_evalscript_allowed_by_strict_dynamic.html] [test_frameancestors.html] -skip-if = fission [test_frameancestors_userpass.html] -skip-if = fission [test_inlinescript.html] [test_inlinestyle.html] [test_invalid_source_expression.html] diff --git a/dom/security/test/csp/test_frameancestors.html b/dom/security/test/csp/test_frameancestors.html index 927aed7f7d71..d12d5fb973e1 100644 --- a/dom/security/test/csp/test_frameancestors.html +++ b/dom/security/test/csp/test_frameancestors.html @@ -51,10 +51,14 @@ var framesThatShouldLoad = { // Number of tests that pass for this file should be 12 (8 violations 4 loads) var expectedViolationsLeft = 8; +// CSP frame-ancestor checks happen in the parent, hence we have to +// proxy the csp violation notifications. +SpecialPowers.registerObservers("csp-on-violate-policy"); + // This is used to watch the blocked data bounce off CSP and allowed data // get sent out to the wire. function examiner() { - SpecialPowers.addObserver(this, "csp-on-violate-policy"); + SpecialPowers.addObserver(this, "specialpowers-csp-on-violate-policy"); } examiner.prototype = { observe(subject, topic, data) { @@ -81,7 +85,7 @@ examiner.prototype = { } - if (topic === "csp-on-violate-policy") { + if (topic === "specialpowers-csp-on-violate-policy") { //these were blocked... record that they were blocked window.frameBlocked(asciiSpec, data); } @@ -90,7 +94,7 @@ examiner.prototype = { // must eventually call this to remove the listener, // or mochitests might get borked. remove() { - SpecialPowers.removeObserver(this, "csp-on-violate-policy"); + SpecialPowers.removeObserver(this, "specialpowers-csp-on-violate-policy"); } } diff --git a/dom/security/test/csp/test_frameancestors_userpass.html b/dom/security/test/csp/test_frameancestors_userpass.html index 0e055a3141f8..383262570745 100644 --- a/dom/security/test/csp/test_frameancestors_userpass.html +++ b/dom/security/test/csp/test_frameancestors_userpass.html @@ -22,10 +22,14 @@ var framesThatShouldLoad = { // Number of tests that pass for this file should be 1 var expectedViolationsLeft = 1; +// CSP frame-ancestor checks happen in the parent, hence we have to +// proxy the csp violation notifications. +SpecialPowers.registerObservers("csp-on-violate-policy"); + // This is used to watch the blocked data bounce off CSP and allowed data // get sent out to the wire. function examiner() { - SpecialPowers.addObserver(this, "csp-on-violate-policy"); + SpecialPowers.addObserver(this, "specialpowers-csp-on-violate-policy"); } examiner.prototype = { observe(subject, topic, data) { @@ -51,8 +55,7 @@ examiner.prototype = { // was not an nsIURI, so it was probably a cross-origin report. } - - if (topic === "csp-on-violate-policy") { + if (topic === "specialpowers-csp-on-violate-policy") { //these were blocked... record that they were blocked window.frameBlocked(asciiSpec, data); } @@ -61,7 +64,7 @@ examiner.prototype = { // must eventually call this to remove the listener, // or mochitests might get borked. remove() { - SpecialPowers.removeObserver(this, "csp-on-violate-policy"); + SpecialPowers.removeObserver(this, "specialpowers-csp-on-violate-policy"); } } diff --git a/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html b/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html index 7002eb7a8e33..9deb9ea85e83 100644 --- a/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html +++ b/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html @@ -21,7 +21,16 @@ var stringBundleService = SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"] .getService(SpecialPowers.Ci.nsIStringBundleService); var localizer = stringBundleService.createBundle("chrome://global/locale/security/csp.properties"); var warningMsg = localizer.formatStringFromName("reportURInotInReportOnlyHeader", [window.location.origin]); + +// Since Bug 1584993 we parse the CSP in the parent too, hence the +// same error message appears twice in the console. +var callCleanUpOnce = false; + function cleanup() { + if (callCleanUpOnce) { + return; + } + callCleanUpOnce = true; SpecialPowers.postConsoleSentinel(); SimpleTest.finish(); } diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index 8478bb7ec847..d4be531c303b 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -109,6 +109,7 @@ #include "DecoderDoctorLogger.h" #include "MediaDecoder.h" #include "mozilla/ClearSiteData.h" +#include "mozilla/dom/DOMSecurityManager.h" #include "mozilla/EditorController.h" #include "mozilla/Fuzzyfox.h" #include "mozilla/HTMLEditorController.h" @@ -308,6 +309,8 @@ nsresult nsLayoutStatics::Initialize() { ClearSiteData::Initialize(); + DOMSecurityManager::Initialize(); + // Reporting API. ReportingHeader::Initialize(); diff --git a/testing/specialpowers/content/SpecialPowersChild.jsm b/testing/specialpowers/content/SpecialPowersChild.jsm index 425f983c0932..e3e5a48d7901 100644 --- a/testing/specialpowers/content/SpecialPowersChild.jsm +++ b/testing/specialpowers/content/SpecialPowersChild.jsm @@ -2255,6 +2255,25 @@ SpecialPowersChild.prototype._proxiedObservers = { "specialpowers-service-worker-shutdown": function(aMessage) { Services.obs.notifyObservers(null, "specialpowers-service-worker-shutdown"); }, + + "specialpowers-csp-on-violate-policy": function(aMessage) { + let subject = null; + + try { + subject = Services.io.newURI(aMessage.data.subject); + } catch (ex) { + // if it's not a valid URI it must be an nsISupportsCString + subject = Cc["@mozilla.org/supports-cstring;1"].createInstance( + Ci.nsISupportsCString + ); + subject.data = aMessage.data.subject; + } + Services.obs.notifyObservers( + subject, + "specialpowers-csp-on-violate-policy", + aMessage.data.data + ); + }, }; SpecialPowersChild.prototype.permissionObserverProxy = { diff --git a/testing/specialpowers/content/SpecialPowersParent.jsm b/testing/specialpowers/content/SpecialPowersParent.jsm index e36c2bd63eef..1011f5a493ff 100644 --- a/testing/specialpowers/content/SpecialPowersParent.jsm +++ b/testing/specialpowers/content/SpecialPowersParent.jsm @@ -160,7 +160,24 @@ class SpecialPowersParent extends JSWindowActorParent { }, type: permission.type, }; - // fall through + this._self.sendAsyncMessage("specialpowers-" + aTopic, msg); + return; + case "csp-on-violate-policy": + // the subject is either an nsIURI or an nsISupportsCString + let subject = null; + if (aSubject instanceof Ci.nsIURI) { + subject = aSubject.asciiSpec; + } else if (aSubject instanceof Ci.nsISupportsCString) { + subject = aSubject.data; + } else { + throw new Error("Subject must be nsIURI or nsISupportsCString"); + } + msg = { + subject, + data: aData, + }; + this._self.sendAsyncMessage("specialpowers-" + aTopic, msg); + return; default: this._self.sendAsyncMessage("specialpowers-" + aTopic, msg); } diff --git a/testing/web-platform/meta/content-security-policy/frame-ancestors/frame-ancestors-from-serviceworker.https.html.ini b/testing/web-platform/meta/content-security-policy/frame-ancestors/frame-ancestors-from-serviceworker.https.html.ini index 1e54c955d70d..d05c8697b8ae 100644 --- a/testing/web-platform/meta/content-security-policy/frame-ancestors/frame-ancestors-from-serviceworker.https.html.ini +++ b/testing/web-platform/meta/content-security-policy/frame-ancestors/frame-ancestors-from-serviceworker.https.html.ini @@ -1,5 +1,4 @@ [frame-ancestors-from-serviceworker.https.html] - expected: TIMEOUT [A 'frame-ancestors' CSP directive set from a serviceworker response with a value 'none' should block rendering.] - expected: TIMEOUT + expected: FAIL