Commit 612b2729 authored by evliu's avatar evliu Committed by Commit Bot

Add WebVTT support for inline styling - style resolution components

Bug: 724598
Change-Id: If27937335d5761a227e62218fe47cdf30bf20c3a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1863357
Commit-Queue: Evan Liu <evliu@google.com>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#706990}
parent 50bdbe40
......@@ -133,6 +133,7 @@ void ElementRuleCollector::CollectMatchingRulesForList(
&context_.GetElement(), SelectorChecker::kVisitedMatchEnabled);
context.scope = match_request.scope;
context.pseudo_id = pseudo_style_request_.pseudo_id;
context.is_from_vtt = match_request.is_from_vtt;
unsigned rejected = 0;
unsigned fast_rejected = 0;
......
......@@ -73,7 +73,8 @@ CSSValueList* ConsumeFontFaceUnicodeRange(CSSParserTokenRange& range) {
CSSValue* ConsumeFontFaceSrcURI(CSSParserTokenRange& range,
const CSSParserContext& context) {
String url =
css_property_parser_helpers::ConsumeUrlAsStringView(range).ToString();
css_property_parser_helpers::ConsumeUrlAsStringView(range, &context)
.ToString();
if (url.IsNull())
return nullptr;
CSSFontFaceSrcValue* uri_value(CSSFontFaceSrcValue::Create(
......
......@@ -668,13 +668,14 @@ CSSStringValue* ConsumeString(CSSParserTokenRange& range) {
range.ConsumeIncludingWhitespace().Value().ToString());
}
StringView ConsumeUrlAsStringView(CSSParserTokenRange& range) {
StringView ConsumeUrlAsStringView(CSSParserTokenRange& range,
const CSSParserContext* context) {
StringView url;
const CSSParserToken& token = range.Peek();
if (token.GetType() == kUrlToken) {
range.ConsumeIncludingWhitespace();
return token.Value();
}
if (token.FunctionId() == CSSValueID::kUrl) {
url = token.Value();
} else if (token.FunctionId() == CSSValueID::kUrl) {
CSSParserTokenRange url_range = range;
CSSParserTokenRange url_args = url_range.ConsumeBlock();
const CSSParserToken& next = url_args.ConsumeIncludingWhitespace();
......@@ -683,15 +684,27 @@ StringView ConsumeUrlAsStringView(CSSParserTokenRange& range) {
DCHECK_EQ(next.GetType(), kStringToken);
range = url_range;
range.ConsumeWhitespace();
return next.Value();
url = next.Value();
}
return StringView();
// Invalidate the URL if only data URLs are allowed and the protocol is not
// data.
if (!url.IsNull() &&
context->ResourceFetchRestriction() ==
ResourceFetchRestriction::kOnlyDataUrls &&
!ProtocolIs(url.ToString(), "data")) {
// The StringView must be instantiated with an empty string otherwise the
// URL will incorrectly be identified as null. The resource should behave as
// if it failed to load.
url = StringView("");
}
return url;
}
CSSURIValue* ConsumeUrl(CSSParserTokenRange& range,
const CSSParserContext* context) {
StringView url = ConsumeUrlAsStringView(range);
StringView url = ConsumeUrlAsStringView(range, context);
if (url.IsNull())
return nullptr;
String url_string = url.ToString();
......@@ -1758,7 +1771,8 @@ static CSSValue* ConsumeImageSet(CSSParserTokenRange& range,
CSSParserTokenRange args = ConsumeFunction(range_copy);
auto* image_set = MakeGarbageCollected<CSSImageSetValue>(context->Mode());
do {
AtomicString url_value = ConsumeUrlAsStringView(args).ToAtomicString();
AtomicString url_value =
ConsumeUrlAsStringView(args, context).ToAtomicString();
if (url_value.IsNull())
return nullptr;
......@@ -1801,7 +1815,7 @@ static bool IsGeneratedImage(CSSValueID id) {
CSSValue* ConsumeImage(CSSParserTokenRange& range,
const CSSParserContext* context,
ConsumeGeneratedImagePolicy generated_image) {
AtomicString uri = ConsumeUrlAsStringView(range).ToAtomicString();
AtomicString uri = ConsumeUrlAsStringView(range, context).ToAtomicString();
if (!uri.IsNull())
return CreateCSSImageValueWithReferrer(uri, context);
if (range.Peek().GetType() == kFunctionToken) {
......
......@@ -94,7 +94,8 @@ CSSIdentifierValue* ConsumeIdent(CSSParserTokenRange&);
CSSCustomIdentValue* ConsumeCustomIdent(CSSParserTokenRange&,
const CSSParserContext&);
CSSStringValue* ConsumeString(CSSParserTokenRange&);
StringView ConsumeUrlAsStringView(CSSParserTokenRange&);
StringView ConsumeUrlAsStringView(CSSParserTokenRange&,
const CSSParserContext*);
cssvalue::CSSURIValue* ConsumeUrl(CSSParserTokenRange&,
const CSSParserContext*);
......
......@@ -41,11 +41,13 @@ class MatchRequest {
MatchRequest(RuleSet* rule_set,
const ContainerNode* scope = nullptr,
const CSSStyleSheet* css_sheet = nullptr,
unsigned style_sheet_index = 0)
unsigned style_sheet_index = 0,
bool is_from_vtt = false)
: rule_set(rule_set),
scope(scope),
style_sheet(css_sheet),
style_sheet_index(style_sheet_index) {
style_sheet_index(style_sheet_index),
is_from_vtt(is_from_vtt) {
// Now that we're about to read from the RuleSet, we're done adding more
// rules to the set and we should make sure it's compacted.
rule_set->CompactRulesIfNeeded();
......@@ -55,6 +57,7 @@ class MatchRequest {
Member<const ContainerNode> scope;
Member<const CSSStyleSheet> style_sheet;
const unsigned style_sheet_index;
bool is_from_vtt;
};
} // namespace blink
......
......@@ -58,7 +58,6 @@
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/element_rule_collector.h"
#include "third_party/blink/renderer/core/css/font_face.h"
#include "third_party/blink/renderer/core/css/media_query_evaluator.h"
#include "third_party/blink/renderer/core/css/page_rule_collector.h"
#include "third_party/blink/renderer/core/css/part_names.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
......@@ -66,7 +65,6 @@
#include "third_party/blink/renderer/core/css/resolver/css_variable_animator.h"
#include "third_party/blink/renderer/core/css/resolver/css_variable_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/match_result.h"
#include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/selector_filter_parent_scope.h"
#include "third_party/blink/renderer/core/css/resolver/style_adjuster.h"
......@@ -90,6 +88,9 @@
#include "third_party/blink/renderer/core/html/custom/custom_element_definition.h"
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "third_party/blink/renderer/core/html/html_slot_element.h"
#include "third_party/blink/renderer/core/html/track/text_track.h"
#include "third_party/blink/renderer/core/html/track/vtt/vtt_cue.h"
#include "third_party/blink/renderer/core/html/track/vtt/vtt_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/media_type_names.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
......@@ -312,6 +313,38 @@ static void MatchSlottedRules(const Element& element,
}
}
const static TextTrack* GetTextTrackFromElement(const Element& element) {
if (auto* vtt_element = DynamicTo<VTTElement>(element))
return vtt_element->GetTrack();
if (auto* vtt_cue_background_box = DynamicTo<VTTCueBackgroundBox>(element))
return vtt_cue_background_box->GetTrack();
return nullptr;
}
static void MatchVTTRules(const Element& element,
ElementRuleCollector& collector) {
const TextTrack* text_track = GetTextTrackFromElement(element);
if (!text_track)
return;
const HeapVector<Member<CSSStyleSheet>>& styles =
text_track->GetCSSStyleSheets();
if (!styles.IsEmpty()) {
int style_sheet_index = 0;
collector.ClearMatchedRules();
for (CSSStyleSheet* style : styles) {
RuleSet* rule_set =
element.GetDocument().GetStyleEngine().RuleSetForSheet(*style);
if (rule_set) {
collector.CollectMatchingRules(
MatchRequest(rule_set, nullptr /* scope */, style,
style_sheet_index, true /* is_from_webvtt */));
style_sheet_index++;
}
}
collector.SortAndTransferMatchedRules();
}
}
// Matches rules from the element's scope. The selectors may cross shadow
// boundaries during matching, like for :host-context.
static void MatchElementScopeRules(const Element& element,
......@@ -324,6 +357,7 @@ static void MatchElementScopeRules(const Element& element,
collector.SortAndTransferMatchedRules();
}
MatchVTTRules(element, collector);
if (element.IsStyledElement() && element.InlineStyle() &&
!collector.IsCollectingForPseudoElement()) {
// Inline style is immutable as long as there is no CSSOM wrapper.
......
......@@ -88,8 +88,10 @@ static bool MatchesListBoxPseudoClass(const Element& element) {
return html_select_element && !html_select_element->UsesMenuList();
}
static bool MatchesTagName(const Element& element,
const QualifiedName& tag_q_name) {
static bool MatchesTagName(
const Element& element,
const QualifiedName& tag_q_name,
const SelectorChecker::SelectorCheckingContext& context) {
if (tag_q_name == AnyQName())
return true;
const AtomicString& local_name = tag_q_name.LocalName();
......@@ -105,8 +107,12 @@ static bool MatchesTagName(const Element& element,
return false;
}
const AtomicString& namespace_uri = tag_q_name.NamespaceURI();
return namespace_uri == g_star_atom ||
namespace_uri == element.namespaceURI();
if (namespace_uri == g_star_atom)
return true;
// VTT style sheets should apply to a hypothetical document with no namespace
if (context.is_from_vtt)
return namespace_uri.IsEmpty();
return namespace_uri == element.namespaceURI();
}
static Element* ParentElement(
......@@ -204,6 +210,26 @@ static bool IsLastOfType(Element& element, const QualifiedName& type) {
return !ElementTraversal::NextSibling(element, HasTagName(type));
}
bool SelectorChecker::Match(const SelectorCheckingContext& context,
MatchResult& result) const {
DCHECK(context.selector);
if (context.is_from_vtt)
return MatchVTTBlockSelector(context, result);
return MatchSelector(context, result) == kSelectorMatches;
}
bool SelectorChecker::MatchVTTBlockSelector(
const SelectorCheckingContext& context,
MatchResult& result) const {
DCHECK(context.selector);
if (context.selector->IsLastInTagHistory() ||
context.selector->TagHistory()->Specificity() != 0) {
return false;
}
return MatchSelector(context, result) == kSelectorMatches;
}
// Recursive check of selectors and combinators
// It can return 4 different values:
// * SelectorMatches - the selector matches the element e
......@@ -330,6 +356,14 @@ SelectorChecker::MatchStatus SelectorChecker::MatchForRelation(
next_context.previous_element = context.element;
next_context.pseudo_id = kPseudoIdNone;
// Rules that come from a WebVTT STYLE block apply to a hypothetical
// document with a single empty element with no explicit name, no namespace,
// no attribute, no classes, no IDs, and unknown primary language that acts
// as the originating element for the cue pseudo-elements. This element
// must not be generally selectable.
if (context.is_from_vtt && relation != CSSSelector::kShadowPseudo)
return kSelectorFailsCompletely;
switch (relation) {
case CSSSelector::kShadowDeepAsDescendant:
Deprecation::CountDeprecation(context.element->GetDocument(),
......@@ -690,7 +724,7 @@ bool SelectorChecker::CheckOne(const SelectorCheckingContext& context,
switch (selector.Match()) {
case CSSSelector::kTag:
return MatchesTagName(element, selector.TagQName());
return MatchesTagName(element, selector.TagQName(), context);
case CSSSelector::kClass:
return element.HasClass() &&
element.ClassNames().Contains(selector.Value());
......@@ -896,7 +930,8 @@ bool SelectorChecker::CheckPseudoClass(const SelectorCheckingContext& context,
for (sub_context.selector = selector.SelectorList()->First();
sub_context.selector; sub_context.selector = CSSSelectorList::Next(
*sub_context.selector)) {
if (Match(sub_context))
MatchResult sub_result;
if (MatchSelector(sub_context, sub_result) == kSelectorMatches)
return true;
}
} break;
......@@ -1150,7 +1185,8 @@ bool SelectorChecker::CheckPseudoElement(const SelectorCheckingContext& context,
for (sub_context.selector = selector.SelectorList()->First();
sub_context.selector; sub_context.selector = CSSSelectorList::Next(
*sub_context.selector)) {
if (Match(sub_context))
MatchResult sub_result;
if (MatchSelector(sub_context, sub_result) == kSelectorMatches)
return true;
}
return false;
......
......@@ -115,7 +115,8 @@ class SelectorChecker {
in_rightmost_compound(true),
has_scrollbar_pseudo(false),
has_selection_pseudo(false),
treat_shadow_host_as_normal_scope(false) {}
treat_shadow_host_as_normal_scope(false),
is_from_vtt(false) {}
const CSSSelector* selector;
Member<Element> element;
......@@ -128,6 +129,7 @@ class SelectorChecker {
bool has_scrollbar_pseudo;
bool has_selection_pseudo;
bool treat_shadow_host_as_normal_scope;
bool is_from_vtt;
};
struct MatchResult {
......@@ -140,11 +142,7 @@ class SelectorChecker {
unsigned specificity;
};
bool Match(const SelectorCheckingContext& context,
MatchResult& result) const {
DCHECK(context.selector);
return MatchSelector(context, result) == kSelectorMatches;
}
bool Match(const SelectorCheckingContext& context, MatchResult& result) const;
bool Match(const SelectorCheckingContext& context) const {
MatchResult ignore_result;
......@@ -196,6 +194,8 @@ class SelectorChecker {
MatchStatus MatchForPseudoShadow(const SelectorCheckingContext&,
const ContainerNode*,
MatchResult&) const;
bool MatchVTTBlockSelector(const SelectorCheckingContext& context,
MatchResult& result) const;
bool CheckPseudoClass(const SelectorCheckingContext&, MatchResult&) const;
bool CheckPseudoElement(const SelectorCheckingContext&, MatchResult&) const;
bool CheckScrollbarPseudoClass(const SelectorCheckingContext&,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment