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;
}
Example #2
0
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;
}
Example #3
0
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);
}
Example #4
0
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;
}
Example #5
0
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;
}
Example #6
0
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);
}
Example #8
0
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()));
  }
}
Example #9
0
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);
    }
}