57488b177a1c5d77b1daf80cb45d83a3c6545cb3
[WebKit-https.git] / Source / WebCore / page / ContentSecurityPolicy.cpp
1 /*
2  * Copyright (C) 2011 Google, Inc. All rights reserved.
3  * Copyright (C) 2013 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 "DOMStringList.h"
31 #include "Document.h"
32 #include "FormData.h"
33 #include "FormDataList.h"
34 #include "Frame.h"
35 #include "InspectorInstrumentation.h"
36 #include "JSMainThreadExecState.h"
37 #include "PingLoader.h"
38 #include "RuntimeEnabledFeatures.h"
39 #include "SchemeRegistry.h"
40 #include "ScriptState.h"
41 #include "SecurityOrigin.h"
42 #include "SecurityPolicyViolationEvent.h"
43 #include "TextEncoding.h"
44 #include "URL.h"
45 #include <inspector/InspectorValues.h>
46 #include <inspector/ScriptCallStack.h>
47 #include <inspector/ScriptCallStackFactory.h>
48 #include <wtf/HashSet.h>
49 #include <wtf/text/TextPosition.h>
50
51 using namespace Inspector;
52
53 namespace WebCore {
54
55 // Normally WebKit uses "static" for internal linkage, but using "static" for
56 // these functions causes a compile error because these functions are used as
57 // template parameters.
58 namespace {
59
60 bool isDirectiveNameCharacter(UChar c)
61 {
62     return isASCIIAlphanumeric(c) || c == '-';
63 }
64
65 bool isDirectiveValueCharacter(UChar c)
66 {
67     return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
68 }
69
70 bool isNonceCharacter(UChar c)
71 {
72     return (c >= 0x21 && c <= 0x7e) && c != ',' && c != ';'; // VCHAR - ',' - ';'
73 }
74
75 bool isSourceCharacter(UChar c)
76 {
77     return !isASCIISpace(c);
78 }
79
80 bool isPathComponentCharacter(UChar c)
81 {
82     return c != '?' && c != '#';
83 }
84
85 bool isHostCharacter(UChar c)
86 {
87     return isASCIIAlphanumeric(c) || c == '-';
88 }
89
90 bool isSchemeContinuationCharacter(UChar c)
91 {
92     return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.';
93 }
94
95 bool isNotASCIISpace(UChar c)
96 {
97     return !isASCIISpace(c);
98 }
99
100 bool isNotColonOrSlash(UChar c)
101 {
102     return c != ':' && c != '/';
103 }
104
105 bool isMediaTypeCharacter(UChar c)
106 {
107     return !isASCIISpace(c) && c != '/';
108 }
109
110 // CSP 1.0 Directives
111 static const char connectSrc[] = "connect-src";
112 static const char defaultSrc[] = "default-src";
113 static const char fontSrc[] = "font-src";
114 static const char frameSrc[] = "frame-src";
115 static const char imgSrc[] = "img-src";
116 static const char mediaSrc[] = "media-src";
117 static const char objectSrc[] = "object-src";
118 static const char reportURI[] = "report-uri";
119 static const char sandbox[] = "sandbox";
120 static const char scriptSrc[] = "script-src";
121 static const char styleSrc[] = "style-src";
122
123 // CSP 1.1 Directives
124 static const char baseURI[] = "base-uri";
125 static const char formAction[] = "form-action";
126 static const char pluginTypes[] = "plugin-types";
127 static const char scriptNonce[] = "script-nonce";
128 #if ENABLE(CSP_NEXT)
129 static const char reflectedXSS[] = "reflected-xss";
130 #endif
131
132 bool isDirectiveName(const String& name)
133 {
134     return (equalIgnoringCase(name, connectSrc)
135         || equalIgnoringCase(name, defaultSrc)
136         || equalIgnoringCase(name, fontSrc)
137         || equalIgnoringCase(name, frameSrc)
138         || equalIgnoringCase(name, imgSrc)
139         || equalIgnoringCase(name, mediaSrc)
140         || equalIgnoringCase(name, objectSrc)
141         || equalIgnoringCase(name, reportURI)
142         || equalIgnoringCase(name, sandbox)
143         || equalIgnoringCase(name, scriptSrc)
144         || equalIgnoringCase(name, styleSrc)
145 #if ENABLE(CSP_NEXT)
146         || equalIgnoringCase(name, baseURI)
147         || equalIgnoringCase(name, formAction)
148         || equalIgnoringCase(name, pluginTypes)
149         || equalIgnoringCase(name, scriptNonce)
150         || equalIgnoringCase(name, reflectedXSS)
151 #endif
152     );
153 }
154
155 } // namespace
156
157 static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter)
158 {
159     if (position < end && *position == delimiter) {
160         ++position;
161         return true;
162     }
163     return false;
164 }
165
166 template<bool characterPredicate(UChar)>
167 static bool skipExactly(const UChar*& position, const UChar* end)
168 {
169     if (position < end && characterPredicate(*position)) {
170         ++position;
171         return true;
172     }
173     return false;
174 }
175
176 static void skipUntil(const UChar*& position, const UChar* end, UChar delimiter)
177 {
178     while (position < end && *position != delimiter)
179         ++position;
180 }
181
182 template<bool characterPredicate(UChar)>
183 static void skipWhile(const UChar*& position, const UChar* end)
184 {
185     while (position < end && characterPredicate(*position))
186         ++position;
187 }
188
189 static bool isSourceListNone(const String& value)
190 {
191     auto characters = StringView(value).upconvertedCharacters();
192     const UChar* begin = characters;
193     const UChar* end = characters + value.length();
194     skipWhile<isASCIISpace>(begin, end);
195
196     const UChar* position = begin;
197     skipWhile<isSourceCharacter>(position, end);
198     if (!equalIgnoringCase("'none'", begin, position - begin))
199         return false;
200
201     skipWhile<isASCIISpace>(position, end);
202     if (position != end)
203         return false;
204
205     return true;
206 }
207
208 class CSPSource {
209 public:
210     CSPSource(ContentSecurityPolicy* policy, const String& scheme, const String& host, int port, const String& path, bool hostHasWildcard, bool portHasWildcard)
211         : m_policy(policy)
212         , m_scheme(scheme)
213         , m_host(host)
214         , m_port(port)
215         , m_path(path)
216         , m_hostHasWildcard(hostHasWildcard)
217         , m_portHasWildcard(portHasWildcard)
218     {
219     }
220
221     bool matches(const URL& url) const
222     {
223         if (!schemeMatches(url))
224             return false;
225         if (isSchemeOnly())
226             return true;
227         return hostMatches(url) && portMatches(url) && pathMatches(url);
228     }
229
230 private:
231     bool schemeMatches(const URL& url) const
232     {
233         if (m_scheme.isEmpty()) {
234             String protectedResourceScheme(m_policy->securityOrigin()->protocol());
235 #if ENABLE(CSP_NEXT)
236             if (equalIgnoringCase("http", protectedResourceScheme))
237                 return url.protocolIsInHTTPFamily();
238 #endif
239             return equalIgnoringCase(url.protocol(), protectedResourceScheme);
240         }
241         return equalIgnoringCase(url.protocol(), m_scheme);
242     }
243
244     bool hostMatches(const URL& url) const
245     {
246         const String& host = url.host();
247         if (equalIgnoringCase(host, m_host))
248             return true;
249         return m_hostHasWildcard && host.endsWith("." + m_host, false);
250
251     }
252
253     bool pathMatches(const URL& url) const
254     {
255         if (m_path.isEmpty())
256             return true;
257
258         String path = decodeURLEscapeSequences(url.path());
259
260         if (m_path.endsWith("/"))
261             return path.startsWith(m_path, false);
262
263         return path == m_path;
264     }
265
266     bool portMatches(const URL& url) const
267     {
268         if (m_portHasWildcard)
269             return true;
270
271         int port = url.port();
272
273         if (port == m_port)
274             return true;
275
276         if (!port)
277             return isDefaultPortForProtocol(m_port, url.protocol());
278
279         if (!m_port)
280             return isDefaultPortForProtocol(port, url.protocol());
281
282         return false;
283     }
284
285     bool isSchemeOnly() const { return m_host.isEmpty(); }
286
287     ContentSecurityPolicy* m_policy;
288     String m_scheme;
289     String m_host;
290     int m_port;
291     String m_path;
292
293     bool m_hostHasWildcard;
294     bool m_portHasWildcard;
295 };
296
297 class CSPSourceList {
298 public:
299     CSPSourceList(ContentSecurityPolicy*, const String& directiveName);
300
301     void parse(const String&);
302     bool matches(const URL&);
303     bool allowInline() const { return m_allowInline; }
304     bool allowEval() const { return m_allowEval; }
305
306 private:
307     void parse(const UChar* begin, const UChar* end);
308
309     bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, bool& hostHasWildcard, bool& portHasWildcard);
310     bool parseScheme(const UChar* begin, const UChar* end, String& scheme);
311     bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard);
312     bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard);
313     bool parsePath(const UChar* begin, const UChar* end, String& path);
314
315     void addSourceSelf();
316     void addSourceStar();
317     void addSourceUnsafeInline();
318     void addSourceUnsafeEval();
319
320     ContentSecurityPolicy* m_policy;
321     Vector<CSPSource> m_list;
322     String m_directiveName;
323     bool m_allowStar;
324     bool m_allowInline;
325     bool m_allowEval;
326 };
327
328 CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName)
329     : m_policy(policy)
330     , m_directiveName(directiveName)
331     , m_allowStar(false)
332     , m_allowInline(false)
333     , m_allowEval(false)
334 {
335 }
336
337 void CSPSourceList::parse(const String& value)
338 {
339     // We represent 'none' as an empty m_list.
340     if (isSourceListNone(value))
341         return;
342     auto characters = StringView(value).upconvertedCharacters();
343     parse(characters, characters + value.length());
344 }
345
346 bool CSPSourceList::matches(const URL& url)
347 {
348     if (m_allowStar)
349         return true;
350
351     URL effectiveURL = SecurityOrigin::shouldUseInnerURL(url) ? SecurityOrigin::extractInnerURL(url) : url;
352
353     for (size_t i = 0; i < m_list.size(); ++i) {
354         if (m_list[i].matches(effectiveURL))
355             return true;
356     }
357
358     return false;
359 }
360
361 // source-list       = *WSP [ source *( 1*WSP source ) *WSP ]
362 //                   / *WSP "'none'" *WSP
363 //
364 void CSPSourceList::parse(const UChar* begin, const UChar* end)
365 {
366     const UChar* position = begin;
367
368     while (position < end) {
369         skipWhile<isASCIISpace>(position, end);
370         if (position == end)
371             return;
372
373         const UChar* beginSource = position;
374         skipWhile<isSourceCharacter>(position, end);
375
376         String scheme, host, path;
377         int port = 0;
378         bool hostHasWildcard = false;
379         bool portHasWildcard = false;
380
381         if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) {
382             // Wildcard hosts and keyword sources ('self', 'unsafe-inline',
383             // etc.) aren't stored in m_list, but as attributes on the source
384             // list itself.
385             if (scheme.isEmpty() && host.isEmpty())
386                 continue;
387             if (isDirectiveName(host))
388                 m_policy->reportDirectiveAsSourceExpression(m_directiveName, host);
389             m_list.append(CSPSource(m_policy, scheme, host, port, path, hostHasWildcard, portHasWildcard));
390         } else
391             m_policy->reportInvalidSourceExpression(m_directiveName, String(beginSource, position - beginSource));
392
393         ASSERT(position == end || isASCIISpace(*position));
394      }
395 }
396
397 // source            = scheme ":"
398 //                   / ( [ scheme "://" ] host [ port ] [ path ] )
399 //                   / "'self'"
400 //
401 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
402                                 String& scheme, String& host, int& port, String& path,
403                                 bool& hostHasWildcard, bool& portHasWildcard)
404 {
405     if (begin == end)
406         return false;
407
408     if (equalIgnoringCase("'none'", begin, end - begin))
409         return false;
410
411     if (end - begin == 1 && *begin == '*') {
412         addSourceStar();
413         return true;
414     }
415
416     if (equalIgnoringCase("'self'", begin, end - begin)) {
417         addSourceSelf();
418         return true;
419     }
420
421     if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) {
422         addSourceUnsafeInline();
423         return true;
424     }
425
426     if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) {
427         addSourceUnsafeEval();
428         return true;
429     }
430
431     const UChar* position = begin;
432     const UChar* beginHost = begin;
433     const UChar* beginPath = end;
434     const UChar* beginPort = 0;
435
436     skipWhile<isNotColonOrSlash>(position, end);
437
438     if (position == end) {
439         // host
440         //     ^
441         return parseHost(beginHost, position, host, hostHasWildcard);
442     }
443
444     if (position < end && *position == '/') {
445         // host/path || host/ || /
446         //     ^            ^    ^
447         if (!parseHost(beginHost, position, host, hostHasWildcard)
448             || !parsePath(position, end, path)
449             || position != end)
450             return false;
451         return true;
452     }
453
454     if (position < end && *position == ':') {
455         if (end - position == 1) {
456             // scheme:
457             //       ^
458             return parseScheme(begin, position, scheme);
459         }
460
461         if (position[1] == '/') {
462             // scheme://host || scheme://
463             //       ^                ^
464             if (!parseScheme(begin, position, scheme)
465                 || !skipExactly(position, end, ':')
466                 || !skipExactly(position, end, '/')
467                 || !skipExactly(position, end, '/'))
468                 return false;
469             if (position == end)
470                 return true;
471             beginHost = position;
472             skipWhile<isNotColonOrSlash>(position, end);
473         }
474
475         if (position < end && *position == ':') {
476             // host:port || scheme://host:port
477             //     ^                     ^
478             beginPort = position;
479             skipUntil(position, end, '/');
480         }
481     }
482
483     if (position < end && *position == '/') {
484         // scheme://host/path || scheme://host:port/path
485         //              ^                          ^
486         if (position == beginHost)
487             return false;
488
489         beginPath = position;
490     }
491
492     if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostHasWildcard))
493         return false;
494
495     if (beginPort) {
496         if (!parsePort(beginPort, beginPath, port, portHasWildcard))
497             return false;
498     } else {
499         port = 0;
500     }
501
502     if (beginPath != end) {
503         if (!parsePath(beginPath, end, path))
504             return false;
505     }
506
507     return true;
508 }
509
510 //                     ; <scheme> production from RFC 3986
511 // scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
512 //
513 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme)
514 {
515     ASSERT(begin <= end);
516     ASSERT(scheme.isEmpty());
517
518     if (begin == end)
519         return false;
520
521     const UChar* position = begin;
522
523     if (!skipExactly<isASCIIAlpha>(position, end))
524         return false;
525
526     skipWhile<isSchemeContinuationCharacter>(position, end);
527
528     if (position != end)
529         return false;
530
531     scheme = String(begin, end - begin);
532     return true;
533 }
534
535 // host              = [ "*." ] 1*host-char *( "." 1*host-char )
536 //                   / "*"
537 // host-char         = ALPHA / DIGIT / "-"
538 //
539 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard)
540 {
541     ASSERT(begin <= end);
542     ASSERT(host.isEmpty());
543     ASSERT(!hostHasWildcard);
544
545     if (begin == end)
546         return false;
547
548     const UChar* position = begin;
549
550     if (skipExactly(position, end, '*')) {
551         hostHasWildcard = true;
552
553         if (position == end)
554             return true;
555
556         if (!skipExactly(position, end, '.'))
557             return false;
558     }
559
560     const UChar* hostBegin = position;
561
562     while (position < end) {
563         if (!skipExactly<isHostCharacter>(position, end))
564             return false;
565
566         skipWhile<isHostCharacter>(position, end);
567
568         if (position < end && !skipExactly(position, end, '.'))
569             return false;
570     }
571
572     ASSERT(position == end);
573     host = String(hostBegin, end - hostBegin);
574     return true;
575 }
576
577 bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path)
578 {
579     ASSERT(begin <= end);
580     ASSERT(path.isEmpty());
581
582     const UChar* position = begin;
583     skipWhile<isPathComponentCharacter>(position, end);
584     // path/to/file.js?query=string || path/to/file.js#anchor
585     //                ^                               ^
586     if (position < end)
587         m_policy->reportInvalidPathCharacter(m_directiveName, String(begin, end - begin), *position);
588
589     path = decodeURLEscapeSequences(String(begin, position - begin));
590
591     ASSERT(position <= end);
592     ASSERT(position == end || (*position == '#' || *position == '?'));
593     return true;
594 }
595
596 // port              = ":" ( 1*DIGIT / "*" )
597 //
598 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard)
599 {
600     ASSERT(begin <= end);
601     ASSERT(!port);
602     ASSERT(!portHasWildcard);
603
604     if (!skipExactly(begin, end, ':'))
605         ASSERT_NOT_REACHED();
606
607     if (begin == end)
608         return false;
609
610     if (end - begin == 1 && *begin == '*') {
611         port = 0;
612         portHasWildcard = true;
613         return true;
614     }
615
616     const UChar* position = begin;
617     skipWhile<isASCIIDigit>(position, end);
618
619     if (position != end)
620         return false;
621
622     bool ok;
623     port = charactersToIntStrict(begin, end - begin, &ok);
624     return ok;
625 }
626
627 void CSPSourceList::addSourceSelf()
628 {
629     m_list.append(CSPSource(m_policy, m_policy->securityOrigin()->protocol(), m_policy->securityOrigin()->host(), m_policy->securityOrigin()->port(), String(), false, false));
630 }
631
632 void CSPSourceList::addSourceStar()
633 {
634     m_allowStar = true;
635 }
636
637 void CSPSourceList::addSourceUnsafeInline()
638 {
639     m_allowInline = true;
640 }
641
642 void CSPSourceList::addSourceUnsafeEval()
643 {
644     m_allowEval = true;
645 }
646
647 class CSPDirective {
648 public:
649     CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
650         : m_name(name)
651         , m_text(name + ' ' + value)
652         , m_policy(policy)
653     {
654     }
655
656     const String& text() const { return m_text; }
657
658 protected:
659     const ContentSecurityPolicy* policy() const { return m_policy; }
660
661 private:
662     String m_name;
663     String m_text;
664     ContentSecurityPolicy* m_policy;
665 };
666
667 class NonceDirective : public CSPDirective {
668 public:
669     NonceDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
670         : CSPDirective(name, value, policy)
671     {
672         parse(value);
673     }
674
675     bool allows(const String& nonce) const
676     {
677         return (!m_scriptNonce.isEmpty() && nonce.stripWhiteSpace() == m_scriptNonce);
678     }
679
680 private:
681     void parse(const String& value)
682     {
683         String nonce;
684         auto characters = StringView(value).upconvertedCharacters();
685         const UChar* position = characters;
686         const UChar* end = position + value.length();
687
688         skipWhile<isASCIISpace>(position, end);
689         const UChar* nonceBegin = position;
690         if (position == end) {
691             policy()->reportInvalidNonce(String());
692             m_scriptNonce = "";
693             return;
694         }
695         skipWhile<isNonceCharacter>(position, end);
696         if (nonceBegin < position)
697             nonce = String(nonceBegin, position - nonceBegin);
698
699         // Trim off trailing whitespace: If we're not at the end of the string, log
700         // an error.
701         skipWhile<isASCIISpace>(position, end);
702         if (position < end) {
703             policy()->reportInvalidNonce(value);
704             m_scriptNonce = "";
705         } else
706             m_scriptNonce = nonce;
707     }
708
709     String m_scriptNonce;
710 };
711
712 class MediaListDirective : public CSPDirective {
713 public:
714     MediaListDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
715         : CSPDirective(name, value, policy)
716     {
717         parse(value);
718     }
719
720     bool allows(const String& type)
721     {
722         return m_pluginTypes.contains(type);
723     }
724
725 private:
726     void parse(const String& value)
727     {
728         auto characters = StringView(value).upconvertedCharacters();
729         const UChar* begin = characters;
730         const UChar* position = begin;
731         const UChar* end = begin + value.length();
732
733         // 'plugin-types ____;' OR 'plugin-types;'
734         if (value.isEmpty()) {
735             policy()->reportInvalidPluginTypes(value);
736             return;
737         }
738
739         while (position < end) {
740             // _____ OR _____mime1/mime1
741             // ^        ^
742             skipWhile<isASCIISpace>(position, end);
743             if (position == end)
744                 return;
745
746             // mime1/mime1 mime2/mime2
747             // ^
748             begin = position;
749             if (!skipExactly<isMediaTypeCharacter>(position, end)) {
750                 skipWhile<isNotASCIISpace>(position, end);
751                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
752                 continue;
753             }
754             skipWhile<isMediaTypeCharacter>(position, end);
755
756             // mime1/mime1 mime2/mime2
757             //      ^
758             if (!skipExactly(position, end, '/')) {
759                 skipWhile<isNotASCIISpace>(position, end);
760                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
761                 continue;
762             }
763
764             // mime1/mime1 mime2/mime2
765             //       ^
766             if (!skipExactly<isMediaTypeCharacter>(position, end)) {
767                 skipWhile<isNotASCIISpace>(position, end);
768                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
769                 continue;
770             }
771             skipWhile<isMediaTypeCharacter>(position, end);
772
773             // mime1/mime1 mime2/mime2 OR mime1/mime1  OR mime1/mime1/error
774             //            ^                          ^               ^
775             if (position < end && isNotASCIISpace(*position)) {
776                 skipWhile<isNotASCIISpace>(position, end);
777                 policy()->reportInvalidPluginTypes(String(begin, position - begin));
778                 continue;
779             }
780             m_pluginTypes.add(String(begin, position - begin));
781
782             ASSERT(position == end || isASCIISpace(*position));
783         }
784     }
785
786     HashSet<String> m_pluginTypes;
787 };
788
789 class SourceListDirective : public CSPDirective {
790 public:
791     SourceListDirective(const String& name, const String& value, ContentSecurityPolicy* policy)
792         : CSPDirective(name, value, policy)
793         , m_sourceList(policy, name)
794     {
795         m_sourceList.parse(value);
796     }
797
798     bool allows(const URL& url)
799     {
800         return m_sourceList.matches(url.isEmpty() ? policy()->url() : url);
801     }
802
803     bool allowInline() const { return m_sourceList.allowInline(); }
804     bool allowEval() const { return m_sourceList.allowEval(); }
805
806 private:
807     CSPSourceList m_sourceList;
808 };
809
810 class CSPDirectiveList {
811     WTF_MAKE_FAST_ALLOCATED;
812 public:
813     static PassOwnPtr<CSPDirectiveList> create(ContentSecurityPolicy*, const String&, ContentSecurityPolicy::HeaderType);
814
815     const String& header() const { return m_header; }
816     ContentSecurityPolicy::HeaderType headerType() const { return m_headerType; }
817
818     bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
819     bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
820     bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
821     bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const;
822     bool allowEval(JSC::ExecState*, ContentSecurityPolicy::ReportingStatus) const;
823     bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL&) const;
824     bool allowPluginType(const String& type, const String& typeAttribute, const URL&, ContentSecurityPolicy::ReportingStatus) const;
825
826     bool allowScriptFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
827     bool allowObjectFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
828     bool allowChildFrameFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
829     bool allowImageFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
830     bool allowStyleFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
831     bool allowFontFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
832     bool allowMediaFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
833     bool allowConnectToSource(const URL&, ContentSecurityPolicy::ReportingStatus) const;
834     bool allowFormAction(const URL&, ContentSecurityPolicy::ReportingStatus) const;
835     bool allowBaseURI(const URL&, ContentSecurityPolicy::ReportingStatus) const;
836
837     void gatherReportURIs(DOMStringList&) const;
838     const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; }
839     ContentSecurityPolicy::ReflectedXSSDisposition reflectedXSSDisposition() const { return m_reflectedXSSDisposition; }
840     bool isReportOnly() const { return m_reportOnly; }
841     const Vector<URL>& reportURIs() const { return m_reportURIs; }
842
843 private:
844     CSPDirectiveList(ContentSecurityPolicy*, ContentSecurityPolicy::HeaderType);
845
846     void parse(const String&);
847
848     bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value);
849     void parseReportURI(const String& name, const String& value);
850     void parseScriptNonce(const String& name, const String& value);
851     void parsePluginTypes(const String& name, const String& value);
852     void parseReflectedXSS(const String& name, const String& value);
853     void addDirective(const String& name, const String& value);
854     void applySandboxPolicy(const String& name, const String& sandboxPolicy);
855
856     template <class CSPDirectiveType>
857     void setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>&);
858
859     SourceListDirective* operativeDirective(SourceListDirective*) const;
860     void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL = URL(), const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = 0) const;
861
862     bool checkEval(SourceListDirective*) const;
863     bool checkInline(SourceListDirective*) const;
864     bool checkNonce(NonceDirective*, const String&) const;
865     bool checkSource(SourceListDirective*, const URL&) const;
866     bool checkMediaType(MediaListDirective*, const String& type, const String& typeAttribute) const;
867
868     void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; }
869
870     bool checkEvalAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = 0) const;
871     bool checkInlineAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const;
872     bool checkNonceAndReportViolation(NonceDirective*, const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const;
873
874     bool checkSourceAndReportViolation(SourceListDirective*, const URL&, const String& effectiveDirective) const;
875     bool checkMediaTypeAndReportViolation(MediaListDirective*, const String& type, const String& typeAttribute, const String& consoleMessage) const;
876
877     bool denyIfEnforcingPolicy() const { return m_reportOnly; }
878
879     ContentSecurityPolicy* m_policy;
880
881     String m_header;
882     ContentSecurityPolicy::HeaderType m_headerType;
883
884     bool m_reportOnly;
885     bool m_haveSandboxPolicy;
886     ContentSecurityPolicy::ReflectedXSSDisposition m_reflectedXSSDisposition;
887
888     OwnPtr<MediaListDirective> m_pluginTypes;
889     OwnPtr<NonceDirective> m_scriptNonce;
890     OwnPtr<SourceListDirective> m_baseURI;
891     OwnPtr<SourceListDirective> m_connectSrc;
892     OwnPtr<SourceListDirective> m_defaultSrc;
893     OwnPtr<SourceListDirective> m_fontSrc;
894     OwnPtr<SourceListDirective> m_formAction;
895     OwnPtr<SourceListDirective> m_frameSrc;
896     OwnPtr<SourceListDirective> m_imgSrc;
897     OwnPtr<SourceListDirective> m_mediaSrc;
898     OwnPtr<SourceListDirective> m_objectSrc;
899     OwnPtr<SourceListDirective> m_scriptSrc;
900     OwnPtr<SourceListDirective> m_styleSrc;
901
902     Vector<URL> m_reportURIs;
903
904     String m_evalDisabledErrorMessage;
905 };
906
907 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, ContentSecurityPolicy::HeaderType type)
908     : m_policy(policy)
909     , m_headerType(type)
910     , m_reportOnly(false)
911     , m_haveSandboxPolicy(false)
912     , m_reflectedXSSDisposition(ContentSecurityPolicy::ReflectedXSSUnset)
913 {
914     m_reportOnly = (type == ContentSecurityPolicy::Report || type == ContentSecurityPolicy::PrefixedReport);
915 }
916
917 PassOwnPtr<CSPDirectiveList> CSPDirectiveList::create(ContentSecurityPolicy* policy, const String& header, ContentSecurityPolicy::HeaderType type)
918 {
919     OwnPtr<CSPDirectiveList> directives = adoptPtr(new CSPDirectiveList(policy, type));
920     directives->parse(header);
921
922     if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) {
923         String message = makeString("Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"", directives->operativeDirective(directives->m_scriptSrc.get())->text(), "\".\n");
924         directives->setEvalDisabledErrorMessage(message);
925     }
926
927     if (directives->isReportOnly() && directives->reportURIs().isEmpty())
928         policy->reportMissingReportURI(header);
929
930     return directives.release();
931 }
932
933 void CSPDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
934 {
935     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
936     m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header, contextURL, contextLine, state);
937 }
938
939 bool CSPDirectiveList::checkEval(SourceListDirective* directive) const
940 {
941     return !directive || directive->allowEval();
942 }
943
944 bool CSPDirectiveList::checkInline(SourceListDirective* directive) const
945 {
946     return !directive || directive->allowInline();
947 }
948
949 bool CSPDirectiveList::checkNonce(NonceDirective* directive, const String& nonce) const
950 {
951     return !directive || directive->allows(nonce);
952 }
953
954 bool CSPDirectiveList::checkSource(SourceListDirective* directive, const URL& url) const
955 {
956     return !directive || directive->allows(url);
957 }
958
959 bool CSPDirectiveList::checkMediaType(MediaListDirective* directive, const String& type, const String& typeAttribute) const
960 {
961     if (!directive)
962         return true;
963     if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type)
964         return false;
965     return directive->allows(type);
966 }
967
968 SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const
969 {
970     return directive ? directive : m_defaultSrc.get();
971 }
972
973 bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
974 {
975     if (checkEval(directive))
976         return true;
977
978     String suffix = String();
979     if (directive == m_defaultSrc)
980         suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.";
981
982     reportViolation(directive->text(), scriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine, state);
983     if (!m_reportOnly) {
984         m_policy->reportBlockedScriptExecutionToInspector(directive->text());
985         return false;
986     }
987     return true;
988 }
989
990 bool CSPDirectiveList::checkNonceAndReportViolation(NonceDirective* directive, const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const
991 {
992     if (checkNonce(directive, nonce))
993         return true;
994     reportViolation(directive->text(), scriptNonce, consoleMessage + "\"" + directive->text() + "\".\n", URL(), contextURL, contextLine);
995     return denyIfEnforcingPolicy();
996 }
997
998 bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const
999 {
1000     if (checkMediaType(directive, type, typeAttribute))
1001         return true;
1002
1003     String message = makeString(consoleMessage, "\'", directive->text(), "\'.");
1004     if (typeAttribute.isEmpty())
1005         message = message + " When enforcing the 'plugin-types' directive, the plugin's media type must be explicitly declared with a 'type' attribute on the containing element (e.g. '<object type=\"[TYPE GOES HERE]\" ...>').";
1006
1007     reportViolation(directive->text(), pluginTypes, message + "\n", URL());
1008     return denyIfEnforcingPolicy();
1009 }
1010
1011 bool CSPDirectiveList::checkInlineAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const
1012 {
1013     if (checkInline(directive))
1014         return true;
1015
1016     String suffix = String();
1017     if (directive == m_defaultSrc)
1018         suffix = makeString(" Note that '", (isScript ? "script" : "style"), "-src' was not explicitly set, so 'default-src' is used as a fallback.");
1019
1020     reportViolation(directive->text(), isScript ? scriptSrc : styleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine);
1021
1022     if (!m_reportOnly) {
1023         if (isScript)
1024             m_policy->reportBlockedScriptExecutionToInspector(directive->text());
1025         return false;
1026     }
1027     return true;
1028 }
1029
1030 bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const URL& url, const String& effectiveDirective) const
1031 {
1032     if (checkSource(directive, url))
1033         return true;
1034
1035     String prefix;
1036     if (baseURI == effectiveDirective)
1037         prefix = "Refused to set the document's base URI to '";
1038     else if (connectSrc == effectiveDirective)
1039         prefix = "Refused to connect to '";
1040     else if (fontSrc == effectiveDirective)
1041         prefix = "Refused to load the font '";
1042     else if (formAction == effectiveDirective)
1043         prefix = "Refused to send form data to '";
1044     else if (frameSrc == effectiveDirective)
1045         prefix = "Refused to frame '";
1046     else if (imgSrc == effectiveDirective)
1047         prefix = "Refused to load the image '";
1048     else if (mediaSrc == effectiveDirective)
1049         prefix = "Refused to load media from '";
1050     else if (objectSrc == effectiveDirective)
1051         prefix = "Refused to load plugin data from '";
1052     else if (scriptSrc == effectiveDirective)
1053         prefix = "Refused to load the script '";
1054     else if (styleSrc == effectiveDirective)
1055         prefix = "Refused to load the stylesheet '";
1056
1057     String suffix = String();
1058     if (directive == m_defaultSrc)
1059         suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback.";
1060
1061     reportViolation(directive->text(), effectiveDirective, prefix + url.stringCenterEllipsizedToLength() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url);
1062     return denyIfEnforcingPolicy();
1063 }
1064
1065 bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1066 {
1067     DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: ")));
1068     if (reportingStatus == ContentSecurityPolicy::SendReport) {
1069         return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true)
1070                 && checkNonceAndReportViolation(m_scriptNonce.get(), String(), consoleMessage, contextURL, contextLine));
1071     } else {
1072         return (checkInline(operativeDirective(m_scriptSrc.get()))
1073                 && checkNonce(m_scriptNonce.get(), String()));
1074     }
1075 }
1076
1077 bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1078 {
1079     DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute inline event handler because it violates the following Content Security Policy directive: ")));
1080     if (reportingStatus == ContentSecurityPolicy::SendReport) {
1081         return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true)
1082                 && checkNonceAndReportViolation(m_scriptNonce.get(), String(), consoleMessage, contextURL, contextLine));
1083     } else {
1084         return (checkInline(operativeDirective(m_scriptSrc.get()))
1085                 && checkNonce(m_scriptNonce.get(), String()));
1086     }
1087 }
1088
1089 bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1090 {
1091     DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute inline script because it violates the following Content Security Policy directive: ")));
1092     return reportingStatus == ContentSecurityPolicy::SendReport ?
1093         checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) :
1094         checkInline(operativeDirective(m_scriptSrc.get()));
1095 }
1096
1097 bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1098 {
1099     DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to apply inline style because it violates the following Content Security Policy directive: ")));
1100     return reportingStatus == ContentSecurityPolicy::SendReport ?
1101         checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false) :
1102         checkInline(operativeDirective(m_styleSrc.get()));
1103 }
1104
1105 bool CSPDirectiveList::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1106 {
1107     DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to evaluate script because it violates the following Content Security Policy directive: ")));
1108     return reportingStatus == ContentSecurityPolicy::SendReport ?
1109         checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, String(), WTF::OrdinalNumber::beforeFirst(), state) :
1110         checkEval(operativeDirective(m_scriptSrc.get()));
1111 }
1112
1113 bool CSPDirectiveList::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& url) const
1114 {
1115     DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute script because it violates the following Content Security Policy directive: ")));
1116     if (url.isEmpty())
1117         return checkNonceAndReportViolation(m_scriptNonce.get(), nonce, consoleMessage, contextURL, contextLine);
1118     return checkNonceAndReportViolation(m_scriptNonce.get(), nonce, "Refused to load '" + url.stringCenterEllipsizedToLength() + "' because it violates the following Content Security Policy directive: ", contextURL, contextLine);
1119 }
1120
1121 bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1122 {
1123     return reportingStatus == ContentSecurityPolicy::SendReport ?
1124         checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.stringCenterEllipsizedToLength() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") :
1125         checkMediaType(m_pluginTypes.get(), type, typeAttribute);
1126 }
1127
1128 bool CSPDirectiveList::allowScriptFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1129 {
1130     return reportingStatus == ContentSecurityPolicy::SendReport ?
1131         checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, scriptSrc) :
1132         checkSource(operativeDirective(m_scriptSrc.get()), url);
1133 }
1134
1135 bool CSPDirectiveList::allowObjectFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1136 {
1137     if (url.isBlankURL())
1138         return true;
1139     return reportingStatus == ContentSecurityPolicy::SendReport ?
1140         checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, objectSrc) :
1141         checkSource(operativeDirective(m_objectSrc.get()), url);
1142 }
1143
1144 bool CSPDirectiveList::allowChildFrameFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1145 {
1146     if (url.isBlankURL())
1147         return true;
1148     return reportingStatus == ContentSecurityPolicy::SendReport ?
1149         checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, frameSrc) :
1150         checkSource(operativeDirective(m_frameSrc.get()), url);
1151 }
1152
1153 bool CSPDirectiveList::allowImageFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1154 {
1155     return reportingStatus == ContentSecurityPolicy::SendReport ?
1156         checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, imgSrc) :
1157         checkSource(operativeDirective(m_imgSrc.get()), url);
1158 }
1159
1160 bool CSPDirectiveList::allowStyleFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1161 {
1162     return reportingStatus == ContentSecurityPolicy::SendReport ?
1163         checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, styleSrc) :
1164         checkSource(operativeDirective(m_styleSrc.get()), url);
1165 }
1166
1167 bool CSPDirectiveList::allowFontFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1168 {
1169     return reportingStatus == ContentSecurityPolicy::SendReport ?
1170         checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, fontSrc) :
1171         checkSource(operativeDirective(m_fontSrc.get()), url);
1172 }
1173
1174 bool CSPDirectiveList::allowMediaFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1175 {
1176     return reportingStatus == ContentSecurityPolicy::SendReport ?
1177         checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, mediaSrc) :
1178         checkSource(operativeDirective(m_mediaSrc.get()), url);
1179 }
1180
1181 bool CSPDirectiveList::allowConnectToSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1182 {
1183     return reportingStatus == ContentSecurityPolicy::SendReport ?
1184         checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, connectSrc) :
1185         checkSource(operativeDirective(m_connectSrc.get()), url);
1186 }
1187
1188 void CSPDirectiveList::gatherReportURIs(DOMStringList& list) const
1189 {
1190     for (size_t i = 0; i < m_reportURIs.size(); ++i)
1191         list.append(m_reportURIs[i].string());
1192 }
1193
1194 bool CSPDirectiveList::allowFormAction(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1195 {
1196     return reportingStatus == ContentSecurityPolicy::SendReport ?
1197         checkSourceAndReportViolation(m_formAction.get(), url, formAction) :
1198         checkSource(m_formAction.get(), url);
1199 }
1200
1201 bool CSPDirectiveList::allowBaseURI(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1202 {
1203     return reportingStatus == ContentSecurityPolicy::SendReport ?
1204         checkSourceAndReportViolation(m_baseURI.get(), url, baseURI) :
1205         checkSource(m_baseURI.get(), url);
1206 }
1207
1208 // policy            = directive-list
1209 // directive-list    = [ directive *( ";" [ directive ] ) ]
1210 //
1211 void CSPDirectiveList::parse(const String& policy)
1212 {
1213     m_header = policy;
1214     if (policy.isEmpty())
1215         return;
1216
1217     auto characters = StringView(policy).upconvertedCharacters();
1218     const UChar* position = characters;
1219     const UChar* end = position + policy.length();
1220
1221     while (position < end) {
1222         const UChar* directiveBegin = position;
1223         skipUntil(position, end, ';');
1224
1225         String name, value;
1226         if (parseDirective(directiveBegin, position, name, value)) {
1227             ASSERT(!name.isEmpty());
1228             addDirective(name, value);
1229         }
1230
1231         ASSERT(position == end || *position == ';');
1232         skipExactly(position, end, ';');
1233     }
1234 }
1235
1236 // directive         = *WSP [ directive-name [ WSP directive-value ] ]
1237 // directive-name    = 1*( ALPHA / DIGIT / "-" )
1238 // directive-value   = *( WSP / <VCHAR except ";"> )
1239 //
1240 bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
1241 {
1242     ASSERT(name.isEmpty());
1243     ASSERT(value.isEmpty());
1244
1245     const UChar* position = begin;
1246     skipWhile<isASCIISpace>(position, end);
1247
1248     // Empty directive (e.g. ";;;"). Exit early.
1249     if (position == end)
1250         return false;
1251
1252     const UChar* nameBegin = position;
1253     skipWhile<isDirectiveNameCharacter>(position, end);
1254
1255     // The directive-name must be non-empty.
1256     if (nameBegin == position) {
1257         skipWhile<isNotASCIISpace>(position, end);
1258         m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin));
1259         return false;
1260     }
1261
1262     name = String(nameBegin, position - nameBegin);
1263
1264     if (position == end)
1265         return true;
1266
1267     if (!skipExactly<isASCIISpace>(position, end)) {
1268         skipWhile<isNotASCIISpace>(position, end);
1269         m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin));
1270         return false;
1271     }
1272
1273     skipWhile<isASCIISpace>(position, end);
1274
1275     const UChar* valueBegin = position;
1276     skipWhile<isDirectiveValueCharacter>(position, end);
1277
1278     if (position != end) {
1279         m_policy->reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin));
1280         return false;
1281     }
1282
1283     // The directive-value may be empty.
1284     if (valueBegin == position)
1285         return true;
1286
1287     value = String(valueBegin, position - valueBegin);
1288     return true;
1289 }
1290
1291 void CSPDirectiveList::parseReportURI(const String& name, const String& value)
1292 {
1293     if (!m_reportURIs.isEmpty()) {
1294         m_policy->reportDuplicateDirective(name);
1295         return;
1296     }
1297
1298     auto characters = StringView(value).upconvertedCharacters();
1299     const UChar* position = characters;
1300     const UChar* end = position + value.length();
1301
1302     while (position < end) {
1303         skipWhile<isASCIISpace>(position, end);
1304
1305         const UChar* urlBegin = position;
1306         skipWhile<isNotASCIISpace>(position, end);
1307
1308         if (urlBegin < position) {
1309             String url = String(urlBegin, position - urlBegin);
1310             m_reportURIs.append(m_policy->completeURL(url));
1311         }
1312     }
1313 }
1314
1315
1316 template<class CSPDirectiveType>
1317 void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>& directive)
1318 {
1319     if (directive) {
1320         m_policy->reportDuplicateDirective(name);
1321         return;
1322     }
1323     directive = adoptPtr(new CSPDirectiveType(name, value, m_policy));
1324 }
1325
1326 void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy)
1327 {
1328     if (m_haveSandboxPolicy) {
1329         m_policy->reportDuplicateDirective(name);
1330         return;
1331     }
1332     m_haveSandboxPolicy = true;
1333     String invalidTokens;
1334     m_policy->enforceSandboxFlags(SecurityContext::parseSandboxPolicy(sandboxPolicy, invalidTokens));
1335     if (!invalidTokens.isNull())
1336         m_policy->reportInvalidSandboxFlags(invalidTokens);
1337 }
1338
1339 void CSPDirectiveList::parseReflectedXSS(const String& name, const String& value)
1340 {
1341     if (m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset) {
1342         m_policy->reportDuplicateDirective(name);
1343         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1344         return;
1345     }
1346
1347     if (value.isEmpty()) {
1348         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1349         m_policy->reportInvalidReflectedXSS(value);
1350         return;
1351     }
1352
1353     auto characters = StringView(value).upconvertedCharacters();
1354     const UChar* position = characters;
1355     const UChar* end = position + value.length();
1356
1357     skipWhile<isASCIISpace>(position, end);
1358     const UChar* begin = position;
1359     skipWhile<isNotASCIISpace>(position, end);
1360
1361     // value1
1362     //       ^
1363     if (equalIgnoringCase("allow", begin, position - begin))
1364         m_reflectedXSSDisposition = ContentSecurityPolicy::AllowReflectedXSS;
1365     else if (equalIgnoringCase("filter", begin, position - begin))
1366         m_reflectedXSSDisposition = ContentSecurityPolicy::FilterReflectedXSS;
1367     else if (equalIgnoringCase("block", begin, position - begin))
1368         m_reflectedXSSDisposition = ContentSecurityPolicy::BlockReflectedXSS;
1369     else {
1370         m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1371         m_policy->reportInvalidReflectedXSS(value);
1372         return;
1373     }
1374
1375     skipWhile<isASCIISpace>(position, end);
1376     if (position == end && m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset)
1377         return;
1378
1379     // value1 value2
1380     //        ^
1381     m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid;
1382     m_policy->reportInvalidReflectedXSS(value);
1383 }
1384
1385 void CSPDirectiveList::addDirective(const String& name, const String& value)
1386 {
1387     ASSERT(!name.isEmpty());
1388
1389     if (equalIgnoringCase(name, defaultSrc))
1390         setCSPDirective<SourceListDirective>(name, value, m_defaultSrc);
1391     else if (equalIgnoringCase(name, scriptSrc))
1392         setCSPDirective<SourceListDirective>(name, value, m_scriptSrc);
1393     else if (equalIgnoringCase(name, objectSrc))
1394         setCSPDirective<SourceListDirective>(name, value, m_objectSrc);
1395     else if (equalIgnoringCase(name, frameSrc))
1396         setCSPDirective<SourceListDirective>(name, value, m_frameSrc);
1397     else if (equalIgnoringCase(name, imgSrc))
1398         setCSPDirective<SourceListDirective>(name, value, m_imgSrc);
1399     else if (equalIgnoringCase(name, styleSrc))
1400         setCSPDirective<SourceListDirective>(name, value, m_styleSrc);
1401     else if (equalIgnoringCase(name, fontSrc))
1402         setCSPDirective<SourceListDirective>(name, value, m_fontSrc);
1403     else if (equalIgnoringCase(name, mediaSrc))
1404         setCSPDirective<SourceListDirective>(name, value, m_mediaSrc);
1405     else if (equalIgnoringCase(name, connectSrc))
1406         setCSPDirective<SourceListDirective>(name, value, m_connectSrc);
1407     else if (equalIgnoringCase(name, sandbox))
1408         applySandboxPolicy(name, value);
1409     else if (equalIgnoringCase(name, reportURI))
1410         parseReportURI(name, value);
1411 #if ENABLE(CSP_NEXT)
1412     else if (m_policy->experimentalFeaturesEnabled()) {
1413         if (equalIgnoringCase(name, baseURI))
1414             setCSPDirective<SourceListDirective>(name, value, m_baseURI);
1415         else if (equalIgnoringCase(name, formAction))
1416             setCSPDirective<SourceListDirective>(name, value, m_formAction);
1417         else if (equalIgnoringCase(name, pluginTypes))
1418             setCSPDirective<MediaListDirective>(name, value, m_pluginTypes);
1419         else if (equalIgnoringCase(name, scriptNonce))
1420             setCSPDirective<NonceDirective>(name, value, m_scriptNonce);
1421         else if (equalIgnoringCase(name, reflectedXSS))
1422             parseReflectedXSS(name, value);
1423         else
1424             m_policy->reportUnsupportedDirective(name);
1425     }
1426 #endif
1427     else
1428         m_policy->reportUnsupportedDirective(name);
1429 }
1430
1431 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext* scriptExecutionContext)
1432     : m_scriptExecutionContext(scriptExecutionContext)
1433     , m_overrideInlineStyleAllowed(false)
1434 {
1435 }
1436
1437 ContentSecurityPolicy::~ContentSecurityPolicy()
1438 {
1439 }
1440
1441 void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) 
1442 {
1443     ASSERT(m_policies.isEmpty());
1444     for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter)
1445         didReceiveHeader((*iter)->header(), (*iter)->headerType());
1446 }
1447
1448 void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type)
1449 {
1450     // RFC2616, section 4.2 specifies that headers appearing multiple times can
1451     // be combined with a comma. Walk the header string, and parse each comma
1452     // separated chunk as a separate header.
1453     auto characters = StringView(header).upconvertedCharacters();
1454     const UChar* begin = characters;
1455     const UChar* position = begin;
1456     const UChar* end = begin + header.length();
1457     while (position < end) {
1458         skipUntil(position, end, ',');
1459
1460         // header1,header2 OR header1
1461         //        ^                  ^
1462         OwnPtr<CSPDirectiveList> policy = CSPDirectiveList::create(this, String(begin, position - begin), type);
1463         if (!policy->isReportOnly() && !policy->allowEval(0, SuppressReport))
1464             m_scriptExecutionContext->disableEval(policy->evalDisabledErrorMessage());
1465
1466         m_policies.append(policy.release());
1467
1468         // Skip the comma, and begin the next header from the current position.
1469         ASSERT(position == end || *position == ',');
1470         skipExactly(position, end, ',');
1471         begin = position;
1472     }
1473 }
1474
1475 void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value)
1476 {
1477     m_overrideInlineStyleAllowed = value;
1478 }
1479
1480 const String& ContentSecurityPolicy::deprecatedHeader() const
1481 {
1482     return m_policies.isEmpty() ? emptyString() : m_policies[0]->header();
1483 }
1484
1485 ContentSecurityPolicy::HeaderType ContentSecurityPolicy::deprecatedHeaderType() const
1486 {
1487     return m_policies.isEmpty() ? Enforce : m_policies[0]->headerType();
1488 }
1489
1490 template<bool (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const>
1491 bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus)
1492 {
1493     for (size_t i = 0; i < policies.size(); ++i) {
1494         if (!(policies[i].get()->*allowed)(reportingStatus))
1495             return false;
1496     }
1497     return true;
1498 }
1499
1500 template<bool (CSPDirectiveList::*allowed)(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus) const>
1501 bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus)
1502 {
1503     for (size_t i = 0; i < policies.size(); ++i) {
1504         if (!(policies[i].get()->*allowed)(state, reportingStatus))
1505             return false;
1506     }
1507     return true;
1508 }
1509
1510 template<bool (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const>
1511 bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus)
1512 {
1513     for (size_t i = 0; i < policies.size(); ++i) {
1514         if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus))
1515             return false;
1516     }
1517     return true;
1518 }
1519
1520 template<bool (CSPDirectiveList::*allowed)(const String&, const String&, const WTF::OrdinalNumber&, const URL&) const>
1521 bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& url)
1522 {
1523     for (size_t i = 0; i < policies.size(); ++i) {
1524         if (!(policies[i].get()->*allowed)(nonce, contextURL, contextLine, url))
1525             return false;
1526     }
1527     return true;
1528 }
1529
1530 template<bool (CSPDirectiveList::*allowFromURL)(const URL&, ContentSecurityPolicy::ReportingStatus) const>
1531 bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus)
1532 {
1533     if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol()))
1534         return true;
1535
1536     for (size_t i = 0; i < policies.size(); ++i) {
1537         if (!(policies[i].get()->*allowFromURL)(url, reportingStatus))
1538             return false;
1539     }
1540     return true;
1541 }
1542
1543 bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1544 {
1545     return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus);
1546 }
1547
1548 bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1549 {
1550     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus);
1551 }
1552
1553 bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1554 {
1555     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus);
1556 }
1557
1558 bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1559 {
1560     if (m_overrideInlineStyleAllowed)
1561         return true;
1562     return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus);
1563 }
1564
1565 bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1566 {
1567     return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, state, reportingStatus);
1568 }
1569
1570 String ContentSecurityPolicy::evalDisabledErrorMessage() const
1571 {
1572     for (size_t i = 0; i < m_policies.size(); ++i) {
1573         if (!m_policies[i]->allowEval(0, SuppressReport))
1574             return m_policies[i]->evalDisabledErrorMessage();
1575     }
1576     return String();
1577 }
1578
1579 bool ContentSecurityPolicy::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& url) const
1580 {
1581     return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce, contextURL, contextLine, url);
1582 }
1583
1584 bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1585 {
1586     for (size_t i = 0; i < m_policies.size(); ++i) {
1587         if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus))
1588             return false;
1589     }
1590     return true;
1591 }
1592
1593 bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1594 {
1595     return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus);
1596 }
1597
1598 bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1599 {
1600     return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus);
1601 }
1602
1603 bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1604 {
1605     return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus);
1606 }
1607
1608 bool ContentSecurityPolicy::allowImageFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1609 {
1610     return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus);
1611 }
1612
1613 bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1614 {
1615     return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus);
1616 }
1617
1618 bool ContentSecurityPolicy::allowFontFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1619 {
1620     return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus);
1621 }
1622
1623 bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1624 {
1625     return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus);
1626 }
1627
1628 bool ContentSecurityPolicy::allowConnectToSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1629 {
1630     return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus);
1631 }
1632
1633 bool ContentSecurityPolicy::allowFormAction(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1634 {
1635     return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus);
1636 }
1637
1638 bool ContentSecurityPolicy::allowBaseURI(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const
1639 {
1640     return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus);
1641 }
1642
1643 bool ContentSecurityPolicy::isActive() const
1644 {
1645     return !m_policies.isEmpty();
1646 }
1647
1648 ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const
1649 {
1650     ReflectedXSSDisposition disposition = ReflectedXSSUnset;
1651     for (size_t i = 0; i < m_policies.size(); ++i) {
1652         if (m_policies[i]->reflectedXSSDisposition() > disposition)
1653             disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition());
1654     }
1655     return disposition;
1656 }
1657
1658 void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const
1659 {
1660     for (size_t i = 0; i < m_policies.size(); ++i)
1661         m_policies[i]->gatherReportURIs(list);
1662 }
1663
1664 SecurityOrigin* ContentSecurityPolicy::securityOrigin() const
1665 {
1666     return m_scriptExecutionContext->securityOrigin();
1667 }
1668
1669 const URL& ContentSecurityPolicy::url() const
1670 {
1671     return m_scriptExecutionContext->url();
1672 }
1673
1674 URL ContentSecurityPolicy::completeURL(const String& url) const
1675 {
1676     return m_scriptExecutionContext->completeURL(url);
1677 }
1678
1679 void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const
1680 {
1681     m_scriptExecutionContext->enforceSandboxFlags(mask);
1682 }
1683
1684 static String stripURLForUseInReport(Document* document, const URL& url)
1685 {
1686     if (!url.isValid())
1687         return String();
1688     if (!url.isHierarchical() || url.protocolIs("file"))
1689         return url.protocol();
1690     return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString();
1691 }
1692
1693 #if ENABLE(CSP_NEXT)
1694 static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const URL& blockedURL, const String& header)
1695 {
1696     init.documentURI = document->url().string();
1697     init.referrer = document->referrer();
1698     init.blockedURI = stripURLForUseInReport(document, blockedURL);
1699     init.violatedDirective = directiveText;
1700     init.effectiveDirective = effectiveDirective;
1701     init.originalPolicy = header;
1702     init.sourceFile = String();
1703     init.lineNumber = 0;
1704
1705     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
1706     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
1707     if (callFrame && callFrame->lineNumber()) {
1708         URL source = URL(URL(), callFrame->sourceURL());
1709         init.sourceFile = stripURLForUseInReport(document, source);
1710         init.lineNumber = callFrame->lineNumber();
1711     }
1712 }
1713 #endif
1714
1715 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const Vector<URL>& reportURIs, const String& header, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
1716 {
1717     logToConsole(consoleMessage, contextURL, contextLine, state);
1718
1719     // FIXME: Support sending reports from worker.
1720     if (!m_scriptExecutionContext->isDocument())
1721         return;
1722
1723     Document* document = toDocument(m_scriptExecutionContext);
1724     Frame* frame = document->frame();
1725     if (!frame)
1726         return;
1727
1728 #if ENABLE(CSP_NEXT)
1729     if (experimentalFeaturesEnabled()) {
1730         // FIXME: This code means that we're gathering information like line numbers twice. Once we can bring this out from behind the flag, we should reuse the data gathered here when generating the JSON report below.
1731         SecurityPolicyViolationEventInit init;
1732         gatherSecurityPolicyViolationEventData(init, document, directiveText, effectiveDirective, blockedURL, header);
1733         document->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, init));
1734     }
1735 #endif
1736
1737     if (reportURIs.isEmpty())
1738         return;
1739
1740     // We need to be careful here when deciding what information to send to the
1741     // report-uri. Currently, we send only the current document's URL and the
1742     // directive that was violated. The document's URL is safe to send because
1743     // it's the document itself that's requesting that it be sent. You could
1744     // make an argument that we shouldn't send HTTPS document URLs to HTTP
1745     // report-uris (for the same reasons that we supress the Referer in that
1746     // case), but the Referer is sent implicitly whereas this request is only
1747     // sent explicitly. As for which directive was violated, that's pretty
1748     // harmless information.
1749
1750     RefPtr<InspectorObject> cspReport = InspectorObject::create();
1751     cspReport->setString(ASCIILiteral("document-uri"), document->url().strippedForUseAsReferrer());
1752     cspReport->setString(ASCIILiteral("referrer"), document->referrer());
1753     cspReport->setString(ASCIILiteral("violated-directive"), directiveText);
1754 #if ENABLE(CSP_NEXT)
1755     if (experimentalFeaturesEnabled())
1756         cspReport->setString(ASCIILiteral("effective-directive"), effectiveDirective);
1757 #else
1758     UNUSED_PARAM(effectiveDirective);
1759 #endif
1760     cspReport->setString(ASCIILiteral("original-policy"), header);
1761     cspReport->setString(ASCIILiteral("blocked-uri"), stripURLForUseInReport(document, blockedURL));
1762
1763     RefPtr<ScriptCallStack> stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2);
1764     const ScriptCallFrame* callFrame = stack->firstNonNativeCallFrame();
1765     if (callFrame && callFrame->lineNumber()) {
1766         URL source = URL(URL(), callFrame->sourceURL());
1767         cspReport->setString(ASCIILiteral("source-file"), stripURLForUseInReport(document, source));
1768         cspReport->setNumber(ASCIILiteral("line-number"), callFrame->lineNumber());
1769     }
1770
1771     RefPtr<InspectorObject> reportObject = InspectorObject::create();
1772     reportObject->setObject(ASCIILiteral("csp-report"), cspReport.release());
1773
1774     RefPtr<FormData> report = FormData::create(reportObject->toJSONString().utf8());
1775
1776     for (const auto& url : reportURIs)
1777         PingLoader::sendViolationReport(*frame, url, report);
1778 }
1779
1780 void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const
1781 {
1782     DEPRECATED_DEFINE_STATIC_LOCAL(String, allow, (ASCIILiteral("allow")));
1783     DEPRECATED_DEFINE_STATIC_LOCAL(String, options, (ASCIILiteral("options")));
1784     DEPRECATED_DEFINE_STATIC_LOCAL(String, policyURI, (ASCIILiteral("policy-uri")));
1785     DEPRECATED_DEFINE_STATIC_LOCAL(String, allowMessage, (ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect.")));
1786     DEPRECATED_DEFINE_STATIC_LOCAL(String, optionsMessage, (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.")));
1787     DEPRECATED_DEFINE_STATIC_LOCAL(String, policyURIMessage, (ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header.")));
1788
1789     String message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n");
1790     if (equalIgnoringCase(name, allow))
1791         message = allowMessage;
1792     else if (equalIgnoringCase(name, options))
1793         message = optionsMessage;
1794     else if (equalIgnoringCase(name, policyURI))
1795         message = policyURIMessage;
1796
1797     logToConsole(message);
1798 }
1799
1800 void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const
1801 {
1802     String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?";
1803     logToConsole(message);
1804 }
1805
1806 void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const
1807 {
1808     String message = makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n");
1809     logToConsole(message);
1810 }
1811
1812 void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const
1813 {
1814     String message;
1815     if (pluginType.isNull())
1816         message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n";
1817     else
1818         message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n");
1819     logToConsole(message);
1820 }
1821
1822 void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const
1823 {
1824     logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags);
1825 }
1826
1827 void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const
1828 {
1829     logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\".");
1830 }
1831
1832 void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const
1833 {
1834     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.");
1835     logToConsole(message);
1836 }
1837
1838 void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const
1839 {
1840     ASSERT(invalidChar == '#' || invalidChar == '?');
1841
1842     String ignoring = "The fragment identifier, including the '#', will be ignored.";
1843     if (invalidChar == '?')
1844         ignoring = "The query component, including the '?', will be ignored.";
1845     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring);
1846     logToConsole(message);
1847 }
1848
1849 void ContentSecurityPolicy::reportInvalidNonce(const String& nonce) const
1850 {
1851     String message = makeString("Ignoring invalid Content Security Policy script nonce: '", nonce, "'.\n");
1852     logToConsole(message);
1853 }
1854
1855 void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const
1856 {
1857     String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored.");
1858     if (equalIgnoringCase(source, "'none'"))
1859         message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list.");
1860     logToConsole(message);
1861 }
1862
1863 void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const
1864 {
1865     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.");
1866 }
1867
1868 void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const
1869 {
1870     // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number
1871     m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state);
1872 }
1873
1874 void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const
1875 {
1876     InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText);
1877 }
1878
1879 bool ContentSecurityPolicy::experimentalFeaturesEnabled() const
1880 {
1881 #if ENABLE(CSP_NEXT)
1882     return RuntimeEnabledFeatures::sharedFeatures().experimentalContentSecurityPolicyFeaturesEnabled();
1883 #else
1884     return false;
1885 #endif
1886 }
1887
1888 }