/* * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "third_party/blink/renderer/core/loader/link_loader.h" #include "third_party/blink/public/platform/web_prerender.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/use_counter.h" #include "third_party/blink/renderer/core/loader/importance_attribute.h" #include "third_party/blink/renderer/core/loader/link_load_parameters.h" #include "third_party/blink/renderer/core/loader/link_loader_client.h" #include "third_party/blink/renderer/core/loader/preload_helper.h" #include "third_party/blink/renderer/core/loader/private/prerender_handle.h" #include "third_party/blink/renderer/core/loader/resource/css_style_sheet_resource.h" #include "third_party/blink/renderer/core/loader/subresource_integrity_helper.h" #include "third_party/blink/renderer/core/origin_trials/origin_trials.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_client.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h" #include "third_party/blink/renderer/platform/loader/subresource_integrity.h" #include "third_party/blink/renderer/platform/prerender.h" namespace blink { class NetworkHintsInterface; namespace { unsigned PrerenderRelTypesFromRelAttribute( const LinkRelAttribute& rel_attribute, Document& document) { unsigned result = 0; if (rel_attribute.IsLinkPrerender()) { result |= kPrerenderRelTypePrerender; UseCounter::Count(document, WebFeature::kLinkRelPrerender); } if (rel_attribute.IsLinkNext()) { result |= kPrerenderRelTypeNext; UseCounter::Count(document, WebFeature::kLinkRelNext); } return result; } } // namespace LinkLoader* LinkLoader::Create(LinkLoaderClient* client) { return MakeGarbageCollected(client, client->GetLoadingTaskRunner()); } class LinkLoader::FinishObserver final : public GarbageCollectedFinalized, public ResourceFinishObserver { USING_GARBAGE_COLLECTED_MIXIN(FinishObserver); USING_PRE_FINALIZER(FinishObserver, ClearResource); public: FinishObserver(LinkLoader* loader, Resource* resource) : loader_(loader), resource_(resource) { resource_->AddFinishObserver( this, loader_->client_->GetLoadingTaskRunner().get()); } // ResourceFinishObserver implementation void NotifyFinished() override { if (!resource_) return; loader_->NotifyFinished(); ClearResource(); } String DebugName() const override { return "LinkLoader::ResourceFinishObserver"; } Resource* GetResource() { return resource_; } void ClearResource() { if (!resource_) return; resource_->RemoveFinishObserver(this); resource_ = nullptr; } void Trace(blink::Visitor* visitor) override { visitor->Trace(loader_); visitor->Trace(resource_); blink::ResourceFinishObserver::Trace(visitor); } private: Member loader_; Member resource_; }; LinkLoader::LinkLoader(LinkLoaderClient* client, scoped_refptr task_runner) : client_(client) { DCHECK(client_); } LinkLoader::~LinkLoader() = default; void LinkLoader::NotifyFinished() { DCHECK(finish_observer_); Resource* resource = finish_observer_->GetResource(); if (resource->ErrorOccurred()) client_->LinkLoadingErrored(); else client_->LinkLoaded(); } // https://html.spec.whatwg.org/#link-type-modulepreload void LinkLoader::NotifyModuleLoadFinished(ModuleScript* module) { // Step 11. "If result is null, fire an event named error at the link element, // and return." [spec text] // Step 12. "Fire an event named load at the link element." [spec text] if (!module) client_->LinkLoadingErrored(); else client_->LinkLoaded(); } void LinkLoader::DidStartPrerender() { client_->DidStartLinkPrerender(); } void LinkLoader::DidStopPrerender() { client_->DidStopLinkPrerender(); } void LinkLoader::DidSendLoadForPrerender() { client_->DidSendLoadForLinkPrerender(); } void LinkLoader::DidSendDOMContentLoadedForPrerender() { client_->DidSendDOMContentLoadedForLinkPrerender(); } Resource* LinkLoader::GetResourceForTesting() { return finish_observer_ ? finish_observer_->GetResource() : nullptr; } bool LinkLoader::LoadLink( const LinkLoadParameters& params, Document& document, const NetworkHintsInterface& network_hints_interface) { // If any loading process is in progress, abort it. Abort(); if (!client_->ShouldLoadLink()) return false; PreloadHelper::DnsPrefetchIfNeeded(params, &document, document.GetFrame(), network_hints_interface, PreloadHelper::kLinkCalledFromMarkup); PreloadHelper::PreconnectIfNeeded(params, &document, document.GetFrame(), network_hints_interface, PreloadHelper::kLinkCalledFromMarkup); Resource* resource = PreloadHelper::PreloadIfNeeded( params, document, NullURL(), PreloadHelper::kLinkCalledFromMarkup, nullptr, client_->IsLinkCreatedByParser() ? kParserInserted : kNotParserInserted); if (!resource) { resource = PreloadHelper::PrefetchIfNeeded(params, document); } if (resource) finish_observer_ = MakeGarbageCollected(this, resource); PreloadHelper::ModulePreloadIfNeeded(params, document, nullptr, this); if (const unsigned prerender_rel_types = PrerenderRelTypesFromRelAttribute(params.rel, document)) { if (!prerender_) { prerender_ = PrerenderHandle::Create(document, this, params.href, prerender_rel_types); } else if (prerender_->Url() != params.href) { prerender_->Cancel(); prerender_ = PrerenderHandle::Create(document, this, params.href, prerender_rel_types); } // TODO(gavinp): Handle changes to rel types of existing prerenders. } else if (prerender_) { prerender_->Cancel(); prerender_.Clear(); } return true; } void LinkLoader::LoadStylesheet(const LinkLoadParameters& params, const AtomicString& local_name, const WTF::TextEncoding& charset, FetchParameters::DeferOption defer_option, Document& document, ResourceClient* link_client) { ResourceRequest resource_request(document.CompleteURL(params.href)); resource_request.SetReferrerPolicy(params.referrer_policy); mojom::FetchImportanceMode importance_mode = GetFetchImportanceAttributeValue(params.importance); DCHECK(importance_mode == mojom::FetchImportanceMode::kImportanceAuto || origin_trials::PriorityHintsEnabled(&document)); resource_request.SetFetchImportanceMode(importance_mode); ResourceLoaderOptions options; options.initiator_info.name = local_name; FetchParameters link_fetch_params(resource_request, options); link_fetch_params.SetCharset(charset); link_fetch_params.SetDefer(defer_option); link_fetch_params.SetContentSecurityPolicyNonce(params.nonce); CrossOriginAttributeValue cross_origin = params.cross_origin; if (cross_origin != kCrossOriginAttributeNotSet) { link_fetch_params.SetCrossOriginAccessControl(document.GetSecurityOrigin(), cross_origin); } String integrity_attr = params.integrity; if (!integrity_attr.IsEmpty()) { IntegrityMetadataSet metadata_set; SubresourceIntegrity::ParseIntegrityAttribute( integrity_attr, SubresourceIntegrityHelper::GetFeatures(&document), metadata_set); link_fetch_params.SetIntegrityMetadata(metadata_set); link_fetch_params.MutableResourceRequest().SetFetchIntegrity( integrity_attr); } CSSStyleSheetResource::Fetch(link_fetch_params, document.Fetcher(), link_client); } void LinkLoader::Abort() { if (prerender_) { prerender_->Cancel(); prerender_.Clear(); } if (finish_observer_) { finish_observer_->ClearResource(); finish_observer_ = nullptr; } } void LinkLoader::Trace(blink::Visitor* visitor) { visitor->Trace(finish_observer_); visitor->Trace(client_); visitor->Trace(prerender_); SingleModuleClient::Trace(visitor); PrerenderClient::Trace(visitor); } } // namespace blink