Bug 1716846 [wpt PR 29402] - Update wpt metadata, a=testonly
[gecko.git] / widget / android / GeckoEditableSupport.h
blobdd9f5a5f2f108822528701bf03d6a9c0cbdb9d22
1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_widget_GeckoEditableSupport_h
7 #define mozilla_widget_GeckoEditableSupport_h
9 #include "nsAppShell.h"
10 #include "nsIWidget.h"
11 #include "nsTArray.h"
13 #include "mozilla/java/GeckoEditableChildNatives.h"
14 #include "mozilla/java/SessionTextInputWrappers.h"
15 #include "mozilla/TextEventDispatcher.h"
16 #include "mozilla/TextEventDispatcherListener.h"
17 #include "mozilla/UniquePtr.h"
19 class nsWindow;
21 namespace mozilla {
23 class TextComposition;
25 namespace dom {
26 class BrowserChild;
29 namespace widget {
31 class GeckoEditableSupport final
32 : public TextEventDispatcherListener,
33 public java::GeckoEditableChild::Natives<GeckoEditableSupport> {
35 Rules for managing IME between Gecko and Java:
37 * Gecko controls the text content, and Java shadows the Gecko text
38 through text updates
39 * Gecko and Java maintain separate selections, and synchronize when
40 needed through selection updates and set-selection events
41 * Java controls the composition, and Gecko shadows the Java
42 composition through update composition events
45 using EditableBase = java::GeckoEditableChild::Natives<GeckoEditableSupport>;
46 using EditableClient = java::SessionTextInput::EditableClient;
47 using EditableListener = java::SessionTextInput::EditableListener;
49 enum FlushChangesFlag {
50 // Not retrying.
51 FLUSH_FLAG_NONE,
52 // Retrying due to IME text changes during flush.
53 FLUSH_FLAG_RETRY,
54 // Retrying due to IME sync exceptions during flush.
55 FLUSH_FLAG_RECOVER
58 enum RemoveCompositionFlag { CANCEL_IME_COMPOSITION, COMMIT_IME_COMPOSITION };
60 const bool mIsRemote;
61 jni::NativeWeakPtr<GeckoViewSupport> mWindow; // Parent only
62 RefPtr<TextEventDispatcher> mDispatcher;
63 java::GeckoEditableChild::GlobalRef mEditable;
64 bool mEditableAttached;
65 InputContext mInputContext;
66 AutoTArray<UniquePtr<mozilla::WidgetEvent>, 4> mIMEKeyEvents;
67 IMENotification::TextChangeData mIMEPendingTextChange;
68 RefPtr<TextRangeArray> mIMERanges;
69 RefPtr<Runnable> mDisposeRunnable;
70 int32_t mIMEMaskEventsCount; // Mask events when > 0.
71 int32_t mIMEFocusCount; // We are focused when > 0.
72 bool mIMEDelaySynchronizeReply; // We reply asynchronously when true.
73 int32_t mIMEActiveSynchronizeCount; // The number of replies being delayed.
74 int32_t mIMEActiveCompositionCount; // The number of compositions expected.
75 uint32_t mDisposeBlockCount;
76 bool mIMESelectionChanged;
77 bool mIMETextChangedDuringFlush;
78 bool mIMEMonitorCursor;
80 nsIWidget* GetWidget() const;
81 nsWindow* GetNsWindow() const;
83 nsresult BeginInputTransaction(TextEventDispatcher* aDispatcher) {
84 if (mIsRemote) {
85 return aDispatcher->BeginInputTransaction(this);
86 } else {
87 return aDispatcher->BeginNativeInputTransaction();
91 virtual ~GeckoEditableSupport() {}
93 RefPtr<TextComposition> GetComposition() const;
94 bool RemoveComposition(RemoveCompositionFlag aFlag = COMMIT_IME_COMPOSITION);
95 void SendIMEDummyKeyEvent(nsIWidget* aWidget, EventMessage msg);
96 void AddIMETextChange(const IMENotification::TextChangeDataBase& aChange);
97 void PostFlushIMEChanges();
98 void FlushIMEChanges(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
99 void FlushIMEText(FlushChangesFlag aFlags = FLUSH_FLAG_NONE);
100 void AsyncNotifyIME(int32_t aNotification);
101 void UpdateCompositionRects();
102 bool DoReplaceText(int32_t aStart, int32_t aEnd, jni::String::Param aText);
103 bool DoUpdateComposition(int32_t aStart, int32_t aEnd, int32_t aFlags);
104 void OnNotifyIMEOfCompositionEventHandled();
105 void NotifyIMEContext(const InputContext& aContext,
106 const InputContextAction& aAction);
108 public:
109 template <typename Functor>
110 static void OnNativeCall(Functor&& aCall) {
111 struct IMEEvent : nsAppShell::LambdaEvent<Functor> {
112 explicit IMEEvent(Functor&& l)
113 : nsAppShell::LambdaEvent<Functor>(std::move(l)) {}
115 bool IsUIEvent() const override {
116 using GES = GeckoEditableSupport;
117 if (this->lambda.IsTarget(&GES::OnKeyEvent) ||
118 this->lambda.IsTarget(&GES::OnImeReplaceText) ||
119 this->lambda.IsTarget(&GES::OnImeUpdateComposition)) {
120 return true;
122 return false;
125 void Run() override {
126 if (NS_WARN_IF(!this->lambda.GetNativeObject())) {
127 // Ignore stale calls after disposal.
128 jni::GetGeckoThreadEnv()->ExceptionClear();
129 return;
131 nsAppShell::LambdaEvent<Functor>::Run();
134 nsAppShell::PostEvent(mozilla::MakeUnique<IMEEvent>(std::move(aCall)));
137 static void SetOnBrowserChild(dom::BrowserChild* aBrowserChild);
139 // Constructor for main process GeckoEditableChild.
140 GeckoEditableSupport(jni::NativeWeakPtr<GeckoViewSupport> aWindow,
141 java::GeckoEditableChild::Param aEditableChild)
142 : mIsRemote(!aWindow.IsAttached()),
143 mWindow(aWindow),
144 mEditable(aEditableChild),
145 mEditableAttached(!mIsRemote),
146 mIMERanges(new TextRangeArray()),
147 mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet
148 mIMEFocusCount(0),
149 mIMEDelaySynchronizeReply(false),
150 mIMEActiveSynchronizeCount(0),
151 mDisposeBlockCount(0),
152 mIMESelectionChanged(false),
153 mIMETextChangedDuringFlush(false),
154 mIMEMonitorCursor(false) {}
156 // Constructor for content process GeckoEditableChild.
157 explicit GeckoEditableSupport(java::GeckoEditableChild::Param aEditableChild)
158 : GeckoEditableSupport(nullptr, aEditableChild) {}
160 NS_DECL_ISUPPORTS
162 // TextEventDispatcherListener methods
163 NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
164 const IMENotification& aNotification) override;
166 NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;
168 NS_IMETHOD_(void)
169 OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
171 NS_IMETHOD_(void)
172 WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher,
173 WidgetKeyboardEvent& aKeyboardEvent,
174 uint32_t aIndexOfKeypress, void* aData) override;
176 void SetInputContext(const InputContext& aContext,
177 const InputContextAction& aAction);
179 InputContext GetInputContext();
181 bool HasIMEFocus() const { return mIMEFocusCount != 0; }
183 void AddBlocker() { mDisposeBlockCount++; }
185 void ReleaseBlocker() {
186 mDisposeBlockCount--;
188 if (!mDisposeBlockCount && mDisposeRunnable) {
189 if (HasIMEFocus()) {
190 // If we have IME focus, GeckoEditableChild is already attached again.
191 // So disposer is unnecessary.
192 mDisposeRunnable = nullptr;
193 return;
196 RefPtr<GeckoEditableSupport> self(this);
197 RefPtr<Runnable> disposer = std::move(mDisposeRunnable);
199 nsAppShell::PostEvent(
200 [self = std::move(self), disposer = std::move(disposer)] {
201 self->mEditableAttached = false;
202 disposer->Run();
207 bool IsGeckoEditableUsed() const { return mDisposeBlockCount != 0; }
209 // GeckoEditableChild methods
210 using EditableBase::AttachNative;
211 using EditableBase::DisposeNative;
213 const java::GeckoEditableChild::Ref& GetJavaEditable() { return mEditable; }
215 void OnWeakNonIntrusiveDetach(already_AddRefed<Runnable> aDisposer) {
216 RefPtr<GeckoEditableSupport> self(this);
217 nsAppShell::PostEvent(
218 [self = std::move(self), disposer = RefPtr<Runnable>(aDisposer)] {
219 if (self->IsGeckoEditableUsed()) {
220 // Current calling stack uses GeckoEditableChild, so we should
221 // not dispose it now.
222 self->mDisposeRunnable = disposer;
223 return;
225 self->mEditableAttached = false;
226 disposer->Run();
230 // Transfer to a new parent.
231 void TransferParent(jni::Object::Param aEditableParent);
233 // Handle an Android KeyEvent.
234 void OnKeyEvent(int32_t aAction, int32_t aKeyCode, int32_t aScanCode,
235 int32_t aMetaState, int32_t aKeyPressMetaState, int64_t aTime,
236 int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
237 int32_t aFlags, bool aIsSynthesizedImeKey,
238 jni::Object::Param originalEvent);
240 // Synchronize Gecko thread with the InputConnection thread.
241 void OnImeSynchronize();
243 // Replace a range of text with new text.
244 void OnImeReplaceText(int32_t aStart, int32_t aEnd, jni::String::Param aText);
246 // Add styling for a range within the active composition.
247 void OnImeAddCompositionRange(int32_t aStart, int32_t aEnd,
248 int32_t aRangeType, int32_t aRangeStyle,
249 int32_t aRangeLineStyle, bool aRangeBoldLine,
250 int32_t aRangeForeColor,
251 int32_t aRangeBackColor,
252 int32_t aRangeLineColor);
254 // Update styling for the active composition using previous-added ranges.
255 void OnImeUpdateComposition(int32_t aStart, int32_t aEnd, int32_t aFlags);
257 // Set cursor mode whether IME requests
258 void OnImeRequestCursorUpdates(int aRequestMode);
260 // Commit current composition to sync Gecko text state with Java.
261 void OnImeRequestCommit();
264 } // namespace widget
265 } // namespace mozilla
267 #endif // mozilla_widget_GeckoEditableSupport_h