Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / editor / libeditor / EditorEventListener.cpp
blob299057d17f47c4c88767f594eea245ea8f928546
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 sw=2 et tw=78: */
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/. */
7 #include "EditorEventListener.h"
9 #include "EditorBase.h" // for EditorBase, etc.
10 #include "EditorUtils.h" // for EditorUtils
11 #include "HTMLEditor.h" // for HTMLEditor
12 #include "TextEditor.h" // for TextEditor
14 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
15 #include "mozilla/AutoRestore.h"
16 #include "mozilla/ContentEvents.h" // for InternalFocusEvent
17 #include "mozilla/EventListenerManager.h" // for EventListenerManager
18 #include "mozilla/EventStateManager.h" // for EventStateManager
19 #include "mozilla/IMEStateManager.h" // for IMEStateManager
20 #include "mozilla/LookAndFeel.h" // for LookAndFeel
21 #include "mozilla/NativeKeyBindingsType.h" // for NativeKeyBindingsType
22 #include "mozilla/Preferences.h" // for Preferences
23 #include "mozilla/PresShell.h" // for PresShell
24 #include "mozilla/TextEvents.h" // for WidgetCompositionEvent
25 #include "mozilla/dom/DataTransfer.h"
26 #include "mozilla/dom/Document.h" // for Document
27 #include "mozilla/dom/DOMStringList.h"
28 #include "mozilla/dom/DragEvent.h"
29 #include "mozilla/dom/Element.h" // for Element
30 #include "mozilla/dom/Event.h" // for Event
31 #include "mozilla/dom/EventTarget.h" // for EventTarget
32 #include "mozilla/dom/HTMLTextAreaElement.h"
33 #include "mozilla/dom/MouseEvent.h" // for MouseEvent
34 #include "mozilla/dom/Selection.h"
36 #include "nsAString.h"
37 #include "nsCaret.h" // for nsCaret
38 #include "nsDebug.h" // for NS_WARNING, etc.
39 #include "nsFocusManager.h" // for nsFocusManager
40 #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::input
41 #include "nsIContent.h" // for nsIContent
42 #include "nsIContentInlines.h" // for nsINode::IsInDesignMode()
43 #include "nsIController.h" // for nsIController
44 #include "nsID.h"
45 #include "nsIFormControl.h" // for nsIFormControl, etc.
46 #include "nsINode.h" // for nsINode, etc.
47 #include "nsIWidget.h" // for nsIWidget
48 #include "nsLiteralString.h" // for NS_LITERAL_STRING
49 #include "nsPIWindowRoot.h" // for nsPIWindowRoot
50 #include "nsPrintfCString.h" // for nsPrintfCString
51 #include "nsRange.h"
52 #include "nsServiceManagerUtils.h" // for do_GetService
53 #include "nsString.h" // for nsAutoString
54 #include "nsQueryObject.h" // for do_QueryObject
55 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
56 # include "nsContentUtils.h" // for nsContentUtils, etc.
57 # include "nsIBidiKeyboard.h" // for nsIBidiKeyboard
58 #endif
60 #include "mozilla/dom/BrowserParent.h"
62 class nsPresContext;
64 namespace mozilla {
66 using namespace dom;
68 MOZ_CAN_RUN_SCRIPT static void DoCommandCallback(Command aCommand,
69 void* aData) {
70 Document* doc = static_cast<Document*>(aData);
71 nsPIDOMWindowOuter* win = doc->GetWindow();
72 if (!win) {
73 return;
75 nsCOMPtr<nsPIWindowRoot> root = win->GetTopWindowRoot();
76 if (!root) {
77 return;
80 const char* commandStr = WidgetKeyboardEvent::GetCommandStr(aCommand);
82 nsCOMPtr<nsIController> controller;
83 root->GetControllerForCommand(commandStr, false /* for any window */,
84 getter_AddRefs(controller));
85 if (!controller) {
86 return;
89 bool commandEnabled;
90 if (NS_WARN_IF(NS_FAILED(
91 controller->IsCommandEnabled(commandStr, &commandEnabled)))) {
92 return;
94 if (commandEnabled) {
95 controller->DoCommand(commandStr);
99 EditorEventListener::EditorEventListener()
100 : mEditorBase(nullptr),
101 mCommitText(false),
102 mInTransaction(false),
103 mMouseDownOrUpConsumedByIME(false)
104 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
106 mHaveBidiKeyboards(false),
107 mShouldSwitchTextDirection(false),
108 mSwitchToRTL(false)
109 #endif
113 EditorEventListener::~EditorEventListener() {
114 if (mEditorBase) {
115 NS_WARNING("We've not been uninstalled yet");
116 Disconnect();
120 nsresult EditorEventListener::Connect(EditorBase* aEditorBase) {
121 if (NS_WARN_IF(!aEditorBase)) {
122 return NS_ERROR_INVALID_ARG;
125 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
126 nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
127 if (bidiKeyboard) {
128 bool haveBidiKeyboards = false;
129 bidiKeyboard->GetHaveBidiKeyboards(&haveBidiKeyboards);
130 mHaveBidiKeyboards = haveBidiKeyboards;
132 #endif
134 mEditorBase = aEditorBase;
136 nsresult rv = InstallToEditor();
137 if (NS_FAILED(rv)) {
138 NS_WARNING("EditorEventListener::InstallToEditor() failed");
139 Disconnect();
141 return rv;
144 nsresult EditorEventListener::InstallToEditor() {
145 MOZ_ASSERT(mEditorBase, "The caller must set mEditorBase");
147 EventTarget* eventTarget = mEditorBase->GetDOMEventTarget();
148 if (NS_WARN_IF(!eventTarget)) {
149 return NS_ERROR_FAILURE;
152 // register the event listeners with the listener manager
153 EventListenerManager* eventListenerManager =
154 eventTarget->GetOrCreateListenerManager();
155 if (NS_WARN_IF(!eventListenerManager)) {
156 return NS_ERROR_FAILURE;
159 // For non-html editor, ie.TextEditor, we want to preserve
160 // the event handling order to ensure listeners that are
161 // added to <input> and <texarea> still working as expected.
162 EventListenerFlags flags = mEditorBase->IsHTMLEditor()
163 ? TrustedEventsAtSystemGroupCapture()
164 : TrustedEventsAtSystemGroupBubble();
165 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
166 eventListenerManager->AddEventListenerByType(this, u"keydown"_ns, flags);
167 eventListenerManager->AddEventListenerByType(this, u"keyup"_ns, flags);
168 #endif
170 eventListenerManager->AddEventListenerByType(this, u"keypress"_ns, flags);
171 eventListenerManager->AddEventListenerByType(this, u"dragover"_ns, flags);
172 eventListenerManager->AddEventListenerByType(this, u"dragleave"_ns, flags);
173 eventListenerManager->AddEventListenerByType(this, u"drop"_ns, flags);
174 // XXX We should add the mouse event listeners as system event group.
175 // E.g., web applications cannot prevent middle mouse paste by
176 // preventDefault() of click event at bubble phase.
177 // However, if we do so, all click handlers in any frames and frontend
178 // code need to check if it's editable. It makes easier create new bugs.
179 eventListenerManager->AddEventListenerByType(this, u"mousedown"_ns,
180 TrustedEventsAtCapture());
181 eventListenerManager->AddEventListenerByType(this, u"mouseup"_ns,
182 TrustedEventsAtCapture());
183 eventListenerManager->AddEventListenerByType(this, u"click"_ns,
184 TrustedEventsAtCapture());
185 eventListenerManager->AddEventListenerByType(
186 this, u"auxclick"_ns, TrustedEventsAtSystemGroupCapture());
187 // Focus event doesn't bubble so adding the listener to capturing phase as
188 // system event group.
189 eventListenerManager->AddEventListenerByType(
190 this, u"blur"_ns, TrustedEventsAtSystemGroupCapture());
191 eventListenerManager->AddEventListenerByType(
192 this, u"focus"_ns, TrustedEventsAtSystemGroupCapture());
193 eventListenerManager->AddEventListenerByType(
194 this, u"text"_ns, TrustedEventsAtSystemGroupBubble());
195 eventListenerManager->AddEventListenerByType(
196 this, u"compositionstart"_ns, TrustedEventsAtSystemGroupBubble());
197 eventListenerManager->AddEventListenerByType(
198 this, u"compositionend"_ns, TrustedEventsAtSystemGroupBubble());
200 return NS_OK;
203 void EditorEventListener::Disconnect() {
204 if (DetachedFromEditor()) {
205 return;
207 UninstallFromEditor();
209 const OwningNonNull<EditorBase> editorBase = *mEditorBase;
210 mEditorBase = nullptr;
212 nsFocusManager* fm = nsFocusManager::GetFocusManager();
213 if (fm) {
214 nsIContent* focusedContent = fm->GetFocusedElement();
215 mozilla::dom::Element* root = editorBase->GetRoot();
216 if (focusedContent && root &&
217 focusedContent->IsInclusiveDescendantOf(root)) {
218 // Reset the Selection ancestor limiter and SelectionController state
219 // that EditorBase::InitializeSelection set up.
220 DebugOnly<nsresult> rvIgnored = editorBase->FinalizeSelection();
221 NS_WARNING_ASSERTION(
222 NS_SUCCEEDED(rvIgnored),
223 "EditorBase::FinalizeSelection() failed, but ignored");
228 void EditorEventListener::UninstallFromEditor() {
229 CleanupDragDropCaret();
231 EventTarget* eventTarget = mEditorBase->GetDOMEventTarget();
232 if (NS_WARN_IF(!eventTarget)) {
233 return;
236 EventListenerManager* eventListenerManager =
237 eventTarget->GetOrCreateListenerManager();
238 if (NS_WARN_IF(!eventListenerManager)) {
239 return;
242 EventListenerFlags flags = mEditorBase->IsHTMLEditor()
243 ? TrustedEventsAtSystemGroupCapture()
244 : TrustedEventsAtSystemGroupBubble();
245 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
246 eventListenerManager->RemoveEventListenerByType(this, u"keydown"_ns, flags);
247 eventListenerManager->RemoveEventListenerByType(this, u"keyup"_ns, flags);
248 #endif
249 eventListenerManager->RemoveEventListenerByType(this, u"keypress"_ns, flags);
250 eventListenerManager->RemoveEventListenerByType(this, u"dragover"_ns, flags);
251 eventListenerManager->RemoveEventListenerByType(this, u"dragleave"_ns, flags);
252 eventListenerManager->RemoveEventListenerByType(this, u"drop"_ns, flags);
253 eventListenerManager->RemoveEventListenerByType(this, u"mousedown"_ns,
254 TrustedEventsAtCapture());
255 eventListenerManager->RemoveEventListenerByType(this, u"mouseup"_ns,
256 TrustedEventsAtCapture());
257 eventListenerManager->RemoveEventListenerByType(this, u"click"_ns,
258 TrustedEventsAtCapture());
259 eventListenerManager->RemoveEventListenerByType(
260 this, u"auxclick"_ns, TrustedEventsAtSystemGroupCapture());
261 eventListenerManager->RemoveEventListenerByType(
262 this, u"blur"_ns, TrustedEventsAtSystemGroupCapture());
263 eventListenerManager->RemoveEventListenerByType(
264 this, u"focus"_ns, TrustedEventsAtSystemGroupCapture());
265 eventListenerManager->RemoveEventListenerByType(
266 this, u"text"_ns, TrustedEventsAtSystemGroupBubble());
267 eventListenerManager->RemoveEventListenerByType(
268 this, u"compositionstart"_ns, TrustedEventsAtSystemGroupBubble());
269 eventListenerManager->RemoveEventListenerByType(
270 this, u"compositionend"_ns, TrustedEventsAtSystemGroupBubble());
273 PresShell* EditorEventListener::GetPresShell() const {
274 MOZ_ASSERT(!DetachedFromEditor());
275 return mEditorBase->GetPresShell();
278 nsPresContext* EditorEventListener::GetPresContext() const {
279 PresShell* presShell = GetPresShell();
280 return presShell ? presShell->GetPresContext() : nullptr;
283 bool EditorEventListener::EditorHasFocus() {
284 MOZ_ASSERT(!DetachedFromEditor());
285 const Element* focusedElement = mEditorBase->GetFocusedElement();
286 return focusedElement && focusedElement->IsInComposedDoc();
289 NS_IMPL_ISUPPORTS(EditorEventListener, nsIDOMEventListener)
291 bool EditorEventListener::DetachedFromEditor() const { return !mEditorBase; }
293 bool EditorEventListener::DetachedFromEditorOrDefaultPrevented(
294 WidgetEvent* aWidgetEvent) const {
295 return NS_WARN_IF(!aWidgetEvent) || DetachedFromEditor() ||
296 aWidgetEvent->DefaultPrevented();
299 bool EditorEventListener::EnsureCommitComposition() {
300 MOZ_ASSERT(!DetachedFromEditor());
301 RefPtr<EditorBase> editorBase(mEditorBase);
302 editorBase->CommitComposition();
303 return !DetachedFromEditor();
306 NS_IMETHODIMP EditorEventListener::HandleEvent(Event* aEvent) {
307 // Let's handle each event with the message of the internal event of the
308 // coming event. If the DOM event was created with improper interface,
309 // e.g., keydown event is created with |new MouseEvent("keydown", {});|,
310 // its message is always 0. Therefore, we can ban such strange event easy.
311 // However, we need to handle strange "focus" and "blur" event. See the
312 // following code of this switch statement.
313 // NOTE: Each event handler may require specific event interface. Before
314 // calling it, this queries the specific interface. If it would fail,
315 // each event handler would just ignore the event. So, in this method,
316 // you don't need to check if the QI succeeded before each call.
317 WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
319 if (DetachedFromEditor()) {
320 return NS_OK;
323 // For nested documents with multiple HTMLEditor registered on different
324 // nsWindowRoot, make sure the HTMLEditor for the original event target
325 // handles the events.
326 if (mEditorBase->IsHTMLEditor()) {
327 nsCOMPtr<nsINode> originalEventTargetNode =
328 nsINode::FromEventTargetOrNull(aEvent->GetOriginalTarget());
330 if (originalEventTargetNode &&
331 mEditorBase != originalEventTargetNode->OwnerDoc()->GetHTMLEditor()) {
332 return NS_OK;
334 if (!originalEventTargetNode && internalEvent->mMessage == eFocus &&
335 aEvent->GetCurrentTarget()->IsRootWindow()) {
336 return NS_OK;
340 switch (internalEvent->mMessage) {
341 // dragover and drop
342 case eDragOver:
343 case eDrop: {
344 // The editor which is registered on nsWindowRoot shouldn't handle
345 // drop events when it can be handled by Input or TextArea element on
346 // the chain.
347 if (aEvent->GetCurrentTarget()->IsRootWindow() &&
348 TextControlElement::FromEventTargetOrNull(
349 internalEvent->GetDOMEventTarget())) {
350 return NS_OK;
352 // aEvent should be grabbed by the caller since this is
353 // nsIDOMEventListener method. However, our clang plugin cannot check it
354 // if we use Event::As*Event(). So, we need to grab it by ourselves.
355 RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
356 nsresult rv = DragOverOrDrop(dragEvent);
357 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
358 "EditorEventListener::DragOverOrDrop() failed");
359 return rv;
361 // DragLeave
362 case eDragLeave: {
363 RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
364 nsresult rv = DragLeave(dragEvent);
365 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
366 "EditorEventListener::DragLeave() failed");
367 return rv;
369 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
370 // keydown
371 case eKeyDown: {
372 nsresult rv = KeyDown(internalEvent->AsKeyboardEvent());
373 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
374 "EditorEventListener::KeyDown() failed");
375 return rv;
377 // keyup
378 case eKeyUp: {
379 nsresult rv = KeyUp(internalEvent->AsKeyboardEvent());
380 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
381 "EditorEventListener::KeyUp() failed");
382 return rv;
384 #endif // #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
385 // keypress
386 case eKeyPress: {
387 nsresult rv = KeyPress(internalEvent->AsKeyboardEvent());
388 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
389 "EditorEventListener::KeyPress() failed");
390 return rv;
392 // mousedown
393 case eMouseDown: {
394 // EditorEventListener may receive (1) all mousedown, mouseup and click
395 // events, (2) only mousedown event or (3) only mouseup event.
396 // mMouseDownOrUpConsumedByIME is used only for ignoring click event if
397 // preceding mousedown and/or mouseup event is consumed by IME.
398 // Therefore, even if case #2 or case #3 occurs,
399 // mMouseDownOrUpConsumedByIME is true here. Therefore, we should always
400 // overwrite it here.
401 mMouseDownOrUpConsumedByIME =
402 NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent());
403 if (mMouseDownOrUpConsumedByIME) {
404 return NS_OK;
406 RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
407 if (NS_WARN_IF(!mouseEvent)) {
408 return NS_OK;
410 nsresult rv = MouseDown(mouseEvent);
411 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
412 "EditorEventListener::MouseDown() failed");
413 return rv;
415 // mouseup
416 case eMouseUp: {
417 // See above comment in the eMouseDown case, first.
418 // This code assumes that case #1 is occuring. However, if case #3 may
419 // occurs after case #2 and the mousedown is consumed,
420 // mMouseDownOrUpConsumedByIME is true even though EditorEventListener
421 // has not received the preceding mousedown event of this mouseup event.
422 // So, mMouseDownOrUpConsumedByIME may be invalid here. However,
423 // this is not a matter because mMouseDownOrUpConsumedByIME is referred
424 // only by eMouseClick case but click event is fired only in case #1.
425 // So, before a click event is fired, mMouseDownOrUpConsumedByIME is
426 // always initialized in the eMouseDown case if it's referred.
427 if (NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent())) {
428 mMouseDownOrUpConsumedByIME = true;
430 if (mMouseDownOrUpConsumedByIME) {
431 return NS_OK;
433 RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
434 if (NS_WARN_IF(!mouseEvent)) {
435 return NS_OK;
437 nsresult rv = MouseUp(mouseEvent);
438 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
439 "EditorEventListener::MouseUp() failed");
440 return rv;
442 // click
443 case eMouseClick: {
444 WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
445 // Don't handle non-primary click events
446 if (widgetMouseEvent->mButton != MouseButton::ePrimary) {
447 return NS_OK;
449 [[fallthrough]];
451 // auxclick
452 case eMouseAuxClick: {
453 WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
454 if (NS_WARN_IF(!widgetMouseEvent)) {
455 return NS_OK;
457 // If the preceding mousedown event or mouseup event was consumed,
458 // editor shouldn't handle this click event.
459 if (mMouseDownOrUpConsumedByIME) {
460 mMouseDownOrUpConsumedByIME = false;
461 widgetMouseEvent->PreventDefault();
462 return NS_OK;
464 nsresult rv = MouseClick(widgetMouseEvent);
465 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
466 "EditorEventListener::MouseClick() failed");
467 return rv;
469 // focus
470 case eFocus: {
471 const InternalFocusEvent* focusEvent = internalEvent->AsFocusEvent();
472 if (NS_WARN_IF(!focusEvent)) {
473 return NS_ERROR_FAILURE;
475 nsresult rv = Focus(*focusEvent);
476 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
477 "EditorEventListener::Focus() failed");
478 return rv;
480 // blur
481 case eBlur: {
482 const InternalFocusEvent* blurEvent = internalEvent->AsFocusEvent();
483 if (NS_WARN_IF(!blurEvent)) {
484 return NS_ERROR_FAILURE;
486 nsresult rv = Blur(*blurEvent);
487 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
488 "EditorEventListener::Blur() failed");
489 return rv;
491 // text
492 case eCompositionChange: {
493 nsresult rv =
494 HandleChangeComposition(internalEvent->AsCompositionEvent());
495 NS_WARNING_ASSERTION(
496 NS_SUCCEEDED(rv),
497 "EditorEventListener::HandleChangeComposition() failed");
498 return rv;
500 // compositionstart
501 case eCompositionStart: {
502 nsresult rv = HandleStartComposition(internalEvent->AsCompositionEvent());
503 NS_WARNING_ASSERTION(
504 NS_SUCCEEDED(rv),
505 "EditorEventListener::HandleStartComposition() failed");
506 return rv;
508 // compositionend
509 case eCompositionEnd: {
510 HandleEndComposition(internalEvent->AsCompositionEvent());
511 return NS_OK;
513 default:
514 break;
517 #ifdef DEBUG
518 nsAutoString eventType;
519 aEvent->GetType(eventType);
520 nsPrintfCString assertMessage(
521 "Editor doesn't handle \"%s\" event "
522 "because its internal event doesn't have proper message",
523 NS_ConvertUTF16toUTF8(eventType).get());
524 NS_ASSERTION(false, assertMessage.get());
525 #endif
527 return NS_OK;
530 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
532 // This function is borrowed from Chromium's ImeInput::IsCtrlShiftPressed
533 bool IsCtrlShiftPressed(const WidgetKeyboardEvent* aKeyboardEvent,
534 bool& isRTL) {
535 MOZ_ASSERT(aKeyboardEvent);
536 // To check if a user is pressing only a control key and a right-shift key
537 // (or a left-shift key), we use the steps below:
538 // 1. Check if a user is pressing a control key and a right-shift key (or
539 // a left-shift key).
540 // 2. If the condition 1 is true, we should check if there are any other
541 // keys pressed at the same time.
542 // To ignore the keys checked in 1, we set their status to 0 before
543 // checking the key status.
545 if (!aKeyboardEvent->IsControl()) {
546 return false;
549 switch (aKeyboardEvent->mLocation) {
550 case eKeyLocationRight:
551 isRTL = true;
552 break;
553 case eKeyLocationLeft:
554 isRTL = false;
555 break;
556 default:
557 return false;
560 // Scan the key status to find pressed keys. We should abandon changing the
561 // text direction when there are other pressed keys.
562 return !aKeyboardEvent->IsAlt() && !aKeyboardEvent->IsMeta();
565 // This logic is mostly borrowed from Chromium's
566 // RenderWidgetHostViewWin::OnKeyEvent.
568 nsresult EditorEventListener::KeyUp(const WidgetKeyboardEvent* aKeyboardEvent) {
569 if (NS_WARN_IF(!aKeyboardEvent) || DetachedFromEditor()) {
570 return NS_OK;
573 if (!mHaveBidiKeyboards) {
574 return NS_OK;
577 // XXX Why doesn't this method check if it's consumed?
578 RefPtr<EditorBase> editorBase(mEditorBase);
579 if ((aKeyboardEvent->mKeyCode == NS_VK_SHIFT ||
580 aKeyboardEvent->mKeyCode == NS_VK_CONTROL) &&
581 mShouldSwitchTextDirection &&
582 (editorBase->IsTextEditor() ||
583 editorBase->AsHTMLEditor()->IsPlaintextMailComposer())) {
584 editorBase->SwitchTextDirectionTo(mSwitchToRTL
585 ? EditorBase::TextDirection::eRTL
586 : EditorBase::TextDirection::eLTR);
587 mShouldSwitchTextDirection = false;
589 return NS_OK;
592 nsresult EditorEventListener::KeyDown(
593 const WidgetKeyboardEvent* aKeyboardEvent) {
594 if (NS_WARN_IF(!aKeyboardEvent) || DetachedFromEditor()) {
595 return NS_OK;
598 if (!mHaveBidiKeyboards) {
599 return NS_OK;
602 // XXX Why isn't this method check if it's consumed?
603 if (aKeyboardEvent->mKeyCode == NS_VK_SHIFT) {
604 bool switchToRTL;
605 if (IsCtrlShiftPressed(aKeyboardEvent, switchToRTL)) {
606 mShouldSwitchTextDirection = true;
607 mSwitchToRTL = switchToRTL;
609 } else if (aKeyboardEvent->mKeyCode != NS_VK_CONTROL) {
610 // In case the user presses any other key besides Ctrl and Shift
611 mShouldSwitchTextDirection = false;
613 return NS_OK;
616 #endif // #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
618 nsresult EditorEventListener::KeyPress(WidgetKeyboardEvent* aKeyboardEvent) {
619 if (NS_WARN_IF(!aKeyboardEvent)) {
620 return NS_OK;
623 RefPtr<EditorBase> editorBase(mEditorBase);
624 if (!editorBase->IsAcceptableInputEvent(aKeyboardEvent) ||
625 DetachedFromEditorOrDefaultPrevented(aKeyboardEvent)) {
626 return NS_OK;
629 // The exposed root of our editor may have been hidden or destroyed by a
630 // preceding event listener. However, the destruction has not occurred yet if
631 // pending notifications have not been flushed yet. Therefore, before
632 // handling user input, we need to get the latest state and if it's now
633 // destroyed with the flushing, we should just ignore this event instead of
634 // returning error since this is just a event listener.
635 RefPtr<Document> document = editorBase->GetDocument();
636 if (!document) {
637 return NS_OK;
639 document->FlushPendingNotifications(FlushType::Layout);
640 if (editorBase->Destroyed() || DetachedFromEditor()) {
641 return NS_OK;
644 nsresult rv = editorBase->HandleKeyPressEvent(aKeyboardEvent);
645 if (NS_FAILED(rv)) {
646 NS_WARNING("EditorBase::HandleKeyPressEvent() failed");
647 return rv;
650 auto GetWidget = [&]() -> nsIWidget* {
651 if (aKeyboardEvent->mWidget) {
652 return aKeyboardEvent->mWidget;
654 // If the event is created by chrome script, the widget is always nullptr.
655 return IMEStateManager::GetWidgetForTextInputHandling();
658 if (DetachedFromEditor()) {
659 return NS_OK;
662 if (LookAndFeel::GetInt(LookAndFeel::IntID::HideCursorWhileTyping)) {
663 if (nsPresContext* pc = GetPresContext()) {
664 if (nsIWidget* widget = GetWidget()) {
665 pc->EventStateManager()->StartHidingCursorWhileTyping(widget);
670 if (aKeyboardEvent->DefaultPrevented()) {
671 return NS_OK;
674 if (!ShouldHandleNativeKeyBindings(aKeyboardEvent)) {
675 return NS_OK;
678 // Now, ask the native key bindings to handle the event.
680 nsIWidget* widget = GetWidget();
681 if (NS_WARN_IF(!widget)) {
682 return NS_OK;
685 RefPtr<Document> doc = editorBase->GetDocument();
687 // WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
688 // If the event is created by chrome script, it is nullptr but we need to
689 // execute native key bindings. Therefore, we need to set widget to
690 // WidgetEvent::mWidget temporarily.
691 AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(aKeyboardEvent->mWidget);
692 aKeyboardEvent->mWidget = widget;
693 if (aKeyboardEvent->ExecuteEditCommands(NativeKeyBindingsType::RichTextEditor,
694 DoCommandCallback, doc)) {
695 aKeyboardEvent->PreventDefault();
697 return NS_OK;
700 nsresult EditorEventListener::MouseClick(WidgetMouseEvent* aMouseClickEvent) {
701 if (NS_WARN_IF(!aMouseClickEvent) || DetachedFromEditor()) {
702 return NS_OK;
704 // nothing to do if editor isn't editable or clicked on out of the editor.
705 OwningNonNull<EditorBase> editorBase = *mEditorBase;
706 if (editorBase->IsReadonly() ||
707 !editorBase->IsAcceptableInputEvent(aMouseClickEvent)) {
708 return NS_OK;
711 // Notifies clicking on editor to IMEStateManager even when the event was
712 // consumed.
713 if (EditorHasFocus()) {
714 if (RefPtr<nsPresContext> presContext = GetPresContext()) {
715 RefPtr<Element> focusedElement = mEditorBase->GetFocusedElement();
716 IMEStateManager::OnClickInEditor(*presContext, focusedElement,
717 *aMouseClickEvent);
718 if (DetachedFromEditor()) {
719 return NS_OK;
724 if (DetachedFromEditorOrDefaultPrevented(aMouseClickEvent)) {
725 // We're done if 'preventdefault' is true (see for example bug 70698).
726 return NS_OK;
729 // If we got a mouse down inside the editing area, we should force the
730 // IME to commit before we change the cursor position.
731 if (!EnsureCommitComposition()) {
732 return NS_OK;
735 // XXX The following code is hack for our buggy "click" and "auxclick"
736 // implementation. "auxclick" event was added recently, however,
737 // any non-primary button click event handlers in our UI still keep
738 // listening to "click" events. Additionally, "auxclick" event is
739 // fired after "click" events and even if we do this in the system event
740 // group, middle click opens new tab before us. Therefore, we need to
741 // handle middle click at capturing phase of the default group even
742 // though this makes web apps cannot prevent middle click paste with
743 // calling preventDefault() of "click" nor "auxclick".
745 if (aMouseClickEvent->mButton != MouseButton::eMiddle ||
746 !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
747 return NS_OK;
750 RefPtr<PresShell> presShell = GetPresShell();
751 if (NS_WARN_IF(!presShell)) {
752 return NS_OK;
754 nsPresContext* presContext = GetPresContext();
755 if (NS_WARN_IF(!presContext)) {
756 return NS_OK;
758 MOZ_ASSERT(!aMouseClickEvent->DefaultPrevented());
759 nsEventStatus status = nsEventStatus_eIgnore;
760 RefPtr<EventStateManager> esm = presContext->EventStateManager();
761 DebugOnly<nsresult> rvIgnored = esm->HandleMiddleClickPaste(
762 presShell, aMouseClickEvent, &status, editorBase);
763 NS_WARNING_ASSERTION(
764 NS_SUCCEEDED(rvIgnored),
765 "EventStateManager::HandleMiddleClickPaste() failed, but ignored");
766 if (status == nsEventStatus_eConsumeNoDefault) {
767 // We no longer need to StopImmediatePropagation here since
768 // ClickHandlerChild.sys.mjs checks for and ignores editables, so won't
769 // re-handle the event
770 aMouseClickEvent->PreventDefault();
772 return NS_OK;
775 bool EditorEventListener::NotifyIMEOfMouseButtonEvent(
776 WidgetMouseEvent* aMouseEvent) {
777 MOZ_ASSERT(aMouseEvent);
779 if (!EditorHasFocus()) {
780 return false;
783 RefPtr<nsPresContext> presContext = GetPresContext();
784 if (NS_WARN_IF(!presContext)) {
785 return false;
787 RefPtr<Element> focusedElement = mEditorBase->GetFocusedElement();
788 return IMEStateManager::OnMouseButtonEventInEditor(
789 *presContext, focusedElement, *aMouseEvent);
792 nsresult EditorEventListener::MouseDown(MouseEvent* aMouseEvent) {
793 // FYI: We don't need to check if it's already consumed here because
794 // we need to commit composition at mouse button operation.
795 // FYI: This may be called by HTMLEditorEventListener::MouseDown() even
796 // when the event is not acceptable for committing composition.
797 if (DetachedFromEditor()) {
798 return NS_OK;
800 Unused << EnsureCommitComposition();
801 return NS_OK;
805 * Drag event implementation
808 void EditorEventListener::RefuseToDropAndHideCaret(DragEvent* aDragEvent) {
809 MOZ_ASSERT(aDragEvent->WidgetEventPtr()->mFlags.mInSystemGroup);
811 aDragEvent->PreventDefault();
812 aDragEvent->StopImmediatePropagation();
813 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
814 if (dataTransfer) {
815 dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
817 if (mCaret) {
818 mCaret->SetVisible(false);
822 nsresult EditorEventListener::DragOverOrDrop(DragEvent* aDragEvent) {
823 MOZ_ASSERT(aDragEvent);
824 MOZ_ASSERT(aDragEvent->WidgetEventPtr()->mMessage == eDrop ||
825 aDragEvent->WidgetEventPtr()->mMessage == eDragOver);
827 if (aDragEvent->WidgetEventPtr()->mMessage == eDrop) {
828 CleanupDragDropCaret();
829 MOZ_ASSERT(!mCaret);
830 } else {
831 InitializeDragDropCaret();
832 MOZ_ASSERT(mCaret);
835 if (DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr())) {
836 return NS_OK;
839 int32_t dropOffset = -1;
840 nsCOMPtr<nsIContent> dropParentContent =
841 aDragEvent->GetRangeParentContentAndOffset(&dropOffset);
842 if (NS_WARN_IF(!dropParentContent) || NS_WARN_IF(dropOffset < 0)) {
843 return NS_ERROR_FAILURE;
845 if (DetachedFromEditor()) {
846 RefuseToDropAndHideCaret(aDragEvent);
847 return NS_OK;
850 bool notEditable =
851 !dropParentContent->IsEditable() || mEditorBase->IsReadonly();
853 // First of all, hide caret if we won't insert the drop data into the editor
854 // obviously.
855 if (mCaret && (IsFileControlTextBox() || notEditable)) {
856 mCaret->SetVisible(false);
859 // If we're a native anonymous <input> element in <input type="file">,
860 // we don't need to handle the drop.
861 if (IsFileControlTextBox()) {
862 return NS_OK;
865 // If the drop target isn't ediable, the drop should be handled by the
866 // element.
867 if (notEditable) {
868 // If we're a text control element which is readonly or disabled,
869 // we should refuse to drop.
870 if (mEditorBase->IsTextEditor()) {
871 RefuseToDropAndHideCaret(aDragEvent);
872 return NS_OK;
874 // Otherwise, we shouldn't handle the drop.
875 return NS_OK;
878 // If the drag event does not have any data which we can handle, we should
879 // refuse to drop even if some parents can handle it because user may be
880 // trying to drop it on us, not our parent. For example, users don't want
881 // to drop HTML data to parent contenteditable element if they drop it on
882 // a child <input> element.
883 if (!DragEventHasSupportingData(aDragEvent)) {
884 RefuseToDropAndHideCaret(aDragEvent);
885 return NS_OK;
888 // If we don't accept the data drop at the point, for example, while dragging
889 // selection, it's not allowed dropping on its selection ranges. In this
890 // case, any parents shouldn't handle the drop instead of us, for example,
891 // dropping text shouldn't be treated as URL and load new page.
892 if (!CanInsertAtDropPosition(aDragEvent)) {
893 RefuseToDropAndHideCaret(aDragEvent);
894 return NS_OK;
897 WidgetDragEvent* asWidgetEvent = aDragEvent->WidgetEventPtr()->AsDragEvent();
898 AutoRestore<bool> inHTMLEditorEventListener(
899 asWidgetEvent->mInHTMLEditorEventListener);
900 if (mEditorBase->IsHTMLEditor()) {
901 asWidgetEvent->mInHTMLEditorEventListener = true;
903 aDragEvent->PreventDefault();
905 aDragEvent->StopImmediatePropagation();
907 if (asWidgetEvent->mMessage == eDrop) {
908 RefPtr<EditorBase> editorBase = mEditorBase;
909 nsresult rv = editorBase->HandleDropEvent(aDragEvent);
910 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
911 "EditorBase::HandleDropEvent() failed");
912 return rv;
915 MOZ_ASSERT(asWidgetEvent->mMessage == eDragOver);
917 // If we handle the dragged item, we need to adjust drop effect here
918 // because once DataTransfer is retrieved, DragEvent has initialized it
919 // with nsContentUtils::SetDataTransferInEvent() but it does not check
920 // whether the content is movable or not.
921 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
922 if (dataTransfer &&
923 dataTransfer->DropEffectInt() == nsIDragService::DRAGDROP_ACTION_MOVE) {
924 nsCOMPtr<nsINode> dragSource = dataTransfer->GetMozSourceNode();
925 if (dragSource && !dragSource->IsEditable()) {
926 // In this case, we shouldn't allow "move" because the drag source
927 // isn't editable.
928 dataTransfer->SetDropEffectInt(
929 nsContentUtils::FilterDropEffect(nsIDragService::DRAGDROP_ACTION_COPY,
930 dataTransfer->EffectAllowedInt()));
934 if (!mCaret) {
935 return NS_OK;
938 mCaret->SetVisible(true);
939 mCaret->SetCaretPosition(dropParentContent, dropOffset);
941 return NS_OK;
944 void EditorEventListener::InitializeDragDropCaret() {
945 if (mCaret) {
946 return;
949 RefPtr<PresShell> presShell = GetPresShell();
950 if (NS_WARN_IF(!presShell)) {
951 return;
954 mCaret = new nsCaret();
955 DebugOnly<nsresult> rvIgnored = mCaret->Init(presShell);
956 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
957 "nsCaret::Init() failed, but ignored");
958 mCaret->SetCaretReadOnly(true);
959 // This is to avoid the requirement that the Selection is Collapsed which
960 // it can't be when dragging a selection in the same shell.
961 // See nsCaret::IsVisible().
962 mCaret->SetVisibilityDuringSelection(true);
964 presShell->SetCaret(mCaret);
967 void EditorEventListener::CleanupDragDropCaret() {
968 if (!mCaret) {
969 return;
972 mCaret->SetVisible(false); // hide it, so that it turns off its timer
974 RefPtr<PresShell> presShell = GetPresShell();
975 if (presShell) {
976 presShell->RestoreCaret();
979 mCaret->Terminate();
980 mCaret = nullptr;
983 nsresult EditorEventListener::DragLeave(DragEvent* aDragEvent) {
984 // XXX If aDragEvent was created by chrome script, its defaultPrevented
985 // may be true, though. We shouldn't handle such event but we don't
986 // have a way to distinguish if coming event is created by chrome script.
987 NS_WARNING_ASSERTION(!aDragEvent->WidgetEventPtr()->DefaultPrevented(),
988 "eDragLeave shouldn't be cancelable");
989 if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
990 return NS_OK;
993 CleanupDragDropCaret();
995 return NS_OK;
998 bool EditorEventListener::DragEventHasSupportingData(
999 DragEvent* aDragEvent) const {
1000 MOZ_ASSERT(
1001 !DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
1002 MOZ_ASSERT(aDragEvent->GetDataTransfer());
1004 // Plaintext editors only support dropping text. Otherwise, HTML and files
1005 // can be dropped as well.
1006 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
1007 if (!dataTransfer) {
1008 NS_WARNING("No data transfer returned");
1009 return false;
1011 return dataTransfer->HasType(NS_LITERAL_STRING_FROM_CSTRING(kTextMime)) ||
1012 dataTransfer->HasType(
1013 NS_LITERAL_STRING_FROM_CSTRING(kMozTextInternal)) ||
1014 (mEditorBase->IsHTMLEditor() &&
1015 !mEditorBase->AsHTMLEditor()->IsPlaintextMailComposer() &&
1016 (dataTransfer->HasType(NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime)) ||
1017 dataTransfer->HasType(NS_LITERAL_STRING_FROM_CSTRING(kFileMime))));
1020 bool EditorEventListener::CanInsertAtDropPosition(DragEvent* aDragEvent) {
1021 MOZ_ASSERT(
1022 !DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
1023 MOZ_ASSERT(!mEditorBase->IsReadonly());
1024 MOZ_ASSERT(DragEventHasSupportingData(aDragEvent));
1026 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
1027 if (NS_WARN_IF(!dataTransfer)) {
1028 return false;
1031 // If there is no source node, this is probably an external drag and the
1032 // drop is allowed. The later checks rely on checking if the drag target
1033 // is the same as the drag source.
1034 nsCOMPtr<nsINode> sourceNode = dataTransfer->GetMozSourceNode();
1035 if (!sourceNode) {
1036 return true;
1039 // There is a source node, so compare the source documents and this document.
1040 // Disallow drops on the same document.
1042 RefPtr<Document> targetDocument = mEditorBase->GetDocument();
1043 if (NS_WARN_IF(!targetDocument)) {
1044 return false;
1047 RefPtr<Document> sourceDocument = sourceNode->OwnerDoc();
1049 // If the source and the dest are not same document, allow to drop it always.
1050 if (targetDocument != sourceDocument) {
1051 return true;
1054 // If the source node is a remote browser, treat this as coming from a
1055 // different document and allow the drop.
1056 if (BrowserParent::GetFrom(nsIContent::FromNode(sourceNode))) {
1057 return true;
1060 RefPtr<Selection> selection = mEditorBase->GetSelection();
1061 if (!selection) {
1062 return false;
1065 // If selection is collapsed, allow to drop it always.
1066 if (selection->IsCollapsed()) {
1067 return true;
1070 int32_t dropOffset = -1;
1071 nsCOMPtr<nsIContent> dropParentContent =
1072 aDragEvent->GetRangeParentContentAndOffset(&dropOffset);
1073 if (!dropParentContent || NS_WARN_IF(dropOffset < 0) ||
1074 NS_WARN_IF(DetachedFromEditor())) {
1075 return false;
1078 return !nsContentUtils::IsPointInSelection(*selection, *dropParentContent,
1079 dropOffset);
1082 nsresult EditorEventListener::HandleStartComposition(
1083 WidgetCompositionEvent* aCompositionStartEvent) {
1084 if (NS_WARN_IF(!aCompositionStartEvent)) {
1085 return NS_ERROR_FAILURE;
1087 if (DetachedFromEditor()) {
1088 return NS_OK;
1090 RefPtr<EditorBase> editorBase(mEditorBase);
1091 if (!editorBase->IsAcceptableInputEvent(aCompositionStartEvent)) {
1092 return NS_OK;
1094 // Although, "compositionstart" should be cancelable, but currently,
1095 // eCompositionStart event coming from widget is not cancelable.
1096 MOZ_ASSERT(!aCompositionStartEvent->DefaultPrevented(),
1097 "eCompositionStart shouldn't be cancelable");
1098 nsresult rv = editorBase->OnCompositionStart(*aCompositionStartEvent);
1099 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1100 "EditorBase::OnCompositionStart() failed");
1101 return rv;
1104 nsresult EditorEventListener::HandleChangeComposition(
1105 WidgetCompositionEvent* aCompositionChangeEvent) {
1106 if (NS_WARN_IF(!aCompositionChangeEvent)) {
1107 return NS_ERROR_FAILURE;
1109 MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
1110 "eCompositionChange event shouldn't be cancelable");
1111 if (DetachedFromEditor()) {
1112 return NS_OK;
1114 RefPtr<EditorBase> editorBase(mEditorBase);
1115 if (!editorBase->IsAcceptableInputEvent(aCompositionChangeEvent)) {
1116 return NS_OK;
1119 // if we are readonly, then do nothing.
1120 if (editorBase->IsReadonly()) {
1121 return NS_OK;
1124 nsresult rv = editorBase->OnCompositionChange(*aCompositionChangeEvent);
1125 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1126 "EditorBase::OnCompositionChange() failed");
1127 return rv;
1130 void EditorEventListener::HandleEndComposition(
1131 WidgetCompositionEvent* aCompositionEndEvent) {
1132 if (NS_WARN_IF(!aCompositionEndEvent) || DetachedFromEditor()) {
1133 return;
1135 RefPtr<EditorBase> editorBase(mEditorBase);
1136 if (!editorBase->IsAcceptableInputEvent(aCompositionEndEvent)) {
1137 return;
1139 MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
1140 "eCompositionEnd shouldn't be cancelable");
1142 editorBase->OnCompositionEnd(*aCompositionEndEvent);
1145 nsresult EditorEventListener::Focus(const InternalFocusEvent& aFocusEvent) {
1146 if (DetachedFromEditor()) {
1147 return NS_OK;
1150 nsCOMPtr<nsINode> originalEventTargetNode =
1151 nsINode::FromEventTargetOrNull(aFocusEvent.GetOriginalDOMEventTarget());
1152 if (NS_WARN_IF(!originalEventTargetNode)) {
1153 return NS_ERROR_UNEXPECTED;
1156 // If the target is a document node but it's not editable, we should
1157 // ignore it because actual focused element's event is going to come.
1158 if (originalEventTargetNode->IsDocument()) {
1159 if (!originalEventTargetNode->IsInDesignMode()) {
1160 return NS_OK;
1163 // We should not receive focus events whose target is not a content node
1164 // unless the node is a document node.
1165 else if (NS_WARN_IF(!originalEventTargetNode->IsContent())) {
1166 return NS_OK;
1169 const OwningNonNull<EditorBase> editorBase(*mEditorBase);
1170 DebugOnly<nsresult> rvIgnored = editorBase->OnFocus(*originalEventTargetNode);
1171 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "EditorBase::OnFocus() failed");
1172 return NS_OK; // Don't return error code to the event listener manager.
1175 nsresult EditorEventListener::Blur(const InternalFocusEvent& aBlurEvent) {
1176 if (DetachedFromEditor()) {
1177 return NS_OK;
1180 DebugOnly<nsresult> rvIgnored = mEditorBase->OnBlur(aBlurEvent.mTarget);
1181 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), "EditorBase::OnBlur() failed");
1182 return NS_OK; // Don't return error code to the event listener manager.
1185 bool EditorEventListener::IsFileControlTextBox() {
1186 MOZ_ASSERT(!DetachedFromEditor());
1188 RefPtr<EditorBase> editorBase(mEditorBase);
1189 Element* rootElement = editorBase->GetRoot();
1190 if (!rootElement || !rootElement->ChromeOnlyAccess()) {
1191 return false;
1193 nsIContent* parent = rootElement->FindFirstNonChromeOnlyAccessContent();
1194 if (!parent || !parent->IsHTMLElement(nsGkAtoms::input)) {
1195 return false;
1197 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(parent);
1198 return formControl->ControlType() == FormControlType::InputFile;
1201 bool EditorEventListener::ShouldHandleNativeKeyBindings(
1202 WidgetKeyboardEvent* aKeyboardEvent) {
1203 MOZ_ASSERT(!DetachedFromEditor());
1205 // Only return true if the target of the event is a desendant of the active
1206 // editing host in order to match the similar decision made in
1207 // nsXBLWindowKeyHandler.
1208 // Note that IsAcceptableInputEvent doesn't check for the active editing
1209 // host for keyboard events, otherwise this check would have been
1210 // unnecessary. IsAcceptableInputEvent currently makes a similar check for
1211 // mouse events.
1213 nsCOMPtr<nsIContent> targetContent = nsIContent::FromEventTargetOrNull(
1214 aKeyboardEvent->GetOriginalDOMEventTarget());
1215 if (NS_WARN_IF(!targetContent)) {
1216 return false;
1219 RefPtr<HTMLEditor> htmlEditor = HTMLEditor::GetFrom(mEditorBase);
1220 if (!htmlEditor) {
1221 return false;
1224 if (htmlEditor->IsInDesignMode()) {
1225 // Don't need to perform any checks in designMode documents.
1226 return true;
1229 nsIContent* editingHost = htmlEditor->ComputeEditingHost();
1230 if (!editingHost) {
1231 return false;
1234 return targetContent->IsInclusiveDescendantOf(editingHost);
1237 } // namespace mozilla