Unreviewed build fix
[WebKit-https.git] / Source / WebCore / loader / appcache / ApplicationCacheHost.cpp
1 /*
2  * Copyright (C) 2008-2016 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 #include "ApplicationCache.h"
30 #include "ApplicationCacheGroup.h"
31 #include "ApplicationCacheResource.h"
32 #include "ContentSecurityPolicy.h"
33 #include "CustomHeaderFields.h"
34 #include "DocumentLoader.h"
35 #include "DOMApplicationCache.h"
36 #include "EventNames.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameLoaderClient.h"
40 #include "InspectorInstrumentation.h"
41 #include "Page.h"
42 #include "ProgressEvent.h"
43 #include "ResourceHandle.h"
44 #include "ResourceRequest.h"
45 #include "Settings.h"
46 #include "SubresourceLoader.h"
47 #include <wtf/FileSystem.h>
48 #include <wtf/UUID.h>
49
50 namespace WebCore {
51
52 ApplicationCacheHost::ApplicationCacheHost(DocumentLoader& documentLoader)
53     : m_documentLoader(documentLoader)
54 {
55 }
56
57 ApplicationCacheHost::~ApplicationCacheHost()
58 {
59     ASSERT(!m_applicationCache || !m_candidateApplicationCacheGroup || m_applicationCache->group() == m_candidateApplicationCacheGroup);
60     if (m_applicationCache)
61         m_applicationCache->group()->disassociateDocumentLoader(m_documentLoader);
62     else if (m_candidateApplicationCacheGroup)
63         m_candidateApplicationCacheGroup->disassociateDocumentLoader(m_documentLoader);
64 }
65
66 void ApplicationCacheHost::selectCacheWithoutManifest()
67 {
68     ASSERT(m_documentLoader.frame());
69     ApplicationCacheGroup::selectCacheWithoutManifestURL(*m_documentLoader.frame());
70 }
71
72 void ApplicationCacheHost::selectCacheWithManifest(const URL& manifestURL)
73 {
74     ASSERT(m_documentLoader.frame());
75     ApplicationCacheGroup::selectCache(*m_documentLoader.frame(), manifestURL);
76 }
77
78 void ApplicationCacheHost::maybeLoadMainResource(const ResourceRequest& request, SubstituteData& substituteData)
79 {
80     // Check if this request should be loaded from the application cache
81     if (!substituteData.isValid() && isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) {
82         ASSERT(!m_mainResourceApplicationCache);
83
84         m_mainResourceApplicationCache = ApplicationCacheGroup::cacheForMainRequest(request, &m_documentLoader);
85
86         if (m_mainResourceApplicationCache) {
87             // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource.
88             ApplicationCacheResource* resource = m_mainResourceApplicationCache->resourceForRequest(request);
89
90             // ApplicationCache resources have fragment identifiers stripped off of their URLs,
91             // but we'll need to restore that for the SubstituteData.
92             ResourceResponse responseToUse = resource->response();
93             if (request.url().hasFragmentIdentifier()) {
94                 URL url = responseToUse.url();
95                 url.setFragmentIdentifier(request.url().fragmentIdentifier());
96                 responseToUse.setURL(url);
97             }
98
99             substituteData = SubstituteData(&resource->data(),
100                                             URL(),
101                                             responseToUse,
102                                             SubstituteData::SessionHistoryVisibility::Visible);
103         }
104     }
105 }
106
107 void ApplicationCacheHost::maybeLoadMainResourceForRedirect(const ResourceRequest& request, SubstituteData& substituteData)
108 {
109     ASSERT(status() == UNCACHED);
110     maybeLoadMainResource(request, substituteData);
111 }
112
113 bool ApplicationCacheHost::maybeLoadFallbackForMainResponse(const ResourceRequest& request, const ResourceResponse& r)
114 {
115     if (r.httpStatusCode() / 100 == 4 || r.httpStatusCode() / 100 == 5) {
116         ASSERT(!m_mainResourceApplicationCache);
117         if (isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) {
118             m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, &m_documentLoader);
119
120             if (scheduleLoadFallbackResourceFromApplicationCache(m_documentLoader.mainResourceLoader(), m_mainResourceApplicationCache.get()))
121                 return true;
122         }
123     }
124     return false;
125 }
126
127 bool ApplicationCacheHost::maybeLoadFallbackForMainError(const ResourceRequest& request, const ResourceError& error)
128 {
129     if (!error.isCancellation()) {
130         ASSERT(!m_mainResourceApplicationCache);
131         if (isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request)) {
132             m_mainResourceApplicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request, &m_documentLoader);
133
134             if (scheduleLoadFallbackResourceFromApplicationCache(m_documentLoader.mainResourceLoader(), m_mainResourceApplicationCache.get()))
135                 return true;
136         }
137     }
138     return false;
139 }
140
141 void ApplicationCacheHost::mainResourceDataReceived(const char*, int, long long, bool)
142 {
143 }
144
145 void ApplicationCacheHost::failedLoadingMainResource()
146 {
147     auto* group = m_candidateApplicationCacheGroup;
148     if (!group && m_applicationCache) {
149         if (mainResourceApplicationCache()) {
150             // Even when the main resource is being loaded from an application cache, loading can fail if aborted.
151             return;
152         }
153         group = m_applicationCache->group();
154     }
155     
156     if (group)
157         group->failedLoadingMainResource(m_documentLoader);
158 }
159
160 void ApplicationCacheHost::finishedLoadingMainResource()
161 {
162     auto* group = candidateApplicationCacheGroup();
163     if (!group && applicationCache() && !mainResourceApplicationCache())
164         group = applicationCache()->group();
165     
166     if (group)
167         group->finishedLoadingMainResource(m_documentLoader);
168 }
169
170 bool ApplicationCacheHost::maybeLoadResource(ResourceLoader& loader, const ResourceRequest& request, const URL& originalURL)
171 {
172     if (loader.options().applicationCacheMode != ApplicationCacheMode::Use)
173         return false;
174
175     if (!isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(request))
176         return false;
177     
178     if (request.url() != originalURL)
179         return false;
180
181 #if ENABLE(SERVICE_WORKER)
182     if (loader.options().serviceWorkerRegistrationIdentifier)
183         return false;
184 #endif
185
186     ApplicationCacheResource* resource;
187     if (!shouldLoadResourceFromApplicationCache(request, resource))
188         return false;
189
190     if (resource)
191         m_documentLoader.scheduleSubstituteResourceLoad(loader, *resource);
192     else
193         m_documentLoader.scheduleCannotShowURLError(loader);
194     return true;
195 }
196
197 bool ApplicationCacheHost::maybeLoadFallbackForRedirect(ResourceLoader* resourceLoader, ResourceRequest& request, const ResourceResponse& redirectResponse)
198 {
199     if (resourceLoader && resourceLoader->options().applicationCacheMode != ApplicationCacheMode::Use)
200         return false;
201
202     if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
203         if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
204             return true;
205     }
206     return false;
207 }
208
209 bool ApplicationCacheHost::maybeLoadFallbackForResponse(ResourceLoader* resourceLoader, const ResourceResponse& response)
210 {
211     if (resourceLoader && resourceLoader->options().applicationCacheMode != ApplicationCacheMode::Use)
212         return false;
213
214     if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5) {
215         if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
216             return true;
217     }
218     return false;
219 }
220
221 bool ApplicationCacheHost::maybeLoadFallbackForError(ResourceLoader* resourceLoader, const ResourceError& error)
222 {
223     if (resourceLoader && resourceLoader->options().applicationCacheMode != ApplicationCacheMode::Use)
224         return false;
225
226     if (!error.isCancellation()) {
227         if (resourceLoader == m_documentLoader.mainResourceLoader())
228             return maybeLoadFallbackForMainError(resourceLoader->request(), error);
229         if (scheduleLoadFallbackResourceFromApplicationCache(resourceLoader))
230             return true;
231     }
232     return false;
233 }
234
235 URL ApplicationCacheHost::createFileURL(const String& path)
236 {
237     // FIXME: Can we just use fileURLWithFileSystemPath instead?
238 #if USE(CF) && PLATFORM(WIN)
239     URL url(adoptCF(CFURLCreateWithFileSystemPath(0, path.createCFString().get(), kCFURLWindowsPathStyle, false)).get());
240 #else
241     URL url;
242     url.setProtocol("file"_s);
243     url.setPath(path);
244 #endif
245     return url;
246 }
247
248 static inline RefPtr<SharedBuffer> bufferFromResource(ApplicationCacheResource& resource)
249 {
250     // FIXME: Clients probably do not need a copy of the SharedBuffer.
251     // Remove the call to copy() once we ensure SharedBuffer will not be modified.
252     if (resource.path().isEmpty())
253         return resource.data().copy();
254     return SharedBuffer::createWithContentsOfFile(resource.path());
255 }
256
257 bool ApplicationCacheHost::maybeLoadSynchronously(ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data)
258 {
259     ApplicationCacheResource* resource;
260     if (!shouldLoadResourceFromApplicationCache(request, resource))
261         return false;
262
263     auto responseData = resource ? bufferFromResource(*resource) : nullptr;
264     if (!responseData) {
265         error = m_documentLoader.frameLoader()->client().cannotShowURLError(request);
266         return true;
267     }
268
269     response = resource->response();
270     data = WTFMove(responseData);
271     return true;
272 }
273
274 void ApplicationCacheHost::maybeLoadFallbackSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data)
275 {
276     // 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,
277     // 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
278     // corresponding to the matched namespace.
279     if ((!error.isNull() && !error.isCancellation())
280          || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5
281          || !protocolHostAndPortAreEqual(request.url(), response.url())) {
282         ApplicationCacheResource* resource;
283         if (getApplicationCacheFallbackResource(request, resource)) {
284             response = resource->response();
285             // FIXME: Clients proably do not need a copy of the SharedBuffer.
286             // Remove the call to copy() once we ensure SharedBuffer will not be modified.
287             data = resource->data().copy();
288         }
289     }
290 }
291
292 bool ApplicationCacheHost::canCacheInPageCache()
293 {
294     return !applicationCache() && !candidateApplicationCacheGroup();
295 }
296
297 void ApplicationCacheHost::setDOMApplicationCache(DOMApplicationCache* domApplicationCache)
298 {
299     ASSERT(!m_domApplicationCache || !domApplicationCache);
300     m_domApplicationCache = makeWeakPtr(domApplicationCache);
301 }
302
303 void ApplicationCacheHost::notifyDOMApplicationCache(const AtomicString& eventType, int total, int done)
304 {
305     if (eventType != eventNames().progressEvent)
306         InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader.frame());
307
308     if (m_defersEvents) {
309         // Event dispatching is deferred until document.onload has fired.
310         m_deferredEvents.append({ eventType, total, done });
311         return;
312     }
313
314     dispatchDOMEvent(eventType, total, done);
315 }
316
317 void ApplicationCacheHost::stopLoadingInFrame(Frame& frame)
318 {
319     ASSERT(!m_applicationCache || !m_candidateApplicationCacheGroup || m_applicationCache->group() == m_candidateApplicationCacheGroup);
320
321     if (m_candidateApplicationCacheGroup)
322         m_candidateApplicationCacheGroup->stopLoadingInFrame(frame);
323     else if (m_applicationCache)
324         m_applicationCache->group()->stopLoadingInFrame(frame);
325 }
326
327 void ApplicationCacheHost::stopDeferringEvents()
328 {
329     Ref<DocumentLoader> protect(m_documentLoader);
330
331     // Note, do not cache the size in a local variable.
332     // This code needs to properly handle the case where more events are added to
333     // m_deferredEvents while iterating it. This is why we don't use a modern for loop.
334     for (size_t i = 0; i < m_deferredEvents.size(); ++i) {
335         auto& event = m_deferredEvents[i];
336         dispatchDOMEvent(event.eventType, event.progressTotal, event.progressDone);
337     }
338
339     m_deferredEvents.clear();
340     m_defersEvents = false;
341 }
342
343 Vector<ApplicationCacheHost::ResourceInfo> ApplicationCacheHost::resourceList()
344 {
345     auto* cache = applicationCache();
346     if (!cache || !cache->isComplete())
347         return { };
348
349     return WTF::map(cache->resources(), [] (auto& urlAndResource) -> ApplicationCacheHost::ResourceInfo {
350         ASSERT(urlAndResource.value);
351         auto& resource = *urlAndResource.value;
352
353         unsigned type = resource.type();
354         bool isMaster = type & ApplicationCacheResource::Master;
355         bool isManifest = type & ApplicationCacheResource::Manifest;
356         bool isExplicit = type & ApplicationCacheResource::Explicit;
357         bool isForeign = type & ApplicationCacheResource::Foreign;
358         bool isFallback = type & ApplicationCacheResource::Fallback;
359
360         return { resource.url(), isMaster, isManifest, isFallback, isForeign, isExplicit, resource.estimatedSizeInStorage() };
361     });
362 }
363
364 ApplicationCacheHost::CacheInfo ApplicationCacheHost::applicationCacheInfo()
365 {
366     auto* cache = applicationCache();
367     if (!cache || !cache->isComplete())
368         return { { }, 0, 0, 0 };
369
370     // FIXME: Add "Creation Time" and "Update Time" to Application Caches.
371     return { cache->manifestResource()->url(), 0, 0, cache->estimatedSizeInStorage() };
372 }
373
374 static Ref<Event> createApplicationCacheEvent(const AtomicString& eventType, int total, int done)
375 {
376     if (eventType == eventNames().progressEvent)
377         return ProgressEvent::create(eventType, true, done, total);
378     return Event::create(eventType, Event::CanBubble::No, Event::IsCancelable::No);
379 }
380
381 void ApplicationCacheHost::dispatchDOMEvent(const AtomicString& eventType, int total, int done)
382 {
383     if (!m_domApplicationCache || !m_domApplicationCache->frame())
384         return;
385
386     m_domApplicationCache->dispatchEvent(createApplicationCacheEvent(eventType, total, done));
387 }
388
389 void ApplicationCacheHost::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group)
390 {
391     ASSERT(!m_applicationCache);
392     m_candidateApplicationCacheGroup = group;
393 }
394     
395 void ApplicationCacheHost::setApplicationCache(RefPtr<ApplicationCache>&& applicationCache)
396 {
397     if (m_candidateApplicationCacheGroup) {
398         ASSERT(!m_applicationCache);
399         m_candidateApplicationCacheGroup = nullptr;
400     }
401     m_applicationCache = WTFMove(applicationCache);
402 }
403
404 bool ApplicationCacheHost::shouldLoadResourceFromApplicationCache(const ResourceRequest& originalRequest, ApplicationCacheResource*& resource)
405 {
406     auto* cache = applicationCache();
407     if (!cache || !cache->isComplete())
408         return false;
409
410     ResourceRequest request(originalRequest);
411     if (auto* loaderFrame = m_documentLoader.frame()) {
412         if (auto* document = loaderFrame->document())
413             document->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load);
414     }
415     
416     // If the resource is not to be fetched using the HTTP GET mechanism or equivalent, or if its URL has a different
417     // <scheme> component than the application cache's manifest, then fetch the resource normally.
418     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request) || !equalIgnoringASCIICase(request.url().protocol(), cache->manifestResource()->url().protocol()))
419         return false;
420
421     // If the resource's URL is an master entry, the manifest, an explicit entry, or a fallback entry
422     // in the application cache, then get the resource from the cache (instead of fetching it).
423     resource = cache->resourceForURL(request.url());
424
425     // Resources that match fallback namespaces or online whitelist entries are fetched from the network,
426     // unless they are also cached.
427     if (!resource && (cache->allowsAllNetworkRequests() || cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url())))
428         return false;
429
430     // Resources that are not present in the manifest will always fail to load (at least, after the
431     // cache has been primed the first time), making the testing of offline applications simpler.
432     return true;
433 }
434
435 bool ApplicationCacheHost::getApplicationCacheFallbackResource(const ResourceRequest& request, ApplicationCacheResource*& resource, ApplicationCache* cache)
436 {
437     if (!cache) {
438         cache = applicationCache();
439         if (!cache)
440             return false;
441     }
442     if (!cache->isComplete())
443         return false;
444     
445     // If the resource is not a HTTP/HTTPS GET, then abort
446     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
447         return false;
448
449     URL fallbackURL;
450     if (cache->isURLInOnlineWhitelist(request.url()))
451         return false;
452     if (!cache->urlMatchesFallbackNamespace(request.url(), &fallbackURL))
453         return false;
454
455     resource = cache->resourceForURL(fallbackURL);
456     ASSERT(resource);
457     return true;
458 }
459
460 bool ApplicationCacheHost::scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader* loader, ApplicationCache* cache)
461 {
462     if (!loader)
463         return false;
464
465     if (!isApplicationCacheEnabled() && !isApplicationCacheBlockedForRequest(loader->request()))
466         return false;
467
468 #if ENABLE(SERVICE_WORKER)
469     if (loader->options().serviceWorkerRegistrationIdentifier)
470         return false;
471 #endif
472
473     ApplicationCacheResource* resource;
474     if (!getApplicationCacheFallbackResource(loader->request(), resource, cache))
475         return false;
476
477     loader->willSwitchToSubstituteResource();
478     m_documentLoader.scheduleSubstituteResourceLoad(*loader, *resource);
479     return true;
480 }
481
482 ApplicationCacheHost::Status ApplicationCacheHost::status() const
483 {
484     auto* cache = applicationCache();
485     if (!cache)
486         return UNCACHED;
487
488     switch (cache->group()->updateStatus()) {
489     case ApplicationCacheGroup::Checking:
490         return CHECKING;
491     case ApplicationCacheGroup::Downloading:
492         return DOWNLOADING;
493     case ApplicationCacheGroup::Idle:
494         if (cache->group()->isObsolete())
495             return OBSOLETE;
496         if (cache != cache->group()->newestCache())
497             return UPDATEREADY;
498         return IDLE;
499     }
500
501     ASSERT_NOT_REACHED();
502     return UNCACHED;
503 }
504
505 bool ApplicationCacheHost::update()
506 {
507     auto* cache = applicationCache();
508     if (!cache)
509         return false;
510     auto* frame = m_documentLoader.frame();
511     if (!frame)
512         return false;
513     cache->group()->update(*frame, ApplicationCacheUpdateWithoutBrowsingContext);
514     return true;
515 }
516
517 bool ApplicationCacheHost::swapCache()
518 {
519     auto* cache = applicationCache();
520     if (!cache)
521         return false;
522
523     // If the group of application caches to which cache belongs has the lifecycle status obsolete, unassociate document from cache.
524     if (cache->group()->isObsolete()) {
525         cache->group()->disassociateDocumentLoader(m_documentLoader);
526         return true;
527     }
528
529     // If there is no newer cache, raise an InvalidStateError exception.
530     auto* newestCache = cache->group()->newestCache();
531     if (cache == newestCache)
532         return false;
533     
534     ASSERT(cache->group() == newestCache->group());
535     setApplicationCache(newestCache);
536     InspectorInstrumentation::updateApplicationCacheStatus(m_documentLoader.frame());
537     return true;
538 }
539
540 void ApplicationCacheHost::abort()
541 {
542     auto* frame = m_documentLoader.frame();
543     if (!frame)
544         return;
545     if (auto* cacheGroup = candidateApplicationCacheGroup())
546         cacheGroup->abort(*frame);
547     else if (auto* cache = applicationCache())
548         cache->group()->abort(*frame);
549 }
550
551 bool ApplicationCacheHost::isApplicationCacheEnabled()
552 {
553     return m_documentLoader.frame() && m_documentLoader.frame()->settings().offlineWebApplicationCacheEnabled() && !m_documentLoader.frame()->page()->usesEphemeralSession();
554 }
555
556 bool ApplicationCacheHost::isApplicationCacheBlockedForRequest(const ResourceRequest& request)
557 {
558     auto* frame = m_documentLoader.frame();
559     if (!frame)
560         return false;
561     if (frame->isMainFrame())
562         return false;
563     return !SecurityOrigin::create(request.url())->canAccessApplicationCache(frame->document()->topOrigin());
564 }
565
566 }  // namespace WebCore