Modernize some aspects of text codecs, eliminate WebKit use of strcasecmp
[WebKit-https.git] / Source / WebCore / page / csp / ContentSecurityPolicy.cpp
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "ContentSecurityPolicy.h"
29
30 #include "ContentSecurityPolicyDirective.h"
31 #include "ContentSecurityPolicyDirectiveList.h"
32 #include "ContentSecurityPolicyDirectiveNames.h"
33 #include "ContentSecurityPolicyHash.h"
34 #include "ContentSecurityPolicySource.h"
35 #include "ContentSecurityPolicySourceList.h"
36 #include "DOMStringList.h"
37 #include "Document.h"
38 #include "DocumentLoader.h"
39 #include "EventNames.h"
40 #include "FormData.h"
41 #include "Frame.h"
42 #include "HTMLParserIdioms.h"
43 #include "InspectorInstrumentation.h"
44 #include "JSDOMWindowProxy.h"
45 #include "JSMainThreadExecState.h"
46 #include "ParsingUtilities.h"
47 #include "PingLoader.h"
48 #include "ResourceRequest.h"
49 #include "RuntimeEnabledFeatures.h"
50 #include "SchemeRegistry.h"
51 #include "SecurityOrigin.h"
52 #include "SecurityPolicyViolationEvent.h"
53 #include "Settings.h"
54 #include "TextEncoding.h"
55 #include <inspector/ScriptCallStack.h>
56 #include <inspector/ScriptCallStackFactory.h>
57 #include <pal/crypto/CryptoDigest.h>
58 #include <wtf/JSONValues.h>
59 #include <wtf/SetForScope.h>
60 #include <wtf/text/StringBuilder.h>
61 #include <wtf/text/TextPosition.h>
62
63
64 namespace WebCore {
65 using namespace Inspector;
66
67 static String consoleMessageForViolation(const char* effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const char* prefix, const char* subject = "it")
68 {
69     StringBuilder result;
70     if (violatedDirective.directiveList().isReportOnly())
71         result.appendLiteral("[Report Only] ");
72     result.append(prefix);
73     if (!blockedURL.isEmpty()) {
74         result.append(' ');
75         result.append(blockedURL.stringCenterEllipsizedToLength());
76     }
77     result.appendLiteral(" because ");
78     result.append(subject);
79     if (violatedDirective.isDefaultSrc()) {
80         result.appendLiteral(" appears in neither the ");
81         result.append(effectiveViolatedDirective);
82         result.appendLiteral(" directive nor the default-src directive of the Content Security Policy.");
83     } else {
84         result.appendLiteral(" does not appear in the ");
85         result.append(effectiveViolatedDirective);
86         result.appendLiteral(" directive of the Content Security Policy.");
87     }
88     return result.toString();
89 }
90
91 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext& scriptExecutionContext)
92     : m_scriptExecutionContext(&scriptExecutionContext)
93     , m_sandboxFlags(SandboxNone)
94 {
95     ASSERT(scriptExecutionContext.securityOrigin());
96     updateSourceSelf(*scriptExecutionContext.securityOrigin());
97 }
98
99 ContentSecurityPolicy::ContentSecurityPolicy(const SecurityOrigin& securityOrigin, const Frame* frame)
100     : m_frame(frame)
101     , m_sandboxFlags(SandboxNone)
102 {
103     updateSourceSelf(securityOrigin);
104 }
105
106 ContentSecurityPolicy::~ContentSecurityPolicy() = default;
107
108 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 
109 {
110     if (m_hasAPIPolicy)
111         return;
112     ASSERT(m_policies.isEmpty());
113     for (auto& policy : other->m_policies)
114         didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::Inherited);
115 }
116
117 void ContentSecurityPolicy::copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy& other)
118 {
119     m_upgradeInsecureRequests = other.m_upgradeInsecureRequests;
120     m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end());
121 }
122
123 bool ContentSecurityPolicy::allowRunningOrDisplayingInsecureContent(const URL& url)
124 {
125     bool allow = true;
126     bool isReportOnly = false;
127     for (auto& policy : m_policies) {
128         if (!policy->hasBlockAllMixedContentDirective())
129             continue;
130
131         isReportOnly = policy->isReportOnly();
132
133         StringBuilder consoleMessage;
134         if (isReportOnly)
135             consoleMessage.appendLiteral("[Report Only] ");
136         consoleMessage.append("Blocked mixed content ");
137         consoleMessage.append(url.stringCenterEllipsizedToLength());
138         consoleMessage.appendLiteral(" because ");
139         consoleMessage.append("'block-all-mixed-content' appears in the Content Security Policy.");
140         reportViolation(ContentSecurityPolicyDirectiveNames::blockAllMixedContent, ContentSecurityPolicyDirectiveNames::blockAllMixedContent, *policy, url, consoleMessage.toString());
141
142         if (!isReportOnly)
143             allow = false;
144     }
145     return allow;
146 }
147
148 void ContentSecurityPolicy::didCreateWindowProxy(JSDOMWindowProxy& windowProxy) const
149 {
150     auto* window = windowProxy.window();
151     ASSERT(window);
152     ASSERT(window->scriptExecutionContext());
153     ASSERT(window->scriptExecutionContext()->contentSecurityPolicy() == this);
154     if (!windowProxy.world().isNormal()) {
155         window->setEvalEnabled(true);
156         return;
157     }
158     window->setEvalEnabled(m_lastPolicyEvalDisabledErrorMessage.isNull(), m_lastPolicyEvalDisabledErrorMessage);
159     window->setWebAssemblyEnabled(m_lastPolicyWebAssemblyDisabledErrorMessage.isNull(), m_lastPolicyWebAssemblyDisabledErrorMessage);
160 }
161
162 ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() const
163 {
164     if (!m_cachedResponseHeaders) {
165         ContentSecurityPolicyResponseHeaders result;
166         result.m_headers.reserveInitialCapacity(m_policies.size());
167         for (auto& policy : m_policies)
168             result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() });
169         m_cachedResponseHeaders = WTFMove(result);
170     }
171     return *m_cachedResponseHeaders;
172 }
173
174 void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers, ReportParsingErrors reportParsingErrors)
175 {
176     SetForScope<bool> isReportingEnabled(m_isReportingEnabled, reportParsingErrors == ReportParsingErrors::Yes);
177     for (auto& header : headers.m_headers)
178         didReceiveHeader(header.first, header.second, ContentSecurityPolicy::PolicyFrom::HTTPHeader);
179 }
180
181 void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom)
182 {
183     if (m_hasAPIPolicy)
184         return;
185
186     if (policyFrom == PolicyFrom::API) {
187         ASSERT(m_policies.isEmpty());
188         m_hasAPIPolicy = true;
189     }
190
191     m_cachedResponseHeaders = std::nullopt;
192
193     // RFC2616, section 4.2 specifies that headers appearing multiple times can
194     // be combined with a comma. Walk the header string, and parse each comma
195     // separated chunk as a separate header.
196     auto characters = StringView(header).upconvertedCharacters();
197     const UChar* begin = characters;
198     const UChar* position = begin;
199     const UChar* end = begin + header.length();
200     while (position < end) {
201         skipUntil<UChar>(position, end, ',');
202
203         // header1,header2 OR header1
204         //        ^                  ^
205         m_policies.append(ContentSecurityPolicyDirectiveList::create(*this, String(begin, position - begin), type, policyFrom));
206
207         // Skip the comma, and begin the next header from the current position.
208         ASSERT(position == end || *position == ',');
209         skipExactly<UChar>(position, end, ',');
210         begin = position;
211     }
212
213     if (m_scriptExecutionContext)
214         applyPolicyToScriptExecutionContext();
215 }
216
217 void ContentSecurityPolicy::updateSourceSelf(const SecurityOrigin& securityOrigin)
218 {
219     m_selfSourceProtocol = securityOrigin.protocol();
220     m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false);
221 }
222
223 void ContentSecurityPolicy::applyPolicyToScriptExecutionContext()
224 {
225     ASSERT(m_scriptExecutionContext);
226
227     // Update source self as the security origin may have changed between the time we were created and now.
228     // For instance, we may have been initially created for an about:blank iframe that later inherited the
229     // security origin of its owner document.
230     ASSERT(m_scriptExecutionContext->securityOrigin());
231     updateSourceSelf(*m_scriptExecutionContext->securityOrigin());
232
233     bool enableStrictMixedContentMode = false;
234     for (auto& policy : m_policies) {
235         const ContentSecurityPolicyDirective* violatedDirective = policy->violatedDirectiveForUnsafeEval();
236         if (violatedDirective && !violatedDirective->directiveList().isReportOnly()) {
237             m_lastPolicyEvalDisabledErrorMessage = policy->evalDisabledErrorMessage();
238             m_lastPolicyWebAssemblyDisabledErrorMessage = policy->webAssemblyDisabledErrorMessage();
239         }
240         if (policy->hasBlockAllMixedContentDirective() && !policy->isReportOnly())
241             enableStrictMixedContentMode = true;
242     }
243
244     if (!m_lastPolicyEvalDisabledErrorMessage.isNull())
245         m_scriptExecutionContext->disableEval(m_lastPolicyEvalDisabledErrorMessage);
246     if (!m_lastPolicyWebAssemblyDisabledErrorMessage.isNull())
247         m_scriptExecutionContext->disableWebAssembly(m_lastPolicyWebAssemblyDisabledErrorMessage);
248     if (m_sandboxFlags != SandboxNone && is<Document>(m_scriptExecutionContext))
249         m_scriptExecutionContext->enforceSandboxFlags(m_sandboxFlags);
250     if (enableStrictMixedContentMode)
251         m_scriptExecutionContext->setStrictMixedContentMode(true);
252 }
253
254 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
255 {
256     m_overrideInlineStyleAllowed = value;
257 }
258
259 bool ContentSecurityPolicy::urlMatchesSelf(const URL& url) const
260 {
261     return m_selfSource->matches(url);
262 }
263
264 bool ContentSecurityPolicy::allowContentSecurityPolicySourceStarToMatchAnyProtocol() const
265 {
266     if (is<Document>(m_scriptExecutionContext))
267         return downcast<Document>(*m_scriptExecutionContext).settings().allowContentSecurityPolicySourceStarToMatchAnyProtocol();
268     return false;
269 }
270
271 bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const
272 {
273     if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http"))
274         return url.protocolIsInHTTPFamily();
275     return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol);
276 }
277
278 template<typename Predicate, typename... Args>
279 typename std::enable_if<!std::is_convertible<Predicate, ContentSecurityPolicy::ViolatedDirectiveCallback>::value, bool>::type ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, Predicate&& predicate, Args&&... args) const
280 {
281     bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly;
282     for (auto& policy : m_policies) {
283         if (policy->isReportOnly() != isReportOnly)
284             continue;
285         if ((policy.get()->*predicate)(std::forward<Args>(args)...))
286             return false;
287     }
288     return true;
289 }
290
291 template<typename Predicate, typename... Args>
292 bool ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const
293 {
294     bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly;
295     bool isAllowed = true;
296     for (auto& policy : m_policies) {
297         if (policy->isReportOnly() != isReportOnly)
298             continue;
299         if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) {
300             isAllowed = false;
301             callback(*violatedDirective);
302         }
303     }
304     return isAllowed;
305 }
306
307 template<typename Predicate, typename... Args>
308 bool ContentSecurityPolicy::allPoliciesAllow(ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const
309 {
310     bool isAllowed = true;
311     for (auto& policy : m_policies) {
312         if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) {
313             if (!violatedDirective->directiveList().isReportOnly())
314                 isAllowed = false;
315             callback(*violatedDirective);
316         }
317     }
318     return isAllowed;
319 }
320
321 template<typename Predicate>
322 ContentSecurityPolicy::HashInEnforcedAndReportOnlyPoliciesPair ContentSecurityPolicy::findHashOfContentInPolicies(Predicate&& predicate, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm> algorithms) const
323 {
324     if (algorithms.isEmpty() || content.isEmpty())
325         return { false, false };
326
327     // FIXME: We should compute the document encoding once and cache it instead of computing it on each invocation.
328     TextEncoding documentEncoding;
329     if (is<Document>(m_scriptExecutionContext))
330         documentEncoding = downcast<Document>(*m_scriptExecutionContext).textEncoding();
331     const TextEncoding& encodingToUse = documentEncoding.isValid() ? documentEncoding : UTF8Encoding();
332
333     // FIXME: Compute the digest with respect to the raw bytes received from the page.
334     // See <https://bugs.webkit.org/show_bug.cgi?id=155184>.
335     auto encodedContent = encodingToUse.encode(content, UnencodableHandling::Entities);
336     bool foundHashInEnforcedPolicies = false;
337     bool foundHashInReportOnlyPolicies = false;
338     for (auto algorithm : algorithms) {
339         ContentSecurityPolicyHash hash = cryptographicDigestForBytes(algorithm, encodedContent.data(), encodedContent.size());
340         if (!foundHashInEnforcedPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, std::forward<Predicate>(predicate), hash))
341             foundHashInEnforcedPolicies = true;
342         if (!foundHashInReportOnlyPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, std::forward<Predicate>(predicate), hash))
343             foundHashInReportOnlyPolicies = true;
344         if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
345             break;
346     }
347     return { foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies };
348 }
349
350 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
351 {
352     if (overrideContentSecurityPolicy)
353         return true;
354     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
355         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
356         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
357         if (!violatedDirective.directiveList().isReportOnly())
358             reportBlockedScriptExecutionToInspector(violatedDirective.text());
359     };
360     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
361 }
362
363 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const
364 {
365     if (overrideContentSecurityPolicy)
366         return true;
367     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
368         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script for an inline event handler", "'unsafe-inline'");
369         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
370         if (!violatedDirective.directiveList().isReportOnly())
371             reportBlockedScriptExecutionToInspector(violatedDirective.text());
372     };
373     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
374 }
375
376 bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
377 {
378     if (overrideContentSecurityPolicy)
379         return true;
380     String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
381     if (strippedNonce.isEmpty())
382         return false;
383     // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>.
384     return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce, strippedNonce);
385 }
386
387 bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const
388 {
389     if (overrideContentSecurityPolicy)
390         return true;
391     String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce);
392     if (strippedNonce.isEmpty())
393         return false;
394     // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>.
395     return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce, strippedNonce);
396 }
397
398 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy) const
399 {
400     if (overrideContentSecurityPolicy)
401         return true;
402     bool foundHashInEnforcedPolicies;
403     bool foundHashInReportOnlyPolicies;
404     std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash, scriptContent, m_hashAlgorithmsForInlineScripts);
405     if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
406         return true;
407     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
408         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'");
409         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
410         if (!violatedDirective.directiveList().isReportOnly())
411             reportBlockedScriptExecutionToInspector(violatedDirective.text());
412     };
413     // FIXME: We should not report that the inline script violated a policy when its hash matched a source
414     // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>.
415     if (!foundHashInReportOnlyPolicies)
416         allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
417     return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript);
418 }
419
420 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy) const
421 {
422     if (overrideContentSecurityPolicy)
423         return true;
424     if (m_overrideInlineStyleAllowed)
425         return true;
426     bool foundHashInEnforcedPolicies;
427     bool foundHashInReportOnlyPolicies;
428     std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash, styleContent, m_hashAlgorithmsForInlineStylesheets);
429     if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies)
430         return true;
431     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
432         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), "Refused to apply a stylesheet", "its hash, its nonce, or 'unsafe-inline'");
433         reportViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber()));
434     };
435     // FIXME: We should not report that the inline stylesheet violated a policy when its hash matched a source
436     // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>.
437     if (!foundHashInReportOnlyPolicies)
438         allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
439     return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle);
440 }
441
442 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy) const
443 {
444     if (overrideContentSecurityPolicy)
445         return true;
446     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
447         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "'unsafe-eval'");
448         reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, state);
449         if (!violatedDirective.directiveList().isReportOnly())
450             reportBlockedScriptExecutionToInspector(violatedDirective.text());
451     };
452     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval);
453 }
454
455 bool ContentSecurityPolicy::allowFrameAncestors(const Frame& frame, const URL& url, bool overrideContentSecurityPolicy) const
456 {
457     if (overrideContentSecurityPolicy)
458         return true;
459     Frame& topFrame = frame.tree().top();
460     if (&frame == &topFrame)
461         return true;
462     String sourceURL;
463     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
464     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
465         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, "Refused to load");
466         reportViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
467     };
468     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor, frame);
469 }
470
471 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy) const
472 {
473     if (overrideContentSecurityPolicy)
474         return true;
475     String sourceURL;
476     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
477     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
478         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, "Refused to load", "its MIME type");
479         reportViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
480     };
481     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType, type, typeAttribute);
482 }
483
484 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
485 {
486     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
487         return true;
488     // As per section object-src of the Content Security Policy Level 3 spec., <http://w3c.github.io/webappsec-csp> (Editor's Draft, 29 February 2016),
489     // "If plugin content is loaded without an associated URL (perhaps an object element lacks a data attribute, but loads some default plugin based
490     // on the specified type), it MUST be blocked if object-src's value is 'none', but will otherwise be allowed".
491     String sourceURL;
492     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
493     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
494         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, "Refused to load");
495         reportViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
496     };
497     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes);
498 }
499
500 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
501 {
502     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
503         return true;
504     String sourceURL;
505     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
506     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
507         const char* effectiveViolatedDirective = violatedDirective.name() == ContentSecurityPolicyDirectiveNames::frameSrc ? ContentSecurityPolicyDirectiveNames::frameSrc : ContentSecurityPolicyDirectiveNames::childSrc;
508         String consoleMessage = consoleMessageForViolation(effectiveViolatedDirective, violatedDirective, url, "Refused to load");
509         reportViolation(effectiveViolatedDirective, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
510     };
511     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame, url, redirectResponseReceived == RedirectResponseReceived::Yes);
512 }
513
514 bool ContentSecurityPolicy::allowResourceFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const char* name, ResourcePredicate resourcePredicate) const
515 {
516     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
517         return true;
518     String sourceURL;
519     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
520     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
521         String consoleMessage = consoleMessageForViolation(name, violatedDirective, url, "Refused to load");
522         reportViolation(name, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
523     };
524     return allPoliciesAllow(WTFMove(handleViolatedDirective), resourcePredicate, url, redirectResponseReceived == RedirectResponseReceived::Yes);
525 }
526
527 bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
528 {
529     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::childSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext);
530 }
531
532 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
533 {
534     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::scriptSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScript);
535 }
536
537 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
538 {
539     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::imgSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForImage);
540 }
541
542 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
543 {
544     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::styleSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle);
545 }
546
547 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
548 {
549     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::fontSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFont);
550 }
551
552 #if ENABLE(APPLICATION_MANIFEST)
553 bool ContentSecurityPolicy::allowManifestFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
554 {
555     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::manifestSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForManifest);
556 }
557 #endif // ENABLE(APPLICATION_MANIFEST)
558
559 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
560 {
561     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::mediaSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia);
562 }
563
564 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const
565 {
566     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
567         return true;
568     String sourceURL;
569     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
570     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
571         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, "Refused to connect to");
572         reportViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
573     };
574     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes);
575 }
576
577 bool ContentSecurityPolicy::allowFormAction(const URL& url, RedirectResponseReceived redirectResponseReceived) const
578 {
579     return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::formAction, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction);
580 }
581
582 bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy) const
583 {
584     if (overrideContentSecurityPolicy)
585         return true;
586     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying()))
587         return true;
588     String sourceURL;
589     TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber());
590     auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) {
591         String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, "Refused to change the document base URL to");
592         reportViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, consoleMessage, sourceURL, sourcePosition);
593     };
594     return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI, url);
595 }
596
597 static String stripURLForUseInReport(Document& document, const URL& url)
598 {
599     if (!url.isValid())
600         return String();
601     if (!url.isHierarchical() || url.protocolIs("file"))
602         return url.protocol().toString();
603     return document.securityOrigin().canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString();
604 }
605
606 void ContentSecurityPolicy::reportViolation(const String& violatedDirective, const ContentSecurityPolicyDirective& effectiveViolatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
607 {
608     // FIXME: Extract source file and source position from JSC::ExecState.
609     return reportViolation(violatedDirective, effectiveViolatedDirective.text(), effectiveViolatedDirective.directiveList(), blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
610 }
611
612 void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const
613 {
614     // FIXME: Extract source file and source position from JSC::ExecState.
615     return reportViolation(effectiveViolatedDirective, violatedDirective, violatedDirectiveList, blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state);
616 }
617
618 void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* state) const
619 {
620     return reportViolation(effectiveViolatedDirective, violatedDirective.text(), violatedDirective.directiveList(), blockedURL, consoleMessage, sourceURL, sourcePosition, state);
621 }
622
623 void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* state) const
624 {
625     logToConsole(consoleMessage, sourceURL, sourcePosition.m_line, state);
626
627     if (!m_isReportingEnabled)
628         return;
629
630     // FIXME: Support sending reports from worker.
631     if (!is<Document>(m_scriptExecutionContext) && !m_frame)
632         return;
633
634     ASSERT(!m_frame || effectiveViolatedDirective == ContentSecurityPolicyDirectiveNames::frameAncestors);
635
636     auto& document = is<Document>(m_scriptExecutionContext) ? downcast<Document>(*m_scriptExecutionContext) : *m_frame->document();
637     auto* frame = document.frame();
638     ASSERT(!m_frame || m_frame == frame);
639     if (!frame)
640         return;
641
642     String documentURI;
643     String blockedURI;
644     if (is<Document>(m_scriptExecutionContext)) {
645         documentURI = document.url().strippedForUseAsReferrer();
646         blockedURI = stripURLForUseInReport(document, blockedURL);
647     } else {
648         // The URL of |document| may not have been initialized (say, when reporting a frame-ancestors violation).
649         // So, we use the URL of the blocked document for the protected document URL.
650         documentURI = blockedURL;
651         blockedURI = blockedURL;
652     }
653     String violatedDirectiveText = violatedDirective;
654     String originalPolicy = violatedDirectiveList.header();
655     String referrer = document.referrer();
656     ASSERT(document.loader());
657     // FIXME: Is it policy to not use the status code for HTTPS, or is that a bug?
658     unsigned short statusCode = document.url().protocolIs("http") && document.loader() ? document.loader()->response().httpStatusCode() : 0;
659
660     String sourceFile;
661     int lineNumber = 0;
662     int columnNumber = 0;
663     auto stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
664     auto* callFrame = stack->firstNonNativeCallFrame();
665     if (callFrame && callFrame->lineNumber()) {
666         sourceFile = stripURLForUseInReport(document, URL(URL(), callFrame->sourceURL()));
667         lineNumber = callFrame->lineNumber();
668         columnNumber = callFrame->columnNumber();
669     }
670
671     // 1. Dispatch violation event.
672     bool canBubble = false;
673     bool cancelable = false;
674     document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, canBubble, cancelable, documentURI, referrer, blockedURI, violatedDirectiveText, effectiveViolatedDirective, originalPolicy, sourceFile, statusCode, lineNumber, columnNumber));
675
676     // 2. Send violation report (if applicable).
677     auto& reportURIs = violatedDirectiveList.reportURIs();
678     if (reportURIs.isEmpty())
679         return;
680
681     // We need to be careful here when deciding what information to send to the
682     // report-uri. Currently, we send only the current document's URL and the
683     // directive that was violated. The document's URL is safe to send because
684     // it's the document itself that's requesting that it be sent. You could
685     // make an argument that we shouldn't send HTTPS document URLs to HTTP
686     // report-uris (for the same reasons that we suppress the Referer in that
687     // case), but the Referer is sent implicitly whereas this request is only
688     // sent explicitly. As for which directive was violated, that's pretty
689     // harmless information.
690
691     auto cspReport = JSON::Object::create();
692     cspReport->setString(ASCIILiteral("document-uri"), documentURI);
693     cspReport->setString(ASCIILiteral("referrer"), referrer);
694     cspReport->setString(ASCIILiteral("violated-directive"), violatedDirectiveText);
695     cspReport->setString(ASCIILiteral("effective-directive"), effectiveViolatedDirective);
696     cspReport->setString(ASCIILiteral("original-policy"), originalPolicy);
697     cspReport->setString(ASCIILiteral("blocked-uri"), blockedURI);
698     cspReport->setInteger(ASCIILiteral("status-code"), statusCode);
699     if (!sourceFile.isNull()) {
700         cspReport->setString(ASCIILiteral("source-file"), sourceFile);
701         cspReport->setInteger(ASCIILiteral("line-number"), lineNumber);
702         cspReport->setInteger(ASCIILiteral("column-number"), columnNumber);
703     }
704
705     auto reportObject = JSON::Object::create();
706     reportObject->setObject(ASCIILiteral("csp-report"), WTFMove(cspReport));
707
708     auto report = FormData::create(reportObject->toJSONString().utf8());
709     for (const auto& url : reportURIs)
710         PingLoader::sendViolationReport(*frame, is<Document>(m_scriptExecutionContext) ? document.completeURL(url) : document.completeURL(url, blockedURL), report.copyRef(), ViolationReportType::ContentSecurityPolicy);
711 }
712
713 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
714 {
715     String message;
716     if (equalLettersIgnoringASCIICase(name, "allow"))
717         message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.");
718     else if (equalLettersIgnoringASCIICase(name, "options"))
719         message = ASCIILiteral("The 'options' directive has been replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 'script-src' and 'style-src' directives. Please use those directives instead, as 'options' has no effect.");
720     else if (equalLettersIgnoringASCIICase(name, "policy-uri"))
721         message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.");
722     else
723         message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline?
724
725     logToConsole(message);
726 }
727
728 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
729 {
730     logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?");
731 }
732
733 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
734 {
735     logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"));
736 }
737
738 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
739 {
740     String message;
741     if (pluginType.isNull())
742         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
743     else
744         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
745     logToConsole(message);
746 }
747
748 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
749 {
750     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
751 }
752
753 void ContentSecurityPolicy::reportInvalidDirectiveInReportOnlyMode(const String& directiveName) const
754 {
755     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered in a report-only policy.");
756 }
757
758 void ContentSecurityPolicy::reportInvalidDirectiveInHTTPEquivMeta(const String& directiveName) const
759 {
760     logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered via an HTML meta element.");
761 }
762
763 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
764 {
765     String message = makeString("The value for Content Security Policy directive '", directiveName, "' contains an invalid character: '", value, "'. Non-whitespace characters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1.");
766     logToConsole(message);
767 }
768
769 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
770 {
771     ASSERT(invalidChar == '#' || invalidChar == '?');
772
773     String ignoring;
774     if (invalidChar == '?')
775         ignoring = "The query component, including the '?', will be ignored.";
776     else
777         ignoring = "The fragment identifier, including the '#', will be ignored.";
778
779     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
780     logToConsole(message);
781 }
782
783 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
784 {
785     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
786     if (equalLettersIgnoringASCIICase(source, "'none'"))
787         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
788     logToConsole(message);
789 }
790
791 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
792 {
793     logToConsole("The Content Security Policy '" + policy + "' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header.");
794 }
795
796 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
797 {
798     if (!m_isReportingEnabled)
799         return;
800
801     // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
802     if (m_scriptExecutionContext)
803         m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
804     else if (m_frame && m_frame->document())
805         static_cast<ScriptExecutionContext*>(m_frame->document())->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
806 }
807
808 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
809 {
810     if (m_scriptExecutionContext)
811         InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
812 }
813
814 void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(ResourceRequest& request, InsecureRequestType requestType) const
815 {
816     URL url = request.url();
817     upgradeInsecureRequestIfNeeded(url, requestType);
818     request.setURL(url);
819 }
820
821 void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(URL& url, InsecureRequestType requestType) const
822 {
823     if (!url.protocolIs("http") && !url.protocolIs("ws"))
824         return;
825
826     bool upgradeRequest = m_insecureNavigationRequestsToUpgrade.contains(SecurityOrigin::create(url));
827     if (requestType == InsecureRequestType::Load || requestType == InsecureRequestType::FormSubmission)
828         upgradeRequest |= m_upgradeInsecureRequests;
829     
830     if (!upgradeRequest)
831         return;
832
833     if (url.protocolIs("http"))
834         url.setProtocol("https");
835     else if (url.protocolIs("ws"))
836         url.setProtocol("wss");
837     else
838         return;
839     
840     if (url.port() && url.port().value() == 80)
841         url.setPort(443);
842 }
843
844 void ContentSecurityPolicy::setUpgradeInsecureRequests(bool upgradeInsecureRequests)
845 {
846     m_upgradeInsecureRequests = upgradeInsecureRequests;
847     if (!m_upgradeInsecureRequests)
848         return;
849
850     if (!m_scriptExecutionContext)
851         return;
852
853     // Store the upgrade domain as an 'insecure' protocol so we can quickly identify
854     // origins we should upgrade.
855     URL upgradeURL = m_scriptExecutionContext->url();
856     if (upgradeURL.protocolIs("https"))
857         upgradeURL.setProtocol("http");
858     else if (upgradeURL.protocolIs("wss"))
859         upgradeURL.setProtocol("ws");
860     
861     m_insecureNavigationRequestsToUpgrade.add(SecurityOrigin::create(upgradeURL));
862 }
863
864 void ContentSecurityPolicy::inheritInsecureNavigationRequestsToUpgradeFromOpener(const ContentSecurityPolicy& other)
865 {
866     m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end());
867 }
868
869 HashSet<RefPtr<SecurityOrigin>>&& ContentSecurityPolicy::takeNavigationRequestsToUpgrade()
870 {
871     return WTFMove(m_insecureNavigationRequestsToUpgrade);
872 }
873
874 void ContentSecurityPolicy::setInsecureNavigationRequestsToUpgrade(HashSet<RefPtr<SecurityOrigin>>&& insecureNavigationRequests)
875 {
876     m_insecureNavigationRequestsToUpgrade = WTFMove(insecureNavigationRequests);
877 }
878
879 }