Remove ResourceLoadInfo
[WebKit-https.git] / Source / WebCore / platform / network / ResourceResponseBase.cpp
1 /*
2  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "ResourceResponseBase.h"
29
30 #include "HTTPParsers.h"
31 #include "PlatformMemoryInstrumentation.h"
32 #include "ResourceResponse.h"
33 #include <wtf/CurrentTime.h>
34 #include <wtf/MathExtras.h>
35 #include <wtf/MemoryInstrumentationHashMap.h>
36 #include <wtf/StdLibExtras.h>
37
38 using namespace std;
39
40 namespace WebCore {
41
42 static void parseCacheHeader(const String& header, Vector<pair<String, String> >& result);
43
44 inline const ResourceResponse& ResourceResponseBase::asResourceResponse() const
45 {
46     return *static_cast<const ResourceResponse*>(this);
47 }
48
49 ResourceResponseBase::ResourceResponseBase()  
50     : m_expectedContentLength(0)
51     , m_httpStatusCode(0)
52     , m_lastModifiedDate(0)
53     , m_wasCached(false)
54     , m_connectionID(0)
55     , m_connectionReused(false)
56     , m_isNull(true)
57     , m_haveParsedCacheControlHeader(false)
58     , m_haveParsedAgeHeader(false)
59     , m_haveParsedDateHeader(false)
60     , m_haveParsedExpiresHeader(false)
61     , m_haveParsedLastModifiedHeader(false)
62     , m_cacheControlContainsNoCache(false)
63     , m_cacheControlContainsNoStore(false)
64     , m_cacheControlContainsMustRevalidate(false)
65     , m_cacheControlMaxAge(0.0)
66     , m_age(0.0)
67     , m_date(0.0)
68     , m_expires(0.0)
69     , m_lastModified(0.0)
70 {
71 }
72
73 ResourceResponseBase::ResourceResponseBase(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename)
74     : m_url(url)
75     , m_mimeType(mimeType)
76     , m_expectedContentLength(expectedLength)
77     , m_textEncodingName(textEncodingName)
78     , m_suggestedFilename(filename)
79     , m_httpStatusCode(0)
80     , m_lastModifiedDate(0)
81     , m_wasCached(false)
82     , m_connectionID(0)
83     , m_connectionReused(false)
84     , m_isNull(false)
85     , m_haveParsedCacheControlHeader(false)
86     , m_haveParsedAgeHeader(false)
87     , m_haveParsedDateHeader(false)
88     , m_haveParsedExpiresHeader(false)
89     , m_haveParsedLastModifiedHeader(false)
90     , m_cacheControlContainsNoCache(false)
91     , m_cacheControlContainsNoStore(false)
92     , m_cacheControlContainsMustRevalidate(false)
93     , m_cacheControlMaxAge(0.0)
94     , m_age(0.0)
95     , m_date(0.0)
96     , m_expires(0.0)
97     , m_lastModified(0.0)
98 {
99 }
100
101 PassOwnPtr<ResourceResponse> ResourceResponseBase::adopt(PassOwnPtr<CrossThreadResourceResponseData> data)
102 {
103     OwnPtr<ResourceResponse> response = adoptPtr(new ResourceResponse);
104     response->setURL(data->m_url);
105     response->setMimeType(data->m_mimeType);
106     response->setExpectedContentLength(data->m_expectedContentLength);
107     response->setTextEncodingName(data->m_textEncodingName);
108     response->setSuggestedFilename(data->m_suggestedFilename);
109
110     response->setHTTPStatusCode(data->m_httpStatusCode);
111     response->setHTTPStatusText(data->m_httpStatusText);
112
113     response->lazyInit(CommonAndUncommonFields);
114     response->m_httpHeaderFields.adopt(data->m_httpHeaders.release());
115     response->setLastModifiedDate(data->m_lastModifiedDate);
116     response->setResourceLoadTiming(data->m_resourceLoadTiming.release());
117     response->doPlatformAdopt(data);
118     return response.release();
119 }
120
121 PassOwnPtr<CrossThreadResourceResponseData> ResourceResponseBase::copyData() const
122 {
123     OwnPtr<CrossThreadResourceResponseData> data = adoptPtr(new CrossThreadResourceResponseData);
124     data->m_url = url().copy();
125     data->m_mimeType = mimeType().isolatedCopy();
126     data->m_expectedContentLength = expectedContentLength();
127     data->m_textEncodingName = textEncodingName().isolatedCopy();
128     data->m_suggestedFilename = suggestedFilename().isolatedCopy();
129     data->m_httpStatusCode = httpStatusCode();
130     data->m_httpStatusText = httpStatusText().isolatedCopy();
131     data->m_httpHeaders = httpHeaderFields().copyData();
132     data->m_lastModifiedDate = lastModifiedDate();
133     if (m_resourceLoadTiming)
134         data->m_resourceLoadTiming = m_resourceLoadTiming->deepCopy();
135     return asResourceResponse().doPlatformCopyData(data.release());
136 }
137
138 bool ResourceResponseBase::isHTTP() const
139 {
140     lazyInit(CommonFieldsOnly);
141
142     String protocol = m_url.protocol();
143
144     return equalIgnoringCase(protocol, "http")  || equalIgnoringCase(protocol, "https");
145 }
146
147 const KURL& ResourceResponseBase::url() const
148 {
149     lazyInit(CommonFieldsOnly);
150
151     return m_url; 
152 }
153
154 void ResourceResponseBase::setURL(const KURL& url)
155 {
156     lazyInit(CommonFieldsOnly);
157     m_isNull = false;
158
159     m_url = url;
160
161     // FIXME: Should invalidate or update platform response if present.
162 }
163
164 const String& ResourceResponseBase::mimeType() const
165 {
166     lazyInit(CommonFieldsOnly);
167
168     return m_mimeType; 
169 }
170
171 void ResourceResponseBase::setMimeType(const String& mimeType)
172 {
173     lazyInit(CommonFieldsOnly);
174     m_isNull = false;
175
176     // FIXME: MIME type is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_mimeType.
177     m_mimeType = mimeType;
178
179     // FIXME: Should invalidate or update platform response if present.
180 }
181
182 long long ResourceResponseBase::expectedContentLength() const 
183 {
184     lazyInit(CommonFieldsOnly);
185
186     return m_expectedContentLength;
187 }
188
189 void ResourceResponseBase::setExpectedContentLength(long long expectedContentLength)
190 {
191     lazyInit(CommonFieldsOnly);
192     m_isNull = false;
193
194     // FIXME: Content length is determined by HTTP Content-Length header. We should update the header, so that it doesn't disagree with m_expectedContentLength.
195     m_expectedContentLength = expectedContentLength; 
196
197     // FIXME: Should invalidate or update platform response if present.
198 }
199
200 const String& ResourceResponseBase::textEncodingName() const
201 {
202     lazyInit(CommonFieldsOnly);
203
204     return m_textEncodingName;
205 }
206
207 void ResourceResponseBase::setTextEncodingName(const String& encodingName)
208 {
209     lazyInit(CommonFieldsOnly);
210     m_isNull = false;
211
212     // FIXME: Text encoding is determined by HTTP Content-Type header. We should update the header, so that it doesn't disagree with m_textEncodingName.
213     m_textEncodingName = encodingName;
214
215     // FIXME: Should invalidate or update platform response if present.
216 }
217
218 // FIXME should compute this on the fly
219 const String& ResourceResponseBase::suggestedFilename() const
220 {
221     lazyInit(AllFields);
222
223     return m_suggestedFilename;
224 }
225
226 void ResourceResponseBase::setSuggestedFilename(const String& suggestedName)
227 {
228     lazyInit(AllFields);
229     m_isNull = false;
230
231     // FIXME: Suggested file name is calculated based on other headers. There should not be a setter for it.
232     m_suggestedFilename = suggestedName; 
233
234     // FIXME: Should invalidate or update platform response if present.
235 }
236
237 int ResourceResponseBase::httpStatusCode() const
238 {
239     lazyInit(CommonFieldsOnly);
240
241     return m_httpStatusCode;
242 }
243
244 void ResourceResponseBase::setHTTPStatusCode(int statusCode)
245 {
246     lazyInit(CommonFieldsOnly);
247
248     m_httpStatusCode = statusCode;
249
250     // FIXME: Should invalidate or update platform response if present.
251 }
252
253 const String& ResourceResponseBase::httpStatusText() const 
254 {
255     lazyInit(CommonAndUncommonFields);
256
257     return m_httpStatusText; 
258 }
259
260 void ResourceResponseBase::setHTTPStatusText(const String& statusText) 
261 {
262     lazyInit(CommonAndUncommonFields);
263
264     m_httpStatusText = statusText; 
265
266     // FIXME: Should invalidate or update platform response if present.
267 }
268
269 String ResourceResponseBase::httpHeaderField(const AtomicString& name) const
270 {
271     lazyInit(CommonFieldsOnly);
272
273     // If we already have the header, just return it instead of consuming memory by grabing all headers.
274     String value = m_httpHeaderFields.get(name);
275     if (!value.isEmpty())        
276         return value;
277
278     lazyInit(CommonAndUncommonFields);
279
280     return m_httpHeaderFields.get(name); 
281 }
282
283 String ResourceResponseBase::httpHeaderField(const char* name) const
284 {
285     lazyInit(CommonFieldsOnly);
286
287     // If we already have the header, just return it instead of consuming memory by grabing all headers.
288     String value = m_httpHeaderFields.get(name);
289     if (!value.isEmpty())
290         return value;
291
292     lazyInit(CommonAndUncommonFields);
293
294     return m_httpHeaderFields.get(name); 
295 }
296
297 void ResourceResponseBase::updateHeaderParsedState(const AtomicString& name)
298 {
299     DEFINE_STATIC_LOCAL(const AtomicString, ageHeader, ("age", AtomicString::ConstructFromLiteral));
300     DEFINE_STATIC_LOCAL(const AtomicString, cacheControlHeader, ("cache-control", AtomicString::ConstructFromLiteral));
301     DEFINE_STATIC_LOCAL(const AtomicString, dateHeader, ("date", AtomicString::ConstructFromLiteral));
302     DEFINE_STATIC_LOCAL(const AtomicString, expiresHeader, ("expires", AtomicString::ConstructFromLiteral));
303     DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified", AtomicString::ConstructFromLiteral));
304     DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma", AtomicString::ConstructFromLiteral));
305
306     if (equalIgnoringCase(name, ageHeader))
307         m_haveParsedAgeHeader = false;
308     else if (equalIgnoringCase(name, cacheControlHeader) || equalIgnoringCase(name, pragmaHeader))
309         m_haveParsedCacheControlHeader = false;
310     else if (equalIgnoringCase(name, dateHeader))
311         m_haveParsedDateHeader = false;
312     else if (equalIgnoringCase(name, expiresHeader))
313         m_haveParsedExpiresHeader = false;
314     else if (equalIgnoringCase(name, lastModifiedHeader))
315         m_haveParsedLastModifiedHeader = false;
316 }
317
318 void ResourceResponseBase::setHTTPHeaderField(const AtomicString& name, const String& value)
319 {
320     lazyInit(CommonAndUncommonFields);
321
322     updateHeaderParsedState(name);
323
324     m_httpHeaderFields.set(name, value);
325
326     // FIXME: Should invalidate or update platform response if present.
327 }
328
329 void ResourceResponseBase::addHTTPHeaderField(const AtomicString& name, const String& value)
330 {
331     lazyInit(CommonAndUncommonFields);
332
333     updateHeaderParsedState(name);
334
335     HTTPHeaderMap::AddResult result = m_httpHeaderFields.add(name, value);
336     if (!result.isNewEntry)
337         result.iterator->value.append(", " + value);
338 }
339
340 const HTTPHeaderMap& ResourceResponseBase::httpHeaderFields() const
341 {
342     lazyInit(CommonAndUncommonFields);
343
344     return m_httpHeaderFields;
345 }
346
347 void ResourceResponseBase::parseCacheControlDirectives() const
348 {
349     ASSERT(!m_haveParsedCacheControlHeader);
350
351     lazyInit(CommonFieldsOnly);
352
353     m_haveParsedCacheControlHeader = true;
354
355     m_cacheControlContainsMustRevalidate = false;
356     m_cacheControlContainsNoCache = false;
357     m_cacheControlMaxAge = numeric_limits<double>::quiet_NaN();
358
359     DEFINE_STATIC_LOCAL(const AtomicString, cacheControlString, ("cache-control", AtomicString::ConstructFromLiteral));
360     DEFINE_STATIC_LOCAL(const AtomicString, noCacheDirective, ("no-cache", AtomicString::ConstructFromLiteral));
361     DEFINE_STATIC_LOCAL(const AtomicString, noStoreDirective, ("no-store", AtomicString::ConstructFromLiteral));
362     DEFINE_STATIC_LOCAL(const AtomicString, mustRevalidateDirective, ("must-revalidate", AtomicString::ConstructFromLiteral));
363     DEFINE_STATIC_LOCAL(const AtomicString, maxAgeDirective, ("max-age", AtomicString::ConstructFromLiteral));
364
365     String cacheControlValue = m_httpHeaderFields.get(cacheControlString);
366     if (!cacheControlValue.isEmpty()) {
367         Vector<pair<String, String> > directives;
368         parseCacheHeader(cacheControlValue, directives);
369
370         size_t directivesSize = directives.size();
371         for (size_t i = 0; i < directivesSize; ++i) {
372             // RFC2616 14.9.1: A no-cache directive with a value is only meaningful for proxy caches.
373             // It should be ignored by a browser level cache.
374             if (equalIgnoringCase(directives[i].first, noCacheDirective) && directives[i].second.isEmpty())
375                 m_cacheControlContainsNoCache = true;
376             else if (equalIgnoringCase(directives[i].first, noStoreDirective))
377                 m_cacheControlContainsNoStore = true;
378             else if (equalIgnoringCase(directives[i].first, mustRevalidateDirective))
379                 m_cacheControlContainsMustRevalidate = true;
380             else if (equalIgnoringCase(directives[i].first, maxAgeDirective)) {
381                 if (!std::isnan(m_cacheControlMaxAge)) {
382                     // First max-age directive wins if there are multiple ones.
383                     continue;
384                 }
385                 bool ok;
386                 double maxAge = directives[i].second.toDouble(&ok);
387                 if (ok)
388                     m_cacheControlMaxAge = maxAge;
389             }
390         }
391     }
392
393     if (!m_cacheControlContainsNoCache) {
394         // Handle Pragma: no-cache
395         // This is deprecated and equivalent to Cache-control: no-cache
396         // Don't bother tokenizing the value, it is not important
397         DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma", AtomicString::ConstructFromLiteral));
398         String pragmaValue = m_httpHeaderFields.get(pragmaHeader);
399
400         m_cacheControlContainsNoCache = pragmaValue.lower().contains(noCacheDirective);
401     }
402 }
403     
404 bool ResourceResponseBase::cacheControlContainsNoCache() const
405 {
406     if (!m_haveParsedCacheControlHeader)
407         parseCacheControlDirectives();
408     return m_cacheControlContainsNoCache;
409 }
410
411 bool ResourceResponseBase::cacheControlContainsNoStore() const
412 {
413     if (!m_haveParsedCacheControlHeader)
414         parseCacheControlDirectives();
415     return m_cacheControlContainsNoStore;
416 }
417
418 bool ResourceResponseBase::cacheControlContainsMustRevalidate() const
419 {
420     if (!m_haveParsedCacheControlHeader)
421         parseCacheControlDirectives();
422     return m_cacheControlContainsMustRevalidate;
423 }
424
425 bool ResourceResponseBase::hasCacheValidatorFields() const
426 {
427     lazyInit(CommonFieldsOnly);
428
429     DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified", AtomicString::ConstructFromLiteral));
430     DEFINE_STATIC_LOCAL(const AtomicString, eTagHeader, ("etag", AtomicString::ConstructFromLiteral));
431     return !m_httpHeaderFields.get(lastModifiedHeader).isEmpty() || !m_httpHeaderFields.get(eTagHeader).isEmpty();
432 }
433
434 double ResourceResponseBase::cacheControlMaxAge() const
435 {
436     if (!m_haveParsedCacheControlHeader)
437         parseCacheControlDirectives();
438     return m_cacheControlMaxAge;
439 }
440
441 static double parseDateValueInHeader(const HTTPHeaderMap& headers, const AtomicString& headerName)
442 {
443     String headerValue = headers.get(headerName);
444     if (headerValue.isEmpty())
445         return std::numeric_limits<double>::quiet_NaN(); 
446     // This handles all date formats required by RFC2616:
447     // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
448     // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
449     // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
450     double dateInMilliseconds = parseDate(headerValue);
451     if (!std::isfinite(dateInMilliseconds))
452         return std::numeric_limits<double>::quiet_NaN();
453     return dateInMilliseconds / 1000;
454 }
455
456 double ResourceResponseBase::date() const
457 {
458     lazyInit(CommonFieldsOnly);
459
460     if (!m_haveParsedDateHeader) {
461         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("date", AtomicString::ConstructFromLiteral));
462         m_date = parseDateValueInHeader(m_httpHeaderFields, headerName);
463         m_haveParsedDateHeader = true;
464     }
465     return m_date;
466 }
467
468 double ResourceResponseBase::age() const
469 {
470     lazyInit(CommonFieldsOnly);
471
472     if (!m_haveParsedAgeHeader) {
473         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("age", AtomicString::ConstructFromLiteral));
474         String headerValue = m_httpHeaderFields.get(headerName);
475         bool ok;
476         m_age = headerValue.toDouble(&ok);
477         if (!ok)
478             m_age = std::numeric_limits<double>::quiet_NaN();
479         m_haveParsedAgeHeader = true;
480     }
481     return m_age;
482 }
483
484 double ResourceResponseBase::expires() const
485 {
486     lazyInit(CommonFieldsOnly);
487
488     if (!m_haveParsedExpiresHeader) {
489         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("expires", AtomicString::ConstructFromLiteral));
490         m_expires = parseDateValueInHeader(m_httpHeaderFields, headerName);
491         m_haveParsedExpiresHeader = true;
492     }
493     return m_expires;
494 }
495
496 double ResourceResponseBase::lastModified() const
497 {
498     lazyInit(CommonFieldsOnly);
499
500     if (!m_haveParsedLastModifiedHeader) {
501         DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("last-modified", AtomicString::ConstructFromLiteral));
502         m_lastModified = parseDateValueInHeader(m_httpHeaderFields, headerName);
503         m_haveParsedLastModifiedHeader = true;
504     }
505     return m_lastModified;
506 }
507
508 bool ResourceResponseBase::isAttachment() const
509 {
510     lazyInit(CommonAndUncommonFields);
511
512     DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("content-disposition", AtomicString::ConstructFromLiteral));
513     String value = m_httpHeaderFields.get(headerName);
514     size_t loc = value.find(';');
515     if (loc != notFound)
516         value = value.left(loc);
517     value = value.stripWhiteSpace();
518     DEFINE_STATIC_LOCAL(const AtomicString, attachmentString, ("attachment", AtomicString::ConstructFromLiteral));
519     return equalIgnoringCase(value, attachmentString);
520 }
521   
522 void ResourceResponseBase::setLastModifiedDate(time_t lastModifiedDate)
523 {
524     lazyInit(CommonAndUncommonFields);
525
526     m_lastModifiedDate = lastModifiedDate;
527
528     // FIXME: Should invalidate or update platform response if present.
529 }
530
531 time_t ResourceResponseBase::lastModifiedDate() const
532 {
533     lazyInit(CommonAndUncommonFields);
534
535     return m_lastModifiedDate;
536 }
537
538 bool ResourceResponseBase::wasCached() const
539 {
540     lazyInit(CommonAndUncommonFields);
541
542     return m_wasCached;
543 }
544
545 void ResourceResponseBase::setWasCached(bool value)
546 {
547     m_wasCached = value;
548 }
549
550 bool ResourceResponseBase::connectionReused() const
551 {
552     lazyInit(CommonAndUncommonFields);
553
554     return m_connectionReused;
555 }
556
557 void ResourceResponseBase::setConnectionReused(bool connectionReused)
558 {
559     lazyInit(CommonAndUncommonFields);
560
561     m_connectionReused = connectionReused;
562 }
563
564 unsigned ResourceResponseBase::connectionID() const
565 {
566     lazyInit(CommonAndUncommonFields);
567
568     return m_connectionID;
569 }
570
571 void ResourceResponseBase::setConnectionID(unsigned connectionID)
572 {
573     lazyInit(CommonAndUncommonFields);
574
575     m_connectionID = connectionID;
576 }
577
578 ResourceLoadTiming* ResourceResponseBase::resourceLoadTiming() const
579 {
580     lazyInit(CommonAndUncommonFields);
581
582     return m_resourceLoadTiming.get();
583 }
584
585 void ResourceResponseBase::setResourceLoadTiming(PassRefPtr<ResourceLoadTiming> resourceLoadTiming)
586 {
587     lazyInit(CommonAndUncommonFields);
588
589     m_resourceLoadTiming = resourceLoadTiming;
590 }
591
592 void ResourceResponseBase::lazyInit(InitLevel initLevel) const
593 {
594     const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit(initLevel);
595 }
596
597 void ResourceResponseBase::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
598 {
599     MemoryClassInfo info(memoryObjectInfo, this, PlatformMemoryTypes::Loader);
600     info.addMember(m_url, "url");
601     info.addMember(m_mimeType, "mimeType");
602     info.addMember(m_textEncodingName, "textEncodingName");
603     info.addMember(m_suggestedFilename, "suggestedFilename");
604     info.addMember(m_httpStatusText, "httpStatusText");
605     info.addMember(m_httpHeaderFields, "httpHeaderFields");
606     info.addMember(m_resourceLoadTiming, "resourceLoadTiming");
607 }
608     
609 bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResponse& b)
610 {
611     if (a.isNull() != b.isNull())
612         return false;  
613     if (a.url() != b.url())
614         return false;
615     if (a.mimeType() != b.mimeType())
616         return false;
617     if (a.expectedContentLength() != b.expectedContentLength())
618         return false;
619     if (a.textEncodingName() != b.textEncodingName())
620         return false;
621     if (a.suggestedFilename() != b.suggestedFilename())
622         return false;
623     if (a.httpStatusCode() != b.httpStatusCode())
624         return false;
625     if (a.httpStatusText() != b.httpStatusText())
626         return false;
627     if (a.httpHeaderFields() != b.httpHeaderFields())
628         return false;
629     if (a.resourceLoadTiming() && b.resourceLoadTiming() && *a.resourceLoadTiming() == *b.resourceLoadTiming())
630         return ResourceResponse::platformCompare(a, b);
631     if (a.resourceLoadTiming() != b.resourceLoadTiming())
632         return false;
633     return ResourceResponse::platformCompare(a, b);
634 }
635
636 static bool isCacheHeaderSeparator(UChar c)
637 {
638     // See RFC 2616, Section 2.2
639     switch (c) {
640         case '(':
641         case ')':
642         case '<':
643         case '>':
644         case '@':
645         case ',':
646         case ';':
647         case ':':
648         case '\\':
649         case '"':
650         case '/':
651         case '[':
652         case ']':
653         case '?':
654         case '=':
655         case '{':
656         case '}':
657         case ' ':
658         case '\t':
659             return true;
660         default:
661             return false;
662     }
663 }
664
665 static bool isControlCharacter(UChar c)
666 {
667     return c < ' ' || c == 127;
668 }
669
670 static inline String trimToNextSeparator(const String& str)
671 {
672     return str.substring(0, str.find(isCacheHeaderSeparator));
673 }
674
675 static void parseCacheHeader(const String& header, Vector<pair<String, String> >& result)
676 {
677     const String safeHeader = header.removeCharacters(isControlCharacter);
678     unsigned max = safeHeader.length();
679     for (unsigned pos = 0; pos < max; /* pos incremented in loop */) {
680         size_t nextCommaPosition = safeHeader.find(',', pos);
681         size_t nextEqualSignPosition = safeHeader.find('=', pos);
682         if (nextEqualSignPosition != notFound && (nextEqualSignPosition < nextCommaPosition || nextCommaPosition == notFound)) {
683             // Get directive name, parse right hand side of equal sign, then add to map
684             String directive = trimToNextSeparator(safeHeader.substring(pos, nextEqualSignPosition - pos).stripWhiteSpace());
685             pos += nextEqualSignPosition - pos + 1;
686
687             String value = safeHeader.substring(pos, max - pos).stripWhiteSpace();
688             if (value[0] == '"') {
689                 // The value is a quoted string
690                 size_t nextDoubleQuotePosition = value.find('"', 1);
691                 if (nextDoubleQuotePosition != notFound) {
692                     // Store the value as a quoted string without quotes
693                     result.append(pair<String, String>(directive, value.substring(1, nextDoubleQuotePosition - 1).stripWhiteSpace()));
694                     pos += (safeHeader.find('"', pos) - pos) + nextDoubleQuotePosition + 1;
695                     // Move past next comma, if there is one
696                     size_t nextCommaPosition2 = safeHeader.find(',', pos);
697                     if (nextCommaPosition2 != notFound)
698                         pos += nextCommaPosition2 - pos + 1;
699                     else
700                         return; // Parse error if there is anything left with no comma
701                 } else {
702                     // Parse error; just use the rest as the value
703                     result.append(pair<String, String>(directive, trimToNextSeparator(value.substring(1, value.length() - 1).stripWhiteSpace())));
704                     return;
705                 }
706             } else {
707                 // The value is a token until the next comma
708                 size_t nextCommaPosition2 = value.find(',');
709                 if (nextCommaPosition2 != notFound) {
710                     // The value is delimited by the next comma
711                     result.append(pair<String, String>(directive, trimToNextSeparator(value.substring(0, nextCommaPosition2).stripWhiteSpace())));
712                     pos += (safeHeader.find(',', pos) - pos) + 1;
713                 } else {
714                     // The rest is the value; no change to value needed
715                     result.append(pair<String, String>(directive, trimToNextSeparator(value)));
716                     return;
717                 }
718             }
719         } else if (nextCommaPosition != notFound && (nextCommaPosition < nextEqualSignPosition || nextEqualSignPosition == notFound)) {
720             // Add directive to map with empty string as value
721             result.append(pair<String, String>(trimToNextSeparator(safeHeader.substring(pos, nextCommaPosition - pos).stripWhiteSpace()), ""));
722             pos += nextCommaPosition - pos + 1;
723         } else {
724             // Add last directive to map with empty string as value
725             result.append(pair<String, String>(trimToNextSeparator(safeHeader.substring(pos, max - pos).stripWhiteSpace()), ""));
726             return;
727         }
728     }
729 }
730
731 }