void FrameFetchContext::upgradeInsecureRequest(FetchRequest& fetchRequest) { KURL url = fetchRequest.resourceRequest().url(); // Tack an 'Upgrade-Insecure-Requests' header to outgoing navigational requests, as described in // https://w3c.github.io/webappsec/specs/upgrade/#feature-detect if (fetchRequest.resourceRequest().frameType() != WebURLRequest::FrameTypeNone) fetchRequest.mutableResourceRequest().addHTTPHeaderField("Upgrade-Insecure-Requests", "1"); if (m_document && m_document->insecureRequestsPolicy() == SecurityContext::InsecureRequestsUpgrade && url.protocolIs("http")) { ASSERT(m_document->insecureNavigationsToUpgrade()); // We always upgrade requests that meet any of the following criteria: // // 1. Are for subresources (including nested frames). // 2. Are form submissions. // 3. Whose hosts are contained in the document's InsecureNavigationSet. const ResourceRequest& request = fetchRequest.resourceRequest(); if (request.frameType() == WebURLRequest::FrameTypeNone || request.frameType() == WebURLRequest::FrameTypeNested || request.requestContext() == WebURLRequest::RequestContextForm || (!url.host().isNull() && m_document->insecureNavigationsToUpgrade()->contains(url.host().impl()->hash()))) { UseCounter::count(m_document, UseCounter::UpgradeInsecureRequestsUpgradedRequest); url.setProtocol("https"); if (url.port() == 80) url.setPort(443); fetchRequest.mutableResourceRequest().setURL(url); } } }
void FrameFetchContext::upgradeInsecureRequest(FetchRequest& fetchRequest) { KURL url = fetchRequest.resourceRequest().url(); // Tack an 'Upgrade-Insecure-Requests' header to outgoing navigational requests, as described in // https://w3c.github.io/webappsec/specs/upgrade/#feature-detect if (fetchRequest.resourceRequest().frameType() != WebURLRequest::FrameTypeNone) fetchRequest.mutableResourceRequest().addHTTPHeaderField("Upgrade-Insecure-Requests", "1"); // If we don't yet have an |m_document| (because we're loading an iframe, for instance), check the FrameLoader's policy. WebInsecureRequestPolicy relevantPolicy = m_document ? m_document->getInsecureRequestPolicy() : frame()->loader().getInsecureRequestPolicy(); SecurityContext::InsecureNavigationsSet* relevantNavigationSet = m_document ? m_document->insecureNavigationsToUpgrade() : frame()->loader().insecureNavigationsToUpgrade(); if (url.protocolIs("http") && relevantPolicy & kUpgradeInsecureRequests) { // We always upgrade requests that meet any of the following criteria: // // 1. Are for subresources (including nested frames). // 2. Are form submissions. // 3. Whose hosts are contained in the document's InsecureNavigationSet. const ResourceRequest& request = fetchRequest.resourceRequest(); if (request.frameType() == WebURLRequest::FrameTypeNone || request.frameType() == WebURLRequest::FrameTypeNested || request.requestContext() == WebURLRequest::RequestContextForm || (!url.host().isNull() && relevantNavigationSet->contains(url.host().impl()->hash()))) { UseCounter::count(m_document, UseCounter::UpgradeInsecureRequestsUpgradedRequest); url.setProtocol("https"); if (url.port() == 80) url.setPort(443); fetchRequest.mutableResourceRequest().setURL(url); } } }
void ResourceFetcher::requestLoadStarted(Resource* resource, const FetchRequest& request, ResourceLoadStartType type) { if (type == ResourceLoadingFromCache) notifyLoadedFromMemoryCache(resource); if (request.resourceRequest().url().protocolIsData()) return; m_validatedURLs.add(request.resourceRequest().url()); }
ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type type, FetchRequest& request, const String& charset) { ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url())); WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data()); addAdditionalRequestHeaders(request.mutableResourceRequest(), type); ResourcePtr<Resource> resource = createResource(type, request.resourceRequest(), charset); memoryCache()->add(resource.get()); return resource; }
ResourcePtr<ImageResource> ImageResource::fetch(FetchRequest& request, ResourceFetcher* fetcher) { if (request.resourceRequest().requestContext() == WebURLRequest::RequestContextUnspecified) request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage); if (fetcher->context().pageDismissalEventBeingDispatched()) { KURL requestURL = request.resourceRequest().url(); if (requestURL.isValid() && fetcher->context().canRequest(Resource::Image, request.resourceRequest(), requestURL, request.options(), request.forPreload(), request.originRestriction())) fetcher->context().sendImagePing(requestURL); return 0; } if (fetcher->clientDefersImage(request.resourceRequest().url())) request.setDefer(FetchRequest::DeferredByClient); return toImageResource(fetcher->requestResource(request, ImageResourceFactory())); }
ResourcePtr<Resource> LinkFetchResource::fetch(Resource::Type type, FetchRequest& request, ResourceFetcher* fetcher) { ASSERT(type == LinkPrefetch); ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); fetcher->determineRequestContext(request.mutableResourceRequest(), type); return fetcher->requestResource(request, LinkResourceFactory(type)); }
ResourcePtr<ScriptResource> ScriptResource::fetch(FetchRequest& request, ResourceFetcher* fetcher) { ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextScript); ResourcePtr<ScriptResource> resource = toScriptResource(fetcher->requestResource(request, ScriptResourceFactory())); if (resource && !request.integrityMetadata().isEmpty()) resource->setIntegrityMetadata(request.integrityMetadata()); return resource; }
void expectHeader(const char* input, const char* headerName, bool isPresent, const char* headerValue) { KURL inputURL(ParsedURLString, input); FetchRequest fetchRequest = FetchRequest(ResourceRequest(inputURL), FetchInitiatorInfo()); fetchContext->addClientHintsIfNecessary(fetchRequest); EXPECT_STREQ(isPresent ? headerValue : "", fetchRequest.resourceRequest().httpHeaderField(headerName).utf8().data()); }
void expectUpgrade(const char* input, WebURLRequest::RequestContext requestContext, WebURLRequest::FrameType frameType, const char* expected) { KURL inputURL(ParsedURLString, input); KURL expectedURL(ParsedURLString, expected); FetchRequest fetchRequest = FetchRequest(ResourceRequest(inputURL), FetchInitiatorInfo()); fetchRequest.mutableResourceRequest().setRequestContext(requestContext); fetchRequest.mutableResourceRequest().setFrameType(frameType); fetchContext->upgradeInsecureRequest(fetchRequest); EXPECT_STREQ(expectedURL.getString().utf8().data(), fetchRequest.resourceRequest().url().getString().utf8().data()); EXPECT_EQ(expectedURL.protocol(), fetchRequest.resourceRequest().url().protocol()); EXPECT_EQ(expectedURL.host(), fetchRequest.resourceRequest().url().host()); EXPECT_EQ(expectedURL.port(), fetchRequest.resourceRequest().url().port()); EXPECT_EQ(expectedURL.hasPort(), fetchRequest.resourceRequest().url().hasPort()); EXPECT_EQ(expectedURL.path(), fetchRequest.resourceRequest().url().path()); }
void expectHTTPSHeader(const char* input, WebURLRequest::FrameType frameType, bool shouldPrefer) { KURL inputURL(ParsedURLString, input); FetchRequest fetchRequest = FetchRequest(ResourceRequest(inputURL), FetchInitiatorInfo()); fetchRequest.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextScript); fetchRequest.mutableResourceRequest().setFrameType(frameType); fetchContext->upgradeInsecureRequest(fetchRequest); EXPECT_STREQ(shouldPrefer ? "1" : "", fetchRequest.resourceRequest().httpHeaderField(HTTPNames::Upgrade_Insecure_Requests).utf8().data()); }
void expectHeader(const char* input, const char* headerName, bool isPresent, const char* headerValue, float width = 0) { KURL inputURL(ParsedURLString, input); FetchRequest fetchRequest = FetchRequest(ResourceRequest(inputURL), FetchInitiatorInfo()); if (width > 0) { FetchRequest::ResourceWidth resourceWidth; resourceWidth.width = width; resourceWidth.isSet = true; fetchRequest.setResourceWidth(resourceWidth); } fetchContext->addClientHintsIfNecessary(fetchRequest); EXPECT_STREQ(isPresent ? headerValue : "", fetchRequest.resourceRequest().httpHeaderField(headerName).utf8().data()); }
ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, const FetchRequest& fetchRequest, Resource* existingResource) const { const ResourceRequest& request = fetchRequest.resourceRequest(); if (!existingResource) return Load; // If the same URL has been loaded as a different type, we need to reload. if (existingResource->type() != type) { WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch."); return Reload; } // Do not load from cache if images are not enabled. The load for this image will be blocked // in ImageResource::load. if (FetchRequest::DeferredByClient == fetchRequest.defer()) return Reload; // Always use data uris. // FIXME: Extend this to non-images. if (type == Resource::Image && request.url().protocolIsData()) return Use; if (!existingResource->canReuse(request)) return Reload; // Never use cache entries for downloadToFile requests. The caller expects the resource in a file. if (request.downloadToFile()) return Reload; // Certain requests (e.g., XHRs) might have manually set headers that require revalidation. // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch // of things about how revalidation works that manual headers violate, so punt to Reload instead. if (request.isConditional()) return Reload; // Don't reload resources while pasting. if (m_allowStaleResources) return Use; if (!fetchRequest.options().canReuseRequest(existingResource->options())) return Reload; // CachePolicyHistoryBuffer uses the cache no matter what. CachePolicy cachePolicy = context().cachePolicy(document()); if (cachePolicy == CachePolicyHistoryBuffer) return Use; // Don't reuse resources with Cache-control: no-store. if (existingResource->hasCacheControlNoStoreHeader()) { WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store."); return Reload; } // If credentials were sent with the previous request and won't be // with this one, or vice versa, re-fetch the resource. // // This helps with the case where the server sends back // "Access-Control-Allow-Origin: *" all the time, but some of the // client's requests are made without CORS and some with. if (existingResource->resourceRequest().allowStoredCredentials() != request.allowStoredCredentials()) { WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings."); return Reload; } // During the initial load, avoid loading the same resource multiple times for a single document, // even if the cache policies would tell us to. // We also group loads of the same resource together. // Raw resources are exempted, as XHRs fall into this category and may have user-set Cache-Control: // headers or other factors that require separate requests. if (type != Resource::Raw) { if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url())) return Use; if (existingResource->isLoading()) return Use; } // CachePolicyReload always reloads if (cachePolicy == CachePolicyReload) { WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload."); return Reload; } // We'll try to reload the resource if it failed last time. if (existingResource->errorOccurred()) { WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state"); return Reload; } // List of available images logic allows images to be re-used without cache validation. We restrict this only to images // from memory cache which are the same as the version in the current document. if (type == Resource::Image && existingResource == cachedResource(request.url())) return Use; // Check if the cache headers requires us to revalidate (cache expiration for example). if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders() || request.cacheControlContainsNoCache()) { // See if the resource has usable ETag or Last-modified headers. if (existingResource->canUseCacheValidator()) return Revalidate; // No, must reload. WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators."); return Reload; } return Use; }
ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request) { ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw); TRACE_EVENT0("blink", "ResourceFetcher::requestResource"); KURL url = request.resourceRequest().url(); WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), ResourceTypeName(type)); // If only the fragment identifiers differ, it is the same resource. url = MemoryCache::removeFragmentIdentifierIfNeeded(url); if (!url.isValid()) return 0; if (!canRequest(type, url, request.options(), request.originRestriction())) return 0; if (LocalFrame* f = frame()) f->loaderClient()->dispatchWillRequestResource(&request); // See if we can use an existing resource from the cache. ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url); const RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get()); switch (policy) { case Reload: memoryCache()->remove(resource.get()); // Fall through case Load: resource = createResourceForLoading(type, request, request.charset()); break; case Revalidate: resource = createResourceForRevalidation(request, resource.get()); break; case Use: memoryCache()->updateForAccess(resource.get()); break; } if (!resource) return 0; if (!resource->hasClients()) m_deadStatsRecorder.update(policy); if (policy != Use) resource->setIdentifier(createUniqueIdentifier()); ResourceLoadPriority priority = loadPriority(type, request); if (priority != resource->resourceRequest().priority()) { resource->mutableResourceRequest().setPriority(priority); resource->didChangePriority(priority, 0); } if (resourceNeedsLoad(resource.get(), request, policy)) { if (!shouldLoadNewResource(type)) { if (memoryCache()->contains(resource.get())) memoryCache()->remove(resource.get()); return 0; } resource->load(this, request.options()); // For asynchronous loads that immediately fail, it's sufficient to return a // null Resource, as it indicates that something prevented the load from starting. // If there's a network error, that failure will happen asynchronously. However, if // a sync load receives a network error, it will have already happened by this point. // In that case, the requester should have access to the relevant ResourceError, so // we need to return a non-null Resource. if (resource->errorOccurred()) { if (memoryCache()->contains(resource.get())) memoryCache()->remove(resource.get()); return 0; } } requestLoadStarted(resource.get(), request, policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork); ASSERT(resource->url() == url.string()); m_documentResources.set(resource->url(), resource); return resource; }
ResourcePtr<DocumentResource> DocumentResource::fetchSVGDocument(FetchRequest& request, ResourceFetcher* fetcher) { ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage); return toDocumentResource(fetcher->requestResource(request, SVGDocumentResourceFactory())); }
ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request) { ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextFont); return toFontResource(requestResource(Resource::Font, request)); }
ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request) { request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer); ResourcePtr<Resource> resource = requestResource(Resource::Image, request); return resource && resource->type() == Resource::Image ? toImageResource(resource) : 0; }
ResourcePtr<FontResource> FontResource::fetch(FetchRequest& request, ResourceFetcher* fetcher) { ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextFont); return toFontResource(fetcher->requestResource(request, FontResourceFactory())); }