summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/parser/HTMLPreloadScanner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/parser/HTMLPreloadScanner.cpp')
-rw-r--r--Source/WebCore/html/parser/HTMLPreloadScanner.cpp295
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);
}
}