Commit 8b89eefd authored by Anupam Snigdha's avatar Anupam Snigdha Committed by Commit Bot

EditContext prototype implementation

This change contains the prototype implementation of EditContext APIs.
EditContext simplifies the process of integrating a web app with advanced
text input methods such as IME, VK shape-writing, speech recognition etc.
EditContext decouples text input from the HTML DOM view.
Rather than having the web platform infer the data required to enable
sophisticated text input mechanisms from the HTML DOM, the author will
provide that data explicitly through the API surface of the EditContext.
Additionally, EditContext communicates events driven from text input UI
to JavaScript.

Explainer: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/
           master/EditContext/explainer.md

Test: run_web_tests web_tests/editing/input/edit-context.html

Bug: 999184

Change-Id: I36f115bf37e6ea2adb64c2f42e024dc800b56480
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1868848
Commit-Queue: Anupam Snigdha <snianu@microsoft.com>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713160}
parent 1badd783
...@@ -80,6 +80,13 @@ class WebInputMethodController { ...@@ -80,6 +80,13 @@ class WebInputMethodController {
virtual bool GetCompositionCharacterBounds(WebVector<WebRect>& bounds) { virtual bool GetCompositionCharacterBounds(WebVector<WebRect>& bounds) {
return false; return false;
} }
// Populate |control_bounds| and |selection_bounds| with the bounds fetched
// from the active EditContext
virtual void GetLayoutBounds(WebRect& control_bounds,
WebRect& selection_bounds) = 0;
// Returns true if there is an active EditContext
virtual bool IsEditContextActive() const = 0;
}; };
} // namespace blink } // namespace blink
......
...@@ -143,6 +143,7 @@ core_idl_files = ...@@ -143,6 +143,7 @@ core_idl_files =
"trustedtypes/trusted_type_policy.idl", "trustedtypes/trusted_type_policy.idl",
"trustedtypes/trusted_type_policy_factory.idl", "trustedtypes/trusted_type_policy_factory.idl",
"editing/selection.idl", "editing/selection.idl",
"editing/ime/edit_context.idl",
"editing/ime/text_update_event.idl", "editing/ime/text_update_event.idl",
"editing/ime/text_format_update_event.idl", "editing/ime/text_format_update_event.idl",
"events/animation_event.idl", "events/animation_event.idl",
...@@ -626,6 +627,7 @@ core_dictionary_idl_files = ...@@ -626,6 +627,7 @@ core_dictionary_idl_files =
"dom/events/event_init.idl", "dom/events/event_init.idl",
"dom/events/event_listener_options.idl", "dom/events/event_listener_options.idl",
"dom/events/event_modifier_init.idl", "dom/events/event_modifier_init.idl",
"editing/ime/edit_context_init.idl",
"editing/ime/text_update_event_init.idl", "editing/ime/text_update_event_init.idl",
"editing/ime/text_format_update_event_init.idl", "editing/ime/text_format_update_event_init.idl",
"events/animation_event_init.idl", "events/animation_event_init.idl",
......
...@@ -144,6 +144,8 @@ blink_core_sources("editing") { ...@@ -144,6 +144,8 @@ blink_core_sources("editing") {
"frame_selection.h", "frame_selection.h",
"granularity_strategy.cc", "granularity_strategy.cc",
"granularity_strategy.h", "granularity_strategy.h",
"ime/edit_context.cc",
"ime/edit_context.h",
"ime/ime_text_span.cc", "ime/ime_text_span.cc",
"ime/ime_text_span.h", "ime/ime_text_span.h",
"ime/ime_text_span_vector_builder.cc", "ime/ime_text_span_vector_builder.cc",
......
This diff is collapsed.
This diff is collapsed.
// Copyright 2019 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.
// The goal of the EditContext is to expose the lower-level APIs provided by
// modern operating systems to facilitate various input modalities to unlock
// advanced editing scenarios. For more information please refer
// https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/EditContext/explainer.md
[
Constructor(optional EditContextInit options),
ConstructorCallWith=ScriptState,
Exposed=Window,
RuntimeEnabled=EditContext
] interface EditContext : EventTarget {
void focus();
void blur();
[RaisesException] void updateSelection(unsigned long start, unsigned long end);
void updateLayout(DOMRect controlBounds, DOMRect selectionBounds);
[RaisesException] void updateText(unsigned long start, unsigned long end, DOMString newText);
attribute DOMString text;
[RaisesException=Setter] attribute unsigned long selectionStart;
[RaisesException=Setter] attribute unsigned long selectionEnd;
attribute EditContextInputMode inputMode;
attribute EditContextInputPanelPolicy inputPanelPolicy;
attribute EditContextEnterKeyHint enterKeyHint;
// Event handler attributes
attribute EventHandler ontextupdate;
attribute EventHandler ontextformatupdate;
attribute EventHandler oncompositionstart;
attribute EventHandler oncompositionend;
};
// Copyright 2019 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.
enum EditContextInputMode { "none", "text", "decimal", "search", "email", "numeric", "tel", "url", "password" };
enum EditContextEnterKeyHint { "enter", "done", "go", "next", "previous", "search", "send" };
enum EditContextInputPanelPolicy { "auto", "manual" };
dictionary EditContextInit {
DOMString text;
unsigned long selectionStart;
unsigned long selectionEnd;
EditContextInputMode inputMode;
EditContextInputPanelPolicy inputPanelPolicy;
EditContextEnterKeyHint enterKeyHint;
};
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "third_party/blink/renderer/core/editing/editor.h" #include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.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/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/edit_context.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h" #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
#include "third_party/blink/renderer/core/editing/markers/suggestion_marker_properties.h" #include "third_party/blink/renderer/core/editing/markers/suggestion_marker_properties.h"
#include "third_party/blink/renderer/core/editing/reveal_selection_scope.h" #include "third_party/blink/renderer/core/editing/reveal_selection_scope.h"
...@@ -1555,6 +1556,7 @@ void InputMethodController::WillChangeFocus() { ...@@ -1555,6 +1556,7 @@ void InputMethodController::WillChangeFocus() {
void InputMethodController::Trace(Visitor* visitor) { void InputMethodController::Trace(Visitor* visitor) {
visitor->Trace(frame_); visitor->Trace(frame_);
visitor->Trace(composition_range_); visitor->Trace(composition_range_);
visitor->Trace(active_edit_context_);
DocumentShutdownObserver::Trace(visitor); DocumentShutdownObserver::Trace(visitor);
} }
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
namespace blink { namespace blink {
class Editor; class Editor;
class EditContext;
class LocalFrame; class LocalFrame;
class Range; class Range;
enum class TypingContinuation; enum class TypingContinuation;
...@@ -113,6 +114,12 @@ class CORE_EXPORT InputMethodController final ...@@ -113,6 +114,12 @@ class CORE_EXPORT InputMethodController final
// Call this when we will change focus. // Call this when we will change focus.
void WillChangeFocus(); void WillChangeFocus();
// Returns the |EditContext| that is currently active
EditContext* GetActiveEditContext() const { return active_edit_context_; }
void SetActiveEditContext(EditContext* edit_context) {
active_edit_context_ = edit_context;
}
private: private:
friend class InputMethodControllerTest; friend class InputMethodControllerTest;
...@@ -121,6 +128,7 @@ class CORE_EXPORT InputMethodController final ...@@ -121,6 +128,7 @@ class CORE_EXPORT InputMethodController final
Member<LocalFrame> frame_; Member<LocalFrame> frame_;
Member<Range> composition_range_; Member<Range> composition_range_;
Member<EditContext> active_edit_context_;
bool has_composition_; bool has_composition_;
Editor& GetEditor() const; Editor& GetEditor() const;
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TEXT_FORMAT_UPDATE_EVENT_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_TEXT_FORMAT_UPDATE_EVENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TEXT_FORMAT_UPDATE_EVENT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_TEXT_FORMAT_UPDATE_EVENT_H_
#include "base/macros.h" #include "base/macros.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
...@@ -56,4 +56,4 @@ class CORE_EXPORT TextFormatUpdateEvent final : public Event { ...@@ -56,4 +56,4 @@ class CORE_EXPORT TextFormatUpdateEvent final : public Event {
}; };
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TEXT_FORMAT_UPDATE_EVENT_H_ #endif // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_TEXT_FORMAT_UPDATE_EVENT_H_
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TEXT_UPDATE_EVENT_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_TEXT_UPDATE_EVENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TEXT_UPDATE_EVENT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_TEXT_UPDATE_EVENT_H_
#include "base/macros.h" #include "base/macros.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
...@@ -59,4 +59,4 @@ class CORE_EXPORT TextUpdateEvent final : public Event { ...@@ -59,4 +59,4 @@ class CORE_EXPORT TextUpdateEvent final : public Event {
}; };
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_TEXT_UPDATE_EVENT_H_ #endif // THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_IME_TEXT_UPDATE_EVENT_H_
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
"AccessibleNode", "AccessibleNode",
"Animation", "Animation",
"Clipboard", "Clipboard",
"EditContext",
"FontFaceSet", "FontFaceSet",
"MediaQueryList", "MediaQueryList",
"BroadcastChannel", "BroadcastChannel",
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "third_party/blink/renderer/core/editing/editor.h" #include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.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/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/edit_context.h"
#include "third_party/blink/renderer/core/editing/ime/ime_text_span_vector_builder.h" #include "third_party/blink/renderer/core/editing/ime/ime_text_span_vector_builder.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h" #include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/plain_text_range.h" #include "third_party/blink/renderer/core/editing/plain_text_range.h"
...@@ -37,12 +38,22 @@ void WebInputMethodControllerImpl::Trace(blink::Visitor* visitor) { ...@@ -37,12 +38,22 @@ void WebInputMethodControllerImpl::Trace(blink::Visitor* visitor) {
visitor->Trace(web_frame_); visitor->Trace(web_frame_);
} }
bool WebInputMethodControllerImpl::IsEditContextActive() const {
return GetInputMethodController().GetActiveEditContext();
}
bool WebInputMethodControllerImpl::SetComposition( bool WebInputMethodControllerImpl::SetComposition(
const WebString& text, const WebString& text,
const WebVector<WebImeTextSpan>& ime_text_spans, const WebVector<WebImeTextSpan>& ime_text_spans,
const WebRange& replacement_range, const WebRange& replacement_range,
int selection_start, int selection_start,
int selection_end) { int selection_end) {
if (IsEditContextActive()) {
return GetInputMethodController().GetActiveEditContext()->SetComposition(
text, ime_text_spans, replacement_range, selection_start,
selection_end);
}
if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) { if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) {
return plugin->SetComposition(text, ime_text_spans, replacement_range, return plugin->SetComposition(text, ime_text_spans, replacement_range,
selection_start, selection_end); selection_start, selection_end);
...@@ -94,6 +105,12 @@ bool WebInputMethodControllerImpl::FinishComposingText( ...@@ -94,6 +105,12 @@ bool WebInputMethodControllerImpl::FinishComposingText(
// in WebViewImpl::focusedLocalFrameInWidget(), we will reach here with // in WebViewImpl::focusedLocalFrameInWidget(), we will reach here with
// |web_frame_| not focused on page. // |web_frame_| not focused on page.
if (IsEditContextActive()) {
return GetInputMethodController()
.GetActiveEditContext()
->FinishComposingText(selection_behavior);
}
if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported())
return plugin->FinishComposingText(selection_behavior); return plugin->FinishComposingText(selection_behavior);
...@@ -115,6 +132,11 @@ bool WebInputMethodControllerImpl::CommitText( ...@@ -115,6 +132,11 @@ bool WebInputMethodControllerImpl::CommitText(
std::unique_ptr<UserGestureIndicator> gesture_indicator = std::unique_ptr<UserGestureIndicator> gesture_indicator =
LocalFrame::NotifyUserActivation(GetFrame()); LocalFrame::NotifyUserActivation(GetFrame());
if (IsEditContextActive()) {
return GetInputMethodController().GetActiveEditContext()->CommitText(
text, ime_text_spans, replacement_range, relative_caret_position);
}
if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) { if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) {
return plugin->CommitText(text, ime_text_spans, replacement_range, return plugin->CommitText(text, ime_text_spans, replacement_range,
relative_caret_position); relative_caret_position);
...@@ -136,6 +158,9 @@ bool WebInputMethodControllerImpl::CommitText( ...@@ -136,6 +158,9 @@ bool WebInputMethodControllerImpl::CommitText(
} }
WebTextInputInfo WebInputMethodControllerImpl::TextInputInfo() { WebTextInputInfo WebInputMethodControllerImpl::TextInputInfo() {
if (IsEditContextActive())
return GetInputMethodController().GetActiveEditContext()->TextInputInfo();
return GetFrame()->GetInputMethodController().TextInputInfo(); return GetFrame()->GetInputMethodController().TextInputInfo();
} }
...@@ -146,10 +171,27 @@ int WebInputMethodControllerImpl::ComputeWebTextInputNextPreviousFlags() { ...@@ -146,10 +171,27 @@ int WebInputMethodControllerImpl::ComputeWebTextInputNextPreviousFlags() {
} }
WebTextInputType WebInputMethodControllerImpl::TextInputType() { WebTextInputType WebInputMethodControllerImpl::TextInputType() {
if (IsEditContextActive())
return GetInputMethodController().GetActiveEditContext()->TextInputType();
return GetFrame()->GetInputMethodController().TextInputType(); return GetFrame()->GetInputMethodController().TextInputType();
} }
void WebInputMethodControllerImpl::GetLayoutBounds(WebRect& control_bounds,
WebRect& selection_bounds) {
if (IsEditContextActive()) {
return GetInputMethodController().GetActiveEditContext()->GetLayoutBounds(
control_bounds, selection_bounds);
}
}
WebRange WebInputMethodControllerImpl::CompositionRange() { WebRange WebInputMethodControllerImpl::CompositionRange() {
if (IsEditContextActive()) {
return GetInputMethodController()
.GetActiveEditContext()
->CompositionRange();
}
EphemeralRange range = EphemeralRange range =
GetFrame()->GetInputMethodController().CompositionEphemeralRange(); GetFrame()->GetInputMethodController().CompositionEphemeralRange();
...@@ -166,6 +208,9 @@ WebRange WebInputMethodControllerImpl::CompositionRange() { ...@@ -166,6 +208,9 @@ WebRange WebInputMethodControllerImpl::CompositionRange() {
bool WebInputMethodControllerImpl::GetCompositionCharacterBounds( bool WebInputMethodControllerImpl::GetCompositionCharacterBounds(
WebVector<WebRect>& bounds) { WebVector<WebRect>& bounds) {
if (IsEditContextActive())
return false;
WebRange range = CompositionRange(); WebRange range = CompositionRange();
if (range.IsEmpty()) if (range.IsEmpty())
return false; return false;
...@@ -187,6 +232,12 @@ bool WebInputMethodControllerImpl::GetCompositionCharacterBounds( ...@@ -187,6 +232,12 @@ bool WebInputMethodControllerImpl::GetCompositionCharacterBounds(
} }
WebRange WebInputMethodControllerImpl::GetSelectionOffsets() const { WebRange WebInputMethodControllerImpl::GetSelectionOffsets() const {
if (IsEditContextActive()) {
return GetInputMethodController()
.GetActiveEditContext()
->GetSelectionOffsets();
}
// TODO(editing-dev): The use of UpdateStyleAndLayout // TODO(editing-dev): The use of UpdateStyleAndLayout
// needs to be audited. See http://crbug.com/590369 for more details. // needs to be audited. See http://crbug.com/590369 for more details.
GetFrame()->GetDocument()->UpdateStyleAndLayout(); GetFrame()->GetDocument()->UpdateStyleAndLayout();
......
...@@ -49,6 +49,10 @@ class CORE_EXPORT WebInputMethodControllerImpl ...@@ -49,6 +49,10 @@ class CORE_EXPORT WebInputMethodControllerImpl
WebRange GetSelectionOffsets() const override; WebRange GetSelectionOffsets() const override;
void GetLayoutBounds(WebRect& control_bounds,
WebRect& selection_bounds) override;
bool IsEditContextActive() const override;
void Trace(blink::Visitor*); void Trace(blink::Visitor*);
private: private:
......
...@@ -160,6 +160,7 @@ ...@@ -160,6 +160,7 @@
#include "third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.h" #include "third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.h"
#include "third_party/blink/renderer/core/editing/finder/text_finder.h" #include "third_party/blink/renderer/core/editing/finder/text_finder.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h" #include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/edit_context.h"
#include "third_party/blink/renderer/core/editing/ime/ime_text_span_vector_builder.h" #include "third_party/blink/renderer/core/editing/ime/ime_text_span_vector_builder.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h" #include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h" #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
...@@ -1352,6 +1353,12 @@ bool WebLocalFrameImpl::SetCompositionFromExistingText( ...@@ -1352,6 +1353,12 @@ bool WebLocalFrameImpl::SetCompositionFromExistingText(
int composition_end, int composition_end,
const WebVector<WebImeTextSpan>& ime_text_spans) { const WebVector<WebImeTextSpan>& ime_text_spans) {
TRACE_EVENT0("blink", "WebLocalFrameImpl::setCompositionFromExistingText"); TRACE_EVENT0("blink", "WebLocalFrameImpl::setCompositionFromExistingText");
if (EditContext* edit_context =
GetFrame()->GetInputMethodController().GetActiveEditContext()) {
return edit_context->SetCompositionFromExistingText(
composition_start, composition_end, ime_text_spans);
}
if (!GetFrame()->GetEditor().CanEdit()) if (!GetFrame()->GetEditor().CanEdit())
return false; return false;
...@@ -1371,6 +1378,12 @@ bool WebLocalFrameImpl::SetCompositionFromExistingText( ...@@ -1371,6 +1378,12 @@ bool WebLocalFrameImpl::SetCompositionFromExistingText(
void WebLocalFrameImpl::ExtendSelectionAndDelete(int before, int after) { void WebLocalFrameImpl::ExtendSelectionAndDelete(int before, int after) {
TRACE_EVENT0("blink", "WebLocalFrameImpl::extendSelectionAndDelete"); TRACE_EVENT0("blink", "WebLocalFrameImpl::extendSelectionAndDelete");
if (EditContext* edit_context =
GetFrame()->GetInputMethodController().GetActiveEditContext()) {
edit_context->ExtendSelectionAndDelete(before, after);
return;
}
if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) { if (WebPlugin* plugin = FocusedPluginIfInputMethodSupported()) {
plugin->ExtendSelectionAndDelete(before, after); plugin->ExtendSelectionAndDelete(before, after);
return; return;
......
This diff is collapsed.
...@@ -1953,6 +1953,34 @@ interface DynamicsCompressorNode : AudioNode ...@@ -1953,6 +1953,34 @@ interface DynamicsCompressorNode : AudioNode
getter release getter release
getter threshold getter threshold
method constructor method constructor
interface EditContext : EventTarget
attribute @@toStringTag
getter enterKeyHint
getter inputMode
getter inputPanelPolicy
getter oncompositionend
getter oncompositionstart
getter ontextformatupdate
getter ontextupdate
getter selectionEnd
getter selectionStart
getter text
method blur
method constructor
method focus
method updateLayout
method updateSelection
method updateText
setter enterKeyHint
setter inputMode
setter inputPanelPolicy
setter oncompositionend
setter oncompositionstart
setter ontextformatupdate
setter ontextupdate
setter selectionEnd
setter selectionStart
setter text
interface Element : Node interface Element : Node
attribute @@toStringTag attribute @@toStringTag
attribute @@unscopables attribute @@unscopables
......
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