Skip to content

Commit

Permalink
[MQ4] Simplify dynamic media feature handling with legacy evaluator
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=247263
<rdar://problem/101758954>

Reviewed by Alan Baradlay.

This will make it simpler to implement dynamic features in the new evaluator.

* Source/WebCore/css/LegacyMediaQueryEvaluator.cpp:
(WebCore::LegacyMediaQueryEvaluator::evaluate const):

Remove the dynamic feature related parameters from evaluate().

(WebCore::mediaQueryDynamicDependencies):

Add a function to find dynamic dependencies separately from evaluation.

(WebCore::LegacyMediaQueryEvaluator::evaluateForChanges const): Deleted.
* Source/WebCore/css/LegacyMediaQueryEvaluator.h:
(WebCore::MediaQueryDynamicResults::append): Deleted.
(WebCore::MediaQueryDynamicResults::isEmpty const): Deleted.

Replace with a simple OptionSet.

* Source/WebCore/css/parser/SizesAttributeParser.cpp:
(WebCore::SizesAttributeParser::SizesAttributeParser):
(WebCore::SizesAttributeParser::mediaConditionMatches):
(WebCore::SizesAttributeParser::parse):

Collect the dynamic queries and the results to a member.

* Source/WebCore/css/parser/SizesAttributeParser.h:
(WebCore::SizesAttributeParser::dynamicMediaConditionResults const):
* Source/WebCore/css/query/MediaQuery.h:
* Source/WebCore/html/HTMLImageElement.cpp:
(WebCore::HTMLImageElement::bestFitSourceFromPictureElement):
(WebCore::HTMLImageElement::evaluateDynamicMediaQueryDependencies):

Evaluate whole queries instead of individual expressions.

(WebCore::HTMLImageElement::selectImageSource):
* Source/WebCore/html/HTMLImageElement.h:
* Source/WebCore/style/RuleSetBuilder.cpp:
(WebCore::Style::RuleSetBuilder::updateDynamicMediaQueries):
(WebCore::Style::RuleSetBuilder::MediaQueryCollector::pushAndEvaluate):
* Source/WebCore/style/RuleSetBuilder.h:
* Source/WebCore/style/UserAgentStyle.cpp:
(WebCore::Style::UserAgentStyle::addToDefaultStyle):

Canonical link: https://commits.webkit.org/256170@main
  • Loading branch information
anttijk committed Oct 31, 2022
1 parent 3e4d37c commit 6848a1d
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 88 deletions.
65 changes: 27 additions & 38 deletions Source/WebCore/css/LegacyMediaQueryEvaluator.cpp
Expand Up @@ -143,7 +143,7 @@ static bool applyRestrictor(LegacyMediaQuery::Restrictor r, bool value)
return r == LegacyMediaQuery::Not ? !value : value;
}

bool LegacyMediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, MediaQueryDynamicResults* dynamicResults, Mode mode) const
bool LegacyMediaQueryEvaluator::evaluate(const MediaQuerySet& querySet) const
{
LOG_WITH_STREAM(MediaQueries, stream << "LegacyMediaQueryEvaluator::evaluate on " << (m_document ? m_document->url().string() : emptyString()));

Expand All @@ -163,37 +163,14 @@ bool LegacyMediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, MediaQue

if (mediaTypeMatch(query.mediaType())) {
auto& expressions = query.expressions();

// Iterate through expressions, stop if any of them eval to false (AND semantics).
bool isDynamic = false;
size_t j = 0;
for (; j < expressions.size(); ++j) {
bool expressionResult = evaluate(expressions[j]);
if (dynamicResults) {
if (expressions[j].isViewportDependent()) {
isDynamic = true;
dynamicResults->viewport.append({ expressions[j], expressionResult });
}
if (isAppearanceDependent(expressions[j].mediaFeature())) {
isDynamic = true;
dynamicResults->appearance.append({ expressions[j], expressionResult });
}
if (isAccessibilitySettingsDependent(expressions[j].mediaFeature())) {
isDynamic = true;
dynamicResults->accessibilitySettings.append({ expressions[j], expressionResult });
}
}
if (mode == Mode::AlwaysMatchDynamic && isDynamic)
continue;

if (!expressionResult)
break;
}

if (mode == Mode::AlwaysMatchDynamic && isDynamic) {
result = true;
continue;
}

// Assume true if we are at the end of the list, otherwise assume false.
result = applyRestrictor(query.restrictor(), expressions.size() == j);
} else
Expand All @@ -204,19 +181,6 @@ bool LegacyMediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, MediaQue
return result;
}

bool LegacyMediaQueryEvaluator::evaluateForChanges(const MediaQueryDynamicResults& dynamicResults) const
{
auto hasChanges = [&](auto& dynamicResultsVector) {
for (auto& dynamicResult : dynamicResultsVector) {
if (evaluate(dynamicResult.expression) != dynamicResult.result)
return true;
}
return false;
};

return hasChanges(dynamicResults.viewport) || hasChanges(dynamicResults.appearance) || hasChanges(dynamicResults.accessibilitySettings);
}

template<typename T, typename U> bool compareValue(T a, U b, MediaFeaturePrefix op)
{
switch (op) {
Expand Down Expand Up @@ -974,4 +938,29 @@ bool LegacyMediaQueryEvaluator::mediaAttributeMatches(Document& document, const
return LegacyMediaQueryEvaluator { "screen"_s, document, &document.renderView()->style() }.evaluate(mediaQueries.get());
}


OptionSet<MQ::MediaQueryDynamicDependency> mediaQueryDynamicDependencies(const MediaQuerySet& queries, const LegacyMediaQueryEvaluator& evaluator)
{
OptionSet<MQ::MediaQueryDynamicDependency> result;

for (auto& query : queries.queryVector()) {
if (query.ignored() || (!query.expressions().size() && query.mediaType().isEmpty()))
continue;

if (!evaluator.mediaTypeMatch(query.mediaType()))
continue;

for (auto& expression : query.expressions()) {
if (expression.isViewportDependent())
result.add(MQ::MediaQueryDynamicDependency::Viewport);
if (isAppearanceDependent(expression.mediaFeature()))
result.add(MQ::MediaQueryDynamicDependency::Appearance);
if (isAccessibilitySettingsDependent(expression.mediaFeature()))
result.add(MQ::MediaQueryDynamicDependency::Accessibility);
}
}

return result;
}

} // WebCore
23 changes: 5 additions & 18 deletions Source/WebCore/css/LegacyMediaQueryEvaluator.h
Expand Up @@ -28,6 +28,7 @@
#pragma once

#include "IntSize.h"
#include "MediaQuery.h"
#include "MediaQueryExpression.h"
#include <wtf/WeakPtr.h>

Expand All @@ -39,24 +40,10 @@ class MediaQuerySet;
class RenderStyle;

struct MediaQueryResult {
MediaQueryExpression expression;
Ref<const MediaQuerySet> query;
bool result;
};

struct MediaQueryDynamicResults {
Vector<MediaQueryResult> viewport;
Vector<MediaQueryResult> appearance;
Vector<MediaQueryResult> accessibilitySettings;

void append(const MediaQueryDynamicResults& other)
{
viewport.appendVector(other.viewport);
appearance.appendVector(other.appearance);
accessibilitySettings.appendVector(other.accessibilitySettings);
}
bool isEmpty() const { return viewport.isEmpty() && appearance.isEmpty() && accessibilitySettings.isEmpty(); }
};

using MediaQueryViewportState = std::tuple<IntSize, float, bool>;

MediaQueryViewportState mediaQueryViewportStateForDocument(const Document&);
Expand All @@ -82,10 +69,8 @@ class LegacyMediaQueryEvaluator {

// Evaluates media query subexpression, ie "and (media-feature: value)" part.
bool evaluate(const MediaQueryExpression&) const;
bool evaluateForChanges(const MediaQueryDynamicResults&) const;

enum class Mode { Normal, AlwaysMatchDynamic };
WEBCORE_EXPORT bool evaluate(const MediaQuerySet&, MediaQueryDynamicResults* = nullptr, Mode = Mode::Normal) const;
WEBCORE_EXPORT bool evaluate(const MediaQuerySet&) const;

static bool mediaAttributeMatches(Document&, const String& attributeValue);

Expand All @@ -96,4 +81,6 @@ class LegacyMediaQueryEvaluator {
bool m_fallbackResult { false };
};

OptionSet<MQ::MediaQueryDynamicDependency> mediaQueryDynamicDependencies(const MediaQuerySet&, const LegacyMediaQueryEvaluator&);

} // namespace
14 changes: 9 additions & 5 deletions Source/WebCore/css/parser/SizesAttributeParser.cpp
Expand Up @@ -67,9 +67,8 @@ float SizesAttributeParser::computeLength(double value, CSSUnitType type, const
return clampTo<float>(CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, type, value));
}

SizesAttributeParser::SizesAttributeParser(const String& attribute, const Document& document, MediaQueryDynamicResults* mediaQueryDynamicResults)
SizesAttributeParser::SizesAttributeParser(const String& attribute, const Document& document)
: m_document(document)
, m_mediaQueryDynamicResults(mediaQueryDynamicResults)
{
m_isValid = parse(CSSTokenizer(attribute).tokenRange());
}
Expand Down Expand Up @@ -112,7 +111,7 @@ bool SizesAttributeParser::mediaConditionMatches(const MediaQuerySet& mediaCondi
if (!renderer)
return false;
auto& style = renderer->style();
return LegacyMediaQueryEvaluator { "screen"_s, m_document, &style }.evaluate(mediaCondition, m_mediaQueryDynamicResults);
return LegacyMediaQueryEvaluator { "screen"_s, m_document, &style }.evaluate(mediaCondition);
}

bool SizesAttributeParser::parse(CSSParserTokenRange range)
Expand All @@ -134,8 +133,13 @@ bool SizesAttributeParser::parse(CSSParserTokenRange range)
float length;
if (!calculateLengthInPixels(range.makeSubRange(lengthTokenStart, lengthTokenEnd), length))
continue;
RefPtr<MediaQuerySet> mediaCondition = LegacyMediaQueryParser::parseMediaCondition(range.makeSubRange(mediaConditionStart, lengthTokenStart), MediaQueryParserContext(m_document));
if (!mediaCondition || !mediaConditionMatches(*mediaCondition))
auto mediaCondition = LegacyMediaQueryParser::parseMediaCondition(range.makeSubRange(mediaConditionStart, lengthTokenStart), MediaQueryParserContext(m_document));
if (!mediaCondition)
continue;
bool matches = mediaConditionMatches(*mediaCondition);
if (!mediaQueryDynamicDependencies(*mediaCondition, { "screen"_s, false }).isEmpty())
m_dynamicMediaConditionResults.append({ *mediaCondition, matches });
if (!matches)
continue;
m_length = length;
m_lengthWasSet = true;
Expand Down
12 changes: 6 additions & 6 deletions Source/WebCore/css/parser/SizesAttributeParser.h
Expand Up @@ -31,24 +31,25 @@

#include "CSSParserTokenRange.h"
#include "CSSPrimitiveValue.h"
#include "LegacyMediaQueryEvaluator.h"
#include <wtf/text/WTFString.h>

namespace WebCore {

class CSSValue;
class Document;
class MediaQuerySet;
struct MediaQueryDynamicResults;


class SizesAttributeParser {
public:
SizesAttributeParser(const String&, const Document&, MediaQueryDynamicResults* = nullptr);
SizesAttributeParser(const String&, const Document&);

float length();

static float defaultLength(const Document&);
static float computeLength(double value, CSSUnitType, const Document&);

auto& dynamicMediaConditionResults() const { return m_dynamicMediaConditionResults; }

private:
bool parse(CSSParserTokenRange);
float effectiveSize();
Expand All @@ -57,8 +58,7 @@ class SizesAttributeParser {
unsigned effectiveSizeDefaultValue();

const Document& m_document;
RefPtr<MediaQuerySet> m_mediaCondition;
MediaQueryDynamicResults* m_mediaQueryDynamicResults { nullptr };
Vector<MediaQueryResult> m_dynamicMediaConditionResults;
float m_length { 0 };
bool m_lengthWasSet { false };
bool m_isValid { false };
Expand Down
6 changes: 6 additions & 0 deletions Source/WebCore/css/query/MediaQuery.h
Expand Up @@ -39,5 +39,11 @@ struct MediaQuery {

using MediaQueryList = Vector<MediaQuery>;

enum class MediaQueryDynamicDependency : uint8_t {
Viewport = 1 << 0,
Appearance = 1 << 1,
Accessibility = 1 << 2,
};

}
}
30 changes: 23 additions & 7 deletions Source/WebCore/html/HTMLImageElement.cpp
Expand Up @@ -229,11 +229,18 @@ ImageCandidate HTMLImageElement::bestFitSourceFromPictureElement()
auto* queries = source.parsedMediaAttribute(document());
LOG(MediaQueries, "HTMLImageElement %p bestFitSourceFromPictureElement evaluating media queries", this);

auto evaluation = !queries || evaluator.evaluate(*queries, &m_mediaQueryDynamicResults);
if (!evaluation)
auto result = !queries || evaluator.evaluate(*queries);

if (queries && !mediaQueryDynamicDependencies(*queries, evaluator).isEmpty())
m_dynamicMediaQueryResults.append({ *queries, result });

if (!result)
continue;

SizesAttributeParser sizesParser(source.attributeWithoutSynchronization(sizesAttr).string(), document(), &m_mediaQueryDynamicResults);
SizesAttributeParser sizesParser(source.attributeWithoutSynchronization(sizesAttr).string(), document());

m_dynamicMediaQueryResults.appendVector(sizesParser.dynamicMediaConditionResults());

auto sourceSize = sizesParser.length();

candidate = bestFitSourceForImageAttributes(document().deviceScaleFactor(), nullAtom(), srcset, sourceSize);
Expand All @@ -251,30 +258,39 @@ void HTMLImageElement::evaluateDynamicMediaQueryDependencies()
RefPtr documentElement = document().documentElement();
LegacyMediaQueryEvaluator evaluator { document().printing() ? "print"_s : "screen"_s, document(), documentElement ? documentElement->computedStyle() : nullptr };

if (!evaluator.evaluateForChanges(m_mediaQueryDynamicResults))
auto hasChanges = [&] {
for (auto& condition : m_dynamicMediaQueryResults) {
if (condition.result != evaluator.evaluate(condition.query.get()))
return true;
}
return false;
}();

if (!hasChanges)
return;

selectImageSource(RelevantMutation::No);
}

void HTMLImageElement::selectImageSource(RelevantMutation relevantMutation)
{
m_mediaQueryDynamicResults = { };
m_dynamicMediaQueryResults = { };
document().removeDynamicMediaQueryDependentImage(*this);

// First look for the best fit source from our <picture> parent if we have one.
ImageCandidate candidate = bestFitSourceFromPictureElement();
if (candidate.isEmpty()) {
setSourceElement(nullptr);
// If we don't have a <picture> or didn't find a source, then we use our own attributes.
SizesAttributeParser sizesParser(attributeWithoutSynchronization(sizesAttr).string(), document(), &m_mediaQueryDynamicResults);
SizesAttributeParser sizesParser(attributeWithoutSynchronization(sizesAttr).string(), document());
m_dynamicMediaQueryResults.appendVector(sizesParser.dynamicMediaConditionResults());
auto sourceSize = sizesParser.length();
candidate = bestFitSourceForImageAttributes(document().deviceScaleFactor(), attributeWithoutSynchronization(srcAttr), attributeWithoutSynchronization(srcsetAttr), sourceSize);
}
setBestFitURLAndDPRFromImageCandidate(candidate);
m_imageLoader->updateFromElementIgnoringPreviousError(relevantMutation);

if (!m_mediaQueryDynamicResults.isEmpty())
if (!m_dynamicMediaQueryResults.isEmpty())
document().addDynamicMediaQueryDependentImage(*this);
}

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/html/HTMLImageElement.h
Expand Up @@ -236,7 +236,7 @@ class HTMLImageElement : public HTMLElement, public FormNamedItem, public Active
WeakPtr<HTMLPictureElement, WeakPtrImplWithEventTargetData> m_pictureElement;
// The source element that was selected to provide the source URL.
WeakPtr<HTMLSourceElement, WeakPtrImplWithEventTargetData> m_sourceElement;
MediaQueryDynamicResults m_mediaQueryDynamicResults;
Vector<MediaQueryResult> m_dynamicMediaQueryResults;

#if ENABLE(ATTACHMENT_ELEMENT)
String m_pendingClonedAttachmentID;
Expand Down
18 changes: 8 additions & 10 deletions Source/WebCore/style/RuleSetBuilder.cpp
Expand Up @@ -349,7 +349,7 @@ void RuleSetBuilder::addMutatingRulesToResolver()

void RuleSetBuilder::updateDynamicMediaQueries()
{
if (m_mediaQueryCollector.hasViewportDependentMediaQueries)
if (m_mediaQueryCollector.allDynamicDependencies.contains(MQ::MediaQueryDynamicDependency::Viewport))
m_ruleSet->m_hasViewportDependentMediaQueries = true;

if (!m_mediaQueryCollector.dynamicMediaQueryRules.isEmpty()) {
Expand All @@ -368,19 +368,17 @@ bool RuleSetBuilder::MediaQueryCollector::pushAndEvaluate(const MediaQuerySet* s
if (!set)
return true;

// Only evaluate static expressions that require style rebuild.
MediaQueryDynamicResults dynamicResults;
auto mode = collectDynamic ? LegacyMediaQueryEvaluator::Mode::AlwaysMatchDynamic : LegacyMediaQueryEvaluator::Mode::Normal;
auto dynamicDependencies = mediaQueryDynamicDependencies(*set, evaluator);

bool result = evaluator.evaluate(*set, &dynamicResults, mode);
allDynamicDependencies.add(dynamicDependencies);

if (!dynamicResults.viewport.isEmpty())
hasViewportDependentMediaQueries = true;

if (!dynamicResults.isEmpty())
if (!dynamicDependencies.isEmpty()) {
dynamicContextStack.append({ *set });
if (collectDynamic)
return true;
}

return result;
return evaluator.evaluate(*set);
}

void RuleSetBuilder::MediaQueryCollector::pop(const MediaQuerySet* set)
Expand Down
3 changes: 2 additions & 1 deletion Source/WebCore/style/RuleSetBuilder.h
Expand Up @@ -21,6 +21,7 @@

#pragma once

#include "MediaQuery.h"
#include "RuleSet.h"

namespace WebCore {
Expand Down Expand Up @@ -63,7 +64,7 @@ class RuleSetBuilder {
Vector<DynamicContext> dynamicContextStack { };

Vector<RuleSet::DynamicMediaQueryRules> dynamicMediaQueryRules { };
bool hasViewportDependentMediaQueries { false };
OptionSet<MQ::MediaQueryDynamicDependency> allDynamicDependencies { };

bool pushAndEvaluate(const MediaQuerySet*);
void pop(const MediaQuerySet*);
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/style/UserAgentStyle.cpp
Expand Up @@ -129,9 +129,9 @@ void UserAgentStyle::addToDefaultStyle(StyleSheetContents& sheet)
continue;
auto& mediaRule = downcast<StyleRuleMedia>(*rule);
auto& mediaQuery = mediaRule.mediaQueries();
if (screenEval().evaluate(mediaQuery, nullptr))
if (screenEval().evaluate(mediaQuery))
continue;
if (printEval().evaluate(mediaQuery, nullptr))
if (printEval().evaluate(mediaQuery))
continue;
mediaQueryStyleSheet->parserAppendRule(mediaRule.copy());
}
Expand Down

0 comments on commit 6848a1d

Please sign in to comment.