OSDN Git Service

am 381c572c: (-s ours) am f0073a99: Use STLPort instead of our stripped version....
[android-x86/external-webkit.git] / WebCore / loader / appcache / ApplicationCacheHost.cpp
1 /*
2  * Copyright (C) 2008, 2009 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 "ApplicationCacheHost.h"
28
29 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
30
31 #include "ApplicationCache.h"
32 #include "ApplicationCacheGroup.h"
33 #include "ApplicationCacheResource.h"
34 #include "DocumentLoader.h"
35 #include "DOMApplicationCache.h"
36 #include "Frame.h"
37 #include "FrameLoader.h"
38 #include "FrameLoaderClient.h"
39 #include "MainResourceLoader.h"
40 #include "ResourceLoader.h"
41 #include "ResourceRequest.h"
42 #include "Settings.h"
43
44 namespace WebCore {
45
46 ApplicationCacheHost::ApplicationCacheHost(DocumentLoader* documentLoader)
47     : m_domApplicationCache(0)
48     , m_documentLoader(documentLoader)
49     , m_defersEvents(true)
50     , m_candidateApplicationCacheGroup(0)
51 {
52     ASSERT(m_documentLoader);
53 }
54
55 ApplicationCacheHost::~ApplicationCacheHost()
56 {
57     if (m_applicationCache)
58         m_applicationCache->group()->disassociateDocumentLoader(m_documentLoader);
59     else if (m_candidateApplicationCacheGroup)
60         m_candidateApplicationCacheGroup->disassociateDocumentLoader(m_documentLoader);
61 }
62
63 void ApplicationCacheHost::selectCacheWithoutManifest()
64 {
65     ApplicationCacheGroup::selectCacheWithoutManifestURL(m_documentLoader->frame());
66 }
67
68 void ApplicationCacheHost::selectCacheWithManifest(const KURL& manifestURL)
69 {
70     ApplicationCacheGroup::selectCache(m_documentLoader->frame(), manifestURL);
71 }
72
73 void ApplicationCacheHost::maybeLoadMainResource(ResourceRequest& request, SubstituteData& substituteData)
74 {
75     // Check if this request should be loaded from the application cache
76     if (!substituteData.isValid() && isApplicationCacheEnabled()) {
77         ASSERT(!m_mainResourceApplicationCache);
78
79         m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, m_documentLoader);
80
81         if (m_mainResourceApplicationCache) {
82             // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource.
83             ApplicationCacheResource* resource = m_mainResourceApplicationCache->resourceForRequest(request);
84             substituteData = SubstituteData(resource->data(), 
85                                             resource->response().mimeType(),
86                                             resource->response().textEncodingName(), KURL());
87         }
88     }
89 }
90
91 bool ApplicationCacheHost::maybeLoadFallbackForMainResponse(const ResourceRequest& request, const ResourceResponse& r)
92 {
93     if (r.httpStatusCode() / 100 == 4 || r.httpStatusCode() / 100 == 5) {
94         ASSERT(!m_mainResourceApplicationCache);
95         if (isApplicationCacheEnabled()) {
96             m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, documentLoader());
97
98             if (scheduleLoadFallbackResourceFromApplicationCache(documentLoader()->mainResourceLoader(), m_mainResourceApplicationCache.get()))
99                 return true;
100         }
101     }
102     return false;
103 }
104
105 bool ApplicationCacheHost::maybeLoadFallbackForMainError(const ResourceRequest& request, const ResourceError& error)
106 {
107     if (!error.isCancellation()) {
108         ASSERT(!m_mainResourceApplicationCache);
109         if (isApplicationCacheEnabled()) {
110             m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, m_documentLoader);
111
112             if (scheduleLoadFallbackResourceFromApplicationCache(documentLoader()->mainResourceLoader(), m_mainResourceApplicationCache.get()))
113                 return true;
114         }
115     }
116     return false;
117 }
118
119 void ApplicationCacheHost::mainResourceDataReceived(const char*, int, long long, bool)
120 {
121     // This method is here to facilitate alternate implemetations of this interface by the host browser.
122 }
123
124 void ApplicationCacheHost::failedLoadingMainResource()
125 {
126     ApplicationCacheGroup* group = m_candidateApplicationCacheGroup;
127     if (!group && m_applicationCache) {
128         ASSERT(!mainResourceApplicationCache()); // If the main resource were loaded from a cache, it wouldn't fail.
129         group = m_applicationCache->group();
130     }
131     
132     if (group)
133         group->failedLoadingMainResource(m_documentLoader);
134 }
135
136 void ApplicationCacheHost::finishedLoadingMainResource()
137 {
138     ApplicationCacheGroup* group = candidateApplicationCacheGroup();
139     if (!group && applicationCache() && !mainResourceApplicationCache())
140         group = applicationCache()->group();
141     
142     if (group)
143         group->finishedLoadingMainResource(m_documentLoader);
144 }
145
146 bool ApplicationCacheHost::maybeLoadResource(ResourceLoader* loader, ResourceRequest& request, const KURL& originalURL)
147 {
148     if (!isApplicationCacheEnabled())
149         return false;
150     
151     if (request.url() != originalURL)
152         return false;
153
154     ApplicationCacheResource* resource;
155     if (!shouldLoadResourceFromApplicationCache(request, resource))
156         return false;
157     
158     m_documentLoader->m_pendingSubstituteResources.set(loader, resource);
159     m_documentLoader->deliverSubstituteResourcesAfterDelay();
160         
161     return true;
162 }
163
164 bool ApplicationCacheHost::maybeLoadFallbackForRedirect(ResourceLoader* resourceLoader, ResourceRequest& request, const ResourceResponse& redirectResponse)
165 {
166     if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url()))
167         if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
168             return true;
169     return false;
170 }
171
172 bool ApplicationCacheHost::maybeLoadFallbackForResponse(ResourceLoader* resourceLoader, const ResourceResponse& response)
173 {
174     if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5)
175         if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
176             return true;
177     return false;
178 }
179
180 bool ApplicationCacheHost::maybeLoadFallbackForError(ResourceLoader* resourceLoader, const ResourceError& error)
181 {
182     if (!error.isCancellation())
183         if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
184             return true;
185     return false;
186 }
187
188 bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
189 {
190     ApplicationCacheResource* resource;
191     if (shouldLoadResourceFromApplicationCache(request, resource)) {
192         if (resource) {
193             response = resource->response();
194             data.append(resource->data()->data(), resource->data()->size());
195         } else {
196             error = documentLoader()->frameLoader()->client()->cannotShowURLError(request);
197         }
198         return true;
199     }
200     return false;
201 }
202
203 void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
204 {
205     // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent,
206     // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry
207     // corresponding to the matched namespace.
208     if ((!error.isNull() && !error.isCancellation())
209          || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5
210          || !protocolHostAndPortAreEqual(request.url(), response.url())) {
211         ApplicationCacheResource* resource;
212         if (getApplicationCacheFallbackResource(request, resource)) {
213             response = resource->response();
214             data.clear();
215             data.append(resource->data()->data(), resource->data()->size());
216         }
217     }
218 }
219
220 bool ApplicationCacheHost::canCacheInPageCache() const 
221 {
222     return !applicationCache() && !candidateApplicationCacheGroup();
223 }
224
225 void ApplicationCacheHost::setDOMApplicationCache(DOMApplicationCache* domApplicationCache)
226 {
227     ASSERT(!m_domApplicationCache || !domApplicationCache);
228     m_domApplicationCache = domApplicationCache;
229 }
230
231 void ApplicationCacheHost::notifyDOMApplicationCache(EventID id)
232 {
233     if (m_defersEvents) {
234         // Events are deferred until document.onload has fired.
235         m_deferredEvents.append(id);
236         return;
237     }
238     if (m_domApplicationCache) {
239         ExceptionCode ec = 0;
240         m_domApplicationCache->dispatchEvent(Event::create(DOMApplicationCache::toEventType(id), false, false), ec);
241         ASSERT(!ec);    
242     }
243 }
244
245 void ApplicationCacheHost::stopDeferringEvents()
246 {
247     RefPtr<DocumentLoader> protect(documentLoader());
248     for (unsigned i = 0; i < m_deferredEvents.size(); ++i) {
249         EventID id = m_deferredEvents[i];
250         if (m_domApplicationCache) {
251             ExceptionCode ec = 0;
252             m_domApplicationCache->dispatchEvent(Event::create(DOMApplicationCache::toEventType(id), false, false), ec);
253             ASSERT(!ec);
254         }
255     }
256     m_deferredEvents.clear();
257     m_defersEvents = false;
258 }
259
260 void ApplicationCacheHost::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group)
261 {
262     ASSERT(!m_applicationCache);
263     m_candidateApplicationCacheGroup = group;
264 }
265     
266 void ApplicationCacheHost::setApplicationCache(PassRefPtr<ApplicationCache> applicationCache)
267 {
268     if (m_candidateApplicationCacheGroup) {
269         ASSERT(!m_applicationCache);
270         m_candidateApplicationCacheGroup = 0;
271     }
272
273     m_applicationCache = applicationCache;
274 }
275
276 bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource)
277 {
278     ApplicationCache* cache = applicationCache();
279     if (!cache || !cache->isComplete())
280         return false;
281
282     // If the resource is not to be fetched using the HTTP GET mechanism or equivalent, or if its URL has a different
283     // <scheme> component than the application cache's manifest, then fetch the resource normally.
284     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringCase(request.url().protocol(), cache->manifestResource()->url().protocol()))
285         return false;
286
287     // If the resource's URL is an master entry, the manifest, an explicit entry, or a fallback entry
288     // in the application cache, then get the resource from the cache (instead of fetching it).
289     resource = cache->resourceForURL(request.url());
290
291     // Resources that match fallback namespaces or online whitelist entries are fetched from the network,
292     // unless they are also cached.
293     if (!resource && (cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url())))
294         return false;
295
296     // Resources that are not present in the manifest will always fail to load (at least, after the
297     // cache has been primed the first time), making the testing of offline applications simpler.
298     return true;
299 }
300
301 bool ApplicationCacheHost::getApplicationCacheFallbackResource(const ResourceRequest& request, ApplicationCacheResource*& resource, ApplicationCache* cache)
302 {
303     if (!cache) {
304         cache = applicationCache();
305         if (!cache)
306             return false;
307     }
308     if (!cache->isComplete())
309         return false;
310     
311     // If the resource is not a HTTP/HTTPS GET, then abort
312     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
313         return false;
314
315     KURL fallbackURL;
316     if (!cache->urlMatchesFallbackNamespace(request.url(), &fallbackURL))
317         return false;
318
319     resource = cache->resourceForURL(fallbackURL);
320     ASSERT(resource);
321
322     return true;
323 }
324
325 bool ApplicationCacheHost::scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader* loader, ApplicationCache* cache)
326 {
327     if (!isApplicationCacheEnabled())
328         return false;
329
330     ApplicationCacheResource* resource;
331     if (!getApplicationCacheFallbackResource(loader->request(), resource, cache))
332         return false;
333
334     m_documentLoader->m_pendingSubstituteResources.set(loader, resource);
335     m_documentLoader->deliverSubstituteResourcesAfterDelay();
336     
337     loader->handle()->cancel();
338
339     return true;
340 }
341
342 ApplicationCacheHost::Status ApplicationCacheHost::status() const
343 {
344     ApplicationCache* cache = applicationCache();    
345     if (!cache)
346         return UNCACHED;
347
348     switch (cache->group()->updateStatus()) {
349         case ApplicationCacheGroup::Checking:
350             return CHECKING;
351         case ApplicationCacheGroup::Downloading:
352             return DOWNLOADING;
353         case ApplicationCacheGroup::Idle: {
354             if (cache->group()->isObsolete())
355                 return OBSOLETE;
356             if (cache != cache->group()->newestCache())
357                 return UPDATEREADY;
358             return IDLE;
359         }
360     }
361
362     ASSERT_NOT_REACHED();
363     return UNCACHED;
364 }
365
366 bool ApplicationCacheHost::update()
367 {
368     ApplicationCache* cache = applicationCache();
369     if (!cache)
370         return false;
371     cache->group()->update(m_documentLoader->frame(), ApplicationCacheUpdateWithoutBrowsingContext);
372     return true;
373 }
374
375 bool ApplicationCacheHost::swapCache()
376 {
377     ApplicationCache* cache = applicationCache();
378     if (!cache)
379         return false;
380
381     // If the group of application caches to which cache belongs has the lifecycle status obsolete, unassociate document from cache.
382     if (cache->group()->isObsolete()) {
383         cache->group()->disassociateDocumentLoader(m_documentLoader);
384         return true;
385     }
386
387     // If there is no newer cache, raise an INVALID_STATE_ERR exception.
388     ApplicationCache* newestCache = cache->group()->newestCache();
389     if (cache == newestCache)
390         return false;
391     
392     ASSERT(cache->group() == newestCache->group());
393     setApplicationCache(newestCache);
394     
395     return true;
396 }
397
398 bool ApplicationCacheHost::isApplicationCacheEnabled()
399 {
400     return m_documentLoader->frame()->settings()
401            && m_documentLoader->frame()->settings()->offlineWebApplicationCacheEnabled();
402 }
403
404 }  // namespace WebCore
405
406 #endif  // ENABLE(OFFLINE_WEB_APPLICATIONS)