~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/loader/cache/CachedResource.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
 
3
    Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
 
4
    Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
 
5
    Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
 
6
    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
 
7
 
 
8
    This library is free software; you can redistribute it and/or
 
9
    modify it under the terms of the GNU Library General Public
 
10
    License as published by the Free Software Foundation; either
 
11
    version 2 of the License, or (at your option) any later version.
 
12
 
 
13
    This library is distributed in the hope that it will be useful,
 
14
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
    Library General Public License for more details.
 
17
 
 
18
    You should have received a copy of the GNU Library General Public License
 
19
    along with this library; see the file COPYING.LIB.  If not, write to
 
20
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
21
    Boston, MA 02110-1301, USA.
 
22
*/
 
23
 
 
24
#include "config.h"
 
25
#include "CachedResource.h"
 
26
 
 
27
#include "MemoryCache.h"
 
28
#include "CachedMetadata.h"
 
29
#include "CachedResourceClient.h"
 
30
#include "CachedResourceClientWalker.h"
 
31
#include "CachedResourceHandle.h"
 
32
#include "CachedResourceLoader.h"
 
33
#include "CrossOriginAccessControl.h"
 
34
#include "Document.h"
 
35
#include "DocumentLoader.h"
 
36
#include "FrameLoaderClient.h"
 
37
#include "InspectorInstrumentation.h"
 
38
#include "KURL.h"
 
39
#include "LoaderStrategy.h"
 
40
#include "Logging.h"
 
41
#include "PlatformStrategies.h"
 
42
#include "PurgeableBuffer.h"
 
43
#include "ResourceBuffer.h"
 
44
#include "ResourceHandle.h"
 
45
#include "ResourceLoadScheduler.h"
 
46
#include "SecurityOrigin.h"
 
47
#include "SecurityPolicy.h"
 
48
#include "SubresourceLoader.h"
 
49
#include "WebCoreMemoryInstrumentation.h"
 
50
#include <wtf/CurrentTime.h>
 
51
#include <wtf/MathExtras.h>
 
52
#include <wtf/MemoryInstrumentationHashCountedSet.h>
 
53
#include <wtf/MemoryInstrumentationHashSet.h>
 
54
#include <wtf/RefCountedLeakCounter.h>
 
55
#include <wtf/StdLibExtras.h>
 
56
#include <wtf/text/CString.h>
 
57
#include <wtf/Vector.h>
 
58
 
 
59
namespace WTF {
 
60
 
 
61
template<> struct SequenceMemoryInstrumentationTraits<WebCore::CachedResourceClient*> {
 
62
    template <typename I> static void reportMemoryUsage(I, I, MemoryClassInfo&) { }
 
63
};
 
64
 
 
65
}
 
66
 
 
67
using namespace WTF;
 
68
 
 
69
namespace WebCore {
 
70
    
 
71
static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
 
72
{
 
73
    switch (type) {
 
74
    case CachedResource::MainResource:
 
75
        return ResourceLoadPriorityVeryHigh;
 
76
    case CachedResource::CSSStyleSheet:
 
77
        return ResourceLoadPriorityHigh;
 
78
    case CachedResource::Script:
 
79
    case CachedResource::FontResource:
 
80
    case CachedResource::RawResource:
 
81
        return ResourceLoadPriorityMedium;
 
82
    case CachedResource::ImageResource:
 
83
        return ResourceLoadPriorityLow;
 
84
#if ENABLE(XSLT)
 
85
    case CachedResource::XSLStyleSheet:
 
86
        return ResourceLoadPriorityHigh;
 
87
#endif
 
88
#if ENABLE(SVG)
 
89
    case CachedResource::SVGDocumentResource:
 
90
        return ResourceLoadPriorityLow;
 
91
#endif
 
92
#if ENABLE(LINK_PREFETCH)
 
93
    case CachedResource::LinkPrefetch:
 
94
        return ResourceLoadPriorityVeryLow;
 
95
    case CachedResource::LinkSubresource:
 
96
        return ResourceLoadPriorityVeryLow;
 
97
#endif
 
98
#if ENABLE(VIDEO_TRACK)
 
99
    case CachedResource::TextTrackResource:
 
100
        return ResourceLoadPriorityLow;
 
101
#endif
 
102
#if ENABLE(CSS_SHADERS)
 
103
    case CachedResource::ShaderResource:
 
104
        return ResourceLoadPriorityMedium;
 
105
#endif
 
106
    }
 
107
    ASSERT_NOT_REACHED();
 
108
    return ResourceLoadPriorityLow;
 
109
}
 
110
 
 
111
#if PLATFORM(CHROMIUM) || PLATFORM(BLACKBERRY)
 
112
static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
 
113
{
 
114
    switch (type) {
 
115
    case CachedResource::MainResource:
 
116
        return ResourceRequest::TargetIsMainFrame;
 
117
    case CachedResource::CSSStyleSheet:
 
118
#if ENABLE(XSLT)
 
119
    case CachedResource::XSLStyleSheet:
 
120
#endif
 
121
        return ResourceRequest::TargetIsStyleSheet;
 
122
    case CachedResource::Script: 
 
123
        return ResourceRequest::TargetIsScript;
 
124
    case CachedResource::FontResource:
 
125
        return ResourceRequest::TargetIsFontResource;
 
126
    case CachedResource::ImageResource:
 
127
        return ResourceRequest::TargetIsImage;
 
128
#if ENABLE(CSS_SHADERS)
 
129
    case CachedResource::ShaderResource:
 
130
#endif
 
131
    case CachedResource::RawResource:
 
132
        return ResourceRequest::TargetIsSubresource;    
 
133
#if ENABLE(LINK_PREFETCH)
 
134
    case CachedResource::LinkPrefetch:
 
135
        return ResourceRequest::TargetIsPrefetch;
 
136
    case CachedResource::LinkSubresource:
 
137
        return ResourceRequest::TargetIsSubresource;
 
138
#endif
 
139
#if ENABLE(VIDEO_TRACK)
 
140
    case CachedResource::TextTrackResource:
 
141
        return ResourceRequest::TargetIsTextTrack;
 
142
#endif
 
143
#if ENABLE(SVG)
 
144
    case CachedResource::SVGDocumentResource:
 
145
        return ResourceRequest::TargetIsImage;
 
146
#endif
 
147
    }
 
148
    ASSERT_NOT_REACHED();
 
149
    return ResourceRequest::TargetIsSubresource;
 
150
}
 
151
#endif
 
152
 
 
153
DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource"));
 
154
 
 
155
CachedResource::CachedResource(const ResourceRequest& request, Type type)
 
156
    : m_resourceRequest(request)
 
157
    , m_loadPriority(defaultPriorityForResourceType(type))
 
158
    , m_responseTimestamp(currentTime())
 
159
    , m_decodedDataDeletionTimer(this, &CachedResource::decodedDataDeletionTimerFired)
 
160
    , m_lastDecodedAccessTime(0)
 
161
    , m_loadFinishTime(0)
 
162
    , m_encodedSize(0)
 
163
    , m_decodedSize(0)
 
164
    , m_accessCount(0)
 
165
    , m_handleCount(0)
 
166
    , m_preloadCount(0)
 
167
    , m_preloadResult(PreloadNotReferenced)
 
168
    , m_inLiveDecodedResourcesList(false)
 
169
    , m_requestedFromNetworkingLayer(false)
 
170
    , m_inCache(false)
 
171
    , m_loading(false)
 
172
    , m_switchingClientsToRevalidatedResource(false)
 
173
    , m_type(type)
 
174
    , m_status(Pending)
 
175
#ifndef NDEBUG
 
176
    , m_deleted(false)
 
177
    , m_lruIndex(0)
 
178
#endif
 
179
    , m_nextInAllResourcesList(0)
 
180
    , m_prevInAllResourcesList(0)
 
181
    , m_nextInLiveResourcesList(0)
 
182
    , m_prevInLiveResourcesList(0)
 
183
    , m_owningCachedResourceLoader(0)
 
184
    , m_resourceToRevalidate(0)
 
185
    , m_proxyResource(0)
 
186
{
 
187
    ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum.
 
188
#ifndef NDEBUG
 
189
    cachedResourceLeakCounter.increment();
 
190
#endif
 
191
}
 
192
 
 
193
CachedResource::~CachedResource()
 
194
{
 
195
    ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
 
196
    ASSERT(canDelete());
 
197
    ASSERT(!inCache());
 
198
    ASSERT(!m_deleted);
 
199
    ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this);
 
200
 
 
201
#ifndef NDEBUG
 
202
    m_deleted = true;
 
203
    cachedResourceLeakCounter.decrement();
 
204
#endif
 
205
 
 
206
    if (m_owningCachedResourceLoader)
 
207
        m_owningCachedResourceLoader->removeCachedResource(this);
 
208
}
 
209
 
 
210
void CachedResource::failBeforeStarting()
 
211
{
 
212
    // FIXME: What if resources in other frames were waiting for this revalidation?
 
213
    LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data());
 
214
    if (m_resourceToRevalidate) 
 
215
        memoryCache()->revalidationFailed(this); 
 
216
    error(CachedResource::LoadError);
 
217
}
 
218
 
 
219
void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader* cachedResourceLoader)
 
220
{
 
221
    // Note: We skip the Content-Security-Policy check here because we check
 
222
    // the Content-Security-Policy at the CachedResourceLoader layer so we can
 
223
    // handle different resource types differently.
 
224
 
 
225
    FrameLoader* frameLoader = cachedResourceLoader->frame()->loader();
 
226
    String outgoingReferrer;
 
227
    String outgoingOrigin;
 
228
    if (m_resourceRequest.httpReferrer().isNull()) {
 
229
        outgoingReferrer = frameLoader->outgoingReferrer();
 
230
        outgoingOrigin = frameLoader->outgoingOrigin();
 
231
    } else {
 
232
        outgoingReferrer = m_resourceRequest.httpReferrer();
 
233
        outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
 
234
    }
 
235
 
 
236
    outgoingReferrer = SecurityPolicy::generateReferrerHeader(cachedResourceLoader->document()->referrerPolicy(), m_resourceRequest.url(), outgoingReferrer);
 
237
    if (outgoingReferrer.isEmpty())
 
238
        m_resourceRequest.clearHTTPReferrer();
 
239
    else if (!m_resourceRequest.httpReferrer())
 
240
        m_resourceRequest.setHTTPReferrer(outgoingReferrer);
 
241
    FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
 
242
 
 
243
    frameLoader->addExtraFieldsToSubresourceRequest(m_resourceRequest);
 
244
}
 
245
 
 
246
void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
 
247
{
 
248
    if (!cachedResourceLoader->frame()) {
 
249
        failBeforeStarting();
 
250
        return;
 
251
    }
 
252
 
 
253
    FrameLoader* frameLoader = cachedResourceLoader->frame()->loader();
 
254
    if (options.securityCheck == DoSecurityCheck && (frameLoader->state() == FrameStateProvisional || !frameLoader->activeDocumentLoader() || frameLoader->activeDocumentLoader()->isStopping())) {
 
255
        failBeforeStarting();
 
256
        return;
 
257
    }
 
258
 
 
259
    m_options = options;
 
260
    m_loading = true;
 
261
 
 
262
#if PLATFORM(CHROMIUM) || PLATFORM(BLACKBERRY)
 
263
    if (m_resourceRequest.targetType() == ResourceRequest::TargetIsUnspecified)
 
264
        m_resourceRequest.setTargetType(cachedResourceTypeToTargetType(type()));
 
265
#endif
 
266
 
 
267
    if (!accept().isEmpty())
 
268
        m_resourceRequest.setHTTPAccept(accept());
 
269
 
 
270
    if (isCacheValidator()) {
 
271
        CachedResource* resourceToRevalidate = m_resourceToRevalidate;
 
272
        ASSERT(resourceToRevalidate->canUseCacheValidator());
 
273
        ASSERT(resourceToRevalidate->isLoaded());
 
274
        const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
 
275
        const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
 
276
        if (!lastModified.isEmpty() || !eTag.isEmpty()) {
 
277
            ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
 
278
            if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
 
279
                m_resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
 
280
            if (!lastModified.isEmpty())
 
281
                m_resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
 
282
            if (!eTag.isEmpty())
 
283
                m_resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
 
284
        }
 
285
    }
 
286
 
 
287
#if ENABLE(LINK_PREFETCH)
 
288
    if (type() == CachedResource::LinkPrefetch || type() == CachedResource::LinkSubresource)
 
289
        m_resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
 
290
#endif
 
291
    m_resourceRequest.setPriority(loadPriority());
 
292
 
 
293
    if (type() != MainResource)
 
294
        addAdditionalRequestHeaders(cachedResourceLoader);
 
295
 
 
296
#if USE(PLATFORM_STRATEGIES)
 
297
    m_loader = platformStrategies()->loaderStrategy()->resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, m_resourceRequest, m_resourceRequest.priority(), options);
 
298
#else
 
299
    m_loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, m_resourceRequest, m_resourceRequest.priority(), options);
 
300
#endif
 
301
 
 
302
    if (!m_loader) {
 
303
        failBeforeStarting();
 
304
        return;
 
305
    }
 
306
 
 
307
    m_status = Pending;
 
308
}
 
309
 
 
310
void CachedResource::checkNotify()
 
311
{
 
312
    if (isLoading())
 
313
        return;
 
314
 
 
315
    CachedResourceClientWalker<CachedResourceClient> w(m_clients);
 
316
    while (CachedResourceClient* c = w.next())
 
317
        c->notifyFinished(this);
 
318
}
 
319
 
 
320
void CachedResource::data(PassRefPtr<ResourceBuffer>, bool allDataReceived)
 
321
{
 
322
    if (!allDataReceived)
 
323
        return;
 
324
    
 
325
    setLoading(false);
 
326
    checkNotify();
 
327
}
 
328
 
 
329
void CachedResource::error(CachedResource::Status status)
 
330
{
 
331
    setStatus(status);
 
332
    ASSERT(errorOccurred());
 
333
    m_data.clear();
 
334
 
 
335
    setLoading(false);
 
336
    checkNotify();
 
337
}
 
338
 
 
339
void CachedResource::finish()
 
340
{
 
341
    if (!errorOccurred())
 
342
        m_status = Cached;
 
343
}
 
344
 
 
345
bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin)
 
346
{
 
347
    String errorDescription;
 
348
    return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription);
 
349
}
 
350
 
 
351
bool CachedResource::isExpired() const
 
352
{
 
353
    if (m_response.isNull())
 
354
        return false;
 
355
 
 
356
    return currentAge() > freshnessLifetime();
 
357
}
 
358
    
 
359
double CachedResource::currentAge() const
 
360
{
 
361
    // RFC2616 13.2.3
 
362
    // No compensation for latency as that is not terribly important in practice
 
363
    double dateValue = m_response.date();
 
364
    double apparentAge = isfinite(dateValue) ? std::max(0., m_responseTimestamp - dateValue) : 0;
 
365
    double ageValue = m_response.age();
 
366
    double correctedReceivedAge = isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge;
 
367
    double residentTime = currentTime() - m_responseTimestamp;
 
368
    return correctedReceivedAge + residentTime;
 
369
}
 
370
    
 
371
double CachedResource::freshnessLifetime() const
 
372
{
 
373
    // Cache non-http resources liberally
 
374
    if (!m_response.url().protocolIsInHTTPFamily())
 
375
        return std::numeric_limits<double>::max();
 
376
 
 
377
    // RFC2616 13.2.4
 
378
    double maxAgeValue = m_response.cacheControlMaxAge();
 
379
    if (isfinite(maxAgeValue))
 
380
        return maxAgeValue;
 
381
    double expiresValue = m_response.expires();
 
382
    double dateValue = m_response.date();
 
383
    double creationTime = isfinite(dateValue) ? dateValue : m_responseTimestamp;
 
384
    if (isfinite(expiresValue))
 
385
        return expiresValue - creationTime;
 
386
    double lastModifiedValue = m_response.lastModified();
 
387
    if (isfinite(lastModifiedValue))
 
388
        return (creationTime - lastModifiedValue) * 0.1;
 
389
    // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0.
 
390
    return 0;
 
391
}
 
392
 
 
393
void CachedResource::setResponse(const ResourceResponse& response)
 
394
{
 
395
    m_response = response;
 
396
    m_responseTimestamp = currentTime();
 
397
    String encoding = response.textEncodingName();
 
398
    if (!encoding.isNull())
 
399
        setEncoding(encoding);
 
400
}
 
401
 
 
402
void CachedResource::setSerializedCachedMetadata(const char* data, size_t size)
 
403
{
 
404
    // We only expect to receive cached metadata from the platform once.
 
405
    // If this triggers, it indicates an efficiency problem which is most
 
406
    // likely unexpected in code designed to improve performance.
 
407
    ASSERT(!m_cachedMetadata);
 
408
 
 
409
    m_cachedMetadata = CachedMetadata::deserialize(data, size);
 
410
}
 
411
 
 
412
void CachedResource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size)
 
413
{
 
414
    // Currently, only one type of cached metadata per resource is supported.
 
415
    // If the need arises for multiple types of metadata per resource this could
 
416
    // be enhanced to store types of metadata in a map.
 
417
    ASSERT(!m_cachedMetadata);
 
418
 
 
419
    m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size);
 
420
    ResourceHandle::cacheMetadata(m_response, m_cachedMetadata->serialize());
 
421
}
 
422
 
 
423
CachedMetadata* CachedResource::cachedMetadata(unsigned dataTypeID) const
 
424
{
 
425
    if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID)
 
426
        return 0;
 
427
    return m_cachedMetadata.get();
 
428
}
 
429
 
 
430
void CachedResource::stopLoading()
 
431
{
 
432
    ASSERT(m_loader);            
 
433
    m_loader = 0;
 
434
 
 
435
    CachedResourceHandle<CachedResource> protect(this);
 
436
 
 
437
    // All loads finish with data(allDataReceived = true) or error(), except for
 
438
    // canceled loads, which silently set our request to 0. Be sure to notify our
 
439
    // client in that case, so we don't seem to continue loading forever.
 
440
    if (isLoading()) {
 
441
        ASSERT(!m_error.isNull());
 
442
        setLoading(false);
 
443
        setStatus(LoadError);
 
444
        checkNotify();
 
445
    }
 
446
}
 
447
 
 
448
void CachedResource::addClient(CachedResourceClient* client)
 
449
{
 
450
    if (addClientToSet(client))
 
451
        didAddClient(client);
 
452
}
 
453
 
 
454
void CachedResource::didAddClient(CachedResourceClient* c)
 
455
{
 
456
    if (m_decodedDataDeletionTimer.isActive())
 
457
        m_decodedDataDeletionTimer.stop();
 
458
 
 
459
    if (m_clientsAwaitingCallback.contains(c)) {
 
460
        m_clients.add(c);
 
461
        m_clientsAwaitingCallback.remove(c);
 
462
    }
 
463
    if (!isLoading() && !stillNeedsLoad())
 
464
        c->notifyFinished(this);
 
465
}
 
466
 
 
467
bool CachedResource::addClientToSet(CachedResourceClient* client)
 
468
{
 
469
    ASSERT(!isPurgeable());
 
470
 
 
471
    if (m_preloadResult == PreloadNotReferenced) {
 
472
        if (isLoaded())
 
473
            m_preloadResult = PreloadReferencedWhileComplete;
 
474
        else if (m_requestedFromNetworkingLayer)
 
475
            m_preloadResult = PreloadReferencedWhileLoading;
 
476
        else
 
477
            m_preloadResult = PreloadReferenced;
 
478
    }
 
479
    if (!hasClients() && inCache())
 
480
        memoryCache()->addToLiveResourcesSize(this);
 
481
 
 
482
    if (m_type == RawResource && !m_response.isNull() && !m_proxyResource) {
 
483
        // Certain resources (especially XHRs) do crazy things if an asynchronous load returns
 
484
        // synchronously (e.g., scripts may not have set all the state they need to handle the load).
 
485
        // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources,
 
486
        // we schedule the callbacks and ensure we never finish synchronously.
 
487
        ASSERT(!m_clientsAwaitingCallback.contains(client));
 
488
        m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client));
 
489
        return false;
 
490
    }
 
491
 
 
492
    m_clients.add(client);
 
493
    return true;
 
494
}
 
495
 
 
496
void CachedResource::removeClient(CachedResourceClient* client)
 
497
{
 
498
    OwnPtr<CachedResourceCallback> callback = m_clientsAwaitingCallback.take(client);
 
499
    if (callback) {
 
500
        ASSERT(!m_clients.contains(client));
 
501
        callback->cancel();
 
502
        callback.clear();
 
503
    } else {
 
504
        ASSERT(m_clients.contains(client));
 
505
        m_clients.remove(client);
 
506
        didRemoveClient(client);
 
507
    }
 
508
 
 
509
    bool deleted = deleteIfPossible();
 
510
    if (!deleted && !hasClients() && inCache()) {
 
511
        memoryCache()->removeFromLiveResourcesSize(this);
 
512
        memoryCache()->removeFromLiveDecodedResourcesList(this);
 
513
        allClientsRemoved();
 
514
        destroyDecodedDataIfNeeded();
 
515
        if (response().cacheControlContainsNoStore()) {
 
516
            // RFC2616 14.9.2:
 
517
            // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
 
518
            // "... History buffers MAY store such responses as part of their normal operation."
 
519
            // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
 
520
            if (url().protocolIs("https"))
 
521
                memoryCache()->remove(this);
 
522
        } else
 
523
            memoryCache()->prune();
 
524
    }
 
525
    // This object may be dead here.
 
526
}
 
527
 
 
528
void CachedResource::destroyDecodedDataIfNeeded()
 
529
{
 
530
    if (!m_decodedSize)
 
531
        return;
 
532
 
 
533
    if (double interval = memoryCache()->deadDecodedDataDeletionInterval())
 
534
        m_decodedDataDeletionTimer.startOneShot(interval);
 
535
}
 
536
 
 
537
void CachedResource::decodedDataDeletionTimerFired(Timer<CachedResource>*)
 
538
{
 
539
    destroyDecodedData();
 
540
}
 
541
 
 
542
bool CachedResource::deleteIfPossible()
 
543
{
 
544
    if (canDelete() && !inCache()) {
 
545
        InspectorInstrumentation::willDestroyCachedResource(this);
 
546
        delete this;
 
547
        return true;
 
548
    }
 
549
    return false;
 
550
}
 
551
 
 
552
void CachedResource::setDecodedSize(unsigned size)
 
553
{
 
554
    if (size == m_decodedSize)
 
555
        return;
 
556
 
 
557
    int delta = size - m_decodedSize;
 
558
 
 
559
    // The object must now be moved to a different queue, since its size has been changed.
 
560
    // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
 
561
    // queue.
 
562
    if (inCache())
 
563
        memoryCache()->removeFromLRUList(this);
 
564
    
 
565
    m_decodedSize = size;
 
566
   
 
567
    if (inCache()) { 
 
568
        // Now insert into the new LRU list.
 
569
        memoryCache()->insertInLRUList(this);
 
570
        
 
571
        // Insert into or remove from the live decoded list if necessary.
 
572
        // When inserting into the LiveDecodedResourcesList it is possible
 
573
        // that the m_lastDecodedAccessTime is still zero or smaller than
 
574
        // the m_lastDecodedAccessTime of the current list head. This is a
 
575
        // violation of the invariant that the list is to be kept sorted
 
576
        // by access time. The weakening of the invariant does not pose
 
577
        // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
 
578
        if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
 
579
            memoryCache()->insertInLiveDecodedResourcesList(this);
 
580
        else if (!m_decodedSize && m_inLiveDecodedResourcesList)
 
581
            memoryCache()->removeFromLiveDecodedResourcesList(this);
 
582
 
 
583
        // Update the cache's size totals.
 
584
        memoryCache()->adjustSize(hasClients(), delta);
 
585
    }
 
586
}
 
587
 
 
588
void CachedResource::setEncodedSize(unsigned size)
 
589
{
 
590
    if (size == m_encodedSize)
 
591
        return;
 
592
 
 
593
    // The size cannot ever shrink (unless it is being nulled out because of an error).  If it ever does, assert.
 
594
    ASSERT(size == 0 || size >= m_encodedSize);
 
595
    
 
596
    int delta = size - m_encodedSize;
 
597
 
 
598
    // The object must now be moved to a different queue, since its size has been changed.
 
599
    // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
 
600
    // queue.
 
601
    if (inCache())
 
602
        memoryCache()->removeFromLRUList(this);
 
603
    
 
604
    m_encodedSize = size;
 
605
   
 
606
    if (inCache()) { 
 
607
        // Now insert into the new LRU list.
 
608
        memoryCache()->insertInLRUList(this);
 
609
        
 
610
        // Update the cache's size totals.
 
611
        memoryCache()->adjustSize(hasClients(), delta);
 
612
    }
 
613
}
 
614
 
 
615
void CachedResource::didAccessDecodedData(double timeStamp)
 
616
{
 
617
    m_lastDecodedAccessTime = timeStamp;
 
618
    
 
619
    if (inCache()) {
 
620
        if (m_inLiveDecodedResourcesList) {
 
621
            memoryCache()->removeFromLiveDecodedResourcesList(this);
 
622
            memoryCache()->insertInLiveDecodedResourcesList(this);
 
623
        }
 
624
        memoryCache()->prune();
 
625
    }
 
626
}
 
627
    
 
628
void CachedResource::setResourceToRevalidate(CachedResource* resource) 
 
629
 
630
    ASSERT(resource);
 
631
    ASSERT(!m_resourceToRevalidate);
 
632
    ASSERT(resource != this);
 
633
    ASSERT(m_handlesToRevalidate.isEmpty());
 
634
    ASSERT(resource->type() == type());
 
635
 
 
636
    LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource);
 
637
 
 
638
    // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances.
 
639
    // https://bugs.webkit.org/show_bug.cgi?id=28604.
 
640
    // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate.
 
641
    ASSERT(!resource->m_proxyResource);
 
642
 
 
643
    resource->m_proxyResource = this;
 
644
    m_resourceToRevalidate = resource;
 
645
}
 
646
 
 
647
void CachedResource::clearResourceToRevalidate() 
 
648
 
649
    ASSERT(m_resourceToRevalidate);
 
650
    if (m_switchingClientsToRevalidatedResource)
 
651
        return;
 
652
 
 
653
    // A resource may start revalidation before this method has been called, so check that this resource is still the proxy resource before clearing it out.
 
654
    if (m_resourceToRevalidate->m_proxyResource == this) {
 
655
        m_resourceToRevalidate->m_proxyResource = 0;
 
656
        m_resourceToRevalidate->deleteIfPossible();
 
657
    }
 
658
    m_handlesToRevalidate.clear();
 
659
    m_resourceToRevalidate = 0;
 
660
    deleteIfPossible();
 
661
}
 
662
    
 
663
void CachedResource::switchClientsToRevalidatedResource()
 
664
{
 
665
    ASSERT(m_resourceToRevalidate);
 
666
    ASSERT(m_resourceToRevalidate->inCache());
 
667
    ASSERT(!inCache());
 
668
 
 
669
    LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate);
 
670
 
 
671
    m_switchingClientsToRevalidatedResource = true;
 
672
    HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end();
 
673
    for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
 
674
        CachedResourceHandleBase* handle = *it;
 
675
        handle->m_resource = m_resourceToRevalidate;
 
676
        m_resourceToRevalidate->registerHandle(handle);
 
677
        --m_handleCount;
 
678
    }
 
679
    ASSERT(!m_handleCount);
 
680
    m_handlesToRevalidate.clear();
 
681
 
 
682
    Vector<CachedResourceClient*> clientsToMove;
 
683
    HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end();
 
684
    for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
 
685
        CachedResourceClient* client = it->key;
 
686
        unsigned count = it->value;
 
687
        while (count) {
 
688
            clientsToMove.append(client);
 
689
            --count;
 
690
        }
 
691
    }
 
692
    // Equivalent of calling removeClient() for all clients
 
693
    m_clients.clear();
 
694
 
 
695
    unsigned moveCount = clientsToMove.size();
 
696
    for (unsigned n = 0; n < moveCount; ++n)
 
697
        m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
 
698
    for (unsigned n = 0; n < moveCount; ++n) {
 
699
        // Calling didAddClient may do anything, including trying to cancel revalidation.
 
700
        // Assert that it didn't succeed.
 
701
        ASSERT(m_resourceToRevalidate);
 
702
        // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
 
703
        if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n]))
 
704
            m_resourceToRevalidate->didAddClient(clientsToMove[n]);
 
705
    }
 
706
    m_switchingClientsToRevalidatedResource = false;
 
707
}
 
708
    
 
709
void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
 
710
{
 
711
    m_responseTimestamp = currentTime();
 
712
 
 
713
    DEFINE_STATIC_LOCAL(const AtomicString, contentHeaderPrefix, ("content-", AtomicString::ConstructFromLiteral));
 
714
    // RFC2616 10.3.5
 
715
    // Update cached headers from the 304 response
 
716
    const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
 
717
    HTTPHeaderMap::const_iterator end = newHeaders.end();
 
718
    for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) {
 
719
        // Don't allow 304 response to update content headers, these can't change but some servers send wrong values.
 
720
        if (it->key.startsWith(contentHeaderPrefix, false))
 
721
            continue;
 
722
        m_response.setHTTPHeaderField(it->key, it->value);
 
723
    }
 
724
}
 
725
 
 
726
void CachedResource::registerHandle(CachedResourceHandleBase* h)
 
727
{
 
728
    ++m_handleCount;
 
729
    if (m_resourceToRevalidate)
 
730
        m_handlesToRevalidate.add(h);
 
731
}
 
732
 
 
733
void CachedResource::unregisterHandle(CachedResourceHandleBase* h)
 
734
{
 
735
    ASSERT(m_handleCount > 0);
 
736
    --m_handleCount;
 
737
 
 
738
    if (m_resourceToRevalidate)
 
739
         m_handlesToRevalidate.remove(h);
 
740
 
 
741
    if (!m_handleCount)
 
742
        deleteIfPossible();
 
743
}
 
744
 
 
745
bool CachedResource::canUseCacheValidator() const
 
746
{
 
747
    if (m_loading || errorOccurred())
 
748
        return false;
 
749
 
 
750
    if (m_response.cacheControlContainsNoStore())
 
751
        return false;
 
752
    return m_response.hasCacheValidatorFields();
 
753
}
 
754
 
 
755
bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const
 
756
{    
 
757
    ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify);
 
758
 
 
759
    if (cachePolicy == CachePolicyRevalidate)
 
760
        return true;
 
761
 
 
762
    if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
 
763
        LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
 
764
        return true;
 
765
    }
 
766
 
 
767
    if (cachePolicy == CachePolicyCache) {
 
768
        if (m_response.cacheControlContainsMustRevalidate() && isExpired()) {
 
769
            LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this);
 
770
            return true;
 
771
        }
 
772
        return false;
 
773
    }
 
774
 
 
775
    // CachePolicyVerify
 
776
    if (isExpired()) {
 
777
        LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
 
778
        return true;
 
779
    }
 
780
 
 
781
    return false;
 
782
}
 
783
 
 
784
bool CachedResource::isSafeToMakePurgeable() const
 
785
 
786
    return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
 
787
}
 
788
 
 
789
bool CachedResource::makePurgeable(bool purgeable) 
 
790
 
791
    if (purgeable) {
 
792
        ASSERT(isSafeToMakePurgeable());
 
793
 
 
794
        if (m_purgeableData) {
 
795
            ASSERT(!m_data);
 
796
            return true;
 
797
        }
 
798
        if (!m_data)
 
799
            return false;
 
800
        
 
801
        // Should not make buffer purgeable if it has refs other than this since we don't want two copies.
 
802
        if (!m_data->hasOneRef())
 
803
            return false;
 
804
 
 
805
        m_data->createPurgeableBuffer();
 
806
        if (!m_data->hasPurgeableBuffer())
 
807
            return false;
 
808
 
 
809
        m_purgeableData = m_data->releasePurgeableBuffer();
 
810
        m_purgeableData->setPurgePriority(purgePriority());
 
811
        m_purgeableData->makePurgeable(true);
 
812
        m_data.clear();
 
813
        return true;
 
814
    }
 
815
 
 
816
    if (!m_purgeableData)
 
817
        return true;
 
818
    ASSERT(!m_data);
 
819
    ASSERT(!hasClients());
 
820
 
 
821
    if (!m_purgeableData->makePurgeable(false))
 
822
        return false; 
 
823
 
 
824
    m_data = ResourceBuffer::adoptSharedBuffer(SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()));
 
825
    return true;
 
826
}
 
827
 
 
828
bool CachedResource::isPurgeable() const
 
829
{
 
830
    return m_purgeableData && m_purgeableData->isPurgeable();
 
831
}
 
832
 
 
833
bool CachedResource::wasPurged() const
 
834
{
 
835
    return m_purgeableData && m_purgeableData->wasPurged();
 
836
}
 
837
 
 
838
unsigned CachedResource::overheadSize() const
 
839
{
 
840
    static const int kAverageClientsHashMapSize = 384;
 
841
    return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2;
 
842
}
 
843
    
 
844
void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority) 
 
845
 
846
    if (loadPriority == ResourceLoadPriorityUnresolved)
 
847
        return;
 
848
    m_loadPriority = loadPriority;
 
849
}
 
850
 
 
851
 
 
852
CachedResource::CachedResourceCallback::CachedResourceCallback(CachedResource* resource, CachedResourceClient* client)
 
853
    : m_resource(resource)
 
854
    , m_client(client)
 
855
    , m_callbackTimer(this, &CachedResourceCallback::timerFired)
 
856
{
 
857
    m_callbackTimer.startOneShot(0);
 
858
}
 
859
 
 
860
void CachedResource::CachedResourceCallback::cancel()
 
861
{
 
862
    if (m_callbackTimer.isActive())
 
863
        m_callbackTimer.stop();
 
864
}
 
865
 
 
866
void CachedResource::CachedResourceCallback::timerFired(Timer<CachedResourceCallback>*)
 
867
{
 
868
    m_resource->didAddClient(m_client);
 
869
}
 
870
 
 
871
void CachedResource::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
 
872
{
 
873
    MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CachedResource);
 
874
    info.addMember(m_resourceRequest);
 
875
    info.addMember(m_clients);
 
876
    info.addMember(m_accept);
 
877
    info.addMember(m_loader);
 
878
    info.addMember(m_response);
 
879
    info.addMember(m_data);
 
880
    info.addMember(m_cachedMetadata);
 
881
    info.addMember(m_nextInAllResourcesList);
 
882
    info.addMember(m_prevInAllResourcesList);
 
883
    info.addMember(m_nextInLiveResourcesList);
 
884
    info.addMember(m_prevInLiveResourcesList);
 
885
    info.addMember(m_owningCachedResourceLoader);
 
886
    info.addMember(m_resourceToRevalidate);
 
887
    info.addMember(m_proxyResource);
 
888
    info.addMember(m_handlesToRevalidate);
 
889
 
 
890
    if (m_purgeableData && !m_purgeableData->wasPurged())
 
891
        info.addRawBuffer(m_purgeableData.get(), m_purgeableData->size());
 
892
}
 
893
 
 
894
}