Commit 1de71b63 authored by Rakina Zata Amni's avatar Rakina Zata Amni Committed by Commit Bot

Implement Document.createCSSStyleSheet

We previously implemented constructable CSSStyleSheet synchronously,
but discussions in  https://github.com/WICG/construct-stylesheets/issues/2
have gravitated away from that and we are now interested in asynchronous
creation of CSSStyleSheet.

This CL added Document.createCSSStyleSheet that returns a
Promise<CSSStyleSheet>, while also removing the exposed CSSStyleSheet
that was previously exposed. Note that because CSS parsing is still
done on the main thread, this function is actually still blocking.

The previously implemented CSSStyleSheet is changed to only produce
empty CSSStyleSheets in this CL: crrev.com/c/1126898

Bug: 807560
Change-Id: I9f9d17ae04829ff399ae384f8b3a6d97a3b0613b
Reviewed-on: https://chromium-review.googlesource.com/1126754
Commit-Queue: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: default avatarHayato Ito <hayato@chromium.org>
Reviewed-by: default avatarTakayoshi Kochi <kochi@chromium.org>
Reviewed-by: default avatarFergal Daly <fergal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575600}
parent c81707c5
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
const redStyleTexts = [".red { color: red; }", ".red + span + span { color: red; }"]; const redStyleTexts = [".red { color: red; }", ".red + span + span { color: red; }"];
test(() => { test(() => {
// Style sheets can be created const sheet = new CSSStyleSheet({title: "Red", disabled: true, media: "screen, print"});
const sheet = new CSSStyleSheet({title: "Red", disabled: true, media: "screen, print"});
assert_equals(sheet.title, "Red"); assert_equals(sheet.title, "Red");
assert_equals(sheet.ownerNode, null); assert_equals(sheet.ownerNode, null);
assert_equals(sheet.ownerRule, null); assert_equals(sheet.ownerRule, null);
...@@ -28,4 +27,24 @@ const sheet = new CSSStyleSheet({title: "Red", disabled: true, media: "screen, p ...@@ -28,4 +27,24 @@ const sheet = new CSSStyleSheet({title: "Red", disabled: true, media: "screen, p
assert_equals(sheet.cssRules.length, 2); assert_equals(sheet.cssRules.length, 2);
assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]); assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]);
}, 'Empty CSSStyleSheet can be constructed using script'); }, 'Empty CSSStyleSheet can be constructed using script');
promise_test(() => {
const promise_sheet = document.createCSSStyleSheet(redStyleTexts[0], {title: "Red", disabled: true, media: "screen, print"});
return promise_sheet.then(function(sheet) {
assert_equals(sheet.title, "Red");
assert_equals(sheet.ownerNode, null);
assert_equals(sheet.ownerRule, null);
assert_equals(sheet.media.length, 2);
assert_equals(sheet.media.item(0), "screen");
assert_equals(sheet.media.item(1), "print");
assert_true(sheet.disabled);
assert_equals(sheet.cssRules.length, 1);
assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]);
sheet.insertRule(redStyleTexts[1]);
assert_equals(sheet.cssRules.length, 2);
assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]);
});
}, 'Document.createCSSStyleSheet produces Promise<CSSStyleSheet>');
</script> </script>
...@@ -1629,6 +1629,7 @@ interface Document : Node ...@@ -1629,6 +1629,7 @@ interface Document : Node
method createAttribute method createAttribute
method createAttributeNS method createAttributeNS
method createCDATASection method createCDATASection
method createCSSStyleSheet
method createComment method createComment
method createDocumentFragment method createDocumentFragment
method createElement method createElement
......
...@@ -204,6 +204,13 @@ class CORE_EXPORT CSSStyleSheet final : public StyleSheet { ...@@ -204,6 +204,13 @@ class CORE_EXPORT CSSStyleSheet final : public StyleSheet {
FRIEND_TEST_ALL_PREFIXES( FRIEND_TEST_ALL_PREFIXES(
CSSStyleSheetTest, CSSStyleSheetTest,
CSSStyleSheetConstructionWithNonEmptyCSSStyleSheetInit); CSSStyleSheetConstructionWithNonEmptyCSSStyleSheetInit);
FRIEND_TEST_ALL_PREFIXES(
CSSStyleSheetTest,
CreateCSSStyleSheetWithEmptyCSSStyleSheetInitAndText);
FRIEND_TEST_ALL_PREFIXES(
CSSStyleSheetTest,
CreateCSSStyleSheetWithNonEmptyCSSStyleSheetInitAndText);
bool AlternateFromConstructor() const { return alternate_from_constructor_; } bool AlternateFromConstructor() const { return alternate_from_constructor_; }
Member<StyleSheetContents> contents_; Member<StyleSheetContents> contents_;
......
...@@ -2,10 +2,15 @@ ...@@ -2,10 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/bindings/core/v8/media_list_or_string.h" #include "third_party/blink/renderer/bindings/core/v8/media_list_or_string.h"
#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_css_style_sheet.h"
#include "third_party/blink/renderer/core/css/css_rule_list.h" #include "third_party/blink/renderer/core/css/css_rule_list.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/css_style_sheet_init.h" #include "third_party/blink/renderer/core/css/css_style_sheet_init.h"
#include "third_party/blink/renderer/core/css/media_list.h" #include "third_party/blink/renderer/core/css/media_list.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/core/testing/page_test_base.h"
...@@ -18,6 +23,27 @@ class CSSStyleSheetTest : public PageTestBase { ...@@ -18,6 +23,27 @@ class CSSStyleSheetTest : public PageTestBase {
PageTestBase::SetUp(); PageTestBase::SetUp();
RuntimeEnabledFeatures::SetConstructableStylesheetsEnabled(true); RuntimeEnabledFeatures::SetConstructableStylesheetsEnabled(true);
} }
class FunctionForTest : public ScriptFunction {
public:
static v8::Local<v8::Function> CreateFunction(ScriptState* script_state,
ScriptValue* output) {
FunctionForTest* self = new FunctionForTest(script_state, output);
return self->BindToV8Function();
}
private:
FunctionForTest(ScriptState* script_state, ScriptValue* output)
: ScriptFunction(script_state), output_(output) {}
ScriptValue Call(ScriptValue value) override {
DCHECK(!value.IsEmpty());
*output_ = value;
return value;
}
ScriptValue* output_;
};
}; };
TEST_F(CSSStyleSheetTest, ConstructorWithoutRuntimeFlagThrowsException) { TEST_F(CSSStyleSheetTest, ConstructorWithoutRuntimeFlagThrowsException) {
...@@ -67,4 +93,72 @@ TEST_F(CSSStyleSheetTest, ...@@ -67,4 +93,72 @@ TEST_F(CSSStyleSheetTest,
ASSERT_FALSE(exception_state.HadException()); ASSERT_FALSE(exception_state.HadException());
} }
TEST_F(CSSStyleSheetTest,
CreateCSSStyleSheetWithEmptyCSSStyleSheetInitAndText) {
V8TestingScope scope;
DummyExceptionStateForTesting exception_state;
ScriptPromise promise = GetDocument().createCSSStyleSheet(
scope.GetScriptState(), "", CSSStyleSheetInit(), exception_state);
EXPECT_FALSE(promise.IsEmpty());
ASSERT_FALSE(exception_state.HadException());
ScriptValue on_fulfilled, on_rejected;
promise.Then(
FunctionForTest::CreateFunction(scope.GetScriptState(), &on_fulfilled),
FunctionForTest::CreateFunction(scope.GetScriptState(), &on_rejected));
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
CSSStyleSheet* sheet = V8CSSStyleSheet::ToImplWithTypeCheck(
scope.GetIsolate(), on_fulfilled.V8Value());
EXPECT_TRUE(sheet->href().IsNull());
EXPECT_EQ(sheet->parentStyleSheet(), nullptr);
EXPECT_EQ(sheet->ownerNode(), nullptr);
EXPECT_EQ(sheet->ownerRule(), nullptr);
EXPECT_EQ(sheet->media()->length(), 0U);
EXPECT_EQ(sheet->title(), StringImpl::empty_);
EXPECT_FALSE(sheet->AlternateFromConstructor());
EXPECT_FALSE(sheet->disabled());
EXPECT_EQ(sheet->cssRules(exception_state)->length(), 0U);
ASSERT_FALSE(exception_state.HadException());
}
TEST_F(CSSStyleSheetTest,
CreateCSSStyleSheetWithNonEmptyCSSStyleSheetInitAndText) {
String styleText[2] = {".red { color: red; }",
".red + span + span { color: red; }"};
CSSStyleSheetInit init;
init.setMedia(MediaListOrString::FromString("screen, print"));
init.setTitle("test");
init.setAlternate(true);
init.setDisabled(true);
V8TestingScope scope;
DummyExceptionStateForTesting exception_state;
ScriptPromise promise = GetDocument().createCSSStyleSheet(
scope.GetScriptState(), styleText[0] + styleText[1], init,
exception_state);
EXPECT_FALSE(promise.IsEmpty());
ASSERT_FALSE(exception_state.HadException());
ScriptValue on_fulfilled, on_rejected;
promise.Then(
FunctionForTest::CreateFunction(scope.GetScriptState(), &on_fulfilled),
FunctionForTest::CreateFunction(scope.GetScriptState(), &on_rejected));
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
CSSStyleSheet* sheet = V8CSSStyleSheet::ToImplWithTypeCheck(
scope.GetIsolate(), on_fulfilled.V8Value());
EXPECT_TRUE(sheet->href().IsNull());
EXPECT_EQ(sheet->parentStyleSheet(), nullptr);
EXPECT_EQ(sheet->ownerNode(), nullptr);
EXPECT_EQ(sheet->ownerRule(), nullptr);
EXPECT_EQ(sheet->media()->length(), 2U);
EXPECT_EQ(sheet->media()->mediaText(), init.media().GetAsString());
EXPECT_EQ(sheet->title(), init.title());
EXPECT_TRUE(sheet->AlternateFromConstructor());
EXPECT_TRUE(sheet->disabled());
EXPECT_EQ(sheet->cssRules(exception_state)->length(), 2U);
EXPECT_EQ(sheet->cssRules(exception_state)->item(0)->cssText(), styleText[0]);
EXPECT_EQ(sheet->cssRules(exception_state)->item(1)->cssText(), styleText[1]);
ASSERT_FALSE(exception_state.HadException());
}
} // namespace blink } // namespace blink
...@@ -1117,6 +1117,26 @@ Element* Document::CreateElement(const QualifiedName& q_name, ...@@ -1117,6 +1117,26 @@ Element* Document::CreateElement(const QualifiedName& q_name,
flags, is); flags, is);
} }
ScriptPromise Document::createCSSStyleSheet(ScriptState* script_state,
const String& text,
ExceptionState& exception_state) {
return Document::createCSSStyleSheet(script_state, text, CSSStyleSheetInit(),
exception_state);
}
ScriptPromise Document::createCSSStyleSheet(ScriptState* script_state,
const String& text,
const CSSStyleSheetInit& options,
ExceptionState& exception_state) {
// Even though this function returns a Promise, it actually does all the work
// at once here because CSS parsing is done synchronously on the main thread.
// TODO(rakina): Find a way to improve this.
CSSStyleSheet* sheet = CSSStyleSheet::Create(*this, options, exception_state);
sheet->SetText(text);
return ScriptPromise::Cast(script_state,
ScriptValue::From(script_state, sheet));
}
ScriptValue Document::registerElement(ScriptState* script_state, ScriptValue Document::registerElement(ScriptState* script_state,
const AtomicString& name, const AtomicString& name,
const ElementRegistrationOptions& options, const ElementRegistrationOptions& options,
......
...@@ -36,7 +36,10 @@ ...@@ -36,7 +36,10 @@
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "third_party/blink/public/platform/web_focus_type.h" #include "third_party/blink/public/platform/web_focus_type.h"
#include "third_party/blink/public/platform/web_insecure_request_policy.h" #include "third_party/blink/public/platform/web_insecure_request_policy.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/css_style_sheet_init.h"
#include "third_party/blink/renderer/core/dom/container_node.h" #include "third_party/blink/renderer/core/dom/container_node.h"
#include "third_party/blink/renderer/core/dom/create_element_flags.h" #include "third_party/blink/renderer/core/dom/create_element_flags.h"
#include "third_party/blink/renderer/core/dom/document_encoding_data.h" #include "third_party/blink/renderer/core/dom/document_encoding_data.h"
...@@ -358,6 +361,15 @@ class CORE_EXPORT Document : public ContainerNode, ...@@ -358,6 +361,15 @@ class CORE_EXPORT Document : public ContainerNode,
Element* CreateRawElement(const QualifiedName&, Element* CreateRawElement(const QualifiedName&,
const CreateElementFlags = CreateElementFlags()); const CreateElementFlags = CreateElementFlags());
ScriptPromise createCSSStyleSheet(ScriptState*,
const String&,
ExceptionState&);
ScriptPromise createCSSStyleSheet(ScriptState*,
const String&,
const CSSStyleSheetInit&,
ExceptionState&);
Element* ElementFromPoint(double x, double y) const; Element* ElementFromPoint(double x, double y) const;
HeapVector<Member<Element>> ElementsFromPoint(double x, double y) const; HeapVector<Member<Element>> ElementsFromPoint(double x, double y) const;
Range* caretRangeFromPoint(int x, int y); Range* caretRangeFromPoint(int x, int y);
......
...@@ -75,6 +75,8 @@ typedef (HTMLScriptElement or SVGScriptElement) HTMLOrSVGScriptElement; ...@@ -75,6 +75,8 @@ typedef (HTMLScriptElement or SVGScriptElement) HTMLOrSVGScriptElement;
[NewObject] Range createRange(); [NewObject] Range createRange();
[CallWith=ScriptState, NewObject, RaisesException, RuntimeEnabled=ConstructableStylesheets] Promise<CSSStyleSheet> createCSSStyleSheet(DOMString text, optional CSSStyleSheetInit options);
// NodeFilter.SHOW_ALL = 0xFFFFFFFF // NodeFilter.SHOW_ALL = 0xFFFFFFFF
[NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null); [NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
[NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null); [NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
......
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