Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / parser / HTMLPreloadScanner.cpp
blob13165ba5a435e236c59281815e7812f868ac6699
1 /*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/
4 * Copyright (C) 2010 Google Inc. All Rights Reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "config.h"
29 #include "core/html/parser/HTMLPreloadScanner.h"
31 #include "core/HTMLNames.h"
32 #include "core/InputTypeNames.h"
33 #include "core/css/MediaList.h"
34 #include "core/css/MediaQueryEvaluator.h"
35 #include "core/css/MediaValuesCached.h"
36 #include "core/css/parser/SizesAttributeParser.h"
37 #include "core/dom/Document.h"
38 #include "core/frame/Settings.h"
39 #include "core/html/HTMLImageElement.h"
40 #include "core/html/HTMLMetaElement.h"
41 #include "core/html/LinkRelAttribute.h"
42 #include "core/html/parser/HTMLParserIdioms.h"
43 #include "core/html/parser/HTMLSrcsetParser.h"
44 #include "core/html/parser/HTMLTokenizer.h"
45 #include "platform/RuntimeEnabledFeatures.h"
46 #include "platform/TraceEvent.h"
47 #include "wtf/MainThread.h"
49 namespace blink {
51 using namespace HTMLNames;
53 static bool match(const StringImpl* impl, const QualifiedName& qName)
55 return impl == qName.localName().impl();
58 static bool match(const AtomicString& name, const QualifiedName& qName)
60 ASSERT(isMainThread());
61 return qName.localName() == name;
64 static bool match(const String& name, const QualifiedName& qName)
66 return threadSafeMatch(name, qName);
69 static const StringImpl* tagImplFor(const HTMLToken::DataVector& data)
71 AtomicString tagName(data);
72 const StringImpl* result = tagName.impl();
73 if (result->isStatic())
74 return result;
75 return nullptr;
78 static const StringImpl* tagImplFor(const String& tagName)
80 const StringImpl* result = tagName.impl();
81 if (result->isStatic())
82 return result;
83 return nullptr;
86 static String initiatorFor(const StringImpl* tagImpl)
88 ASSERT(tagImpl);
89 if (match(tagImpl, imgTag))
90 return imgTag.localName();
91 if (match(tagImpl, inputTag))
92 return inputTag.localName();
93 if (match(tagImpl, linkTag))
94 return linkTag.localName();
95 if (match(tagImpl, scriptTag))
96 return scriptTag.localName();
97 if (match(tagImpl, videoTag))
98 return videoTag.localName();
99 ASSERT_NOT_REACHED();
100 return emptyString();
103 static bool mediaAttributeMatches(const MediaValues& mediaValues, const String& attributeValue)
105 RefPtrWillBeRawPtr<MediaQuerySet> mediaQueries = MediaQuerySet::createOffMainThread(attributeValue);
106 MediaQueryEvaluator mediaQueryEvaluator(mediaValues);
107 return mediaQueryEvaluator.eval(mediaQueries.get());
110 class TokenPreloadScanner::StartTagScanner {
111 STACK_ALLOCATED();
112 public:
113 StartTagScanner(const StringImpl* tagImpl, PassRefPtrWillBeRawPtr<MediaValues> mediaValues)
114 : m_tagImpl(tagImpl)
115 , m_linkIsStyleSheet(false)
116 , m_linkIsPreconnect(false)
117 , m_linkIsImport(false)
118 , m_matchedMediaAttribute(true)
119 , m_inputIsImage(false)
120 , m_sourceSize(0)
121 , m_sourceSizeSet(false)
122 , m_isCORSEnabled(false)
123 , m_defer(FetchRequest::NoDefer)
124 , m_allowCredentials(DoNotAllowStoredCredentials)
125 , m_mediaValues(mediaValues)
126 , m_referrerPolicySet(false)
127 , m_referrerPolicy(ReferrerPolicyDefault)
129 ASSERT(m_mediaValues->isCached());
130 if (match(m_tagImpl, imgTag)
131 || match(m_tagImpl, sourceTag)) {
132 m_sourceSize = SizesAttributeParser(m_mediaValues, String()).length();
133 return;
135 if ( !match(m_tagImpl, inputTag)
136 && !match(m_tagImpl, linkTag)
137 && !match(m_tagImpl, scriptTag)
138 && !match(m_tagImpl, videoTag))
139 m_tagImpl = 0;
142 enum URLReplacement {
143 AllowURLReplacement,
144 DisallowURLReplacement
147 void processAttributes(const HTMLToken::AttributeList& attributes)
149 ASSERT(isMainThread());
150 if (!m_tagImpl)
151 return;
152 for (const HTMLToken::Attribute& htmlTokenAttribute : attributes) {
153 AtomicString attributeName(htmlTokenAttribute.name);
154 String attributeValue = StringImpl::create8BitIfPossible(htmlTokenAttribute.value);
155 processAttribute(attributeName, attributeValue);
159 void processAttributes(const Vector<CompactHTMLToken::Attribute>& attributes)
161 if (!m_tagImpl)
162 return;
163 for (const CompactHTMLToken::Attribute& htmlTokenAttribute : attributes)
164 processAttribute(htmlTokenAttribute.name, htmlTokenAttribute.value);
167 void handlePictureSourceURL(PictureData& pictureData)
169 if (match(m_tagImpl, sourceTag) && m_matchedMediaAttribute && pictureData.sourceURL.isEmpty()) {
170 pictureData.sourceURL = m_srcsetImageCandidate.toString();
171 pictureData.sourceSizeSet = m_sourceSizeSet;
172 pictureData.sourceSize = m_sourceSize;
173 pictureData.picked = true;
174 } else if (match(m_tagImpl, imgTag) && !pictureData.sourceURL.isEmpty()) {
175 setUrlToLoad(pictureData.sourceURL, AllowURLReplacement);
179 PassOwnPtr<PreloadRequest> createPreloadRequest(const KURL& predictedBaseURL, const SegmentedString& source, const ClientHintsPreferences& clientHintsPreferences, const PictureData& pictureData, const ReferrerPolicy documentReferrerPolicy)
181 PreloadRequest::RequestType requestType = PreloadRequest::RequestTypePreload;
182 if (shouldPreconnect())
183 requestType = PreloadRequest::RequestTypePreconnect;
184 else if (!shouldPreload() || !m_matchedMediaAttribute)
185 return nullptr;
187 TextPosition position = TextPosition(source.currentLine(), source.currentColumn());
188 FetchRequest::ResourceWidth resourceWidth;
189 float sourceSize = m_sourceSize;
190 bool sourceSizeSet = m_sourceSizeSet;
191 if (pictureData.picked) {
192 sourceSizeSet = pictureData.sourceSizeSet;
193 sourceSize = pictureData.sourceSize;
195 if (sourceSizeSet) {
196 resourceWidth.width = sourceSize;
197 resourceWidth.isSet = true;
200 // The element's 'referrerpolicy' attribute (if present) takes precedence over the document's referrer policy.
201 ReferrerPolicy referrerPolicy = (m_referrerPolicy != ReferrerPolicyDefault && RuntimeEnabledFeatures::referrerPolicyAttributeEnabled()) ? m_referrerPolicy : documentReferrerPolicy;
202 OwnPtr<PreloadRequest> request = PreloadRequest::create(initiatorFor(m_tagImpl), position, m_urlToLoad, predictedBaseURL, resourceType(), referrerPolicy, resourceWidth, clientHintsPreferences, requestType);
203 if (isCORSEnabled())
204 request->setCrossOriginEnabled(allowStoredCredentials());
205 request->setCharset(charset());
206 request->setDefer(m_defer);
207 return request.release();
210 private:
211 template<typename NameType>
212 void processScriptAttribute(const NameType& attributeName, const String& attributeValue)
214 // FIXME - Don't set crossorigin multiple times.
215 if (match(attributeName, srcAttr))
216 setUrlToLoad(attributeValue, DisallowURLReplacement);
217 else if (match(attributeName, crossoriginAttr))
218 setCrossOriginAllowed(attributeValue);
219 else if (match(attributeName, asyncAttr))
220 setDefer(FetchRequest::LazyLoad);
221 else if (match(attributeName, deferAttr))
222 setDefer(FetchRequest::LazyLoad);
225 template<typename NameType>
226 void processImgAttribute(const NameType& attributeName, const String& attributeValue)
228 if (match(attributeName, srcAttr) && m_imgSrcUrl.isNull()) {
229 m_imgSrcUrl = attributeValue;
230 setUrlToLoad(bestFitSourceForImageAttributes(m_mediaValues->devicePixelRatio(), m_sourceSize, attributeValue, m_srcsetImageCandidate), AllowURLReplacement);
231 } else if (match(attributeName, crossoriginAttr)) {
232 setCrossOriginAllowed(attributeValue);
233 } else if (match(attributeName, srcsetAttr) && m_srcsetImageCandidate.isEmpty()) {
234 m_srcsetAttributeValue = attributeValue;
235 m_srcsetImageCandidate = bestFitSourceForSrcsetAttribute(m_mediaValues->devicePixelRatio(), m_sourceSize, attributeValue);
236 setUrlToLoad(bestFitSourceForImageAttributes(m_mediaValues->devicePixelRatio(), m_sourceSize, m_imgSrcUrl, m_srcsetImageCandidate), AllowURLReplacement);
237 } else if (match(attributeName, sizesAttr) && !m_sourceSizeSet) {
238 m_sourceSize = SizesAttributeParser(m_mediaValues, attributeValue).length();
239 m_sourceSizeSet = true;
240 if (!m_srcsetImageCandidate.isEmpty()) {
241 m_srcsetImageCandidate = bestFitSourceForSrcsetAttribute(m_mediaValues->devicePixelRatio(), m_sourceSize, m_srcsetAttributeValue);
242 setUrlToLoad(bestFitSourceForImageAttributes(m_mediaValues->devicePixelRatio(), m_sourceSize, m_imgSrcUrl, m_srcsetImageCandidate), AllowURLReplacement);
244 } else if (!m_referrerPolicySet && RuntimeEnabledFeatures::referrerPolicyAttributeEnabled() && match(attributeName, referrerpolicyAttr) && !attributeValue.isNull()) {
245 m_referrerPolicySet = true;
246 SecurityPolicy::referrerPolicyFromString(attributeValue, &m_referrerPolicy);
250 template<typename NameType>
251 void processLinkAttribute(const NameType& attributeName, const String& attributeValue)
253 // FIXME - Don't set rel/media/crossorigin multiple times.
254 if (match(attributeName, hrefAttr)) {
255 setUrlToLoad(attributeValue, DisallowURLReplacement);
256 } else if (match(attributeName, relAttr)) {
257 LinkRelAttribute rel(attributeValue);
258 m_linkIsStyleSheet = rel.isStyleSheet() && !rel.isAlternate() && rel.iconType() == InvalidIcon && !rel.isDNSPrefetch();
259 m_linkIsPreconnect = rel.isPreconnect();
260 m_linkIsImport = rel.isImport();
261 } else if (match(attributeName, mediaAttr)) {
262 m_matchedMediaAttribute = mediaAttributeMatches(*m_mediaValues, attributeValue);
263 } else if (match(attributeName, crossoriginAttr)) {
264 setCrossOriginAllowed(attributeValue);
268 template<typename NameType>
269 void processInputAttribute(const NameType& attributeName, const String& attributeValue)
271 // FIXME - Don't set type multiple times.
272 if (match(attributeName, srcAttr))
273 setUrlToLoad(attributeValue, DisallowURLReplacement);
274 else if (match(attributeName, typeAttr))
275 m_inputIsImage = equalIgnoringCase(attributeValue, InputTypeNames::image);
278 template<typename NameType>
279 void processSourceAttribute(const NameType& attributeName, const String& attributeValue)
281 if (match(attributeName, srcsetAttr) && m_srcsetImageCandidate.isEmpty()) {
282 m_srcsetAttributeValue = attributeValue;
283 m_srcsetImageCandidate = bestFitSourceForSrcsetAttribute(m_mediaValues->devicePixelRatio(), m_sourceSize, attributeValue);
284 } else if (match(attributeName, sizesAttr) && !m_sourceSizeSet) {
285 m_sourceSize = SizesAttributeParser(m_mediaValues, attributeValue).length();
286 m_sourceSizeSet = true;
287 if (!m_srcsetImageCandidate.isEmpty()) {
288 m_srcsetImageCandidate = bestFitSourceForSrcsetAttribute(m_mediaValues->devicePixelRatio(), m_sourceSize, m_srcsetAttributeValue);
290 } else if (match(attributeName, mediaAttr)) {
291 // FIXME - Don't match media multiple times.
292 m_matchedMediaAttribute = mediaAttributeMatches(*m_mediaValues, attributeValue);
296 template<typename NameType>
297 void processVideoAttribute(const NameType& attributeName, const String& attributeValue)
299 if (match(attributeName, posterAttr))
300 setUrlToLoad(attributeValue, DisallowURLReplacement);
303 template<typename NameType>
304 void processAttribute(const NameType& attributeName, const String& attributeValue)
306 if (match(attributeName, charsetAttr))
307 m_charset = attributeValue;
309 if (match(m_tagImpl, scriptTag))
310 processScriptAttribute(attributeName, attributeValue);
311 else if (match(m_tagImpl, imgTag))
312 processImgAttribute(attributeName, attributeValue);
313 else if (match(m_tagImpl, linkTag))
314 processLinkAttribute(attributeName, attributeValue);
315 else if (match(m_tagImpl, inputTag))
316 processInputAttribute(attributeName, attributeValue);
317 else if (match(m_tagImpl, sourceTag))
318 processSourceAttribute(attributeName, attributeValue);
319 else if (match(m_tagImpl, videoTag))
320 processVideoAttribute(attributeName, attributeValue);
323 void setUrlToLoad(const String& value, URLReplacement replacement)
325 // We only respect the first src/href, per HTML5:
326 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#attribute-name-state
327 if (replacement == DisallowURLReplacement && !m_urlToLoad.isEmpty())
328 return;
329 String url = stripLeadingAndTrailingHTMLSpaces(value);
330 if (url.isEmpty())
331 return;
332 m_urlToLoad = url;
335 const String& charset() const
337 // FIXME: Its not clear that this if is needed, the loader probably ignores charset for image requests anyway.
338 if (match(m_tagImpl, imgTag) || match(m_tagImpl, videoTag))
339 return emptyString();
340 return m_charset;
343 Resource::Type resourceType() const
345 if (match(m_tagImpl, scriptTag))
346 return Resource::Script;
347 if (match(m_tagImpl, imgTag) || match(m_tagImpl, videoTag) || (match(m_tagImpl, inputTag) && m_inputIsImage))
348 return Resource::Image;
349 if (match(m_tagImpl, linkTag) && m_linkIsStyleSheet)
350 return Resource::CSSStyleSheet;
351 if (m_linkIsPreconnect)
352 return Resource::Raw;
353 if (match(m_tagImpl, linkTag) && m_linkIsImport)
354 return Resource::ImportResource;
355 ASSERT_NOT_REACHED();
356 return Resource::Raw;
359 bool shouldPreconnect() const
361 return match(m_tagImpl, linkTag) && m_linkIsPreconnect && !m_urlToLoad.isEmpty();
364 bool shouldPreload() const
366 if (m_urlToLoad.isEmpty())
367 return false;
368 if (match(m_tagImpl, linkTag) && !m_linkIsStyleSheet && !m_linkIsImport)
369 return false;
370 if (match(m_tagImpl, inputTag) && !m_inputIsImage)
371 return false;
372 return true;
375 bool isCORSEnabled() const
377 return m_isCORSEnabled;
380 StoredCredentials allowStoredCredentials() const
382 return m_allowCredentials;
385 void setCrossOriginAllowed(const String& corsSetting)
387 m_isCORSEnabled = true;
388 if (!corsSetting.isNull() && equalIgnoringCase(stripLeadingAndTrailingHTMLSpaces(corsSetting), "use-credentials"))
389 m_allowCredentials = AllowStoredCredentials;
390 else
391 m_allowCredentials = DoNotAllowStoredCredentials;
394 void setDefer(FetchRequest::DeferOption defer)
396 m_defer = defer;
399 bool defer() const
401 return m_defer;
404 const StringImpl* m_tagImpl;
405 String m_urlToLoad;
406 ImageCandidate m_srcsetImageCandidate;
407 String m_charset;
408 bool m_linkIsStyleSheet;
409 bool m_linkIsPreconnect;
410 bool m_linkIsImport;
411 bool m_matchedMediaAttribute;
412 bool m_inputIsImage;
413 String m_imgSrcUrl;
414 String m_srcsetAttributeValue;
415 float m_sourceSize;
416 bool m_sourceSizeSet;
417 bool m_isCORSEnabled;
418 FetchRequest::DeferOption m_defer;
419 StoredCredentials m_allowCredentials;
420 RefPtrWillBeMember<MediaValues> m_mediaValues;
421 bool m_referrerPolicySet;
422 ReferrerPolicy m_referrerPolicy;
425 TokenPreloadScanner::TokenPreloadScanner(const KURL& documentURL, PassOwnPtr<CachedDocumentParameters> documentParameters)
426 : m_documentURL(documentURL)
427 , m_inStyle(false)
428 , m_inPicture(false)
429 , m_isAppCacheEnabled(false)
430 , m_isCSPEnabled(false)
431 , m_templateCount(0)
432 , m_documentParameters(documentParameters)
434 ASSERT(m_documentParameters.get());
435 ASSERT(m_documentParameters->mediaValues.get());
436 ASSERT(m_documentParameters->mediaValues->isCached());
439 TokenPreloadScanner::~TokenPreloadScanner()
443 TokenPreloadScannerCheckpoint TokenPreloadScanner::createCheckpoint()
445 TokenPreloadScannerCheckpoint checkpoint = m_checkpoints.size();
446 m_checkpoints.append(Checkpoint(m_predictedBaseElementURL, m_inStyle, m_isAppCacheEnabled, m_isCSPEnabled, m_templateCount));
447 return checkpoint;
450 void TokenPreloadScanner::rewindTo(TokenPreloadScannerCheckpoint checkpointIndex)
452 ASSERT(checkpointIndex < m_checkpoints.size()); // If this ASSERT fires, checkpointIndex is invalid.
453 const Checkpoint& checkpoint = m_checkpoints[checkpointIndex];
454 m_predictedBaseElementURL = checkpoint.predictedBaseElementURL;
455 m_inStyle = checkpoint.inStyle;
456 m_isAppCacheEnabled = checkpoint.isAppCacheEnabled;
457 m_isCSPEnabled = checkpoint.isCSPEnabled;
458 m_templateCount = checkpoint.templateCount;
459 m_cssScanner.reset();
460 m_checkpoints.clear();
463 void TokenPreloadScanner::scan(const HTMLToken& token, const SegmentedString& source, PreloadRequestStream& requests)
465 scanCommon(token, source, requests);
468 void TokenPreloadScanner::scan(const CompactHTMLToken& token, const SegmentedString& source, PreloadRequestStream& requests)
470 scanCommon(token, source, requests);
473 static void handleMetaViewport(const String& attributeValue, CachedDocumentParameters* documentParameters)
475 if (!documentParameters->viewportMetaEnabled)
476 return;
477 ViewportDescription description(ViewportDescription::ViewportMeta);
478 HTMLMetaElement::getViewportDescriptionFromContentAttribute(attributeValue, description, nullptr, documentParameters->viewportMetaZeroValuesQuirk);
479 FloatSize initialViewport(documentParameters->mediaValues->deviceWidth(), documentParameters->mediaValues->deviceHeight());
480 PageScaleConstraints constraints = description.resolve(initialViewport, documentParameters->defaultViewportMinWidth);
481 MediaValuesCached* cachedMediaValues = static_cast<MediaValuesCached*>(documentParameters->mediaValues.get());
482 cachedMediaValues->setViewportHeight(constraints.layoutSize.height());
483 cachedMediaValues->setViewportWidth(constraints.layoutSize.width());
486 static void handleMetaReferrer(const String& attributeValue, CachedDocumentParameters* documentParameters, CSSPreloadScanner* cssScanner)
488 if (attributeValue.isEmpty() || attributeValue.isNull() || !SecurityPolicy::referrerPolicyFromString(attributeValue, &documentParameters->referrerPolicy)) {
489 documentParameters->referrerPolicy = ReferrerPolicyDefault;
491 cssScanner->setReferrerPolicy(documentParameters->referrerPolicy);
494 template <typename Token>
495 static void handleMetaNameAttribute(const Token& token, CachedDocumentParameters* documentParameters, CSSPreloadScanner* cssScanner)
497 const typename Token::Attribute* nameAttribute = token.getAttributeItem(nameAttr);
498 if (!nameAttribute)
499 return;
501 String nameAttributeValue(nameAttribute->value);
502 const typename Token::Attribute* contentAttribute = token.getAttributeItem(contentAttr);
503 if (!contentAttribute)
504 return;
506 String contentAttributeValue(contentAttribute->value);
507 if (equalIgnoringCase(nameAttributeValue, "viewport")) {
508 handleMetaViewport(contentAttributeValue, documentParameters);
509 return;
512 if (equalIgnoringCase(nameAttributeValue, "referrer")) {
513 handleMetaReferrer(contentAttributeValue, documentParameters, cssScanner);
517 template <typename Token>
518 void TokenPreloadScanner::scanCommon(const Token& token, const SegmentedString& source, PreloadRequestStream& requests)
520 if (!m_documentParameters->doHtmlPreloadScanning)
521 return;
523 // Disable preload for documents with AppCache.
524 if (m_isAppCacheEnabled)
525 return;
527 // http://crbug.com/434230 Disable preload for documents with CSP <meta> tags
528 if (m_isCSPEnabled)
529 return;
531 switch (token.type()) {
532 case HTMLToken::Character: {
533 if (!m_inStyle)
534 return;
535 m_cssScanner.scan(token.data(), source, requests);
536 return;
538 case HTMLToken::EndTag: {
539 const StringImpl* tagImpl = tagImplFor(token.data());
540 if (match(tagImpl, templateTag)) {
541 if (m_templateCount)
542 --m_templateCount;
543 return;
545 if (match(tagImpl, styleTag)) {
546 if (m_inStyle)
547 m_cssScanner.reset();
548 m_inStyle = false;
549 return;
551 if (match(tagImpl, pictureTag))
552 m_inPicture = false;
553 return;
555 case HTMLToken::StartTag: {
556 if (m_templateCount)
557 return;
558 const StringImpl* tagImpl = tagImplFor(token.data());
559 if (match(tagImpl, templateTag)) {
560 ++m_templateCount;
561 return;
563 if (match(tagImpl, styleTag)) {
564 m_inStyle = true;
565 return;
567 if (match(tagImpl, baseTag)) {
568 // The first <base> element is the one that wins.
569 if (!m_predictedBaseElementURL.isEmpty())
570 return;
571 updatePredictedBaseURL(token);
572 return;
574 if (match(tagImpl, htmlTag) && token.getAttributeItem(manifestAttr)) {
575 m_isAppCacheEnabled = true;
576 return;
578 if (match(tagImpl, metaTag)) {
579 const typename Token::Attribute* equivAttribute = token.getAttributeItem(http_equivAttr);
580 if (equivAttribute) {
581 String equivAttributeValue(equivAttribute->value);
582 if (equalIgnoringCase(equivAttributeValue, "content-security-policy")) {
583 m_isCSPEnabled = true;
584 } else if (equalIgnoringCase(equivAttributeValue, "accept-ch")) {
585 const typename Token::Attribute* contentAttribute = token.getAttributeItem(contentAttr);
586 if (contentAttribute)
587 m_clientHintsPreferences.updateFromAcceptClientHintsHeader(String(contentAttribute->value), nullptr);
589 return;
592 handleMetaNameAttribute(token, m_documentParameters.get(), &m_cssScanner);
595 if (match(tagImpl, pictureTag)) {
596 m_inPicture = true;
597 m_pictureData = PictureData();
598 return;
601 StartTagScanner scanner(tagImpl, m_documentParameters->mediaValues);
602 scanner.processAttributes(token.attributes());
603 if (m_inPicture)
604 scanner.handlePictureSourceURL(m_pictureData);
605 OwnPtr<PreloadRequest> request = scanner.createPreloadRequest(m_predictedBaseElementURL, source, m_clientHintsPreferences, m_pictureData, m_documentParameters->referrerPolicy);
606 if (request)
607 requests.append(request.release());
608 return;
610 default: {
611 return;
616 template<typename Token>
617 void TokenPreloadScanner::updatePredictedBaseURL(const Token& token)
619 ASSERT(m_predictedBaseElementURL.isEmpty());
620 if (const typename Token::Attribute* hrefAttribute = token.getAttributeItem(hrefAttr)) {
621 KURL url(m_documentURL, stripLeadingAndTrailingHTMLSpaces(hrefAttribute->value));
622 m_predictedBaseElementURL = url.isValid() ? url.copy() : KURL();
626 HTMLPreloadScanner::HTMLPreloadScanner(const HTMLParserOptions& options, const KURL& documentURL, PassOwnPtr<CachedDocumentParameters> documentParameters)
627 : m_scanner(documentURL, documentParameters)
628 , m_tokenizer(HTMLTokenizer::create(options))
632 HTMLPreloadScanner::~HTMLPreloadScanner()
636 void HTMLPreloadScanner::appendToEnd(const SegmentedString& source)
638 m_source.append(source);
641 void HTMLPreloadScanner::scan(ResourcePreloader* preloader, const KURL& startingBaseElementURL)
643 ASSERT(isMainThread()); // HTMLTokenizer::updateStateFor only works on the main thread.
645 TRACE_EVENT1("blink", "HTMLPreloadScanner::scan", "source_length", m_source.length());
647 // When we start scanning, our best prediction of the baseElementURL is the real one!
648 if (!startingBaseElementURL.isEmpty())
649 m_scanner.setPredictedBaseElementURL(startingBaseElementURL);
651 PreloadRequestStream requests;
653 while (m_tokenizer->nextToken(m_source, m_token)) {
654 if (m_token.type() == HTMLToken::StartTag)
655 m_tokenizer->updateStateFor(attemptStaticStringCreation(m_token.name(), Likely8Bit));
656 m_scanner.scan(m_token, m_source, requests);
657 m_token.clear();
660 preloader->takeAndPreload(requests);
663 CachedDocumentParameters::CachedDocumentParameters(Document* document, PassRefPtrWillBeRawPtr<MediaValues> givenMediaValues)
665 ASSERT(isMainThread());
666 ASSERT(document);
667 doHtmlPreloadScanning = !document->settings() || document->settings()->doHtmlPreloadScanning();
668 if (givenMediaValues)
669 mediaValues = givenMediaValues;
670 else
671 mediaValues = MediaValuesCached::create(*document);
672 ASSERT(mediaValues->isSafeToSendToAnotherThread());
673 defaultViewportMinWidth = document->viewportDefaultMinWidth();
674 viewportMetaZeroValuesQuirk = document->settings() && document->settings()->viewportMetaZeroValuesQuirk();
675 viewportMetaEnabled = document->settings() && document->settings()->viewportMetaEnabled();
676 referrerPolicy = ReferrerPolicyDefault;