CSP should handle empty URLs as agreed at TPAC
[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 "Document.h"
31 #include "FormData.h"
32 #include "FormDataList.h"
33 #include "Frame.h"
34 #include "PingLoader.h"
35 #include "ScriptCallStack.h"
36 #include "SecurityOrigin.h"
37 #include "TextEncoding.h"
38 #include <wtf/text/WTFString.h>
39
40 namespace WebCore {
41
42 // Normally WebKit uses "static" for internal linkage, but using "static" for
43 // these functions causes a compile error because these functions are used as
44 // template parameters.
45 namespace {
46
47 bool isDirectiveNameCharacter(UChar c)
48 {
49     return isASCIIAlphanumeric(c) || c == '-';
50 }
51
52 bool isDirectiveValueCharacter(UChar c)
53 {
54     return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR
55 }
56
57 bool isSourceCharacter(UChar c)
58 {
59     return !isASCIISpace(c);
60 }
61
62 bool isHostCharacter(UChar c)
63 {
64     return isASCIIAlphanumeric(c) || c == '-';
65 }
66
67 bool isSchemeContinuationCharacter(UChar c)
68 {
69     return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.';
70 }
71
72 bool isNotASCIISpace(UChar c)
73 {
74     return !isASCIISpace(c);
75 }
76
77 } // namespace
78
79 static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter)
80 {
81     if (position < end && *position == delimiter) {
82         ++position;
83         return true;
84     }
85     return false;
86 }
87
88 template<bool characterPredicate(UChar)>
89 static bool skipExactly(const UChar*& position, const UChar* end)
90 {
91     if (position < end && characterPredicate(*position)) {
92         ++position;
93         return true;
94     }
95     return false;
96 }
97
98 static void skipUtil(const UChar*& position, const UChar* end, UChar delimiter)
99 {
100     while (position < end && *position != delimiter)
101         ++position;
102 }
103
104 template<bool characterPredicate(UChar)>
105 static void skipWhile(const UChar*& position, const UChar* end)
106 {
107     while (position < end && characterPredicate(*position))
108         ++position;
109 }
110
111 class CSPSource {
112 public:
113     CSPSource(const String& scheme, const String& host, int port, bool hostHasWildcard, bool portHasWildcard)
114         : m_scheme(scheme)
115         , m_host(host)
116         , m_port(port)
117         , m_hostHasWildcard(hostHasWildcard)
118         , m_portHasWildcard(portHasWildcard)
119     {
120     }
121
122     bool matches(const KURL& url) const
123     {
124         if (!schemeMatches(url))
125             return false;
126         if (isSchemeOnly())
127             return true;
128         return hostMatches(url) && portMatches(url);
129     }
130
131 private:
132     bool schemeMatches(const KURL& url) const
133     {
134         return equalIgnoringCase(url.protocol(), m_scheme);
135     }
136
137     bool hostMatches(const KURL& url) const
138     {
139         const String& host = url.host();
140         if (equalIgnoringCase(host, m_host))
141             return true;
142         return m_hostHasWildcard && host.endsWith("." + m_host, false);
143
144     }
145
146     bool portMatches(const KURL& url) const
147     {
148         if (m_portHasWildcard)
149             return true;
150
151         int port = url.port();
152
153         if (port == m_port)
154             return true;
155
156         if (!port)
157             return isDefaultPortForProtocol(m_port, m_scheme);
158
159         if (!m_port)
160             return isDefaultPortForProtocol(port, m_scheme);
161
162         return false;
163     }
164
165     bool isSchemeOnly() const { return m_host.isEmpty(); }
166
167     String m_scheme;
168     String m_host;
169     int m_port;
170
171     bool m_hostHasWildcard;
172     bool m_portHasWildcard;
173 };
174
175 class CSPSourceList {
176 public:
177     explicit CSPSourceList(SecurityOrigin*);
178
179     void parse(const String&);
180     bool matches(const KURL&);
181     bool allowInline() const { return m_allowInline; }
182     bool allowEval() const { return m_allowEval; }
183
184 private:
185     void parse(const UChar* begin, const UChar* end);
186
187     bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, bool& hostHasWildcard, bool& portHasWildcard);
188     bool parseScheme(const UChar* begin, const UChar* end, String& scheme);
189     bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard);
190     bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard);
191
192     void addSourceSelf();
193     void addSourceStar();
194     void addSourceUnsafeInline();
195     void addSourceUnsafeEval();
196
197     SecurityOrigin* m_origin;
198     Vector<CSPSource> m_list;
199     bool m_allowStar;
200     bool m_allowInline;
201     bool m_allowEval;
202 };
203
204 CSPSourceList::CSPSourceList(SecurityOrigin* origin)
205     : m_origin(origin)
206     , m_allowStar(false)
207     , m_allowInline(false)
208     , m_allowEval(false)
209 {
210 }
211
212 void CSPSourceList::parse(const String& value)
213 {
214     parse(value.characters(), value.characters() + value.length());
215 }
216
217 bool CSPSourceList::matches(const KURL& url)
218 {
219     if (m_allowStar)
220         return true;
221
222     for (size_t i = 0; i < m_list.size(); ++i) {
223         if (m_list[i].matches(url))
224             return true;
225     }
226
227     return false;
228 }
229
230 // source-list       = *WSP [ source *( 1*WSP source ) *WSP ]
231 //                   / *WSP "'none'" *WSP
232 //
233 void CSPSourceList::parse(const UChar* begin, const UChar* end)
234 {
235     const UChar* position = begin;
236
237     bool isFirstSourceInList = true;
238     while (position < end) {
239         skipWhile<isASCIISpace>(position, end);
240         const UChar* beginSource = position;
241         skipWhile<isSourceCharacter>(position, end);
242
243         if (isFirstSourceInList && equalIgnoringCase("'none'", beginSource, position - beginSource))
244             return; // We represent 'none' as an empty m_list.
245         isFirstSourceInList = false;
246
247         String scheme, host;
248         int port = 0;
249         bool hostHasWildcard = false;
250         bool portHasWildcard = false;
251
252         if (parseSource(beginSource, position, scheme, host, port, hostHasWildcard, portHasWildcard)) {
253             if (scheme.isEmpty())
254                 scheme = m_origin->protocol();
255             m_list.append(CSPSource(scheme, host, port, hostHasWildcard, portHasWildcard));
256         }
257
258         ASSERT(position == end || isASCIISpace(*position));
259      }
260 }
261
262 // source            = scheme ":"
263 //                   / ( [ scheme "://" ] host [ port ] )
264 //                   / "'self'"
265 //
266 bool CSPSourceList::parseSource(const UChar* begin, const UChar* end,
267                                 String& scheme, String& host, int& port,
268                                 bool& hostHasWildcard, bool& portHasWildcard)
269 {
270     if (begin == end)
271         return false;
272
273     if (end - begin == 1 && *begin == '*') {
274         addSourceStar();
275         return false;
276     }
277
278     if (equalIgnoringCase("'self'", begin, end - begin)) {
279         addSourceSelf();
280         return false;
281     }
282
283     if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) {
284         addSourceUnsafeInline();
285         return false;
286     }
287
288     if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) {
289         addSourceUnsafeEval();
290         return false;
291     }
292
293     const UChar* position = begin;
294
295     const UChar* beginHost = begin;
296     skipUtil(position, end, ':');
297
298     if (position == end) {
299         // This must be a host-only source.
300         if (!parseHost(beginHost, position, host, hostHasWildcard))
301             return false;
302         return true;
303     }
304
305     if (end - position == 1) {
306         ASSERT(*position == ':');
307         // This must be a scheme-only source.
308         if (!parseScheme(begin, position, scheme))
309             return false;
310         return true;
311     }
312
313     ASSERT(end - position >= 2);
314     if (position[1] == '/') {
315         if (!parseScheme(begin, position, scheme)
316             || !skipExactly(position, end, ':')
317             || !skipExactly(position, end, '/')
318             || !skipExactly(position, end, '/'))
319             return false;
320         beginHost = position;
321         skipUtil(position, end, ':');
322     }
323
324     if (position == beginHost)
325         return false;
326
327     if (!parseHost(beginHost, position, host, hostHasWildcard))
328         return false;
329
330     if (position == end) {
331         port = 0;
332         return true;
333     }
334
335     if (!skipExactly(position, end, ':'))
336         ASSERT_NOT_REACHED();
337
338     if (!parsePort(position, end, port, portHasWildcard))
339         return false;
340
341     return true;
342 }
343
344 //                     ; <scheme> production from RFC 3986
345 // scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
346 //
347 bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme)
348 {
349     ASSERT(begin <= end);
350     ASSERT(scheme.isEmpty());
351
352     if (begin == end)
353         return false;
354
355     const UChar* position = begin;
356
357     if (!skipExactly<isASCIIAlpha>(position, end))
358         return false;
359
360     skipWhile<isSchemeContinuationCharacter>(position, end);
361
362     if (position != end)
363         return false;
364
365     scheme = String(begin, end - begin);
366     return true;
367 }
368
369 // host              = [ "*." ] 1*host-char *( "." 1*host-char )
370 //                   / "*"
371 // host-char         = ALPHA / DIGIT / "-"
372 //
373 bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard)
374 {
375     ASSERT(begin <= end);
376     ASSERT(host.isEmpty());
377     ASSERT(!hostHasWildcard);
378
379     if (begin == end)
380         return false;
381
382     const UChar* position = begin;
383
384     if (skipExactly(position, end, '*')) {
385         hostHasWildcard = true;
386
387         if (position == end)
388             return true;
389
390         if (!skipExactly(position, end, '.'))
391             return false;
392     }
393
394     const UChar* hostBegin = position;
395
396     while (position < end) {
397         if (!skipExactly<isHostCharacter>(position, end))
398             return false;
399
400         skipWhile<isHostCharacter>(position, end);
401
402         if (position < end && !skipExactly(position, end, '.'))
403             return false;
404     }
405
406     ASSERT(position == end);
407     host = String(hostBegin, end - hostBegin);
408     return true;
409 }
410
411 // port              = ":" ( 1*DIGIT / "*" )
412 //
413 bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard)
414 {
415     ASSERT(begin <= end);
416     ASSERT(!port);
417     ASSERT(!portHasWildcard);
418
419     if (begin == end)
420         return false;
421
422     if (end - begin == 1 && *begin == '*') {
423         port = 0;
424         portHasWildcard = true;
425         return true;
426     }
427
428     const UChar* position = begin;
429     skipWhile<isASCIIDigit>(position, end);
430
431     if (position != end)
432         return false;
433
434     bool ok;
435     port = charactersToIntStrict(begin, end - begin, &ok);
436     return ok;
437 }
438
439 void CSPSourceList::addSourceSelf()
440 {
441     m_list.append(CSPSource(m_origin->protocol(), m_origin->host(), m_origin->port(), false, false));
442 }
443
444 void CSPSourceList::addSourceStar()
445 {
446     m_allowStar = true;
447 }
448
449 void CSPSourceList::addSourceUnsafeInline()
450 {
451     m_allowInline = true;
452 }
453
454 void CSPSourceList::addSourceUnsafeEval()
455 {
456     m_allowEval = true;
457 }
458
459 class CSPDirective {
460 public:
461     CSPDirective(const String& name, const String& value, ScriptExecutionContext* context)
462         : m_sourceList(context->securityOrigin())
463         , m_text(name + ' ' + value)
464         , m_selfURL(context->url())
465     {
466         m_sourceList.parse(value);
467     }
468
469     bool allows(const KURL& url)
470     {
471         return m_sourceList.matches(url.isEmpty() ? m_selfURL : url);
472     }
473
474     bool allowInline() const { return m_sourceList.allowInline(); }
475     bool allowEval() const { return m_sourceList.allowEval(); }
476
477     const String& text() { return m_text; }
478
479 private:
480     CSPSourceList m_sourceList;
481     String m_text;
482     KURL m_selfURL;
483 };
484
485 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext* scriptExecutionContext)
486     : m_havePolicy(false)
487     , m_scriptExecutionContext(scriptExecutionContext)
488     , m_reportOnly(false)
489 {
490 }
491
492 ContentSecurityPolicy::~ContentSecurityPolicy()
493 {
494 }
495
496 void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type)
497 {
498     if (m_havePolicy)
499         return; // The first policy wins.
500
501     parse(header);
502     m_havePolicy = true;
503
504     switch (type) {
505     case ReportOnly:
506         m_reportOnly = true;
507         return;
508     case EnforcePolicy:
509         ASSERT(!m_reportOnly);
510         break;
511     }
512
513     if (!checkEval(operativeDirective(m_scriptSrc.get())))
514         m_scriptExecutionContext->disableEval();
515 }
516
517 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& consoleMessage) const
518 {
519     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
520     m_scriptExecutionContext->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String(), 0);
521
522     if (m_reportURLs.isEmpty())
523         return;
524
525     // FIXME: Support sending reports from worker.
526     if (!m_scriptExecutionContext->isDocument())
527         return;
528
529     Document* document = static_cast<Document*>(m_scriptExecutionContext);
530     Frame* frame = document->frame();
531     if (!frame)
532         return;
533
534     // We need to be careful here when deciding what information to send to the
535     // report-uri. Currently, we send only the current document's URL and the
536     // directive that was violated. The document's URL is safe to send because
537     // it's the document itself that's requesting that it be sent. You could
538     // make an argument that we shouldn't send HTTPS document URLs to HTTP
539     // report-uris (for the same reasons that we supress the Referer in that
540     // case), but the Referer is sent implicitly whereas this request is only
541     // sent explicitly. As for which directive was violated, that's pretty
542     // harmless information.
543
544     FormDataList reportList(UTF8Encoding());
545     reportList.appendData("document-url", document->url());
546     if (!directiveText.isEmpty())
547         reportList.appendData("violated-directive", directiveText);
548
549     RefPtr<FormData> report = FormData::create(reportList, UTF8Encoding());
550
551     for (size_t i = 0; i < m_reportURLs.size(); ++i)
552         PingLoader::reportContentSecurityPolicyViolation(frame, m_reportURLs[i], report);
553 }
554
555 void ContentSecurityPolicy::logUnrecognizedDirective(const String& name) const
556 {
557     String message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n");
558     m_scriptExecutionContext->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String(), 0);
559 }
560
561 bool ContentSecurityPolicy::checkEval(CSPDirective* directive) const
562 {
563     return !directive || directive->allowEval();
564 }
565
566 CSPDirective* ContentSecurityPolicy::operativeDirective(CSPDirective* directive) const
567 {
568     return directive ? directive : m_defaultSrc.get();
569 }
570
571 bool ContentSecurityPolicy::checkInlineAndReportViolation(CSPDirective* directive, const String& consoleMessage) const
572 {
573     if (!directive || directive->allowInline())
574         return true;
575     reportViolation(directive->text(), consoleMessage);
576     return denyIfEnforcingPolicy();
577 }
578
579 bool ContentSecurityPolicy::checkEvalAndReportViolation(CSPDirective* directive, const String& consoleMessage) const
580 {
581     if (checkEval(directive))
582         return true;
583     reportViolation(directive->text(), consoleMessage);
584     return denyIfEnforcingPolicy();
585 }
586
587 bool ContentSecurityPolicy::checkSourceAndReportViolation(CSPDirective* directive, const KURL& url, const String& type) const
588 {
589     if (!directive || directive->allows(url))
590         return true;
591     reportViolation(directive->text(), "Refused to load " + type + " from '" + url.string() + "' because of Content-Security-Policy.\n");
592     return denyIfEnforcingPolicy();
593 }
594
595 bool ContentSecurityPolicy::allowJavaScriptURLs() const
596 {
597     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute JavaScript URL because of Content-Security-Policy.\n"));
598     return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
599 }
600
601 bool ContentSecurityPolicy::allowInlineEventHandlers() const
602 {
603     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline event handler because of Content-Security-Policy.\n"));
604     return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
605 }
606
607 bool ContentSecurityPolicy::allowInlineScript() const
608 {
609     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline script because of Content-Security-Policy.\n"));
610     return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
611 }
612
613 bool ContentSecurityPolicy::allowInlineStyle() const
614 {
615     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to apply inline style because of Content-Security-Policy.\n"));
616     return checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage);
617 }
618
619 bool ContentSecurityPolicy::allowEval() const
620 {
621     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to evaluate script because of Content-Security-Policy.\n"));
622     return checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
623 }
624
625 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url) const
626 {
627     DEFINE_STATIC_LOCAL(String, type, ("script"));
628     return checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, type);
629 }
630
631 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url) const
632 {
633     DEFINE_STATIC_LOCAL(String, type, ("object"));
634     return checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, type);
635 }
636
637 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url) const
638 {
639     DEFINE_STATIC_LOCAL(String, type, ("frame"));
640     return checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, type);
641 }
642
643 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url) const
644 {
645     DEFINE_STATIC_LOCAL(String, type, ("image"));
646     return checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, type);
647 }
648
649 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url) const
650 {
651     DEFINE_STATIC_LOCAL(String, type, ("style"));
652     return checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, type);
653 }
654
655 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url) const
656 {
657     DEFINE_STATIC_LOCAL(String, type, ("font"));
658     return checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, type);
659 }
660
661 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url) const
662 {
663     DEFINE_STATIC_LOCAL(String, type, ("media"));
664     return checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, type);
665 }
666
667 bool ContentSecurityPolicy::allowConnectFromSource(const KURL& url) const
668 {
669     DEFINE_STATIC_LOCAL(String, type, ("connect"));
670     return checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, type);
671 }
672
673 // policy            = directive-list
674 // directive-list    = [ directive *( ";" [ directive ] ) ]
675 //
676 void ContentSecurityPolicy::parse(const String& policy)
677 {
678     ASSERT(!m_havePolicy);
679
680     if (policy.isEmpty())
681         return;
682
683     const UChar* position = policy.characters();
684     const UChar* end = position + policy.length();
685
686     while (position < end) {
687         const UChar* directiveBegin = position;
688         skipUtil(position, end, ';');
689
690         String name, value;
691         if (parseDirective(directiveBegin, position, name, value)) {
692             ASSERT(!name.isEmpty());
693             addDirective(name, value);
694         }
695
696         ASSERT(position == end || *position == ';');
697         skipExactly(position, end, ';');
698     }
699 }
700
701 // directive         = *WSP [ directive-name [ WSP directive-value ] ]
702 // directive-name    = 1*( ALPHA / DIGIT / "-" )
703 // directive-value   = *( WSP / <VCHAR except ";"> )
704 //
705 bool ContentSecurityPolicy::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
706 {
707     ASSERT(name.isEmpty());
708     ASSERT(value.isEmpty());
709
710     const UChar* position = begin;
711     skipWhile<isASCIISpace>(position, end);
712
713     const UChar* nameBegin = position;
714     skipWhile<isDirectiveNameCharacter>(position, end);
715
716     // The directive-name must be non-empty.
717     if (nameBegin == position)
718         return false;
719
720     name = String(nameBegin, position - nameBegin);
721
722     if (position == end)
723         return true;
724
725     if (!skipExactly<isASCIISpace>(position, end))
726         return false;
727
728     skipWhile<isASCIISpace>(position, end);
729
730     const UChar* valueBegin = position;
731     skipWhile<isDirectiveValueCharacter>(position, end);
732
733     if (position != end)
734         return false;
735
736     // The directive-value may be empty.
737     if (valueBegin == position)
738         return true;
739
740     value = String(valueBegin, position - valueBegin);
741     return true;
742 }
743
744 void ContentSecurityPolicy::parseReportURI(const String& value)
745 {
746     const UChar* position = value.characters();
747     const UChar* end = position + value.length();
748
749     while (position < end) {
750         skipWhile<isASCIISpace>(position, end);
751
752         const UChar* urlBegin = position;
753         skipWhile<isNotASCIISpace>(position, end);
754
755         if (urlBegin < position) {
756             String url = String(urlBegin, position - urlBegin);
757             m_reportURLs.append(m_scriptExecutionContext->completeURL(url));
758         }
759     }
760 }
761
762 PassOwnPtr<CSPDirective> ContentSecurityPolicy::createCSPDirective(const String& name, const String& value)
763 {
764     return adoptPtr(new CSPDirective(name, value, m_scriptExecutionContext));
765 }
766
767 void ContentSecurityPolicy::addDirective(const String& name, const String& value)
768 {
769     DEFINE_STATIC_LOCAL(String, defaultSrc, ("default-src"));
770     DEFINE_STATIC_LOCAL(String, scriptSrc, ("script-src"));
771     DEFINE_STATIC_LOCAL(String, objectSrc, ("object-src"));
772     DEFINE_STATIC_LOCAL(String, frameSrc, ("frame-src"));
773     DEFINE_STATIC_LOCAL(String, imgSrc, ("img-src"));
774     DEFINE_STATIC_LOCAL(String, styleSrc, ("style-src"));
775     DEFINE_STATIC_LOCAL(String, fontSrc, ("font-src"));
776     DEFINE_STATIC_LOCAL(String, mediaSrc, ("media-src"));
777     DEFINE_STATIC_LOCAL(String, connectSrc, ("connect-src"));
778     DEFINE_STATIC_LOCAL(String, reportURI, ("report-uri"));
779
780     ASSERT(!name.isEmpty());
781
782     if (!m_defaultSrc && equalIgnoringCase(name, defaultSrc))
783         m_defaultSrc = createCSPDirective(name, value);
784     else if (!m_scriptSrc && equalIgnoringCase(name, scriptSrc))
785         m_scriptSrc = createCSPDirective(name, value);
786     else if (!m_objectSrc && equalIgnoringCase(name, objectSrc))
787         m_objectSrc = createCSPDirective(name, value);
788     else if (!m_frameSrc && equalIgnoringCase(name, frameSrc))
789         m_frameSrc = createCSPDirective(name, value);
790     else if (!m_imgSrc && equalIgnoringCase(name, imgSrc))
791         m_imgSrc = createCSPDirective(name, value);
792     else if (!m_styleSrc && equalIgnoringCase(name, styleSrc))
793         m_styleSrc = createCSPDirective(name, value);
794     else if (!m_fontSrc && equalIgnoringCase(name, fontSrc))
795         m_fontSrc = createCSPDirective(name, value);
796     else if (!m_mediaSrc && equalIgnoringCase(name, mediaSrc))
797         m_mediaSrc = createCSPDirective(name, value);
798     else if (!m_connectSrc && equalIgnoringCase(name, connectSrc))
799         m_connectSrc = createCSPDirective(name, value);
800     else if (m_reportURLs.isEmpty() && equalIgnoringCase(name, reportURI))
801         parseReportURI(value);
802     else
803         logUnrecognizedDirective(name);
804 }
805
806 }