| /* |
| * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
| * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ |
| * Copyright (C) 2010 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: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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/html/parser/html_preload_scanner.h" |
| |
| #include <memory> |
| #include "base/optional.h" |
| #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" |
| #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h" |
| #include "third_party/blink/renderer/core/css/css_primitive_value.h" |
| #include "third_party/blink/renderer/core/css/css_property_name.h" |
| #include "third_party/blink/renderer/core/css/media_list.h" |
| #include "third_party/blink/renderer/core/css/media_query_evaluator.h" |
| #include "third_party/blink/renderer/core/css/media_values_cached.h" |
| #include "third_party/blink/renderer/core/css/parser/css_parser.h" |
| #include "third_party/blink/renderer/core/css/parser/sizes_attribute_parser.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/settings.h" |
| #include "third_party/blink/renderer/core/frame/viewport_data.h" |
| #include "third_party/blink/renderer/core/html/cross_origin_attribute.h" |
| #include "third_party/blink/renderer/core/html/html_dimension.h" |
| #include "third_party/blink/renderer/core/html/html_image_element.h" |
| #include "third_party/blink/renderer/core/html/html_meta_element.h" |
| #include "third_party/blink/renderer/core/html/link_rel_attribute.h" |
| #include "third_party/blink/renderer/core/html/link_web_bundle.h" |
| #include "third_party/blink/renderer/core/html/loading_attribute.h" |
| #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" |
| #include "third_party/blink/renderer/core/html/parser/html_srcset_parser.h" |
| #include "third_party/blink/renderer/core/html/parser/html_tokenizer.h" |
| #include "third_party/blink/renderer/core/html_names.h" |
| #include "third_party/blink/renderer/core/input_type_names.h" |
| #include "third_party/blink/renderer/core/loader/document_loader.h" |
| #include "third_party/blink/renderer/core/loader/importance_attribute.h" |
| #include "third_party/blink/renderer/core/loader/preload_helper.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/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/core/script/script_loader.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource.h" |
| #include "third_party/blink/renderer/platform/loader/subresource_integrity.h" |
| #include "third_party/blink/renderer/platform/network/mime/content_type.h" |
| #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| bool Match(const StringImpl* impl, const QualifiedName& q_name) { |
| return impl == q_name.LocalName().Impl(); |
| } |
| |
| bool Match(const AtomicString& name, const QualifiedName& q_name) { |
| DCHECK(IsMainThread()); |
| return q_name.LocalName() == name; |
| } |
| |
| bool Match(const String& name, const QualifiedName& q_name) { |
| return ThreadSafeMatch(name, q_name); |
| } |
| |
| const StringImpl* TagImplFor(const HTMLToken::DataVector& data) { |
| AtomicString tag_name(data); |
| const StringImpl* result = tag_name.Impl(); |
| if (result->IsStatic()) |
| return result; |
| return nullptr; |
| } |
| |
| const StringImpl* TagImplFor(const String& tag_name) { |
| const StringImpl* result = tag_name.Impl(); |
| if (result->IsStatic()) |
| return result; |
| return nullptr; |
| } |
| |
| String InitiatorFor(const StringImpl* tag_impl) { |
| DCHECK(tag_impl); |
| if (Match(tag_impl, html_names::kImgTag)) |
| return html_names::kImgTag.LocalName(); |
| if (Match(tag_impl, html_names::kInputTag)) |
| return html_names::kInputTag.LocalName(); |
| if (Match(tag_impl, html_names::kLinkTag)) |
| return html_names::kLinkTag.LocalName(); |
| if (Match(tag_impl, html_names::kScriptTag)) |
| return html_names::kScriptTag.LocalName(); |
| if (Match(tag_impl, html_names::kVideoTag)) |
| return html_names::kVideoTag.LocalName(); |
| NOTREACHED(); |
| return g_empty_string; |
| } |
| |
| bool MediaAttributeMatches(const MediaValuesCached& media_values, |
| const String& attribute_value) { |
| // Since this is for preload scanning only, ExecutionContext-based origin |
| // trials for media queries are not needed. |
| scoped_refptr<MediaQuerySet> media_queries = |
| MediaQuerySet::Create(attribute_value, nullptr); |
| MediaQueryEvaluator media_query_evaluator(media_values); |
| return media_query_evaluator.Eval(*media_queries); |
| } |
| |
| void ParseWebBundleUrlsAndFillHash(const AtomicString& value, |
| HashSet<KURL>& url_hash) { |
| // Parse the attribute value as a space-separated list of urls |
| SpaceSplitString urls(value); |
| for (wtf_size_t i = 0; i < urls.size(); ++i) { |
| KURL url = LinkWebBundle::ParseResourceUrl(urls[i]); |
| if (url.IsValid()) { |
| url_hash.insert(std::move(url)); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| class TokenPreloadScanner::StartTagScanner { |
| STACK_ALLOCATED(); |
| |
| public: |
| StartTagScanner(const StringImpl* tag_impl, |
| MediaValuesCached* media_values, |
| SubresourceIntegrity::IntegrityFeatures features, |
| TokenPreloadScanner::ScannerType scanner_type, |
| bool priority_hints_origin_trial_enabled, |
| const HashSet<String>* disabled_image_types) |
| : tag_impl_(tag_impl), |
| link_is_style_sheet_(false), |
| link_is_preconnect_(false), |
| link_is_preload_(false), |
| link_is_modulepreload_(false), |
| link_is_import_(false), |
| link_is_webbundle_(false), |
| matched_(true), |
| input_is_image_(false), |
| nomodule_attribute_value_(false), |
| source_size_(0), |
| source_size_set_(false), |
| defer_(FetchParameters::kNoDefer), |
| cross_origin_(kCrossOriginAttributeNotSet), |
| importance_(mojom::FetchImportanceMode::kImportanceAuto), |
| importance_mode_set_(false), |
| media_values_(media_values), |
| referrer_policy_set_(false), |
| referrer_policy_(network::mojom::ReferrerPolicy::kDefault), |
| integrity_attr_set_(false), |
| is_async_(false), |
| integrity_features_(features), |
| loading_attr_value_(LoadingAttributeValue::kAuto), |
| width_attr_dimension_type_( |
| HTMLImageElement::LazyLoadDimensionType::kNotAbsolute), |
| height_attr_dimension_type_( |
| HTMLImageElement::LazyLoadDimensionType::kNotAbsolute), |
| inline_style_dimensions_type_( |
| HTMLImageElement::LazyLoadDimensionType::kNotAbsolute), |
| scanner_type_(scanner_type), |
| priority_hints_origin_trial_enabled_( |
| priority_hints_origin_trial_enabled), |
| disabled_image_types_(disabled_image_types) { |
| if (Match(tag_impl_, html_names::kImgTag) || |
| Match(tag_impl_, html_names::kSourceTag) || |
| Match(tag_impl_, html_names::kLinkTag)) { |
| source_size_ = |
| SizesAttributeParser(media_values_, String(), nullptr).length(); |
| return; |
| } |
| if (!Match(tag_impl_, html_names::kInputTag) && |
| !Match(tag_impl_, html_names::kScriptTag) && |
| !Match(tag_impl_, html_names::kVideoTag)) |
| tag_impl_ = nullptr; |
| } |
| |
| enum URLReplacement { kAllowURLReplacement, kDisallowURLReplacement }; |
| |
| void ProcessAttributes(const HTMLToken::AttributeList& attributes) { |
| DCHECK(IsMainThread()); |
| if (!tag_impl_) |
| return; |
| for (const HTMLToken::Attribute& html_token_attribute : attributes) { |
| AtomicString attribute_name(html_token_attribute.GetName()); |
| String attribute_value = html_token_attribute.Value8BitIfNecessary(); |
| ProcessAttribute(attribute_name, attribute_value); |
| } |
| PostProcessAfterAttributes(); |
| } |
| |
| void ProcessAttributes( |
| const Vector<CompactHTMLToken::Attribute>& attributes) { |
| if (!tag_impl_) |
| return; |
| for (const CompactHTMLToken::Attribute& html_token_attribute : attributes) |
| ProcessAttribute(html_token_attribute.GetName(), |
| html_token_attribute.Value()); |
| PostProcessAfterAttributes(); |
| } |
| |
| void PostProcessAfterAttributes() { |
| if (Match(tag_impl_, html_names::kImgTag) || |
| (link_is_preload_ && as_attribute_value_ == "image")) |
| SetUrlFromImageAttributes(); |
| } |
| |
| void HandlePictureSourceURL(PictureData& picture_data) { |
| if (Match(tag_impl_, html_names::kSourceTag) && matched_ && |
| picture_data.source_url.IsEmpty()) { |
| // Must create an IsolatedCopy() since the srcset attribute value will get |
| // sent back to the main thread between when we set this, and when we |
| // process the closing tag which would clear picture_data_. Having any ref |
| // to a string we're going to send will fail |
| // IsSafeToSendToAnotherThread(). |
| picture_data.source_url = |
| srcset_image_candidate_.ToString().IsolatedCopy(); |
| picture_data.source_size_set = source_size_set_; |
| picture_data.source_size = source_size_; |
| picture_data.picked = true; |
| } else if (Match(tag_impl_, html_names::kImgTag) && |
| !picture_data.source_url.IsEmpty()) { |
| SetUrlToLoad(picture_data.source_url, kAllowURLReplacement); |
| } |
| } |
| |
| bool MaybeUpdateExclusionInfo( |
| const KURL& document_url, |
| scoped_refptr<const PreloadRequest::ExclusionInfo>& exclusion_info) { |
| if (!IsLinkRelWebBundle()) |
| return false; |
| HashSet<KURL> scopes; |
| HashSet<KURL> resources; |
| if (exclusion_info) { |
| scopes = exclusion_info->scopes(); |
| resources = exclusion_info->resources(); |
| } |
| ParseWebBundleUrlsAndFillHash(scopes_attribute_value_, scopes); |
| ParseWebBundleUrlsAndFillHash(resources_attribute_value_, resources); |
| exclusion_info = base::MakeRefCounted<PreloadRequest::ExclusionInfo>( |
| document_url, std::move(scopes), std::move(resources)); |
| return true; |
| } |
| |
| std::unique_ptr<PreloadRequest> CreatePreloadRequest( |
| const KURL& predicted_base_url, |
| const SegmentedString& source, |
| const ClientHintsPreferences& client_hints_preferences, |
| const PictureData& picture_data, |
| const CachedDocumentParameters& document_parameters, |
| const PreloadRequest::ExclusionInfo* exclusion_info, |
| bool treat_links_as_in_body) { |
| PreloadRequest::RequestType request_type = |
| PreloadRequest::kRequestTypePreload; |
| base::Optional<ResourceType> type; |
| if (ShouldPreconnect()) { |
| request_type = PreloadRequest::kRequestTypePreconnect; |
| } else { |
| if (IsLinkRelPreload()) { |
| request_type = PreloadRequest::kRequestTypeLinkRelPreload; |
| type = ResourceTypeForLinkPreload(); |
| if (type == base::nullopt) |
| return nullptr; |
| } else if (IsLinkRelModulePreload()) { |
| request_type = PreloadRequest::kRequestTypeLinkRelPreload; |
| type = ResourceType::kScript; |
| } |
| if (!ShouldPreload(type)) { |
| return nullptr; |
| } |
| } |
| |
| TextPosition position = |
| TextPosition(source.CurrentLine(), source.CurrentColumn()); |
| FetchParameters::ResourceWidth resource_width; |
| float source_size = source_size_; |
| bool source_size_set = source_size_set_; |
| if (picture_data.picked) { |
| source_size_set = picture_data.source_size_set; |
| source_size = picture_data.source_size; |
| } |
| ResourceFetcher::IsImageSet is_image_set = |
| (picture_data.picked || !srcset_image_candidate_.IsEmpty()) |
| ? ResourceFetcher::kImageIsImageSet |
| : ResourceFetcher::kImageNotImageSet; |
| |
| if (source_size_set) { |
| resource_width.width = source_size; |
| resource_width.is_set = true; |
| } |
| |
| if (type == base::nullopt) |
| type = GetResourceType(); |
| |
| // The element's 'referrerpolicy' attribute (if present) takes precedence |
| // over the document's referrer policy. |
| network::mojom::ReferrerPolicy referrer_policy = |
| (referrer_policy_ != network::mojom::ReferrerPolicy::kDefault) |
| ? referrer_policy_ |
| : document_parameters.referrer_policy; |
| auto request = PreloadRequest::CreateIfNeeded( |
| InitiatorFor(tag_impl_), position, url_to_load_, predicted_base_url, |
| type.value(), referrer_policy, PreloadRequest::kDocumentIsReferrer, |
| is_image_set, exclusion_info, resource_width, client_hints_preferences, |
| request_type); |
| if (!request) |
| return nullptr; |
| |
| bool is_module = (type_attribute_value_ == "module"); |
| bool is_script = Match(tag_impl_, html_names::kScriptTag); |
| if ((is_script && is_module) || IsLinkRelModulePreload()) { |
| is_module = true; |
| request->SetScriptType(mojom::blink::ScriptType::kModule); |
| } |
| |
| request->SetCrossOrigin(cross_origin_); |
| request->SetImportance(importance_); |
| request->SetNonce(nonce_); |
| request->SetCharset(Charset()); |
| request->SetDefer(defer_); |
| |
| RenderBlockingBehavior render_blocking_behavior = |
| RenderBlockingBehavior::kUnset; |
| if (is_script && (is_module || defer_ == FetchParameters::kLazyLoad)) { |
| render_blocking_behavior = |
| is_async_ ? RenderBlockingBehavior::kPotentiallyBlocking |
| : RenderBlockingBehavior::kNonBlocking; |
| } else if (is_script || type == ResourceType::kCSSStyleSheet) { |
| // CSS here is render blocking, as non blocking doesn't get preloaded. |
| // JS here is a blocking one, as others would've been caught by the |
| // previous condition. |
| render_blocking_behavior = |
| treat_links_as_in_body ? RenderBlockingBehavior::kInBodyParserBlocking |
| : RenderBlockingBehavior::kBlocking; |
| } |
| request->SetRenderBlockingBehavior(render_blocking_behavior); |
| |
| if (type == ResourceType::kImage && Match(tag_impl_, html_names::kImgTag) && |
| IsLazyLoadImageDeferable(document_parameters)) { |
| return nullptr; |
| } |
| // Do not set integrity metadata for <link> elements for destinations not |
| // supporting SRI (crbug.com/1058045). |
| // A corresponding check for non-preload-scanner code path is in |
| // PreloadHelper::PreloadIfNeeded(). |
| // TODO(crbug.com/981419): Honor the integrity attribute value for all |
| // supported preload destinations, not just the destinations that support |
| // SRI in the first place. |
| if (type == ResourceType::kScript || type == ResourceType::kCSSStyleSheet) { |
| request->SetIntegrityMetadata(integrity_metadata_); |
| } |
| |
| if (scanner_type_ == ScannerType::kInsertion) |
| request->SetFromInsertionScanner(true); |
| |
| return request; |
| } |
| |
| private: |
| template <typename NameType> |
| void ProcessScriptAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| // FIXME - Don't set crossorigin multiple times. |
| if (Match(attribute_name, html_names::kSrcAttr)) { |
| SetUrlToLoad(attribute_value, kDisallowURLReplacement); |
| } else if (Match(attribute_name, html_names::kCrossoriginAttr)) { |
| SetCrossOrigin(attribute_value); |
| } else if (Match(attribute_name, html_names::kNonceAttr)) { |
| SetNonce(attribute_value); |
| } else if (Match(attribute_name, html_names::kAsyncAttr)) { |
| is_async_ = true; |
| SetDefer(FetchParameters::kLazyLoad); |
| } else if (Match(attribute_name, html_names::kDeferAttr)) { |
| SetDefer(FetchParameters::kLazyLoad); |
| } else if (!integrity_attr_set_ && |
| Match(attribute_name, html_names::kIntegrityAttr)) { |
| integrity_attr_set_ = true; |
| SubresourceIntegrity::ParseIntegrityAttribute( |
| attribute_value, integrity_features_, integrity_metadata_); |
| } else if (Match(attribute_name, html_names::kTypeAttr)) { |
| type_attribute_value_ = attribute_value; |
| } else if (Match(attribute_name, html_names::kLanguageAttr)) { |
| language_attribute_value_ = attribute_value; |
| } else if (Match(attribute_name, html_names::kNomoduleAttr)) { |
| nomodule_attribute_value_ = true; |
| } else if (!referrer_policy_set_ && |
| Match(attribute_name, html_names::kReferrerpolicyAttr) && |
| !attribute_value.IsNull()) { |
| SetReferrerPolicy(attribute_value, |
| kDoNotSupportReferrerPolicyLegacyKeywords); |
| } else if (!importance_mode_set_ && |
| Match(attribute_name, html_names::kImportanceAttr) && |
| priority_hints_origin_trial_enabled_) { |
| SetImportance(attribute_value); |
| } |
| } |
| |
| template <typename NameType> |
| void ProcessImgAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| if (Match(attribute_name, html_names::kSrcAttr) && img_src_url_.IsNull()) { |
| img_src_url_ = attribute_value; |
| } else if (Match(attribute_name, html_names::kCrossoriginAttr)) { |
| SetCrossOrigin(attribute_value); |
| } else if (Match(attribute_name, html_names::kSrcsetAttr) && |
| srcset_attribute_value_.IsNull()) { |
| srcset_attribute_value_ = attribute_value; |
| } else if (Match(attribute_name, html_names::kSizesAttr) && |
| !source_size_set_) { |
| ParseSourceSize(attribute_value); |
| } else if (!referrer_policy_set_ && |
| Match(attribute_name, html_names::kReferrerpolicyAttr) && |
| !attribute_value.IsNull()) { |
| SetReferrerPolicy(attribute_value, kSupportReferrerPolicyLegacyKeywords); |
| } else if (!importance_mode_set_ && |
| Match(attribute_name, html_names::kImportanceAttr) && |
| priority_hints_origin_trial_enabled_) { |
| SetImportance(attribute_value); |
| } else if (loading_attr_value_ == LoadingAttributeValue::kAuto && |
| Match(attribute_name, html_names::kLoadingAttr) && |
| RuntimeEnabledFeatures::LazyImageLoadingEnabled()) { |
| loading_attr_value_ = GetLoadingAttributeValue(attribute_value); |
| } else if (width_attr_dimension_type_ == |
| HTMLImageElement::LazyLoadDimensionType::kNotAbsolute && |
| Match(attribute_name, html_names::kWidthAttr) && |
| RuntimeEnabledFeatures::LazyImageLoadingEnabled()) { |
| width_attr_dimension_type_ = |
| HTMLImageElement::GetAttributeLazyLoadDimensionType(attribute_value); |
| } else if (height_attr_dimension_type_ == |
| HTMLImageElement::LazyLoadDimensionType::kNotAbsolute && |
| Match(attribute_name, html_names::kHeightAttr) && |
| RuntimeEnabledFeatures::LazyImageLoadingEnabled()) { |
| height_attr_dimension_type_ = |
| HTMLImageElement::GetAttributeLazyLoadDimensionType(attribute_value); |
| } else if (inline_style_dimensions_type_ == |
| HTMLImageElement::LazyLoadDimensionType::kNotAbsolute && |
| Match(attribute_name, html_names::kStyleAttr) && |
| RuntimeEnabledFeatures::LazyImageLoadingEnabled()) { |
| CSSParserMode mode = |
| media_values_->StrictMode() ? kHTMLStandardMode : kHTMLQuirksMode; |
| const ImmutableCSSPropertyValueSet* property_set = |
| CSSParser::ParseInlineStyleDeclaration( |
| attribute_value, mode, SecureContextMode::kInsecureContext); |
| inline_style_dimensions_type_ = |
| HTMLImageElement::GetInlineStyleDimensionsType(property_set); |
| } |
| } |
| |
| void SetUrlFromImageAttributes() { |
| srcset_image_candidate_ = |
| BestFitSourceForSrcsetAttribute(media_values_->DevicePixelRatio(), |
| source_size_, srcset_attribute_value_); |
| SetUrlToLoad(BestFitSourceForImageAttributes( |
| media_values_->DevicePixelRatio(), source_size_, |
| img_src_url_, srcset_image_candidate_), |
| kAllowURLReplacement); |
| } |
| |
| template <typename NameType> |
| void ProcessLinkAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| // FIXME - Don't set rel/media/crossorigin multiple times. |
| if (Match(attribute_name, html_names::kHrefAttr)) { |
| SetUrlToLoad(attribute_value, kDisallowURLReplacement); |
| // Used in SetUrlFromImageAttributes() when as=image. |
| img_src_url_ = attribute_value; |
| } else if (Match(attribute_name, html_names::kRelAttr)) { |
| LinkRelAttribute rel(attribute_value); |
| link_is_style_sheet_ = |
| rel.IsStyleSheet() && !rel.IsAlternate() && |
| rel.GetIconType() == mojom::blink::FaviconIconType::kInvalid && |
| !rel.IsDNSPrefetch(); |
| link_is_preconnect_ = rel.IsPreconnect(); |
| link_is_preload_ = rel.IsLinkPreload(); |
| link_is_modulepreload_ = rel.IsModulePreload(); |
| link_is_import_ = rel.IsImport(); |
| link_is_webbundle_ = rel.IsWebBundle(); |
| } else if (Match(attribute_name, html_names::kMediaAttr)) { |
| matched_ &= MediaAttributeMatches(*media_values_, attribute_value); |
| } else if (Match(attribute_name, html_names::kCrossoriginAttr)) { |
| SetCrossOrigin(attribute_value); |
| } else if (Match(attribute_name, html_names::kNonceAttr)) { |
| SetNonce(attribute_value); |
| } else if (Match(attribute_name, html_names::kAsAttr)) { |
| as_attribute_value_ = attribute_value.DeprecatedLower(); |
| } else if (Match(attribute_name, html_names::kTypeAttr)) { |
| type_attribute_value_ = attribute_value; |
| } else if (!referrer_policy_set_ && |
| Match(attribute_name, html_names::kReferrerpolicyAttr) && |
| !attribute_value.IsNull()) { |
| SetReferrerPolicy(attribute_value, |
| kDoNotSupportReferrerPolicyLegacyKeywords); |
| } else if (!integrity_attr_set_ && |
| Match(attribute_name, html_names::kIntegrityAttr)) { |
| integrity_attr_set_ = true; |
| SubresourceIntegrity::ParseIntegrityAttribute( |
| attribute_value, integrity_features_, integrity_metadata_); |
| } else if (Match(attribute_name, html_names::kImagesrcsetAttr) && |
| srcset_attribute_value_.IsNull()) { |
| srcset_attribute_value_ = attribute_value; |
| } else if (Match(attribute_name, html_names::kImagesizesAttr) && |
| !source_size_set_) { |
| ParseSourceSize(attribute_value); |
| } else if (!importance_mode_set_ && |
| Match(attribute_name, html_names::kImportanceAttr) && |
| priority_hints_origin_trial_enabled_) { |
| SetImportance(attribute_value); |
| } else if (Match(attribute_name, html_names::kScopesAttr)) { |
| scopes_attribute_value_ = AtomicString(attribute_value); |
| } else if (Match(attribute_name, html_names::kResourcesAttr)) { |
| resources_attribute_value_ = AtomicString(attribute_value); |
| } |
| } |
| |
| template <typename NameType> |
| void ProcessInputAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| // FIXME - Don't set type multiple times. |
| if (Match(attribute_name, html_names::kSrcAttr)) { |
| SetUrlToLoad(attribute_value, kDisallowURLReplacement); |
| } else if (Match(attribute_name, html_names::kTypeAttr)) { |
| input_is_image_ = |
| EqualIgnoringASCIICase(attribute_value, input_type_names::kImage); |
| } |
| } |
| |
| template <typename NameType> |
| void ProcessSourceAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| if (Match(attribute_name, html_names::kSrcsetAttr) && |
| srcset_image_candidate_.IsEmpty()) { |
| srcset_attribute_value_ = attribute_value; |
| srcset_image_candidate_ = BestFitSourceForSrcsetAttribute( |
| media_values_->DevicePixelRatio(), source_size_, attribute_value); |
| } else if (Match(attribute_name, html_names::kSizesAttr) && |
| !source_size_set_) { |
| ParseSourceSize(attribute_value); |
| if (!srcset_image_candidate_.IsEmpty()) { |
| srcset_image_candidate_ = BestFitSourceForSrcsetAttribute( |
| media_values_->DevicePixelRatio(), source_size_, |
| srcset_attribute_value_); |
| } |
| } else if (Match(attribute_name, html_names::kMediaAttr)) { |
| // FIXME - Don't match media multiple times. |
| matched_ &= MediaAttributeMatches(*media_values_, attribute_value); |
| } else if (Match(attribute_name, html_names::kTypeAttr)) { |
| matched_ &= HTMLImageElement::SupportedImageType(attribute_value, |
| disabled_image_types_); |
| } |
| } |
| |
| template <typename NameType> |
| void ProcessVideoAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| if (Match(attribute_name, html_names::kPosterAttr)) |
| SetUrlToLoad(attribute_value, kDisallowURLReplacement); |
| else if (Match(attribute_name, html_names::kCrossoriginAttr)) |
| SetCrossOrigin(attribute_value); |
| } |
| |
| template <typename NameType> |
| void ProcessAttribute(const NameType& attribute_name, |
| const String& attribute_value) { |
| if (Match(attribute_name, html_names::kCharsetAttr)) |
| charset_ = attribute_value; |
| |
| if (Match(tag_impl_, html_names::kScriptTag)) |
| ProcessScriptAttribute(attribute_name, attribute_value); |
| else if (Match(tag_impl_, html_names::kImgTag)) |
| ProcessImgAttribute(attribute_name, attribute_value); |
| else if (Match(tag_impl_, html_names::kLinkTag)) |
| ProcessLinkAttribute(attribute_name, attribute_value); |
| else if (Match(tag_impl_, html_names::kInputTag)) |
| ProcessInputAttribute(attribute_name, attribute_value); |
| else if (Match(tag_impl_, html_names::kSourceTag)) |
| ProcessSourceAttribute(attribute_name, attribute_value); |
| else if (Match(tag_impl_, html_names::kVideoTag)) |
| ProcessVideoAttribute(attribute_name, attribute_value); |
| } |
| |
| bool IsLazyLoadImageDeferable( |
| const CachedDocumentParameters& document_parameters) { |
| if (!document_parameters.lazy_load_image_observer) |
| return false; |
| |
| bool is_fully_loadable = |
| document_parameters.lazy_load_image_observer |
| ->IsFullyLoadableFirstKImageAndDecrementCount(); |
| if (document_parameters.lazy_load_image_setting == |
| LocalFrame::LazyLoadImageSetting::kDisabled) { |
| return false; |
| } |
| |
| switch (loading_attr_value_) { |
| case LoadingAttributeValue::kEager: |
| return false; |
| case LoadingAttributeValue::kLazy: |
| return true; |
| case LoadingAttributeValue::kAuto: |
| if ((width_attr_dimension_type_ == |
| HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall && |
| height_attr_dimension_type_ == |
| HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall) || |
| inline_style_dimensions_type_ == |
| HTMLImageElement::LazyLoadDimensionType::kAbsoluteSmall) { |
| // Fetch small images eagerly. |
| return false; |
| } else if (is_fully_loadable || |
| document_parameters.lazy_load_image_setting != |
| LocalFrame::LazyLoadImageSetting::kEnabledAutomatic) { |
| return false; |
| } |
| break; |
| } |
| return true; |
| } |
| |
| void SetUrlToLoad(const String& value, URLReplacement replacement) { |
| // We only respect the first src/href, per HTML5: |
| // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#attribute-name-state |
| if (replacement == kDisallowURLReplacement && !url_to_load_.IsEmpty()) |
| return; |
| String url = StripLeadingAndTrailingHTMLSpaces(value); |
| if (url.IsEmpty()) |
| return; |
| url_to_load_ = url; |
| } |
| |
| const String& Charset() const { |
| // FIXME: Its not clear that this if is needed, the loader probably ignores |
| // charset for image requests anyway. |
| if (Match(tag_impl_, html_names::kImgTag) || |
| Match(tag_impl_, html_names::kVideoTag)) |
| return g_empty_string; |
| return charset_; |
| } |
| |
| base::Optional<ResourceType> ResourceTypeForLinkPreload() const { |
| DCHECK(link_is_preload_); |
| return PreloadHelper::GetResourceTypeFromAsAttribute(as_attribute_value_); |
| } |
| |
| ResourceType GetResourceType() const { |
| if (Match(tag_impl_, html_names::kScriptTag)) |
| return ResourceType::kScript; |
| if (Match(tag_impl_, html_names::kImgTag) || |
| Match(tag_impl_, html_names::kVideoTag) || |
| (Match(tag_impl_, html_names::kInputTag) && input_is_image_)) |
| return ResourceType::kImage; |
| if (Match(tag_impl_, html_names::kLinkTag) && link_is_style_sheet_) |
| return ResourceType::kCSSStyleSheet; |
| if (link_is_preconnect_) |
| return ResourceType::kRaw; |
| if (Match(tag_impl_, html_names::kLinkTag) && link_is_import_) |
| return ResourceType::kImportResource; |
| NOTREACHED(); |
| return ResourceType::kRaw; |
| } |
| |
| bool ShouldPreconnect() const { |
| return Match(tag_impl_, html_names::kLinkTag) && link_is_preconnect_ && |
| !url_to_load_.IsEmpty(); |
| } |
| |
| bool IsLinkRelPreload() const { |
| return Match(tag_impl_, html_names::kLinkTag) && link_is_preload_ && |
| !url_to_load_.IsEmpty(); |
| } |
| |
| bool IsLinkRelModulePreload() const { |
| return Match(tag_impl_, html_names::kLinkTag) && link_is_modulepreload_ && |
| !url_to_load_.IsEmpty(); |
| } |
| |
| bool IsLinkRelWebBundle() const { |
| return Match(tag_impl_, html_names::kLinkTag) && link_is_webbundle_; |
| } |
| |
| bool ShouldPreloadLink(base::Optional<ResourceType>& type) const { |
| if (link_is_style_sheet_) { |
| return type_attribute_value_.IsEmpty() || |
| MIMETypeRegistry::IsSupportedStyleSheetMIMEType( |
| ContentType(type_attribute_value_).GetType()); |
| } else if (link_is_preload_) { |
| if (type == ResourceType::kImage) { |
| return HTMLImageElement::SupportedImageType(type_attribute_value_, |
| disabled_image_types_); |
| } |
| if (type_attribute_value_.IsEmpty()) |
| return true; |
| String type_from_attribute = ContentType(type_attribute_value_).GetType(); |
| if ((type == ResourceType::kFont && |
| !MIMETypeRegistry::IsSupportedFontMIMEType(type_from_attribute)) || |
| (type == ResourceType::kCSSStyleSheet && |
| !MIMETypeRegistry::IsSupportedStyleSheetMIMEType( |
| type_from_attribute))) { |
| return false; |
| } |
| } else if (link_is_modulepreload_) { |
| return true; |
| } else if (!link_is_import_) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ShouldPreload(base::Optional<ResourceType>& type) const { |
| if (url_to_load_.IsEmpty()) |
| return false; |
| if (!matched_) |
| return false; |
| if (Match(tag_impl_, html_names::kLinkTag)) |
| return ShouldPreloadLink(type); |
| if (Match(tag_impl_, html_names::kInputTag) && !input_is_image_) |
| return false; |
| if (Match(tag_impl_, html_names::kScriptTag)) { |
| ScriptLoader::ScriptTypeAtPrepare script_type = |
| ScriptLoader::GetScriptTypeAtPrepare( |
| type_attribute_value_, language_attribute_value_, |
| ScriptLoader::kAllowLegacyTypeInTypeAttribute); |
| switch (script_type) { |
| case ScriptLoader::ScriptTypeAtPrepare::kInvalid: |
| return false; |
| |
| case ScriptLoader::ScriptTypeAtPrepare::kImportMap: |
| // TODO(crbug.com/922212): External import maps are not yet supported. |
| return false; |
| |
| case ScriptLoader::ScriptTypeAtPrepare::kClassic: |
| case ScriptLoader::ScriptTypeAtPrepare::kModule: |
| if (ScriptLoader::BlockForNoModule(script_type, |
| nomodule_attribute_value_)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| void ParseSourceSize(const String& attribute_value) { |
| source_size_ = |
| SizesAttributeParser(media_values_, attribute_value, nullptr).length(); |
| source_size_set_ = true; |
| } |
| |
| void SetCrossOrigin(const String& cors_setting) { |
| cross_origin_ = GetCrossOriginAttributeValue(cors_setting); |
| } |
| |
| void SetReferrerPolicy( |
| const String& attribute_value, |
| ReferrerPolicyLegacyKeywordsSupport legacy_keywords_support) { |
| referrer_policy_set_ = true; |
| SecurityPolicy::ReferrerPolicyFromString( |
| attribute_value, legacy_keywords_support, &referrer_policy_); |
| } |
| |
| void SetImportance(const String& importance) { |
| DCHECK(priority_hints_origin_trial_enabled_); |
| importance_mode_set_ = true; |
| importance_ = GetFetchImportanceAttributeValue(importance); |
| } |
| |
| void SetNonce(const String& nonce) { nonce_ = nonce; } |
| |
| void SetDefer(FetchParameters::DeferOption defer) { defer_ = defer; } |
| |
| bool Defer() const { return defer_ != FetchParameters::kNoDefer; } |
| |
| const StringImpl* tag_impl_; |
| String url_to_load_; |
| ImageCandidate srcset_image_candidate_; |
| String charset_; |
| bool link_is_style_sheet_; |
| bool link_is_preconnect_; |
| bool link_is_preload_; |
| bool link_is_modulepreload_; |
| bool link_is_import_; |
| bool link_is_webbundle_; |
| bool matched_; |
| bool input_is_image_; |
| String img_src_url_; |
| String srcset_attribute_value_; |
| String as_attribute_value_; |
| String type_attribute_value_; |
| String language_attribute_value_; |
| AtomicString scopes_attribute_value_; |
| AtomicString resources_attribute_value_; |
| bool nomodule_attribute_value_; |
| float source_size_; |
| bool source_size_set_; |
| FetchParameters::DeferOption defer_; |
| CrossOriginAttributeValue cross_origin_; |
| mojom::FetchImportanceMode importance_; |
| bool importance_mode_set_; |
| String nonce_; |
| MediaValuesCached* media_values_; |
| bool referrer_policy_set_; |
| network::mojom::ReferrerPolicy referrer_policy_; |
| bool integrity_attr_set_; |
| bool is_async_; |
| IntegrityMetadataSet integrity_metadata_; |
| SubresourceIntegrity::IntegrityFeatures integrity_features_; |
| LoadingAttributeValue loading_attr_value_; |
| HTMLImageElement::LazyLoadDimensionType width_attr_dimension_type_; |
| HTMLImageElement::LazyLoadDimensionType height_attr_dimension_type_; |
| HTMLImageElement::LazyLoadDimensionType inline_style_dimensions_type_; |
| TokenPreloadScanner::ScannerType scanner_type_; |
| // For explanation, see TokenPreloadScanner's declaration. |
| bool priority_hints_origin_trial_enabled_; |
| const HashSet<String>* disabled_image_types_; |
| }; |
| |
| TokenPreloadScanner::TokenPreloadScanner( |
| const KURL& document_url, |
| std::unique_ptr<CachedDocumentParameters> document_parameters, |
| const MediaValuesCached::MediaValuesCachedData& media_values_cached_data, |
| const ScannerType scanner_type, |
| bool priority_hints_origin_trial_enabled) |
| : document_url_(document_url), |
| in_style_(false), |
| in_picture_(false), |
| in_script_(false), |
| seen_body_(false), |
| seen_img_(false), |
| template_count_(0), |
| document_parameters_(std::move(document_parameters)), |
| media_values_( |
| MakeGarbageCollected<MediaValuesCached>(media_values_cached_data)), |
| scanner_type_(scanner_type), |
| priority_hints_origin_trial_enabled_(priority_hints_origin_trial_enabled), |
| did_rewind_(false) { |
| DCHECK(document_parameters_.get()); |
| DCHECK(media_values_.Get()); |
| DCHECK(document_url.IsValid()); |
| css_scanner_.SetReferrerPolicy(document_parameters_->referrer_policy); |
| } |
| |
| TokenPreloadScanner::~TokenPreloadScanner() = default; |
| |
| TokenPreloadScannerCheckpoint TokenPreloadScanner::CreateCheckpoint() { |
| TokenPreloadScannerCheckpoint checkpoint = checkpoints_.size(); |
| checkpoints_.push_back(Checkpoint(predicted_base_element_url_, in_style_, |
| in_script_, template_count_, |
| exclusion_info_)); |
| return checkpoint; |
| } |
| |
| void TokenPreloadScanner::RewindTo( |
| TokenPreloadScannerCheckpoint checkpoint_index) { |
| // If this ASSERT fires, checkpointIndex is invalid. |
| DCHECK_LT(checkpoint_index, checkpoints_.size()); |
| const Checkpoint& checkpoint = checkpoints_[checkpoint_index]; |
| predicted_base_element_url_ = checkpoint.predicted_base_element_url; |
| in_style_ = checkpoint.in_style; |
| template_count_ = checkpoint.template_count; |
| exclusion_info_ = checkpoint.exclusion_info; |
| |
| did_rewind_ = true; |
| in_script_ = checkpoint.in_script; |
| |
| css_scanner_.Reset(); |
| checkpoints_.clear(); |
| } |
| |
| void TokenPreloadScanner::Scan(const HTMLToken& token, |
| const SegmentedString& source, |
| PreloadRequestStream& requests, |
| base::Optional<ViewportDescription>* viewport, |
| bool* is_csp_meta_tag) { |
| ScanCommon(token, source, requests, viewport, is_csp_meta_tag); |
| } |
| |
| void TokenPreloadScanner::Scan(const CompactHTMLToken& token, |
| const SegmentedString& source, |
| PreloadRequestStream& requests, |
| base::Optional<ViewportDescription>* viewport, |
| bool* is_csp_meta_tag) { |
| ScanCommon(token, source, requests, viewport, is_csp_meta_tag); |
| } |
| |
| static void HandleMetaViewport( |
| const String& attribute_value, |
| const CachedDocumentParameters* document_parameters, |
| MediaValuesCached* media_values, |
| base::Optional<ViewportDescription>* viewport) { |
| if (!document_parameters->viewport_meta_enabled) |
| return; |
| ViewportDescription description(ViewportDescription::kViewportMeta); |
| HTMLMetaElement::GetViewportDescriptionFromContentAttribute( |
| attribute_value, description, nullptr, |
| document_parameters->viewport_meta_zero_values_quirk); |
| if (viewport) |
| *viewport = description; |
| FloatSize initial_viewport(media_values->DeviceWidth(), |
| media_values->DeviceHeight()); |
| PageScaleConstraints constraints = description.Resolve( |
| initial_viewport, document_parameters->default_viewport_min_width); |
| media_values->OverrideViewportDimensions(constraints.layout_size.Width(), |
| constraints.layout_size.Height()); |
| } |
| |
| static void HandleMetaReferrer(const String& attribute_value, |
| CachedDocumentParameters* document_parameters, |
| CSSPreloadScanner* css_scanner) { |
| network::mojom::ReferrerPolicy meta_referrer_policy = |
| network::mojom::ReferrerPolicy::kDefault; |
| if (!attribute_value.IsEmpty() && !attribute_value.IsNull() && |
| SecurityPolicy::ReferrerPolicyFromString( |
| attribute_value, kSupportReferrerPolicyLegacyKeywords, |
| &meta_referrer_policy)) { |
| document_parameters->referrer_policy = meta_referrer_policy; |
| } |
| css_scanner->SetReferrerPolicy(document_parameters->referrer_policy); |
| } |
| |
| template <typename Token> |
| static void HandleMetaNameAttribute( |
| const Token& token, |
| CachedDocumentParameters* document_parameters, |
| MediaValuesCached* media_values, |
| CSSPreloadScanner* css_scanner, |
| base::Optional<ViewportDescription>* viewport) { |
| const typename Token::Attribute* name_attribute = |
| token.GetAttributeItem(html_names::kNameAttr); |
| if (!name_attribute) |
| return; |
| |
| String name_attribute_value(name_attribute->Value()); |
| const typename Token::Attribute* content_attribute = |
| token.GetAttributeItem(html_names::kContentAttr); |
| if (!content_attribute) |
| return; |
| |
| String content_attribute_value(content_attribute->Value()); |
| if (EqualIgnoringASCIICase(name_attribute_value, "viewport")) { |
| HandleMetaViewport(content_attribute_value, document_parameters, |
| media_values, viewport); |
| return; |
| } |
| |
| if (EqualIgnoringASCIICase(name_attribute_value, "referrer")) { |
| HandleMetaReferrer(content_attribute_value, document_parameters, |
| css_scanner); |
| } |
| } |
| |
| template <typename Token> |
| void TokenPreloadScanner::ScanCommon( |
| const Token& token, |
| const SegmentedString& source, |
| PreloadRequestStream& requests, |
| base::Optional<ViewportDescription>* viewport, |
| bool* is_csp_meta_tag) { |
| if (!document_parameters_->do_html_preload_scanning) |
| return; |
| |
| switch (token.GetType()) { |
| case HTMLToken::kCharacter: { |
| if (in_style_) { |
| css_scanner_.Scan(token.Data(), source, requests, |
| predicted_base_element_url_, exclusion_info_.get()); |
| } |
| return; |
| } |
| case HTMLToken::kEndTag: { |
| const StringImpl* tag_impl = TagImplFor(token.Data()); |
| if (Match(tag_impl, html_names::kTemplateTag)) { |
| if (template_count_) |
| --template_count_; |
| return; |
| } |
| if (Match(tag_impl, html_names::kStyleTag)) { |
| if (in_style_) |
| css_scanner_.Reset(); |
| in_style_ = false; |
| return; |
| } |
| if (Match(tag_impl, html_names::kScriptTag)) { |
| in_script_ = false; |
| return; |
| } |
| if (Match(tag_impl, html_names::kPictureTag)) { |
| in_picture_ = false; |
| picture_data_.picked = false; |
| } |
| return; |
| } |
| case HTMLToken::kStartTag: { |
| const StringImpl* tag_impl = TagImplFor(token.Data()); |
| if (Match(tag_impl, html_names::kTemplateTag)) { |
| ++template_count_; |
| return; |
| } |
| if (template_count_) |
| return; |
| if (Match(tag_impl, html_names::kStyleTag)) { |
| in_style_ = true; |
| return; |
| } |
| // Don't early return, because the StartTagScanner needs to look at these |
| // too. |
| if (Match(tag_impl, html_names::kScriptTag)) { |
| in_script_ = true; |
| } |
| if (Match(tag_impl, html_names::kBaseTag)) { |
| // The first <base> element is the one that wins. |
| if (!predicted_base_element_url_.IsEmpty()) |
| return; |
| UpdatePredictedBaseURL(token); |
| return; |
| } |
| if (Match(tag_impl, html_names::kMetaTag)) { |
| const typename Token::Attribute* equiv_attribute = |
| token.GetAttributeItem(html_names::kHttpEquivAttr); |
| if (equiv_attribute) { |
| String equiv_attribute_value(equiv_attribute->Value()); |
| if (EqualIgnoringASCIICase(equiv_attribute_value, |
| "content-security-policy")) { |
| *is_csp_meta_tag = true; |
| } else if (EqualIgnoringASCIICase(equiv_attribute_value, |
| "accept-ch")) { |
| const typename Token::Attribute* content_attribute = |
| token.GetAttributeItem(html_names::kContentAttr); |
| if (content_attribute) { |
| client_hints_preferences_.UpdateFromHttpEquivAcceptCH( |
| content_attribute->Value(), document_url_, nullptr); |
| } |
| } |
| return; |
| } |
| |
| HandleMetaNameAttribute(token, document_parameters_.get(), |
| media_values_.Get(), &css_scanner_, viewport); |
| } |
| |
| if (Match(tag_impl, html_names::kBodyTag)) { |
| seen_body_ = true; |
| } else if (Match(tag_impl, html_names::kImgTag)) { |
| seen_img_ = true; |
| } else if (Match(tag_impl, html_names::kPictureTag)) { |
| in_picture_ = true; |
| picture_data_ = PictureData(); |
| return; |
| } else if (!Match(tag_impl, html_names::kSourceTag) && |
| !Match(tag_impl, html_names::kImgTag)) { |
| // If found an "atypical" picture child, don't process it as a picture |
| // child. |
| in_picture_ = false; |
| picture_data_.picked = false; |
| } |
| |
| StartTagScanner scanner( |
| tag_impl, media_values_, document_parameters_->integrity_features, |
| scanner_type_, priority_hints_origin_trial_enabled_, |
| &document_parameters_->disabled_image_types); |
| scanner.ProcessAttributes(token.Attributes()); |
| |
| if (scanner.MaybeUpdateExclusionInfo(document_url_, exclusion_info_)) { |
| // This means the tag is <link rel=webbundle>. We don't preload the |
| // web bundle request. |
| return; |
| } |
| |
| // TODO(yoav): ViewportWidth is currently racy and might be zero in some |
| // cases, at least in tests. That problem will go away once |
| // ParseHTMLOnMainThread lands and MediaValuesCached is eliminated. |
| if (in_picture_ && media_values_->ViewportWidth()) |
| scanner.HandlePictureSourceURL(picture_data_); |
| std::unique_ptr<PreloadRequest> request = scanner.CreatePreloadRequest( |
| predicted_base_element_url_, source, client_hints_preferences_, |
| picture_data_, *document_parameters_, exclusion_info_.get(), |
| seen_img_ || seen_body_); |
| if (request) { |
| requests.push_back(std::move(request)); |
| } |
| return; |
| } |
| default: { return; } |
| } |
| } |
| |
| template <typename Token> |
| void TokenPreloadScanner::UpdatePredictedBaseURL(const Token& token) { |
| DCHECK(predicted_base_element_url_.IsEmpty()); |
| if (const typename Token::Attribute* href_attribute = |
| token.GetAttributeItem(html_names::kHrefAttr)) { |
| KURL url(document_url_, StripLeadingAndTrailingHTMLSpaces( |
| href_attribute->Value8BitIfNecessary())); |
| predicted_base_element_url_ = |
| url.IsValid() && !url.ProtocolIsData() ? url.Copy() : KURL(); |
| } |
| } |
| |
| HTMLPreloadScanner::HTMLPreloadScanner( |
| const HTMLParserOptions& options, |
| const KURL& document_url, |
| std::unique_ptr<CachedDocumentParameters> document_parameters, |
| const MediaValuesCached::MediaValuesCachedData& media_values_cached_data, |
| const TokenPreloadScanner::ScannerType scanner_type) |
| : scanner_(document_url, |
| std::move(document_parameters), |
| media_values_cached_data, |
| scanner_type, |
| options.priority_hints_origin_trial_enabled), |
| tokenizer_(std::make_unique<HTMLTokenizer>(options)) {} |
| |
| HTMLPreloadScanner::~HTMLPreloadScanner() = default; |
| |
| void HTMLPreloadScanner::AppendToEnd(const SegmentedString& source) { |
| source_.Append(source); |
| } |
| |
| PreloadRequestStream HTMLPreloadScanner::Scan( |
| const KURL& starting_base_element_url, |
| base::Optional<ViewportDescription>* viewport, |
| bool& has_csp_meta_tag) { |
| // HTMLTokenizer::updateStateFor only works on the main thread. |
| DCHECK(IsMainThread()); |
| |
| TRACE_EVENT1("blink", "HTMLPreloadScanner::scan", "source_length", |
| source_.length()); |
| |
| // When we start scanning, our best prediction of the baseElementURL is the |
| // real one! |
| if (!starting_base_element_url.IsEmpty()) |
| scanner_.SetPredictedBaseElementURL(starting_base_element_url); |
| |
| PreloadRequestStream requests; |
| |
| while (tokenizer_->NextToken(source_, token_)) { |
| if (token_.GetType() == HTMLToken::kStartTag) |
| tokenizer_->UpdateStateFor( |
| AttemptStaticStringCreation(token_.GetName(), kLikely8Bit)); |
| bool seen_csp_meta_tag = false; |
| scanner_.Scan(token_, source_, requests, viewport, &seen_csp_meta_tag); |
| has_csp_meta_tag |= seen_csp_meta_tag; |
| token_.Clear(); |
| // Don't preload anything if a CSP meta tag is found. We should rarely find |
| // them here because the HTMLPreloadScanner is only used for the synchronous |
| // parsing path. |
| if (seen_csp_meta_tag) { |
| // Reset the tokenizer, to avoid re-scanning tokens that we are about to |
| // start parsing. |
| source_.Clear(); |
| tokenizer_->Reset(); |
| return requests; |
| } |
| } |
| |
| return requests; |
| } |
| |
| CachedDocumentParameters::CachedDocumentParameters(Document* document) { |
| DCHECK(IsMainThread()); |
| DCHECK(document); |
| do_html_preload_scanning = |
| !document->GetSettings() || |
| document->GetSettings()->GetDoHtmlPreloadScanning(); |
| default_viewport_min_width = |
| document->GetViewportData().ViewportDefaultMinWidth(); |
| viewport_meta_zero_values_quirk = |
| document->GetSettings() && |
| document->GetSettings()->GetViewportMetaZeroValuesQuirk(); |
| viewport_meta_enabled = document->GetSettings() && |
| document->GetSettings()->GetViewportMetaEnabled(); |
| referrer_policy = document->GetReferrerPolicy(); |
| integrity_features = |
| SubresourceIntegrityHelper::GetFeatures(document->GetExecutionContext()); |
| if (document->Loader() && document->Loader()->GetFrame()) { |
| lazy_load_image_setting = |
| document->Loader()->GetFrame()->GetLazyLoadImageSetting(); |
| lazy_load_image_observer = document->EnsureLazyLoadImageObserver(); |
| } else { |
| lazy_load_image_setting = LocalFrame::LazyLoadImageSetting::kDisabled; |
| } |
| probe::GetDisabledImageTypes(document->GetExecutionContext(), |
| &disabled_image_types); |
| } |
| |
| } // namespace blink |