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