Bug 1686495 [wpt PR 27132] - Add tests for proposed WebDriver Shadow DOM support...
[gecko.git] / editor / libeditor / EditorEventListener.cpp
blob42cf7a5fd8f6f48eeb8c7d05c3ffa7b8e9945be9
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 "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
10 #include "mozilla/AutoRestore.h"
11 #include "mozilla/ContentEvents.h" // for InternalFocusEvent
12 #include "mozilla/EditorBase.h" // for EditorBase, etc.
13 #include "mozilla/EventListenerManager.h" // for EventListenerManager
14 #include "mozilla/EventStateManager.h" // for EventStateManager
15 #include "mozilla/HTMLEditor.h" // for HTMLEditor
16 #include "mozilla/IMEStateManager.h" // for IMEStateManager
17 #include "mozilla/Preferences.h" // for Preferences
18 #include "mozilla/PresShell.h" // for PresShell
19 #include "mozilla/TextEditor.h" // for TextEditor
20 #include "mozilla/TextEvents.h" // for WidgetCompositionEvent
21 #include "mozilla/dom/Element.h" // for Element
22 #include "mozilla/dom/Event.h" // for Event
23 #include "mozilla/dom/EventTarget.h" // for EventTarget
24 #include "mozilla/dom/MouseEvent.h" // for MouseEvent
25 #include "mozilla/dom/Selection.h"
26 #include "nsAString.h"
27 #include "nsCaret.h" // for nsCaret
28 #include "nsDebug.h" // for NS_WARNING, etc.
29 #include "nsFocusManager.h" // for nsFocusManager
30 #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::input
31 #include "nsIContent.h" // for nsIContent
32 #include "nsIController.h" // for nsIController
33 #include "nsID.h"
34 #include "mozilla/dom/DOMStringList.h"
35 #include "mozilla/dom/DataTransfer.h"
36 #include "mozilla/dom/DragEvent.h"
37 #include "mozilla/dom/Document.h" // for Document
38 #include "nsIFormControl.h" // for nsIFormControl, etc.
39 #include "nsINode.h" // for nsINode, ::NODE_IS_EDITABLE, etc.
40 #include "nsIWidget.h" // for nsIWidget
41 #include "nsLiteralString.h" // for NS_LITERAL_STRING
42 #include "nsPIWindowRoot.h" // for nsPIWindowRoot
43 #include "nsPrintfCString.h" // for nsPrintfCString
44 #include "nsRange.h"
45 #include "nsServiceManagerUtils.h" // for do_GetService
46 #include "nsString.h" // for nsAutoString
47 #include "nsQueryObject.h" // for do_QueryObject
48 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
49 # include "nsContentUtils.h" // for nsContentUtils, etc.
50 # include "nsIBidiKeyboard.h" // for nsIBidiKeyboard
51 #endif
53 #include "mozilla/dom/BrowserParent.h"
55 class nsPresContext;
57 namespace mozilla {
59 using namespace dom;
61 MOZ_CAN_RUN_SCRIPT static void DoCommandCallback(Command aCommand,
62 void* aData) {
63 Document* doc = static_cast<Document*>(aData);
64 nsPIDOMWindowOuter* win = doc->GetWindow();
65 if (!win) {
66 return;
68 nsCOMPtr<nsPIWindowRoot> root = win->GetTopWindowRoot();
69 if (!root) {
70 return;
73 const char* commandStr = WidgetKeyboardEvent::GetCommandStr(aCommand);
75 nsCOMPtr<nsIController> controller;
76 root->GetControllerForCommand(commandStr, false /* for any window */,
77 getter_AddRefs(controller));
78 if (!controller) {
79 return;
82 bool commandEnabled;
83 if (NS_WARN_IF(NS_FAILED(
84 controller->IsCommandEnabled(commandStr, &commandEnabled)))) {
85 return;
87 if (commandEnabled) {
88 controller->DoCommand(commandStr);
92 EditorEventListener::EditorEventListener()
93 : mEditorBase(nullptr),
94 mCommitText(false),
95 mInTransaction(false),
96 mMouseDownOrUpConsumedByIME(false)
97 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
99 mHaveBidiKeyboards(false),
100 mShouldSwitchTextDirection(false),
101 mSwitchToRTL(false)
102 #endif
106 EditorEventListener::~EditorEventListener() {
107 if (mEditorBase) {
108 NS_WARNING("We've not been uninstalled yet");
109 Disconnect();
113 nsresult EditorEventListener::Connect(EditorBase* aEditorBase) {
114 if (NS_WARN_IF(!aEditorBase)) {
115 return NS_ERROR_INVALID_ARG;
118 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
119 nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
120 if (bidiKeyboard) {
121 bool haveBidiKeyboards = false;
122 bidiKeyboard->GetHaveBidiKeyboards(&haveBidiKeyboards);
123 mHaveBidiKeyboards = haveBidiKeyboards;
125 #endif
127 mEditorBase = aEditorBase;
129 nsresult rv = InstallToEditor();
130 if (NS_FAILED(rv)) {
131 NS_WARNING("EditorEventListener::InstallToEditor() failed");
132 Disconnect();
134 return rv;
137 nsresult EditorEventListener::InstallToEditor() {
138 MOZ_ASSERT(mEditorBase, "The caller must set mEditorBase");
140 EventTarget* eventTarget = mEditorBase->GetDOMEventTarget();
141 if (NS_WARN_IF(!eventTarget)) {
142 return NS_ERROR_FAILURE;
145 // register the event listeners with the listener manager
146 EventListenerManager* eventListenerManager =
147 eventTarget->GetOrCreateListenerManager();
148 if (NS_WARN_IF(!eventListenerManager)) {
149 return NS_ERROR_FAILURE;
152 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
153 eventListenerManager->AddEventListenerByType(
154 this, u"keydown"_ns, TrustedEventsAtSystemGroupBubble());
155 eventListenerManager->AddEventListenerByType(
156 this, u"keyup"_ns, TrustedEventsAtSystemGroupBubble());
157 #endif
158 eventListenerManager->AddEventListenerByType(
159 this, u"keypress"_ns, TrustedEventsAtSystemGroupBubble());
160 eventListenerManager->AddEventListenerByType(
161 this, u"dragenter"_ns, TrustedEventsAtSystemGroupBubble());
162 eventListenerManager->AddEventListenerByType(
163 this, u"dragover"_ns, TrustedEventsAtSystemGroupBubble());
164 eventListenerManager->AddEventListenerByType(
165 this, u"dragexit"_ns, TrustedEventsAtSystemGroupBubble());
166 eventListenerManager->AddEventListenerByType(
167 this, u"drop"_ns, TrustedEventsAtSystemGroupBubble());
168 // XXX We should add the mouse event listeners as system event group.
169 // E.g., web applications cannot prevent middle mouse paste by
170 // preventDefault() of click event at bubble phase.
171 // However, if we do so, all click handlers in any frames and frontend
172 // code need to check if it's editable. It makes easier create new bugs.
173 eventListenerManager->AddEventListenerByType(this, u"mousedown"_ns,
174 TrustedEventsAtCapture());
175 eventListenerManager->AddEventListenerByType(this, u"mouseup"_ns,
176 TrustedEventsAtCapture());
177 eventListenerManager->AddEventListenerByType(this, u"click"_ns,
178 TrustedEventsAtCapture());
179 eventListenerManager->AddEventListenerByType(
180 this, u"auxclick"_ns, TrustedEventsAtSystemGroupCapture());
181 // Focus event doesn't bubble so adding the listener to capturing phase as
182 // system event group.
183 eventListenerManager->AddEventListenerByType(
184 this, u"blur"_ns, TrustedEventsAtSystemGroupCapture());
185 eventListenerManager->AddEventListenerByType(
186 this, u"focus"_ns, TrustedEventsAtSystemGroupCapture());
187 eventListenerManager->AddEventListenerByType(
188 this, u"text"_ns, TrustedEventsAtSystemGroupBubble());
189 eventListenerManager->AddEventListenerByType(
190 this, u"compositionstart"_ns, TrustedEventsAtSystemGroupBubble());
191 eventListenerManager->AddEventListenerByType(
192 this, u"compositionend"_ns, TrustedEventsAtSystemGroupBubble());
194 return NS_OK;
197 void EditorEventListener::Disconnect() {
198 if (DetachedFromEditor()) {
199 return;
201 UninstallFromEditor();
203 nsFocusManager* fm = nsFocusManager::GetFocusManager();
204 if (fm) {
205 nsIContent* focusedContent = fm->GetFocusedElement();
206 mozilla::dom::Element* root = mEditorBase->GetRoot();
207 if (focusedContent && root &&
208 focusedContent->IsInclusiveDescendantOf(root)) {
209 // Reset the Selection ancestor limiter and SelectionController state
210 // that EditorBase::InitializeSelection set up.
211 DebugOnly<nsresult> rvIgnored = mEditorBase->FinalizeSelection();
212 NS_WARNING_ASSERTION(
213 NS_SUCCEEDED(rvIgnored),
214 "EditorBase::FinalizeSelection() failed, but ignored");
218 mEditorBase = nullptr;
221 void EditorEventListener::UninstallFromEditor() {
222 CleanupDragDropCaret();
224 EventTarget* eventTarget = mEditorBase->GetDOMEventTarget();
225 if (NS_WARN_IF(!eventTarget)) {
226 return;
229 EventListenerManager* eventListenerManager =
230 eventTarget->GetOrCreateListenerManager();
231 if (NS_WARN_IF(!eventListenerManager)) {
232 return;
235 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
236 eventListenerManager->RemoveEventListenerByType(
237 this, u"keydown"_ns, TrustedEventsAtSystemGroupBubble());
238 eventListenerManager->RemoveEventListenerByType(
239 this, u"keyup"_ns, TrustedEventsAtSystemGroupBubble());
240 #endif
241 eventListenerManager->RemoveEventListenerByType(
242 this, u"keypress"_ns, TrustedEventsAtSystemGroupBubble());
243 eventListenerManager->RemoveEventListenerByType(
244 this, u"dragenter"_ns, TrustedEventsAtSystemGroupBubble());
245 eventListenerManager->RemoveEventListenerByType(
246 this, u"dragover"_ns, TrustedEventsAtSystemGroupBubble());
247 eventListenerManager->RemoveEventListenerByType(
248 this, u"dragexit"_ns, TrustedEventsAtSystemGroupBubble());
249 eventListenerManager->RemoveEventListenerByType(
250 this, u"drop"_ns, TrustedEventsAtSystemGroupBubble());
251 eventListenerManager->RemoveEventListenerByType(this, u"mousedown"_ns,
252 TrustedEventsAtCapture());
253 eventListenerManager->RemoveEventListenerByType(this, u"mouseup"_ns,
254 TrustedEventsAtCapture());
255 eventListenerManager->RemoveEventListenerByType(this, u"click"_ns,
256 TrustedEventsAtCapture());
257 eventListenerManager->RemoveEventListenerByType(
258 this, u"auxclick"_ns, TrustedEventsAtSystemGroupCapture());
259 eventListenerManager->RemoveEventListenerByType(
260 this, u"blur"_ns, TrustedEventsAtSystemGroupCapture());
261 eventListenerManager->RemoveEventListenerByType(
262 this, u"focus"_ns, TrustedEventsAtSystemGroupCapture());
263 eventListenerManager->RemoveEventListenerByType(
264 this, u"text"_ns, TrustedEventsAtSystemGroupBubble());
265 eventListenerManager->RemoveEventListenerByType(
266 this, u"compositionstart"_ns, TrustedEventsAtSystemGroupBubble());
267 eventListenerManager->RemoveEventListenerByType(
268 this, u"compositionend"_ns, TrustedEventsAtSystemGroupBubble());
271 PresShell* EditorEventListener::GetPresShell() const {
272 MOZ_ASSERT(!DetachedFromEditor());
273 return mEditorBase->GetPresShell();
276 nsPresContext* EditorEventListener::GetPresContext() const {
277 PresShell* presShell = GetPresShell();
278 return presShell ? presShell->GetPresContext() : nullptr;
281 nsIContent* EditorEventListener::GetFocusedRootContent() {
282 MOZ_ASSERT(!DetachedFromEditor());
283 nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
284 if (!focusedContent) {
285 return nullptr;
288 Document* composedDoc = focusedContent->GetComposedDoc();
289 if (NS_WARN_IF(!composedDoc)) {
290 return nullptr;
293 if (composedDoc->HasFlag(NODE_IS_EDITABLE)) {
294 return nullptr;
297 return focusedContent;
300 bool EditorEventListener::EditorHasFocus() {
301 MOZ_ASSERT(!DetachedFromEditor());
302 nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
303 if (!focusedContent) {
304 return false;
306 return !!focusedContent->GetComposedDoc();
309 NS_IMPL_ISUPPORTS(EditorEventListener, nsIDOMEventListener)
311 bool EditorEventListener::DetachedFromEditor() const { return !mEditorBase; }
313 bool EditorEventListener::DetachedFromEditorOrDefaultPrevented(
314 WidgetEvent* aWidgetEvent) const {
315 return NS_WARN_IF(!aWidgetEvent) || DetachedFromEditor() ||
316 aWidgetEvent->DefaultPrevented();
319 bool EditorEventListener::EnsureCommitComposition() {
320 MOZ_ASSERT(!DetachedFromEditor());
321 RefPtr<EditorBase> editorBase(mEditorBase);
322 editorBase->CommitComposition();
323 return !DetachedFromEditor();
326 NS_IMETHODIMP EditorEventListener::HandleEvent(Event* aEvent) {
327 // Let's handle each event with the message of the internal event of the
328 // coming event. If the DOM event was created with improper interface,
329 // e.g., keydown event is created with |new MouseEvent("keydown", {});|,
330 // its message is always 0. Therefore, we can ban such strange event easy.
331 // However, we need to handle strange "focus" and "blur" event. See the
332 // following code of this switch statement.
333 // NOTE: Each event handler may require specific event interface. Before
334 // calling it, this queries the specific interface. If it would fail,
335 // each event handler would just ignore the event. So, in this method,
336 // you don't need to check if the QI succeeded before each call.
337 WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
338 switch (internalEvent->mMessage) {
339 // dragenter
340 case eDragEnter: {
341 // aEvent should be grabbed by the caller since this is
342 // nsIDOMEventListener method. However, our clang plugin cannot check it
343 // if we use Event::As*Event(). So, we need to grab it by ourselves.
344 RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
345 nsresult rv = DragEnter(dragEvent);
346 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
347 "EditorEventListener::DragEnter() failed");
348 return rv;
350 // dragover and drop
351 case eDragOver:
352 case eDrop: {
353 RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
354 nsresult rv = DragOverOrDrop(dragEvent);
355 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
356 "EditorEventListener::DragOverOrDrop() failed");
357 return rv;
359 // dragexit
360 case eDragExit: {
361 RefPtr<DragEvent> dragEvent = aEvent->AsDragEvent();
362 nsresult rv = DragExit(dragEvent);
363 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
364 "EditorEventListener::DragExit() failed");
365 return rv;
367 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
368 // keydown
369 case eKeyDown: {
370 nsresult rv = KeyDown(internalEvent->AsKeyboardEvent());
371 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
372 "EditorEventListener::KeyDown() failed");
373 return rv;
375 // keyup
376 case eKeyUp: {
377 nsresult rv = KeyUp(internalEvent->AsKeyboardEvent());
378 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
379 "EditorEventListener::KeyUp() failed");
380 return rv;
382 #endif // #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
383 // keypress
384 case eKeyPress: {
385 nsresult rv = KeyPress(internalEvent->AsKeyboardEvent());
386 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
387 "EditorEventListener::KeyPress() failed");
388 return rv;
390 // mousedown
391 case eMouseDown: {
392 // EditorEventListener may receive (1) all mousedown, mouseup and click
393 // events, (2) only mousedown event or (3) only mouseup event.
394 // mMouseDownOrUpConsumedByIME is used only for ignoring click event if
395 // preceding mousedown and/or mouseup event is consumed by IME.
396 // Therefore, even if case #2 or case #3 occurs,
397 // mMouseDownOrUpConsumedByIME is true here. Therefore, we should always
398 // overwrite it here.
399 mMouseDownOrUpConsumedByIME =
400 NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent());
401 if (mMouseDownOrUpConsumedByIME) {
402 return NS_OK;
404 RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
405 if (NS_WARN_IF(!mouseEvent)) {
406 return NS_OK;
408 nsresult rv = MouseDown(mouseEvent);
409 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
410 "EditorEventListener::MouseDown() failed");
411 return rv;
413 // mouseup
414 case eMouseUp: {
415 // See above comment in the eMouseDown case, first.
416 // This code assumes that case #1 is occuring. However, if case #3 may
417 // occurs after case #2 and the mousedown is consumed,
418 // mMouseDownOrUpConsumedByIME is true even though EditorEventListener
419 // has not received the preceding mousedown event of this mouseup event.
420 // So, mMouseDownOrUpConsumedByIME may be invalid here. However,
421 // this is not a matter because mMouseDownOrUpConsumedByIME is referred
422 // only by eMouseClick case but click event is fired only in case #1.
423 // So, before a click event is fired, mMouseDownOrUpConsumedByIME is
424 // always initialized in the eMouseDown case if it's referred.
425 if (NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent())) {
426 mMouseDownOrUpConsumedByIME = true;
428 if (mMouseDownOrUpConsumedByIME) {
429 return NS_OK;
431 RefPtr<MouseEvent> mouseEvent = aEvent->AsMouseEvent();
432 if (NS_WARN_IF(!mouseEvent)) {
433 return NS_OK;
435 nsresult rv = MouseUp(mouseEvent);
436 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
437 "EditorEventListener::MouseUp() failed");
438 return rv;
440 // click
441 case eMouseClick: {
442 WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
443 // Don't handle non-primary click events
444 if (widgetMouseEvent->mButton != MouseButton::ePrimary) {
445 return NS_OK;
447 [[fallthrough]];
449 // auxclick
450 case eMouseAuxClick: {
451 WidgetMouseEvent* widgetMouseEvent = internalEvent->AsMouseEvent();
452 if (NS_WARN_IF(!widgetMouseEvent)) {
453 return NS_OK;
455 // If the preceding mousedown event or mouseup event was consumed,
456 // editor shouldn't handle this click event.
457 if (mMouseDownOrUpConsumedByIME) {
458 mMouseDownOrUpConsumedByIME = false;
459 widgetMouseEvent->PreventDefault();
460 return NS_OK;
462 nsresult rv = MouseClick(widgetMouseEvent);
463 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
464 "EditorEventListener::MouseClick() failed");
465 return rv;
467 // focus
468 case eFocus: {
469 nsresult rv = Focus(internalEvent->AsFocusEvent());
470 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
471 "EditorEventListener::Focus() failed");
472 return rv;
474 // blur
475 case eBlur: {
476 nsresult rv = Blur(internalEvent->AsFocusEvent());
477 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
478 "EditorEventListener::Blur() failed");
479 return rv;
481 // text
482 case eCompositionChange: {
483 nsresult rv =
484 HandleChangeComposition(internalEvent->AsCompositionEvent());
485 NS_WARNING_ASSERTION(
486 NS_SUCCEEDED(rv),
487 "EditorEventListener::HandleChangeComposition() failed");
488 return rv;
490 // compositionstart
491 case eCompositionStart: {
492 nsresult rv = HandleStartComposition(internalEvent->AsCompositionEvent());
493 NS_WARNING_ASSERTION(
494 NS_SUCCEEDED(rv),
495 "EditorEventListener::HandleStartComposition() failed");
496 return rv;
498 // compositionend
499 case eCompositionEnd: {
500 HandleEndComposition(internalEvent->AsCompositionEvent());
501 return NS_OK;
503 default:
504 break;
507 #ifdef DEBUG
508 nsAutoString eventType;
509 aEvent->GetType(eventType);
510 nsPrintfCString assertMessage(
511 "Editor doesn't handle \"%s\" event "
512 "because its internal event doesn't have proper message",
513 NS_ConvertUTF16toUTF8(eventType).get());
514 NS_ASSERTION(false, assertMessage.get());
515 #endif
517 return NS_OK;
520 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
522 // This function is borrowed from Chromium's ImeInput::IsCtrlShiftPressed
523 bool IsCtrlShiftPressed(const WidgetKeyboardEvent* aKeyboardEvent,
524 bool& isRTL) {
525 MOZ_ASSERT(aKeyboardEvent);
526 // To check if a user is pressing only a control key and a right-shift key
527 // (or a left-shift key), we use the steps below:
528 // 1. Check if a user is pressing a control key and a right-shift key (or
529 // a left-shift key).
530 // 2. If the condition 1 is true, we should check if there are any other
531 // keys pressed at the same time.
532 // To ignore the keys checked in 1, we set their status to 0 before
533 // checking the key status.
535 if (!aKeyboardEvent->IsControl()) {
536 return false;
539 switch (aKeyboardEvent->mLocation) {
540 case eKeyLocationRight:
541 isRTL = true;
542 break;
543 case eKeyLocationLeft:
544 isRTL = false;
545 break;
546 default:
547 return false;
550 // Scan the key status to find pressed keys. We should abandon changing the
551 // text direction when there are other pressed keys.
552 if (aKeyboardEvent->IsAlt() || aKeyboardEvent->IsOS()) {
553 return false;
556 return true;
559 // This logic is mostly borrowed from Chromium's
560 // RenderWidgetHostViewWin::OnKeyEvent.
562 nsresult EditorEventListener::KeyUp(const WidgetKeyboardEvent* aKeyboardEvent) {
563 if (NS_WARN_IF(!aKeyboardEvent) || DetachedFromEditor()) {
564 return NS_OK;
567 if (!mHaveBidiKeyboards) {
568 return NS_OK;
571 // XXX Why doesn't this method check if it's consumed?
572 RefPtr<EditorBase> editorBase(mEditorBase);
573 if ((aKeyboardEvent->mKeyCode == NS_VK_SHIFT ||
574 aKeyboardEvent->mKeyCode == NS_VK_CONTROL) &&
575 mShouldSwitchTextDirection && editorBase->IsPlaintextEditor()) {
576 editorBase->SwitchTextDirectionTo(mSwitchToRTL
577 ? EditorBase::TextDirection::eRTL
578 : EditorBase::TextDirection::eLTR);
579 mShouldSwitchTextDirection = false;
581 return NS_OK;
584 nsresult EditorEventListener::KeyDown(
585 const WidgetKeyboardEvent* aKeyboardEvent) {
586 if (NS_WARN_IF(!aKeyboardEvent) || DetachedFromEditor()) {
587 return NS_OK;
590 if (!mHaveBidiKeyboards) {
591 return NS_OK;
594 // XXX Why isn't this method check if it's consumed?
595 if (aKeyboardEvent->mKeyCode == NS_VK_SHIFT) {
596 bool switchToRTL;
597 if (IsCtrlShiftPressed(aKeyboardEvent, switchToRTL)) {
598 mShouldSwitchTextDirection = true;
599 mSwitchToRTL = switchToRTL;
601 } else if (aKeyboardEvent->mKeyCode != NS_VK_CONTROL) {
602 // In case the user presses any other key besides Ctrl and Shift
603 mShouldSwitchTextDirection = false;
605 return NS_OK;
608 #endif // #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
610 nsresult EditorEventListener::KeyPress(WidgetKeyboardEvent* aKeyboardEvent) {
611 if (NS_WARN_IF(!aKeyboardEvent)) {
612 return NS_OK;
615 RefPtr<EditorBase> editorBase(mEditorBase);
616 if (!editorBase->IsAcceptableInputEvent(aKeyboardEvent) ||
617 DetachedFromEditorOrDefaultPrevented(aKeyboardEvent)) {
618 return NS_OK;
621 nsresult rv = editorBase->HandleKeyPressEvent(aKeyboardEvent);
622 if (NS_FAILED(rv)) {
623 NS_WARNING("EditorBase::HandleKeyPressEvent() failed");
624 return rv;
626 if (DetachedFromEditorOrDefaultPrevented(aKeyboardEvent)) {
627 return NS_OK;
630 if (!ShouldHandleNativeKeyBindings(aKeyboardEvent)) {
631 return NS_OK;
634 // Now, ask the native key bindings to handle the event.
635 nsIWidget* widget = aKeyboardEvent->mWidget;
636 // If the event is created by chrome script, the widget is always nullptr.
637 if (!widget) {
638 nsPresContext* presContext = GetPresContext();
639 if (NS_WARN_IF(!presContext)) {
640 return NS_OK;
642 widget = presContext->GetNearestWidget();
643 if (NS_WARN_IF(!widget)) {
644 return NS_OK;
648 RefPtr<Document> doc = editorBase->GetDocument();
650 // WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
651 // If the event is created by chrome script, it is nullptr but we need to
652 // execute native key bindings. Therefore, we need to set widget to
653 // WidgetEvent::mWidget temporarily.
654 AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(aKeyboardEvent->mWidget);
655 aKeyboardEvent->mWidget = widget;
656 if (aKeyboardEvent->ExecuteEditCommands(
657 nsIWidget::NativeKeyBindingsForRichTextEditor, DoCommandCallback,
658 doc)) {
659 aKeyboardEvent->PreventDefault();
661 return NS_OK;
664 nsresult EditorEventListener::MouseClick(WidgetMouseEvent* aMouseClickEvent) {
665 if (NS_WARN_IF(!aMouseClickEvent) || DetachedFromEditor()) {
666 return NS_OK;
668 // nothing to do if editor isn't editable or clicked on out of the editor.
669 RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
670 if (textEditor->IsReadonly() ||
671 !textEditor->IsAcceptableInputEvent(aMouseClickEvent)) {
672 return NS_OK;
675 // Notifies clicking on editor to IMEStateManager even when the event was
676 // consumed.
677 if (EditorHasFocus()) {
678 RefPtr<nsPresContext> presContext = GetPresContext();
679 if (presContext) {
680 IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
681 aMouseClickEvent);
682 if (DetachedFromEditor()) {
683 return NS_OK;
688 if (DetachedFromEditorOrDefaultPrevented(aMouseClickEvent)) {
689 // We're done if 'preventdefault' is true (see for example bug 70698).
690 return NS_OK;
693 // If we got a mouse down inside the editing area, we should force the
694 // IME to commit before we change the cursor position.
695 if (!EnsureCommitComposition()) {
696 return NS_OK;
699 // XXX The following code is hack for our buggy "click" and "auxclick"
700 // implementation. "auxclick" event was added recently, however,
701 // any non-primary button click event handlers in our UI still keep
702 // listening to "click" events. Additionally, "auxclick" event is
703 // fired after "click" events and even if we do this in the system event
704 // group, middle click opens new tab before us. Therefore, we need to
705 // handle middle click at capturing phase of the default group even
706 // though this makes web apps cannot prevent middle click paste with
707 // calling preventDefault() of "click" nor "auxclick".
709 if (aMouseClickEvent->mButton != MouseButton::eMiddle ||
710 !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
711 return NS_OK;
714 RefPtr<PresShell> presShell = GetPresShell();
715 if (NS_WARN_IF(!presShell)) {
716 return NS_OK;
718 nsPresContext* presContext = GetPresContext();
719 if (NS_WARN_IF(!presContext)) {
720 return NS_OK;
722 MOZ_ASSERT(!aMouseClickEvent->DefaultPrevented());
723 nsEventStatus status = nsEventStatus_eIgnore;
724 RefPtr<EventStateManager> esm = presContext->EventStateManager();
725 DebugOnly<nsresult> rvIgnored = esm->HandleMiddleClickPaste(
726 presShell, aMouseClickEvent, &status, textEditor);
727 NS_WARNING_ASSERTION(
728 NS_SUCCEEDED(rvIgnored),
729 "EventStateManager::HandleMiddleClickPaste() failed, but ignored");
730 if (status == nsEventStatus_eConsumeNoDefault) {
731 // We no longer need to StopImmediatePropagation here since
732 // ClickHandlerChild.jsm checks for and ignores editables, so won't
733 // re-handle the event
734 aMouseClickEvent->PreventDefault();
736 return NS_OK;
739 bool EditorEventListener::NotifyIMEOfMouseButtonEvent(
740 WidgetMouseEvent* aMouseEvent) {
741 MOZ_ASSERT(aMouseEvent);
743 if (!EditorHasFocus()) {
744 return false;
747 RefPtr<nsPresContext> presContext = GetPresContext();
748 if (NS_WARN_IF(!presContext)) {
749 return false;
751 nsCOMPtr<nsIContent> focusedRootContent = GetFocusedRootContent();
752 return IMEStateManager::OnMouseButtonEventInEditor(
753 presContext, focusedRootContent, aMouseEvent);
756 nsresult EditorEventListener::MouseDown(MouseEvent* aMouseEvent) {
757 // FYI: We don't need to check if it's already consumed here because
758 // we need to commit composition at mouse button operation.
759 // FYI: This may be called by HTMLEditorEventListener::MouseDown() even
760 // when the event is not acceptable for committing composition.
761 if (DetachedFromEditor()) {
762 return NS_OK;
764 Unused << EnsureCommitComposition();
765 return NS_OK;
769 * Drag event implementation
772 nsresult EditorEventListener::DragEnter(DragEvent* aDragEvent) {
773 if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
774 return NS_OK;
777 RefPtr<PresShell> presShell = GetPresShell();
778 if (NS_WARN_IF(!presShell)) {
779 return NS_OK;
782 if (!mCaret) {
783 mCaret = new nsCaret();
784 DebugOnly<nsresult> rvIgnored = mCaret->Init(presShell);
785 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
786 "nsCaret::Init() failed, but ignored");
787 mCaret->SetCaretReadOnly(true);
788 // This is to avoid the requirement that the Selection is Collapsed which
789 // it can't be when dragging a selection in the same shell.
790 // See nsCaret::IsVisible().
791 mCaret->SetVisibilityDuringSelection(true);
794 presShell->SetCaret(mCaret);
796 nsresult rv = DragOverOrDrop(aDragEvent);
797 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
798 "EditorEventListener::DragOverOrDrop() failed");
799 return rv;
802 void EditorEventListener::RefuseToDropAndHideCaret(DragEvent* aDragEvent) {
803 MOZ_ASSERT(aDragEvent->WidgetEventPtr()->mFlags.mInSystemGroup);
805 aDragEvent->PreventDefault();
806 aDragEvent->StopImmediatePropagation();
807 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
808 if (dataTransfer) {
809 dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
811 if (mCaret) {
812 mCaret->SetVisible(false);
816 nsresult EditorEventListener::DragOverOrDrop(DragEvent* aDragEvent) {
817 MOZ_ASSERT(aDragEvent);
818 MOZ_ASSERT(aDragEvent->WidgetEventPtr()->mMessage == eDrop ||
819 aDragEvent->WidgetEventPtr()->mMessage == eDragOver ||
820 aDragEvent->WidgetEventPtr()->mMessage == eDragEnter);
822 if (aDragEvent->WidgetEventPtr()->mMessage == eDrop) {
823 CleanupDragDropCaret();
824 MOZ_ASSERT(!mCaret);
827 if (DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr())) {
828 return NS_OK;
831 int32_t dropOffset = -1;
832 nsCOMPtr<nsIContent> dropParentContent =
833 aDragEvent->GetRangeParentContentAndOffset(&dropOffset);
834 if (NS_WARN_IF(!dropParentContent)) {
835 return NS_ERROR_FAILURE;
837 if (DetachedFromEditor()) {
838 RefuseToDropAndHideCaret(aDragEvent);
839 return NS_OK;
842 bool notEditable =
843 !dropParentContent->IsEditable() || mEditorBase->IsReadonly();
845 // First of all, hide caret if we won't insert the drop data into the editor
846 // obviously.
847 if (mCaret && (IsFileControlTextBox() || notEditable)) {
848 mCaret->SetVisible(false);
851 // If we're a native anonymous <input> element in <input type="file">,
852 // we don't need to handle the drop.
853 if (IsFileControlTextBox()) {
854 return NS_OK;
857 // If the drop target isn't ediable, the drop should be handled by the
858 // element.
859 if (notEditable) {
860 // If we're a text control element which is readonly or disabled,
861 // we should refuse to drop.
862 if (!mEditorBase->AsHTMLEditor()) {
863 RefuseToDropAndHideCaret(aDragEvent);
864 return NS_OK;
866 // Otherwise, we shouldn't handle the drop.
867 return NS_OK;
870 // If the drag event does not have any data which we can handle, we should
871 // refuse to drop even if some parents can handle it because user may be
872 // trying to drop it on us, not our parent. For example, users don't want
873 // to drop HTML data to parent contenteditable element if they drop it on
874 // a child <input> element.
875 if (!DragEventHasSupportingData(aDragEvent)) {
876 RefuseToDropAndHideCaret(aDragEvent);
877 return NS_OK;
880 // If we don't accept the data drop at the point, for example, while dragging
881 // selection, it's not allowed dropping on its selection ranges. In this
882 // case, any parents shouldn't handle the drop instead of us, for example,
883 // dropping text shouldn't be treated as URL and load new page.
884 if (!CanInsertAtDropPosition(aDragEvent)) {
885 RefuseToDropAndHideCaret(aDragEvent);
886 return NS_OK;
889 aDragEvent->PreventDefault();
890 aDragEvent->StopImmediatePropagation();
892 if (aDragEvent->WidgetEventPtr()->mMessage == eDrop) {
893 RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
894 nsresult rv = textEditor->OnDrop(aDragEvent);
895 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "TextEditor::OnDrop() failed");
896 return rv;
899 MOZ_ASSERT(aDragEvent->WidgetEventPtr()->mMessage == eDragOver ||
900 aDragEvent->WidgetEventPtr()->mMessage == eDragEnter);
902 // If we handle the dragged item, we need to adjust drop effect here
903 // because once DataTransfer is retrieved, DragEvent has initialized it
904 // with nsContentUtils::SetDataTransferInEvent() but it does not check
905 // whether the content is movable or not.
906 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
907 if (dataTransfer &&
908 dataTransfer->DropEffectInt() == nsIDragService::DRAGDROP_ACTION_MOVE) {
909 nsCOMPtr<nsINode> dragSource = dataTransfer->GetMozSourceNode();
910 if (dragSource && !dragSource->IsEditable()) {
911 // In this case, we shouldn't allow "move" because the drag source
912 // isn't editable.
913 dataTransfer->SetDropEffectInt(
914 nsContentUtils::FilterDropEffect(nsIDragService::DRAGDROP_ACTION_COPY,
915 dataTransfer->EffectAllowedInt()));
919 if (!mCaret) {
920 return NS_OK;
923 mCaret->SetVisible(true);
924 mCaret->SetCaretPosition(dropParentContent, dropOffset);
926 return NS_OK;
929 void EditorEventListener::CleanupDragDropCaret() {
930 if (!mCaret) {
931 return;
934 mCaret->SetVisible(false); // hide it, so that it turns off its timer
936 RefPtr<PresShell> presShell = GetPresShell();
937 if (presShell) {
938 presShell->RestoreCaret();
941 mCaret->Terminate();
942 mCaret = nullptr;
945 nsresult EditorEventListener::DragExit(DragEvent* aDragEvent) {
946 // XXX If aDragEvent was created by chrome script, its defaultPrevented
947 // may be true, though. We shouldn't handle such event but we don't
948 // have a way to distinguish if coming event is created by chrome script.
949 NS_WARNING_ASSERTION(!aDragEvent->WidgetEventPtr()->DefaultPrevented(),
950 "eDragExit shouldn't be cancelable");
951 if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
952 return NS_OK;
955 CleanupDragDropCaret();
957 return NS_OK;
960 bool EditorEventListener::DragEventHasSupportingData(
961 DragEvent* aDragEvent) const {
962 MOZ_ASSERT(
963 !DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
964 MOZ_ASSERT(aDragEvent->GetDataTransfer());
966 // Plaintext editors only support dropping text. Otherwise, HTML and files
967 // can be dropped as well.
968 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
969 if (!dataTransfer) {
970 NS_WARNING("No data transfer returned");
971 return false;
973 return dataTransfer->HasType(NS_LITERAL_STRING_FROM_CSTRING(kTextMime)) ||
974 dataTransfer->HasType(
975 NS_LITERAL_STRING_FROM_CSTRING(kMozTextInternal)) ||
976 (!mEditorBase->IsPlaintextEditor() &&
977 (dataTransfer->HasType(NS_LITERAL_STRING_FROM_CSTRING(kHTMLMime)) ||
978 dataTransfer->HasType(NS_LITERAL_STRING_FROM_CSTRING(kFileMime))));
981 bool EditorEventListener::CanInsertAtDropPosition(DragEvent* aDragEvent) {
982 MOZ_ASSERT(
983 !DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
984 MOZ_ASSERT(!mEditorBase->IsReadonly());
985 MOZ_ASSERT(DragEventHasSupportingData(aDragEvent));
987 DataTransfer* dataTransfer = aDragEvent->GetDataTransfer();
988 if (NS_WARN_IF(!dataTransfer)) {
989 return false;
992 // If there is no source node, this is probably an external drag and the
993 // drop is allowed. The later checks rely on checking if the drag target
994 // is the same as the drag source.
995 nsCOMPtr<nsINode> sourceNode = dataTransfer->GetMozSourceNode();
996 if (!sourceNode) {
997 return true;
1000 // There is a source node, so compare the source documents and this document.
1001 // Disallow drops on the same document.
1003 RefPtr<Document> targetDocument = mEditorBase->GetDocument();
1004 if (NS_WARN_IF(!targetDocument)) {
1005 return false;
1008 RefPtr<Document> sourceDocument = sourceNode->OwnerDoc();
1010 // If the source and the dest are not same document, allow to drop it always.
1011 if (targetDocument != sourceDocument) {
1012 return true;
1015 // If the source node is a remote browser, treat this as coming from a
1016 // different document and allow the drop.
1017 if (BrowserParent::GetFrom(nsIContent::FromNode(sourceNode))) {
1018 return true;
1021 RefPtr<Selection> selection = mEditorBase->GetSelection();
1022 if (!selection) {
1023 return false;
1026 // If selection is collapsed, allow to drop it always.
1027 if (selection->IsCollapsed()) {
1028 return true;
1031 int32_t dropOffset = -1;
1032 nsCOMPtr<nsIContent> dropParentContent =
1033 aDragEvent->GetRangeParentContentAndOffset(&dropOffset);
1034 if (!dropParentContent || NS_WARN_IF(DetachedFromEditor())) {
1035 return false;
1038 return !EditorUtils::IsPointInSelection(*selection, *dropParentContent,
1039 dropOffset);
1042 nsresult EditorEventListener::HandleStartComposition(
1043 WidgetCompositionEvent* aCompositionStartEvent) {
1044 if (NS_WARN_IF(!aCompositionStartEvent)) {
1045 return NS_ERROR_FAILURE;
1047 if (DetachedFromEditor()) {
1048 return NS_OK;
1050 RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
1051 if (!textEditor->IsAcceptableInputEvent(aCompositionStartEvent)) {
1052 return NS_OK;
1054 // Although, "compositionstart" should be cancelable, but currently,
1055 // eCompositionStart event coming from widget is not cancelable.
1056 MOZ_ASSERT(!aCompositionStartEvent->DefaultPrevented(),
1057 "eCompositionStart shouldn't be cancelable");
1058 nsresult rv = textEditor->OnCompositionStart(*aCompositionStartEvent);
1059 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1060 "TextEditor::OnCompositionStart() failed");
1061 return rv;
1064 nsresult EditorEventListener::HandleChangeComposition(
1065 WidgetCompositionEvent* aCompositionChangeEvent) {
1066 if (NS_WARN_IF(!aCompositionChangeEvent)) {
1067 return NS_ERROR_FAILURE;
1069 MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
1070 "eCompositionChange event shouldn't be cancelable");
1071 if (DetachedFromEditor()) {
1072 return NS_OK;
1074 RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
1075 if (!textEditor->IsAcceptableInputEvent(aCompositionChangeEvent)) {
1076 return NS_OK;
1079 // if we are readonly, then do nothing.
1080 if (textEditor->IsReadonly()) {
1081 return NS_OK;
1084 nsresult rv = textEditor->OnCompositionChange(*aCompositionChangeEvent);
1085 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1086 "TextEditor::OnCompositionChange() failed");
1087 return rv;
1090 void EditorEventListener::HandleEndComposition(
1091 WidgetCompositionEvent* aCompositionEndEvent) {
1092 if (NS_WARN_IF(!aCompositionEndEvent) || DetachedFromEditor()) {
1093 return;
1095 RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
1096 if (!textEditor->IsAcceptableInputEvent(aCompositionEndEvent)) {
1097 return;
1099 MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
1100 "eCompositionEnd shouldn't be cancelable");
1102 textEditor->OnCompositionEnd(*aCompositionEndEvent);
1105 nsresult EditorEventListener::Focus(InternalFocusEvent* aFocusEvent) {
1106 if (NS_WARN_IF(!aFocusEvent) || DetachedFromEditor()) {
1107 return NS_OK;
1110 RefPtr<EditorBase> editorBase(mEditorBase);
1112 // Spell check a textarea the first time that it is focused.
1113 SpellCheckIfNeeded();
1114 if (DetachedFromEditor()) {
1115 return NS_OK;
1118 EventTarget* target = aFocusEvent->GetOriginalDOMEventTarget();
1119 nsCOMPtr<nsINode> eventTargetNode = do_QueryInterface(target);
1120 if (NS_WARN_IF(!eventTargetNode)) {
1121 return NS_ERROR_UNEXPECTED;
1124 // If the target is a document node but it's not editable, we should ignore
1125 // it because actual focused element's event is going to come.
1126 if (eventTargetNode->IsDocument() &&
1127 !eventTargetNode->HasFlag(NODE_IS_EDITABLE)) {
1128 return NS_OK;
1131 if (eventTargetNode->IsContent()) {
1132 nsIContent* content =
1133 eventTargetNode->AsContent()->FindFirstNonChromeOnlyAccessContent();
1134 // XXX If the focus event target is a form control in contenteditable
1135 // element, perhaps, the parent HTML editor should do nothing by this
1136 // handler. However, FindSelectionRoot() returns the root element of the
1137 // contenteditable editor. So, the editableRoot value is invalid for
1138 // the plain text editor, and it will be set to the wrong limiter of
1139 // the selection. However, fortunately, actual bugs are not found yet.
1140 nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(content);
1142 // make sure that the element is really focused in case an earlier
1143 // listener in the chain changed the focus.
1144 if (editableRoot) {
1145 nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
1146 if (NS_WARN_IF(!focusManager)) {
1147 return NS_OK;
1150 nsIContent* focusedContent = focusManager->GetFocusedElement();
1151 if (!focusedContent) {
1152 return NS_OK;
1155 nsCOMPtr<nsIContent> originalTargetAsContent =
1156 do_QueryInterface(aFocusEvent->GetOriginalDOMEventTarget());
1158 if (!SameCOMIdentity(
1159 focusedContent->FindFirstNonChromeOnlyAccessContent(),
1160 originalTargetAsContent->FindFirstNonChromeOnlyAccessContent())) {
1161 return NS_OK;
1166 editorBase->OnFocus(*eventTargetNode);
1167 if (DetachedFromEditorOrDefaultPrevented(aFocusEvent)) {
1168 return NS_OK;
1171 RefPtr<nsPresContext> presContext = GetPresContext();
1172 if (NS_WARN_IF(!presContext)) {
1173 return NS_OK;
1175 nsCOMPtr<nsIContent> focusedContent = editorBase->GetFocusedContentForIME();
1176 IMEStateManager::OnFocusInEditor(presContext, focusedContent, *editorBase);
1178 return NS_OK;
1181 nsresult EditorEventListener::Blur(InternalFocusEvent* aBlurEvent) {
1182 if (NS_WARN_IF(!aBlurEvent) || DetachedFromEditor()) {
1183 return NS_OK;
1186 // check if something else is focused. If another element is focused, then
1187 // we should not change the selection.
1188 nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
1189 if (NS_WARN_IF(!focusManager)) {
1190 return NS_OK;
1193 Element* focusedElement = focusManager->GetFocusedElement();
1194 if (!focusedElement) {
1195 RefPtr<EditorBase> editorBase(mEditorBase);
1196 DebugOnly<nsresult> rvIgnored = editorBase->FinalizeSelection();
1197 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
1198 "EditorBase::FinalizeSelection() failed, but ignored");
1200 return NS_OK;
1203 void EditorEventListener::SpellCheckIfNeeded() {
1204 MOZ_ASSERT(!DetachedFromEditor());
1206 // If the spell check skip flag is still enabled from creation time,
1207 // disable it because focused editors are allowed to spell check.
1208 RefPtr<EditorBase> editorBase(mEditorBase);
1209 if (!editorBase->ShouldSkipSpellCheck()) {
1210 return;
1212 DebugOnly<nsresult> rvIgnored =
1213 editorBase->RemoveFlags(nsIEditor::eEditorSkipSpellCheck);
1214 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
1215 "EditorBase::RemoveFlags() failed, but ignored");
1218 bool EditorEventListener::IsFileControlTextBox() {
1219 MOZ_ASSERT(!DetachedFromEditor());
1221 RefPtr<EditorBase> editorBase(mEditorBase);
1222 Element* rootElement = editorBase->GetRoot();
1223 if (!rootElement || !rootElement->ChromeOnlyAccess()) {
1224 return false;
1226 nsIContent* parent = rootElement->FindFirstNonChromeOnlyAccessContent();
1227 if (!parent || !parent->IsHTMLElement(nsGkAtoms::input)) {
1228 return false;
1230 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(parent);
1231 return formControl->ControlType() == NS_FORM_INPUT_FILE;
1234 bool EditorEventListener::ShouldHandleNativeKeyBindings(
1235 WidgetKeyboardEvent* aKeyboardEvent) {
1236 MOZ_ASSERT(!DetachedFromEditor());
1238 // Only return true if the target of the event is a desendant of the active
1239 // editing host in order to match the similar decision made in
1240 // nsXBLWindowKeyHandler.
1241 // Note that IsAcceptableInputEvent doesn't check for the active editing
1242 // host for keyboard events, otherwise this check would have been
1243 // unnecessary. IsAcceptableInputEvent currently makes a similar check for
1244 // mouse events.
1246 nsCOMPtr<nsIContent> targetContent =
1247 do_QueryInterface(aKeyboardEvent->GetDOMEventTarget());
1248 if (NS_WARN_IF(!targetContent)) {
1249 return false;
1252 RefPtr<EditorBase> editorBase(mEditorBase);
1253 HTMLEditor* htmlEditor = editorBase->AsHTMLEditor();
1254 if (!htmlEditor) {
1255 return false;
1258 RefPtr<Document> doc = editorBase->GetDocument();
1259 if (doc->HasFlag(NODE_IS_EDITABLE)) {
1260 // Don't need to perform any checks in designMode documents.
1261 return true;
1264 nsIContent* editingHost = htmlEditor->GetActiveEditingHost();
1265 if (!editingHost) {
1266 return false;
1269 return targetContent->IsInclusiveDescendantOf(editingHost);
1272 } // namespace mozilla