Commit 87eae6df authored by Rodney Ding's avatar Rodney Ding Committed by Commit Bot

Fix CSS.supports for origin trial enabled property/value

- currently when CSS.supports is called it does not pass in the
  ExecutionContext to the css parser context, hence it does not support
  Origin Trial enabled properties/values.

- This changeset added plumbing to pass in the top level document to
  CSSParserContext which we extracted from ExecutionContext.

- Repurposed one of the ParseValue overload and change all its callsite
  to pass in ExecutionContext, they were previously passing in
  ExecutionContext->GetSecureContext()

- CSSParserContext used in support parsing are constructed using the
  document if the execution context belongs to one. We then override
  the parser mode to kHTMLStandardMode as quirks mode values must not
  be supported in CSS.supports method according to
  https://quirks.spec.whatwg.org/#css
  This is also in line with current behaviours before document
  is added to the context.

- Added virtual test that checks for CSS.supports(condition) and
  CSS.supports(property, value)

Bug: 1134237
Change-Id: I4c1b3cd2707f3ce5495ee298a909e3f42946fc09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2477014Reviewed-by: default avatarJeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarAnders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: default avatarJason Chase <chasej@chromium.org>
Commit-Queue: Rodney Ding <rodneyding@google.com>
Cr-Commit-Position: refs/heads/master@{#821909}
parent aa46d57a
...@@ -67,14 +67,13 @@ bool DOMWindowCSS::supports(const ExecutionContext* execution_context, ...@@ -67,14 +67,13 @@ bool DOMWindowCSS::supports(const ExecutionContext* execution_context,
auto* dummy_style = auto* dummy_style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
return CSSParser::ParseValue(dummy_style, unresolved_property, value, false, return CSSParser::ParseValue(dummy_style, unresolved_property, value, false,
execution_context->GetSecureContextMode()) execution_context)
.did_parse; .did_parse;
} }
bool DOMWindowCSS::supports(const ExecutionContext* execution_context, bool DOMWindowCSS::supports(const ExecutionContext* execution_context,
const String& condition_text) { const String& condition_text) {
return CSSParser::ParseSupportsCondition( return CSSParser::ParseSupportsCondition(condition_text, execution_context);
condition_text, execution_context->GetSecureContextMode());
} }
String DOMWindowCSS::escape(const String& ident) { String DOMWindowCSS::escape(const String& ident) {
......
...@@ -166,8 +166,7 @@ bool FontFaceSetDocument::ResolveFontStyle(const String& font_string, ...@@ -166,8 +166,7 @@ bool FontFaceSetDocument::ResolveFontStyle(const String& font_string,
// Interpret fontString in the same way as the 'font' attribute of // Interpret fontString in the same way as the 'font' attribute of
// CanvasRenderingContext2D. // CanvasRenderingContext2D.
auto* parsed_style = CSSParser::ParseFont( auto* parsed_style = CSSParser::ParseFont(font_string, GetExecutionContext());
font_string, GetExecutionContext()->GetSecureContextMode());
if (!parsed_style) if (!parsed_style)
return false; return false;
......
...@@ -76,8 +76,7 @@ bool FontFaceSetWorker::ResolveFontStyle(const String& font_string, ...@@ -76,8 +76,7 @@ bool FontFaceSetWorker::ResolveFontStyle(const String& font_string,
// Interpret fontString in the same way as the 'font' attribute of // Interpret fontString in the same way as the 'font' attribute of
// CanvasRenderingContext2D. // CanvasRenderingContext2D.
auto* parsed_style = CSSParser::ParseFont( auto* parsed_style = CSSParser::ParseFont(font_string, GetExecutionContext());
font_string, GetExecutionContext()->GetSecureContextMode());
if (!parsed_style) if (!parsed_style)
return false; return false;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "third_party/blink/renderer/core/css/style_color.h" #include "third_party/blink/renderer/core/css/style_color.h"
#include "third_party/blink/renderer/core/css/style_rule.h" #include "third_party/blink/renderer/core/css/style_rule.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h" #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/heap/heap.h"
...@@ -90,10 +91,12 @@ MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue( ...@@ -90,10 +91,12 @@ MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue(
CSSPropertyID unresolved_property, CSSPropertyID unresolved_property,
const String& string, const String& string,
bool important, bool important,
SecureContextMode secure_context_mode) { const ExecutionContext* execution_context) {
return ParseValue(declaration, unresolved_property, string, important, return ParseValue(
secure_context_mode, declaration, unresolved_property, string, important,
static_cast<StyleSheetContents*>(nullptr)); execution_context ? execution_context->GetSecureContextMode()
: SecureContextMode::kInsecureContext,
static_cast<StyleSheetContents*>(nullptr), execution_context);
} }
MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue( MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue(
...@@ -102,7 +105,8 @@ MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue( ...@@ -102,7 +105,8 @@ MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue(
const String& string, const String& string,
bool important, bool important,
SecureContextMode secure_context_mode, SecureContextMode secure_context_mode,
StyleSheetContents* style_sheet) { StyleSheetContents* style_sheet,
const ExecutionContext* execution_context) {
if (string.IsEmpty()) { if (string.IsEmpty()) {
bool did_parse = false; bool did_parse = false;
bool did_change = false; bool did_change = false;
...@@ -124,6 +128,12 @@ MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue( ...@@ -124,6 +128,12 @@ MutableCSSPropertyValueSet::SetResult CSSParser::ParseValue(
context = context =
MakeGarbageCollected<CSSParserContext>(style_sheet->ParserContext()); MakeGarbageCollected<CSSParserContext>(style_sheet->ParserContext());
context->SetMode(parser_mode); context->SetMode(parser_mode);
} else if (IsA<LocalDOMWindow>(execution_context)) {
// Create parser context using document if it exists so it can check for
// origin trial enabled property/value.
context = MakeGarbageCollected<CSSParserContext>(
*To<LocalDOMWindow>(execution_context)->document());
context->SetMode(parser_mode);
} else { } else {
context = MakeGarbageCollected<CSSParserContext>(parser_mode, context = MakeGarbageCollected<CSSParserContext>(parser_mode,
secure_context_mode); secure_context_mode);
...@@ -211,13 +221,23 @@ StyleRuleKeyframe* CSSParser::ParseKeyframeRule(const CSSParserContext* context, ...@@ -211,13 +221,23 @@ StyleRuleKeyframe* CSSParser::ParseKeyframeRule(const CSSParserContext* context,
return To<StyleRuleKeyframe>(keyframe); return To<StyleRuleKeyframe>(keyframe);
} }
bool CSSParser::ParseSupportsCondition(const String& condition, bool CSSParser::ParseSupportsCondition(
SecureContextMode secure_context_mode) { const String& condition,
const ExecutionContext* execution_context) {
// window.CSS.supports requires to parse as-if it was wrapped in parenthesis. // window.CSS.supports requires to parse as-if it was wrapped in parenthesis.
String wrapped_condition = "(" + condition + ")"; String wrapped_condition = "(" + condition + ")";
CSSTokenizer tokenizer(wrapped_condition); CSSTokenizer tokenizer(wrapped_condition);
CSSParserTokenStream stream(tokenizer); CSSParserTokenStream stream(tokenizer);
CSSParserImpl parser(StrictCSSParserContext(secure_context_mode)); DCHECK(execution_context);
// Create parser context using document so it can check for origin trial
// enabled property/value.
CSSParserContext* context = MakeGarbageCollected<CSSParserContext>(
*To<LocalDOMWindow>(execution_context)->document());
// Override the parser mode interpreted from the document as the spec
// https://quirks.spec.whatwg.org/#css requires quirky values and colors
// must not be supported in CSS.supports() method.
context->SetMode(kHTMLStandardMode);
CSSParserImpl parser(context);
CSSSupportsParser::Result result = CSSSupportsParser::Result result =
CSSSupportsParser::ConsumeSupportsCondition(stream, parser); CSSSupportsParser::ConsumeSupportsCondition(stream, parser);
if (!stream.AtEnd()) if (!stream.AtEnd())
...@@ -293,11 +313,13 @@ CSSPrimitiveValue* CSSParser::ParseLengthPercentage( ...@@ -293,11 +313,13 @@ CSSPrimitiveValue* CSSParser::ParseLengthPercentage(
kValueRangeAll); kValueRangeAll);
} }
MutableCSSPropertyValueSet* CSSParser::ParseFont(const String& string, MutableCSSPropertyValueSet* CSSParser::ParseFont(
SecureContextMode mode) { const String& string,
const ExecutionContext* execution_context) {
auto* set = auto* set =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
ParseValue(set, CSSPropertyID::kFont, string, true /* important */, mode); ParseValue(set, CSSPropertyID::kFont, string, true /* important */,
execution_context);
if (set->IsEmpty()) if (set->IsEmpty())
return nullptr; return nullptr;
const CSSValue* font_size = const CSSValue* font_size =
......
...@@ -18,6 +18,7 @@ class Color; ...@@ -18,6 +18,7 @@ class Color;
class CSSParserObserver; class CSSParserObserver;
class CSSSelectorList; class CSSSelectorList;
class Element; class Element;
class ExecutionContext;
class ImmutableCSSPropertyValueSet; class ImmutableCSSPropertyValueSet;
class StyleRuleBase; class StyleRuleBase;
class StyleRuleKeyframe; class StyleRuleKeyframe;
...@@ -59,14 +60,15 @@ class CORE_EXPORT CSSParser { ...@@ -59,14 +60,15 @@ class CORE_EXPORT CSSParser {
CSSPropertyID unresolved_property, CSSPropertyID unresolved_property,
const String&, const String&,
bool important, bool important,
SecureContextMode); const ExecutionContext* execution_context = nullptr);
static MutableCSSPropertyValueSet::SetResult ParseValue( static MutableCSSPropertyValueSet::SetResult ParseValue(
MutableCSSPropertyValueSet*, MutableCSSPropertyValueSet*,
CSSPropertyID unresolved_property, CSSPropertyID unresolved_property,
const String&, const String&,
bool important, bool important,
SecureContextMode, SecureContextMode,
StyleSheetContents*); StyleSheetContents*,
const ExecutionContext* execution_context = nullptr);
static MutableCSSPropertyValueSet::SetResult ParseValueForCustomProperty( static MutableCSSPropertyValueSet::SetResult ParseValueForCustomProperty(
MutableCSSPropertyValueSet*, MutableCSSPropertyValueSet*,
...@@ -96,7 +98,7 @@ class CORE_EXPORT CSSParser { ...@@ -96,7 +98,7 @@ class CORE_EXPORT CSSParser {
static StyleRuleKeyframe* ParseKeyframeRule(const CSSParserContext*, static StyleRuleKeyframe* ParseKeyframeRule(const CSSParserContext*,
const String&); const String&);
static bool ParseSupportsCondition(const String&, SecureContextMode); static bool ParseSupportsCondition(const String&, const ExecutionContext*);
// The color will only be changed when string contains a valid CSS color, so // The color will only be changed when string contains a valid CSS color, so
// callers can set it to a default color and ignore the boolean result. // callers can set it to a default color and ignore the boolean result.
...@@ -119,7 +121,7 @@ class CORE_EXPORT CSSParser { ...@@ -119,7 +121,7 @@ class CORE_EXPORT CSSParser {
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-font // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-font
// https://drafts.csswg.org/css-font-loading/#find-the-matching-font-faces // https://drafts.csswg.org/css-font-loading/#find-the-matching-font-faces
static MutableCSSPropertyValueSet* ParseFont(const String&, static MutableCSSPropertyValueSet* ParseFont(const String&,
SecureContextMode); const ExecutionContext*);
private: private:
static MutableCSSPropertyValueSet::SetResult ParseValue( static MutableCSSPropertyValueSet::SetResult ParseValue(
......
...@@ -52,7 +52,7 @@ class CORE_EXPORT CSSParserContext final ...@@ -52,7 +52,7 @@ class CORE_EXPORT CSSParserContext final
SecureContextMode, SecureContextMode,
SelectorProfile = kLiveProfile, SelectorProfile = kLiveProfile,
const Document* use_counter_document = nullptr); const Document* use_counter_document = nullptr);
CSSParserContext(const Document&); explicit CSSParserContext(const Document&);
CSSParserContext(const Document&, CSSParserContext(const Document&,
const KURL& base_url_override, const KURL& base_url_override,
bool origin_clean, bool origin_clean,
......
...@@ -13,8 +13,7 @@ namespace blink { ...@@ -13,8 +13,7 @@ namespace blink {
TEST(FontStyleResolverTest, Simple) { TEST(FontStyleResolverTest, Simple) {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, CSSPropertyID::kFont, "15px Ahem", true, CSSParser::ParseValue(style, CSSPropertyID::kFont, "15px Ahem", true);
SecureContextMode::kInsecureContext);
FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr); FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr);
...@@ -26,8 +25,7 @@ TEST(FontStyleResolverTest, Simple) { ...@@ -26,8 +25,7 @@ TEST(FontStyleResolverTest, Simple) {
TEST(FontStyleResolverTest, InvalidSize) { TEST(FontStyleResolverTest, InvalidSize) {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, CSSPropertyID::kFont, "-1px Ahem", true, CSSParser::ParseValue(style, CSSPropertyID::kFont, "-1px Ahem", true);
SecureContextMode::kInsecureContext);
FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr); FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr);
...@@ -39,8 +37,7 @@ TEST(FontStyleResolverTest, InvalidSize) { ...@@ -39,8 +37,7 @@ TEST(FontStyleResolverTest, InvalidSize) {
TEST(FontStyleResolverTest, InvalidWeight) { TEST(FontStyleResolverTest, InvalidWeight) {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, CSSPropertyID::kFont, "wrong 1px Ahem", true, CSSParser::ParseValue(style, CSSPropertyID::kFont, "wrong 1px Ahem", true);
SecureContextMode::kInsecureContext);
FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr); FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr);
...@@ -53,8 +50,7 @@ TEST(FontStyleResolverTest, InvalidEverything) { ...@@ -53,8 +50,7 @@ TEST(FontStyleResolverTest, InvalidEverything) {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, CSSPropertyID::kFont, CSSParser::ParseValue(style, CSSPropertyID::kFont,
"wrong wrong wrong 1px Ahem", true, "wrong wrong wrong 1px Ahem", true);
SecureContextMode::kInsecureContext);
FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr); FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr);
...@@ -66,8 +62,7 @@ TEST(FontStyleResolverTest, InvalidEverything) { ...@@ -66,8 +62,7 @@ TEST(FontStyleResolverTest, InvalidEverything) {
TEST(FontStyleResolverTest, RelativeSize) { TEST(FontStyleResolverTest, RelativeSize) {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, CSSPropertyID::kFont, "italic 2ex Ahem", true, CSSParser::ParseValue(style, CSSPropertyID::kFont, "italic 2ex Ahem", true);
SecureContextMode::kInsecureContext);
FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr); FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr);
......
...@@ -27,8 +27,7 @@ class CSSParserThreadedTest : public MultiThreadedTest { ...@@ -27,8 +27,7 @@ class CSSParserThreadedTest : public MultiThreadedTest {
const String& text) { const String& text) {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, prop, text, true, CSSParser::ParseValue(style, prop, text, true);
SecureContextMode::kInsecureContext);
return style; return style;
} }
}; };
......
...@@ -34,8 +34,7 @@ TSAN_TEST(FontObjectThreadedTest, GetFontDefinition) { ...@@ -34,8 +34,7 @@ TSAN_TEST(FontObjectThreadedTest, GetFontDefinition) {
RunOnThreads([]() { RunOnThreads([]() {
auto* style = auto* style =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode); MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
CSSParser::ParseValue(style, CSSPropertyID::kFont, "15px Ahem", true, CSSParser::ParseValue(style, CSSPropertyID::kFont, "15px Ahem", true);
SecureContextMode::kInsecureContext);
FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr); FontDescription desc = FontStyleResolver::ComputeFont(*style, nullptr);
......
...@@ -91,8 +91,8 @@ MutableCSSPropertyValueSet* CanvasFontCache::ParseFont( ...@@ -91,8 +91,8 @@ MutableCSSPropertyValueSet* CanvasFontCache::ParseFont(
DCHECK(!add_result.is_new_entry); DCHECK(!add_result.is_new_entry);
parsed_style = i->value; parsed_style = i->value;
} else { } else {
parsed_style = CSSParser::ParseFont( parsed_style =
font_string, document_->GetExecutionContext()->GetSecureContextMode()); CSSParser::ParseFont(font_string, document_->GetExecutionContext());
if (!parsed_style) if (!parsed_style)
return nullptr; return nullptr;
fetched_fonts_.insert(font_string, parsed_style); fetched_fonts_.insert(font_string, parsed_style);
......
...@@ -408,9 +408,8 @@ void OffscreenCanvasRenderingContext2D::setFont(const String& new_font) { ...@@ -408,9 +408,8 @@ void OffscreenCanvasRenderingContext2D::setFont(const String& new_font) {
if (!style) if (!style)
return; return;
CSSParser::ParseValue( CSSParser::ParseValue(style, CSSPropertyID::kFont, new_font, true,
style, CSSPropertyID::kFont, new_font, true, Host()->GetTopExecutionContext());
Host()->GetTopExecutionContext()->GetSecureContextMode());
// According to // According to
// http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html, // http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html,
......
...@@ -6182,8 +6182,6 @@ crbug.com/1133821 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-a ...@@ -6182,8 +6182,6 @@ crbug.com/1133821 virtual/off-main-thread-css-paint/external/wpt/css/css-paint-a
crbug.com/1135405 http/tests/devtools/sources/debugger-pause/debugger-pause-infinite-loop.js [ Pass Timeout ] crbug.com/1135405 http/tests/devtools/sources/debugger-pause/debugger-pause-infinite-loop.js [ Pass Timeout ]
crbug.com/1135978 [ Linux Release ] fast/dom/css-delete-doc.html [ Pass Timeout ] crbug.com/1135978 [ Linux Release ] fast/dom/css-delete-doc.html [ Pass Timeout ]
crbug.com/1134237 virtual/origin-trials-runtimeflags-disabled/http/tests/origin_trials/webexposed/beforematch-origin-trial-interfaces.html [ Failure ]
# Sheriff 2020-10-09 # Sheriff 2020-10-09
crbug.com/1136687 external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html [ Pass Failure ] crbug.com/1136687 external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_supercedes_capture.html [ Pass Failure ]
crbug.com/1136726 [ Linux ] virtual/gpu-rasterization/images/imagemap-focus-ring-outline-color-not-inherited-from-map.html [ Failure ] crbug.com/1136726 [ Linux ] virtual/gpu-rasterization/images/imagemap-focus-ring-outline-color-not-inherited-from-map.html [ Failure ]
......
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Generate token with the command:
generate_token.py http://127.0.0.1:8000 BeforeMatchEvent --expire-timestamp=2000000000 --version 3
-->
<meta http-equiv="origin-trial" content="A6qG2zhYsapjwU1J8SBwg56JXGIaUKzBXWy2hJ8V1U2fX/ZRPCwdajgTdBT/BFdDTmst9Dl2uFqsxc/3LPqmFgQAAABYeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQmVmb3JlTWF0Y2hFdmVudCIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
test(t => {
assert_equals(document.body.onbeforematch, null,
'onbeforematch should be null instead of undefined when the feature is enabled.');
assert_true(CSS.supports('content-visibility: hidden-matchable'),
'CSS.supports() should show that hidden-matchable rule is enabled when the feature is enabled.');
}, 'Tests feature detection for the BeforeMatchEvent origin trial');
</script>
...@@ -16,7 +16,7 @@ test(t => { ...@@ -16,7 +16,7 @@ test(t => {
'onbeforematch should be null instead of undefined when the feature is enabled.'); 'onbeforematch should be null instead of undefined when the feature is enabled.');
assert_true(CSS.supports('content-visibility', 'hidden-matchable'), assert_true(CSS.supports('content-visibility', 'hidden-matchable'),
'CSS.supports() should show that hidden-matchable is enabled when the feature is enabled.'); 'CSS.supports() should show that hidden-matchable value is enabled when the feature is enabled.');
}, 'Tests feature detection for the BeforeMatchEvent origin trial'); }, 'Tests feature detection for the BeforeMatchEvent origin trial');
</script> </script>
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