cc38a2bfd644b4394f7b1e6d31a6ded8556d8fce
[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, SecurityOrigin* origin)
462         : m_sourceList(origin)
463         , m_text(name + ' ' + value)
464     {
465         m_sourceList.parse(value);
466     }
467
468     bool allows(const KURL& url)
469     {
470         return m_sourceList.matches(url);
471     }
472
473     bool allowInline() const { return m_sourceList.allowInline(); }
474     bool allowEval() const { return m_sourceList.allowEval(); }
475
476     const String& text() { return m_text; }
477
478 private:
479     CSPSourceList m_sourceList;
480     String m_text;
481 };
482
483 ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext* scriptExecutionContext)
484     : m_havePolicy(false)
485     , m_scriptExecutionContext(scriptExecutionContext)
486     , m_reportOnly(false)
487 {
488 }
489
490 ContentSecurityPolicy::~ContentSecurityPolicy()
491 {
492 }
493
494 void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type)
495 {
496     if (m_havePolicy)
497         return; // The first policy wins.
498
499     parse(header);
500     m_havePolicy = true;
501
502     switch (type) {
503     case ReportOnly:
504         m_reportOnly = true;
505         return;
506     case EnforcePolicy:
507         ASSERT(!m_reportOnly);
508         break;
509     }
510
511     if (!checkEval(operativeDirective(m_scriptSrc.get())))
512         m_scriptExecutionContext->disableEval();
513 }
514
515 void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& consoleMessage) const
516 {
517     String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage;
518     m_scriptExecutionContext->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String(), 0);
519
520     if (m_reportURLs.isEmpty())
521         return;
522
523     // FIXME: Support sending reports from worker.
524     if (!m_scriptExecutionContext->isDocument())
525         return;
526
527     Document* document = static_cast<Document*>(m_scriptExecutionContext);
528     Frame* frame = document->frame();
529     if (!frame)
530         return;
531
532     // We need to be careful here when deciding what information to send to the
533     // report-uri. Currently, we send only the current document's URL and the
534     // directive that was violated. The document's URL is safe to send because
535     // it's the document itself that's requesting that it be sent. You could
536     // make an argument that we shouldn't send HTTPS document URLs to HTTP
537     // report-uris (for the same reasons that we supress the Referer in that
538     // case), but the Referer is sent implicitly whereas this request is only
539     // sent explicitly. As for which directive was violated, that's pretty
540     // harmless information.
541
542     FormDataList reportList(UTF8Encoding());
543     reportList.appendData("document-url", document->url());
544     if (!directiveText.isEmpty())
545         reportList.appendData("violated-directive", directiveText);
546
547     RefPtr<FormData> report = FormData::create(reportList, UTF8Encoding());
548
549     for (size_t i = 0; i < m_reportURLs.size(); ++i)
550         PingLoader::reportContentSecurityPolicyViolation(frame, m_reportURLs[i], report);
551 }
552
553 void ContentSecurityPolicy::logUnrecognizedDirective(const String& name) const
554 {
555     String message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n");
556     m_scriptExecutionContext->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String(), 0);
557 }
558
559 bool ContentSecurityPolicy::checkEval(CSPDirective* directive) const
560 {
561     return !directive || directive->allowEval();
562 }
563
564 CSPDirective* ContentSecurityPolicy::operativeDirective(CSPDirective* directive) const
565 {
566     return directive ? directive : m_defaultSrc.get();
567 }
568
569 bool ContentSecurityPolicy::checkInlineAndReportViolation(CSPDirective* directive, const String& consoleMessage) const
570 {
571     if (!directive || directive->allowInline())
572         return true;
573     reportViolation(directive->text(), consoleMessage);
574     return denyIfEnforcingPolicy();
575 }
576
577 bool ContentSecurityPolicy::checkEvalAndReportViolation(CSPDirective* directive, const String& consoleMessage) const
578 {
579     if (checkEval(directive))
580         return true;
581     reportViolation(directive->text(), consoleMessage);
582     return denyIfEnforcingPolicy();
583 }
584
585 bool ContentSecurityPolicy::checkSourceAndReportViolation(CSPDirective* directive, const KURL& url, const String& type) const
586 {
587     if (!directive || directive->allows(url))
588         return true;
589     reportViolation(directive->text(), "Refused to load " + type + " from '" + url.string() + "' because of Content-Security-Policy.\n");
590     return denyIfEnforcingPolicy();
591 }
592
593 bool ContentSecurityPolicy::allowJavaScriptURLs() const
594 {
595     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute JavaScript URL because of Content-Security-Policy.\n"));
596     return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
597 }
598
599 bool ContentSecurityPolicy::allowInlineEventHandlers() const
600 {
601     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline event handler because of Content-Security-Policy.\n"));
602     return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
603 }
604
605 bool ContentSecurityPolicy::allowInlineScript() const
606 {
607     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute inline script because of Content-Security-Policy.\n"));
608     return checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
609 }
610
611 bool ContentSecurityPolicy::allowInlineStyle() const
612 {
613     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to apply inline style because of Content-Security-Policy.\n"));
614     return checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage);
615 }
616
617 bool ContentSecurityPolicy::allowEval() const
618 {
619     DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to evaluate script because of Content-Security-Policy.\n"));
620     return checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage);
621 }
622
623 bool ContentSecurityPolicy::allowScriptFromSource(const KURL& url) const
624 {
625     DEFINE_STATIC_LOCAL(String, type, ("script"));
626     return checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, type);
627 }
628
629 bool ContentSecurityPolicy::allowObjectFromSource(const KURL& url) const
630 {
631     DEFINE_STATIC_LOCAL(String, type, ("object"));
632     return checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, type);
633 }
634
635 bool ContentSecurityPolicy::allowChildFrameFromSource(const KURL& url) const
636 {
637     DEFINE_STATIC_LOCAL(String, type, ("frame"));
638     return checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, type);
639 }
640
641 bool ContentSecurityPolicy::allowImageFromSource(const KURL& url) const
642 {
643     DEFINE_STATIC_LOCAL(String, type, ("image"));
644     return checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, type);
645 }
646
647 bool ContentSecurityPolicy::allowStyleFromSource(const KURL& url) const
648 {
649     DEFINE_STATIC_LOCAL(String, type, ("style"));
650     return checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, type);
651 }
652
653 bool ContentSecurityPolicy::allowFontFromSource(const KURL& url) const
654 {
655     DEFINE_STATIC_LOCAL(String, type, ("font"));
656     return checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, type);
657 }
658
659 bool ContentSecurityPolicy::allowMediaFromSource(const KURL& url) const
660 {
661     DEFINE_STATIC_LOCAL(String, type, ("media"));
662     return checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, type);
663 }
664
665 bool ContentSecurityPolicy::allowConnectFromSource(const KURL& url) const
666 {
667     DEFINE_STATIC_LOCAL(String, type, ("connect"));
668     return checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, type);
669 }
670
671 // policy            = directive-list
672 // directive-list    = [ directive *( ";" [ directive ] ) ]
673 //
674 void ContentSecurityPolicy::parse(const String& policy)
675 {
676     ASSERT(!m_havePolicy);
677
678     if (policy.isEmpty())
679         return;
680
681     const UChar* position = policy.characters();
682     const UChar* end = position + policy.length();
683
684     while (position < end) {
685         const UChar* directiveBegin = position;
686         skipUtil(position, end, ';');
687
688         String name, value;
689         if (parseDirective(directiveBegin, position, name, value)) {
690             ASSERT(!name.isEmpty());
691             addDirective(name, value);
692         }
693
694         ASSERT(position == end || *position == ';');
695         skipExactly(position, end, ';');
696     }
697 }
698
699 // directive         = *WSP [ directive-name [ WSP directive-value ] ]
700 // directive-name    = 1*( ALPHA / DIGIT / "-" )
701 // directive-value   = *( WSP / <VCHAR except ";"> )
702 //
703 bool ContentSecurityPolicy::parseDirective(const UChar* begin, const UChar* end, String& name, String& value)
704 {
705     ASSERT(name.isEmpty());
706     ASSERT(value.isEmpty());
707
708     const UChar* position = begin;
709     skipWhile<isASCIISpace>(position, end);
710
711     const UChar* nameBegin = position;
712     skipWhile<isDirectiveNameCharacter>(position, end);
713
714     // The directive-name must be non-empty.
715     if (nameBegin == position)
716         return false;
717
718     name = String(nameBegin, position - nameBegin);
719
720     if (position == end)
721         return true;
722
723     if (!skipExactly<isASCIISpace>(position, end))
724         return false;
725
726     skipWhile<isASCIISpace>(position, end);
727
728     const UChar* valueBegin = position;
729     skipWhile<isDirectiveValueCharacter>(position, end);
730
731     if (position != end)
732         return false;
733
734     // The directive-value may be empty.
735     if (valueBegin == position)
736         return true;
737
738     value = String(valueBegin, position - valueBegin);
739     return true;
740 }
741
742 void ContentSecurityPolicy::parseReportURI(const String& value)
743 {
744     const UChar* position = value.characters();
745     const UChar* end = position + value.length();
746
747     while (position < end) {
748         skipWhile<isASCIISpace>(position, end);
749
750         const UChar* urlBegin = position;
751         skipWhile<isNotASCIISpace>(position, end);
752
753         if (urlBegin < position) {
754             String url = String(urlBegin, position - urlBegin);
755             m_reportURLs.append(m_scriptExecutionContext->completeURL(url));
756         }
757     }
758 }
759
760 PassOwnPtr<CSPDirective> ContentSecurityPolicy::createCSPDirective(const String& name, const String& value)
761 {
762     return adoptPtr(new CSPDirective(name, value, m_scriptExecutionContext->securityOrigin()));
763 }
764
765 void ContentSecurityPolicy::addDirective(const String& name, const String& value)
766 {
767     DEFINE_STATIC_LOCAL(String, defaultSrc, ("default-src"));
768     DEFINE_STATIC_LOCAL(String, scriptSrc, ("script-src"));
769     DEFINE_STATIC_LOCAL(String, objectSrc, ("object-src"));
770     DEFINE_STATIC_LOCAL(String, frameSrc, ("frame-src"));
771     DEFINE_STATIC_LOCAL(String, imgSrc, ("img-src"));
772     DEFINE_STATIC_LOCAL(String, styleSrc, ("style-src"));
773     DEFINE_STATIC_LOCAL(String, fontSrc, ("font-src"));
774     DEFINE_STATIC_LOCAL(String, mediaSrc, ("media-src"));
775     DEFINE_STATIC_LOCAL(String, connectSrc, ("connect-src"));
776     DEFINE_STATIC_LOCAL(String, reportURI, ("report-uri"));
777
778     ASSERT(!name.isEmpty());
779
780     if (!m_defaultSrc && equalIgnoringCase(name, defaultSrc))
781         m_defaultSrc = createCSPDirective(name, value);
782     else if (!m_scriptSrc && equalIgnoringCase(name, scriptSrc))
783         m_scriptSrc = createCSPDirective(name, value);
784     else if (!m_objectSrc && equalIgnoringCase(name, objectSrc))
785         m_objectSrc = createCSPDirective(name, value);
786     else if (!m_frameSrc && equalIgnoringCase(name, frameSrc))
787         m_frameSrc = createCSPDirective(name, value);
788     else if (!m_imgSrc && equalIgnoringCase(name, imgSrc))
789         m_imgSrc = createCSPDirective(name, value);
790     else if (!m_styleSrc && equalIgnoringCase(name, styleSrc))
791         m_styleSrc = createCSPDirective(name, value);
792     else if (!m_fontSrc && equalIgnoringCase(name, fontSrc))
793         m_fontSrc = createCSPDirective(name, value);
794     else if (!m_mediaSrc && equalIgnoringCase(name, mediaSrc))
795         m_mediaSrc = createCSPDirective(name, value);
796     else if (!m_connectSrc && equalIgnoringCase(name, connectSrc))
797         m_connectSrc = createCSPDirective(name, value);
798     else if (m_reportURLs.isEmpty() && equalIgnoringCase(name, reportURI))
799         parseReportURI(value);
800     else
801         logUnrecognizedDirective(name);
802 }
803
804 }