Use normal loading path for ping loads
[WebKit-https.git] / Source / WebCore / loader / LinkLoader.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2016-2017 Apple 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 are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32
33 #include "config.h"
34 #include "LinkLoader.h"
35
36 #include "CSSStyleSheet.h"
37 #include "CachedCSSStyleSheet.h"
38 #include "CachedResourceLoader.h"
39 #include "CachedResourceRequest.h"
40 #include "ContainerNode.h"
41 #include "CrossOriginAccessControl.h"
42 #include "Document.h"
43 #include "Frame.h"
44 #include "FrameLoader.h"
45 #include "FrameLoaderClient.h"
46 #include "FrameView.h"
47 #include "LinkHeader.h"
48 #include "LinkPreloadResourceClients.h"
49 #include "LinkRelAttribute.h"
50 #include "LoaderStrategy.h"
51 #include "MIMETypeRegistry.h"
52 #include "MediaQueryEvaluator.h"
53 #include "PlatformStrategies.h"
54 #include "ResourceError.h"
55 #include "RuntimeEnabledFeatures.h"
56 #include "Settings.h"
57 #include "StyleResolver.h"
58
59 namespace WebCore {
60
61 LinkLoader::LinkLoader(LinkLoaderClient& client)
62     : m_client(client)
63 {
64 }
65
66 LinkLoader::~LinkLoader()
67 {
68     if (m_cachedLinkResource)
69         m_cachedLinkResource->removeClient(*this);
70     if (m_preloadResourceClient)
71         m_preloadResourceClient->clear();
72 }
73
74 void LinkLoader::triggerEvents(const CachedResource& resource)
75 {
76     if (resource.errorOccurred())
77         m_client.linkLoadingErrored();
78     else
79         m_client.linkLoaded();
80 }
81
82 void LinkLoader::notifyFinished(CachedResource& resource)
83 {
84     ASSERT_UNUSED(resource, m_cachedLinkResource.get() == &resource);
85
86     triggerEvents(*m_cachedLinkResource);
87
88     m_cachedLinkResource->removeClient(*this);
89     m_cachedLinkResource = nullptr;
90 }
91
92 void LinkLoader::loadLinksFromHeader(const String& headerValue, const URL& baseURL, Document& document, MediaAttributeCheck mediaAttributeCheck)
93 {
94     if (headerValue.isEmpty())
95         return;
96     LinkHeaderSet headerSet(headerValue);
97     for (auto& header : headerSet) {
98         if (!header.valid() || header.url().isEmpty() || header.rel().isEmpty())
99             continue;
100         if ((mediaAttributeCheck == MediaAttributeCheck::MediaAttributeNotEmpty && header.media().isEmpty())
101             || (mediaAttributeCheck == MediaAttributeCheck::MediaAttributeEmpty && !header.media().isEmpty())) {
102                 continue;
103         }
104
105         LinkRelAttribute relAttribute(document, header.rel());
106         URL url(baseURL, header.url());
107         // Sanity check to avoid re-entrancy here.
108         if (equalIgnoringFragmentIdentifier(url, baseURL))
109             continue;
110         preconnectIfNeeded(relAttribute, url, document, header.crossOrigin());
111         preloadIfNeeded(relAttribute, url, document, header.as(), header.media(), header.mimeType(), header.crossOrigin(), nullptr);
112     }
113 }
114
115 Optional<CachedResource::Type> LinkLoader::resourceTypeFromAsAttribute(const String& as)
116 {
117     if (equalLettersIgnoringASCIICase(as, "fetch"))
118         return CachedResource::Type::RawResource;
119     if (equalLettersIgnoringASCIICase(as, "image"))
120         return CachedResource::Type::ImageResource;
121     if (equalLettersIgnoringASCIICase(as, "script"))
122         return CachedResource::Type::Script;
123     if (equalLettersIgnoringASCIICase(as, "style"))
124         return CachedResource::Type::CSSStyleSheet;
125     if (RuntimeEnabledFeatures::sharedFeatures().mediaPreloadingEnabled() && (equalLettersIgnoringASCIICase(as, "video") || equalLettersIgnoringASCIICase(as, "audio")))
126         return CachedResource::Type::MediaResource;
127     if (equalLettersIgnoringASCIICase(as, "font"))
128         return CachedResource::Type::FontResource;
129 #if ENABLE(VIDEO_TRACK)
130     if (equalLettersIgnoringASCIICase(as, "track"))
131         return CachedResource::Type::TextTrackResource;
132 #endif
133     return WTF::nullopt;
134 }
135
136 static std::unique_ptr<LinkPreloadResourceClient> createLinkPreloadResourceClient(CachedResource& resource, LinkLoader& loader)
137 {
138     switch (resource.type()) {
139     case CachedResource::Type::ImageResource:
140         return std::make_unique<LinkPreloadImageResourceClient>(loader, downcast<CachedImage>(resource));
141     case CachedResource::Type::Script:
142         return std::make_unique<LinkPreloadDefaultResourceClient>(loader, downcast<CachedScript>(resource));
143     case CachedResource::Type::CSSStyleSheet:
144         return std::make_unique<LinkPreloadStyleResourceClient>(loader, downcast<CachedCSSStyleSheet>(resource));
145     case CachedResource::Type::FontResource:
146         return std::make_unique<LinkPreloadFontResourceClient>(loader, downcast<CachedFont>(resource));
147 #if ENABLE(VIDEO_TRACK)
148     case CachedResource::Type::TextTrackResource:
149         return std::make_unique<LinkPreloadDefaultResourceClient>(loader, downcast<CachedTextTrack>(resource));
150 #endif
151     case CachedResource::Type::MediaResource:
152         ASSERT(RuntimeEnabledFeatures::sharedFeatures().mediaPreloadingEnabled());
153         FALLTHROUGH;
154     case CachedResource::Type::RawResource:
155         return std::make_unique<LinkPreloadRawResourceClient>(loader, downcast<CachedRawResource>(resource));
156     case CachedResource::Type::MainResource:
157     case CachedResource::Type::Icon:
158 #if ENABLE(SVG_FONTS)
159     case CachedResource::Type::SVGFontResource:
160 #endif
161     case CachedResource::Type::SVGDocumentResource:
162 #if ENABLE(XSLT)
163     case CachedResource::Type::XSLStyleSheet:
164 #endif
165     case CachedResource::Type::Beacon:
166     case CachedResource::Type::Ping:
167     case CachedResource::Type::LinkPrefetch:
168 #if ENABLE(APPLICATION_MANIFEST)
169     case CachedResource::Type::ApplicationManifest:
170 #endif
171         // None of these values is currently supported as an `as` value.
172         ASSERT_NOT_REACHED();
173     }
174     return nullptr;
175 }
176
177 bool LinkLoader::isSupportedType(CachedResource::Type resourceType, const String& mimeType)
178 {
179     if (mimeType.isEmpty())
180         return true;
181     switch (resourceType) {
182     case CachedResource::Type::ImageResource:
183         return MIMETypeRegistry::isSupportedImageVideoOrSVGMIMEType(mimeType);
184     case CachedResource::Type::Script:
185         return MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType);
186     case CachedResource::Type::CSSStyleSheet:
187         return MIMETypeRegistry::isSupportedStyleSheetMIMEType(mimeType);
188     case CachedResource::Type::FontResource:
189         return MIMETypeRegistry::isSupportedFontMIMEType(mimeType);
190     case CachedResource::Type::MediaResource:
191         if (!RuntimeEnabledFeatures::sharedFeatures().mediaPreloadingEnabled())
192             ASSERT_NOT_REACHED();
193         return MIMETypeRegistry::isSupportedMediaMIMEType(mimeType);
194
195 #if ENABLE(VIDEO_TRACK)
196     case CachedResource::Type::TextTrackResource:
197         return MIMETypeRegistry::isSupportedTextTrackMIMEType(mimeType);
198 #endif
199     case CachedResource::Type::RawResource:
200 #if ENABLE(APPLICATION_MANIFEST)
201     case CachedResource::Type::ApplicationManifest:
202 #endif
203         return true;
204     default:
205         ASSERT_NOT_REACHED();
206     }
207     return false;
208 }
209
210 void LinkLoader::preconnectIfNeeded(const LinkRelAttribute& relAttribute, const URL& href, Document& document, const String& crossOrigin)
211 {
212     if (!relAttribute.isLinkPreconnect || !href.isValid() || !href.protocolIsInHTTPFamily() || !document.frame())
213         return;
214     ASSERT(document.settings().linkPreconnectEnabled());
215     StoredCredentialsPolicy storageCredentialsPolicy = StoredCredentialsPolicy::Use;
216     if (equalIgnoringASCIICase(crossOrigin, "anonymous") && document.securityOrigin().canAccess(SecurityOrigin::create(href)))
217         storageCredentialsPolicy = StoredCredentialsPolicy::DoNotUse;
218     ASSERT(document.frame()->loader().networkingContext());
219     platformStrategies()->loaderStrategy()->preconnectTo(document.frame()->loader(), href, storageCredentialsPolicy, [weakDocument = makeWeakPtr(document), href](ResourceError error) {
220         if (!weakDocument)
221             return;
222
223         if (!error.isNull())
224             weakDocument->addConsoleMessage(MessageSource::Network, MessageLevel::Error, makeString("Failed to preconnect to "_s, href.string(), ". Error: "_s, error.localizedDescription()));
225         else
226             weakDocument->addConsoleMessage(MessageSource::Network, MessageLevel::Info, makeString("Successfuly preconnected to "_s, href.string()));
227     });
228 }
229
230 std::unique_ptr<LinkPreloadResourceClient> LinkLoader::preloadIfNeeded(const LinkRelAttribute& relAttribute, const URL& href, Document& document, const String& as, const String& media, const String& mimeType, const String& crossOriginMode, LinkLoader* loader)
231 {
232     if (!document.loader() || !relAttribute.isLinkPreload)
233         return nullptr;
234
235     ASSERT(RuntimeEnabledFeatures::sharedFeatures().linkPreloadEnabled());
236     if (!href.isValid()) {
237         document.addConsoleMessage(MessageSource::Other, MessageLevel::Error, "<link rel=preload> has an invalid `href` value"_s);
238         return nullptr;
239     }
240     auto type = LinkLoader::resourceTypeFromAsAttribute(as);
241     if (!type) {
242         document.addConsoleMessage(MessageSource::Other, MessageLevel::Error, "<link rel=preload> must have a valid `as` value"_s);
243         return nullptr;
244     }
245     if (!MediaQueryEvaluator::mediaAttributeMatches(document, media))
246         return nullptr;
247     if (!isSupportedType(type.value(), mimeType))
248         return nullptr;
249
250     auto options = CachedResourceLoader::defaultCachedResourceOptions();
251     auto linkRequest = createPotentialAccessControlRequest(document.completeURL(href), document, crossOriginMode, WTFMove(options));
252     linkRequest.setPriority(CachedResource::defaultPriorityForResourceType(type.value()));
253     linkRequest.setInitiator("link");
254     linkRequest.setIgnoreForRequestCount(true);
255     linkRequest.setIsLinkPreload();
256
257     auto cachedLinkResource = document.cachedResourceLoader().preload(type.value(), WTFMove(linkRequest)).value_or(nullptr);
258
259     if (cachedLinkResource && cachedLinkResource->type() != *type)
260         return nullptr;
261
262     if (cachedLinkResource && loader)
263         return createLinkPreloadResourceClient(*cachedLinkResource, *loader);
264     return nullptr;
265 }
266
267 void LinkLoader::prefetchIfNeeded(const LinkRelAttribute& relAttribute, const URL& href, Document& document)
268 {
269     if (!relAttribute.isLinkPrefetch || !href.isValid() || !document.frame() || !m_client.shouldLoadLink())
270         return;
271
272     ASSERT(RuntimeEnabledFeatures::sharedFeatures().linkPrefetchEnabled());
273     Optional<ResourceLoadPriority> priority;
274     CachedResource::Type type = CachedResource::Type::LinkPrefetch;
275
276     if (m_cachedLinkResource) {
277         m_cachedLinkResource->removeClient(*this);
278         m_cachedLinkResource = nullptr;
279     }
280     ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
281     options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck;
282     m_cachedLinkResource = document.cachedResourceLoader().requestLinkResource(type, CachedResourceRequest(ResourceRequest(document.completeURL(href)), options, priority)).value_or(nullptr);
283     if (m_cachedLinkResource)
284         m_cachedLinkResource->addClient(*this);
285 }
286
287 void LinkLoader::cancelLoad()
288 {
289     if (m_preloadResourceClient)
290         m_preloadResourceClient->clear();
291 }
292
293 bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const URL& href, const String& as, const String& media, const String& mimeType, const String& crossOrigin, Document& document)
294 {
295     if (relAttribute.isDNSPrefetch) {
296         // FIXME: The href attribute of the link element can be in "//hostname" form, and we shouldn't attempt
297         // to complete that as URL <https://bugs.webkit.org/show_bug.cgi?id=48857>.
298         if (document.settings().dnsPrefetchingEnabled() && href.isValid() && !href.isEmpty() && document.frame())
299             document.frame()->loader().client().prefetchDNS(href.host().toString());
300     }
301
302     preconnectIfNeeded(relAttribute, href, document, crossOrigin);
303
304     if (m_client.shouldLoadLink()) {
305         auto resourceClient = preloadIfNeeded(relAttribute, href, document, as, media, mimeType, crossOrigin, this);
306         if (m_preloadResourceClient)
307             m_preloadResourceClient->clear();
308         if (resourceClient)
309             m_preloadResourceClient = WTFMove(resourceClient);
310     }
311
312     prefetchIfNeeded(relAttribute, href, document);
313
314     return true;
315 }
316
317 }