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