summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc')
-rw-r--r--chromium/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc187
1 files changed, 187 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc b/chromium/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
new file mode 100644
index 00000000000..87478d8b386
--- /dev/null
+++ b/chromium/third_party/blink/renderer/modules/canvas/canvas2d/canvas_formatted_text.cc
@@ -0,0 +1,187 @@
+// Copyright 2020 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.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"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h"
+#include "third_party/blink/renderer/core/paint/paint_info.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
+
+namespace blink {
+
+void CanvasFormattedText::Trace(Visitor* visitor) const {
+ visitor->Trace(text_runs_);
+ ScriptWrappable::Trace(visitor);
+}
+
+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);
+ // 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();
+}
+
+LayoutBlockFlow* CanvasFormattedText::GetLayoutBlock(
+ Document& document,
+ const FontDescription& defaultFont) {
+ scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
+ style->SetDisplay(EDisplay::kBlock);
+ style->SetFontDescription(defaultFont);
+ block_->SetStyle(style);
+ return block_;
+}
+
+CanvasFormattedTextRun* CanvasFormattedText::appendRun(
+ CanvasFormattedTextRun* run,
+ ExceptionState& exception_state) {
+ if (!CheckRunIsNotParented(run, &exception_state))
+ return nullptr;
+ text_runs_.push_back(run);
+ 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,
+ double x,
+ double y,
+ double wrap_width,
+ FloatRect& bounds) {
+ LayoutBlockFlow* block = GetLayoutBlock(document, font);
+ NGBlockNode block_node(block);
+ NGInlineNode node(block);
+ // Call IsEmptyInline to force prepare layout.
+ if (node.IsEmptyInline())
+ return nullptr;
+
+ // 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},
+ /* is_new_fc */ true);
+ LayoutUnit available_logical_width(wrap_width);
+ LogicalSize available_size = {available_logical_width, kIndefiniteSize};
+ builder.SetAvailableSize(available_size);
+ NGConstraintSpace space = builder.ToConstraintSpace();
+ scoped_refptr<const NGLayoutResult> block_results =
+ block_node.Layout(space, nullptr);
+ const auto& fragment =
+ To<NGPhysicalBoxFragment>(block_results->PhysicalFragment());
+ block->RecalcInlineChildrenVisualOverflow();
+ bounds = FloatRect(block->PhysicalVisualOverflowRect());
+
+ PaintController paint_controller(PaintController::Usage::kTransient);
+ paint_controller.UpdateCurrentPaintChunkProperties(nullptr,
+ PropertyTreeState::Root());
+ GraphicsContext graphics_context(paint_controller);
+ PhysicalOffset physical_offset((LayoutUnit(x)), (LayoutUnit(y)));
+ NGBoxFragmentPainter box_fragment_painter(fragment);
+ PaintInfo paint_info(graphics_context, CullRect::Infinite(),
+ PaintPhase::kForeground, kGlobalPaintNormalPhase,
+ kPaintLayerPaintingRenderingClipPathAsMask |
+ kPaintLayerPaintingRenderingResourceSubtree);
+ box_fragment_painter.PaintObject(paint_info, physical_offset);
+ paint_controller.CommitNewDisplayItems();
+ paint_controller.FinishCycle();
+ sk_sp<PaintRecord> recording =
+ paint_controller.GetPaintArtifact().GetPaintRecord(
+ PropertyTreeState::Root());
+ return recording;
+}
+
+} // namespace blink