CanvasFormattedText Styling Prototype
This change resurrects @sushraja's changes to add styling abilities to
CanvasFormattedText. Note that this implements an older version of the
API, which is changing rapidly.
Original description:
Add support for styling CanvasFormattedText
This change adds a styleMap on the CanvasFormattedText and
CanvasFormattedTextRun. A subset of CSS properties are supported on the
CanvasFormattedText and a smaller subset on the CanvasFormattedTextRun.
A height parameter is added to fillFormattedText so that vertical
writing modes can be supported. The height/width passed to
fillFormattedText are used as the available space, while the
CSS height/width can be set on the CanvasFormattedText to control
height/width explicitly.
Change-Id: I2af3e2bc667f4d6e57da390fdf0648663a75461e
Bug: 1155764
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3491151
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: Fernando Serboncini <fserb@chromium.org>
Commit-Queue: Ian Prest <iapres@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#987620}
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 4520eb7..514ed887 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -91,6 +91,7 @@
"//third_party/blink/renderer/modules/canvas/canvas2d/canvas_filter_dictionary.idl",
"//third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.idl",
"//third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.idl",
+ "//third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.idl",
"//third_party/blink/renderer/modules/canvas/canvas2d/canvas_gradient.idl",
"//third_party/blink/renderer/modules/canvas/canvas2d/canvas_path.idl",
"//third_party/blink/renderer/modules/canvas/canvas2d/canvas_pattern.idl",
diff --git a/third_party/blink/renderer/build/scripts/core/css/css_properties.py b/third_party/blink/renderer/build/scripts/core/css/css_properties.py
index ca94084..afdf0e5 100755
--- a/third_party/blink/renderer/build/scripts/core/css/css_properties.py
+++ b/third_party/blink/renderer/build/scripts/core/css/css_properties.py
@@ -57,6 +57,10 @@
][0]
assert subprop['supports_incremental_style'], \
'%s must be incrementally applicable when its shorthand %s is' % (subprop_name, name)
+ assert not prop['valid_for_canvas_formatted_text'] or prop['is_longhand'], \
+ 'Only longhands can be valid_for_canvas_formatted_text [%s]' % name
+ assert not prop['valid_for_canvas_formatted_text_run'] or prop['is_longhand'], \
+ 'Only longhands can be valid_for_canvas_formatted_text_run [%s]' % name
def validate_alias(alias):
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
index 982e6257..15033a9 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
@@ -47,6 +47,8 @@
(property.valid_for_first_line and 'kValidForFirstLine' or ''),
(property.valid_for_cue and 'kValidForCue' or ''),
(property.valid_for_marker and 'kValidForMarker' or ''),
+ (property.valid_for_canvas_formatted_text and 'kValidForCanvasFormattedText' or ''),
+ (property.valid_for_canvas_formatted_text_run and 'kValidForCanvasFormattedTextRun' or ''),
(is_surrogate and 'kSurrogate' or ''),
(property.font and 'kAffectsFont' or ''),
(property.is_background and 'kBackground' or ''),
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 05604b8..5709944 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -582,7 +582,27 @@
idempotent: {
default: true,
valid_type: "bool",
- }
+ },
+
+ // - valid_for_canvas_formatted_text: true
+ //
+ // Whether the property can be used to style the top-level container
+ // (similar to display:block, but supporting fewer properties), in the
+ // experimental API to bring multiline text rendering to HTML canvas.
+ // https://github.com/WICG/canvas-formatted-text/
+ valid_for_canvas_formatted_text: {
+ default: false,
+ valid_type: "bool",
+ },
+
+ // - valid_for_canvas_formatted_text_run: true
+ //
+ // Whether the property can be used to style individual text runs, in the
+ // experimental API to bring multiline text rendering to HTML canvas.
+ valid_for_canvas_formatted_text_run: {
+ default: false,
+ valid_type: "bool",
+ },
},
// Members in the data objects should appear in the same order as in the
@@ -817,6 +837,8 @@
valid_for_cue: true,
valid_for_marker: true,
valid_for_highlight: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
is_highlight_colors: true,
},
{
@@ -833,6 +855,8 @@
style_builder_custom_functions: ["value"],
priority: "High",
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-family",
@@ -850,6 +874,8 @@
valid_for_cue: true,
valid_for_marker: true,
tree_scoped_value: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-kerning",
@@ -864,6 +890,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-optical-sizing",
@@ -910,6 +938,8 @@
valid_for_first_line: true,
valid_for_cue: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-size-adjust",
@@ -946,6 +976,8 @@
valid_for_first_line: true,
valid_for_cue: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-style",
@@ -962,6 +994,8 @@
valid_for_first_line: true,
valid_for_cue: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-variant-ligatures",
@@ -982,6 +1016,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-variant-caps",
@@ -999,6 +1035,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-variant-east-asian",
@@ -1016,6 +1054,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-variant-numeric",
@@ -1034,6 +1074,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-weight",
@@ -1051,6 +1093,8 @@
valid_for_first_line: true,
valid_for_cue: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-synthesis-weight",
@@ -1112,6 +1156,8 @@
valid_for_first_line: true,
valid_for_marker: true,
computable: false,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "font-variation-settings",
@@ -1129,6 +1175,8 @@
valid_for_cue: true,
valid_for_marker: true,
computable: false,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "-webkit-font-smoothing",
@@ -1187,6 +1235,7 @@
style_builder_custom_functions: ["initial", "inherit", "value"],
priority: "High",
computable: false,
+ valid_for_canvas_formatted_text: true,
},
{
name: "-webkit-text-orientation",
@@ -1209,6 +1258,7 @@
type_name: "WritingMode",
style_builder_custom_functions: ["initial", "inherit", "value"],
priority: "High",
+ valid_for_canvas_formatted_text: true,
},
{
name: "-webkit-writing-mode",
@@ -2681,6 +2731,7 @@
resolver: "vertical",
},
supports_incremental_style: true,
+ valid_for_canvas_formatted_text: true,
},
{
name: "hyphens",
@@ -4281,6 +4332,7 @@
default_value: "start",
getter: "GetTextAlign",
style_builder_custom_functions: ["value"],
+ valid_for_canvas_formatted_text: true,
},
{
name: "text-align-last",
@@ -4314,6 +4366,7 @@
name_for_methods: "TextCombine",
valid_for_marker: true,
computable: false,
+ valid_for_canvas_formatted_text: true,
},
{
name: "text-decoration-color",
@@ -4334,6 +4387,8 @@
valid_for_cue: true,
valid_for_highlight: true,
supports_incremental_style: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-decoration-line",
@@ -4349,6 +4404,8 @@
valid_for_first_line: true,
valid_for_cue: true,
valid_for_highlight: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-decoration-skip-ink",
@@ -4364,6 +4421,8 @@
valid_for_cue: true,
valid_for_marker: true,
valid_for_highlight: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-decoration-style",
@@ -4377,6 +4436,8 @@
valid_for_first_line: true,
valid_for_cue: true,
valid_for_highlight: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-decoration-thickness",
@@ -4395,6 +4456,8 @@
valid_for_first_line: true,
valid_for_highlight: true,
computable: false,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-indent",
@@ -4422,6 +4485,7 @@
typedom_types: ["Keyword"],
valid_for_first_letter: true,
valid_for_first_line: true,
+ valid_for_canvas_formatted_text: true,
},
{
name: "text-overflow",
@@ -4431,6 +4495,7 @@
keywords: ["clip", "ellipsis"],
typedom_types: ["Keyword"],
default_value: "clip",
+ valid_for_canvas_formatted_text: true,
},
{
name: "text-shadow",
@@ -4451,6 +4516,8 @@
valid_for_cue: true,
valid_for_marker: true,
valid_for_highlight: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-size-adjust",
@@ -4479,6 +4546,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-underline-offset",
@@ -4495,6 +4564,8 @@
valid_for_first_letter: true,
valid_for_first_line: true,
computable: false,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "text-underline-position",
@@ -4511,6 +4582,8 @@
typedom_types: ["Keyword"],
valid_for_first_letter: true,
valid_for_first_line: true,
+ valid_for_canvas_formatted_text: true,
+ valid_for_canvas_formatted_text_run: true,
},
{
name: "top",
@@ -4979,6 +5052,7 @@
keywords: ["auto", "loose", "normal", "strict", "after-white-space", "anywhere"],
default_value: "auto",
type_name: "LineBreak",
+ valid_for_canvas_formatted_text: true,
},
{
name: "line-break",
@@ -4988,6 +5062,7 @@
keywords: ["auto", "loose", "normal", "strict", "anywhere"],
typedom_types: ["Keyword"],
valid_for_marker: true,
+ valid_for_canvas_formatted_text: true,
},
// An Apple extension.
{
@@ -5370,6 +5445,7 @@
resolver: "horizontal",
},
supports_incremental_style: true,
+ valid_for_canvas_formatted_text: true,
},
{
name: "will-change",
diff --git a/third_party/blink/renderer/core/css/properties/css_property.h b/third_party/blink/renderer/core/css/properties/css_property.h
index 660bd3e..656266e4 100644
--- a/third_party/blink/renderer/core/css/properties/css_property.h
+++ b/third_party/blink/renderer/core/css/properties/css_property.h
@@ -64,6 +64,12 @@
bool IsValidForCue() const { return flags_ & kValidForCue; }
bool IsValidForMarker() const { return flags_ & kValidForMarker; }
bool IsValidForHighlight() const { return flags_ & kValidForHighlight; }
+ bool IsValidForCanvasFormattedText() const {
+ return flags_ & kValidForCanvasFormattedText;
+ }
+ bool IsValidForCanvasFormattedTextRun() const {
+ return flags_ & kValidForCanvasFormattedTextRun;
+ }
bool IsSurrogate() const { return flags_ & kSurrogate; }
bool AffectsFont() const { return flags_ & kAffectsFont; }
bool IsBackground() const { return flags_ & kBackground; }
@@ -170,6 +176,11 @@
kSupportsIncrementalStyle = 1 << 23,
// See idempotent in css_properties.json5.
kIdempotent = 1 << 24,
+ // Set if the css property can apply to the experiemental canvas
+ // formatted text API to render multiline text in canvas.
+ // https://github.com/WICG/canvas-formatted-text
+ kValidForCanvasFormattedText = 1 << 25,
+ kValidForCanvasFormattedTextRun = 1 << 26,
};
constexpr CSSProperty(CSSPropertyID property_id,
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 1d9d8eb6..97aeb07 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -2070,6 +2070,7 @@
visitor->Trace(selector_filter_);
visitor->Trace(document_);
visitor->Trace(tracker_);
+ visitor->Trace(canvas_formatted_text_element_);
}
bool StyleResolver::IsForcedColorsModeEnabled() const {
@@ -2357,4 +2358,68 @@
#undef PROPAGATE_VALUE
#undef PROPAGATE_FROM
+scoped_refptr<const ComputedStyle> StyleResolver::StyleForCanvasFormattedText(
+ bool is_text_run,
+ const FontDescription& default_font,
+ const CSSPropertyValueSet* css_property_value_set) {
+ return StyleForCanvasFormattedText(is_text_run, &default_font,
+ /*parent_style*/ nullptr,
+ css_property_value_set);
+}
+
+scoped_refptr<const ComputedStyle> StyleResolver::StyleForCanvasFormattedText(
+ bool is_text_run,
+ const ComputedStyle& parent_style,
+ const CSSPropertyValueSet* css_property_value_set) {
+ return StyleForCanvasFormattedText(is_text_run, /*default_font*/ nullptr,
+ &parent_style, css_property_value_set);
+}
+
+scoped_refptr<const ComputedStyle> StyleResolver::StyleForCanvasFormattedText(
+ bool is_text_run,
+ const FontDescription* default_font,
+ const ComputedStyle* parent_style,
+ const CSSPropertyValueSet* css_property_value_set) {
+ DCHECK_NE(!!parent_style, !!default_font)
+ << "only one of `default_font` or `parent_style` should be specified";
+
+ // Set up our initial style properties based on either the `default_font` or
+ // `parent_style`.
+ scoped_refptr<ComputedStyle> style = CreateComputedStyle();
+ if (default_font)
+ style->SetFontDescription(*default_font);
+ else // parent_style
+ style->InheritFrom(*parent_style);
+ style->SetDisplay(is_text_run ? EDisplay::kInline : EDisplay::kBlock);
+
+ // Apply any properties in the `css_property_value_set`.
+ if (css_property_value_set) {
+ // Use a dummy/disconnected element when resolving the styles so that we
+ // don't inherit anything from existing elements.
+ StyleResolverState state(
+ GetDocument(), EnsureElementForCanvasFormattedText(),
+ StyleRecalcContext{},
+ StyleRequest{parent_style ? parent_style : &InitialStyle()});
+ state.SetStyle(style);
+
+ // Use StyleCascade to apply inheritance in the correct order.
+ STACK_UNINITIALIZED StyleCascade cascade(state);
+ cascade.MutableMatchResult().AddMatchedProperties(
+ css_property_value_set,
+ AddMatchedPropertiesOptions::Builder().SetIsInlineStyle(true).Build());
+ cascade.Apply();
+
+ StyleAdjuster::AdjustComputedStyle(state, nullptr);
+ }
+
+ return style;
+}
+
+Element& StyleResolver::EnsureElementForCanvasFormattedText() {
+ if (!canvas_formatted_text_element_)
+ canvas_formatted_text_element_ =
+ MakeGarbageCollected<Element>(html_names::kSpanTag, &GetDocument());
+ return *canvas_formatted_text_element_;
+}
+
} // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index 8a89bab..8e0670c 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -96,6 +96,14 @@
const AtomicString& page_name);
scoped_refptr<const ComputedStyle> StyleForText(Text*);
scoped_refptr<ComputedStyle> StyleForViewport();
+ scoped_refptr<const ComputedStyle> StyleForCanvasFormattedText(
+ bool is_text_run,
+ const ComputedStyle& parent_style,
+ const CSSPropertyValueSet* css_property_value_set);
+ scoped_refptr<const ComputedStyle> StyleForCanvasFormattedText(
+ bool is_text_run,
+ const FontDescription& default_font,
+ const CSSPropertyValueSet* css_property_value_set);
// Propagate computed values from the root or body element to the viewport
// when specified to do so.
@@ -306,12 +314,23 @@
Member<StyleRuleUsageTracker> tracker_;
+ // This is a dummy/disconnected element that we use for CanvasFormattedText
+ // style computations; see `EnsureElementForCanvasFormattedText`.
+ Member<Element> canvas_formatted_text_element_;
+
bool print_media_type_ = false;
bool was_viewport_resized_ = false;
FRIEND_TEST_ALL_PREFIXES(ComputedStyleTest, ApplyInternalLightDarkColor);
friend class StyleResolverTest;
FRIEND_TEST_ALL_PREFIXES(StyleResolverTest, TreeScopedReferences);
+
+ Element& EnsureElementForCanvasFormattedText();
+ scoped_refptr<const ComputedStyle> StyleForCanvasFormattedText(
+ bool is_text_run,
+ const FontDescription* default_font,
+ const ComputedStyle* parent_style,
+ const CSSPropertyValueSet* css_property_value_set);
};
} // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.h b/third_party/blink/renderer/core/css/style_property_serializer.h
index f494e6e..36c6f67 100644
--- a/third_party/blink/renderer/core/css/style_property_serializer.h
+++ b/third_party/blink/renderer/core/css/style_property_serializer.h
@@ -34,7 +34,7 @@
class CSSPropertyValueSet;
class StylePropertyShorthand;
-class StylePropertySerializer {
+class CORE_EXPORT StylePropertySerializer {
STACK_ALLOCATED();
public:
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 7093dbc..8955a54a 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -212,7 +212,7 @@
return text;
}
-LayoutText* LayoutText::CreateAnonymous(
+LayoutText* LayoutText::CreateAnonymousForFormattedText(
Document& doc,
scoped_refptr<const ComputedStyle> style,
scoped_refptr<StringImpl> text,
@@ -220,7 +220,7 @@
LayoutText* layout_text =
LayoutObjectFactory::CreateText(nullptr, std::move(text), legacy);
layout_text->SetDocumentForAnonymous(&doc);
- layout_text->SetStyle(std::move(style));
+ layout_text->SetStyleInternal(std::move(style));
return layout_text;
}
diff --git a/third_party/blink/renderer/core/layout/layout_text.h b/third_party/blink/renderer/core/layout/layout_text.h
index 7a3fca2..0f4ee0a 100644
--- a/third_party/blink/renderer/core/layout/layout_text.h
+++ b/third_party/blink/renderer/core/layout/layout_text.h
@@ -90,10 +90,11 @@
scoped_refptr<const ComputedStyle>,
LegacyLayout);
- static LayoutText* CreateAnonymous(Document&,
- scoped_refptr<const ComputedStyle>,
- scoped_refptr<StringImpl>,
- LegacyLayout legacy);
+ static LayoutText* CreateAnonymousForFormattedText(
+ Document&,
+ scoped_refptr<const ComputedStyle>,
+ scoped_refptr<StringImpl>,
+ LegacyLayout legacy);
const char* GetName() const override {
NOT_DESTROYED();
diff --git a/third_party/blink/renderer/modules/canvas/BUILD.gn b/third_party/blink/renderer/modules/canvas/BUILD.gn
index ca6c394c..d66aaafe 100644
--- a/third_party/blink/renderer/modules/canvas/BUILD.gn
+++ b/third_party/blink/renderer/modules/canvas/BUILD.gn
@@ -17,6 +17,8 @@
"canvas2d/canvas_formatted_text.h",
"canvas2d/canvas_formatted_text_run.cc",
"canvas2d/canvas_formatted_text_run.h",
+ "canvas2d/canvas_formatted_text_style.cc",
+ "canvas2d/canvas_formatted_text_style.h",
"canvas2d/canvas_gradient.cc",
"canvas2d/canvas_gradient.h",
"canvas2d/canvas_image_source_util.cc",
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
index b4469b5..bb48b39 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
@@ -24,6 +24,7 @@
visitor->Trace(text_runs_);
visitor->Trace(block_);
ScriptWrappable::Trace(visitor);
+ CanvasFormattedTextStyle::Trace(visitor);
}
CanvasFormattedText* CanvasFormattedText::Create(
@@ -38,7 +39,8 @@
return canvas_formatted_text;
}
-CanvasFormattedText::CanvasFormattedText(ExecutionContext* execution_context) {
+CanvasFormattedText::CanvasFormattedText(ExecutionContext* execution_context)
+ : CanvasFormattedTextStyle(/* is_text_run */ false) {
// Refrain from extending the use of document, apart from creating layout
// block flow. In the future we should handle execution_context's from worker
// threads that do not have a document.
@@ -63,15 +65,23 @@
block_->Destroy();
}
-LayoutBlockFlow* CanvasFormattedText::GetLayoutBlock(
+void CanvasFormattedText::SetNeedsStyleRecalc() {
+ needs_style_recalc_ = true;
+}
+
+void CanvasFormattedText::UpdateComputedStylesIfNeeded(
Document& document,
const FontDescription& defaultFont) {
- scoped_refptr<ComputedStyle> style =
- document.GetStyleResolver().CreateComputedStyle();
- style->SetDisplay(EDisplay::kBlock);
- style->SetFontDescription(defaultFont);
- block_->SetStyle(style);
- return block_;
+ if (needs_style_recalc_ || current_default_font_ != defaultFont) {
+ auto style = document.GetStyleResolver().StyleForCanvasFormattedText(
+ /*is_text_run*/ false, defaultFont, GetCssPropertySet());
+ block_->SetStyle(style, LayoutObject::ApplyStyleChanges::kNo);
+ block_->SetHorizontalWritingMode(style->IsHorizontalWritingMode());
+ for (auto& text_run : text_runs_)
+ text_run->UpdateStyle(document, /*parent_style*/ *style);
+ needs_style_recalc_ = false;
+ current_default_font_ = defaultFont;
+ }
}
CanvasFormattedTextRun* CanvasFormattedText::appendRun(
@@ -81,6 +91,8 @@
return nullptr;
text_runs_.push_back(run);
block_->AddChild(run->GetLayoutObject());
+ run->SetParent(this);
+ SetNeedsStyleRecalc();
return run;
}
@@ -91,10 +103,13 @@
if (!CheckRunsIndexBound(index, &exception_state) ||
!CheckRunIsNotParented(run, &exception_state))
return nullptr;
+ run->SetParent(this);
block_->AddChild(run->GetLayoutObject(),
text_runs_[index]->GetLayoutObject());
+ text_runs_[index]->SetParent(nullptr);
block_->RemoveChild(text_runs_[index]->GetLayoutObject());
text_runs_[index] = run;
+ SetNeedsStyleRecalc();
return text_runs_[index];
}
@@ -111,6 +126,8 @@
block_->AddChild(run->GetLayoutObject(),
text_runs_[index]->GetLayoutObject());
text_runs_.insert(index, run);
+ run->SetParent(this);
+ SetNeedsStyleRecalc();
return text_runs_[index];
}
@@ -130,6 +147,7 @@
}
for (wtf_size_t i = index; i < index + length; i++) {
+ text_runs_[i]->SetParent(nullptr);
block_->RemoveChild(text_runs_[i]->GetLayoutObject());
}
block_->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
@@ -144,26 +162,26 @@
double x,
double y,
double wrap_width,
+ double wrap_height,
gfx::RectF& bounds) {
- LayoutBlockFlow* block = GetLayoutBlock(document, font);
- NGBlockNode block_node(block);
- NGInlineNode node(block);
+ UpdateComputedStylesIfNeeded(document, font);
+ NGBlockNode block_node(block_);
- // TODO(sushraja) Once we add support for writing mode on the canvas formatted
- // text, fix this to be not hardcoded horizontal top to bottom.
NGConstraintSpaceBuilder builder(
WritingMode::kHorizontalTb,
- {WritingMode::kHorizontalTb, TextDirection::kLtr},
+ {block_->StyleRef().GetWritingMode(), block_->StyleRef().Direction()},
/* is_new_fc */ true);
- LayoutUnit available_logical_width(wrap_width);
- LogicalSize available_size = {available_logical_width, kIndefiniteSize};
+ LayoutUnit available_logical_width(std::max(wrap_width, 0.0));
+ LayoutUnit available_logical_height(std::max(wrap_height, 0.0));
+ LogicalSize available_size = {available_logical_width,
+ available_logical_height};
builder.SetAvailableSize(available_size);
NGConstraintSpace space = builder.ToConstraintSpace();
const NGLayoutResult* block_results = block_node.Layout(space, nullptr);
const auto& fragment =
To<NGPhysicalBoxFragment>(block_results->PhysicalFragment());
- block->RecalcFragmentsVisualOverflow();
- bounds = gfx::RectF(block->PhysicalVisualOverflowRect());
+ block_->RecalcFragmentsVisualOverflow();
+ bounds = gfx::RectF{block_->PhysicalVisualOverflowRect()};
auto* paint_record_builder = MakeGarbageCollected<PaintRecordBuilder>();
PaintInfo paint_info(paint_record_builder->Context(), CullRect::Infinite(),
PaintPhase::kForeground);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.h
index 41acc43..6ca067d 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.h
@@ -25,7 +25,9 @@
class FontDescription;
class LayoutBlockFlow;
-class MODULES_EXPORT CanvasFormattedText final : public ScriptWrappable {
+class MODULES_EXPORT CanvasFormattedText final
+ : public ScriptWrappable,
+ public CanvasFormattedTextStyle {
DEFINE_WRAPPERTYPEINFO();
USING_PRE_FINALIZER(CanvasFormattedText, Dispose);
@@ -108,21 +110,27 @@
unsigned length,
ExceptionState& exception_state);
- LayoutBlockFlow* GetLayoutBlock(Document& document,
- const FontDescription& defaultFont);
-
sk_sp<PaintRecord> PaintFormattedText(Document& document,
const FontDescription& font,
double x,
double y,
double wrap_width,
+ double wrap_height,
gfx::RectF& bounds);
void Dispose();
+ void SetNeedsStyleRecalc() override;
+
+ private:
+ void UpdateComputedStylesIfNeeded(Document& document,
+ const FontDescription& defaultFont);
+
private:
HeapVector<Member<CanvasFormattedTextRun>> text_runs_;
Member<LayoutBlockFlow> block_;
+ FontDescription current_default_font_;
+ bool needs_style_recalc_ = true;
};
} // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.idl
index 0e3b846..a53d44d3 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.idl
@@ -21,4 +21,6 @@
[RaisesException] void deleteRun(unsigned long index, unsigned long length);
readonly attribute unsigned long length;
-};
\ No newline at end of file
+};
+
+CanvasFormattedText includes CanvasFormattedTextStyle;
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.cc
index c3b8bc1..8cd79b4 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.cc
@@ -3,15 +3,15 @@
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.h"
-
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
+#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.h"
namespace blink {
CanvasFormattedTextRun::CanvasFormattedTextRun(
ExecutionContext* execution_context,
const String text)
- : text_(text) {
+ : CanvasFormattedTextStyle(/* is_text_run */ true), text_(text) {
// Refrain from extending the use of document, apart from creating layout
// text. In the future we should handle execution_context's from worker
// threads that do not have a document.
@@ -19,11 +19,18 @@
scoped_refptr<ComputedStyle> style =
document->GetStyleResolver().CreateComputedStyle();
style->SetDisplay(EDisplay::kInline);
- layout_text_ = LayoutText::CreateAnonymous(*document, std::move(style),
- text.Impl(), LegacyLayout::kAuto);
+ layout_text_ = LayoutText::CreateAnonymousForFormattedText(
+ *document, std::move(style), text.Impl(), LegacyLayout::kAuto);
layout_text_->SetIsLayoutNGObjectForCanvasFormattedText(true);
}
+void CanvasFormattedTextRun::UpdateStyle(Document& document,
+ const ComputedStyle& parent_style) {
+ auto style = document.GetStyleResolver().StyleForCanvasFormattedText(
+ /*is_text_run*/ true, parent_style, GetCssPropertySet());
+ layout_text_->SetStyle(style, LayoutObject::ApplyStyleChanges::kNo);
+}
+
void CanvasFormattedTextRun::Dispose() {
AllowDestroyingLayoutObjectInFinalizerScope scope;
if (layout_text_)
@@ -32,7 +39,14 @@
void CanvasFormattedTextRun::Trace(Visitor* visitor) const {
visitor->Trace(layout_text_);
+ visitor->Trace(parent_);
ScriptWrappable::Trace(visitor);
+ CanvasFormattedTextStyle::Trace(visitor);
+}
+
+void CanvasFormattedTextRun::SetNeedsStyleRecalc() {
+ if (parent_ != nullptr)
+ parent_->SetNeedsStyleRecalc();
}
} // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.h
index ea294f87..20abb958 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.h
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/layout/layout_text.h"
+#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/prefinalizer.h"
@@ -14,7 +15,11 @@
namespace blink {
-class MODULES_EXPORT CanvasFormattedTextRun final : public ScriptWrappable {
+class CanvasFormattedText;
+
+class MODULES_EXPORT CanvasFormattedTextRun final
+ : public ScriptWrappable,
+ public CanvasFormattedTextStyle {
DEFINE_WRAPPERTYPEINFO();
USING_PRE_FINALIZER(CanvasFormattedTextRun, Dispose);
@@ -35,14 +40,25 @@
unsigned length() const { return text_.length(); }
LayoutText* GetLayoutObject() { return layout_text_; }
+ void UpdateStyle(Document& document, const ComputedStyle& parent_style);
+
+ void SetParent(CanvasFormattedText* canvas_formatted_text) {
+ parent_ = canvas_formatted_text;
+ }
void Trace(Visitor* visitor) const override;
void Dispose();
+ // Style dirtiness is tracked only at the level of a canvas formatted text
+ // and all run styles are recomputed when a canvas formatted text has its
+ // style recomputed. This can be improved by adding additional granularity
+ // of dirtiness tracking.
+ void SetNeedsStyleRecalc() override;
+
private:
String text_;
-
+ WeakMember<CanvasFormattedText> parent_;
Member<LayoutText> layout_text_;
};
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.idl
index 7e1ea4f..d8d1932 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_run.idl
@@ -9,4 +9,6 @@
[CallWith = ExecutionContext] constructor(DOMString text);
attribute DOMString text;
-};
\ No newline at end of file
+};
+
+CanvasFormattedTextRun includes CanvasFormattedTextStyle;
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.cc
new file mode 100644
index 0000000..5c982100
--- /dev/null
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.cc
@@ -0,0 +1,125 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.h"
+#include "third_party/blink/renderer/core/css/css_font_selector.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_value_list.h"
+#include "third_party/blink/renderer/core/css/properties/longhand.h"
+#include "third_party/blink/renderer/core/css/resolver/font_style_resolver.h"
+#include "third_party/blink/renderer/core/css/resolver/style_adjuster.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
+#include "third_party/blink/renderer/core/css/style_engine.h"
+#include "third_party/blink/renderer/core/css/style_property_serializer.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
+
+namespace blink {
+
+StylePropertyMap* CanvasFormattedTextStyle::styleMap() {
+ if (!style_map_) {
+ style_map_ = MakeGarbageCollected<CanvasFormattedTextStylePropertyMap>(
+ is_text_run_, this);
+ }
+ return style_map_.Get();
+}
+
+const CSSPropertyValueSet* CanvasFormattedTextStyle::GetCssPropertySet() const {
+ return style_map_ ? style_map_->GetCssPropertySet() : nullptr;
+}
+
+void CanvasFormattedTextStyle::Trace(Visitor* visitor) const {
+ visitor->Trace(style_map_);
+}
+
+CanvasFormattedTextStylePropertyMap::CanvasFormattedTextStylePropertyMap(
+ bool is_text_run,
+ CanvasFormattedTextStyle* canvas_formatted_text_style)
+ : is_text_run_(is_text_run) {
+ css_property_value_set_ =
+ MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
+ canvas_formatted_text_style_ = canvas_formatted_text_style;
+}
+
+const CSSValue* CanvasFormattedTextStylePropertyMap::GetProperty(
+ CSSPropertyID property_id) const {
+ return css_property_value_set_->GetPropertyCSSValue(property_id);
+}
+
+const CSSValue* CanvasFormattedTextStylePropertyMap::GetCustomProperty(
+ const AtomicString& property_name) const {
+ // Custom properties or CSS variables are not supported for
+ // CanvasFormattedText at this point.
+ NOTREACHED();
+ return nullptr;
+}
+
+void CanvasFormattedTextStylePropertyMap::ForEachProperty(
+ const IterationCallback& callback) {
+ for (unsigned i = 0; i < css_property_value_set_->PropertyCount(); i++) {
+ const auto& property_reference = css_property_value_set_->PropertyAt(i);
+ callback(property_reference.Name(), property_reference.Value());
+ }
+}
+
+void CanvasFormattedTextStylePropertyMap::SetProperty(
+ CSSPropertyID unresolved_property,
+ const CSSValue& value) {
+ const CSSProperty& prop = CSSProperty::Get(unresolved_property);
+ if ((prop.IsValidForCanvasFormattedText() && !is_text_run_) ||
+ (prop.IsValidForCanvasFormattedTextRun() && is_text_run_)) {
+ css_property_value_set_->SetProperty(unresolved_property, value);
+ if (canvas_formatted_text_style_)
+ canvas_formatted_text_style_->SetNeedsStyleRecalc();
+ }
+}
+
+bool CanvasFormattedTextStylePropertyMap::SetShorthandProperty(
+ CSSPropertyID unresolved_property,
+ const String& string,
+ SecureContextMode secure_context) {
+ MutableCSSPropertyValueSet::SetResult result =
+ css_property_value_set_->SetProperty(unresolved_property, string, false,
+ secure_context);
+ if (canvas_formatted_text_style_ &&
+ result != MutableCSSPropertyValueSet::kParseError)
+ canvas_formatted_text_style_->SetNeedsStyleRecalc();
+ return result != MutableCSSPropertyValueSet::kParseError;
+}
+
+void CanvasFormattedTextStylePropertyMap::SetCustomProperty(const AtomicString&,
+ const CSSValue&) {
+ // Custom properties are not supported on CanvasFormattedText
+ NOTREACHED();
+}
+
+void CanvasFormattedTextStylePropertyMap::RemoveProperty(
+ CSSPropertyID property_id) {
+ bool did_change = css_property_value_set_->RemoveProperty(property_id);
+ if (did_change) {
+ canvas_formatted_text_style_->SetNeedsStyleRecalc();
+ }
+}
+
+void CanvasFormattedTextStylePropertyMap::RemoveCustomProperty(
+ const AtomicString&) {
+ // Custom properties are not supported on CanvasFormattedText
+ NOTREACHED();
+}
+
+void CanvasFormattedTextStylePropertyMap::RemoveAllProperties() {
+ css_property_value_set_->Clear();
+ canvas_formatted_text_style_->SetNeedsStyleRecalc();
+}
+
+String CanvasFormattedTextStylePropertyMap::SerializationForShorthand(
+ const CSSProperty& property) const {
+ DCHECK(property.IsShorthand());
+ return StylePropertySerializer(*css_property_value_set_)
+ .SerializeShorthand(property.PropertyID());
+}
+
+} // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.h
new file mode 100644
index 0000000..87df308
--- /dev/null
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.h
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_FORMATTED_TEXT_STYLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_FORMATTED_TEXT_STYLE_H_
+
+#include "third_party/blink/renderer/core/css/css_property_value_set.h"
+#include "third_party/blink/renderer/core/css/cssom/style_property_map.h"
+
+namespace blink {
+
+class CanvasFormattedTextStylePropertyMap;
+class FontDescription;
+
+class CanvasFormattedTextStyle : public GarbageCollectedMixin {
+ DISALLOW_NEW();
+
+ public:
+ explicit CanvasFormattedTextStyle(bool is_text_run)
+ : is_text_run_(is_text_run) {}
+ StylePropertyMap* styleMap();
+
+ virtual void SetNeedsStyleRecalc() = 0;
+ const CSSPropertyValueSet* GetCssPropertySet() const;
+
+ void Trace(Visitor* visitor) const override;
+
+ private:
+ bool is_text_run_;
+ Member<CanvasFormattedTextStylePropertyMap> style_map_;
+};
+
+class CanvasFormattedTextStylePropertyMap final : public StylePropertyMap {
+ public:
+ explicit CanvasFormattedTextStylePropertyMap(bool is_text_run,
+ CanvasFormattedTextStyle*);
+ CanvasFormattedTextStylePropertyMap(
+ const CanvasFormattedTextStylePropertyMap&) = delete;
+ CanvasFormattedTextStylePropertyMap& operator=(
+ const CanvasFormattedTextStylePropertyMap&) = delete;
+
+ void Trace(Visitor* visitor) const override {
+ visitor->Trace(css_property_value_set_);
+ visitor->Trace(canvas_formatted_text_style_);
+ StylePropertyMap::Trace(visitor);
+ }
+
+ unsigned int size() const final {
+ return css_property_value_set_->PropertyCount();
+ }
+
+ const CSSPropertyValueSet* GetCssPropertySet() const {
+ return css_property_value_set_;
+ }
+
+ protected:
+ const CSSValue* GetProperty(CSSPropertyID) const override;
+ const CSSValue* GetCustomProperty(const AtomicString&) const override;
+ void ForEachProperty(const IterationCallback&) override;
+ void SetProperty(CSSPropertyID, const CSSValue&) override;
+ bool SetShorthandProperty(CSSPropertyID,
+ const String&,
+ SecureContextMode) override;
+ void SetCustomProperty(const AtomicString&, const CSSValue&) override;
+ void RemoveProperty(CSSPropertyID) override;
+ void RemoveCustomProperty(const AtomicString&) override;
+ void RemoveAllProperties() final;
+
+ String SerializationForShorthand(const CSSProperty&) const final;
+
+ private:
+ Member<MutableCSSPropertyValueSet> css_property_value_set_;
+ WeakMember<CanvasFormattedTextStyle> canvas_formatted_text_style_;
+ bool is_text_run_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_FORMATTED_TEXT_STYLE_H_
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.idl
new file mode 100644
index 0000000..8b2f6f55
--- /dev/null
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text_style.idl
@@ -0,0 +1,6 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+interface mixin CanvasFormattedTextStyle{
+ [SameObject] readonly attribute StylePropertyMap styleMap;
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index cc3cbb7..8b1bfba3 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -925,7 +925,8 @@
CanvasFormattedText* formatted_text,
double x,
double y,
- double wrap_width) {
+ double wrap_width,
+ double height) {
if (!formatted_text)
return;
// TODO(crbug.com/1234113): Instrument new canvas APIs.
@@ -937,7 +938,7 @@
gfx::RectF bounds;
sk_sp<PaintRecord> recording = formatted_text->PaintFormattedText(
canvas()->GetDocument(), GetState().GetFontDescription(), x, y,
- wrap_width, bounds);
+ wrap_width, height, bounds);
Draw<OverdrawOp::kNone>(
[recording](cc::PaintCanvas* c,
const cc::PaintFlags* flags) // draw lambda
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index 6ae2ce4..89d3eb07 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -139,7 +139,8 @@
void fillFormattedText(CanvasFormattedText* formatted_text,
double x,
double y,
- double wrap_width);
+ double wrap_width,
+ double height = kIndefiniteSize);
void drawFocusIfNeeded(Element*);
void drawFocusIfNeeded(Path2D*, Element*);
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
index 3e96a86..34e7bba 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.idl
@@ -123,7 +123,7 @@
TextMetrics measureText(DOMString text);
// Render entire CanvasFormattedText with line wrapping (one-shot)
- [RuntimeEnabled=CanvasFormattedText] void fillFormattedText(CanvasFormattedText formattedText, double x, double y, double wrapWidth);
+ [RuntimeEnabled=CanvasFormattedText] void fillFormattedText(CanvasFormattedText formattedText, double x, double y, double wrapWidth, optional double height);
// drawing images
[RaisesException] void drawImage(CanvasImageSource image, unrestricted double x, unrestricted double y);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e77c1ad..47b308dc 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -358,6 +358,7 @@
},
{
name: "CanvasFormattedText",
+ depends_on: ["LayoutNG"],
status: "experimental",
},
{
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 1b05c41..ecf43e7 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -1529,6 +1529,8 @@
# CanvasFormattedText is supported only through layout NG
crbug.com/1176933 fast/canvas/canvas-formattedtext-1.html [ Skip ]
crbug.com/1176933 fast/canvas/canvas-formattedtext-2.html [ Skip ]
+crbug.com/1176933 fast/canvas-api/canvas-formattedtext.html [ Skip ]
+crbug.com/1176933 wpt_internal/canvas/canvas-formattedtext-style.html [ Skip ]
crbug.com/1176933 virtual/gpu/fast/canvas/canvas-formattedtext-1.html [ Skip ]
crbug.com/1176933 virtual/gpu/fast/canvas/canvas-formattedtext-2.html [ Skip ]
@@ -1550,7 +1552,6 @@
crbug.com/591099 external/wpt/css/css-writing-modes/alt-display-vertical-001-manual.html [ Failure ]
crbug.com/591099 external/wpt/css/css-writing-modes/tooltip-display-vertical-001-manual.html [ Failure ]
crbug.com/591099 external/wpt/html/editing/dnd/platform/close-drag-005-manual.html [ Failure ]
-crbug.com/591099 fast/canvas-api/canvas-formattedtext.html [ Crash ]
crbug.com/591099 http/tests/devtools/elements/highlight/highlight-css-flex-item.js [ Failure ]
# Failures accumulated until 2021-5-11
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 381dd32..be8fde7 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -1048,6 +1048,7 @@
interface CanvasFormattedText
attribute @@toStringTag
getter length
+ getter styleMap
method @@iterator
method appendRun
method constructor
@@ -1057,6 +1058,7 @@
method setRun
interface CanvasFormattedTextRun
attribute @@toStringTag
+ getter styleMap
getter text
method constructor
setter text
diff --git a/third_party/blink/web_tests/wpt_internal/canvas/canvas-formattedtext-style.html b/third_party/blink/web_tests/wpt_internal/canvas/canvas-formattedtext-style.html
new file mode 100644
index 0000000..8123d89
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/canvas/canvas-formattedtext-style.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<link rel="match" href="references/canvas-formattedtext-style-expected.html">
+<!-- Mac/GPU tests will have slightly different underline/strikethrough rendering -->
+<meta name=fuzzy content="maxDifference=255;totalPixels=12">
+<html>
+<head>
+ <style>
+ h3 {
+ margin: 0px;
+ }
+ canvas {
+ border: 1px solid black;
+ display: block;
+ margin-bottom: 2px;
+ }
+ </style>
+</head>
+<body>
+ <script>
+ function makeContext(id) {
+ var canvas = document.getElementById(id);
+ var context = canvas.getContext("2d", { alpha: true });
+ context.fillStyle = "#000000";
+ context.clearRect(0, 0, canvas.width, canvas.height);
+ context.fillStyle = "#FFFFFF";
+ context.fillRect(0, 0, canvas.width, canvas.height);
+ context.font = "20px Arial";
+ return context;
+ }
+ function makeRun(text, prop, value) {
+ text.appendRun(new CanvasFormattedTextRun(" "));
+ var textRun = new CanvasFormattedTextRun(prop + ":" + value);
+ textRun.styleMap.set(prop, value);
+ text.appendRun(textRun);
+ }
+ </script>
+ <h3>Test Font Related Styles</h3>
+ <canvas width=500 height=100 id="target1"></canvas>
+ <script>
+ function testFontRelatedStyles() {
+ var context = makeContext("target1");
+ var text = new CanvasFormattedText();
+ text.appendRun(new CanvasFormattedTextRun('Hello World !'));
+ makeRun(text, 'color', 'blue')
+ makeRun(text, 'text-decoration', 'underline')
+ makeRun(text, 'text-decoration', 'line-through')
+ makeRun(text, 'font-family', 'Times New Roman')
+ makeRun(text, 'font-kerning', 'none')
+ makeRun(text, 'font-kerning', 'normal')
+ makeRun(text, 'font-size', '10px')
+
+ // Test font-size-dependant lengths
+ text.appendRun(new CanvasFormattedTextRun(" "));
+ var textRun = new CanvasFormattedTextRun("1em");
+ textRun.styleMap.set('text-decoration', 'underline');
+ textRun.styleMap.set('text-decoration-thickness', '1em');
+ textRun.styleMap.set('text-underline-offset', '-12px');
+ textRun.styleMap.set('font-size', '25px');
+ text.appendRun(textRun);
+
+ context.fillFormattedText(text, 0, 0, 500);
+ };
+ testFontRelatedStyles();
+ </script>
+ <canvas width=500 height=30 id="target7"></canvas>
+ <script>
+ function testFontRelatedStyles2() {
+ var context = makeContext("target7");
+ var text = new CanvasFormattedText();
+ makeRun(text, 'font-weight', 'bold')
+ context.fillFormattedText(text, 0, 0, 500);
+ };
+ testFontRelatedStyles2();
+ </script>
+ <canvas width=500 height=30 id="target8"></canvas>
+ <script>
+ function testFontRelatedStyles3() {
+ var context = makeContext("target8");
+ var text = new CanvasFormattedText();
+ makeRun(text, 'font-stretch', 'condensed')
+ makeRun(text, 'font-variant-caps', 'small-caps');
+ context.fillFormattedText(text, 0, 0, 500);
+ };
+ testFontRelatedStyles3();
+ </script>
+ <canvas width=500 height=30 id="target9"></canvas>
+ <script>
+ function testFontRelatedStyles3() {
+ var context = makeContext("target9");
+ var text = new CanvasFormattedText();
+ makeRun(text, 'font-style', 'italic')
+ context.fillFormattedText(text, 0, 0, 500);
+ };
+ testFontRelatedStyles3();
+ </script>
+
+ <h3>Test RTL</h3>
+ <canvas width=500 height=30 id="target2"></canvas>
+ <script>
+ {
+ var context = makeContext("target2");
+ var text = new CanvasFormattedText();
+ var textRun = new CanvasFormattedTextRun('Hello World !');
+ text.appendRun(textRun);
+ text.styleMap.set('direction', 'rtl');
+ text.styleMap.set('width', '500px');
+ context.fillFormattedText(text, 0, 0, 500);
+ }
+ </script>
+
+ <h3>Test align</h3>
+ <canvas width=500 height=50 id="target3"></canvas>
+ <script>
+ {
+ var context = makeContext("target3");
+ var text = new CanvasFormattedText();
+ var textRun = new CanvasFormattedTextRun('Hello World ! Hello World ! Hello World ! Hello World ! Hello World !');
+ text.appendRun(textRun);
+ text.styleMap.set('text-align', 'center');
+ text.styleMap.set('width', '500px');
+ context.fillFormattedText(text, 0, 0, 500);
+ }
+ </script>
+
+ <h3>Test Writing-Mode</h3>
+ <canvas width=100 height=70 id="target4" style="border:1px solid black; display:inline-block;"></canvas>
+ <script>
+ {
+ var context = makeContext("target4");
+ var text = new CanvasFormattedText();
+ var textRun = new CanvasFormattedTextRun('ABC DEF GHI JKL MNO PQR');
+ text.appendRun(textRun);
+ text.styleMap.set('width', '100px');
+ text.styleMap.set('writing-mode', 'vertical-lr');
+ context.fillFormattedText(text, 0, 0, 100, 70);
+ }
+ </script>
+ <canvas width=100 height=70 id="target5" style="border:1px solid black; display:inline-block;"></canvas>
+ <script>
+ {
+ var context = makeContext("target5");
+ var text = new CanvasFormattedText();
+ var textRun = new CanvasFormattedTextRun('ABC DEF GHI JKL MNO PQR');
+ text.appendRun(textRun);
+ text.styleMap.set('width', '100px');
+ text.styleMap.set('writing-mode', 'vertical-rl');
+ context.fillFormattedText(text, 0, 0, 100, 70);
+ }
+ </script>
+ <canvas width=100 height=70 id="target6" style="border:1px solid black; display:inline-block;"></canvas>
+ <script>
+ {
+ var context = makeContext("target6");
+ var text = new CanvasFormattedText();
+ var textRun = new CanvasFormattedTextRun('ABC DEF GHI JKL MNO PQR');
+ text.appendRun(textRun);
+ text.styleMap.set('width', '100px');
+ text.styleMap.set('writing-mode', 'vertical-rl');
+ text.styleMap.set('text-orientation', 'upright');
+ context.fillFormattedText(text, 0, 0, 100, 70);
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/wpt_internal/canvas/references/canvas-formattedtext-style-expected.html b/third_party/blink/web_tests/wpt_internal/canvas/references/canvas-formattedtext-style-expected.html
new file mode 100644
index 0000000..d9b49423
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/canvas/references/canvas-formattedtext-style-expected.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ h3 {
+ margin: 0px;
+ }
+ div {
+ width: 500px;
+ height: 100px;
+ border: 1px solid black;
+ font: 20px Arial;
+ vertical-align: top;
+ margin-bottom: 2px;
+ }
+
+ span {
+ vertical-align: top;
+ }
+
+ .ib {
+ width: 100px;
+ height: 70px;
+ display: inline-block;
+ overflow: hidden;
+ }
+ </style>
+</head>
+<body>
+ <h3>Test Font Related Styles</h3>
+ <div id="target1">
+ <span>Hello World !</span>
+ </div>
+ <script>
+ function makeRun(id, prop, value) {
+ document.getElementById(id).innerHTML += " <span style='" + prop + ":"
+ + value + "' >" + prop + ":" + value + "</span>";
+ }
+ function testFontRelatedStyles() {
+ makeRun("target1", 'color', 'blue')
+ makeRun("target1", 'text-decoration', 'underline')
+ makeRun("target1", 'text-decoration', 'line-through')
+ makeRun("target1", 'font-family', 'Times New Roman')
+ makeRun("target1", 'font-kerning', 'none')
+ makeRun("target1", 'font-kerning', 'normal')
+ makeRun("target1", 'font-size', '10px')
+ document.getElementById("target1").innerHTML += " <span style='text-decoration:underline; text-decoration-thickness:1em; text-underline-offset:-12px; font-size:25px;'>1em</span>"
+ };
+ testFontRelatedStyles();
+ </script>
+
+ <div id="target7" style="height:30px;">
+ </div>
+ <script>
+ function testFontRelatedStyles2() {
+ makeRun("target7", 'font-weight', 'bold')
+ };
+ testFontRelatedStyles2();
+ </script>
+
+ <div id="target8" style="height:30px;">
+ </div>
+ <script>
+ function testFontRelatedStyles3() {
+ makeRun("target8", 'font-stretch', 'condensed')
+ makeRun("target8", 'font-variant-caps', 'small-caps');
+ };
+ testFontRelatedStyles3();
+ </script>
+
+ <div id="target9" style="height:30px;">
+ </div>
+ <script>
+ function testFontRelatedStyles4() {
+ makeRun("target9", 'font-style', 'italic')
+ };
+ testFontRelatedStyles4();
+ </script>
+
+ <h3>Test RTL</h3>
+ <DIV style="direction:rtl; height:30px;"><span>Hello World !</span></DIV>
+
+ <h3>Test align</h3>
+ <DIV style="text-align:center; height:50px;">
+ <span>
+ Hello World ! Hello World ! Hello World ! Hello World ! Hello World !
+ </span>
+ </DIV>
+
+ <h3>Test Writing-Mode</h3>
+ <DIV class="ib" style="writing-mode:vertical-lr">
+ ABC DEF GHI JKL MNO PQR
+ </DIV>
+ <DIV class="ib" style="writing-mode:vertical-rl">
+ ABC DEF GHI JKL MNO PQR
+ </DIV>
+ <DIV class="ib" style="writing-mode:vertical-rl; text-orientation: upright;">
+ ABC DEF GHI JKL MNO PQR
+ </DIV>
+</body>
+</html>
\ No newline at end of file