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