Commit b0b6da09 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Introduce CachedTextInputInfo class for InputMethodController

This patch Introduces |CachedTextInputInfo| class to cache text content
and text offsets of selection and IME composition to avoid redundant
calculation of them for improving responsiveness.

Background:
Browser process calls |InputMethodController::TextInputInfo()| for every
frame (16ms) and |GetSelectionOffsets()| for every selection changes.
Without caching |TextInputInfo|, these functions do text serializing root
editable element or document body for every time even if layout tree isn't
changed.

Note: |testRunner| doesn't call |GetSelectionOffsets()| because it is
called in |RenderFrameImpl::SyncSelectionIfRequired()| and |testRunner|
doesn't use it.

Note: Speed of |TextInputInfo()| doesn't appeared in performance test,
because it is called in |WidgetBase::WillBeginMainFrame()|. So repeated
call of |selection.modify()| in JavaScript doesn't call it.

Bug: 875155, 892513
Change-Id: Ib7de3849fd94af88db743806e6f8b1624cd944bc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2505348
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#824344}
parent 728b1c60
...@@ -143,6 +143,8 @@ blink_core_sources_editing = [ ...@@ -143,6 +143,8 @@ blink_core_sources_editing = [
"frame_selection.h", "frame_selection.h",
"granularity_strategy.cc", "granularity_strategy.cc",
"granularity_strategy.h", "granularity_strategy.h",
"ime/cached_text_input_info.cc",
"ime/cached_text_input_info.h",
"ime/edit_context.cc", "ime/edit_context.cc",
"ime/edit_context.h", "ime/edit_context.h",
"ime/ime_text_span.cc", "ime/ime_text_span.cc",
...@@ -371,6 +373,7 @@ blink_core_tests_editing = [ ...@@ -371,6 +373,7 @@ blink_core_tests_editing = [
"frame_selection_test.cc", "frame_selection_test.cc",
"granularity_strategy_test.cc", "granularity_strategy_test.cc",
"hit_testing_bidi_test.cc", "hit_testing_bidi_test.cc",
"ime/cached_text_input_info_test.cc",
"ime/ime_text_span_test.cc", "ime/ime_text_span_test.cc",
"ime/input_method_controller_test.cc", "ime/input_method_controller_test.cc",
"inline_box_position_test.cc", "inline_box_position_test.cc",
......
// 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/core/editing/ime/cached_text_input_info.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
// static
TextIteratorBehavior CachedTextInputInfo::Behavior() {
return TextIteratorBehavior::Builder()
.SetEmitsObjectReplacementCharacter(true)
.SetEmitsSpaceForNbsp(true)
.Build();
}
void CachedTextInputInfo::ClearIfNeeded(const LayoutObject& layout_object) {
if (layout_object_ != &layout_object)
return;
container_ = nullptr;
layout_object_ = nullptr;
text_ = g_empty_string;
composition_.Clear();
selection_.Clear();
}
void CachedTextInputInfo::DidLayoutSubtree(const LayoutObject& layout_object) {
// <div style="contain:strict; ...">abc</div> reaches here.
if (!container_)
return;
Node* const node = layout_object.NonPseudoNode();
if (!node)
return;
const ContainerNode* const container =
RootEditableElementOrTreeScopeRootNodeOf(Position(node, 0));
if (!container || !container->GetLayoutObject())
return;
ClearIfNeeded(*container->GetLayoutObject());
}
void CachedTextInputInfo::DidUpdateLayout(const LayoutObject& layout_object) {
ClearIfNeeded(layout_object);
}
void CachedTextInputInfo::EnsureCached(const ContainerNode& container) const {
if (IsValidFor(container))
return;
offset_map_.clear();
container_ = &container;
layout_object_ = container.GetLayoutObject();
composition_.Clear();
selection_.Clear();
text_ = g_empty_string;
TextIteratorAlgorithm<EditingStrategy> it(
EphemeralRange::RangeOfContents(container), Behavior());
if (it.AtEnd())
return;
const bool needs_text = HasEditableStyle(*container_);
// The initial buffer size can be critical for performance:
// https://bugs.webkit.org/show_bug.cgi?id=81192
constexpr unsigned kInitialCapacity = 1 << 15;
StringBuilder builder;
if (needs_text)
builder.ReserveCapacity(kInitialCapacity);
const Node* last_text_node = nullptr;
unsigned length = 0;
for (; !it.AtEnd(); it.Advance()) {
const Node* node = it.GetTextState().PositionNode();
if (last_text_node != node && IsA<Text>(node)) {
last_text_node = node;
offset_map_.insert(To<Text>(node), length);
}
if (needs_text)
it.GetTextState().AppendTextToStringBuilder(builder);
length += it.GetTextState().length();
}
if (!builder.IsEmpty())
text_ = builder.ToString();
}
PlainTextRange CachedTextInputInfo::GetComposition(
const EphemeralRange& range) const {
DCHECK(container_);
return GetPlainTextRangeWithCache(range, &composition_);
}
PlainTextRange CachedTextInputInfo::GetPlainTextRangeWithCache(
const EphemeralRange& range,
CachedPlainTextRange* text_range) const {
if (!text_range->IsValidFor(range))
text_range->Set(range, GetPlainTextRange(range));
return text_range->Get();
}
PlainTextRange CachedTextInputInfo::GetPlainTextRange(
const EphemeralRange& range) const {
if (range.IsNull())
return PlainTextRange();
const unsigned start_offset = RangeLength(
EphemeralRange(Position(*container_, 0), range.StartPosition()));
const unsigned end_offset =
range.IsCollapsed() ? start_offset
: RangeLength(EphemeralRange(Position(*container_, 0),
range.EndPosition()));
DCHECK_EQ(static_cast<unsigned>(TextIterator::RangeLength(
EphemeralRange(Position(*container_, 0), range.EndPosition()),
Behavior())),
end_offset);
return PlainTextRange(start_offset, end_offset);
}
PlainTextRange CachedTextInputInfo::GetSelection(
const EphemeralRange& range) const {
DCHECK(container_);
return GetPlainTextRangeWithCache(range, &selection_);
}
String CachedTextInputInfo::GetText() const {
DCHECK(container_);
DCHECK(HasEditableStyle(*container_));
return text_;
}
bool CachedTextInputInfo::IsValidFor(const ContainerNode& container) const {
return container_ == container &&
layout_object_ == container.GetLayoutObject();
}
void CachedTextInputInfo::LayoutObjectWillBeDestroyed(
const LayoutObject& layout_object) {
ClearIfNeeded(layout_object);
}
unsigned CachedTextInputInfo::RangeLength(const EphemeralRange& range) const {
const Node* const node = range.EndPosition().AnchorNode();
if (range.StartPosition() == Position(*container_, 0) && IsA<Text>(node)) {
const auto it = offset_map_.find(To<Text>(node));
if (it != offset_map_.end()) {
const unsigned length =
it->value +
TextIterator::RangeLength(
EphemeralRange(Position(node, 0), range.EndPosition()),
Behavior());
DCHECK_EQ(
static_cast<unsigned>(TextIterator::RangeLength(range, Behavior())),
length)
<< it->value << " " << range;
return length;
}
}
return TextIterator::RangeLength(range, Behavior());
}
void CachedTextInputInfo::Trace(Visitor* visitor) const {
visitor->Trace(container_);
visitor->Trace(composition_);
visitor->Trace(offset_map_);
visitor->Trace(selection_);
}
void CachedTextInputInfo::CachedPlainTextRange::Clear() {
start_ = end_ = Position();
start_offset_ = end_offset_ = kNotFound;
}
PlainTextRange CachedTextInputInfo::CachedPlainTextRange::Get() const {
if (start_offset_ == kNotFound)
return PlainTextRange();
return PlainTextRange(start_offset_, end_offset_);
}
bool CachedTextInputInfo::CachedPlainTextRange::IsValidFor(
const EphemeralRange& range) const {
return range.StartPosition() == start_ && range.EndPosition() == end_;
}
void CachedTextInputInfo::CachedPlainTextRange::Set(
const EphemeralRange& range,
const PlainTextRange& text_range) {
start_ = range.StartPosition();
end_ = range.EndPosition();
if (text_range.IsNull()) {
start_offset_ = end_offset_ = kNotFound;
} else {
start_offset_ = text_range.Start();
end_offset_ = text_range.End();
}
}
void CachedTextInputInfo::CachedPlainTextRange::Trace(Visitor* visitor) const {
visitor->Trace(start_);
visitor->Trace(end_);
}
} // namespace blink
// 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_CACHED_TEXT_INPUT_INFO_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_CACHED_TEXT_INPUT_INFO_H_
#include "third_party/blink/renderer/core/editing/iterators/text_iterator_behavior.h"
#include "third_party/blink/renderer/core/editing/plain_text_range.h"
#include "third_party/blink/renderer/core/editing/position.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class LayoutObject;
// |CachedTextInputInfo| holds plain text, and plain text range for
// composition and selection in |element_| until layout of |element_|
// changed. This class also provides faster version of |PlaintTextRange|.
class CORE_EXPORT CachedTextInputInfo final {
DISALLOW_NEW();
public:
CachedTextInputInfo(const CachedTextInputInfo&) = delete;
CachedTextInputInfo() = default;
CachedTextInputInfo& operator=(const CachedTextInputInfo&) = delete;
void EnsureCached(const ContainerNode& element) const;
PlainTextRange GetComposition(const EphemeralRange& range) const;
PlainTextRange GetPlainTextRange(const EphemeralRange& range) const;
PlainTextRange GetSelection(const EphemeralRange& range) const;
String GetText() const;
bool IsValidFor(const ContainerNode& element) const;
// For cache invalidation
void DidLayoutSubtree(const LayoutObject& layout_object);
void DidUpdateLayout(const LayoutObject& layout_object);
void LayoutObjectWillBeDestroyed(const LayoutObject& layout_object);
void Trace(Visitor*) const;
private:
class CachedPlainTextRange final {
DISALLOW_NEW();
public:
CachedPlainTextRange(const CachedPlainTextRange&) = delete;
CachedPlainTextRange() = default;
CachedPlainTextRange& operator=(const CachedPlainTextRange&) = delete;
void Clear();
PlainTextRange Get() const;
bool IsValidFor(const EphemeralRange& range) const;
void Set(const EphemeralRange& range, const PlainTextRange& text_range);
void Trace(Visitor*) const;
private:
// |start_| and |end_| can be disconnected from document.
mutable Position start_;
mutable Position end_;
mutable unsigned start_offset_ = kNotFound;
mutable unsigned end_offset_ = kNotFound;
};
static TextIteratorBehavior Behavior();
void ClearIfNeeded(const LayoutObject& layout_object);
PlainTextRange GetPlainTextRangeWithCache(
const EphemeralRange& range,
CachedPlainTextRange* text_range) const;
unsigned RangeLength(const EphemeralRange& range) const;
mutable Member<const ContainerNode> container_;
mutable const LayoutObject* layout_object_ = nullptr;
// Records offset of text node from start of |container_|.
mutable HeapHashMap<Member<const Text>, unsigned> offset_map_;
mutable String text_;
mutable CachedPlainTextRange composition_;
mutable CachedPlainTextRange selection_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_CACHED_TEXT_INPUT_INFO_H_
// 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/core/editing/ime/cached_text_input_info.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/selection_template.h"
#include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
namespace blink {
class CachedTextInputInfoTest : public EditingTestBase {
protected:
CachedTextInputInfo& GetCachedTextInputInfo() {
return GetInputMethodController().GetCachedTextInputInfoForTesting();
}
InputMethodController& GetInputMethodController() {
return GetFrame().GetInputMethodController();
}
};
TEST_F(CachedTextInputInfoTest, Basic) {
GetFrame().Selection().SetSelectionAndEndTyping(
SetSelectionTextToBody("<div contenteditable id=\"sample\">a|b</div>"));
const Element& sample = *GetElementById("sample");
EXPECT_EQ(PlainTextRange(1, 1),
GetInputMethodController().GetSelectionOffsets());
EXPECT_EQ("ab", GetCachedTextInputInfo().GetText());
To<Text>(sample.firstChild())->appendData("X");
EXPECT_EQ(PlainTextRange(1, 1),
GetInputMethodController().GetSelectionOffsets());
EXPECT_EQ("abX", GetCachedTextInputInfo().GetText());
}
TEST_F(CachedTextInputInfoTest, RelayoutBoundary) {
InsertStyleElement(
"#sample { contain: strict; width: 100px; height: 100px; }");
GetFrame().Selection().SetSelectionAndEndTyping(SetSelectionTextToBody(
"<div contenteditable><div id=\"sample\">^a|b</div>"));
const Element& sample = *GetElementById("sample");
ASSERT_TRUE(sample.GetLayoutObject()->IsRelayoutBoundary());
EXPECT_EQ(PlainTextRange(0, 1),
GetInputMethodController().GetSelectionOffsets());
EXPECT_EQ("ab", GetCachedTextInputInfo().GetText());
To<Text>(sample.firstChild())->appendData("X");
EXPECT_EQ(PlainTextRange(0, 1),
GetInputMethodController().GetSelectionOffsets());
EXPECT_EQ("abX", GetCachedTextInputInfo().GetText());
}
} // namespace blink
...@@ -1174,9 +1174,14 @@ String InputMethodController::ComposingText() const { ...@@ -1174,9 +1174,14 @@ String InputMethodController::ComposingText() const {
} }
PlainTextRange InputMethodController::GetSelectionOffsets() const { PlainTextRange InputMethodController::GetSelectionOffsets() const {
EphemeralRange range = FirstEphemeralRangeOf( const EphemeralRange range = FirstEphemeralRangeOf(
GetFrame().Selection().ComputeVisibleSelectionInDOMTreeDeprecated()); GetFrame().Selection().ComputeVisibleSelectionInDOMTreeDeprecated());
return PlainTextRangeForEphemeralRange(range).second; if (range.IsNull())
return PlainTextRange();
const ContainerNode& element =
*RootEditableElementOrTreeScopeRootNodeOf(range.StartPosition());
cached_text_input_info_.EnsureCached(element);
return cached_text_input_info_.GetSelection(range);
} }
EphemeralRange InputMethodController::EphemeralRangeForOffsets( EphemeralRange InputMethodController::EphemeralRangeForOffsets(
...@@ -1516,6 +1521,20 @@ void InputMethodController::GetLayoutBounds(WebRect* control_bounds, ...@@ -1516,6 +1521,20 @@ void InputMethodController::GetLayoutBounds(WebRect* control_bounds,
*control_bounds = EnclosingIntRect(editable_rect_double); *control_bounds = EnclosingIntRect(editable_rect_double);
} }
void InputMethodController::DidLayoutSubtree(
const LayoutObject& layout_object) {
cached_text_input_info_.DidLayoutSubtree(layout_object);
}
void InputMethodController::DidUpdateLayout(const LayoutObject& layout_object) {
cached_text_input_info_.DidUpdateLayout(layout_object);
}
void InputMethodController::LayoutObjectWillBeDestroyed(
const LayoutObject& layout_object) {
cached_text_input_info_.LayoutObjectWillBeDestroyed(layout_object);
}
WebTextInputInfo InputMethodController::TextInputInfo() const { WebTextInputInfo InputMethodController::TextInputInfo() const {
WebTextInputInfo info; WebTextInputInfo info;
if (!IsAvailable()) if (!IsAvailable())
...@@ -1547,21 +1566,18 @@ WebTextInputInfo InputMethodController::TextInputInfo() const { ...@@ -1547,21 +1566,18 @@ WebTextInputInfo InputMethodController::TextInputInfo() const {
DocumentLifecycle::DisallowTransitionScope disallow_transition( DocumentLifecycle::DisallowTransitionScope disallow_transition(
GetDocument().Lifecycle()); GetDocument().Lifecycle());
cached_text_input_info_.EnsureCached(*element);
// Emits an object replacement character for each replaced element so that // Emits an object replacement character for each replaced element so that
// it is exposed to IME and thus could be deleted by IME on android. // it is exposed to IME and thus could be deleted by IME on android.
info.value = PlainText(EphemeralRange::RangeOfContents(*element), info.value = cached_text_input_info_.GetText();
TextIteratorBehavior::Builder()
.SetEmitsObjectReplacementCharacter(true)
.SetEmitsSpaceForNbsp(true)
.Build());
if (info.value.IsEmpty()) if (info.value.IsEmpty())
return info; return info;
EphemeralRange first_range = FirstEphemeralRangeOf( const EphemeralRange& first_range = FirstEphemeralRangeOf(
GetFrame().Selection().ComputeVisibleSelectionInDOMTreeDeprecated()); GetFrame().Selection().ComputeVisibleSelectionInDOMTreeDeprecated());
PlainTextRange selection_plain_text_range = const PlainTextRange& selection_plain_text_range =
PlainTextRangeForEphemeralRange(first_range).second; cached_text_input_info_.GetSelection(first_range);
if (selection_plain_text_range.IsNotNull()) { if (selection_plain_text_range.IsNotNull()) {
info.selection_start = selection_plain_text_range.Start(); info.selection_start = selection_plain_text_range.Start();
info.selection_end = selection_plain_text_range.End(); info.selection_end = selection_plain_text_range.End();
...@@ -1574,9 +1590,9 @@ WebTextInputInfo InputMethodController::TextInputInfo() const { ...@@ -1574,9 +1590,9 @@ WebTextInputInfo InputMethodController::TextInputInfo() const {
GetImeTextSpansAroundPosition(first_range.StartPosition()); GetImeTextSpansAroundPosition(first_range.StartPosition());
} }
EphemeralRange range = CompositionEphemeralRange(); const EphemeralRange& range = CompositionEphemeralRange();
PlainTextRange composition_plain_text_range = const PlainTextRange& composition_plain_text_range =
PlainTextRangeForEphemeralRange(range).second; cached_text_input_info_.GetComposition(range);
if (composition_plain_text_range.IsNotNull()) { if (composition_plain_text_range.IsNotNull()) {
info.composition_start = composition_plain_text_range.Start(); info.composition_start = composition_plain_text_range.Start();
info.composition_end = composition_plain_text_range.End(); info.composition_end = composition_plain_text_range.End();
...@@ -1795,6 +1811,7 @@ void InputMethodController::WillChangeFocus() { ...@@ -1795,6 +1811,7 @@ void InputMethodController::WillChangeFocus() {
} }
void InputMethodController::Trace(Visitor* visitor) const { void InputMethodController::Trace(Visitor* visitor) const {
visitor->Trace(cached_text_input_info_);
visitor->Trace(frame_); visitor->Trace(frame_);
visitor->Trace(composition_range_); visitor->Trace(composition_range_);
visitor->Trace(active_edit_context_); visitor->Trace(active_edit_context_);
...@@ -1835,11 +1852,8 @@ WebVector<ui::ImeTextSpan> InputMethodController::GetImeTextSpansAroundPosition( ...@@ -1835,11 +1852,8 @@ WebVector<ui::ImeTextSpan> InputMethodController::GetImeTextSpansAroundPosition(
const EphemeralRange& marker_ephemeral_range = const EphemeralRange& marker_ephemeral_range =
EphemeralRange(Position(node, marker->StartOffset()), EphemeralRange(Position(node, marker->StartOffset()),
Position(node, marker->EndOffset())); Position(node, marker->EndOffset()));
// TODO(yosin): We should have another way to converting DOM position const PlainTextRange& marker_plain_text_range =
// to offset in root editable, because root editable has big text cached_text_input_info_.GetPlainTextRange(marker_ephemeral_range);
// content, |PlainTextRangeForEphemeralRange()| is slow.
PlainTextRange marker_plain_text_range =
PlainTextRangeForEphemeralRange(marker_ephemeral_range).second;
ime_text_spans.emplace_back( ime_text_spans.emplace_back(
ImeTextSpan(type, marker_plain_text_range.Start(), ImeTextSpan(type, marker_plain_text_range.Start(),
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/editing/commands/typing_command.h" #include "third_party/blink/renderer/core/editing/commands/typing_command.h"
#include "third_party/blink/renderer/core/editing/forward.h" #include "third_party/blink/renderer/core/editing/forward.h"
#include "third_party/blink/renderer/core/editing/ime/cached_text_input_info.h"
#include "third_party/blink/renderer/core/editing/ime/ime_text_span.h" #include "third_party/blink/renderer/core/editing/ime/ime_text_span.h"
#include "third_party/blink/renderer/core/editing/plain_text_range.h" #include "third_party/blink/renderer/core/editing/plain_text_range.h"
#include "third_party/blink/renderer/core/events/input_event.h" #include "third_party/blink/renderer/core/events/input_event.h"
...@@ -110,6 +111,11 @@ class CORE_EXPORT InputMethodController final ...@@ -110,6 +111,11 @@ class CORE_EXPORT InputMethodController final
size_t text_length) const; size_t text_length) const;
void DeleteSurroundingText(int before, int after); void DeleteSurroundingText(int before, int after);
void DeleteSurroundingTextInCodePoints(int before, int after); void DeleteSurroundingTextInCodePoints(int before, int after);
void DidLayoutSubtree(const LayoutObject& layout_object);
void DidUpdateLayout(const LayoutObject& layout_object);
void LayoutObjectWillBeDestroyed(const LayoutObject& layout_object);
WebTextInputInfo TextInputInfo() const; WebTextInputInfo TextInputInfo() const;
// For finding NEXT/PREVIOUS everytime during frame update is a costly // For finding NEXT/PREVIOUS everytime during frame update is a costly
// operation, so making it specific whenever needed by splitting from // operation, so making it specific whenever needed by splitting from
...@@ -140,6 +146,10 @@ class CORE_EXPORT InputMethodController final ...@@ -140,6 +146,10 @@ class CORE_EXPORT InputMethodController final
return last_vk_visibility_request_; return last_vk_visibility_request_;
} }
CachedTextInputInfo& GetCachedTextInputInfoForTesting() {
return cached_text_input_info_;
}
private: private:
friend class InputMethodControllerTest; friend class InputMethodControllerTest;
...@@ -147,6 +157,8 @@ class CORE_EXPORT InputMethodController final ...@@ -147,6 +157,8 @@ class CORE_EXPORT InputMethodController final
bool IsAvailable() const; bool IsAvailable() const;
Member<LocalFrame> frame_; Member<LocalFrame> frame_;
// Root editable text content cache for |TextInputInfo()|.
CachedTextInputInfo cached_text_input_info_;
Member<Range> composition_range_; Member<Range> composition_range_;
Member<EditContext> active_edit_context_; Member<EditContext> active_edit_context_;
bool has_composition_; bool has_composition_;
......
...@@ -195,10 +195,14 @@ PlainTextRange PlainTextRange::Create(const ContainerNode& scope, ...@@ -195,10 +195,14 @@ PlainTextRange PlainTextRange::Create(const ContainerNode& scope,
DocumentLifecycle::DisallowTransitionScope disallow_transition( DocumentLifecycle::DisallowTransitionScope disallow_transition(
scope.GetDocument().Lifecycle()); scope.GetDocument().Lifecycle());
wtf_size_t start = const wtf_size_t start =
TextIterator::RangeLength(Position(scope, 0), range.StartPosition()); TextIterator::RangeLength(Position(scope, 0), range.StartPosition());
wtf_size_t end = // Note: We are not sure which one is bigger, scope to range.end or
TextIterator::RangeLength(Position(scope, 0), range.EndPosition()); // range.start to range.end.
const wtf_size_t end =
range.IsCollapsed()
? start
: TextIterator::RangeLength(Position(scope, 0), range.EndPosition());
return PlainTextRange(start, end); return PlainTextRange(start, end);
} }
...@@ -208,4 +212,11 @@ PlainTextRange PlainTextRange::Create(const ContainerNode& scope, ...@@ -208,4 +212,11 @@ PlainTextRange PlainTextRange::Create(const ContainerNode& scope,
return Create(scope, EphemeralRange(&range)); return Create(scope, EphemeralRange(&range));
} }
CORE_EXPORT std::ostream& operator<<(std::ostream& ostream,
const PlainTextRange& range) {
if (range.IsNull())
return ostream << "{}";
return ostream << "{" << range.Start() << "," << range.End() << "}";
}
} // namespace blink } // namespace blink
...@@ -47,6 +47,14 @@ class CORE_EXPORT PlainTextRange { ...@@ -47,6 +47,14 @@ class CORE_EXPORT PlainTextRange {
explicit PlainTextRange(wtf_size_t location); explicit PlainTextRange(wtf_size_t location);
PlainTextRange(wtf_size_t start, wtf_size_t end); PlainTextRange(wtf_size_t start, wtf_size_t end);
bool operator==(const PlainTextRange& other) const {
return start_ == other.start_ && end_ == other.end_;
}
bool operator!=(const PlainTextRange& other) const {
return !operator==(other);
}
wtf_size_t End() const { wtf_size_t End() const {
DCHECK(IsNotNull()); DCHECK(IsNotNull());
return end_; return end_;
...@@ -81,6 +89,8 @@ class CORE_EXPORT PlainTextRange { ...@@ -81,6 +89,8 @@ class CORE_EXPORT PlainTextRange {
const wtf_size_t end_; const wtf_size_t end_;
}; };
CORE_EXPORT std::ostream& operator<<(std::ostream&, const PlainTextRange&);
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_PLAIN_TEXT_RANGE_H_ #endif // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_PLAIN_TEXT_RANGE_H_
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_client.h" #include "third_party/blink/renderer/core/frame/local_frame_client.h"
...@@ -445,6 +446,10 @@ void LayoutBox::WillBeDestroyed() { ...@@ -445,6 +446,10 @@ void LayoutBox::WillBeDestroyed() {
measure_result_->PhysicalFragment().LayoutObjectWillBeDestroyed(); measure_result_->PhysicalFragment().LayoutObjectWillBeDestroyed();
for (auto result : layout_results_) for (auto result : layout_results_)
result->PhysicalFragment().LayoutObjectWillBeDestroyed(); result->PhysicalFragment().LayoutObjectWillBeDestroyed();
GetDocument()
.GetFrame()
->GetInputMethodController()
.LayoutObjectWillBeDestroyed(*this);
} }
SetSnapContainer(nullptr); SetSnapContainer(nullptr);
...@@ -848,6 +853,8 @@ void LayoutBox::LayoutSubtreeRoot() { ...@@ -848,6 +853,8 @@ void LayoutBox::LayoutSubtreeRoot() {
cb = cb->ContainingBlock(); cb = cb->ContainingBlock();
} }
} }
GetDocument().GetFrame()->GetInputMethodController().DidLayoutSubtree(*this);
} }
void LayoutBox::UpdateLayout() { void LayoutBox::UpdateLayout() {
...@@ -1136,6 +1143,7 @@ void LayoutBox::UpdateAfterLayout() { ...@@ -1136,6 +1143,7 @@ void LayoutBox::UpdateAfterLayout() {
Document& document = GetDocument(); Document& document = GetDocument();
document.IncLayoutCallsCounter(); document.IncLayoutCallsCounter();
document.GetFrame()->GetInputMethodController().DidUpdateLayout(*this);
if (IsLayoutNGObject()) if (IsLayoutNGObject())
document.IncLayoutCallsCounterNG(); document.IncLayoutCallsCounterNG();
} }
......
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