summaryrefslogtreecommitdiffstats
path: root/dom/security
diff options
context:
space:
mode:
Diffstat (limited to 'dom/security')
-rw-r--r--dom/security/ReferrerInfo.cpp6
-rw-r--r--dom/security/ReferrerInfo.h2
-rw-r--r--dom/security/fuzztest/csp_fuzzer.cpp2
-rw-r--r--dom/security/nsCSPContext.cpp14
-rw-r--r--dom/security/nsCSPContext.h2
-rw-r--r--dom/security/nsCSPParser.cpp70
-rw-r--r--dom/security/nsCSPParser.h4
-rw-r--r--dom/security/nsContentSecurityUtils.cpp51
-rw-r--r--dom/security/nsHTTPSOnlyUtils.cpp35
-rw-r--r--dom/security/nsHTTPSOnlyUtils.h12
-rw-r--r--dom/security/test/general/browser.toml10
-rw-r--r--dom/security/test/general/browser_test_http_download.js275
-rw-r--r--dom/security/test/general/file_block_script_wrong_mime_sw.js51
-rw-r--r--dom/security/test/general/http_download_page.html23
-rw-r--r--dom/security/test/general/http_download_server.sjs20
-rw-r--r--dom/security/test/general/mochitest.toml1
-rw-r--r--dom/security/test/general/test_block_script_wrong_mime.html105
-rw-r--r--dom/security/test/gtest/TestCSPParser.cpp3
-rw-r--r--dom/security/test/https-first/browser.toml8
-rw-r--r--dom/security/test/https-first/browser_beforeunload_permit_http.js2
-rw-r--r--dom/security/test/https-first/browser_subdocument_downgrade.js60
-rw-r--r--dom/security/test/https-first/file_empty.html1
-rw-r--r--dom/security/test/https-first/file_mixed_content_auto_upgrade.html2
-rw-r--r--dom/security/test/https-first/file_multiple_redirection.sjs18
-rw-r--r--dom/security/test/https-first/file_subdocument_downgrade.sjs8
-rw-r--r--dom/security/test/https-first/test.ogvbin2344665 -> 0 bytes
-rw-r--r--dom/security/test/https-first/test.webmbin0 -> 97465 bytes
-rw-r--r--dom/security/test/https-first/test_multiple_redirection.html8
-rw-r--r--dom/security/test/https-only/browser_save_as.js10
-rw-r--r--dom/security/test/mixedcontentblocker/browser.toml2
-rw-r--r--dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js6
-rw-r--r--dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js2
-rw-r--r--dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html2
-rw-r--r--dom/security/test/mixedcontentblocker/file_server.sjs4
-rw-r--r--dom/security/test/mixedcontentblocker/mochitest.toml4
-rw-r--r--dom/security/test/mixedcontentblocker/test.ogvbin2344665 -> 0 bytes
-rw-r--r--dom/security/test/mixedcontentblocker/test.webmbin0 -> 97465 bytes
-rw-r--r--dom/security/test/referrer-policy/browser.toml7
-rw-r--r--dom/security/test/referrer-policy/browser_referrer_disallow_cross_site_relaxing.js2
-rw-r--r--dom/security/trusted-types/TrustedTypePolicy.cpp94
-rw-r--r--dom/security/trusted-types/TrustedTypePolicy.h49
-rw-r--r--dom/security/trusted-types/TrustedTypePolicyFactory.cpp48
-rw-r--r--dom/security/trusted-types/TrustedTypePolicyFactory.h21
-rw-r--r--dom/security/trusted-types/TrustedTypeUtils.h12
44 files changed, 561 insertions, 495 deletions
diff --git a/dom/security/ReferrerInfo.cpp b/dom/security/ReferrerInfo.cpp
index 565d4d3284..856aa90f12 100644
--- a/dom/security/ReferrerInfo.cpp
+++ b/dom/security/ReferrerInfo.cpp
@@ -1078,11 +1078,9 @@ ReferrerInfo::Equals(nsIReferrerInfo* aOther, bool* aResult) {
}
NS_IMETHODIMP
-ReferrerInfo::GetComputedReferrerSpec(nsAString& aComputedReferrerSpec) {
+ReferrerInfo::GetComputedReferrerSpec(nsACString& aComputedReferrerSpec) {
aComputedReferrerSpec.Assign(
- mComputedReferrer.isSome()
- ? NS_ConvertUTF8toUTF16(mComputedReferrer.value())
- : EmptyString());
+ mComputedReferrer.isSome() ? mComputedReferrer.value() : EmptyCString());
return NS_OK;
}
diff --git a/dom/security/ReferrerInfo.h b/dom/security/ReferrerInfo.h
index b62afbb934..bfe254c1a9 100644
--- a/dom/security/ReferrerInfo.h
+++ b/dom/security/ReferrerInfo.h
@@ -14,7 +14,7 @@
#include "mozilla/HashFunctions.h"
#include "mozilla/dom/ReferrerPolicyBinding.h"
-#define REFERRERINFOF_CONTRACTID "@mozilla.org/referrer-info;1"
+#define REFERRERINFO_CONTRACTID "@mozilla.org/referrer-info;1"
// 041a129f-10ce-4bda-a60d-e027a26d5ed0
#define REFERRERINFO_CID \
{ \
diff --git a/dom/security/fuzztest/csp_fuzzer.cpp b/dom/security/fuzztest/csp_fuzzer.cpp
index 24f938cb1f..14e2ca05ca 100644
--- a/dom/security/fuzztest/csp_fuzzer.cpp
+++ b/dom/security/fuzztest/csp_fuzzer.cpp
@@ -27,7 +27,7 @@ static int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (ret != NS_OK) return 0;
ret =
- csp->SetRequestContextWithPrincipal(selfURIPrincipal, selfURI, u""_ns, 0);
+ csp->SetRequestContextWithPrincipal(selfURIPrincipal, selfURI, ""_ns, 0);
if (ret != NS_OK) return 0;
NS_ConvertASCIItoUTF16 policy(reinterpret_cast<const char*>(data), size);
diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp
index ed44304484..de67e2bf1c 100644
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -450,15 +450,14 @@ nsCSPContext::AppendPolicy(const nsAString& aPolicyString, bool aReportOnly,
if (policy) {
if (policy->hasDirective(
nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
- nsAutoCString selfURIspec, referrer;
+ nsAutoCString selfURIspec;
if (mSelfURI) {
mSelfURI->GetAsciiSpec(selfURIspec);
}
- CopyUTF16toUTF8(mReferrer, referrer);
CSPCONTEXTLOG(
("nsCSPContext::AppendPolicy added UPGRADE_IF_INSECURE_DIRECTIVE "
"self-uri=%s referrer=%s",
- selfURIspec.get(), referrer.get()));
+ selfURIspec.get(), mReferrer.get()));
}
mPolicies.AppendElement(policy);
@@ -787,7 +786,7 @@ nsCSPContext::SetRequestContextWithDocument(Document* aDocument) {
NS_IMETHODIMP
nsCSPContext::SetRequestContextWithPrincipal(nsIPrincipal* aRequestPrincipal,
nsIURI* aSelfURI,
- const nsAString& aReferrer,
+ const nsACString& aReferrer,
uint64_t aInnerWindowId) {
NS_ENSURE_ARG(aRequestPrincipal);
@@ -812,9 +811,8 @@ nsIPrincipal* nsCSPContext::GetRequestPrincipal() { return mLoadingPrincipal; }
nsIURI* nsCSPContext::GetSelfURI() { return mSelfURI; }
NS_IMETHODIMP
-nsCSPContext::GetReferrer(nsAString& outReferrer) {
- outReferrer.Truncate();
- outReferrer.Append(mReferrer);
+nsCSPContext::GetReferrer(nsACString& outReferrer) {
+ outReferrer.Assign(mReferrer);
return NS_OK;
}
@@ -987,7 +985,7 @@ nsresult nsCSPContext::GatherSecurityPolicyViolationEventData(
CopyUTF8toUTF16(reportDocumentURI, aViolationEventInit.mDocumentURI);
// referrer
- aViolationEventInit.mReferrer = mReferrer;
+ CopyUTF8toUTF16(mReferrer, aViolationEventInit.mReferrer);
// blocked-uri
if (aBlockedURI) {
diff --git a/dom/security/nsCSPContext.h b/dom/security/nsCSPContext.h
index ae47e34cb3..e4fe5af315 100644
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -175,7 +175,7 @@ class nsCSPContext : public nsIContentSecurityPolicy {
uint32_t aViolatedPolicyIndex,
uint32_t aLineNumber, uint32_t aColumnNumber);
- nsString mReferrer;
+ nsCString mReferrer;
uint64_t mInnerWindowID; // used for web console logging
bool mSkipAllowInlineStyleCheck; // used to allow Devtools to edit styles
// When deserializing an nsCSPContext instance, we initially just keep the
diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp
index 2559367831..07812470a3 100644
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -936,14 +936,6 @@ nsCSPDirective* nsCSPParser::directiveName() {
// directive = *WSP [ directive-name [ WSP directive-value ] ]
void nsCSPParser::directive() {
- // Set the directiveName to mCurToken
- // Remember, the directive name is stored at index 0
- mCurToken = mCurDir[0];
-
- CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
- NS_ConvertUTF16toUTF8(mCurToken).get(),
- NS_ConvertUTF16toUTF8(mCurValue).get()));
-
// Make sure that the directive-srcs-array contains at least
// one directive.
if (mCurDir.Length() == 0) {
@@ -953,6 +945,14 @@ void nsCSPParser::directive() {
return;
}
+ // Set the directiveName to mCurToken
+ // Remember, the directive name is stored at index 0
+ mCurToken = mCurDir[0];
+
+ CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
+ NS_ConvertUTF16toUTF8(mCurToken).get(),
+ NS_ConvertUTF16toUTF8(mCurValue).get()));
+
if (CSP_IsEmptyDirective(mCurValue, mCurToken)) {
return;
}
@@ -1029,20 +1029,32 @@ void nsCSPParser::directive() {
srcs.InsertElementAt(0, keyword);
}
+ MaybeWarnAboutIgnoredSources(srcs);
+ MaybeWarnAboutUnsafeInline(*cspDir);
+ MaybeWarnAboutUnsafeEval(*cspDir);
+
+ // Add the newly created srcs to the directive and add the directive to the
+ // policy
+ cspDir->addSrcs(srcs);
+ mPolicy->addDirective(cspDir);
+}
+
+void nsCSPParser::MaybeWarnAboutIgnoredSources(
+ const nsTArray<nsCSPBaseSrc*>& aSrcs) {
// If policy contains 'strict-dynamic' warn about ignored sources.
if (mStrictDynamic &&
!CSP_IsDirective(mCurDir[0],
nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE)) {
- for (uint32_t i = 0; i < srcs.Length(); i++) {
+ for (uint32_t i = 0; i < aSrcs.Length(); i++) {
nsAutoString srcStr;
- srcs[i]->toString(srcStr);
+ aSrcs[i]->toString(srcStr);
// Hashes and nonces continue to apply with 'strict-dynamic', as well as
// 'unsafe-eval', 'wasm-unsafe-eval' and 'unsafe-hashes'.
- if (!srcs[i]->isKeyword(CSP_STRICT_DYNAMIC) &&
- !srcs[i]->isKeyword(CSP_UNSAFE_EVAL) &&
- !srcs[i]->isKeyword(CSP_WASM_UNSAFE_EVAL) &&
- !srcs[i]->isKeyword(CSP_UNSAFE_HASHES) && !srcs[i]->isNonce() &&
- !srcs[i]->isHash()) {
+ if (!aSrcs[i]->isKeyword(CSP_STRICT_DYNAMIC) &&
+ !aSrcs[i]->isKeyword(CSP_UNSAFE_EVAL) &&
+ !aSrcs[i]->isKeyword(CSP_WASM_UNSAFE_EVAL) &&
+ !aSrcs[i]->isKeyword(CSP_UNSAFE_HASHES) && !aSrcs[i]->isNonce() &&
+ !aSrcs[i]->isHash()) {
AutoTArray<nsString, 2> params = {srcStr, mCurDir[0]};
logWarningErrorToConsole(nsIScriptError::warningFlag,
"ignoringScriptSrcForStrictDynamic", params);
@@ -1057,37 +1069,37 @@ void nsCSPParser::directive() {
"strictDynamicButNoHashOrNonce", params);
}
}
+}
+void nsCSPParser::MaybeWarnAboutUnsafeInline(const nsCSPDirective& aDirective) {
// From https://w3c.github.io/webappsec-csp/#allow-all-inline
// follows that when either a hash or nonce is specified, 'unsafe-inline'
// should not apply.
if (mHasHashOrNonce && mUnsafeInlineKeywordSrc &&
- (cspDir->isDefaultDirective() ||
- cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
- cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
- cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) ||
- cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) ||
- cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) ||
- cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE))) {
+ (aDirective.isDefaultDirective() ||
+ aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
+ aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
+ aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) ||
+ aDirective.equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) ||
+ aDirective.equals(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) ||
+ aDirective.equals(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE))) {
// Log to the console that unsafe-inline will be ignored.
AutoTArray<nsString, 2> params = {u"'unsafe-inline'"_ns, mCurDir[0]};
logWarningErrorToConsole(nsIScriptError::warningFlag,
"ignoringSrcWithinNonceOrHashDirective", params);
}
+}
+void nsCSPParser::MaybeWarnAboutUnsafeEval(const nsCSPDirective& aDirective) {
if (mHasAnyUnsafeEval &&
- (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
- cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE))) {
+ (aDirective.equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
+ aDirective.equals(
+ nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE))) {
// Log to the console that (wasm-)unsafe-eval will be ignored.
AutoTArray<nsString, 1> params = {mCurDir[0]};
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnsafeEval",
params);
}
-
- // Add the newly created srcs to the directive and add the directive to the
- // policy
- cspDir->addSrcs(srcs);
- mPolicy->addDirective(cspDir);
}
// policy = [ directive *( ";" [ directive ] ) ]
diff --git a/dom/security/nsCSPParser.h b/dom/security/nsCSPParser.h
index 21679d86a0..28c24440d0 100644
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -139,6 +139,10 @@ class nsCSPParser {
void logWarningErrorToConsole(uint32_t aSeverityFlag, const char* aProperty,
const nsTArray<nsString>& aParams);
+ void MaybeWarnAboutIgnoredSources(const nsTArray<nsCSPBaseSrc*>& aSrcs);
+ void MaybeWarnAboutUnsafeInline(const nsCSPDirective& aDirective);
+ void MaybeWarnAboutUnsafeEval(const nsCSPDirective& aDirective);
+
/**
* When parsing the policy, the parser internally uses the following helper
* variables/members which are used/reset during parsing. The following
diff --git a/dom/security/nsContentSecurityUtils.cpp b/dom/security/nsContentSecurityUtils.cpp
index 01e9c6d5db..7bcbbdd002 100644
--- a/dom/security/nsContentSecurityUtils.cpp
+++ b/dom/security/nsContentSecurityUtils.cpp
@@ -1065,7 +1065,7 @@ nsresult CheckCSPFrameAncestorPolicy(nsIChannel* aChannel,
csp->SuppressParserLogMessages();
nsCOMPtr<nsIURI> selfURI;
- nsAutoString referrerSpec;
+ nsAutoCString referrerSpec;
if (httpChannel) {
aChannel->GetURI(getter_AddRefs(selfURI));
nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
@@ -1367,6 +1367,7 @@ void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) {
StringBeginsWith(aboutSpec, "about:preferences"_ns) ||
StringBeginsWith(aboutSpec, "about:settings"_ns) ||
StringBeginsWith(aboutSpec, "about:downloads"_ns) ||
+ StringBeginsWith(aboutSpec, "about:fingerprinting"_ns) ||
StringBeginsWith(aboutSpec, "about:asrouter"_ns) ||
StringBeginsWith(aboutSpec, "about:newtab"_ns) ||
StringBeginsWith(aboutSpec, "about:logins"_ns) ||
@@ -1670,25 +1671,37 @@ long nsContentSecurityUtils::ClassifyDownload(
nsCOMPtr<nsIURI> contentLocation;
aChannel->GetURI(getter_AddRefs(contentLocation));
- if (StaticPrefs::dom_block_download_insecure()) {
- // If we are not dealing with a potentially trustworthy origin, or a URI
- // that is safe to be loaded like e.g. data:, then we block the load.
- bool isInsecureDownload =
- !nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(
- contentLocation) &&
- !nsMixedContentBlocker::URISafeToBeLoadedInSecureContext(
- contentLocation);
-
- Telemetry::Accumulate(mozilla::Telemetry::INSECURE_DOWNLOADS,
- isInsecureDownload);
-
- if (isInsecureDownload) {
- nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
- if (httpChannel) {
- LogMessageToConsole(httpChannel, "BlockedInsecureDownload");
- }
- return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE;
+ nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();
+ if (!loadingPrincipal) {
+ loadingPrincipal = loadInfo->TriggeringPrincipal();
+ }
+ // Creating a fake Loadinfo that is just used for the MCB check.
+ nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new mozilla::net::LoadInfo(
+ loadingPrincipal, loadInfo->TriggeringPrincipal(), nullptr,
+ nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
+ nsIContentPolicy::TYPE_FETCH);
+ // Disable HTTPS-Only checks for that loadinfo. This is required because
+ // otherwise nsMixedContentBlocker::ShouldLoad would assume that the request
+ // is safe, because HTTPS-Only is handling it.
+ secCheckLoadInfo->SetHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_EXEMPT);
+
+ int16_t decission = nsIContentPolicy::ACCEPT;
+ nsMixedContentBlocker::ShouldLoad(false, // aHadInsecureImageRedirect
+ contentLocation, // aContentLocation,
+ secCheckLoadInfo, // aLoadinfo
+ false, // aReportError
+ &decission // aDecision
+ );
+ Telemetry::Accumulate(mozilla::Telemetry::MIXED_CONTENT_DOWNLOADS,
+ decission != nsIContentPolicy::ACCEPT);
+
+ if (StaticPrefs::dom_block_download_insecure() &&
+ decission != nsIContentPolicy::ACCEPT) {
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (httpChannel) {
+ LogMessageToConsole(httpChannel, "MixedContentBlockedDownload");
}
+ return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE;
}
if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
diff --git a/dom/security/nsHTTPSOnlyUtils.cpp b/dom/security/nsHTTPSOnlyUtils.cpp
index 535efaba4e..31c7408a37 100644
--- a/dom/security/nsHTTPSOnlyUtils.cpp
+++ b/dom/security/nsHTTPSOnlyUtils.cpp
@@ -398,8 +398,7 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
// 4. Don't upgrade if upgraded previously or exempt from upgrades
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
- if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST ||
- httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
+ if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
return false;
}
@@ -619,6 +618,18 @@ nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(
duration);
}
}
+
+ nsresult channelStatus;
+ channel->GetStatus(&channelStatus);
+ if (channelStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL) {
+ if (loadInfo->GetWasSchemelessInput() &&
+ !nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
+ mozilla::glean::httpsfirst::downgraded_on_timer_schemeless
+ .AddToNumerator();
+ } else {
+ mozilla::glean::httpsfirst::downgraded_on_timer.AddToNumerator();
+ }
+ }
}
}
@@ -891,6 +902,13 @@ bool nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(nsIURI* aHTTPSSchemeURI,
return uriEquals;
}
+
+/* static */
+uint32_t nsHTTPSOnlyUtils::GetStatusForSubresourceLoad(
+ uint32_t aHttpsOnlyStatus) {
+ return aHttpsOnlyStatus & ~nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST;
+}
+
/////////////////////////////////////////////////////////////////////
// Implementation of TestHTTPAnswerRunnable
@@ -992,19 +1010,6 @@ TestHTTPAnswerRunnable::OnStartRequest(nsIRequest* aRequest) {
nsresult httpsOnlyChannelStatus;
httpsOnlyChannel->GetStatus(&httpsOnlyChannelStatus);
if (httpsOnlyChannelStatus == NS_OK) {
- bool isPrivateWin =
- loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
- if (!nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin)) {
- // Record HTTPS-First Telemetry
- if (loadInfo->GetWasSchemelessInput() &&
- !nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
- mozilla::glean::httpsfirst::downgraded_on_timer_schemeless
- .AddToNumerator();
- } else {
- mozilla::glean::httpsfirst::downgraded_on_timer.AddToNumerator();
- }
- }
-
httpsOnlyChannel->Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
}
}
diff --git a/dom/security/nsHTTPSOnlyUtils.h b/dom/security/nsHTTPSOnlyUtils.h
index 7e36bfadbd..775fbf39e0 100644
--- a/dom/security/nsHTTPSOnlyUtils.h
+++ b/dom/security/nsHTTPSOnlyUtils.h
@@ -164,6 +164,18 @@ class nsHTTPSOnlyUtils {
nsIURI* aOtherURI,
nsILoadInfo* aLoadInfo);
+ /**
+ * Determines which HTTPS-Only status flags should get propagated to
+ * sub-resources or sub-documents. As sub-resources and sub-documents are
+ * exempt when the top-level document is exempt, we need to copy the "exempt"
+ * flag. The HTTPS-First "upgraded" flag should not be copied to prevent a
+ * unwanted downgrade (Bug 1885949).
+ * @param aHttpsOnlyStatus The HTTPS-Only status of the top-level document.
+ * @return The HTTPS-Only status that the sub-resource/document should
+ * receive.
+ */
+ static uint32_t GetStatusForSubresourceLoad(uint32_t aHttpsOnlyStatus);
+
private:
/**
* Checks if it can be ruled out that the error has something
diff --git a/dom/security/test/general/browser.toml b/dom/security/test/general/browser.toml
index c6d6b4bf79..0f4ec5b224 100644
--- a/dom/security/test/general/browser.toml
+++ b/dom/security/test/general/browser.toml
@@ -48,16 +48,6 @@ support-files = [
"file_gpc_server.sjs",
]
-["browser_test_http_download.js"]
-skip-if = [
- "win11_2009", # Bug 1784764
- "os == 'linux' && !debug",
-]
-support-files = [
- "http_download_page.html",
- "http_download_server.sjs"
-]
-
["browser_test_referrer_loadInOtherProcess.js"]
["browser_test_report_blocking.js"]
diff --git a/dom/security/test/general/browser_test_http_download.js b/dom/security/test/general/browser_test_http_download.js
deleted file mode 100644
index 35e3fdfc4b..0000000000
--- a/dom/security/test/general/browser_test_http_download.js
+++ /dev/null
@@ -1,275 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * https://creativecommons.org/publicdomain/zero/1.0/ */
-
-ChromeUtils.defineESModuleGetters(this, {
- Downloads: "resource://gre/modules/Downloads.sys.mjs",
- DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
-});
-
-const HandlerService = Cc[
- "@mozilla.org/uriloader/handler-service;1"
-].getService(Ci.nsIHandlerService);
-
-const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
-
-// Using insecure HTTP URL for a test cases around HTTP downloads
-let INSECURE_BASE_URL =
- getRootDirectory(gTestPath).replace(
- "chrome://mochitests/content/",
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
- "http://example.com/"
- ) + "http_download_page.html";
-
-function promiseFocus() {
- return new Promise(resolve => {
- waitForFocus(resolve);
- });
-}
-
-async function task_openPanel() {
- await promiseFocus();
-
- let promise = BrowserTestUtils.waitForPopupEvent(
- DownloadsPanel.panel,
- "shown"
- );
- DownloadsPanel.showPanel();
- await promise;
-}
-
-const downloadMonitoringView = {
- _listeners: [],
- onDownloadAdded(download) {
- for (let listener of this._listeners) {
- listener(download);
- }
- this._listeners = [];
- },
- waitForDownload(listener) {
- this._listeners.push(listener);
- },
-};
-
-/**
- * Waits until a download is triggered.
- * Unless the always_ask_before_handling_new_types pref is true, the download
- * will simply be saved, so resolve when the view is notified of the new
- * download. Otherwise, it waits until a prompt is shown, selects the choosen
- * <action>, then accepts the dialog
- * @param [action] Which action to select, either:
- * "handleInternally", "save" or "open".
- * @returns {Promise} Resolved once done.
- */
-
-function shouldTriggerDownload(action = "save") {
- if (
- Services.prefs.getBoolPref(
- "browser.download.always_ask_before_handling_new_types"
- )
- ) {
- return new Promise((resolve, reject) => {
- Services.wm.addListener({
- onOpenWindow(xulWin) {
- Services.wm.removeListener(this);
- let win = xulWin.docShell.domWindow;
- waitForFocus(() => {
- if (
- win.location ==
- "chrome://mozapps/content/downloads/unknownContentType.xhtml"
- ) {
- let dialog = win.document.getElementById("unknownContentType");
- let button = dialog.getButton("accept");
- let actionRadio = win.document.getElementById(action);
- actionRadio.click();
- button.disabled = false;
- dialog.acceptDialog();
- resolve();
- } else {
- reject();
- }
- }, win);
- },
- });
- });
- }
- return new Promise(res => {
- downloadMonitoringView.waitForDownload(res);
- });
-}
-
-const CONSOLE_ERROR_MESSAGE = "We blocked a download that’s not secure";
-
-function shouldConsoleError() {
- // Waits until CONSOLE_ERROR_MESSAGE was logged
- return new Promise((resolve, reject) => {
- function listener(msgObj) {
- let text = msgObj.message;
- if (text.includes(CONSOLE_ERROR_MESSAGE)) {
- Services.console.unregisterListener(listener);
- resolve();
- }
- }
- Services.console.registerListener(listener);
- });
-}
-
-async function resetDownloads() {
- // Removes all downloads from the download List
- const types = new Set();
- let publicList = await Downloads.getList(Downloads.PUBLIC);
- let downloads = await publicList.getAll();
- for (let download of downloads) {
- if (download.contentType) {
- types.add(download.contentType);
- }
- publicList.remove(download);
- await download.finalize(true);
- }
-
- if (types.size) {
- // reset handlers for the contentTypes of any files previously downloaded
- for (let type of types) {
- const mimeInfo = MIMEService.getFromTypeAndExtension(type, "");
- info("resetting handler for type: " + type);
- HandlerService.remove(mimeInfo);
- }
- }
-}
-
-function shouldNotifyDownloadUI() {
- return new Promise(res => {
- downloadMonitoringView.waitForDownload(async aDownload => {
- let { error } = aDownload;
- if (
- error.becauseBlockedByReputationCheck &&
- error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE
- ) {
- // It's an insecure Download, now Check that it has been cleaned up properly
- if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
- throw new Error(`Download target is not empty!`);
- }
- if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
- throw new Error(`Download partFile was not cleaned up properly`);
- }
- // Assert that the Referrer is presnt
- if (!aDownload.source.referrerInfo) {
- throw new Error("The Blocked download is missing the ReferrerInfo");
- }
-
- res(aDownload);
- } else {
- ok(false, "No error for download that was expected to error!");
- }
- });
- });
-}
-
-async function runTest(url, link, checkFunction, description) {
- await SpecialPowers.pushPrefEnv({
- set: [["dom.block_download_insecure", true]],
- });
- await resetDownloads();
-
- let tab = BrowserTestUtils.addTab(gBrowser, url);
- gBrowser.selectedTab = tab;
-
- let browser = gBrowser.getBrowserForTab(tab);
- await BrowserTestUtils.browserLoaded(browser);
-
- info("Checking: " + description);
-
- let checkPromise = checkFunction();
- // Click the Link to trigger the download
- SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
- content.document.getElementById(contentLink).click();
- });
-
- await checkPromise;
-
- ok(true, description);
- BrowserTestUtils.removeTab(tab);
-
- await SpecialPowers.popPrefEnv();
-}
-
-add_setup(async () => {
- let list = await Downloads.getList(Downloads.ALL);
- list.addView(downloadMonitoringView);
- registerCleanupFunction(() => list.removeView(downloadMonitoringView));
-});
-
-// Test Blocking
-add_task(async function test_blocking() {
- for (let prefVal of [true, false]) {
- await SpecialPowers.pushPrefEnv({
- set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
- });
- await runTest(
- INSECURE_BASE_URL,
- "http-link",
- () =>
- Promise.all([
- shouldTriggerDownload(),
- shouldNotifyDownloadUI(),
- shouldConsoleError(),
- ]),
- "Insecure (HTTP) toplevel -> Insecure (HTTP) download should Error"
- );
- await SpecialPowers.popPrefEnv();
- }
-});
-
-// Test Manual Unblocking
-add_task(async function test_manual_unblocking() {
- for (let prefVal of [true, false]) {
- await SpecialPowers.pushPrefEnv({
- set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
- });
- await runTest(
- INSECURE_BASE_URL,
- "http-link",
- async () => {
- let [, download] = await Promise.all([
- shouldTriggerDownload(),
- shouldNotifyDownloadUI(),
- ]);
- await download.unblock();
- Assert.equal(
- download.error,
- null,
- "There should be no error after unblocking"
- );
- },
- "A blocked download should succeed to download after a manual unblock"
- );
- await SpecialPowers.popPrefEnv();
- }
-});
-
-// Test Unblock Download Visible
-add_task(async function test_unblock_download_visible() {
- for (let prefVal of [true, false]) {
- await SpecialPowers.pushPrefEnv({
- set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
- });
- await promiseFocus();
- await runTest(
- INSECURE_BASE_URL,
- "http-link",
- async () => {
- let panelHasOpened = BrowserTestUtils.waitForPopupEvent(
- DownloadsPanel.panel,
- "shown"
- );
- info("awaiting that the download is triggered and added to the list");
- await Promise.all([shouldTriggerDownload(), shouldNotifyDownloadUI()]);
- info("awaiting that the Download list shows itself");
- await panelHasOpened;
- DownloadsPanel.hidePanel();
- ok(true, "The Download Panel should have opened on blocked download");
- },
- "A blocked download should open the download panel"
- );
- await SpecialPowers.popPrefEnv();
- }
-});
diff --git a/dom/security/test/general/file_block_script_wrong_mime_sw.js b/dom/security/test/general/file_block_script_wrong_mime_sw.js
new file mode 100644
index 0000000000..4d8d667af4
--- /dev/null
+++ b/dom/security/test/general/file_block_script_wrong_mime_sw.js
@@ -0,0 +1,51 @@
+/**
+ * Service Worker that runs in 2 modes: 1) direct pass-through via
+ * fetch(event.request) and 2) indirect pass-through via
+ * fetch(event.request.url).
+ *
+ * Because this is updating a pre-existing mochitest that didn't use a SW and
+ * used a single test document, we use a SW idiom where the SW claims the
+ * existing window client. And because we operate in two modes and we
+ * parameterize via URL, we also ensure that we skipWaiting.
+ **/
+
+/* eslint-env serviceworker */
+
+// We are parameterized by "mode".
+const params = new URLSearchParams(location.search);
+const fetchMode = params.get("fetchMode");
+
+// When activating on initial install, claim the existing window client.
+// For synchronziation, also message the controlled document to report our mode.
+self.addEventListener("activate", event => {
+ event.waitUntil(
+ (async () => {
+ await clients.claim();
+ const allClients = await clients.matchAll();
+ for (const client of allClients) {
+ client.postMessage({
+ fetchMode,
+ });
+ }
+ })()
+ );
+});
+
+// When updating the SW to change our mode of operation, skipWaiting so we
+// advance directly to activating without waiting for the test window client
+// to stop being controlled by our previous configuration.
+self.addEventListener("install", () => {
+ self.skipWaiting();
+});
+
+self.addEventListener("fetch", event => {
+ switch (fetchMode) {
+ case "direct":
+ event.respondWith(fetch(event.request));
+ break;
+
+ case "indirect":
+ event.respondWith(fetch(event.request.url));
+ break;
+ }
+});
diff --git a/dom/security/test/general/http_download_page.html b/dom/security/test/general/http_download_page.html
deleted file mode 100644
index c5461eaed3..0000000000
--- a/dom/security/test/general/http_download_page.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE HTML>
-<html>
- <head>
- <title>Test for the download attribute</title>
- </head>
- <body>
- hi
-
- <script>
- const host = window.location.host;
- const path = location.pathname.replace("http_download_page.html","http_download_server.sjs");
-
- const insecureLink = document.createElement("a");
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
- insecureLink.href=`http://${host}/${path}`;
- insecureLink.download="true";
- insecureLink.id="http-link";
- insecureLink.textContent="Not secure Link";
-
- document.body.append(insecureLink);
- </script>
- </body>
-</html>
diff --git a/dom/security/test/general/http_download_server.sjs b/dom/security/test/general/http_download_server.sjs
deleted file mode 100644
index e659df2f40..0000000000
--- a/dom/security/test/general/http_download_server.sjs
+++ /dev/null
@@ -1,20 +0,0 @@
-// force the Browser to Show a Download Prompt
-
-function handleRequest(request, response) {
- let type = "image/png";
- let filename = "hello.png";
- request.queryString.split("&").forEach(val => {
- var [key, value] = val.split("=");
- if (key == "type") {
- type = value;
- }
- if (key == "name") {
- filename = value;
- }
- });
-
- response.setHeader("Cache-Control", "no-cache", false);
- response.setHeader("Content-Disposition", `attachment; filename=${filename}`);
- response.setHeader("Content-Type", type);
- response.write("🙈🙊🐵🙊");
-}
diff --git a/dom/security/test/general/mochitest.toml b/dom/security/test/general/mochitest.toml
index c46b5ecf57..22024fcc67 100644
--- a/dom/security/test/general/mochitest.toml
+++ b/dom/security/test/general/mochitest.toml
@@ -8,6 +8,7 @@ support-files = [
"file_block_toplevel_data_navigation2.html",
"file_block_toplevel_data_navigation3.html",
"file_block_toplevel_data_redirect.sjs",
+ "file_block_script_wrong_mime_sw.js",
"file_block_subresource_redir_to_data.sjs",
"file_same_site_cookies_subrequest.sjs",
"file_same_site_cookies_toplevel_nav.sjs",
diff --git a/dom/security/test/general/test_block_script_wrong_mime.html b/dom/security/test/general/test_block_script_wrong_mime.html
index 7122363dfc..896823a417 100644
--- a/dom/security/test/general/test_block_script_wrong_mime.html
+++ b/dom/security/test/general/test_block_script_wrong_mime.html
@@ -29,7 +29,7 @@ function testScript([mime, shouldLoad]) {
let script = document.createElement("script");
script.onload = () => {
document.body.removeChild(script);
- ok(shouldLoad, `script with mime '${mime}' should load`);
+ ok(shouldLoad, `script with mime '${mime}' should ${shouldLoad ? "" : "NOT "}load`);
resolve();
};
script.onerror = () => {
@@ -47,7 +47,7 @@ function testWorker([mime, shouldLoad]) {
return new Promise((resolve) => {
let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker&mime="+mime);
worker.onmessage = (event) => {
- ok(shouldLoad, `worker with mime '${mime}' should load`)
+ ok(shouldLoad, `worker with mime '${mime}' should ${shouldLoad ? "" : "NOT "}load`);
is(event.data, "worker-loaded", "worker should send correct message");
resolve();
};
@@ -65,7 +65,7 @@ function testWorkerImportScripts([mime, shouldLoad]) {
return new Promise((resolve) => {
let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker-import&mime="+mime);
worker.onmessage = (event) => {
- ok(shouldLoad, `worker/importScripts with mime '${mime}' should load`)
+ ok(shouldLoad, `worker/importScripts with mime '${mime}' should ${shouldLoad ? "" : "NOT "}load`);
is(event.data, "worker-loaded", "worker should send correct message");
resolve();
};
@@ -73,20 +73,103 @@ function testWorkerImportScripts([mime, shouldLoad]) {
ok(!shouldLoad, `worker/importScripts with wrong mime '${mime}' should be blocked`);
error.preventDefault();
resolve();
+ // The worker doesn't self-terminate via close, so let's do it.
+ worker.terminate();
}
worker.postMessage("dummy");
});
}
-SimpleTest.waitForExplicitFinish();
-Promise.all(MIMETypes.map(testScript)).then(() => {
- return Promise.all(MIMETypes.map(testWorker));
-}).then(() => {
- return Promise.all(MIMETypes.map(testWorkerImportScripts));
-}).then(() => {
- return SpecialPowers.popPrefEnv();
-}).then(SimpleTest.finish);
+async function runMimeTypePermutations() {
+ info("### Running document script MIME checks.");
+ for (const mimeType of MIMETypes) {
+ await testScript(mimeType);
+ }
+ info("### Running worker top-level script MIME checks.");
+ for (const mimeType of MIMETypes) {
+ await testWorker(mimeType);
+ }
+
+ info("### Running worker importScripts MIME checks.");
+ for (const mimeType of MIMETypes) {
+ await testWorkerImportScripts(mimeType);
+ }
+}
+
+let gRegistration;
+
+/**
+ * Register and wait for the helper ServiceWorker to be active in the given
+ * mode.
+ */
+async function useServiceWorker({ fetchMode }) {
+ info(`### Registering ServiceWorker with mode '${fetchMode}'`);
+ const activePromise = new Promise((resolve, reject) => {
+ navigator.serviceWorker.addEventListener(
+ "message",
+ event => {
+ if (event.data.fetchMode === fetchMode) {
+ resolve();
+ } else {
+ reject(`wrong fetchMode: ${fetchMode}`);
+ }
+ is(fetchMode, event.data.fetchMode, "right fetch mode");
+ },
+ { once: true });
+ });
+
+ const reg = gRegistration = await navigator.serviceWorker.register(
+ `file_block_script_wrong_mime_sw.js?fetchMode=${fetchMode}`);
+ info("register resolved. " +
+ `installing: ${!!reg.installing} ` +
+ `waiting: ${!!reg.waiting} ` +
+ `active: ${!!reg.active}`);
+
+ await activePromise;
+}
+
+/**
+ * Unregister the ServiceWorker, with the caveat that the ServiceWorker will
+ * still be controlling us until this window goes away.
+ */
+async function cleanupServiceWorkerWithCaveat() {
+ await gRegistration.unregister();
+}
+
+/**
+ * Top-level test that runs the MIME type checks in different ServiceWorker/
+ * network configurations.
+ *
+ * We use the ServiceWorker mechanism that allows ServiceWorkers to claim
+ * existing scope-matching clients in order to make this window controlled and
+ * then run the tests. When changing the SW behavior the SW also needs to
+ * skipWaiting in order to advance to active.
+ */
+async function runNetworkPermutations() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ],
+ });
+
+ info("## Run tests without a ServiceWorker involved.");
+ await runMimeTypePermutations();
+
+ info("## Run tests with a pass-through fetch(event.request) handler.");
+ await useServiceWorker({ fetchMode: "direct" });
+ await runMimeTypePermutations();
+
+ info("## Run tests with a naive URL propagating fetch(event.request.url) handler.");
+ await useServiceWorker({ fetchMode: "indirect" });
+ await runMimeTypePermutations();
+
+ await cleanupServiceWorkerWithCaveat();
+}
+
+add_task(runNetworkPermutations);
</script>
</body>
</html>
diff --git a/dom/security/test/gtest/TestCSPParser.cpp b/dom/security/test/gtest/TestCSPParser.cpp
index b8a4e986b6..19ba0548de 100644
--- a/dom/security/test/gtest/TestCSPParser.cpp
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -93,8 +93,7 @@ nsresult runTest(
// for testing the parser we only need to set a principal which is needed
// to translate the keyword 'self' into an actual URI.
- rv =
- csp->SetRequestContextWithPrincipal(selfURIPrincipal, selfURI, u""_ns, 0);
+ rv = csp->SetRequestContextWithPrincipal(selfURIPrincipal, selfURI, ""_ns, 0);
NS_ENSURE_SUCCESS(rv, rv);
// append a policy
diff --git a/dom/security/test/https-first/browser.toml b/dom/security/test/https-first/browser.toml
index 0c63b8317d..49e2d522f4 100644
--- a/dom/security/test/https-first/browser.toml
+++ b/dom/security/test/https-first/browser.toml
@@ -7,7 +7,7 @@ support-files = ["file_beforeunload_permit_http.html"]
support-files = [
"file_mixed_content_auto_upgrade.html",
"pass.png",
- "test.ogv",
+ "test.webm",
"test.wav",
]
@@ -40,6 +40,12 @@ support-files = [
["browser_navigation.js"]
support-files = ["file_navigation.html"]
+["browser_subdocument_downgrade.js"]
+support-files = [
+ "file_empty.html",
+ "file_subdocument_downgrade.sjs",
+]
+
["browser_schemeless.js"]
["browser_slow_download.js"]
diff --git a/dom/security/test/https-first/browser_beforeunload_permit_http.js b/dom/security/test/https-first/browser_beforeunload_permit_http.js
index 660c1a352d..281def37e9 100644
--- a/dom/security/test/https-first/browser_beforeunload_permit_http.js
+++ b/dom/security/test/https-first/browser_beforeunload_permit_http.js
@@ -162,7 +162,7 @@ async function loadPageAndReload(testCase) {
}
);
is(true, hasInteractedWith, "Simulated successfully user interaction");
- BrowserReloadWithFlags(testCase.reloadFlag);
+ BrowserCommands.reloadWithFlags(testCase.reloadFlag);
await BrowserTestUtils.browserLoaded(browser);
is(true, true, `reload with flag ${testCase.name} was successful`);
}
diff --git a/dom/security/test/https-first/browser_subdocument_downgrade.js b/dom/security/test/https-first/browser_subdocument_downgrade.js
new file mode 100644
index 0000000000..4cb5b4ed2e
--- /dev/null
+++ b/dom/security/test/https-first/browser_subdocument_downgrade.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const EMPTY_URL =
+ "http://example.com/browser/dom/security/test/https-first/file_empty.html";
+const SUBDOCUMENT_URL =
+ "https://example.com/browser/dom/security/test/https-first/file_subdocument_downgrade.sjs";
+
+add_task(async function test_subdocument_downgrade() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // We want to test HTTPS-First
+ ["dom.security.https_first", true],
+ // Makes it easier to detect the error
+ ["security.mixed_content.block_active_content", false],
+ ],
+ });
+
+ // Open a empty document with origin http://example.com, which gets upgraded
+ // to https://example.com by HTTPS-First and thus is marked as
+ // HTTPS_ONLY_UPGRADED_HTTPS_FIRST.
+ await BrowserTestUtils.withNewTab(EMPTY_URL, async browser => {
+ await SpecialPowers.spawn(
+ browser,
+ [SUBDOCUMENT_URL],
+ async SUBDOCUMENT_URL => {
+ function isCrossOriginIframe(iframe) {
+ try {
+ return !iframe.contentDocument;
+ } catch (e) {
+ return true;
+ }
+ }
+ const subdocument = content.document.createElement("iframe");
+ // We open https://example.com/.../file_subdocument_downgrade.sjs in a
+ // iframe, which sends a invalid response if the scheme is https. Thus
+ // we should get an error. But if we accidentally copy the
+ // HTTPS_ONLY_UPGRADED_HTTPS_FIRST flag from the parent into the iframe
+ // loadinfo, HTTPS-First will try to downgrade the iframe. We test that
+ // this doesn't happen.
+ subdocument.src = SUBDOCUMENT_URL;
+ const loadPromise = new Promise(resolve => {
+ subdocument.addEventListener("load", () => {
+ ok(
+ // If the iframe got downgraded, it should now have the origin
+ // http://example.com, which we can detect as being cross-origin.
+ !isCrossOriginIframe(subdocument),
+ "Subdocument should not be downgraded"
+ );
+ resolve();
+ });
+ });
+ content.document.body.appendChild(subdocument);
+ await loadPromise;
+ }
+ );
+ });
+});
diff --git a/dom/security/test/https-first/file_empty.html b/dom/security/test/https-first/file_empty.html
new file mode 100644
index 0000000000..39d495653e
--- /dev/null
+++ b/dom/security/test/https-first/file_empty.html
@@ -0,0 +1 @@
+<!doctype html><html><body></body></html>
diff --git a/dom/security/test/https-first/file_mixed_content_auto_upgrade.html b/dom/security/test/https-first/file_mixed_content_auto_upgrade.html
index 7dda8909a5..5a8bef6bb0 100644
--- a/dom/security/test/https-first/file_mixed_content_auto_upgrade.html
+++ b/dom/security/test/https-first/file_mixed_content_auto_upgrade.html
@@ -6,7 +6,7 @@
<body>
<!--upgradeable resources--->
<img src="http://example.com/browser/dom/security/test/https-first/pass.png">
- <video src="http://example.com/browser/dom/security/test/https-first/test.ogv">
+ <video src="http://example.com/browser/dom/security/test/https-first/test.webm">
<audio src="http://example.com/browser/dom/security/test/https-first/test.wav">
</body>
</html>
diff --git a/dom/security/test/https-first/file_multiple_redirection.sjs b/dom/security/test/https-first/file_multiple_redirection.sjs
index 49098ccdb7..e34a360fa6 100644
--- a/dom/security/test/https-first/file_multiple_redirection.sjs
+++ b/dom/security/test/https-first/file_multiple_redirection.sjs
@@ -5,6 +5,8 @@ const REDIRECT_URI =
"https://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?redirect";
const REDIRECT_URI_HTTP =
"http://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
+const OTHERHOST_REDIRECT_URI_HTTP =
+ "http://example.org/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
const REDIRECT_URI_HTTPS =
"https://example.com/tests/dom/security/test/https-first/file_multiple_redirection.sjs?verify";
@@ -44,6 +46,11 @@ function sendRedirection(query, response) {
if (query.includes("test3")) {
response.setHeader("Strict-Transport-Security", "max-age=60");
response.setHeader("Location", REDIRECT_URI_HTTP, false);
+ return;
+ }
+ // send a redirection to a different http uri
+ if (query.includes("test4")) {
+ response.setHeader("Location", OTHERHOST_REDIRECT_URI_HTTP, false);
}
}
@@ -53,6 +60,11 @@ function handleRequest(request, response) {
// if the query contains a test query start first test
if (query.startsWith("test")) {
+ // all of these should be upgraded
+ if (request.scheme !== "https") {
+ response.setStatusLine(request.httpVersion, 500, "OK");
+ response.write("Request should have been HTTPS.");
+ }
// send a 302 redirection
response.setStatusLine(request.httpVersion, 302, "Found");
response.setHeader("Location", REDIRECT_URI + query, false);
@@ -60,6 +72,10 @@ function handleRequest(request, response) {
}
// Send a redirection
if (query.includes("redirect")) {
+ if (request.scheme !== "https") {
+ response.setStatusLine(request.httpVersion, 500, "OK");
+ response.write("Request should have been HTTPS.");
+ }
response.setStatusLine(request.httpVersion, 302, "Found");
sendRedirection(query, response);
return;
@@ -83,5 +99,5 @@ function handleRequest(request, response) {
// We should never get here, but just in case ...
response.setStatusLine(request.httpVersion, 500, "OK");
- response.write("unexepcted query");
+ response.write("unexpected query");
}
diff --git a/dom/security/test/https-first/file_subdocument_downgrade.sjs b/dom/security/test/https-first/file_subdocument_downgrade.sjs
new file mode 100644
index 0000000000..53ced94ba8
--- /dev/null
+++ b/dom/security/test/https-first/file_subdocument_downgrade.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response) {
+ if (request.scheme === "https") {
+ response.setStatusLine("1.1", 429, "Too Many Requests");
+ } else {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("<!doctype html><html><body></body></html>");
+ }
+}
diff --git a/dom/security/test/https-first/test.ogv b/dom/security/test/https-first/test.ogv
deleted file mode 100644
index 0f83996e5d..0000000000
--- a/dom/security/test/https-first/test.ogv
+++ /dev/null
Binary files differ
diff --git a/dom/security/test/https-first/test.webm b/dom/security/test/https-first/test.webm
new file mode 100644
index 0000000000..221877e303
--- /dev/null
+++ b/dom/security/test/https-first/test.webm
Binary files differ
diff --git a/dom/security/test/https-first/test_multiple_redirection.html b/dom/security/test/https-first/test_multiple_redirection.html
index d631f140e6..678a8133a8 100644
--- a/dom/security/test/https-first/test_multiple_redirection.html
+++ b/dom/security/test/https-first/test_multiple_redirection.html
@@ -37,6 +37,12 @@ Test multiple redirects using https-first and ensure the entire redirect chain i
{name: "test last redirect HSTS", result: "scheme-https", query: "test3"},
// reset: reset hsts header for example.com
{name: "reset HSTS header", result: "scheme-https", query: "reset"},
+ // test 4: http://example.com/...test4 -upgrade-> httpS://example.com/...test4
+ // https://example.com/...test4 -redir-> https://example.com/.../REDIRECT
+ // https://example.com/.../redirect -redir-> http://example.ORG/.../verify
+ // http://example.org/.../verify -upgrade-> httpS://example.ORG/.../verify
+ // Everything should be upgraded and accessed only via HTTPS!
+ {name: "test last redirect other HTTP origin gets upgraded", result: "scheme-https", query: "test4" },
]
let currentTest = 0;
let testWin;
@@ -48,7 +54,7 @@ Test multiple redirects using https-first and ensure the entire redirect chain i
let test = testCase[currentTest];
is(event.data.result,
test.result,
- "same-origin redirect results in " + test.name
+ "redirect results in " + test.name
);
testWin.close();
if (++currentTest < testCase.length) {
diff --git a/dom/security/test/https-only/browser_save_as.js b/dom/security/test/https-only/browser_save_as.js
index fbfdf276a8..28f3df539d 100644
--- a/dom/security/test/https-only/browser_save_as.js
+++ b/dom/security/test/https-only/browser_save_as.js
@@ -155,11 +155,7 @@ async function setHttpsFirstAndOnlyPrefs(httpsFirst, httpsOnly) {
add_task(async function testBaseline() {
// Run with HTTPS-First and HTTPS-Only disabled
await setHttpsFirstAndOnlyPrefs(false, false);
- await runTest(
- "#insecure-link",
- HTTP_LINK,
- "We blocked a download that’s not secure: “http://example.org/”."
- );
+ await runTest("#insecure-link", HTTP_LINK, undefined);
await runTest("#secure-link", HTTPS_LINK, undefined);
});
@@ -173,7 +169,7 @@ add_task(async function testHttpsFirst() {
await runTest(
"#insecure-link",
HTTP_LINK,
- "We blocked a download that’s not secure: “http://example.org/”."
+ "Blocked downloading insecure content “http://example.org/”."
);
await runTest("#secure-link", HTTPS_LINK, undefined);
});
@@ -185,7 +181,7 @@ add_task(async function testHttpsOnly() {
await runTest(
"#insecure-link",
HTTP_LINK,
- "We blocked a download that’s not secure: “http://example.org/”."
+ "Blocked downloading insecure content “http://example.org/”."
);
await runTest("#secure-link", HTTPS_LINK, undefined);
});
diff --git a/dom/security/test/mixedcontentblocker/browser.toml b/dom/security/test/mixedcontentblocker/browser.toml
index 5b0b85cb0b..402e8b91b1 100644
--- a/dom/security/test/mixedcontentblocker/browser.toml
+++ b/dom/security/test/mixedcontentblocker/browser.toml
@@ -15,7 +15,7 @@ support-files = [
support-files = [
"file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html",
"pass.png",
- "test.ogv",
+ "test.webm",
"test.wav",
]
diff --git a/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js b/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js
index 25fee8de3c..57842eb623 100644
--- a/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js
+++ b/dom/security/test/mixedcontentblocker/browser_mixed_content_auth_download.js
@@ -12,10 +12,6 @@ const { PromptTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/PromptTestUtils.sys.mjs"
);
-let authPromptModalType = Services.prefs.getIntPref(
- "prompts.modalType.httpAuth"
-);
-
const downloadMonitoringView = {
_listeners: [],
onDownloadAdded(download) {
@@ -107,7 +103,7 @@ async function runTest(url, link, checkFunction, description) {
// Wait for the auth prompt, enter the login details and close the prompt
await PromptTestUtils.handleNextPrompt(
gBrowser.selectedBrowser,
- { modalType: authPromptModalType, promptType: "promptUserAndPass" },
+ { modalType: Ci.nsIPrompt.MODAL_TYPE_TAB, promptType: "promptUserAndPass" },
{ buttonNumClick: 0, loginInput: "user", passwordInput: "pass" }
);
await checkPromise;
diff --git a/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js b/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js
index b103d83cd7..ee350008aa 100644
--- a/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js
+++ b/dom/security/test/mixedcontentblocker/browser_test_mixed_content_download.js
@@ -101,7 +101,7 @@ function shouldTriggerDownload(action = "save") {
});
}
-const CONSOLE_ERROR_MESSAGE = "We blocked a download that’s not secure";
+const CONSOLE_ERROR_MESSAGE = "Blocked downloading insecure content";
function shouldConsoleError() {
// Waits until CONSOLE_ERROR_MESSAGE was logged
diff --git a/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html b/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html
index 80e97443ed..62e705227f 100644
--- a/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html
+++ b/dom/security/test/mixedcontentblocker/file_csp_block_all_mixedcontent_and_mixed_content_display_upgrade.html
@@ -8,7 +8,7 @@
<body>
<!--upgradeable resources--->
<img id="some-img" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/pass.png" width="100px">
- <video id="some-video" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/test.ogv" width="100px">
+ <video id="some-video" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/test.webm" width="100px">
<audio id="some-audio" src="http://test1.example.com/browser/dom/security/test/mixedcontentblocker/test.wav" width="100px">
</body>
</html>
diff --git a/dom/security/test/mixedcontentblocker/file_server.sjs b/dom/security/test/mixedcontentblocker/file_server.sjs
index 4f86c282ee..90034dad3f 100644
--- a/dom/security/test/mixedcontentblocker/file_server.sjs
+++ b/dom/security/test/mixedcontentblocker/file_server.sjs
@@ -96,8 +96,8 @@ function handleRequest(request, response) {
break;
case "media":
- response.setHeader("Content-Type", "video/ogg", false);
- response.write(loadContentFromFile("tests/dom/media/test/320x240.ogv"));
+ response.setHeader("Content-Type", "video/webm", false);
+ response.write(loadContentFromFile("tests/dom/media/test/vp9.webm"));
break;
case "iframe":
diff --git a/dom/security/test/mixedcontentblocker/mochitest.toml b/dom/security/test/mixedcontentblocker/mochitest.toml
index 17d8cb4608..cf1d4827a0 100644
--- a/dom/security/test/mixedcontentblocker/mochitest.toml
+++ b/dom/security/test/mixedcontentblocker/mochitest.toml
@@ -16,7 +16,9 @@ support-files = [
"file_main_bug803225.html",
"file_main_bug803225_websocket_wsh.py",
"file_server.sjs",
- "!/dom/media/test/320x240.ogv",
+ "!/dom/media/test/vp9.webm",
+ "test.webm",
+ "test.wav",
"!/image/test/mochitest/blue.png",
"file_redirect.html",
"file_redirect_handler.sjs",
diff --git a/dom/security/test/mixedcontentblocker/test.ogv b/dom/security/test/mixedcontentblocker/test.ogv
deleted file mode 100644
index 0f83996e5d..0000000000
--- a/dom/security/test/mixedcontentblocker/test.ogv
+++ /dev/null
Binary files differ
diff --git a/dom/security/test/mixedcontentblocker/test.webm b/dom/security/test/mixedcontentblocker/test.webm
new file mode 100644
index 0000000000..221877e303
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test.webm
Binary files differ
diff --git a/dom/security/test/referrer-policy/browser.toml b/dom/security/test/referrer-policy/browser.toml
index a77046c85b..ba571fec81 100644
--- a/dom/security/test/referrer-policy/browser.toml
+++ b/dom/security/test/referrer-policy/browser.toml
@@ -1,9 +1,10 @@
[DEFAULT]
support-files = ["referrer_page.sjs"]
-["browser_session_history.js"]
-support-files = ["file_session_history.sjs"]
-
["browser_referrer_disallow_cross_site_relaxing.js"]
+skip-if = ["asan"] # too slow
["browser_referrer_telemetry.js"]
+
+["browser_session_history.js"]
+support-files = ["file_session_history.sjs"]
diff --git a/dom/security/test/referrer-policy/browser_referrer_disallow_cross_site_relaxing.js b/dom/security/test/referrer-policy/browser_referrer_disallow_cross_site_relaxing.js
index 7f8df7b34b..84e79af3ef 100644
--- a/dom/security/test/referrer-policy/browser_referrer_disallow_cross_site_relaxing.js
+++ b/dom/security/test/referrer-policy/browser_referrer_disallow_cross_site_relaxing.js
@@ -191,6 +191,8 @@ add_setup(async function () {
set: [
// Disable mixed content blocking to be able to test downgrade scenario.
["security.mixed_content.block_active_content", false],
+ // Disable https-first since we are testing http and https referrers
+ ["dom.security.https_first", false],
],
});
});
diff --git a/dom/security/trusted-types/TrustedTypePolicy.cpp b/dom/security/trusted-types/TrustedTypePolicy.cpp
index 3c4e758ed0..62c58ae1f6 100644
--- a/dom/security/trusted-types/TrustedTypePolicy.cpp
+++ b/dom/security/trusted-types/TrustedTypePolicy.cpp
@@ -6,38 +6,98 @@
#include "mozilla/dom/TrustedTypePolicy.h"
-#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/dom/DOMString.h"
#include "mozilla/dom/TrustedTypePolicyFactory.h"
#include "mozilla/dom/TrustedTypesBinding.h"
+#include <utility>
+
namespace mozilla::dom {
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TrustedTypePolicy, mParentObject)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TrustedTypePolicy, mParentObject,
+ mOptions.mCreateHTMLCallback,
+ mOptions.mCreateScriptCallback,
+ mOptions.mCreateScriptURLCallback)
+
+TrustedTypePolicy::TrustedTypePolicy(TrustedTypePolicyFactory* aParentObject,
+ const nsAString& aName, Options&& aOptions)
+ : mParentObject{aParentObject},
+ mName{aName},
+ mOptions{std::move(aOptions)} {}
JSObject* TrustedTypePolicy::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return TrustedTypePolicy_Binding::Wrap(aCx, this, aGivenProto);
}
-UniquePtr<TrustedHTML> TrustedTypePolicy::CreateHTML(
- JSContext* aJSContext, const nsAString& aInput,
- const Sequence<JS::Value>& aArguments) const {
- // TODO: implement the spec.
- return MakeUnique<TrustedHTML>();
+void TrustedTypePolicy::GetName(DOMString& aResult) const {
+ aResult.SetKnownLiveString(mName);
}
-UniquePtr<TrustedScript> TrustedTypePolicy::CreateScript(
- JSContext* aJSContext, const nsAString& aInput,
- const Sequence<JS::Value>& aArguments) const {
- // TODO: implement the spec.
- return MakeUnique<TrustedScript>();
+#define IMPL_CREATE_TRUSTED_TYPE(_trustedTypeSuffix) \
+ UniquePtr<Trusted##_trustedTypeSuffix> \
+ TrustedTypePolicy::Create##_trustedTypeSuffix( \
+ JSContext* aJSContext, const nsAString& aInput, \
+ const Sequence<JS::Value>& aArguments, ErrorResult& aErrorResult) \
+ const { \
+ /* Invoking the callback could delete the policy and hence the callback. \
+ * Hence keep a strong reference to it on the stack. \
+ */ \
+ RefPtr<Create##_trustedTypeSuffix##Callback> callbackObject = \
+ mOptions.mCreate##_trustedTypeSuffix##Callback; \
+ \
+ return CreateTrustedType<Trusted##_trustedTypeSuffix>( \
+ callbackObject, aInput, aArguments, aErrorResult); \
+ }
+
+IMPL_CREATE_TRUSTED_TYPE(HTML)
+IMPL_CREATE_TRUSTED_TYPE(Script)
+IMPL_CREATE_TRUSTED_TYPE(ScriptURL)
+
+template <typename T, typename CallbackObject>
+UniquePtr<T> TrustedTypePolicy::CreateTrustedType(
+ const RefPtr<CallbackObject>& aCallbackObject, const nsAString& aValue,
+ const Sequence<JS::Value>& aArguments, ErrorResult& aErrorResult) const {
+ nsString policyValue;
+ DetermineTrustedPolicyValue(aCallbackObject, aValue, aArguments, true,
+ aErrorResult, policyValue);
+
+ if (aErrorResult.Failed()) {
+ return nullptr;
+ }
+
+ UniquePtr<T> trustedObject = MakeUnique<T>(std::move(policyValue));
+
+ // TODO: add special handling for `TrustedScript` when default policy support
+ // is added.
+
+ return trustedObject;
}
-UniquePtr<TrustedScriptURL> TrustedTypePolicy::CreateScriptURL(
- JSContext* aJSContext, const nsAString& aInput,
- const Sequence<JS::Value>& aArguments) const {
- // TODO: implement the spec.
- return MakeUnique<TrustedScriptURL>();
+template <typename CallbackObject>
+void TrustedTypePolicy::DetermineTrustedPolicyValue(
+ const RefPtr<CallbackObject>& aCallbackObject, const nsAString& aValue,
+ const Sequence<JS::Value>& aArguments, bool aThrowIfMissing,
+ ErrorResult& aErrorResult, nsAString& aResult) const {
+ if (!aCallbackObject) {
+ // The spec lacks a definition for stringifying null, see
+ // <https://github.com/w3c/trusted-types/issues/469>.
+ aResult = EmptyString();
+
+ if (aThrowIfMissing) {
+ aErrorResult.ThrowTypeError("Function missing.");
+ }
+
+ return;
+ }
+
+ nsString callbackResult;
+ aCallbackObject->Call(aValue, aArguments, callbackResult, aErrorResult,
+ nullptr, CallbackObject::eRethrowExceptions);
+
+ if (!aErrorResult.Failed()) {
+ aResult = std::move(callbackResult);
+ }
}
} // namespace mozilla::dom
diff --git a/dom/security/trusted-types/TrustedTypePolicy.h b/dom/security/trusted-types/TrustedTypePolicy.h
index 22d99947b3..d677088285 100644
--- a/dom/security/trusted-types/TrustedTypePolicy.h
+++ b/dom/security/trusted-types/TrustedTypePolicy.h
@@ -9,19 +9,20 @@
#include "js/TypeDecls.h"
#include "js/Value.h"
+#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/DOMString.h"
#include "mozilla/dom/TrustedHTML.h"
#include "mozilla/dom/TrustedScript.h"
#include "mozilla/dom/TrustedScriptURL.h"
#include "nsISupportsImpl.h"
-#include "nsStringFwd.h"
+#include "nsString.h"
#include "nsWrapperCache.h"
namespace mozilla::dom {
+class DOMString;
class TrustedTypePolicyFactory;
// https://w3c.github.io/trusted-types/dist/spec/#trusted-type-policy
@@ -30,8 +31,14 @@ class TrustedTypePolicy : public nsWrapperCache {
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TrustedTypePolicy)
NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(TrustedTypePolicy)
- explicit TrustedTypePolicy(TrustedTypePolicyFactory* aParentObject)
- : mParentObject{aParentObject} {}
+ struct Options {
+ RefPtr<CreateHTMLCallback> mCreateHTMLCallback;
+ RefPtr<CreateScriptCallback> mCreateScriptCallback;
+ RefPtr<CreateScriptURLCallback> mCreateScriptURLCallback;
+ };
+
+ TrustedTypePolicy(TrustedTypePolicyFactory* aParentObject,
+ const nsAString& aName, Options&& aOptions);
// Required for Web IDL binding.
TrustedTypePolicyFactory* GetParentObject() const { return mParentObject; }
@@ -41,30 +48,46 @@ class TrustedTypePolicy : public nsWrapperCache {
JS::Handle<JSObject*> aGivenProto) override;
// https://w3c.github.io/trusted-types/dist/spec/#trustedtypepolicy-name
- void GetName(DOMString& aResult) const {
- // TODO: impl.
- }
+ void GetName(DOMString& aResult) const;
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicy-createhtml
- UniquePtr<TrustedHTML> CreateHTML(
+ MOZ_CAN_RUN_SCRIPT UniquePtr<TrustedHTML> CreateHTML(
JSContext* aJSContext, const nsAString& aInput,
- const Sequence<JS::Value>& aArguments) const;
+ const Sequence<JS::Value>& aArguments, ErrorResult& aErrorResult) const;
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicy-createscript
- UniquePtr<TrustedScript> CreateScript(
+ MOZ_CAN_RUN_SCRIPT UniquePtr<TrustedScript> CreateScript(
JSContext* aJSContext, const nsAString& aInput,
- const Sequence<JS::Value>& aArguments) const;
+ const Sequence<JS::Value>& aArguments, ErrorResult& aErrorResult) const;
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicy-createscripturl
- UniquePtr<TrustedScriptURL> CreateScriptURL(
+ MOZ_CAN_RUN_SCRIPT UniquePtr<TrustedScriptURL> CreateScriptURL(
JSContext* aJSContext, const nsAString& aInput,
- const Sequence<JS::Value>& aArguments) const;
+ const Sequence<JS::Value>& aArguments, ErrorResult& aErrorResult) const;
private:
// Required because this class is ref-counted.
virtual ~TrustedTypePolicy() = default;
+ // https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-create-a-trusted-type
+ template <typename T, typename CallbackObject>
+ MOZ_CAN_RUN_SCRIPT UniquePtr<T> CreateTrustedType(
+ const RefPtr<CallbackObject>& aCallbackObject, const nsAString& aValue,
+ const Sequence<JS::Value>& aArguments, ErrorResult& aErrorResult) const;
+
+ // https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-get-trusted-type-policy-value
+ //
+ // @param aResult may become void.
+ template <typename CallbackObject>
+ MOZ_CAN_RUN_SCRIPT void DetermineTrustedPolicyValue(
+ const RefPtr<CallbackObject>& aCallbackObject, const nsAString& aValue,
+ const Sequence<JS::Value>& aArguments, bool aThrowIfMissing,
+ ErrorResult& aErrorResult, nsAString& aResult) const;
RefPtr<TrustedTypePolicyFactory> mParentObject;
+
+ const nsString mName;
+
+ Options mOptions;
};
} // namespace mozilla::dom
diff --git a/dom/security/trusted-types/TrustedTypePolicyFactory.cpp b/dom/security/trusted-types/TrustedTypePolicyFactory.cpp
index 448c51eb3b..c2544124e3 100644
--- a/dom/security/trusted-types/TrustedTypePolicyFactory.cpp
+++ b/dom/security/trusted-types/TrustedTypePolicyFactory.cpp
@@ -22,10 +22,50 @@ JSObject* TrustedTypePolicyFactory::WrapObject(
already_AddRefed<TrustedTypePolicy> TrustedTypePolicyFactory::CreatePolicy(
const nsAString& aPolicyName,
const TrustedTypePolicyOptions& aPolicyOptions) {
- // TODO: implement the spec.
- return MakeRefPtr<TrustedTypePolicy>(this).forget();
+ // TODO: add CSP support.
+
+ // TODO: add default policy support; this requires accessing the default
+ // policy on the C++ side, hence already now ref-counting policy
+ // objects.
+
+ TrustedTypePolicy::Options options;
+
+ if (aPolicyOptions.mCreateHTML.WasPassed()) {
+ options.mCreateHTMLCallback = &aPolicyOptions.mCreateHTML.Value();
+ }
+
+ if (aPolicyOptions.mCreateScript.WasPassed()) {
+ options.mCreateScriptCallback = &aPolicyOptions.mCreateScript.Value();
+ }
+
+ if (aPolicyOptions.mCreateScriptURL.WasPassed()) {
+ options.mCreateScriptURLCallback = &aPolicyOptions.mCreateScriptURL.Value();
+ }
+
+ RefPtr<TrustedTypePolicy> policy =
+ MakeRefPtr<TrustedTypePolicy>(this, aPolicyName, std::move(options));
+
+ mCreatedPolicyNames.AppendElement(aPolicyName);
+
+ return policy.forget();
}
+#define IS_TRUSTED_TYPE_IMPL(_trustedTypeSuffix) \
+ bool TrustedTypePolicyFactory::Is##_trustedTypeSuffix( \
+ JSContext*, const JS::Handle<JS::Value>& aValue) const { \
+ /** \
+ * No need to check the internal slot. \
+ * Ensured by the corresponding test: \
+ * <https://searchfox.org/mozilla-central/rev/b60cb73160843adb5a5a3ec8058e75a69b46acf7/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-isXXX.html> \
+ */ \
+ return aValue.isObject() && \
+ IS_INSTANCE_OF(Trusted##_trustedTypeSuffix, &aValue.toObject()); \
+ }
+
+IS_TRUSTED_TYPE_IMPL(HTML);
+IS_TRUSTED_TYPE_IMPL(Script);
+IS_TRUSTED_TYPE_IMPL(ScriptURL);
+
UniquePtr<TrustedHTML> TrustedTypePolicyFactory::EmptyHTML() {
// Preserving the wrapper ensures:
// ```
@@ -37,14 +77,14 @@ UniquePtr<TrustedHTML> TrustedTypePolicyFactory::EmptyHTML() {
// multiple emptyHML objects. Both, the JS- and the C++-objects.
dom::PreserveWrapper(this);
- return MakeUnique<TrustedHTML>();
+ return MakeUnique<TrustedHTML>(EmptyString());
}
UniquePtr<TrustedScript> TrustedTypePolicyFactory::EmptyScript() {
// See the explanation in `EmptyHTML()`.
dom::PreserveWrapper(this);
- return MakeUnique<TrustedScript>();
+ return MakeUnique<TrustedScript>(EmptyString());
}
} // namespace mozilla::dom
diff --git a/dom/security/trusted-types/TrustedTypePolicyFactory.h b/dom/security/trusted-types/TrustedTypePolicyFactory.h
index fea5312cf8..61dae94ed9 100644
--- a/dom/security/trusted-types/TrustedTypePolicyFactory.h
+++ b/dom/security/trusted-types/TrustedTypePolicyFactory.h
@@ -8,13 +8,16 @@
#define DOM_SECURITY_TRUSTED_TYPES_TRUSTEDTYPEPOLICYFACTORY_H_
#include "js/TypeDecls.h"
+#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/TrustedHTML.h"
#include "mozilla/dom/TrustedScript.h"
+#include "mozilla/dom/TrustedScriptURL.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsIGlobalObject.h"
#include "nsISupportsImpl.h"
#include "nsStringFwd.h"
+#include "nsTArray.h"
#include "nsWrapperCache.h"
template <typename T>
@@ -48,25 +51,15 @@ class TrustedTypePolicyFactory : public nsWrapperCache {
const TrustedTypePolicyOptions& aPolicyOptions);
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-ishtml
- bool IsHTML(JSContext* aJSContext,
- const JS::Handle<JS::Value>& aValue) const {
- // TODO: impl.
- return false;
- }
+ bool IsHTML(JSContext* aJSContext, const JS::Handle<JS::Value>& aValue) const;
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-isscript
bool IsScript(JSContext* aJSContext,
- const JS::Handle<JS::Value>& aValue) const {
- // TODO: impl.
- return false;
- }
+ const JS::Handle<JS::Value>& aValue) const;
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-isscripturl
bool IsScriptURL(JSContext* aJSContext,
- const JS::Handle<JS::Value>& aValue) const {
- // TODO: impl.
- return false;
- }
+ const JS::Handle<JS::Value>& aValue) const;
// https://w3c.github.io/trusted-types/dist/spec/#dom-trustedtypepolicyfactory-emptyhtml
UniquePtr<TrustedHTML> EmptyHTML();
@@ -98,6 +91,8 @@ class TrustedTypePolicyFactory : public nsWrapperCache {
virtual ~TrustedTypePolicyFactory() = default;
RefPtr<nsIGlobalObject> mGlobalObject;
+
+ nsTArray<nsString> mCreatedPolicyNames;
};
} // namespace mozilla::dom
diff --git a/dom/security/trusted-types/TrustedTypeUtils.h b/dom/security/trusted-types/TrustedTypeUtils.h
index 90ffc50c38..508c26c3c2 100644
--- a/dom/security/trusted-types/TrustedTypeUtils.h
+++ b/dom/security/trusted-types/TrustedTypeUtils.h
@@ -10,20 +10,26 @@
#include "mozilla/dom/DOMString.h"
#include "mozilla/dom/NonRefcountedDOMObject.h"
#include "mozilla/dom/TrustedTypesBinding.h"
-#include "nsStringFwd.h"
+#include "nsString.h"
#define DECL_TRUSTED_TYPE_CLASS(_class) \
class _class : public mozilla::dom::NonRefcountedDOMObject { \
public: \
+ explicit _class(const nsAString& aData) : mData{aData} {} \
+ \
/* Required for Web IDL binding. */ \
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, \
JS::MutableHandle<JSObject*> aObject); \
\
- void Stringify(nsAString& aResult) const { /* TODO: impl. */ \
+ void Stringify(DOMString& aResult) const { \
+ aResult.SetKnownLiveString(mData); \
} \
\
- void ToJSON(DOMString& aResult) const { /* TODO: impl. */ \
+ void ToJSON(DOMString& aResult) const { \
+ aResult.SetKnownLiveString(mData); \
} \
+ \
+ const nsString mData; \
};
#define IMPL_TRUSTED_TYPE_CLASS(_class) \