diff options
Diffstat (limited to 'Source/WebCore/loader/LinkLoader.cpp')
-rw-r--r-- | Source/WebCore/loader/LinkLoader.cpp | 202 |
1 files changed, 155 insertions, 47 deletions
diff --git a/Source/WebCore/loader/LinkLoader.cpp b/Source/WebCore/loader/LinkLoader.cpp index e63bbf0c2..9b53ce87d 100644 --- a/Source/WebCore/loader/LinkLoader.cpp +++ b/Source/WebCore/loader/LinkLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -37,101 +38,208 @@ #include "CachedResourceLoader.h" #include "CachedResourceRequest.h" #include "ContainerNode.h" -#include "DNS.h" +#include "CrossOriginAccessControl.h" #include "Document.h" #include "Frame.h" +#include "FrameLoaderClient.h" #include "FrameView.h" +#include "LinkHeader.h" +#include "LinkPreloadResourceClients.h" #include "LinkRelAttribute.h" +#include "RuntimeEnabledFeatures.h" #include "Settings.h" #include "StyleResolver.h" namespace WebCore { -LinkLoader::LinkLoader(LinkLoaderClient* client) +LinkLoader::LinkLoader(LinkLoaderClient& client) : m_client(client) - , m_linkLoadTimer(this, &LinkLoader::linkLoadTimerFired) - , m_linkLoadingErrorTimer(this, &LinkLoader::linkLoadingErrorTimerFired) + , m_weakPtrFactory(this) { } LinkLoader::~LinkLoader() { if (m_cachedLinkResource) - m_cachedLinkResource->removeClient(this); + m_cachedLinkResource->removeClient(*this); + if (m_preloadResourceClient) + m_preloadResourceClient->clear(); } -void LinkLoader::linkLoadTimerFired(Timer<LinkLoader>& timer) +void LinkLoader::triggerEvents(const CachedResource& resource) { - ASSERT_UNUSED(timer, &timer == &m_linkLoadTimer); - m_client->linkLoaded(); + if (resource.errorOccurred()) + m_client.linkLoadingErrored(); + else + m_client.linkLoaded(); } -void LinkLoader::linkLoadingErrorTimerFired(Timer<LinkLoader>& timer) +void LinkLoader::notifyFinished(CachedResource& resource) { - ASSERT_UNUSED(timer, &timer == &m_linkLoadingErrorTimer); - m_client->linkLoadingErrored(); + ASSERT_UNUSED(resource, m_cachedLinkResource.get() == &resource); + + triggerEvents(*m_cachedLinkResource); + + m_cachedLinkResource->removeClient(*this); + m_cachedLinkResource = nullptr; } -void LinkLoader::notifyFinished(CachedResource* resource) +void LinkLoader::loadLinksFromHeader(const String& headerValue, const URL& baseURL, Document& document) { - ASSERT_UNUSED(resource, m_cachedLinkResource.get() == resource); + if (headerValue.isEmpty()) + return; + LinkHeaderSet headerSet(headerValue); + for (auto& header : headerSet) { + if (!header.valid() || header.url().isEmpty() || header.rel().isEmpty()) + continue; + + LinkRelAttribute relAttribute(header.rel()); + URL url(baseURL, header.url()); + // Sanity check to avoid re-entrancy here. + if (equalIgnoringFragmentIdentifier(url, baseURL)) + continue; + preloadIfNeeded(relAttribute, url, document, header.as(), header.crossOrigin(), nullptr, nullptr); + } +} - if (m_cachedLinkResource->errorOccurred()) - m_linkLoadingErrorTimer.startOneShot(0); - else - m_linkLoadTimer.startOneShot(0); +std::optional<CachedResource::Type> LinkLoader::resourceTypeFromAsAttribute(const String& as) +{ + if (as.isEmpty()) + return CachedResource::RawResource; + if (equalLettersIgnoringASCIICase(as, "image")) + return CachedResource::ImageResource; + if (equalLettersIgnoringASCIICase(as, "script")) + return CachedResource::Script; + if (equalLettersIgnoringASCIICase(as, "style")) + return CachedResource::CSSStyleSheet; + if (equalLettersIgnoringASCIICase(as, "media")) + return CachedResource::MediaResource; + if (equalLettersIgnoringASCIICase(as, "font")) + return CachedResource::FontResource; +#if ENABLE(VIDEO_TRACK) + if (equalLettersIgnoringASCIICase(as, "track")) + return CachedResource::TextTrackResource; +#endif + return std::nullopt; +} - m_cachedLinkResource->removeClient(this); - m_cachedLinkResource = 0; +static std::unique_ptr<LinkPreloadResourceClient> createLinkPreloadResourceClient(CachedResource& resource, LinkLoader& loader, CachedResource::Type type) +{ + switch (type) { + case CachedResource::ImageResource: + return LinkPreloadImageResourceClient::create(loader, static_cast<CachedImage&>(resource)); + case CachedResource::Script: + return LinkPreloadScriptResourceClient::create(loader, static_cast<CachedScript&>(resource)); + case CachedResource::CSSStyleSheet: + return LinkPreloadStyleResourceClient::create(loader, static_cast<CachedCSSStyleSheet&>(resource)); + case CachedResource::FontResource: + return LinkPreloadFontResourceClient::create(loader, static_cast<CachedFont&>(resource)); + case CachedResource::MediaResource: +#if ENABLE(VIDEO_TRACK) + case CachedResource::TextTrackResource: +#endif + case CachedResource::RawResource: + return LinkPreloadRawResourceClient::create(loader, static_cast<CachedRawResource&>(resource)); + case CachedResource::MainResource: +#if ENABLE(SVG_FONTS) + case CachedResource::SVGFontResource: +#endif + case CachedResource::SVGDocumentResource: +#if ENABLE(XSLT) + case CachedResource::XSLStyleSheet: +#endif +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkSubresource: + case CachedResource::LinkPrefetch: +#endif + // None of these values is currently supported as an `as` value. + ASSERT_NOT_REACHED(); + } + return nullptr; } -bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const String& type, - const String& sizes, const URL& href, Document* document) +std::unique_ptr<LinkPreloadResourceClient> LinkLoader::preloadIfNeeded(const LinkRelAttribute& relAttribute, const URL& href, Document& document, const String& as, const String& crossOriginMode, LinkLoader* loader, LinkLoaderClient* client) { - // We'll record this URL per document, even if we later only use it in top level frames - if (relAttribute.m_iconType != InvalidIcon && href.isValid() && !href.isEmpty()) { - if (!m_client->shouldLoadLink()) - return false; - document->addIconURL(href.string(), type, sizes, relAttribute.m_iconType); + if (!document.loader() || !relAttribute.isLinkPreload) + return nullptr; + + ASSERT(RuntimeEnabledFeatures::sharedFeatures().linkPreloadEnabled()); + if (!href.isValid()) { + document.addConsoleMessage(MessageSource::Other, MessageLevel::Error, String("<link rel=preload> has an invalid `href` value")); + return nullptr; } + auto type = LinkLoader::resourceTypeFromAsAttribute(as); + if (!type) { + document.addConsoleMessage(MessageSource::Other, MessageLevel::Error, String("<link rel=preload> must have a valid `as` value")); + if (client) + client->linkLoadingErrored(); + return nullptr; + } + + ResourceRequest resourceRequest(document.completeURL(href)); + resourceRequest.setIgnoreForRequestCount(true); + CachedResourceRequest linkRequest(WTFMove(resourceRequest), CachedResourceLoader::defaultCachedResourceOptions(), CachedResource::defaultPriorityForResourceType(type.value())); + linkRequest.setInitiator("link"); + linkRequest.setIsLinkPreload(); + + linkRequest.setAsPotentiallyCrossOrigin(crossOriginMode, document); + CachedResourceHandle<CachedResource> cachedLinkResource = document.cachedResourceLoader().preload(type.value(), WTFMove(linkRequest)); + + if (cachedLinkResource && loader) + return createLinkPreloadResourceClient(*cachedLinkResource, *loader, type.value()); + return nullptr; +} + +void LinkLoader::cancelLoad() +{ + if (m_preloadResourceClient) + m_preloadResourceClient->clear(); +} - if (relAttribute.m_isDNSPrefetch) { - Settings* settings = document->settings(); +bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const URL& href, const String& as, const String& crossOrigin, Document& document) +{ + if (relAttribute.isDNSPrefetch) { // FIXME: The href attribute of the link element can be in "//hostname" form, and we shouldn't attempt // to complete that as URL <https://bugs.webkit.org/show_bug.cgi?id=48857>. - if (settings && settings->dnsPrefetchingEnabled() && href.isValid() && !href.isEmpty()) - prefetchDNS(href.host()); + if (document.settings().dnsPrefetchingEnabled() && href.isValid() && !href.isEmpty() && document.frame()) + document.frame()->loader().client().prefetchDNS(href.host()); + } + + if (m_client.shouldLoadLink()) { + auto resourceClient = preloadIfNeeded(relAttribute, href, document, as, crossOrigin, this, &m_client); + if (resourceClient) + m_preloadResourceClient = WTFMove(resourceClient); + else if (m_preloadResourceClient) + m_preloadResourceClient->clear(); } #if ENABLE(LINK_PREFETCH) - if ((relAttribute.m_isLinkPrefetch || relAttribute.m_isLinkSubresource) && href.isValid() && document->frame()) { - if (!m_client->shouldLoadLink()) + if ((relAttribute.isLinkPrefetch || relAttribute.isLinkSubresource) && href.isValid() && document.frame()) { + if (!m_client.shouldLoadLink()) return false; - ResourceLoadPriority priority = ResourceLoadPriorityUnresolved; + + std::optional<ResourceLoadPriority> priority; CachedResource::Type type = CachedResource::LinkPrefetch; - // We only make one request to the cachedresourcelodaer if multiple rel types are - // specified, - if (relAttribute.m_isLinkSubresource) { - priority = ResourceLoadPriorityLow; + if (relAttribute.isLinkSubresource) { + // We only make one request to the cached resource loader if multiple rel types are specified; + // this is the higher priority, which should overwrite the lower priority. + priority = ResourceLoadPriority::Low; type = CachedResource::LinkSubresource; } - CachedResourceRequest linkRequest(ResourceRequest(document->completeURL(href)), priority); - + if (m_cachedLinkResource) { - m_cachedLinkResource->removeClient(this); - m_cachedLinkResource = 0; + m_cachedLinkResource->removeClient(*this); + m_cachedLinkResource = nullptr; } - m_cachedLinkResource = document->cachedResourceLoader()->requestLinkResource(type, linkRequest); + ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions(); + options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck; + m_cachedLinkResource = document.cachedResourceLoader().requestLinkResource(type, CachedResourceRequest(ResourceRequest(document.completeURL(href)), options, priority)); if (m_cachedLinkResource) - m_cachedLinkResource->addClient(this); + m_cachedLinkResource->addClient(*this); } #endif return true; } -void LinkLoader::released() -{ -} - } |