diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/html/parser/HTMLPreloadScanner.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/html/parser/HTMLPreloadScanner.cpp')
-rw-r--r-- | Source/WebCore/html/parser/HTMLPreloadScanner.cpp | 295 |
1 files changed, 190 insertions, 105 deletions
diff --git a/Source/WebCore/html/parser/HTMLPreloadScanner.cpp b/Source/WebCore/html/parser/HTMLPreloadScanner.cpp index 51c81e189..2022bd2bf 100644 --- a/Source/WebCore/html/parser/HTMLPreloadScanner.cpp +++ b/Source/WebCore/html/parser/HTMLPreloadScanner.cpp @@ -22,7 +22,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -30,9 +30,14 @@ #include "HTMLNames.h" #include "HTMLParserIdioms.h" +#include "HTMLSrcsetParser.h" #include "HTMLTokenizer.h" #include "InputTypeNames.h" #include "LinkRelAttribute.h" +#include "MediaList.h" +#include "MediaQueryEvaluator.h" +#include "RenderView.h" +#include "SizesAttributeParser.h" #include <wtf/MainThread.h> namespace WebCore { @@ -56,29 +61,38 @@ TokenPreloadScanner::TagId TokenPreloadScanner::tagIdFor(const HTMLToken::DataVe return TagId::Base; if (tagName == templateTag) return TagId::Template; + if (tagName == metaTag) + return TagId::Meta; + if (tagName == pictureTag) + return TagId::Picture; + if (tagName == sourceTag) + return TagId::Source; return TagId::Unknown; } String TokenPreloadScanner::initiatorFor(TagId tagId) { switch (tagId) { + case TagId::Source: case TagId::Img: - return "img"; + return ASCIILiteral("img"); case TagId::Input: - return "input"; + return ASCIILiteral("input"); case TagId::Link: - return "link"; + return ASCIILiteral("link"); case TagId::Script: - return "script"; + return ASCIILiteral("script"); case TagId::Unknown: case TagId::Style: case TagId::Base: case TagId::Template: + case TagId::Meta: + case TagId::Picture: ASSERT_NOT_REACHED(); - return "unknown"; + return ASCIILiteral("unknown"); } ASSERT_NOT_REACHED(); - return "unknown"; + return ASCIILiteral("unknown"); } class TokenPreloadScanner::StartTagScanner { @@ -86,27 +100,43 @@ public: explicit StartTagScanner(TagId tagId, float deviceScaleFactor = 1.0) : m_tagId(tagId) , m_linkIsStyleSheet(false) + , m_metaIsViewport(false) , m_inputIsImage(false) , m_deviceScaleFactor(deviceScaleFactor) { } - void processAttributes(const HTMLToken::AttributeList& attributes) + void processAttributes(const HTMLToken::AttributeList& attributes, Document& document, Vector<bool>& pictureState) { ASSERT(isMainThread()); if (m_tagId >= TagId::Unknown) return; - for (HTMLToken::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter) { - AtomicString attributeName(iter->name); - String attributeValue = StringImpl::create8BitIfPossible(iter->value); - processAttribute(attributeName, attributeValue); + + for (auto& attribute : attributes) { + AtomicString attributeName(attribute.name); + String attributeValue = StringImpl::create8BitIfPossible(attribute.value); + processAttribute(attributeName, attributeValue, document, pictureState); } - - // Resolve between src and srcSet if we have them. - if (!m_srcSetAttribute.isEmpty()) { - String srcMatchingScale = bestFitSourceForImageAttributes(m_deviceScaleFactor, m_urlToLoad, m_srcSetAttribute); - setUrlToLoad(srcMatchingScale, true); + + if (m_tagId == TagId::Source && !pictureState.isEmpty() && !pictureState.last() && m_mediaMatched && !m_srcSetAttribute.isEmpty()) { + + auto sourceSize = SizesAttributeParser(m_sizesAttribute, document).length(); + ImageCandidate imageCandidate = bestFitSourceForImageAttributes(m_deviceScaleFactor, m_urlToLoad, m_srcSetAttribute, sourceSize); + if (!imageCandidate.isEmpty()) { + pictureState.last() = true; + setUrlToLoad(imageCandidate.string.toString(), true); + } + } + + // Resolve between src and srcSet if we have them and the tag is img. + if (m_tagId == TagId::Img && !m_srcSetAttribute.isEmpty()) { + auto sourceSize = SizesAttributeParser(m_sizesAttribute, document).length(); + ImageCandidate imageCandidate = bestFitSourceForImageAttributes(m_deviceScaleFactor, m_urlToLoad, m_srcSetAttribute, sourceSize); + setUrlToLoad(imageCandidate.string.toString(), true); } + + if (m_metaIsViewport && !m_metaContent.isNull()) + document.processViewport(m_metaContent, ViewportArguments::ViewportMeta); } std::unique_ptr<PreloadRequest> createPreloadRequest(const URL& predictedBaseURL) @@ -114,9 +144,13 @@ public: if (!shouldPreload()) return nullptr; - auto request = std::make_unique<PreloadRequest>(initiatorFor(m_tagId), m_urlToLoad, predictedBaseURL, resourceType(), m_mediaAttribute); + auto request = std::make_unique<PreloadRequest>(initiatorFor(m_tagId), m_urlToLoad, predictedBaseURL, resourceType(), m_mediaAttribute, m_moduleScript); + request->setCrossOriginMode(m_crossOriginMode); + request->setNonce(m_nonceAttribute); - request->setCrossOriginModeAllowsCookies(crossOriginModeAllowsCookies()); + // According to the spec, the module tag ignores the "charset" attribute as the same to the worker's + // importScript. But WebKit supports the "charset" for importScript intentionally. So to be consistent, + // even for the module tags, we handle the "charset" attribute. request->setCharset(charset()); return request; } @@ -128,38 +162,100 @@ public: } private: - template<typename NameType> - void processAttribute(const NameType& attributeName, const String& attributeValue) + void processImageAndScriptAttribute(const AtomicString& attributeName, const String& attributeValue) { - if (match(attributeName, charsetAttr)) + if (match(attributeName, srcAttr)) + setUrlToLoad(attributeValue); + else if (match(attributeName, crossoriginAttr)) + m_crossOriginMode = stripLeadingAndTrailingHTMLSpaces(attributeValue); + else if (match(attributeName, charsetAttr)) m_charset = attributeValue; + } - if (m_tagId == TagId::Script || m_tagId == TagId::Img) { - if (match(attributeName, srcAttr)) - setUrlToLoad(attributeValue); - else if (match(attributeName, srcsetAttr)) + void processAttribute(const AtomicString& attributeName, const String& attributeValue, Document& document, const Vector<bool>& pictureState) + { + bool inPicture = !pictureState.isEmpty(); + bool alreadyMatchedSource = inPicture && pictureState.last(); + + switch (m_tagId) { + case TagId::Img: + if (inPicture && alreadyMatchedSource) + break; + if (match(attributeName, srcsetAttr) && m_srcSetAttribute.isNull()) { m_srcSetAttribute = attributeValue; - else if (match(attributeName, crossoriginAttr) && !attributeValue.isNull()) - m_crossOriginMode = stripLeadingAndTrailingHTMLSpaces(attributeValue); - } else if (m_tagId == TagId::Link) { + break; + } + if (match(attributeName, sizesAttr) && m_sizesAttribute.isNull()) { + m_sizesAttribute = attributeValue; + break; + } + processImageAndScriptAttribute(attributeName, attributeValue); + break; + case TagId::Source: + if (inPicture && alreadyMatchedSource) + break; + if (match(attributeName, srcsetAttr) && m_srcSetAttribute.isNull()) { + m_srcSetAttribute = attributeValue; + break; + } + if (match(attributeName, sizesAttr) && m_sizesAttribute.isNull()) { + m_sizesAttribute = attributeValue; + break; + } + if (match(attributeName, mediaAttr) && m_mediaAttribute.isNull()) { + m_mediaAttribute = attributeValue; + auto mediaSet = MediaQuerySet::create(attributeValue); + auto* documentElement = document.documentElement(); + m_mediaMatched = MediaQueryEvaluator { document.printing() ? "print" : "screen", document, documentElement ? documentElement->computedStyle() : nullptr }.evaluate(mediaSet.get()); + } + break; + case TagId::Script: + if (match(attributeName, typeAttr)) { + m_moduleScript = equalLettersIgnoringASCIICase(attributeValue, "module") ? PreloadRequest::ModuleScript::Yes : PreloadRequest::ModuleScript::No; + break; + } else if (match(attributeName, nonceAttr)) + m_nonceAttribute = attributeValue; + processImageAndScriptAttribute(attributeName, attributeValue); + break; + case TagId::Link: if (match(attributeName, hrefAttr)) setUrlToLoad(attributeValue); else if (match(attributeName, relAttr)) m_linkIsStyleSheet = relAttributeIsStyleSheet(attributeValue); else if (match(attributeName, mediaAttr)) m_mediaAttribute = attributeValue; - } else if (m_tagId == TagId::Input) { + else if (match(attributeName, charsetAttr)) + m_charset = attributeValue; + else if (match(attributeName, crossoriginAttr)) + m_crossOriginMode = stripLeadingAndTrailingHTMLSpaces(attributeValue); + else if (match(attributeName, nonceAttr)) + m_nonceAttribute = attributeValue; + break; + case TagId::Input: if (match(attributeName, srcAttr)) setUrlToLoad(attributeValue); else if (match(attributeName, typeAttr)) - m_inputIsImage = equalIgnoringCase(attributeValue, InputTypeNames::image()); + m_inputIsImage = equalLettersIgnoringASCIICase(attributeValue, "image"); + break; + case TagId::Meta: + if (match(attributeName, contentAttr)) + m_metaContent = attributeValue; + else if (match(attributeName, nameAttr)) + m_metaIsViewport = equalLettersIgnoringASCIICase(attributeValue, "viewport"); + break; + case TagId::Base: + case TagId::Style: + case TagId::Template: + case TagId::Picture: + case TagId::Unknown: + break; } } static bool relAttributeIsStyleSheet(const String& attributeValue) { - LinkRelAttribute rel(attributeValue); - return rel.m_isStyleSheet && !rel.m_isAlternate && rel.m_iconType == InvalidIcon && !rel.m_isDNSPrefetch; + LinkRelAttribute parsedAttribute { attributeValue }; + return parsedAttribute.isStyleSheet && !parsedAttribute.isAlternate && !parsedAttribute.iconType && !parsedAttribute.isDNSPrefetch; } void setUrlToLoad(const String& value, bool allowReplacement = false) @@ -176,20 +272,30 @@ private: const String& charset() const { - // FIXME: Its not clear that this if is needed, the loader probably ignores charset for image requests anyway. - if (m_tagId == TagId::Img) - return emptyString(); return m_charset; } CachedResource::Type resourceType() const { - if (m_tagId == TagId::Script) + switch (m_tagId) { + case TagId::Script: return CachedResource::Script; - if (m_tagId == TagId::Img || (m_tagId == TagId::Input && m_inputIsImage)) + case TagId::Img: + case TagId::Input: + case TagId::Source: + ASSERT(m_tagId != TagId::Input || m_inputIsImage); return CachedResource::ImageResource; - if (m_tagId == TagId::Link && m_linkIsStyleSheet) + case TagId::Link: + ASSERT(m_linkIsStyleSheet); return CachedResource::CSSStyleSheet; + case TagId::Meta: + case TagId::Unknown: + case TagId::Style: + case TagId::Base: + case TagId::Template: + case TagId::Picture: + break; + } ASSERT_NOT_REACHED(); return CachedResource::RawResource; } @@ -199,6 +305,9 @@ private: if (m_urlToLoad.isEmpty()) return false; + if (protocolIs(m_urlToLoad, "data") || protocolIs(m_urlToLoad, "about")) + return false; + if (m_tagId == TagId::Link && !m_linkIsStyleSheet) return false; @@ -208,98 +317,63 @@ private: return true; } - bool crossOriginModeAllowsCookies() - { - return m_crossOriginMode.isNull() || equalIgnoringCase(m_crossOriginMode, "use-credentials"); - } - TagId m_tagId; String m_urlToLoad; String m_srcSetAttribute; + String m_sizesAttribute; + bool m_mediaMatched { true }; String m_charset; String m_crossOriginMode; bool m_linkIsStyleSheet; String m_mediaAttribute; + String m_nonceAttribute; + String m_metaContent; + bool m_metaIsViewport; bool m_inputIsImage; float m_deviceScaleFactor; + PreloadRequest::ModuleScript m_moduleScript { PreloadRequest::ModuleScript::No }; }; TokenPreloadScanner::TokenPreloadScanner(const URL& documentURL, float deviceScaleFactor) : m_documentURL(documentURL) - , m_inStyle(false) , m_deviceScaleFactor(deviceScaleFactor) -#if ENABLE(TEMPLATE_ELEMENT) - , m_templateCount(0) -#endif { } -TokenPreloadScanner::~TokenPreloadScanner() -{ -} - -TokenPreloadScannerCheckpoint TokenPreloadScanner::createCheckpoint() -{ - TokenPreloadScannerCheckpoint checkpoint = m_checkpoints.size(); - m_checkpoints.append(Checkpoint(m_predictedBaseElementURL, m_inStyle -#if ENABLE(TEMPLATE_ELEMENT) - , m_templateCount -#endif - )); - return checkpoint; -} - -void TokenPreloadScanner::rewindTo(TokenPreloadScannerCheckpoint checkpointIndex) -{ - ASSERT(checkpointIndex < m_checkpoints.size()); // If this ASSERT fires, checkpointIndex is invalid. - const Checkpoint& checkpoint = m_checkpoints[checkpointIndex]; - m_predictedBaseElementURL = checkpoint.predictedBaseElementURL; - m_inStyle = checkpoint.inStyle; -#if ENABLE(TEMPLATE_ELEMENT) - m_templateCount = checkpoint.templateCount; -#endif - m_cssScanner.reset(); - m_checkpoints.clear(); -} - -void TokenPreloadScanner::scan(const HTMLToken& token, Vector<std::unique_ptr<PreloadRequest>>& requests) +void TokenPreloadScanner::scan(const HTMLToken& token, Vector<std::unique_ptr<PreloadRequest>>& requests, Document& document) { switch (token.type()) { case HTMLToken::Character: if (!m_inStyle) return; - m_cssScanner.scan(token.data(), requests); + m_cssScanner.scan(token.characters(), requests); return; case HTMLToken::EndTag: { - TagId tagId = tagIdFor(token.data()); -#if ENABLE(TEMPLATE_ELEMENT) + TagId tagId = tagIdFor(token.name()); if (tagId == TagId::Template) { if (m_templateCount) --m_templateCount; return; } -#endif if (tagId == TagId::Style) { if (m_inStyle) m_cssScanner.reset(); m_inStyle = false; - } + } else if (tagId == TagId::Picture && !m_pictureSourceState.isEmpty()) + m_pictureSourceState.removeLast(); + return; } case HTMLToken::StartTag: { -#if ENABLE(TEMPLATE_ELEMENT) if (m_templateCount) return; -#endif - TagId tagId = tagIdFor(token.data()); -#if ENABLE(TEMPLATE_ELEMENT) + TagId tagId = tagIdFor(token.name()); if (tagId == TagId::Template) { ++m_templateCount; return; } -#endif if (tagId == TagId::Style) { m_inStyle = true; return; @@ -311,11 +385,15 @@ void TokenPreloadScanner::scan(const HTMLToken& token, Vector<std::unique_ptr<Pr updatePredictedBaseURL(token); return; } + if (tagId == TagId::Picture) { + m_pictureSourceState.append(false); + return; + } StartTagScanner scanner(tagId, m_deviceScaleFactor); - scanner.processAttributes(token.attributes()); + scanner.processAttributes(token.attributes(), document, m_pictureSourceState); if (auto request = scanner.createPreloadRequest(m_predictedBaseElementURL)) - requests.append(std::move(request)); + requests.append(WTFMove(request)); return; } @@ -324,21 +402,16 @@ void TokenPreloadScanner::scan(const HTMLToken& token, Vector<std::unique_ptr<Pr } } -template<typename Token> -void TokenPreloadScanner::updatePredictedBaseURL(const Token& token) +void TokenPreloadScanner::updatePredictedBaseURL(const HTMLToken& token) { ASSERT(m_predictedBaseElementURL.isEmpty()); - if (const typename Token::Attribute* hrefAttribute = token.getAttributeItem(hrefAttr)) - m_predictedBaseElementURL = URL(m_documentURL, stripLeadingAndTrailingHTMLSpaces(hrefAttribute->value)).copy(); + if (auto* hrefAttribute = findAttribute(token.attributes(), hrefAttr.localName().string())) + m_predictedBaseElementURL = URL(m_documentURL, stripLeadingAndTrailingHTMLSpaces(StringImpl::create8BitIfPossible(hrefAttribute->value))).isolatedCopy(); } HTMLPreloadScanner::HTMLPreloadScanner(const HTMLParserOptions& options, const URL& documentURL, float deviceScaleFactor) : m_scanner(documentURL, deviceScaleFactor) - , m_tokenizer(std::make_unique<HTMLTokenizer>(options)) -{ -} - -HTMLPreloadScanner::~HTMLPreloadScanner() + , m_tokenizer(options) { } @@ -347,24 +420,36 @@ void HTMLPreloadScanner::appendToEnd(const SegmentedString& source) m_source.append(source); } -void HTMLPreloadScanner::scan(HTMLResourcePreloader* preloader, const URL& startingBaseElementURL) +void HTMLPreloadScanner::scan(HTMLResourcePreloader& preloader, Document& document) { ASSERT(isMainThread()); // HTMLTokenizer::updateStateFor only works on the main thread. + const URL& startingBaseElementURL = document.baseElementURL(); + // When we start scanning, our best prediction of the baseElementURL is the real one! if (!startingBaseElementURL.isEmpty()) m_scanner.setPredictedBaseElementURL(startingBaseElementURL); PreloadRequestStream requests; - while (m_tokenizer->nextToken(m_source, m_token)) { - if (m_token.type() == HTMLToken::StartTag) - m_tokenizer->updateStateFor(AtomicString(m_token.name())); - m_scanner.scan(m_token, requests); - m_token.clear(); + while (auto token = m_tokenizer.nextToken(m_source)) { + if (token->type() == HTMLToken::StartTag) + m_tokenizer.updateStateFor(AtomicString(token->name())); + m_scanner.scan(*token, requests, document); } - preloader->preload(std::move(requests)); + preloader.preload(WTFMove(requests)); +} + +bool testPreloadScannerViewportSupport(Document* document) +{ + ASSERT(document); + HTMLParserOptions options(*document); + HTMLPreloadScanner scanner(options, document->url()); + HTMLResourcePreloader preloader(*document); + scanner.appendToEnd(String("<meta name=viewport content='width=400'>")); + scanner.scan(preloader, *document); + return (document->viewportArguments().width == 400); } } |