1 /* -*- Mode: c++; c-basic-offset: 2; tab-width: 4; indent-tabs-mode: nil; -*-
2  * vim: set sw=2 ts=4 expandtab:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "GeckoEditableSupport.h"
8 
9 #include "AndroidRect.h"
10 #include "KeyEvent.h"
11 #include "PuppetWidget.h"
12 #include "nsIContent.h"
13 
14 #include "mozilla/dom/ContentChild.h"
15 #include "mozilla/IMEStateManager.h"
16 #include "mozilla/java/GeckoEditableChildWrappers.h"
17 #include "mozilla/java/GeckoServiceChildProcessWrappers.h"
18 #include "mozilla/Logging.h"
19 #include "mozilla/MiscEvents.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/StaticPrefs_intl.h"
22 #include "mozilla/TextComposition.h"
23 #include "mozilla/TextEventDispatcherListener.h"
24 #include "mozilla/TextEvents.h"
25 #include "mozilla/ToString.h"
26 #include "mozilla/dom/BrowserChild.h"
27 #include "mozilla/widget/GeckoViewSupport.h"
28 
29 #include <android/api-level.h>
30 #include <android/input.h>
31 #include <android/log.h>
32 
33 #ifdef NIGHTLY_BUILD
34 static mozilla::LazyLogModule sGeckoEditableSupportLog("GeckoEditableSupport");
35 #  define ALOGIME(...) \
36     MOZ_LOG(sGeckoEditableSupportLog, LogLevel::Debug, (__VA_ARGS__))
37 #else
38 #  define ALOGIME(args...) \
39     do {                   \
40     } while (0)
41 #endif
42 
ConvertAndroidKeyCodeToDOMKeyCode(int32_t androidKeyCode)43 static uint32_t ConvertAndroidKeyCodeToDOMKeyCode(int32_t androidKeyCode) {
44   // Special-case alphanumeric keycodes because they are most common.
45   if (androidKeyCode >= AKEYCODE_A && androidKeyCode <= AKEYCODE_Z) {
46     return androidKeyCode - AKEYCODE_A + NS_VK_A;
47   }
48 
49   if (androidKeyCode >= AKEYCODE_0 && androidKeyCode <= AKEYCODE_9) {
50     return androidKeyCode - AKEYCODE_0 + NS_VK_0;
51   }
52 
53   switch (androidKeyCode) {
54     // KEYCODE_UNKNOWN (0) ... KEYCODE_HOME (3)
55     case AKEYCODE_BACK:
56       return NS_VK_ESCAPE;
57     // KEYCODE_CALL (5) ... KEYCODE_POUND (18)
58     case AKEYCODE_DPAD_UP:
59       return NS_VK_UP;
60     case AKEYCODE_DPAD_DOWN:
61       return NS_VK_DOWN;
62     case AKEYCODE_DPAD_LEFT:
63       return NS_VK_LEFT;
64     case AKEYCODE_DPAD_RIGHT:
65       return NS_VK_RIGHT;
66     case AKEYCODE_DPAD_CENTER:
67       return NS_VK_RETURN;
68     case AKEYCODE_VOLUME_UP:
69       return NS_VK_VOLUME_UP;
70     case AKEYCODE_VOLUME_DOWN:
71       return NS_VK_VOLUME_DOWN;
72     // KEYCODE_VOLUME_POWER (26) ... KEYCODE_Z (54)
73     case AKEYCODE_COMMA:
74       return NS_VK_COMMA;
75     case AKEYCODE_PERIOD:
76       return NS_VK_PERIOD;
77     case AKEYCODE_ALT_LEFT:
78       return NS_VK_ALT;
79     case AKEYCODE_ALT_RIGHT:
80       return NS_VK_ALT;
81     case AKEYCODE_SHIFT_LEFT:
82       return NS_VK_SHIFT;
83     case AKEYCODE_SHIFT_RIGHT:
84       return NS_VK_SHIFT;
85     case AKEYCODE_TAB:
86       return NS_VK_TAB;
87     case AKEYCODE_SPACE:
88       return NS_VK_SPACE;
89     // KEYCODE_SYM (63) ... KEYCODE_ENVELOPE (65)
90     case AKEYCODE_ENTER:
91       return NS_VK_RETURN;
92     case AKEYCODE_DEL:
93       return NS_VK_BACK;  // Backspace
94     case AKEYCODE_GRAVE:
95       return NS_VK_BACK_QUOTE;
96     // KEYCODE_MINUS (69)
97     case AKEYCODE_EQUALS:
98       return NS_VK_EQUALS;
99     case AKEYCODE_LEFT_BRACKET:
100       return NS_VK_OPEN_BRACKET;
101     case AKEYCODE_RIGHT_BRACKET:
102       return NS_VK_CLOSE_BRACKET;
103     case AKEYCODE_BACKSLASH:
104       return NS_VK_BACK_SLASH;
105     case AKEYCODE_SEMICOLON:
106       return NS_VK_SEMICOLON;
107     // KEYCODE_APOSTROPHE (75)
108     case AKEYCODE_SLASH:
109       return NS_VK_SLASH;
110     // KEYCODE_AT (77) ... KEYCODE_MEDIA_FAST_FORWARD (90)
111     case AKEYCODE_MUTE:
112       return NS_VK_VOLUME_MUTE;
113     case AKEYCODE_PAGE_UP:
114       return NS_VK_PAGE_UP;
115     case AKEYCODE_PAGE_DOWN:
116       return NS_VK_PAGE_DOWN;
117     // KEYCODE_PICTSYMBOLS (94) ... KEYCODE_BUTTON_MODE (110)
118     case AKEYCODE_ESCAPE:
119       return NS_VK_ESCAPE;
120     case AKEYCODE_FORWARD_DEL:
121       return NS_VK_DELETE;
122     case AKEYCODE_CTRL_LEFT:
123       return NS_VK_CONTROL;
124     case AKEYCODE_CTRL_RIGHT:
125       return NS_VK_CONTROL;
126     case AKEYCODE_CAPS_LOCK:
127       return NS_VK_CAPS_LOCK;
128     case AKEYCODE_SCROLL_LOCK:
129       return NS_VK_SCROLL_LOCK;
130     // KEYCODE_META_LEFT (117) ... KEYCODE_FUNCTION (119)
131     case AKEYCODE_SYSRQ:
132       return NS_VK_PRINTSCREEN;
133     case AKEYCODE_BREAK:
134       return NS_VK_PAUSE;
135     case AKEYCODE_MOVE_HOME:
136       return NS_VK_HOME;
137     case AKEYCODE_MOVE_END:
138       return NS_VK_END;
139     case AKEYCODE_INSERT:
140       return NS_VK_INSERT;
141     // KEYCODE_FORWARD (125) ... KEYCODE_MEDIA_RECORD (130)
142     case AKEYCODE_F1:
143       return NS_VK_F1;
144     case AKEYCODE_F2:
145       return NS_VK_F2;
146     case AKEYCODE_F3:
147       return NS_VK_F3;
148     case AKEYCODE_F4:
149       return NS_VK_F4;
150     case AKEYCODE_F5:
151       return NS_VK_F5;
152     case AKEYCODE_F6:
153       return NS_VK_F6;
154     case AKEYCODE_F7:
155       return NS_VK_F7;
156     case AKEYCODE_F8:
157       return NS_VK_F8;
158     case AKEYCODE_F9:
159       return NS_VK_F9;
160     case AKEYCODE_F10:
161       return NS_VK_F10;
162     case AKEYCODE_F11:
163       return NS_VK_F11;
164     case AKEYCODE_F12:
165       return NS_VK_F12;
166     case AKEYCODE_NUM_LOCK:
167       return NS_VK_NUM_LOCK;
168     case AKEYCODE_NUMPAD_0:
169       return NS_VK_NUMPAD0;
170     case AKEYCODE_NUMPAD_1:
171       return NS_VK_NUMPAD1;
172     case AKEYCODE_NUMPAD_2:
173       return NS_VK_NUMPAD2;
174     case AKEYCODE_NUMPAD_3:
175       return NS_VK_NUMPAD3;
176     case AKEYCODE_NUMPAD_4:
177       return NS_VK_NUMPAD4;
178     case AKEYCODE_NUMPAD_5:
179       return NS_VK_NUMPAD5;
180     case AKEYCODE_NUMPAD_6:
181       return NS_VK_NUMPAD6;
182     case AKEYCODE_NUMPAD_7:
183       return NS_VK_NUMPAD7;
184     case AKEYCODE_NUMPAD_8:
185       return NS_VK_NUMPAD8;
186     case AKEYCODE_NUMPAD_9:
187       return NS_VK_NUMPAD9;
188     case AKEYCODE_NUMPAD_DIVIDE:
189       return NS_VK_DIVIDE;
190     case AKEYCODE_NUMPAD_MULTIPLY:
191       return NS_VK_MULTIPLY;
192     case AKEYCODE_NUMPAD_SUBTRACT:
193       return NS_VK_SUBTRACT;
194     case AKEYCODE_NUMPAD_ADD:
195       return NS_VK_ADD;
196     case AKEYCODE_NUMPAD_DOT:
197       return NS_VK_DECIMAL;
198     case AKEYCODE_NUMPAD_COMMA:
199       return NS_VK_SEPARATOR;
200     case AKEYCODE_NUMPAD_ENTER:
201       return NS_VK_RETURN;
202     case AKEYCODE_NUMPAD_EQUALS:
203       return NS_VK_EQUALS;
204     // KEYCODE_NUMPAD_LEFT_PAREN (162) ... KEYCODE_CALCULATOR (210)
205 
206     // Needs to confirm the behavior.  If the key switches the open state
207     // of Japanese IME (or switches input character between Hiragana and
208     // Roman numeric characters), then, it might be better to use
209     // NS_VK_KANJI which is used for Alt+Zenkaku/Hankaku key on Windows.
210     case AKEYCODE_ZENKAKU_HANKAKU:
211       return 0;
212     case AKEYCODE_EISU:
213       return NS_VK_EISU;
214     case AKEYCODE_MUHENKAN:
215       return NS_VK_NONCONVERT;
216     case AKEYCODE_HENKAN:
217       return NS_VK_CONVERT;
218     case AKEYCODE_KATAKANA_HIRAGANA:
219       return 0;
220     case AKEYCODE_YEN:
221       return NS_VK_BACK_SLASH;  // Same as other platforms.
222     case AKEYCODE_RO:
223       return NS_VK_BACK_SLASH;  // Same as other platforms.
224     case AKEYCODE_KANA:
225       return NS_VK_KANA;
226     case AKEYCODE_ASSIST:
227       return NS_VK_HELP;
228 
229     // the A key is the action key for gamepad devices.
230     case AKEYCODE_BUTTON_A:
231       return NS_VK_RETURN;
232 
233     default:
234       ALOG(
235           "ConvertAndroidKeyCodeToDOMKeyCode: "
236           "No DOM keycode for Android keycode %d",
237           int(androidKeyCode));
238       return 0;
239   }
240 }
241 
ConvertAndroidKeyCodeToKeyNameIndex(int32_t keyCode,int32_t action,int32_t domPrintableKeyValue)242 static KeyNameIndex ConvertAndroidKeyCodeToKeyNameIndex(
243     int32_t keyCode, int32_t action, int32_t domPrintableKeyValue) {
244   // Special-case alphanumeric keycodes because they are most common.
245   if (keyCode >= AKEYCODE_A && keyCode <= AKEYCODE_Z) {
246     return KEY_NAME_INDEX_USE_STRING;
247   }
248 
249   if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
250     return KEY_NAME_INDEX_USE_STRING;
251   }
252 
253   switch (keyCode) {
254 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
255   case aNativeKey:                                                     \
256     return aKeyNameIndex;
257 
258 #include "NativeKeyToDOMKeyName.h"
259 
260 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
261 
262     // KEYCODE_0 (7) ... KEYCODE_9 (16)
263     case AKEYCODE_STAR:   // '*' key
264     case AKEYCODE_POUND:  // '#' key
265 
266       // KEYCODE_A (29) ... KEYCODE_Z (54)
267 
268     case AKEYCODE_COMMA:   // ',' key
269     case AKEYCODE_PERIOD:  // '.' key
270     case AKEYCODE_SPACE:
271     case AKEYCODE_GRAVE:          // '`' key
272     case AKEYCODE_MINUS:          // '-' key
273     case AKEYCODE_EQUALS:         // '=' key
274     case AKEYCODE_LEFT_BRACKET:   // '[' key
275     case AKEYCODE_RIGHT_BRACKET:  // ']' key
276     case AKEYCODE_BACKSLASH:      // '\' key
277     case AKEYCODE_SEMICOLON:      // ';' key
278     case AKEYCODE_APOSTROPHE:     // ''' key
279     case AKEYCODE_SLASH:          // '/' key
280     case AKEYCODE_AT:             // '@' key
281     case AKEYCODE_PLUS:           // '+' key
282 
283     case AKEYCODE_NUMPAD_0:
284     case AKEYCODE_NUMPAD_1:
285     case AKEYCODE_NUMPAD_2:
286     case AKEYCODE_NUMPAD_3:
287     case AKEYCODE_NUMPAD_4:
288     case AKEYCODE_NUMPAD_5:
289     case AKEYCODE_NUMPAD_6:
290     case AKEYCODE_NUMPAD_7:
291     case AKEYCODE_NUMPAD_8:
292     case AKEYCODE_NUMPAD_9:
293     case AKEYCODE_NUMPAD_DIVIDE:
294     case AKEYCODE_NUMPAD_MULTIPLY:
295     case AKEYCODE_NUMPAD_SUBTRACT:
296     case AKEYCODE_NUMPAD_ADD:
297     case AKEYCODE_NUMPAD_DOT:
298     case AKEYCODE_NUMPAD_COMMA:
299     case AKEYCODE_NUMPAD_EQUALS:
300     case AKEYCODE_NUMPAD_LEFT_PAREN:
301     case AKEYCODE_NUMPAD_RIGHT_PAREN:
302 
303     case AKEYCODE_YEN:  // yen sign key
304     case AKEYCODE_RO:   // Japanese Ro key
305       return KEY_NAME_INDEX_USE_STRING;
306 
307     case AKEYCODE_NUM:  // XXX Not sure
308     case AKEYCODE_PICTSYMBOLS:
309 
310     case AKEYCODE_BUTTON_A:
311     case AKEYCODE_BUTTON_B:
312     case AKEYCODE_BUTTON_C:
313     case AKEYCODE_BUTTON_X:
314     case AKEYCODE_BUTTON_Y:
315     case AKEYCODE_BUTTON_Z:
316     case AKEYCODE_BUTTON_L1:
317     case AKEYCODE_BUTTON_R1:
318     case AKEYCODE_BUTTON_L2:
319     case AKEYCODE_BUTTON_R2:
320     case AKEYCODE_BUTTON_THUMBL:
321     case AKEYCODE_BUTTON_THUMBR:
322     case AKEYCODE_BUTTON_START:
323     case AKEYCODE_BUTTON_SELECT:
324     case AKEYCODE_BUTTON_MODE:
325 
326     case AKEYCODE_MEDIA_CLOSE:
327 
328     case AKEYCODE_BUTTON_1:
329     case AKEYCODE_BUTTON_2:
330     case AKEYCODE_BUTTON_3:
331     case AKEYCODE_BUTTON_4:
332     case AKEYCODE_BUTTON_5:
333     case AKEYCODE_BUTTON_6:
334     case AKEYCODE_BUTTON_7:
335     case AKEYCODE_BUTTON_8:
336     case AKEYCODE_BUTTON_9:
337     case AKEYCODE_BUTTON_10:
338     case AKEYCODE_BUTTON_11:
339     case AKEYCODE_BUTTON_12:
340     case AKEYCODE_BUTTON_13:
341     case AKEYCODE_BUTTON_14:
342     case AKEYCODE_BUTTON_15:
343     case AKEYCODE_BUTTON_16:
344       return KEY_NAME_INDEX_Unidentified;
345 
346     case AKEYCODE_UNKNOWN:
347       MOZ_ASSERT(action != AKEY_EVENT_ACTION_MULTIPLE,
348                  "Don't call this when action is AKEY_EVENT_ACTION_MULTIPLE!");
349       // It's actually an unknown key if the action isn't ACTION_MULTIPLE.
350       // However, it might cause text input.  So, let's check the value.
351       return domPrintableKeyValue ? KEY_NAME_INDEX_USE_STRING
352                                   : KEY_NAME_INDEX_Unidentified;
353 
354     default:
355       ALOG(
356           "ConvertAndroidKeyCodeToKeyNameIndex: "
357           "No DOM key name index for Android keycode %d",
358           keyCode);
359       return KEY_NAME_INDEX_Unidentified;
360   }
361 }
362 
ConvertAndroidScanCodeToCodeNameIndex(int32_t scanCode)363 static CodeNameIndex ConvertAndroidScanCodeToCodeNameIndex(int32_t scanCode) {
364   switch (scanCode) {
365 #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
366   case aNativeKey:                                                       \
367     return aCodeNameIndex;
368 
369 #include "NativeKeyToDOMCodeName.h"
370 
371 #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX
372 
373     default:
374       return CODE_NAME_INDEX_UNKNOWN;
375   }
376 }
377 
InitKeyEvent(WidgetKeyboardEvent & aEvent,int32_t aAction,int32_t aKeyCode,int32_t aScanCode,int32_t aMetaState,int64_t aTime,int32_t aDomPrintableKeyValue,int32_t aRepeatCount,int32_t aFlags)378 static void InitKeyEvent(WidgetKeyboardEvent& aEvent, int32_t aAction,
379                          int32_t aKeyCode, int32_t aScanCode,
380                          int32_t aMetaState, int64_t aTime,
381                          int32_t aDomPrintableKeyValue, int32_t aRepeatCount,
382                          int32_t aFlags) {
383   const uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(aKeyCode);
384 
385   aEvent.mModifiers = nsWindow::GetModifiers(aMetaState);
386   aEvent.mKeyCode = domKeyCode;
387 
388   aEvent.mIsRepeat =
389       (aEvent.mMessage == eKeyDown || aEvent.mMessage == eKeyPress) &&
390       ((aFlags & java::sdk::KeyEvent::FLAG_LONG_PRESS) || aRepeatCount);
391 
392   aEvent.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(
393       aKeyCode, aAction, aDomPrintableKeyValue);
394   aEvent.mCodeNameIndex = ConvertAndroidScanCodeToCodeNameIndex(aScanCode);
395 
396   if (aEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
397       aDomPrintableKeyValue) {
398     aEvent.mKeyValue = char16_t(aDomPrintableKeyValue);
399   }
400 
401   aEvent.mLocation =
402       WidgetKeyboardEvent::ComputeLocationFromCodeValue(aEvent.mCodeNameIndex);
403   aEvent.mTime = aTime;
404   aEvent.mTimeStamp = nsWindow::GetEventTimeStamp(aTime);
405 }
406 
ConvertAndroidColor(uint32_t aArgb)407 static nscolor ConvertAndroidColor(uint32_t aArgb) {
408   return NS_RGBA((aArgb & 0x00ff0000) >> 16, (aArgb & 0x0000ff00) >> 8,
409                  (aArgb & 0x000000ff), (aArgb & 0xff000000) >> 24);
410 }
411 
ConvertRectArrayToJavaRectFArray(const nsTArray<LayoutDeviceIntRect> & aRects)412 static jni::ObjectArray::LocalRef ConvertRectArrayToJavaRectFArray(
413     const nsTArray<LayoutDeviceIntRect>& aRects) {
414   const size_t length = aRects.Length();
415   auto rects = jni::ObjectArray::New<java::sdk::RectF>(length);
416 
417   for (size_t i = 0; i < length; i++) {
418     const LayoutDeviceIntRect& tmp = aRects[i];
419 
420     auto rect = java::sdk::RectF::New(tmp.x, tmp.y, tmp.XMost(), tmp.YMost());
421     rects->SetElement(i, rect);
422   }
423   return rects;
424 }
425 
426 namespace mozilla {
427 namespace widget {
428 
429 NS_IMPL_ISUPPORTS(GeckoEditableSupport, TextEventDispatcherListener,
430                   nsISupportsWeakReference)
431 
432 // This is the blocker helper class whether disposing GeckoEditableChild now.
433 // During JNI call from GeckoEditableChild, we shouldn't dispose it.
434 class MOZ_RAII AutoGeckoEditableBlocker final {
435  public:
AutoGeckoEditableBlocker(GeckoEditableSupport * aGeckoEditableSupport)436   explicit AutoGeckoEditableBlocker(GeckoEditableSupport* aGeckoEditableSupport)
437       : mGeckoEditable(aGeckoEditableSupport) {
438     mGeckoEditable->AddBlocker();
439   }
~AutoGeckoEditableBlocker()440   ~AutoGeckoEditableBlocker() { mGeckoEditable->ReleaseBlocker(); }
441 
442  private:
443   RefPtr<GeckoEditableSupport> mGeckoEditable;
444 };
445 
GetComposition() const446 RefPtr<TextComposition> GeckoEditableSupport::GetComposition() const {
447   nsCOMPtr<nsIWidget> widget = GetWidget();
448   return widget ? IMEStateManager::GetTextCompositionFor(widget) : nullptr;
449 }
450 
RemoveComposition(RemoveCompositionFlag aFlag)451 bool GeckoEditableSupport::RemoveComposition(RemoveCompositionFlag aFlag) {
452   if (!mDispatcher || !mDispatcher->IsComposing()) {
453     return false;
454   }
455 
456   nsEventStatus status = nsEventStatus_eIgnore;
457 
458   NS_ENSURE_SUCCESS(BeginInputTransaction(mDispatcher), false);
459   mDispatcher->CommitComposition(
460       status, aFlag == CANCEL_IME_COMPOSITION ? &EmptyString() : nullptr);
461   return true;
462 }
463 
OnKeyEvent(int32_t aAction,int32_t aKeyCode,int32_t aScanCode,int32_t aMetaState,int32_t aKeyPressMetaState,int64_t aTime,int32_t aDomPrintableKeyValue,int32_t aRepeatCount,int32_t aFlags,bool aIsSynthesizedImeKey,jni::Object::Param aOriginalEvent)464 void GeckoEditableSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
465                                       int32_t aScanCode, int32_t aMetaState,
466                                       int32_t aKeyPressMetaState, int64_t aTime,
467                                       int32_t aDomPrintableKeyValue,
468                                       int32_t aRepeatCount, int32_t aFlags,
469                                       bool aIsSynthesizedImeKey,
470                                       jni::Object::Param aOriginalEvent) {
471   AutoGeckoEditableBlocker blocker(this);
472 
473   nsCOMPtr<nsIWidget> widget = GetWidget();
474   RefPtr<TextEventDispatcher> dispatcher =
475       mDispatcher ? mDispatcher.get()
476       : widget    ? widget->GetTextEventDispatcher()
477                   : nullptr;
478   NS_ENSURE_TRUE_VOID(dispatcher && widget);
479 
480   if (!aIsSynthesizedImeKey) {
481     if (nsWindow* window = GetNsWindow()) {
482       window->UserActivity();
483     }
484   } else if (aIsSynthesizedImeKey && mIMEMaskEventsCount > 0) {
485     // Don't synthesize editor keys when not focused.
486     return;
487   }
488 
489   EventMessage msg;
490   if (aAction == java::sdk::KeyEvent::ACTION_DOWN) {
491     msg = eKeyDown;
492   } else if (aAction == java::sdk::KeyEvent::ACTION_UP) {
493     msg = eKeyUp;
494   } else if (aAction == java::sdk::KeyEvent::ACTION_MULTIPLE) {
495     // Keys with multiple action are handled in Java,
496     // and we should never see one here
497     MOZ_CRASH("Cannot handle key with multiple action");
498   } else {
499     NS_WARNING("Unknown key action event");
500     return;
501   }
502 
503   nsEventStatus status = nsEventStatus_eIgnore;
504   WidgetKeyboardEvent event(true, msg, widget);
505   InitKeyEvent(event, aAction, aKeyCode, aScanCode, aMetaState, aTime,
506                aDomPrintableKeyValue, aRepeatCount, aFlags);
507 
508   if (nsIWidget::UsePuppetWidgets()) {
509     // Don't use native key bindings.
510     event.PreventNativeKeyBindings();
511   }
512 
513   if (aIsSynthesizedImeKey) {
514     // Keys synthesized by Java IME code are saved in the mIMEKeyEvents
515     // array until the next IME_REPLACE_TEXT event, at which point
516     // these keys are dispatched in sequence.
517     mIMEKeyEvents.AppendElement(UniquePtr<WidgetEvent>(event.Duplicate()));
518   } else {
519     NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(dispatcher));
520     dispatcher->DispatchKeyboardEvent(msg, event, status);
521     if (widget->Destroyed() || status == nsEventStatus_eConsumeNoDefault) {
522       // Skip default processing.
523       return;
524     }
525     mEditable->OnDefaultKeyEvent(aOriginalEvent);
526   }
527 
528   // Only send keypress after keydown.
529   if (msg != eKeyDown) {
530     return;
531   }
532 
533   WidgetKeyboardEvent pressEvent(true, eKeyPress, widget);
534   InitKeyEvent(pressEvent, aAction, aKeyCode, aScanCode, aKeyPressMetaState,
535                aTime, aDomPrintableKeyValue, aRepeatCount, aFlags);
536 
537   if (nsIWidget::UsePuppetWidgets()) {
538     // Don't use native key bindings.
539     pressEvent.PreventNativeKeyBindings();
540   }
541 
542   if (aIsSynthesizedImeKey) {
543     mIMEKeyEvents.AppendElement(UniquePtr<WidgetEvent>(pressEvent.Duplicate()));
544   } else {
545     dispatcher->MaybeDispatchKeypressEvents(pressEvent, status);
546   }
547 }
548 
549 /*
550  * Send dummy key events for pages that are unaware of input events,
551  * to provide web compatibility for pages that depend on key events.
552  */
SendIMEDummyKeyEvent(nsIWidget * aWidget,EventMessage msg)553 void GeckoEditableSupport::SendIMEDummyKeyEvent(nsIWidget* aWidget,
554                                                 EventMessage msg) {
555   AutoGeckoEditableBlocker blocker(this);
556 
557   nsEventStatus status = nsEventStatus_eIgnore;
558   MOZ_ASSERT(mDispatcher);
559 
560   WidgetKeyboardEvent event(true, msg, aWidget);
561   event.mTime = PR_Now() / 1000;
562   // TODO: If we can know scan code of the key event which caused replacing
563   //       composition string, we should set mCodeNameIndex here.  Then,
564   //       we should rename this method because it becomes not a "dummy"
565   //       keyboard event.
566   event.mKeyCode = NS_VK_PROCESSKEY;
567   event.mKeyNameIndex = KEY_NAME_INDEX_Process;
568   // KeyboardEvents marked as "processed by IME" shouldn't cause any edit
569   // actions.  So, we should set their native key binding to none before
570   // dispatch to avoid crash on PuppetWidget and avoid running redundant
571   // path to look for native key bindings.
572   event.PreventNativeKeyBindings();
573   NS_ENSURE_SUCCESS_VOID(BeginInputTransaction(mDispatcher));
574   mDispatcher->DispatchKeyboardEvent(msg, event, status);
575 }
576 
AddIMETextChange(const IMENotification::TextChangeDataBase & aChange)577 void GeckoEditableSupport::AddIMETextChange(
578     const IMENotification::TextChangeDataBase& aChange) {
579   mIMEPendingTextChange.MergeWith(aChange);
580 
581   // We may not be in the middle of flushing,
582   // in which case this flag is meaningless.
583   mIMETextChangedDuringFlush = true;
584 }
585 
PostFlushIMEChanges()586 void GeckoEditableSupport::PostFlushIMEChanges() {
587   if (mIMEPendingTextChange.IsValid() || mIMESelectionChanged) {
588     // Already posted
589     return;
590   }
591 
592   RefPtr<GeckoEditableSupport> self(this);
593 
594   nsAppShell::PostEvent([this, self] {
595     nsCOMPtr<nsIWidget> widget = GetWidget();
596     if (widget && !widget->Destroyed()) {
597       FlushIMEChanges();
598     }
599   });
600 }
601 
FlushIMEChanges(FlushChangesFlag aFlags)602 void GeckoEditableSupport::FlushIMEChanges(FlushChangesFlag aFlags) {
603   // Only send change notifications if we are *not* masking events,
604   // i.e. if we have a focused editor,
605   NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount);
606 
607   if (mIMEDelaySynchronizeReply && mIMEActiveCompositionCount > 0) {
608     // We are still expecting more composition events to be handled. Once
609     // that happens, FlushIMEChanges will be called again.
610     return;
611   }
612 
613   nsCOMPtr<nsIWidget> widget = GetWidget();
614   NS_ENSURE_TRUE_VOID(widget);
615 
616   struct TextRecord {
617     TextRecord() : start(-1), oldEnd(-1), newEnd(-1) {}
618 
619     bool IsValid() const { return start >= 0; }
620 
621     nsString text;
622     int32_t start;
623     int32_t oldEnd;
624     int32_t newEnd;
625   };
626   TextRecord textTransaction;
627 
628   nsEventStatus status = nsEventStatus_eIgnore;
629   bool causedOnlyByComposition = mIMEPendingTextChange.IsValid() &&
630                                  mIMEPendingTextChange.mCausedOnlyByComposition;
631   mIMETextChangedDuringFlush = false;
632 
633   auto shouldAbort = [=](bool aForce) -> bool {
634     if (!aForce && !mIMETextChangedDuringFlush) {
635       return false;
636     }
637     // A query event could have triggered more text changes to come in, as
638     // indicated by our flag. If that happens, try flushing IME changes
639     // again.
640     if (aFlags == FLUSH_FLAG_NONE) {
641       FlushIMEChanges(FLUSH_FLAG_RETRY);
642     } else {
643       // Don't retry if already retrying, to avoid infinite loops.
644       __android_log_print(ANDROID_LOG_WARN, "GeckoEditableSupport",
645                           "Already retrying IME flush");
646     }
647     return true;
648   };
649 
650   if (mIMEPendingTextChange.IsValid() &&
651       (mIMEPendingTextChange.mStartOffset !=
652            mIMEPendingTextChange.mRemovedEndOffset ||
653        mIMEPendingTextChange.mStartOffset !=
654            mIMEPendingTextChange.mAddedEndOffset)) {
655     WidgetQueryContentEvent queryTextContentEvent(true, eQueryTextContent,
656                                                   widget);
657 
658     if (mIMEPendingTextChange.mAddedEndOffset !=
659         mIMEPendingTextChange.mStartOffset) {
660       queryTextContentEvent.InitForQueryTextContent(
661           mIMEPendingTextChange.mStartOffset,
662           mIMEPendingTextChange.mAddedEndOffset -
663               mIMEPendingTextChange.mStartOffset);
664       widget->DispatchEvent(&queryTextContentEvent, status);
665 
666       if (shouldAbort(NS_WARN_IF(queryTextContentEvent.Failed()))) {
667         return;
668       }
669 
670       textTransaction.text = queryTextContentEvent.mReply->DataRef();
671     }
672 
673     textTransaction.start =
674         static_cast<int32_t>(mIMEPendingTextChange.mStartOffset);
675     textTransaction.oldEnd =
676         static_cast<int32_t>(mIMEPendingTextChange.mRemovedEndOffset);
677     textTransaction.newEnd =
678         static_cast<int32_t>(mIMEPendingTextChange.mAddedEndOffset);
679   }
680 
681   int32_t selStart = -1;
682   int32_t selEnd = -1;
683 
684   if (mIMESelectionChanged) {
685     if (mCachedSelection.IsValid()) {
686       selStart = mCachedSelection.mStartOffset;
687       selEnd = mCachedSelection.mEndOffset;
688     } else {
689       // XXX Unfortunately we don't know current selection via selection
690       //     change notification.
691       //     eQuerySelectedText might be newer data than text change data.
692       //     It means that GeckoEditableChild.onSelectionChange may throw
693       //     IllegalArgumentException since we don't merge with newer text
694       //     change.
695       WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
696                                                      widget);
697       widget->DispatchEvent(&querySelectedTextEvent, status);
698 
699       if (shouldAbort(
700               NS_WARN_IF(querySelectedTextEvent.DidNotFindSelection()))) {
701         return;
702       }
703 
704       selStart =
705           static_cast<int32_t>(querySelectedTextEvent.mReply->AnchorOffset());
706       selEnd =
707           static_cast<int32_t>(querySelectedTextEvent.mReply->FocusOffset());
708     }
709 
710     if (aFlags == FLUSH_FLAG_RECOVER && textTransaction.IsValid()) {
711       // Sometimes we get out-of-bounds selection during recovery.
712       // Limit the offsets so we don't crash.
713       const int32_t end = textTransaction.start + textTransaction.text.Length();
714       selStart = std::min(selStart, end);
715       selEnd = std::min(selEnd, end);
716     }
717   }
718 
719   JNIEnv* const env = jni::GetGeckoThreadEnv();
720   auto flushOnException = [=]() -> bool {
721     if (!env->ExceptionCheck()) {
722       return false;
723     }
724     if (aFlags != FLUSH_FLAG_RECOVER) {
725       // First time seeing an exception; try flushing text.
726       env->ExceptionClear();
727       __android_log_print(ANDROID_LOG_WARN, "GeckoEditableSupport",
728                           "Recovering from IME exception");
729       FlushIMEText(FLUSH_FLAG_RECOVER);
730     } else {
731       // Give up because we've already tried.
732 #ifdef RELEASE_OR_BETA
733       env->ExceptionClear();
734 #else
735       MOZ_CATCH_JNI_EXCEPTION(env);
736 #endif
737     }
738     return true;
739   };
740 
741   // Commit the text change and selection change transaction.
742   mIMEPendingTextChange.Clear();
743 
744   if (textTransaction.IsValid()) {
745     mEditable->OnTextChange(textTransaction.text, textTransaction.start,
746                             textTransaction.oldEnd, textTransaction.newEnd);
747     if (flushOnException()) {
748       return;
749     }
750   }
751 
752   while (mIMEDelaySynchronizeReply && mIMEActiveSynchronizeCount) {
753     mIMEActiveSynchronizeCount--;
754     mEditable->NotifyIME(EditableListener::NOTIFY_IME_REPLY_EVENT);
755   }
756   mIMEDelaySynchronizeReply = false;
757   mIMEActiveSynchronizeCount = 0;
758   mIMEActiveCompositionCount = 0;
759 
760   if (mIMESelectionChanged) {
761     mIMESelectionChanged = false;
762     if (mDispatcher) {
763       // mCausedOnlyByComposition may be true on committing text.
764       // So even if true, there is no composition.
765       causedOnlyByComposition &= mDispatcher->IsComposing();
766     }
767     mEditable->OnSelectionChange(selStart, selEnd, causedOnlyByComposition);
768     flushOnException();
769   }
770 }
771 
FlushIMEText(FlushChangesFlag aFlags)772 void GeckoEditableSupport::FlushIMEText(FlushChangesFlag aFlags) {
773   NS_WARNING_ASSERTION(
774       !mIMEDelaySynchronizeReply || !mIMEActiveCompositionCount,
775       "Cannot synchronize Java text with Gecko text");
776 
777   // Notify Java of the newly focused content
778   mIMEPendingTextChange.Clear();
779   mIMESelectionChanged = true;
780 
781   // Use 'INT32_MAX / 2' here because subsequent text changes might combine
782   // with this text change, and overflow might occur if we just use
783   // INT32_MAX.
784   IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE);
785   notification.mTextChangeData.mStartOffset = 0;
786   notification.mTextChangeData.mRemovedEndOffset = INT32_MAX / 2;
787   notification.mTextChangeData.mAddedEndOffset = INT32_MAX / 2;
788   NotifyIME(mDispatcher, notification);
789 
790   FlushIMEChanges(aFlags);
791 }
792 
UpdateCompositionRects()793 void GeckoEditableSupport::UpdateCompositionRects() {
794   nsCOMPtr<nsIWidget> widget = GetWidget();
795   RefPtr<TextComposition> composition(GetComposition());
796   NS_ENSURE_TRUE_VOID(mDispatcher && widget);
797 
798   if (!composition) {
799     return;
800   }
801 
802   nsEventStatus status = nsEventStatus_eIgnore;
803   uint32_t offset = composition->NativeOffsetOfStartComposition();
804   WidgetQueryContentEvent queryTextRectsEvent(true, eQueryTextRectArray,
805                                               widget);
806   queryTextRectsEvent.InitForQueryTextRectArray(offset,
807                                                 composition->String().Length());
808   widget->DispatchEvent(&queryTextRectsEvent, status);
809 
810   auto rects = ConvertRectArrayToJavaRectFArray(
811       queryTextRectsEvent.Succeeded()
812           ? queryTextRectsEvent.mReply->mRectArray
813           : CopyableTArray<mozilla::LayoutDeviceIntRect>());
814 
815   mEditable->UpdateCompositionRects(rects);
816 }
817 
OnImeSynchronize()818 void GeckoEditableSupport::OnImeSynchronize() {
819   AutoGeckoEditableBlocker blocker(this);
820 
821   if (mIMEDelaySynchronizeReply) {
822     // If we are waiting for other events to reply,
823     // queue this reply as well.
824     mIMEActiveSynchronizeCount++;
825     return;
826   }
827   if (!mIMEMaskEventsCount) {
828     FlushIMEChanges();
829   }
830   mEditable->NotifyIME(EditableListener::NOTIFY_IME_REPLY_EVENT);
831 }
832 
OnImeReplaceText(int32_t aStart,int32_t aEnd,jni::String::Param aText)833 void GeckoEditableSupport::OnImeReplaceText(int32_t aStart, int32_t aEnd,
834                                             jni::String::Param aText) {
835   AutoGeckoEditableBlocker blocker(this);
836 
837   if (DoReplaceText(aStart, aEnd, aText)) {
838     mIMEDelaySynchronizeReply = true;
839   }
840 
841   OnImeSynchronize();
842 }
843 
DoReplaceText(int32_t aStart,int32_t aEnd,jni::String::Param aText)844 bool GeckoEditableSupport::DoReplaceText(int32_t aStart, int32_t aEnd,
845                                          jni::String::Param aText) {
846   ALOGIME("IME: IME_REPLACE_TEXT: text=\"%s\"",
847           NS_ConvertUTF16toUTF8(aText->ToString()).get());
848 
849   // Return true if processed and we should reply to the OnImeReplaceText
850   // event later. Return false if _not_ processed and we should reply to the
851   // OnImeReplaceText event now.
852 
853   if (mIMEMaskEventsCount > 0) {
854     // Not focused; still reply to events, but don't do anything else.
855     return false;
856   }
857 
858   if (nsWindow* window = GetNsWindow()) {
859     window->UserActivity();
860   }
861 
862   /*
863       Replace text in Gecko thread from aStart to aEnd with the string text.
864   */
865   nsCOMPtr<nsIWidget> widget = GetWidget();
866   NS_ENSURE_TRUE(mDispatcher && widget, false);
867   NS_ENSURE_SUCCESS(BeginInputTransaction(mDispatcher), false);
868 
869   RefPtr<TextComposition> composition(GetComposition());
870   MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
871 
872   nsString string(aText->ToString());
873   const bool composing = !mIMERanges->IsEmpty();
874   nsEventStatus status = nsEventStatus_eIgnore;
875   bool textChanged = composing;
876   // Whether deleting content before setting or committing composition text.
877   bool performDeletion = false;
878   // Dispatch composition start to set current composition.
879   bool needDispatchCompositionStart = false;
880 
881   if (!mIMEKeyEvents.IsEmpty() || !composition || !mDispatcher->IsComposing() ||
882       uint32_t(aStart) != composition->NativeOffsetOfStartComposition() ||
883       uint32_t(aEnd) != composition->NativeOffsetOfStartComposition() +
884                             composition->String().Length()) {
885     // Only start a new composition if we have key events,
886     // if we don't have an existing composition, or
887     // the replaced text does not match our composition.
888     textChanged |= RemoveComposition();
889 
890 #ifdef NIGHTLY_BUILD
891     {
892       nsEventStatus status = nsEventStatus_eIgnore;
893       WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
894                                                      widget);
895       widget->DispatchEvent(&querySelectedTextEvent, status);
896       if (querySelectedTextEvent.Succeeded()) {
897         ALOGIME(
898             "IME: Current selection: %s",
899             ToString(querySelectedTextEvent.mReply->mOffsetAndData).c_str());
900       }
901     }
902 #endif
903 
904     // If aStart or aEnd is negative value, we use current selection instead
905     // of updating the selection.
906     if (aStart >= 0 && aEnd >= 0) {
907       // Use text selection to set target position(s) for
908       // insert, or replace, of text.
909       WidgetSelectionEvent event(true, eSetSelection, widget);
910       event.mOffset = uint32_t(aStart);
911       event.mLength = uint32_t(aEnd - aStart);
912       event.mExpandToClusterBoundary = false;
913       event.mReason = nsISelectionListener::IME_REASON;
914       widget->DispatchEvent(&event, status);
915     }
916 
917     if (!mIMEKeyEvents.IsEmpty()) {
918       bool ignoreNextKeyPress = false;
919       for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
920         const auto event = mIMEKeyEvents[i]->AsKeyboardEvent();
921         // widget for duplicated events is initially nullptr.
922         event->mWidget = widget;
923 
924         status = nsEventStatus_eIgnore;
925         if (event->mMessage != eKeyPress) {
926           mDispatcher->DispatchKeyboardEvent(event->mMessage, *event, status);
927           // Skip default processing. It means that next key press shouldn't
928           // be dispatched.
929           ignoreNextKeyPress = event->mMessage == eKeyDown &&
930                                status == nsEventStatus_eConsumeNoDefault;
931         } else {
932           if (ignoreNextKeyPress) {
933             // Don't dispatch key press since previous key down is consumed.
934             ignoreNextKeyPress = false;
935             continue;
936           }
937           mDispatcher->MaybeDispatchKeypressEvents(*event, status);
938           if (status == nsEventStatus_eConsumeNoDefault) {
939             textChanged = true;
940           }
941         }
942         if (!mDispatcher || widget->Destroyed()) {
943           // Don't wait for any text change event.
944           textChanged = false;
945           break;
946         }
947       }
948       mIMEKeyEvents.Clear();
949       return textChanged;
950     }
951 
952     if (aStart != aEnd) {
953       if (composing) {
954         // Actually Gecko doesn't start composition, so it is unnecessary to
955         // delete content before setting composition string.
956         needDispatchCompositionStart = true;
957       } else {
958         // Perform a deletion first.
959         performDeletion = true;
960       }
961     }
962   } else if (composition->String().Equals(string)) {
963     /* If the new text is the same as the existing composition text,
964      * the NS_COMPOSITION_CHANGE event does not generate a text
965      * change notification. However, the Java side still expects
966      * one, so we manually generate a notification. */
967     IMENotification::TextChangeData dummyChange(aStart, aEnd, aEnd, false,
968                                                 false);
969     PostFlushIMEChanges();
970     mIMESelectionChanged = true;
971     AddIMETextChange(dummyChange);
972     textChanged = true;
973   }
974 
975   if (StaticPrefs::
976           intl_ime_hack_on_any_apps_fire_key_events_for_composition() ||
977       mInputContext.mMayBeIMEUnaware) {
978     SendIMEDummyKeyEvent(widget, eKeyDown);
979     if (!mDispatcher || widget->Destroyed()) {
980       return false;
981     }
982   }
983 
984   if (needDispatchCompositionStart) {
985     // StartComposition sets composition string from selected string.
986     nsEventStatus status = nsEventStatus_eIgnore;
987     mDispatcher->StartComposition(status);
988     if (!mDispatcher || widget->Destroyed()) {
989       return false;
990     }
991   } else if (performDeletion) {
992     WidgetContentCommandEvent event(true, eContentCommandDelete, widget);
993     event.mTime = PR_Now() / 1000;
994     widget->DispatchEvent(&event, status);
995     if (!mDispatcher || widget->Destroyed()) {
996       return false;
997     }
998     textChanged = true;
999   }
1000 
1001   if (composing) {
1002     mDispatcher->SetPendingComposition(string, mIMERanges);
1003     mDispatcher->FlushPendingComposition(status);
1004     mIMEActiveCompositionCount++;
1005     // Ensure IME ranges are empty.
1006     mIMERanges->Clear();
1007   } else if (!string.IsEmpty() || mDispatcher->IsComposing()) {
1008     mDispatcher->CommitComposition(status, &string);
1009     mIMEActiveCompositionCount++;
1010     textChanged = true;
1011   }
1012   if (!mDispatcher || widget->Destroyed()) {
1013     return false;
1014   }
1015 
1016   if (StaticPrefs::
1017           intl_ime_hack_on_any_apps_fire_key_events_for_composition() ||
1018       mInputContext.mMayBeIMEUnaware) {
1019     SendIMEDummyKeyEvent(widget, eKeyUp);
1020     // Widget may be destroyed after dispatching the above event.
1021   }
1022   return textChanged;
1023 }
1024 
OnImeAddCompositionRange(int32_t aStart,int32_t aEnd,int32_t aRangeType,int32_t aRangeStyle,int32_t aRangeLineStyle,bool aRangeBoldLine,int32_t aRangeForeColor,int32_t aRangeBackColor,int32_t aRangeLineColor)1025 void GeckoEditableSupport::OnImeAddCompositionRange(
1026     int32_t aStart, int32_t aEnd, int32_t aRangeType, int32_t aRangeStyle,
1027     int32_t aRangeLineStyle, bool aRangeBoldLine, int32_t aRangeForeColor,
1028     int32_t aRangeBackColor, int32_t aRangeLineColor) {
1029   AutoGeckoEditableBlocker blocker(this);
1030 
1031   if (mIMEMaskEventsCount > 0) {
1032     // Not focused.
1033     return;
1034   }
1035 
1036   TextRange range;
1037   range.mStartOffset = aStart;
1038   range.mEndOffset = aEnd;
1039   range.mRangeType = ToTextRangeType(aRangeType);
1040   range.mRangeStyle.mDefinedStyles = aRangeStyle;
1041   range.mRangeStyle.mLineStyle = TextRangeStyle::ToLineStyle(aRangeLineStyle);
1042   range.mRangeStyle.mIsBoldLine = aRangeBoldLine;
1043   range.mRangeStyle.mForegroundColor =
1044       ConvertAndroidColor(uint32_t(aRangeForeColor));
1045   range.mRangeStyle.mBackgroundColor =
1046       ConvertAndroidColor(uint32_t(aRangeBackColor));
1047   range.mRangeStyle.mUnderlineColor =
1048       ConvertAndroidColor(uint32_t(aRangeLineColor));
1049   mIMERanges->AppendElement(range);
1050 }
1051 
OnImeUpdateComposition(int32_t aStart,int32_t aEnd,int32_t aFlags)1052 void GeckoEditableSupport::OnImeUpdateComposition(int32_t aStart, int32_t aEnd,
1053                                                   int32_t aFlags) {
1054   AutoGeckoEditableBlocker blocker(this);
1055 
1056   if (DoUpdateComposition(aStart, aEnd, aFlags)) {
1057     mIMEDelaySynchronizeReply = true;
1058   }
1059 }
1060 
DoUpdateComposition(int32_t aStart,int32_t aEnd,int32_t aFlags)1061 bool GeckoEditableSupport::DoUpdateComposition(int32_t aStart, int32_t aEnd,
1062                                                int32_t aFlags) {
1063   if (mIMEMaskEventsCount > 0) {
1064     // Not focused.
1065     return false;
1066   }
1067 
1068   nsCOMPtr<nsIWidget> widget = GetWidget();
1069   nsEventStatus status = nsEventStatus_eIgnore;
1070   NS_ENSURE_TRUE(mDispatcher && widget, false);
1071 
1072   const bool keepCurrent =
1073       !!(aFlags & java::GeckoEditableChild::FLAG_KEEP_CURRENT_COMPOSITION);
1074 
1075   // A composition with no ranges means we want to set the selection.
1076   if (mIMERanges->IsEmpty()) {
1077     if (keepCurrent && mDispatcher->IsComposing()) {
1078       // Don't set selection if we want to keep current composition.
1079       return false;
1080     }
1081 
1082     MOZ_ASSERT(aStart >= 0 && aEnd >= 0);
1083     const bool compositionChanged = RemoveComposition();
1084 
1085     WidgetSelectionEvent selEvent(true, eSetSelection, widget);
1086     selEvent.mOffset = std::min(aStart, aEnd);
1087     selEvent.mLength = std::max(aStart, aEnd) - selEvent.mOffset;
1088     selEvent.mReversed = aStart > aEnd;
1089     selEvent.mExpandToClusterBoundary = false;
1090     widget->DispatchEvent(&selEvent, status);
1091     return compositionChanged;
1092   }
1093 
1094   /**
1095    * Update the composition from aStart to aEnd using information from added
1096    * ranges. This is only used for visual indication and does not affect the
1097    * text content.  Only the offsets are specified and not the text content
1098    * to eliminate the possibility of this event altering the text content
1099    * unintentionally.
1100    */
1101   nsString string;
1102   RefPtr<TextComposition> composition(GetComposition());
1103   MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent());
1104 
1105   if (!composition || !mDispatcher->IsComposing() ||
1106       uint32_t(aStart) != composition->NativeOffsetOfStartComposition() ||
1107       uint32_t(aEnd) != composition->NativeOffsetOfStartComposition() +
1108                             composition->String().Length()) {
1109     if (keepCurrent) {
1110       // Don't start a new composition if we want to keep the current one.
1111       mIMERanges->Clear();
1112       return false;
1113     }
1114 
1115     // Only start new composition if we don't have an existing one,
1116     // or if the existing composition doesn't match the new one.
1117     RemoveComposition();
1118 
1119     {
1120       WidgetSelectionEvent event(true, eSetSelection, widget);
1121       event.mOffset = uint32_t(aStart);
1122       event.mLength = uint32_t(aEnd - aStart);
1123       event.mExpandToClusterBoundary = false;
1124       event.mReason = nsISelectionListener::IME_REASON;
1125       widget->DispatchEvent(&event, status);
1126     }
1127 
1128     {
1129       WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
1130                                                      widget);
1131       widget->DispatchEvent(&querySelectedTextEvent, status);
1132       MOZ_ASSERT(querySelectedTextEvent.Succeeded());
1133       if (querySelectedTextEvent.FoundSelection()) {
1134         string = querySelectedTextEvent.mReply->DataRef();
1135       }
1136     }
1137   } else {
1138     // If the new composition matches the existing composition,
1139     // reuse the old composition.
1140     string = composition->String();
1141   }
1142 
1143   ALOGIME("IME: IME_SET_TEXT: text=\"%s\", length=%zu, range=%zu",
1144           NS_ConvertUTF16toUTF8(string).get(), string.Length(),
1145           mIMERanges->Length());
1146 
1147   if (NS_WARN_IF(NS_FAILED(BeginInputTransaction(mDispatcher)))) {
1148     mIMERanges->Clear();
1149     return false;
1150   }
1151   mDispatcher->SetPendingComposition(string, mIMERanges);
1152   mDispatcher->FlushPendingComposition(status);
1153   mIMEActiveCompositionCount++;
1154   mIMERanges->Clear();
1155   return true;
1156 }
1157 
OnImeRequestCursorUpdates(int aRequestMode)1158 void GeckoEditableSupport::OnImeRequestCursorUpdates(int aRequestMode) {
1159   AutoGeckoEditableBlocker blocker(this);
1160 
1161   if (aRequestMode == EditableClient::ONE_SHOT) {
1162     UpdateCompositionRects();
1163     return;
1164   }
1165 
1166   mIMEMonitorCursor = (aRequestMode == EditableClient::START_MONITOR);
1167 }
1168 
1169 class MOZ_STACK_CLASS AutoSelectionRestore final {
1170  public:
AutoSelectionRestore(nsIWidget * widget,TextEventDispatcher * dispatcher)1171   explicit AutoSelectionRestore(nsIWidget* widget,
1172                                 TextEventDispatcher* dispatcher)
1173       : mWidget(widget), mDispatcher(dispatcher) {
1174     MOZ_ASSERT(widget);
1175     if (!dispatcher || !dispatcher->IsComposing()) {
1176       mOffset = UINT32_MAX;
1177       mLength = UINT32_MAX;
1178       return;
1179     }
1180     WidgetQueryContentEvent querySelectedTextEvent(true, eQuerySelectedText,
1181                                                    widget);
1182     nsEventStatus status = nsEventStatus_eIgnore;
1183     widget->DispatchEvent(&querySelectedTextEvent, status);
1184     if (querySelectedTextEvent.DidNotFindSelection()) {
1185       mOffset = UINT32_MAX;
1186       mLength = UINT32_MAX;
1187       return;
1188     }
1189 
1190     mOffset = querySelectedTextEvent.mReply->StartOffset();
1191     mLength = querySelectedTextEvent.mReply->DataLength();
1192   }
1193 
~AutoSelectionRestore()1194   ~AutoSelectionRestore() {
1195     if (mWidget->Destroyed() || mOffset == UINT32_MAX) {
1196       return;
1197     }
1198 
1199     WidgetSelectionEvent selection(true, eSetSelection, mWidget);
1200     selection.mOffset = mOffset;
1201     selection.mLength = mLength;
1202     selection.mExpandToClusterBoundary = false;
1203     selection.mReason = nsISelectionListener::IME_REASON;
1204     nsEventStatus status = nsEventStatus_eIgnore;
1205     mWidget->DispatchEvent(&selection, status);
1206   }
1207 
1208  private:
1209   nsCOMPtr<nsIWidget> mWidget;
1210   RefPtr<TextEventDispatcher> mDispatcher;
1211   uint32_t mOffset;
1212   uint32_t mLength;
1213 };
1214 
OnImeRequestCommit()1215 void GeckoEditableSupport::OnImeRequestCommit() {
1216   AutoGeckoEditableBlocker blocker(this);
1217 
1218   if (mIMEMaskEventsCount > 0) {
1219     // Not focused.
1220     return;
1221   }
1222 
1223   nsCOMPtr<nsIWidget> widget = GetWidget();
1224   if (NS_WARN_IF(!widget)) {
1225     return;
1226   }
1227 
1228   AutoSelectionRestore restore(widget, mDispatcher);
1229 
1230   RemoveComposition(COMMIT_IME_COMPOSITION);
1231 }
1232 
AsyncNotifyIME(int32_t aNotification)1233 void GeckoEditableSupport::AsyncNotifyIME(int32_t aNotification) {
1234   RefPtr<GeckoEditableSupport> self(this);
1235 
1236   nsAppShell::PostEvent([this, self, aNotification] {
1237     if (!mIMEMaskEventsCount) {
1238       mEditable->NotifyIME(aNotification);
1239     }
1240   });
1241 }
1242 
NotifyIME(TextEventDispatcher * aTextEventDispatcher,const IMENotification & aNotification)1243 nsresult GeckoEditableSupport::NotifyIME(
1244     TextEventDispatcher* aTextEventDispatcher,
1245     const IMENotification& aNotification) {
1246   MOZ_ASSERT(mEditable);
1247 
1248   switch (aNotification.mMessage) {
1249     case REQUEST_TO_COMMIT_COMPOSITION: {
1250       ALOGIME("IME: REQUEST_TO_COMMIT_COMPOSITION");
1251 
1252       RemoveComposition(COMMIT_IME_COMPOSITION);
1253       AsyncNotifyIME(EditableListener::NOTIFY_IME_TO_COMMIT_COMPOSITION);
1254       break;
1255     }
1256 
1257     case REQUEST_TO_CANCEL_COMPOSITION: {
1258       ALOGIME("IME: REQUEST_TO_CANCEL_COMPOSITION");
1259 
1260       RemoveComposition(CANCEL_IME_COMPOSITION);
1261       AsyncNotifyIME(EditableListener::NOTIFY_IME_TO_CANCEL_COMPOSITION);
1262       break;
1263     }
1264 
1265     case NOTIFY_IME_OF_FOCUS: {
1266       ALOGIME("IME: NOTIFY_IME_OF_FOCUS");
1267 
1268       mIMEFocusCount++;
1269 
1270       RefPtr<GeckoEditableSupport> self(this);
1271       RefPtr<TextEventDispatcher> dispatcher = aTextEventDispatcher;
1272 
1273       // Post an event because we have to flush the text before sending a
1274       // focus event, and we may not be able to flush text during the
1275       // NotifyIME call.
1276       nsAppShell::PostEvent([this, self, dispatcher] {
1277         nsCOMPtr<nsIWidget> widget = dispatcher->GetWidget();
1278 
1279         --mIMEMaskEventsCount;
1280         if (!mIMEFocusCount || !widget || widget->Destroyed()) {
1281           return;
1282         }
1283 
1284         mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_TOKEN);
1285 
1286         if (mIsRemote) {
1287           if (!mEditableAttached) {
1288             // Re-attach on focus; see OnRemovedFrom().
1289             jni::NativeWeakPtrHolder<GeckoEditableSupport>::AttachExisting(
1290                 mEditable, do_AddRef(this));
1291             mEditableAttached = true;
1292           }
1293           // Because GeckoEditableSupport in content process doesn't
1294           // manage the active input context, we need to retrieve the
1295           // input context from the widget, for use by
1296           // OnImeReplaceText.
1297           mInputContext = widget->GetInputContext();
1298         }
1299         mDispatcher = dispatcher;
1300         mIMEKeyEvents.Clear();
1301 
1302         mCachedSelection.Reset();
1303 
1304         mIMEDelaySynchronizeReply = false;
1305         mIMEActiveCompositionCount = 0;
1306         FlushIMEText();
1307 
1308         // IME will call requestCursorUpdates after getting context.
1309         // So reset cursor update mode before getting context.
1310         mIMEMonitorCursor = false;
1311 
1312         mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_FOCUS);
1313       });
1314       break;
1315     }
1316 
1317     case NOTIFY_IME_OF_BLUR: {
1318       ALOGIME("IME: NOTIFY_IME_OF_BLUR");
1319 
1320       mIMEFocusCount--;
1321       MOZ_ASSERT(mIMEFocusCount >= 0);
1322 
1323       RefPtr<GeckoEditableSupport> self(this);
1324       nsAppShell::PostEvent([this, self] {
1325         if (!mIMEFocusCount) {
1326           mIMEDelaySynchronizeReply = false;
1327           mIMEActiveSynchronizeCount = 0;
1328           mIMEActiveCompositionCount = 0;
1329           mInputContext.ShutDown();
1330           mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_BLUR);
1331           OnRemovedFrom(mDispatcher);
1332         }
1333       });
1334 
1335       // Mask events because we lost focus. Unmask on the next focus.
1336       mIMEMaskEventsCount++;
1337       break;
1338     }
1339 
1340     case NOTIFY_IME_OF_SELECTION_CHANGE: {
1341       ALOGIME("IME: NOTIFY_IME_OF_SELECTION_CHANGE: SelectionChangeData=%s",
1342               ToString(aNotification.mSelectionChangeData).c_str());
1343 
1344       if (aNotification.mSelectionChangeData.HasRange()) {
1345         mCachedSelection.mStartOffset = static_cast<int32_t>(
1346             aNotification.mSelectionChangeData.AnchorOffset());
1347         mCachedSelection.mEndOffset = static_cast<int32_t>(
1348             aNotification.mSelectionChangeData.FocusOffset());
1349       } else {
1350         mCachedSelection.Reset();
1351       }
1352 
1353       PostFlushIMEChanges();
1354       mIMESelectionChanged = true;
1355       break;
1356     }
1357 
1358     case NOTIFY_IME_OF_TEXT_CHANGE: {
1359       ALOGIME("IME: NOTIFY_IME_OF_TEXT_CHANGE: TextChangeData=%s",
1360               ToString(aNotification.mTextChangeData).c_str());
1361 
1362       /* Make sure Java's selection is up-to-date */
1363       PostFlushIMEChanges();
1364       mIMESelectionChanged = true;
1365       AddIMETextChange(aNotification.mTextChangeData);
1366       break;
1367     }
1368 
1369     case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
1370       ALOGIME("IME: NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED");
1371 
1372       // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED isn't sent per IME call.
1373       // Receiving this event means that Gecko has already handled all IME
1374       // composing events in queue.
1375       //
1376       if (mIsRemote) {
1377         OnNotifyIMEOfCompositionEventHandled();
1378       } else {
1379         // Also, when receiving this event, mIMEDelaySynchronizeReply won't
1380         // update yet on non-e10s case since IME event is posted before updating
1381         // it. So we have to delay handling of this event.
1382         RefPtr<GeckoEditableSupport> self(this);
1383         nsAppShell::PostEvent(
1384             [this, self] { OnNotifyIMEOfCompositionEventHandled(); });
1385       }
1386       break;
1387     }
1388 
1389     default:
1390       break;
1391   }
1392   return NS_OK;
1393 }
1394 
OnNotifyIMEOfCompositionEventHandled()1395 void GeckoEditableSupport::OnNotifyIMEOfCompositionEventHandled() {
1396   // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED may be merged with multiple events,
1397   // so reset count.
1398   mIMEActiveCompositionCount = 0;
1399   if (mIMEDelaySynchronizeReply) {
1400     FlushIMEChanges();
1401   }
1402 
1403   // Hardware keyboard support requires each string rect.
1404   if (mIMEMonitorCursor) {
1405     UpdateCompositionRects();
1406   }
1407 }
1408 
OnRemovedFrom(TextEventDispatcher * aTextEventDispatcher)1409 void GeckoEditableSupport::OnRemovedFrom(
1410     TextEventDispatcher* aTextEventDispatcher) {
1411   mDispatcher = nullptr;
1412 
1413   if (mIsRemote && mEditable->HasEditableParent()) {
1414     // When we're remote, detach every time.
1415     OnWeakNonIntrusiveDetach(NS_NewRunnableFunction(
1416         "GeckoEditableSupport::OnRemovedFrom",
1417         [editable = java::GeckoEditableChild::GlobalRef(mEditable)] {
1418           DisposeNative(editable);
1419         }));
1420   }
1421 }
1422 
WillDispatchKeyboardEvent(TextEventDispatcher * aTextEventDispatcher,WidgetKeyboardEvent & aKeyboardEvent,uint32_t aIndexOfKeypress,void * aData)1423 void GeckoEditableSupport::WillDispatchKeyboardEvent(
1424     TextEventDispatcher* aTextEventDispatcher,
1425     WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
1426     void* aData) {}
1427 
NS_IMETHODIMP_(IMENotificationRequests)1428 NS_IMETHODIMP_(IMENotificationRequests)
1429 GeckoEditableSupport::GetIMENotificationRequests() {
1430   return IMENotificationRequests(IMENotificationRequests::NOTIFY_TEXT_CHANGE);
1431 }
1432 
ShouldKeyboardDismiss(const nsAString & aInputType,const nsAString & aInputmode)1433 static bool ShouldKeyboardDismiss(const nsAString& aInputType,
1434                                   const nsAString& aInputmode) {
1435   // Some input type uses the prompt to input value. So it is unnecessary to
1436   // show software keyboard.
1437   return aInputmode.EqualsLiteral("none") || aInputType.EqualsLiteral("date") ||
1438          aInputType.EqualsLiteral("time") ||
1439          aInputType.EqualsLiteral("month") ||
1440          aInputType.EqualsLiteral("week") ||
1441          aInputType.EqualsLiteral("datetime-local");
1442 }
1443 
SetInputContext(const InputContext & aContext,const InputContextAction & aAction)1444 void GeckoEditableSupport::SetInputContext(const InputContext& aContext,
1445                                            const InputContextAction& aAction) {
1446   // SetInputContext is called from chrome process only
1447   MOZ_ASSERT(XRE_IsParentProcess());
1448   MOZ_ASSERT(mEditable);
1449 
1450   ALOGIME(
1451       "IME: SetInputContext: aContext=%s, "
1452       "aAction={mCause=%s, mFocusChange=%s}",
1453       ToString(aContext).c_str(), ToString(aAction.mCause).c_str(),
1454       ToString(aAction.mFocusChange).c_str());
1455 
1456   mInputContext = aContext;
1457 
1458   if (mInputContext.mIMEState.mEnabled != IMEEnabled::Disabled &&
1459       !ShouldKeyboardDismiss(mInputContext.mHTMLInputType,
1460                              mInputContext.mHTMLInputInputmode) &&
1461       aAction.UserMightRequestOpenVKB()) {
1462     // Don't reset keyboard when we should simply open the vkb
1463     mEditable->NotifyIME(EditableListener::NOTIFY_IME_OPEN_VKB);
1464     return;
1465   }
1466 
1467   // Post an event to keep calls in order relative to NotifyIME.
1468   nsAppShell::PostEvent([this, self = RefPtr<GeckoEditableSupport>(this),
1469                          context = mInputContext, action = aAction] {
1470     nsCOMPtr<nsIWidget> widget = GetWidget();
1471 
1472     if (!widget || widget->Destroyed()) {
1473       return;
1474     }
1475     NotifyIMEContext(context, action);
1476   });
1477 }
1478 
NotifyIMEContext(const InputContext & aContext,const InputContextAction & aAction)1479 void GeckoEditableSupport::NotifyIMEContext(const InputContext& aContext,
1480                                             const InputContextAction& aAction) {
1481   const bool inPrivateBrowsing = aContext.mInPrivateBrowsing;
1482   // isUserAction is used whether opening virtual keyboard. But long press
1483   // shouldn't open it.
1484   const bool isUserAction =
1485       aAction.mCause != InputContextAction::CAUSE_LONGPRESS &&
1486       !(aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME &&
1487         aContext.mIMEState.mEnabled == IMEEnabled::Enabled) &&
1488       (aAction.IsHandlingUserInput() || aContext.mHasHandledUserInput);
1489   const int32_t flags =
1490       (inPrivateBrowsing ? EditableListener::IME_FLAG_PRIVATE_BROWSING : 0) |
1491       (isUserAction ? EditableListener::IME_FLAG_USER_ACTION : 0) |
1492       (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED
1493            ? EditableListener::IME_FOCUS_NOT_CHANGED
1494            : 0);
1495 
1496   mEditable->NotifyIMEContext(
1497       static_cast<int32_t>(aContext.mIMEState.mEnabled),
1498       aContext.mHTMLInputType, aContext.mHTMLInputInputmode,
1499       aContext.mActionHint, aContext.mAutocapitalize, flags);
1500 }
1501 
GetInputContext()1502 InputContext GeckoEditableSupport::GetInputContext() {
1503   // GetInputContext is called from chrome process only
1504   MOZ_ASSERT(XRE_IsParentProcess());
1505   InputContext context = mInputContext;
1506   context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
1507   return context;
1508 }
1509 
TransferParent(jni::Object::Param aEditableParent)1510 void GeckoEditableSupport::TransferParent(jni::Object::Param aEditableParent) {
1511   AutoGeckoEditableBlocker blocker(this);
1512 
1513   mEditable->SetParent(aEditableParent);
1514 
1515   // If we are already focused, make sure the new parent has our token
1516   // and focus information, so it can accept additional calls from us.
1517   if (mIMEFocusCount > 0) {
1518     mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_TOKEN);
1519     if (mIsRemote) {
1520       // GeckoEditableSupport::SetInputContext is called on chrome process
1521       // only, so mInputContext may be still invalid since it is set after
1522       // we have gotton focus.
1523       RefPtr<GeckoEditableSupport> self(this);
1524       nsAppShell::PostEvent([self = std::move(self)] {
1525         NS_WARNING_ASSERTION(
1526             self->mDispatcher,
1527             "Text dispatcher is still null. Why don't we get focus yet?");
1528         self->NotifyIMEContext(self->mInputContext, InputContextAction());
1529       });
1530     } else {
1531       NotifyIMEContext(mInputContext, InputContextAction());
1532     }
1533     mEditable->NotifyIME(EditableListener::NOTIFY_IME_OF_FOCUS);
1534     // We have focus, so don't destroy editable child.
1535     return;
1536   }
1537 
1538   if (mIsRemote && !mDispatcher) {
1539     // Detach now if we were only attached temporarily.
1540     OnRemovedFrom(/* dispatcher */ nullptr);
1541   }
1542 }
1543 
SetOnBrowserChild(dom::BrowserChild * aBrowserChild)1544 void GeckoEditableSupport::SetOnBrowserChild(dom::BrowserChild* aBrowserChild) {
1545   MOZ_ASSERT(!XRE_IsParentProcess());
1546   NS_ENSURE_TRUE_VOID(aBrowserChild);
1547 
1548   const dom::ContentChild* const contentChild =
1549       dom::ContentChild::GetSingleton();
1550   RefPtr<widget::PuppetWidget> widget(aBrowserChild->WebWidget());
1551   NS_ENSURE_TRUE_VOID(contentChild && widget);
1552 
1553   // Get the content/tab ID in order to get the correct
1554   // IGeckoEditableParent object, which GeckoEditableChild uses to
1555   // communicate with the parent process.
1556   const uint64_t contentId = contentChild->GetID();
1557   const uint64_t tabId = aBrowserChild->GetTabId();
1558   NS_ENSURE_TRUE_VOID(contentId && tabId);
1559 
1560   RefPtr<widget::TextEventDispatcherListener> listener =
1561       widget->GetNativeTextEventDispatcherListener();
1562 
1563   if (!listener ||
1564       listener.get() ==
1565           static_cast<widget::TextEventDispatcherListener*>(widget)) {
1566     // We need to set a new listener.
1567     const auto editableChild = java::GeckoEditableChild::New(
1568         /* parent */ nullptr, /* default */ false);
1569 
1570     // Temporarily attach so we can receive the initial editable parent.
1571     auto editableSupport =
1572         jni::NativeWeakPtrHolder<GeckoEditableSupport>::Attach(editableChild,
1573                                                                editableChild);
1574     auto accEditableSupport(editableSupport.Access());
1575     MOZ_RELEASE_ASSERT(accEditableSupport);
1576 
1577     // Tell PuppetWidget to use our listener for IME operations.
1578     widget->SetNativeTextEventDispatcherListener(
1579         accEditableSupport.AsRefPtr().get());
1580 
1581     accEditableSupport->mEditableAttached = true;
1582 
1583     // Connect the new child to a parent that corresponds to the BrowserChild.
1584     java::GeckoServiceChildProcess::GetEditableParent(editableChild, contentId,
1585                                                       tabId);
1586     return;
1587   }
1588 
1589   // We need to update the existing listener to use the new parent.
1590 
1591   // We expect the existing TextEventDispatcherListener to be a
1592   // GeckoEditableSupport object, so we perform a sanity check to make
1593   // sure, by comparing their respective vtable pointers.
1594   const RefPtr<widget::GeckoEditableSupport> dummy =
1595       new widget::GeckoEditableSupport(/* child */ nullptr);
1596   NS_ENSURE_TRUE_VOID(*reinterpret_cast<const uintptr_t*>(listener.get()) ==
1597                       *reinterpret_cast<const uintptr_t*>(dummy.get()));
1598 
1599   const auto support =
1600       static_cast<widget::GeckoEditableSupport*>(listener.get());
1601   if (!support->mEditableAttached) {
1602     // Temporarily attach so we can receive the initial editable parent.
1603     jni::NativeWeakPtrHolder<GeckoEditableSupport>::AttachExisting(
1604         support->GetJavaEditable(), do_AddRef(support));
1605     support->mEditableAttached = true;
1606   }
1607 
1608   // Transfer to a new parent that corresponds to the BrowserChild.
1609   java::GeckoServiceChildProcess::GetEditableParent(support->GetJavaEditable(),
1610                                                     contentId, tabId);
1611 }
1612 
GetWidget() const1613 nsIWidget* GeckoEditableSupport::GetWidget() const {
1614   MOZ_ASSERT(NS_IsMainThread());
1615   return mDispatcher ? mDispatcher->GetWidget() : GetNsWindow();
1616 }
1617 
GetNsWindow() const1618 nsWindow* GeckoEditableSupport::GetNsWindow() const {
1619   MOZ_ASSERT(NS_IsMainThread());
1620 
1621   auto acc(mWindow.Access());
1622   if (!acc) {
1623     return nullptr;
1624   }
1625 
1626   return acc->GetNsWindow();
1627 }
1628 
1629 }  // namespace widget
1630 }  // namespace mozilla
1631