1/*
2 * Copyright (C) 2008 Apple 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 APPLE 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 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 "ApplicationCache.h"
28
29#include "ApplicationCacheGroup.h"
30#include "ApplicationCacheResource.h"
31#include "ApplicationCacheStorage.h"
32#include "ResourceRequest.h"
33#include "SecurityOrigin.h"
34#include <algorithm>
35#include <stdio.h>
36#include <wtf/text/CString.h>
37
38namespace WebCore {
39
40static inline bool fallbackURLLongerThan(const std::pair<URL, URL>& lhs, const std::pair<URL, URL>& rhs)
41{
42    return lhs.first.string().length() > rhs.first.string().length();
43}
44
45ApplicationCache::ApplicationCache()
46    : m_group(0)
47    , m_manifest(0)
48    , m_estimatedSizeInStorage(0)
49    , m_storageID(0)
50{
51}
52
53ApplicationCache::~ApplicationCache()
54{
55    if (m_group && !m_group->isCopy())
56        m_group->cacheDestroyed(this);
57}
58
59void ApplicationCache::setGroup(ApplicationCacheGroup* group)
60{
61    ASSERT(!m_group || group == m_group);
62    m_group = group;
63}
64
65bool ApplicationCache::isComplete() const
66{
67    return !m_group->cacheIsBeingUpdated(this);
68}
69
70void ApplicationCache::setManifestResource(PassRefPtr<ApplicationCacheResource> manifest)
71{
72    ASSERT(manifest);
73    ASSERT(!m_manifest);
74    ASSERT(manifest->type() & ApplicationCacheResource::Manifest);
75
76    m_manifest = manifest.get();
77
78    addResource(manifest);
79}
80
81void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource)
82{
83    ASSERT(resource);
84
85    const String& url = resource->url();
86
87    ASSERT(!m_resources.contains(url));
88
89    if (m_storageID) {
90        ASSERT(!resource->storageID());
91        ASSERT(resource->type() & ApplicationCacheResource::Master);
92
93        // Add the resource to the storage.
94        cacheStorage().store(resource.get(), this);
95    }
96
97    m_estimatedSizeInStorage += resource->estimatedSizeInStorage();
98
99    m_resources.set(url, resource);
100}
101
102unsigned ApplicationCache::removeResource(const String& url)
103{
104    HashMap<String, RefPtr<ApplicationCacheResource>>::iterator it = m_resources.find(url);
105    if (it == m_resources.end())
106        return 0;
107
108    // The resource exists, get its type so we can return it.
109    unsigned type = it->value->type();
110
111    m_estimatedSizeInStorage -= it->value->estimatedSizeInStorage();
112
113    m_resources.remove(it);
114
115    return type;
116}
117
118ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url)
119{
120    ASSERT(!URL(ParsedURLString, url).hasFragmentIdentifier());
121    return m_resources.get(url);
122}
123
124bool ApplicationCache::requestIsHTTPOrHTTPSGet(const ResourceRequest& request)
125{
126    if (!request.url().protocolIsInHTTPFamily())
127        return false;
128
129    if (!equalIgnoringCase(request.httpMethod(), "GET"))
130        return false;
131
132    return true;
133}
134
135ApplicationCacheResource* ApplicationCache::resourceForRequest(const ResourceRequest& request)
136{
137    // We only care about HTTP/HTTPS GET requests.
138    if (!requestIsHTTPOrHTTPSGet(request))
139        return 0;
140
141    URL url(request.url());
142    if (url.hasFragmentIdentifier())
143        url.removeFragmentIdentifier();
144
145    return resourceForURL(url);
146}
147
148void ApplicationCache::setOnlineWhitelist(const Vector<URL>& onlineWhitelist)
149{
150    ASSERT(m_onlineWhitelist.isEmpty());
151    m_onlineWhitelist = onlineWhitelist;
152}
153
154bool ApplicationCache::isURLInOnlineWhitelist(const URL& url)
155{
156    size_t whitelistSize = m_onlineWhitelist.size();
157    for (size_t i = 0; i < whitelistSize; ++i) {
158        if (protocolHostAndPortAreEqual(url, m_onlineWhitelist[i]) && url.string().startsWith(m_onlineWhitelist[i].string()))
159            return true;
160    }
161    return false;
162}
163
164void ApplicationCache::setFallbackURLs(const FallbackURLVector& fallbackURLs)
165{
166    ASSERT(m_fallbackURLs.isEmpty());
167    m_fallbackURLs = fallbackURLs;
168    // FIXME: What's the right behavior if we have 2 or more identical namespace URLs?
169    std::stable_sort(m_fallbackURLs.begin(), m_fallbackURLs.end(), fallbackURLLongerThan);
170}
171
172bool ApplicationCache::urlMatchesFallbackNamespace(const URL& url, URL* fallbackURL)
173{
174    size_t fallbackCount = m_fallbackURLs.size();
175    for (size_t i = 0; i < fallbackCount; ++i) {
176        if (protocolHostAndPortAreEqual(url, m_fallbackURLs[i].first) && url.string().startsWith(m_fallbackURLs[i].first.string())) {
177            if (fallbackURL)
178                *fallbackURL = m_fallbackURLs[i].second;
179            return true;
180        }
181    }
182    return false;
183}
184
185void ApplicationCache::clearStorageID()
186{
187    m_storageID = 0;
188
189    for (const auto& resource : m_resources.values())
190        resource->clearStorageID();
191}
192
193void ApplicationCache::deleteCacheForOrigin(SecurityOrigin* origin)
194{
195    Vector<URL> urls;
196    if (!cacheStorage().manifestURLs(&urls)) {
197        LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs");
198        return;
199    }
200
201    URL originURL(URL(), origin->toString());
202
203    size_t count = urls.size();
204    for (size_t i = 0; i < count; ++i) {
205        if (protocolHostAndPortAreEqual(urls[i], originURL)) {
206            ApplicationCacheGroup* group = cacheStorage().findInMemoryCacheGroup(urls[i]);
207            if (group)
208                group->makeObsolete();
209            else
210                cacheStorage().deleteCacheGroup(urls[i]);
211        }
212    }
213}
214
215int64_t ApplicationCache::diskUsageForOrigin(SecurityOrigin* origin)
216{
217    int64_t usage = 0;
218    cacheStorage().calculateUsageForOrigin(origin, usage);
219    return usage;
220}
221
222#ifndef NDEBUG
223void ApplicationCache::dump()
224{
225    for (const auto& urlAndResource : m_resources) {
226        printf("%s ", urlAndResource.key.utf8().data());
227        ApplicationCacheResource::dumpType(urlAndResource.value->type());
228    }
229}
230#endif
231
232}
233