Skip to content

Commit

Permalink
Add Ability to mutate run collection on the CanvasFormattedText
Browse files Browse the repository at this point in the history
This change adds a set of APIs to allow manipulating the run
collection on a CanvasFormattedText. set, append, insert, delete
, get run are implemented.

One major change here is to introduce CanvasFormattedTextRun as an
interface. This is because we want to share identitiy of a run between
javascript and C++, so that in the future we can support things like
changing the color/font on a text run efficiently. That is consider the
following snippet

1  var a = canvas_formatted_text[0];
2  a.setColor("Blue");
3  canvas_formatted_text[0] = a;

if text runs are a dictionary, at line 3 the C++ code would see a
totally new textrun and will not be able to re-use the layout text
that backs it. This move to make text runs a object type, also results
in making the above code easier to write as just

1 canvas_formatted_text[0].setColor("Blue")

One other decision that was made, was to prohibit adding runs already
in a CanvasFormattedText the same or another CanvasFormattedText.
Doing so will throw a javascript exception. This is because supporting
it would force us to make handling cases like

1 canvas_formatted_text[0] = canvas_formatted_text[1]; //not allowed

tricky and non intuitive.

-If we supported it by removing the run from index 1 and replacing the
 run in index 0, the developer may not expect that.
-We can't support it by having both 0, 1 point to the same text run
 since layout block flow does not work that way.
-The final option of creating a copy of 1 and placing it in 0 also may
 be un-intutive for the developer, setting color on 1 will not change
 color on 0 for example.

Bug: 1155764
Change-Id: I65038365468e8b4fe3f894ca64d79042e2e60acd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2693694
Commit-Queue: Sushanth Rajasankar <Sushraja@microsoft.com>
Reviewed-by: Fernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#857794}
  • Loading branch information
sushraja-msft authored and Chromium LUCI CQ committed Feb 25, 2021
1 parent 8031f3f commit 54637bb
Show file tree
Hide file tree
Showing 14 changed files with 648 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,7 @@ generated_interface_sources_in_modules = [
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_capture_media_stream_track.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_capture_media_stream_track.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_formatted_text.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_formatted_text_run.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_gradient.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_gradient.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_canvas_pattern.cc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,7 @@ const char kTextControlChanged[] = "Text control changed";
const char kSvgChanged[] = "SVG changed";
const char kScrollbarChanged[] = "Scrollbar changed";
const char kDisplayLock[] = "Display lock";
const char kCanvasFormattedTextRunChange[] = "CanvasFormattedText runs changed";
} // namespace layout_invalidation_reason

std::unique_ptr<TracedValue> inspector_layout_invalidation_tracking_event::Data(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ extern const char kTextControlChanged[];
extern const char kSvgChanged[];
extern const char kScrollbarChanged[];
extern const char kDisplayLock[];
extern CORE_EXPORT const char kCanvasFormattedTextRunChange[];
} // namespace layout_invalidation_reason

// LayoutInvalidationReasonForTracing is strictly for tracing. Blink logic must
Expand Down
2 changes: 2 additions & 0 deletions third_party/blink/renderer/modules/canvas/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ blink_modules_sources("canvas") {
"canvas2d/base_rendering_context_2d.h",
"canvas2d/canvas_formatted_text.cc",
"canvas2d/canvas_formatted_text.h",
"canvas2d/canvas_formatted_text_run.cc",
"canvas2d/canvas_formatted_text_run.h",
"canvas2d/canvas_gradient.cc",
"canvas2d/canvas_gradient.h",
"canvas2d/canvas_image_source_util.cc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.h"
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_child_layout_context.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
Expand All @@ -23,15 +24,37 @@ void CanvasFormattedText::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
}

CanvasFormattedText::CanvasFormattedText(Document* document) {
CanvasFormattedText* CanvasFormattedText::Create(
ExecutionContext* execution_context,
const String text) {
CanvasFormattedText* canvas_formatted_text =
MakeGarbageCollected<CanvasFormattedText>(execution_context);
CanvasFormattedTextRun* run =
MakeGarbageCollected<CanvasFormattedTextRun>(execution_context, text);
canvas_formatted_text->text_runs_.push_back(run);
canvas_formatted_text->block_->AddChild(run->GetLayoutObject());
return canvas_formatted_text;
}

CanvasFormattedText::CanvasFormattedText(ExecutionContext* execution_context) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetDisplay(EDisplay::kBlock);
block_ =
LayoutBlockFlow::CreateAnonymous(document, style, LegacyLayout::kAuto);
// 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.
auto* window = To<LocalDOMWindow>(execution_context);
block_ = LayoutBlockFlow::CreateAnonymous(window->document(), style,
LegacyLayout::kAuto);
block_->SetIsLayoutNGObjectForCanvasFormattedText(true);
}

void CanvasFormattedText::Dispose() {
// Detach all the anonymous children we added, since block_->Destroy will
// destroy them. We want the lifetime of the children to be managed by their
// corresponding CanvasFormattedTextRun and not destroyed at this point.
while (block_->FirstChild()) {
block_->RemoveChild(block_->FirstChild());
}
AllowDestroyingLayoutObjectInFinalizerScope scope;
if (block_)
block_->Destroy();
Expand All @@ -48,18 +71,69 @@ LayoutBlockFlow* CanvasFormattedText::GetLayoutBlock(
}

CanvasFormattedTextRun* CanvasFormattedText::appendRun(
CanvasFormattedTextRun* run) {
CanvasFormattedTextRun* run,
ExceptionState& exception_state) {
if (!CheckRunIsNotParented(run, &exception_state))
return nullptr;
text_runs_.push_back(run);
scoped_refptr<ComputedStyle> text_style = ComputedStyle::Create();
text_style->SetDisplay(EDisplay::kInline);
LayoutText* text =
LayoutText::CreateAnonymous(block_->GetDocument(), std::move(text_style),
run->text().Impl(), LegacyLayout::kAuto);
text->SetIsLayoutNGObjectForCanvasFormattedText(true);
block_->AddChild(text);
block_->AddChild(run->GetLayoutObject());
return run;
}

CanvasFormattedTextRun* CanvasFormattedText::setRun(
unsigned index,
CanvasFormattedTextRun* run,
ExceptionState& exception_state) {
if (!CheckRunsIndexBound(index, &exception_state) ||
!CheckRunIsNotParented(run, &exception_state))
return nullptr;
block_->AddChild(run->GetLayoutObject(),
text_runs_[index]->GetLayoutObject());
block_->RemoveChild(text_runs_[index]->GetLayoutObject());
text_runs_[index] = run;
return text_runs_[index];
}

CanvasFormattedTextRun* CanvasFormattedText::insertRun(
unsigned index,
CanvasFormattedTextRun* run,
ExceptionState& exception_state) {
if (!CheckRunIsNotParented(run, &exception_state))
return nullptr;
if (index == text_runs_.size())
return appendRun(run, exception_state);
if (!CheckRunsIndexBound(index, &exception_state))
return nullptr;
block_->AddChild(run->GetLayoutObject(),
text_runs_[index]->GetLayoutObject());
text_runs_.insert(index, run);
return text_runs_[index];
}

void CanvasFormattedText::deleteRun(unsigned index,
unsigned length,
ExceptionState& exception_state) {
if (!CheckRunsIndexBound(index, &exception_state))
return;
// Protect against overflow, do not perform math like index + length <
// text_runs_.size(). The length passed in can be close to INT_MAX.
if (text_runs_.size() - index < length) {
exception_state.ThrowDOMException(
DOMExceptionCode::kIndexSizeError,
ExceptionMessages::IndexExceedsMaximumBound("length", length,
text_runs_.size() - index));
return;
}

for (wtf_size_t i = index; i < index + length; i++) {
block_->RemoveChild(text_runs_[i]->GetLayoutObject());
}
block_->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kCanvasFormattedTextRunChange);
text_runs_.EraseAt(static_cast<wtf_size_t>(index),
static_cast<wtf_size_t>(length));
}

sk_sp<PaintRecord> CanvasFormattedText::PaintFormattedText(
Document& document,
const FontDescription& font,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,22 @@ class MODULES_EXPORT CanvasFormattedText final : public ScriptWrappable {

public:
static CanvasFormattedText* Create(ExecutionContext* execution_context) {
auto* window = To<LocalDOMWindow>(execution_context);
return MakeGarbageCollected<CanvasFormattedText>(window->document());
return MakeGarbageCollected<CanvasFormattedText>(execution_context);
}

explicit CanvasFormattedText(Document*);
static CanvasFormattedText* Create(ExecutionContext* execution_context,
const String text);

static CanvasFormattedText* Create(ExecutionContext* execution_context,
CanvasFormattedTextRun* run,
ExceptionState& exception_state) {
CanvasFormattedText* canvas_formatted_text =
MakeGarbageCollected<CanvasFormattedText>(execution_context);
canvas_formatted_text->appendRun(run, exception_state);
return canvas_formatted_text;
}

explicit CanvasFormattedText(ExecutionContext* execution_context);
CanvasFormattedText(const CanvasFormattedText&) = delete;
CanvasFormattedText& operator=(const CanvasFormattedText&) = delete;

Expand All @@ -56,14 +67,45 @@ class MODULES_EXPORT CanvasFormattedText final : public ScriptWrappable {
return true;
}

bool CheckRunIsNotParented(CanvasFormattedTextRun* run,
ExceptionState* exception_state) const {
if (run->GetLayoutObject() && run->GetLayoutObject()->Parent()) {
if (exception_state) {
exception_state->ThrowDOMException(
DOMExceptionCode::kInvalidModificationError,
"The run is already a part of a formatted text. Remove it from "
"that formatted text before insertion.");
}
return false;
}
return true;
}

CanvasFormattedTextRun* getRun(unsigned index,
ExceptionState& exception_state) const {
if (!CheckRunsIndexBound(index, &exception_state))
return nullptr;
return text_runs_[index];
}

CanvasFormattedTextRun* appendRun(CanvasFormattedTextRun* run);
CanvasFormattedTextRun* appendRun(CanvasFormattedTextRun* run,
ExceptionState& exception_state);

CanvasFormattedTextRun* setRun(unsigned index,
CanvasFormattedTextRun* run,
ExceptionState& exception_state);

CanvasFormattedTextRun* insertRun(unsigned index,
CanvasFormattedTextRun* run,
ExceptionState& exception_state);

void deleteRun(unsigned index, ExceptionState& exception_state) {
deleteRun(index, 1, exception_state);
}

void deleteRun(unsigned index,
unsigned length,
ExceptionState& exception_state);

LayoutBlockFlow* GetLayoutBlock(Document& document,
const FontDescription& defaultFont);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@
]
interface CanvasFormattedText{
[CallWith=ExecutionContext] constructor();
[CallWith=ExecutionContext] constructor(DOMString text);
[CallWith=ExecutionContext, RaisesException] constructor(CanvasFormattedTextRun text);

[RaisesException] getter CanvasFormattedTextRun getRun(unsigned long index);
CanvasFormattedTextRun appendRun(CanvasFormattedTextRun newRun);
[RaisesException] CanvasFormattedTextRun appendRun(
CanvasFormattedTextRun newRun);
[RaisesException] setter CanvasFormattedTextRun setRun(
unsigned long index, CanvasFormattedTextRun run);
[RaisesException] CanvasFormattedTextRun insertRun(
unsigned long index, CanvasFormattedTextRun run);
[RaisesException] void deleteRun(unsigned long index);
[RaisesException] void deleteRun(unsigned long index, unsigned long length);

readonly attribute unsigned long length;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2021 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_run.h"

namespace blink {

CanvasFormattedTextRun::CanvasFormattedTextRun(
ExecutionContext* execution_context,
const String text)
: text_(text) {
scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
style->SetDisplay(EDisplay::kInline);
// 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.
auto* window = To<LocalDOMWindow>(execution_context);
layout_text_ =
LayoutText::CreateAnonymous(*(window->document()), std::move(style),
text.Impl(), LegacyLayout::kAuto);
layout_text_->SetIsLayoutNGObjectForCanvasFormattedText(true);
}

void CanvasFormattedTextRun::Dispose() {
AllowDestroyingLayoutObjectInFinalizerScope scope;
if (layout_text_)
layout_text_->Destroy();
}

void CanvasFormattedTextRun::Trace(Visitor* visitor) const {
ScriptWrappable::Trace(visitor);
}

} // namespace blink
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2021 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_RUN_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_FORMATTED_TEXT_RUN_H_

#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/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"

namespace blink {

class MODULES_EXPORT CanvasFormattedTextRun final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
USING_PRE_FINALIZER(CanvasFormattedTextRun, Dispose);

public:
static CanvasFormattedTextRun* Create(ExecutionContext* execution_context,
const String text) {
return MakeGarbageCollected<CanvasFormattedTextRun>(execution_context,
text);
}

CanvasFormattedTextRun(ExecutionContext*, const String text);
CanvasFormattedTextRun(const CanvasFormattedTextRun&) = delete;
CanvasFormattedTextRun& operator=(const CanvasFormattedTextRun&) = delete;

String text() const { return text_; }
void setText(const String text) { text_ = text; }

unsigned length() const { return text_.length(); }

LayoutText* GetLayoutObject() { return layout_text_; }

void Trace(Visitor* visitor) const override;

void Dispose();

private:
String text_;

LayoutText* layout_text_;
};

} // namespace blink

#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_CANVAS2D_CANVAS_FORMATTED_TEXT_RUN_H_
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Copyright 2021 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.
[
RuntimeEnabled=CanvasFormattedText,
Exposed=Window
]
interface CanvasFormattedTextRun{
[CallWith = ExecutionContext] constructor(DOMString text);

dictionary CanvasFormattedTextRun {
required DOMString text;
DOMString font;
DOMString color;
attribute DOMString text;
};
2 changes: 1 addition & 1 deletion third_party/blink/renderer/modules/canvas/idls.gni
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import("//third_party/blink/renderer/config.gni")

modules_idl_files = [
"canvas2d/canvas_formatted_text.idl",
"canvas2d/canvas_formatted_text_run.idl",
"canvas2d/canvas_gradient.idl",
"canvas2d/canvas_pattern.idl",
"canvas2d/canvas_rendering_context_2d.idl",
Expand All @@ -15,7 +16,6 @@ modules_idl_files = [
]

modules_dictionary_idl_files = [
"canvas2d/canvas_formatted_text_run.idl",
"canvas2d/canvas_rendering_context_2d_settings.idl",
"canvas2d/hit_region_options.idl",
"htmlcanvas/canvas_context_creation_attributes_module.idl",
Expand Down

0 comments on commit 54637bb

Please sign in to comment.