bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const AtomicString& crossOriginMode, const String& type, const KURL& href, Document& document) { if (relAttribute.isDNSPrefetch()) { Settings* settings = document.settings(); // 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()); } // FIXME(crbug.com/323096): Should take care of import. if ((relAttribute.isLinkPrefetch() || relAttribute.isLinkSubresource()) && href.isValid() && document.frame()) { if (!m_client->shouldLoadLink()) return false; Resource::Type type = relAttribute.isLinkSubresource() ? Resource::LinkSubresource : Resource::LinkPrefetch; FetchRequest linkRequest(ResourceRequest(document.completeURL(href)), FetchInitiatorTypeNames::link); if (!crossOriginMode.isNull()) linkRequest.setCrossOriginAccessControl(document.securityOrigin(), crossOriginMode); setResource(document.fetcher()->fetchLinkResource(type, linkRequest)); } if (const unsigned prerenderRelTypes = prerenderRelTypesFromRelAttribute(relAttribute)) { if (!m_prerender) { m_prerender = PrerenderHandle::create(document, this, href, prerenderRelTypes); } else if (m_prerender->url() != href) { m_prerender->cancel(); m_prerender = PrerenderHandle::create(document, this, href, prerenderRelTypes); } // TODO(gavinp): Handle changes to rel types of existing prerenders. } else if (m_prerender) { m_prerender->cancel(); m_prerender.clear(); } return true; }
bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const AtomicString& crossOriginMode, const String& type, const String& as, const KURL& href, Document& document) { // TODO(yoav): Convert all uses of the CrossOriginAttribute to CrossOriginAttributeValue. crbug.com/486689 // FIXME(crbug.com/463266): We're ignoring type here. Maybe we shouldn't. dnsPrefetchIfNeeded(relAttribute, href, document); preconnectIfNeeded(relAttribute, href, document, crossOriginAttributeValue(crossOriginMode)); preloadIfNeeded(relAttribute, href, document, as); // FIXME(crbug.com/323096): Should take care of import. if ((relAttribute.isLinkPrefetch() || relAttribute.isLinkSubresource() || relAttribute.isTransitionExitingStylesheet()) && href.isValid() && document.frame()) { if (!m_client->shouldLoadLink()) return false; Resource::Type type = relAttribute.isLinkSubresource() ? Resource::LinkSubresource : Resource::LinkPrefetch; FetchRequest linkRequest(ResourceRequest(document.completeURL(href)), FetchInitiatorTypeNames::link); if (!crossOriginMode.isNull()) linkRequest.setCrossOriginAccessControl(document.securityOrigin(), crossOriginMode); setResource(document.fetcher()->fetchLinkResource(type, linkRequest)); } if (const unsigned prerenderRelTypes = prerenderRelTypesFromRelAttribute(relAttribute)) { if (!m_prerender) { m_prerender = PrerenderHandle::create(document, this, href, prerenderRelTypes); } else if (m_prerender->url() != href) { m_prerender->cancel(); m_prerender = PrerenderHandle::create(document, this, href, prerenderRelTypes); } // TODO(gavinp): Handle changes to rel types of existing prerenders. } else if (m_prerender) { m_prerender->cancel(); m_prerender.clear(); } return true; }
static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute, const KURL& href, Document& document, const String& as, const String& mimeType, const String& media, CrossOriginAttributeValue crossOrigin, LinkCaller caller, bool& errorOccurred, ViewportDescription* viewportDescription) { if (!document.loader() || !relAttribute.isLinkPreload()) return nullptr; UseCounter::count(document, UseCounter::LinkRelPreload); ASSERT(RuntimeEnabledFeatures::linkPreloadEnabled()); if (!href.isValid() || href.isEmpty()) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> has an invalid `href` value"))); return nullptr; } if (!media.isEmpty()) { MediaValues* mediaValues = MediaValues::createDynamicIfFrameExists(document.frame()); if (viewportDescription) mediaValues->overrideViewportDimensions(viewportDescription->maxWidth.getFloatValue(), viewportDescription->maxHeight.getFloatValue()); // Preload only if media matches MediaQuerySet* mediaQueries = MediaQuerySet::create(media); MediaQueryEvaluator evaluator(*mediaValues); if (!evaluator.eval(mediaQueries)) return nullptr; } if (caller == LinkCalledFromHeader) UseCounter::count(document, UseCounter::LinkHeaderPreload); Resource::Type resourceType; if (!LinkLoader::getResourceTypeFromAsAttribute(as, resourceType)) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> must have a valid `as` value"))); errorOccurred = true; return nullptr; } if (!isSupportedType(resourceType, mimeType)) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> has an unsupported `type` value"))); return nullptr; } ResourceRequest resourceRequest(document.completeURL(href)); ResourceFetcher::determineRequestContext(resourceRequest, resourceType, false); FetchRequest linkRequest(resourceRequest, FetchInitiatorTypeNames::link, document.encodingName()); if (crossOrigin != CrossOriginAttributeNotSet) linkRequest.setCrossOriginAccessControl(document.getSecurityOrigin(), crossOrigin); Settings* settings = document.settings(); if (settings && settings->logPreload()) document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, DebugMessageLevel, String("Preload triggered for " + href.host() + href.path()))); linkRequest.setForPreload(true, monotonicallyIncreasingTime()); linkRequest.setLinkPreload(true); linkRequest.setPriority(document.fetcher()->loadPriority(resourceType, linkRequest)); return document.loader()->startPreload(resourceType, linkRequest); }
bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, CrossOriginAttributeValue crossOrigin, const String& type, const String& as, const String& media, const KURL& href, Document& document, const NetworkHintsInterface& networkHintsInterface) { // TODO(yoav): Do all links need to load only after they're in document??? // TODO(yoav): Convert all uses of the CrossOriginAttribute to CrossOriginAttributeValue. crbug.com/486689 // FIXME(crbug.com/463266): We're ignoring type here, for everything but preload. Maybe we shouldn't. dnsPrefetchIfNeeded(relAttribute, href, document, networkHintsInterface, LinkCalledFromMarkup); preconnectIfNeeded(relAttribute, href, document, crossOrigin, networkHintsInterface, LinkCalledFromMarkup); bool errorOccurred = false; if (m_client->shouldLoadLink()) createLinkPreloadResourceClient(preloadIfNeeded(relAttribute, href, document, as, type, media, crossOrigin, LinkCalledFromMarkup, errorOccurred, nullptr)); if (errorOccurred) m_linkLoadingErrorTimer.startOneShot(0, BLINK_FROM_HERE); if (href.isEmpty() || !href.isValid()) released(); // FIXME(crbug.com/323096): Should take care of import. if (relAttribute.isLinkPrefetch() && href.isValid() && document.frame()) { if (!m_client->shouldLoadLink()) return false; UseCounter::count(document, UseCounter::LinkRelPrefetch); FetchRequest linkRequest(ResourceRequest(document.completeURL(href)), FetchInitiatorTypeNames::link); if (crossOrigin != CrossOriginAttributeNotSet) linkRequest.setCrossOriginAccessControl(document.getSecurityOrigin(), crossOrigin); setResource(LinkFetchResource::fetch(Resource::LinkPrefetch, linkRequest, document.fetcher())); } if (const unsigned prerenderRelTypes = prerenderRelTypesFromRelAttribute(relAttribute, document)) { if (!m_prerender) { m_prerender = PrerenderHandle::create(document, this, href, prerenderRelTypes); } else if (m_prerender->url() != href) { m_prerender->cancel(); m_prerender = PrerenderHandle::create(document, this, href, prerenderRelTypes); } // TODO(gavinp): Handle changes to rel types of existing prerenders. } else if (m_prerender) { m_prerender->cancel(); m_prerender.clear(); } return true; }
bool LinkLoader::loadLink(const LinkRelAttribute& relAttribute, const String& type, const String& sizes, const KURL& href, Document* document) { // 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 (relAttribute.m_isDNSPrefetch) { Settings* settings = document->settings(); // 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 ENABLE(LINK_PREFETCH) if ((relAttribute.m_isLinkPrefetch || relAttribute.m_isLinkPrerender || relAttribute.m_isLinkSubresource) && href.isValid() && document->frame()) { if (!m_client->shouldLoadLink()) return false; ResourceLoadPriority priority = ResourceLoadPriorityUnresolved; 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; type = CachedResource::LinkSubresource; } else if (relAttribute.m_isLinkPrerender) type = CachedResource::LinkPrerender; ResourceRequest linkRequest(document->completeURL(href)); if (m_cachedLinkResource) { m_cachedLinkResource->removeClient(this); m_cachedLinkResource = 0; } m_cachedLinkResource = document->cachedResourceLoader()->requestLinkResource(type, linkRequest, priority); if (m_cachedLinkResource) m_cachedLinkResource->addClient(this); } #endif return true; }
void LinkLoader::preloadIfNeeded(const LinkRelAttribute& relAttribute, const KURL& href, Document& document, const String& as) { if (relAttribute.isLinkPreload()) { ASSERT(RuntimeEnabledFeatures::linkPreloadEnabled()); if (!href.isValid() || href.isEmpty()) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> has an invalid `href` value"))); return; } Resource::Type type; if (!getTypeFromAsAttribute(as, type)) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> must have a valid `as` value"))); return; } FetchRequest linkRequest(ResourceRequest(document.completeURL(href)), FetchInitiatorTypeNames::link); Settings* settings = document.settings(); if (settings && settings->logPreload()) document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, DebugMessageLevel, String("Preload triggered for " + href.host() + href.path()))); setResource(document.fetcher()->fetchLinkPreloadResource(type, linkRequest)); } }
static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute, const KURL& href, Document& document, const String& as, const String& mimeType, CrossOriginAttributeValue crossOrigin, LinkCaller caller, bool& errorOccurred) { if (!document.loader() || !relAttribute.isLinkPreload()) return nullptr; UseCounter::count(document, UseCounter::LinkRelPreload); ASSERT(RuntimeEnabledFeatures::linkPreloadEnabled()); if (!href.isValid() || href.isEmpty()) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> has an invalid `href` value"))); return nullptr; } if (caller == LinkCalledFromHeader) UseCounter::count(document, UseCounter::LinkHeaderPreload); Resource::Type resourceType; if (!LinkLoader::getResourceTypeFromAsAttribute(as, resourceType)) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> must have a valid `as` value"))); errorOccurred = true; return nullptr; } if (!isSupportedType(resourceType, mimeType)) { document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, WarningMessageLevel, String("<link rel=preload> has an unsupported `type` value"))); return nullptr; } ResourceRequest resourceRequest(document.completeURL(href)); ResourceFetcher::determineRequestContext(resourceRequest, resourceType, false); FetchRequest linkRequest(resourceRequest, FetchInitiatorTypeNames::link); linkRequest.setPriority(document.fetcher()->loadPriority(resourceType, linkRequest)); if (crossOrigin != CrossOriginAttributeNotSet) linkRequest.setCrossOriginAccessControl(document.securityOrigin(), crossOrigin); Settings* settings = document.settings(); if (settings && settings->logPreload()) document.addConsoleMessage(ConsoleMessage::create(OtherMessageSource, DebugMessageLevel, String("Preload triggered for " + href.host() + href.path()))); linkRequest.setForPreload(true); linkRequest.setLinkPreload(true); return document.loader()->startPreload(resourceType, linkRequest); }
void LinkLoader::prefetchIfNeeded(Document& document, const KURL& href, const LinkRelAttribute& relAttribute, CrossOriginAttributeValue crossOrigin, ReferrerPolicy referrerPolicy) { if (relAttribute.isLinkPrefetch() && href.isValid() && document.frame()) { UseCounter::count(document, UseCounter::LinkRelPrefetch); FetchRequest linkRequest(ResourceRequest(document.completeURL(href)), FetchInitiatorTypeNames::link); if (referrerPolicy != ReferrerPolicyDefault) { linkRequest.mutableResourceRequest().setHTTPReferrer( SecurityPolicy::generateReferrer(referrerPolicy, href, document.outgoingReferrer())); } if (crossOrigin != CrossOriginAttributeNotSet) { linkRequest.setCrossOriginAccessControl(document.getSecurityOrigin(), crossOrigin); } setResource(LinkFetchResource::fetch(Resource::LinkPrefetch, linkRequest, document.fetcher())); } }
void HTMLLinkElement::process() { if (!inDocument() || m_isInShadowTree) { ASSERT(!m_sheet); return; } String type = m_type.lower(); // IE extension: location of small icon for locationbar / bookmarks // We'll record this URL per document, even if we later only use it in top level frames if (m_relAttribute.m_isIcon && m_url.isValid() && !m_url.isEmpty()) { if (!checkBeforeLoadEvent()) return; document()->setIconURL(m_url.string(), type); } #ifdef ANDROID_APPLE_TOUCH_ICON if ((m_relAttribute.m_isTouchIcon || m_relAttribute.m_isPrecomposedTouchIcon) && m_url.isValid() && !m_url.isEmpty() && document()->frame()) document()->frame()->loader()->client() ->dispatchDidReceiveTouchIconURL(m_url.string(), m_relAttribute.m_isPrecomposedTouchIcon); #endif if (m_relAttribute.m_isDNSPrefetch) { Settings* settings = document()->settings(); // 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() && m_url.isValid() && !m_url.isEmpty()) ResourceHandle::prepareForURL(m_url); } #if ENABLE(LINK_PREFETCH) if ((m_relAttribute.m_isLinkPrefetch || m_relAttribute.m_isLinkPrerender || m_relAttribute.m_isLinkSubresource) && m_url.isValid() && document()->frame()) { if (!checkBeforeLoadEvent()) return; ResourceLoadPriority priority = ResourceLoadPriorityUnresolved; CachedResource::Type type = CachedResource::LinkPrefetch; // We only make one request to the cachedresourcelodaer if multiple rel types are // specified. if (m_relAttribute.m_isLinkSubresource) { priority = ResourceLoadPriorityLow; type = CachedResource::LinkSubresource; } else if (m_relAttribute.m_isLinkPrerender) type = CachedResource::LinkPrerender; ResourceRequest linkRequest(document()->completeURL(m_url)); m_cachedLinkResource = document()->cachedResourceLoader()->requestLinkResource(type, linkRequest, priority); if (m_cachedLinkResource) m_cachedLinkResource->addClient(this); } #endif bool acceptIfTypeContainsTextCSS = document()->page() && document()->page()->settings() && document()->page()->settings()->treatsAnyTextCSSLinkAsStylesheet(); if (m_disabledState != Disabled && (m_relAttribute.m_isStyleSheet || (acceptIfTypeContainsTextCSS && type.contains("text/css"))) && document()->frame() && m_url.isValid()) { String charset = getAttribute(charsetAttr); if (charset.isEmpty() && document()->frame()) charset = document()->charset(); if (m_cachedSheet) { removePendingSheet(); m_cachedSheet->removeClient(this); m_cachedSheet = 0; } if (!checkBeforeLoadEvent()) return; m_loading = true; bool mediaQueryMatches = true; if (!m_media.isEmpty()) { RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(document()); RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(m_media); MediaQueryEvaluator evaluator(document()->frame()->view()->mediaType(), document()->frame(), documentStyle.get()); mediaQueryMatches = evaluator.eval(media.get()); } // Don't hold up render tree construction and script execution on stylesheets // that are not needed for the rendering at the moment. bool blocking = mediaQueryMatches && !isAlternate(); addPendingSheet(blocking ? Blocking : NonBlocking); // Load stylesheets that are not needed for the rendering immediately with low priority. ResourceLoadPriority priority = blocking ? ResourceLoadPriorityUnresolved : ResourceLoadPriorityVeryLow; ResourceRequest request(document()->completeURL(m_url)); m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(request, charset, priority); if (m_cachedSheet) m_cachedSheet->addClient(this); else { // The request may have been denied if (for example) the stylesheet is local and the document is remote. m_loading = false; removePendingSheet(); } } else if (m_sheet) { // we no longer contain a stylesheet, e.g. perhaps rel or type was changed m_sheet = 0; document()->styleSelectorChanged(DeferRecalcStyle); } }