Line data Source code
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/. */
6 :
7 : #include "EditorEventListener.h"
8 :
9 : #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc.
10 : #include "mozilla/ContentEvents.h" // for InternalFocusEvent
11 : #include "mozilla/EditorBase.h" // for EditorBase, etc.
12 : #include "mozilla/EventListenerManager.h" // for EventListenerManager
13 : #include "mozilla/IMEStateManager.h" // for IMEStateManager
14 : #include "mozilla/Preferences.h" // for Preferences
15 : #include "mozilla/TextEvents.h" // for WidgetCompositionEvent
16 : #include "mozilla/dom/Element.h" // for Element
17 : #include "mozilla/dom/Event.h" // for Event
18 : #include "mozilla/dom/EventTarget.h" // for EventTarget
19 : #include "mozilla/dom/Selection.h"
20 : #include "nsAString.h"
21 : #include "nsCaret.h" // for nsCaret
22 : #include "nsDebug.h" // for NS_ENSURE_TRUE, etc.
23 : #include "nsFocusManager.h" // for nsFocusManager
24 : #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::input
25 : #include "nsIClipboard.h" // for nsIClipboard, etc.
26 : #include "nsIContent.h" // for nsIContent
27 : #include "nsIController.h" // for nsIController
28 : #include "nsID.h"
29 : #include "mozilla/dom/DOMStringList.h"
30 : #include "mozilla/dom/DataTransfer.h"
31 : #include "nsIDOMDocument.h" // for nsIDOMDocument
32 : #include "nsIDOMDragEvent.h" // for nsIDOMDragEvent
33 : #include "nsIDOMElement.h" // for nsIDOMElement
34 : #include "nsIDOMEvent.h" // for nsIDOMEvent
35 : #include "nsIDOMEventTarget.h" // for nsIDOMEventTarget
36 : #include "nsIDOMMouseEvent.h" // for nsIDOMMouseEvent
37 : #include "nsIDOMNode.h" // for nsIDOMNode
38 : #include "nsIDocument.h" // for nsIDocument
39 : #include "nsIEditor.h" // for EditorBase::GetSelection, etc.
40 : #include "nsIEditorMailSupport.h" // for nsIEditorMailSupport
41 : #include "nsIFocusManager.h" // for nsIFocusManager
42 : #include "nsIFormControl.h" // for nsIFormControl, etc.
43 : #include "nsIHTMLEditor.h" // for nsIHTMLEditor
44 : #include "nsINode.h" // for nsINode, ::NODE_IS_EDITABLE, etc.
45 : #include "nsIPlaintextEditor.h" // for nsIPlaintextEditor, etc.
46 : #include "nsIPresShell.h" // for nsIPresShell
47 : #include "nsISelectionController.h" // for nsISelectionController, etc.
48 : #include "nsITransferable.h" // for kFileMime, kHTMLMime, etc.
49 : #include "nsIWidget.h" // for nsIWidget
50 : #include "nsLiteralString.h" // for NS_LITERAL_STRING
51 : #include "nsPIWindowRoot.h" // for nsPIWindowRoot
52 : #include "nsPrintfCString.h" // for nsPrintfCString
53 : #include "nsRange.h"
54 : #include "nsServiceManagerUtils.h" // for do_GetService
55 : #include "nsString.h" // for nsAutoString
56 : #include "nsQueryObject.h" // for do_QueryObject
57 : #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
58 : #include "nsContentUtils.h" // for nsContentUtils, etc.
59 : #include "nsIBidiKeyboard.h" // for nsIBidiKeyboard
60 : #endif
61 :
62 : #include "mozilla/dom/TabParent.h"
63 :
64 : class nsPresContext;
65 :
66 : namespace mozilla {
67 :
68 : using namespace dom;
69 :
70 : static void
71 0 : DoCommandCallback(Command aCommand, void* aData)
72 : {
73 0 : nsIDocument* doc = static_cast<nsIDocument*>(aData);
74 0 : nsPIDOMWindowOuter* win = doc->GetWindow();
75 0 : if (!win) {
76 0 : return;
77 : }
78 0 : nsCOMPtr<nsPIWindowRoot> root = win->GetTopWindowRoot();
79 0 : if (!root) {
80 0 : return;
81 : }
82 :
83 0 : const char* commandStr = WidgetKeyboardEvent::GetCommandStr(aCommand);
84 :
85 0 : nsCOMPtr<nsIController> controller;
86 0 : root->GetControllerForCommand(commandStr, getter_AddRefs(controller));
87 0 : if (!controller) {
88 0 : return;
89 : }
90 :
91 : bool commandEnabled;
92 0 : nsresult rv = controller->IsCommandEnabled(commandStr, &commandEnabled);
93 0 : NS_ENSURE_SUCCESS_VOID(rv);
94 0 : if (commandEnabled) {
95 0 : controller->DoCommand(commandStr);
96 : }
97 : }
98 :
99 1 : EditorEventListener::EditorEventListener()
100 : : mEditorBase(nullptr)
101 : , mCommitText(false)
102 : , mInTransaction(false)
103 1 : , mMouseDownOrUpConsumedByIME(false)
104 : #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
105 : , mHaveBidiKeyboards(false)
106 : , mShouldSwitchTextDirection(false)
107 : , mSwitchToRTL(false)
108 : #endif
109 : {
110 1 : }
111 :
112 0 : EditorEventListener::~EditorEventListener()
113 : {
114 0 : if (mEditorBase) {
115 0 : NS_WARNING("We're not uninstalled");
116 0 : Disconnect();
117 : }
118 0 : }
119 :
120 : nsresult
121 2 : EditorEventListener::Connect(EditorBase* aEditorBase)
122 : {
123 2 : NS_ENSURE_ARG(aEditorBase);
124 :
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;
131 : }
132 : #endif
133 :
134 2 : mEditorBase = aEditorBase;
135 :
136 2 : nsresult rv = InstallToEditor();
137 2 : if (NS_FAILED(rv)) {
138 0 : Disconnect();
139 : }
140 2 : return rv;
141 : }
142 :
143 : nsresult
144 2 : EditorEventListener::InstallToEditor()
145 : {
146 2 : NS_PRECONDITION(mEditorBase, "The caller must set mEditorBase");
147 :
148 4 : nsCOMPtr<EventTarget> piTarget = mEditorBase->GetDOMEventTarget();
149 2 : NS_ENSURE_TRUE(piTarget, NS_ERROR_FAILURE);
150 :
151 : // register the event listeners with the listener manager
152 2 : EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
153 2 : NS_ENSURE_STATE(elmP);
154 :
155 : #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
156 : elmP->AddEventListenerByType(this,
157 : NS_LITERAL_STRING("keydown"),
158 : TrustedEventsAtSystemGroupBubble());
159 : elmP->AddEventListenerByType(this,
160 : NS_LITERAL_STRING("keyup"),
161 : TrustedEventsAtSystemGroupBubble());
162 : #endif
163 2 : elmP->AddEventListenerByType(this,
164 4 : NS_LITERAL_STRING("keypress"),
165 8 : TrustedEventsAtSystemGroupBubble());
166 2 : elmP->AddEventListenerByType(this,
167 4 : NS_LITERAL_STRING("dragenter"),
168 8 : TrustedEventsAtSystemGroupBubble());
169 2 : elmP->AddEventListenerByType(this,
170 4 : NS_LITERAL_STRING("dragover"),
171 8 : TrustedEventsAtSystemGroupBubble());
172 2 : elmP->AddEventListenerByType(this,
173 4 : NS_LITERAL_STRING("dragexit"),
174 8 : TrustedEventsAtSystemGroupBubble());
175 2 : elmP->AddEventListenerByType(this,
176 4 : NS_LITERAL_STRING("drop"),
177 8 : TrustedEventsAtSystemGroupBubble());
178 : // XXX We should add the mouse event listeners as system event group.
179 : // E.g., web applications cannot prevent middle mouse paste by
180 : // preventDefault() of click event at bubble phase.
181 : // However, if we do so, all click handlers in any frames and frontend
182 : // code need to check if it's editable. It makes easier create new bugs.
183 2 : elmP->AddEventListenerByType(this,
184 4 : NS_LITERAL_STRING("mousedown"),
185 8 : TrustedEventsAtCapture());
186 2 : elmP->AddEventListenerByType(this,
187 4 : NS_LITERAL_STRING("mouseup"),
188 8 : TrustedEventsAtCapture());
189 2 : elmP->AddEventListenerByType(this,
190 4 : NS_LITERAL_STRING("click"),
191 8 : TrustedEventsAtCapture());
192 : // Focus event doesn't bubble so adding the listener to capturing phase.
193 : // XXX Should we listen focus/blur events of system group too? Or should
194 : // editor notified focus/blur of the element from nsFocusManager
195 : // directly? Because if the event propagation is stopped by JS,
196 : // editor cannot initialize selection as expected.
197 2 : elmP->AddEventListenerByType(this,
198 4 : NS_LITERAL_STRING("blur"),
199 8 : TrustedEventsAtCapture());
200 2 : elmP->AddEventListenerByType(this,
201 4 : NS_LITERAL_STRING("focus"),
202 8 : TrustedEventsAtCapture());
203 2 : elmP->AddEventListenerByType(this,
204 4 : NS_LITERAL_STRING("text"),
205 8 : TrustedEventsAtSystemGroupBubble());
206 2 : elmP->AddEventListenerByType(this,
207 4 : NS_LITERAL_STRING("compositionstart"),
208 8 : TrustedEventsAtSystemGroupBubble());
209 2 : elmP->AddEventListenerByType(this,
210 4 : NS_LITERAL_STRING("compositionend"),
211 8 : TrustedEventsAtSystemGroupBubble());
212 :
213 2 : return NS_OK;
214 : }
215 :
216 : void
217 1 : EditorEventListener::Disconnect()
218 : {
219 1 : if (DetachedFromEditor()) {
220 0 : return;
221 : }
222 1 : UninstallFromEditor();
223 :
224 1 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
225 1 : if (fm) {
226 2 : nsCOMPtr<nsIDOMElement> domFocus;
227 1 : fm->GetFocusedElement(getter_AddRefs(domFocus));
228 2 : nsCOMPtr<nsINode> focusedElement = do_QueryInterface(domFocus);
229 1 : mozilla::dom::Element* root = mEditorBase->GetRoot();
230 1 : if (focusedElement && root &&
231 0 : nsContentUtils::ContentIsDescendantOf(focusedElement, root)) {
232 : // Reset the Selection ancestor limiter and SelectionController state
233 : // that EditorBase::InitializeSelection set up.
234 0 : mEditorBase->FinalizeSelection();
235 : }
236 : }
237 :
238 1 : mEditorBase = nullptr;
239 : }
240 :
241 : void
242 1 : EditorEventListener::UninstallFromEditor()
243 : {
244 2 : nsCOMPtr<EventTarget> piTarget = mEditorBase->GetDOMEventTarget();
245 1 : if (!piTarget) {
246 0 : return;
247 : }
248 :
249 1 : EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
250 1 : if (!elmP) {
251 0 : return;
252 : }
253 :
254 : #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
255 : elmP->RemoveEventListenerByType(this,
256 : NS_LITERAL_STRING("keydown"),
257 : TrustedEventsAtSystemGroupBubble());
258 : elmP->RemoveEventListenerByType(this,
259 : NS_LITERAL_STRING("keyup"),
260 : TrustedEventsAtSystemGroupBubble());
261 : #endif
262 1 : elmP->RemoveEventListenerByType(this,
263 2 : NS_LITERAL_STRING("keypress"),
264 4 : TrustedEventsAtSystemGroupBubble());
265 1 : elmP->RemoveEventListenerByType(this,
266 2 : NS_LITERAL_STRING("dragenter"),
267 4 : TrustedEventsAtSystemGroupBubble());
268 1 : elmP->RemoveEventListenerByType(this,
269 2 : NS_LITERAL_STRING("dragover"),
270 4 : TrustedEventsAtSystemGroupBubble());
271 1 : elmP->RemoveEventListenerByType(this,
272 2 : NS_LITERAL_STRING("dragexit"),
273 4 : TrustedEventsAtSystemGroupBubble());
274 1 : elmP->RemoveEventListenerByType(this,
275 2 : NS_LITERAL_STRING("drop"),
276 4 : TrustedEventsAtSystemGroupBubble());
277 1 : elmP->RemoveEventListenerByType(this,
278 2 : NS_LITERAL_STRING("mousedown"),
279 4 : TrustedEventsAtCapture());
280 1 : elmP->RemoveEventListenerByType(this,
281 2 : NS_LITERAL_STRING("mouseup"),
282 4 : TrustedEventsAtCapture());
283 1 : elmP->RemoveEventListenerByType(this,
284 2 : NS_LITERAL_STRING("click"),
285 4 : TrustedEventsAtCapture());
286 1 : elmP->RemoveEventListenerByType(this,
287 2 : NS_LITERAL_STRING("blur"),
288 4 : TrustedEventsAtCapture());
289 1 : elmP->RemoveEventListenerByType(this,
290 2 : NS_LITERAL_STRING("focus"),
291 4 : TrustedEventsAtCapture());
292 1 : elmP->RemoveEventListenerByType(this,
293 2 : NS_LITERAL_STRING("text"),
294 4 : TrustedEventsAtSystemGroupBubble());
295 1 : elmP->RemoveEventListenerByType(this,
296 2 : NS_LITERAL_STRING("compositionstart"),
297 4 : TrustedEventsAtSystemGroupBubble());
298 1 : elmP->RemoveEventListenerByType(this,
299 2 : NS_LITERAL_STRING("compositionend"),
300 4 : TrustedEventsAtSystemGroupBubble());
301 : }
302 :
303 : already_AddRefed<nsIPresShell>
304 0 : EditorEventListener::GetPresShell()
305 : {
306 0 : MOZ_ASSERT(!DetachedFromEditor());
307 0 : return mEditorBase->GetPresShell();
308 : }
309 :
310 : nsPresContext*
311 0 : EditorEventListener::GetPresContext()
312 : {
313 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
314 0 : return presShell ? presShell->GetPresContext() : nullptr;
315 : }
316 :
317 : nsIContent*
318 0 : EditorEventListener::GetFocusedRootContent()
319 : {
320 0 : MOZ_ASSERT(!DetachedFromEditor());
321 0 : nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
322 0 : if (!focusedContent) {
323 0 : return nullptr;
324 : }
325 :
326 0 : nsIDocument* composedDoc = focusedContent->GetComposedDoc();
327 0 : NS_ENSURE_TRUE(composedDoc, nullptr);
328 :
329 0 : if (composedDoc->HasFlag(NODE_IS_EDITABLE)) {
330 0 : return nullptr;
331 : }
332 :
333 0 : return focusedContent;
334 : }
335 :
336 : bool
337 0 : EditorEventListener::EditorHasFocus()
338 : {
339 0 : MOZ_ASSERT(!DetachedFromEditor());
340 0 : nsCOMPtr<nsIContent> focusedContent = mEditorBase->GetFocusedContent();
341 0 : if (!focusedContent) {
342 0 : return false;
343 : }
344 0 : nsIDocument* composedDoc = focusedContent->GetComposedDoc();
345 0 : return !!composedDoc;
346 : }
347 :
348 95 : NS_IMPL_ISUPPORTS(EditorEventListener, nsIDOMEventListener)
349 :
350 : bool
351 1 : EditorEventListener::DetachedFromEditor() const
352 : {
353 1 : return !mEditorBase;
354 : }
355 :
356 : bool
357 0 : EditorEventListener::DetachedFromEditorOrDefaultPrevented(
358 : WidgetEvent* aWidgetEvent) const
359 : {
360 0 : return NS_WARN_IF(!aWidgetEvent) || DetachedFromEditor() ||
361 0 : aWidgetEvent->DefaultPrevented();
362 : }
363 :
364 : bool
365 0 : EditorEventListener::EnsureCommitCompoisition()
366 : {
367 0 : MOZ_ASSERT(!DetachedFromEditor());
368 0 : RefPtr<EditorBase> editorBase(mEditorBase);
369 0 : editorBase->ForceCompositionEnd();
370 0 : return !DetachedFromEditor();
371 : }
372 :
373 : NS_IMETHODIMP
374 0 : EditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
375 : {
376 : // Let's handle each event with the message of the internal event of the
377 : // coming event. If the DOM event was created with improper interface,
378 : // e.g., keydown event is created with |new MouseEvent("keydown", {});|,
379 : // its message is always 0. Therefore, we can ban such strange event easy.
380 : // However, we need to handle strange "focus" and "blur" event. See the
381 : // following code of this switch statement.
382 : // NOTE: Each event handler may require specific event interface. Before
383 : // calling it, this queries the specific interface. If it would fail,
384 : // each event handler would just ignore the event. So, in this method,
385 : // you don't need to check if the QI succeeded before each call.
386 0 : WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
387 0 : switch (internalEvent->mMessage) {
388 : // dragenter
389 : case eDragEnter: {
390 0 : nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
391 0 : return DragEnter(dragEvent);
392 : }
393 : // dragover
394 : case eDragOver: {
395 0 : nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
396 0 : return DragOver(dragEvent);
397 : }
398 : // dragexit
399 : case eDragExit: {
400 0 : nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
401 0 : return DragExit(dragEvent);
402 : }
403 : // drop
404 : case eDrop: {
405 0 : nsCOMPtr<nsIDOMDragEvent> dragEvent = do_QueryInterface(aEvent);
406 0 : return Drop(dragEvent);
407 : }
408 : #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
409 : // keydown
410 : case eKeyDown: {
411 : return KeyDown(internalEvent->AsKeyboardEvent());
412 : }
413 : // keyup
414 : case eKeyUp:
415 : return KeyUp(internalEvent->AsKeyboardEvent());
416 : #endif // #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
417 : // keypress
418 : case eKeyPress:
419 0 : return KeyPress(internalEvent->AsKeyboardEvent());
420 : // mousedown
421 : case eMouseDown: {
422 : // EditorEventListener may receive (1) all mousedown, mouseup and click
423 : // events, (2) only mousedown event or (3) only mouseup event.
424 : // mMouseDownOrUpConsumedByIME is used only for ignoring click event if
425 : // preceding mousedown and/or mouseup event is consumed by IME.
426 : // Therefore, even if case #2 or case #3 occurs,
427 : // mMouseDownOrUpConsumedByIME is true here. Therefore, we should always
428 : // overwrite it here.
429 0 : mMouseDownOrUpConsumedByIME =
430 0 : NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent());
431 0 : if (mMouseDownOrUpConsumedByIME) {
432 0 : return NS_OK;
433 : }
434 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
435 0 : return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseDown(mouseEvent);
436 : }
437 : // mouseup
438 : case eMouseUp: {
439 : // See above comment in the eMouseDown case, first.
440 : // This code assumes that case #1 is occuring. However, if case #3 may
441 : // occurs after case #2 and the mousedown is consumed,
442 : // mMouseDownOrUpConsumedByIME is true even though EditorEventListener
443 : // has not received the preceding mousedown event of this mouseup event.
444 : // So, mMouseDownOrUpConsumedByIME may be invalid here. However,
445 : // this is not a matter because mMouseDownOrUpConsumedByIME is referred
446 : // only by eMouseClick case but click event is fired only in case #1.
447 : // So, before a click event is fired, mMouseDownOrUpConsumedByIME is
448 : // always initialized in the eMouseDown case if it's referred.
449 0 : if (NotifyIMEOfMouseButtonEvent(internalEvent->AsMouseEvent())) {
450 0 : mMouseDownOrUpConsumedByIME = true;
451 : }
452 0 : if (mMouseDownOrUpConsumedByIME) {
453 0 : return NS_OK;
454 : }
455 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
456 0 : return NS_WARN_IF(!mouseEvent) ? NS_OK : MouseUp(mouseEvent);
457 : }
458 : // click
459 : case eMouseClick: {
460 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
461 0 : NS_ENSURE_TRUE(mouseEvent, NS_OK);
462 : // If the preceding mousedown event or mouseup event was consumed,
463 : // editor shouldn't handle this click event.
464 0 : if (mMouseDownOrUpConsumedByIME) {
465 0 : mMouseDownOrUpConsumedByIME = false;
466 0 : mouseEvent->AsEvent()->PreventDefault();
467 0 : return NS_OK;
468 : }
469 0 : return MouseClick(mouseEvent);
470 : }
471 : // focus
472 : case eFocus:
473 0 : return Focus(internalEvent->AsFocusEvent());
474 : // blur
475 : case eBlur:
476 0 : return Blur(internalEvent->AsFocusEvent());
477 : // text
478 : case eCompositionChange:
479 0 : return HandleChangeComposition(internalEvent->AsCompositionEvent());
480 : // compositionstart
481 : case eCompositionStart:
482 0 : return HandleStartComposition(internalEvent->AsCompositionEvent());
483 : // compositionend
484 : case eCompositionEnd:
485 0 : HandleEndComposition(internalEvent->AsCompositionEvent());
486 0 : return NS_OK;
487 : default:
488 0 : break;
489 : }
490 :
491 : #ifdef DEBUG
492 0 : nsAutoString eventType;
493 0 : aEvent->GetType(eventType);
494 : nsPrintfCString assertMessage("Editor doesn't handle \"%s\" event "
495 : "because its internal event doesn't have proper message",
496 0 : NS_ConvertUTF16toUTF8(eventType).get());
497 0 : NS_ASSERTION(false, assertMessage.get());
498 : #endif
499 :
500 0 : return NS_OK;
501 : }
502 :
503 : #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
504 :
505 : // This function is borrowed from Chromium's ImeInput::IsCtrlShiftPressed
506 : bool IsCtrlShiftPressed(const WidgetKeyboardEvent* aKeyboardEvent, bool& isRTL)
507 : {
508 : MOZ_ASSERT(aKeyboardEvent);
509 : // To check if a user is pressing only a control key and a right-shift key
510 : // (or a left-shift key), we use the steps below:
511 : // 1. Check if a user is pressing a control key and a right-shift key (or
512 : // a left-shift key).
513 : // 2. If the condition 1 is true, we should check if there are any other
514 : // keys pressed at the same time.
515 : // To ignore the keys checked in 1, we set their status to 0 before
516 : // checking the key status.
517 :
518 : if (!aKeyboardEvent->IsControl()) {
519 : return false;
520 : }
521 :
522 : switch (aKeyboardEvent->mLocation) {
523 : case eKeyLocationRight:
524 : isRTL = true;
525 : break;
526 : case eKeyLocationLeft:
527 : isRTL = false;
528 : break;
529 : default:
530 : return false;
531 : }
532 :
533 : // Scan the key status to find pressed keys. We should abandon changing the
534 : // text direction when there are other pressed keys.
535 : if (aKeyboardEvent->IsAlt() || aKeyboardEvent->IsOS()) {
536 : return false;
537 : }
538 :
539 : return true;
540 : }
541 :
542 : // This logic is mostly borrowed from Chromium's
543 : // RenderWidgetHostViewWin::OnKeyEvent.
544 :
545 : nsresult
546 : EditorEventListener::KeyUp(const WidgetKeyboardEvent* aKeyboardEvent)
547 : {
548 : if (NS_WARN_IF(!aKeyboardEvent) || DetachedFromEditor()) {
549 : return NS_OK;
550 : }
551 :
552 : if (!mHaveBidiKeyboards) {
553 : return NS_OK;
554 : }
555 :
556 : // XXX Why doesn't this method check if it's consumed?
557 : RefPtr<EditorBase> editorBase(mEditorBase);
558 : if ((aKeyboardEvent->mKeyCode == NS_VK_SHIFT ||
559 : aKeyboardEvent->mKeyCode == NS_VK_CONTROL) &&
560 : mShouldSwitchTextDirection && editorBase->IsPlaintextEditor()) {
561 : editorBase->SwitchTextDirectionTo(mSwitchToRTL ?
562 : nsIPlaintextEditor::eEditorRightToLeft :
563 : nsIPlaintextEditor::eEditorLeftToRight);
564 : mShouldSwitchTextDirection = false;
565 : }
566 : return NS_OK;
567 : }
568 :
569 : nsresult
570 : EditorEventListener::KeyDown(const WidgetKeyboardEvent* aKeyboardEvent)
571 : {
572 : if (NS_WARN_IF(!aKeyboardEvent) || DetachedFromEditor()) {
573 : return NS_OK;
574 : }
575 :
576 : if (!mHaveBidiKeyboards) {
577 : return NS_OK;
578 : }
579 :
580 : // XXX Why isn't this method check if it's consumed?
581 : if (aKeyboardEvent->mKeyCode == NS_VK_SHIFT) {
582 : bool switchToRTL;
583 : if (IsCtrlShiftPressed(aKeyboardEvent, switchToRTL)) {
584 : mShouldSwitchTextDirection = true;
585 : mSwitchToRTL = switchToRTL;
586 : }
587 : } else if (aKeyboardEvent->mKeyCode != NS_VK_CONTROL) {
588 : // In case the user presses any other key besides Ctrl and Shift
589 : mShouldSwitchTextDirection = false;
590 : }
591 : return NS_OK;
592 : }
593 :
594 : #endif // #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
595 :
596 : nsresult
597 0 : EditorEventListener::KeyPress(WidgetKeyboardEvent* aKeyboardEvent)
598 : {
599 0 : if (NS_WARN_IF(!aKeyboardEvent)) {
600 0 : return NS_OK;
601 : }
602 :
603 0 : RefPtr<EditorBase> editorBase(mEditorBase);
604 0 : if (!editorBase->IsAcceptableInputEvent(aKeyboardEvent) ||
605 0 : DetachedFromEditorOrDefaultPrevented(aKeyboardEvent)) {
606 0 : return NS_OK;
607 : }
608 :
609 0 : nsresult rv = editorBase->HandleKeyPressEvent(aKeyboardEvent);
610 0 : NS_ENSURE_SUCCESS(rv, rv);
611 0 : if (DetachedFromEditorOrDefaultPrevented(aKeyboardEvent)) {
612 0 : return NS_OK;
613 : }
614 :
615 0 : if (!ShouldHandleNativeKeyBindings(aKeyboardEvent)) {
616 0 : return NS_OK;
617 : }
618 :
619 : // Now, ask the native key bindings to handle the event.
620 0 : nsIWidget* widget = aKeyboardEvent->mWidget;
621 : // If the event is created by chrome script, the widget is always nullptr.
622 0 : if (!widget) {
623 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
624 0 : nsPresContext* pc = ps ? ps->GetPresContext() : nullptr;
625 0 : widget = pc ? pc->GetNearestWidget() : nullptr;
626 0 : NS_ENSURE_TRUE(widget, NS_OK);
627 : }
628 :
629 0 : nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
630 :
631 : // WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
632 : // If the event is created by chrome script, it is nullptr but we need to
633 : // execute native key bindings. Therefore, we need to set widget to
634 : // WidgetEvent::mWidget temporarily.
635 0 : AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(aKeyboardEvent->mWidget);
636 0 : aKeyboardEvent->mWidget = widget;
637 0 : if (aKeyboardEvent->ExecuteEditCommands(
638 : nsIWidget::NativeKeyBindingsForRichTextEditor,
639 0 : DoCommandCallback, doc)) {
640 0 : aKeyboardEvent->PreventDefault();
641 : }
642 0 : return NS_OK;
643 : }
644 :
645 : nsresult
646 0 : EditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
647 : {
648 0 : if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
649 0 : return NS_OK;
650 : }
651 : // nothing to do if editor isn't editable or clicked on out of the editor.
652 0 : RefPtr<EditorBase> editorBase(mEditorBase);
653 : WidgetMouseEvent* clickEvent =
654 0 : aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
655 0 : if (editorBase->IsReadonly() || editorBase->IsDisabled() ||
656 0 : !editorBase->IsAcceptableInputEvent(clickEvent)) {
657 0 : return NS_OK;
658 : }
659 :
660 : // Notifies clicking on editor to IMEStateManager even when the event was
661 : // consumed.
662 0 : if (EditorHasFocus()) {
663 0 : nsPresContext* presContext = GetPresContext();
664 0 : if (presContext) {
665 0 : IMEStateManager::OnClickInEditor(presContext, GetFocusedRootContent(),
666 0 : clickEvent);
667 0 : if (DetachedFromEditor()) {
668 0 : return NS_OK;
669 : }
670 : }
671 : }
672 :
673 0 : if (DetachedFromEditorOrDefaultPrevented(clickEvent)) {
674 : // We're done if 'preventdefault' is true (see for example bug 70698).
675 0 : return NS_OK;
676 : }
677 :
678 : // If we got a mouse down inside the editing area, we should force the
679 : // IME to commit before we change the cursor position
680 0 : if (!EnsureCommitCompoisition()) {
681 0 : return NS_OK;
682 : }
683 :
684 0 : if (clickEvent->button == 1) {
685 0 : return HandleMiddleClickPaste(aMouseEvent);
686 : }
687 0 : return NS_OK;
688 : }
689 :
690 : nsresult
691 0 : EditorEventListener::HandleMiddleClickPaste(nsIDOMMouseEvent* aMouseEvent)
692 : {
693 0 : MOZ_ASSERT(aMouseEvent);
694 :
695 : WidgetMouseEvent* clickEvent =
696 0 : aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
697 0 : MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(clickEvent));
698 :
699 0 : if (!Preferences::GetBool("middlemouse.paste", false)) {
700 : // Middle click paste isn't enabled.
701 0 : return NS_OK;
702 : }
703 :
704 : // Set the selection to the point under the mouse cursor:
705 0 : nsCOMPtr<nsIDOMNode> parent;
706 0 : if (NS_FAILED(aMouseEvent->GetRangeParent(getter_AddRefs(parent)))) {
707 0 : return NS_ERROR_NULL_POINTER;
708 : }
709 0 : int32_t offset = 0;
710 0 : if (NS_FAILED(aMouseEvent->GetRangeOffset(&offset))) {
711 0 : return NS_ERROR_NULL_POINTER;
712 : }
713 :
714 0 : RefPtr<EditorBase> editorBase(mEditorBase);
715 0 : RefPtr<Selection> selection = editorBase->GetSelection();
716 0 : if (selection) {
717 0 : selection->Collapse(parent, offset);
718 : }
719 :
720 : // If the ctrl key is pressed, we'll do paste as quotation.
721 : // Would've used the alt key, but the kde wmgr treats alt-middle specially.
722 0 : nsCOMPtr<nsIEditorMailSupport> mailEditor;
723 0 : if (clickEvent->IsControl()) {
724 0 : mailEditor = do_QueryObject(editorBase);
725 : }
726 :
727 : nsresult rv;
728 0 : int32_t clipboard = nsIClipboard::kGlobalClipboard;
729 : nsCOMPtr<nsIClipboard> clipboardService =
730 0 : do_GetService("@mozilla.org/widget/clipboard;1", &rv);
731 0 : if (NS_SUCCEEDED(rv)) {
732 : bool selectionSupported;
733 0 : rv = clipboardService->SupportsSelectionClipboard(&selectionSupported);
734 0 : if (NS_SUCCEEDED(rv) && selectionSupported) {
735 0 : clipboard = nsIClipboard::kSelectionClipboard;
736 : }
737 : }
738 :
739 0 : if (mailEditor) {
740 0 : mailEditor->PasteAsQuotation(clipboard);
741 : } else {
742 0 : editorBase->Paste(clipboard);
743 : }
744 :
745 : // Prevent the event from propagating up to be possibly handled
746 : // again by the containing window:
747 0 : clickEvent->StopPropagation();
748 0 : clickEvent->PreventDefault();
749 :
750 : // We processed the event, whether drop/paste succeeded or not
751 0 : return NS_OK;
752 : }
753 :
754 : bool
755 0 : EditorEventListener::NotifyIMEOfMouseButtonEvent(
756 : WidgetMouseEvent* aMouseEvent)
757 : {
758 0 : MOZ_ASSERT(aMouseEvent);
759 :
760 0 : if (!EditorHasFocus()) {
761 0 : return false;
762 : }
763 :
764 0 : nsPresContext* presContext = GetPresContext();
765 0 : NS_ENSURE_TRUE(presContext, false);
766 0 : return IMEStateManager::OnMouseButtonEventInEditor(presContext,
767 : GetFocusedRootContent(),
768 0 : aMouseEvent);
769 : }
770 :
771 : nsresult
772 0 : EditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
773 : {
774 : // FYI: We don't need to check if it's already consumed here because
775 : // we need to commit composition at mouse button operation.
776 : // FYI: This may be called by HTMLEditorEventListener::MouseDown() even
777 : // when the event is not acceptable for committing composition.
778 0 : if (DetachedFromEditor()) {
779 0 : return NS_OK;
780 : }
781 0 : Unused << EnsureCommitCompoisition();
782 0 : return NS_OK;
783 : }
784 :
785 : nsresult
786 0 : EditorEventListener::HandleChangeComposition(
787 : WidgetCompositionEvent* aCompositionChangeEvent)
788 : {
789 0 : MOZ_ASSERT(!aCompositionChangeEvent->DefaultPrevented(),
790 : "eCompositionChange event shouldn't be cancelable");
791 0 : RefPtr<EditorBase> editorBase(mEditorBase);
792 0 : if (DetachedFromEditor() ||
793 0 : !editorBase->IsAcceptableInputEvent(aCompositionChangeEvent)) {
794 0 : return NS_OK;
795 : }
796 :
797 : // if we are readonly or disabled, then do nothing.
798 0 : if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
799 0 : return NS_OK;
800 : }
801 :
802 0 : return editorBase->UpdateIMEComposition(aCompositionChangeEvent);
803 : }
804 :
805 : /**
806 : * Drag event implementation
807 : */
808 :
809 : nsresult
810 0 : EditorEventListener::DragEnter(nsIDOMDragEvent* aDragEvent)
811 : {
812 0 : if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
813 0 : return NS_OK;
814 : }
815 :
816 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
817 0 : NS_ENSURE_TRUE(presShell, NS_OK);
818 :
819 0 : if (!mCaret) {
820 0 : mCaret = new nsCaret();
821 0 : mCaret->Init(presShell);
822 0 : mCaret->SetCaretReadOnly(true);
823 : // This is to avoid the requirement that the Selection is Collapsed which
824 : // it can't be when dragging a selection in the same shell.
825 : // See nsCaret::IsVisible().
826 0 : mCaret->SetVisibilityDuringSelection(true);
827 : }
828 :
829 0 : presShell->SetCaret(mCaret);
830 :
831 0 : return DragOver(aDragEvent);
832 : }
833 :
834 : nsresult
835 0 : EditorEventListener::DragOver(nsIDOMDragEvent* aDragEvent)
836 : {
837 0 : if (NS_WARN_IF(!aDragEvent) ||
838 0 : DetachedFromEditorOrDefaultPrevented(
839 0 : aDragEvent->AsEvent()->WidgetEventPtr())) {
840 0 : return NS_OK;
841 : }
842 :
843 0 : nsCOMPtr<nsIDOMNode> parent;
844 0 : aDragEvent->GetRangeParent(getter_AddRefs(parent));
845 0 : nsCOMPtr<nsIContent> dropParent = do_QueryInterface(parent);
846 0 : NS_ENSURE_TRUE(dropParent, NS_ERROR_FAILURE);
847 :
848 0 : if (dropParent->IsEditable() && CanDrop(aDragEvent)) {
849 0 : aDragEvent->AsEvent()->PreventDefault(); // consumed
850 :
851 0 : if (!mCaret) {
852 0 : return NS_OK;
853 : }
854 :
855 0 : int32_t offset = 0;
856 0 : nsresult rv = aDragEvent->GetRangeOffset(&offset);
857 0 : NS_ENSURE_SUCCESS(rv, rv);
858 :
859 0 : mCaret->SetVisible(true);
860 0 : mCaret->SetCaretPosition(parent, offset);
861 :
862 0 : return NS_OK;
863 : }
864 :
865 0 : if (!IsFileControlTextBox()) {
866 : // This is needed when dropping on an input, to prevent the editor for
867 : // the editable parent from receiving the event.
868 0 : aDragEvent->AsEvent()->StopPropagation();
869 : }
870 :
871 0 : if (mCaret) {
872 0 : mCaret->SetVisible(false);
873 : }
874 0 : return NS_OK;
875 : }
876 :
877 : void
878 0 : EditorEventListener::CleanupDragDropCaret()
879 : {
880 0 : if (!mCaret) {
881 0 : return;
882 : }
883 :
884 0 : mCaret->SetVisible(false); // hide it, so that it turns off its timer
885 :
886 0 : nsCOMPtr<nsIPresShell> presShell = GetPresShell();
887 0 : if (presShell) {
888 0 : presShell->RestoreCaret();
889 : }
890 :
891 0 : mCaret->Terminate();
892 0 : mCaret = nullptr;
893 : }
894 :
895 : nsresult
896 0 : EditorEventListener::DragExit(nsIDOMDragEvent* aDragEvent)
897 : {
898 : // XXX If aDragEvent was created by chrome script, its defaultPrevented
899 : // may be true, though. We shouldn't handle such event but we don't
900 : // have a way to distinguish if coming event is created by chrome script.
901 0 : NS_WARNING_ASSERTION(
902 : !aDragEvent->AsEvent()->WidgetEventPtr()->DefaultPrevented(),
903 : "eDragExit shouldn't be cancelable");
904 0 : if (NS_WARN_IF(!aDragEvent) || DetachedFromEditor()) {
905 0 : return NS_OK;
906 : }
907 :
908 0 : CleanupDragDropCaret();
909 :
910 0 : return NS_OK;
911 : }
912 :
913 : nsresult
914 0 : EditorEventListener::Drop(nsIDOMDragEvent* aDragEvent)
915 : {
916 0 : CleanupDragDropCaret();
917 :
918 0 : if (NS_WARN_IF(!aDragEvent) ||
919 0 : DetachedFromEditorOrDefaultPrevented(
920 0 : aDragEvent->AsEvent()->WidgetEventPtr())) {
921 0 : return NS_OK;
922 : }
923 :
924 0 : nsCOMPtr<nsIDOMNode> parent;
925 0 : aDragEvent->GetRangeParent(getter_AddRefs(parent));
926 0 : nsCOMPtr<nsIContent> dropParent = do_QueryInterface(parent);
927 0 : NS_ENSURE_TRUE(dropParent, NS_ERROR_FAILURE);
928 :
929 0 : if (!dropParent->IsEditable() || !CanDrop(aDragEvent)) {
930 : // was it because we're read-only?
931 0 : RefPtr<EditorBase> editorBase(mEditorBase);
932 0 : if ((editorBase->IsReadonly() || editorBase->IsDisabled()) &&
933 0 : !IsFileControlTextBox()) {
934 : // it was decided to "eat" the event as this is the "least surprise"
935 : // since someone else handling it might be unintentional and the
936 : // user could probably re-drag to be not over the disabled/readonly
937 : // editfields if that is what is desired.
938 0 : return aDragEvent->AsEvent()->StopPropagation();
939 : }
940 0 : return NS_OK;
941 : }
942 :
943 0 : aDragEvent->AsEvent()->StopPropagation();
944 0 : aDragEvent->AsEvent()->PreventDefault();
945 0 : RefPtr<EditorBase> editorBase(mEditorBase);
946 0 : return editorBase->InsertFromDrop(aDragEvent->AsEvent());
947 : }
948 :
949 : bool
950 0 : EditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
951 : {
952 0 : MOZ_ASSERT(!DetachedFromEditorOrDefaultPrevented(
953 : aEvent->AsEvent()->WidgetEventPtr()));
954 :
955 : // if the target doc is read-only, we can't drop
956 0 : RefPtr<EditorBase> editorBase(mEditorBase);
957 0 : if (editorBase->IsReadonly() || editorBase->IsDisabled()) {
958 0 : return false;
959 : }
960 :
961 0 : nsCOMPtr<nsIDOMDataTransfer> domDataTransfer;
962 0 : aEvent->GetDataTransfer(getter_AddRefs(domDataTransfer));
963 0 : nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer);
964 0 : NS_ENSURE_TRUE(dataTransfer, false);
965 :
966 0 : nsTArray<nsString> types;
967 0 : dataTransfer->GetTypes(types, CallerType::System);
968 :
969 : // Plaintext editors only support dropping text. Otherwise, HTML and files
970 : // can be dropped as well.
971 0 : if (!types.Contains(NS_LITERAL_STRING(kTextMime)) &&
972 0 : !types.Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
973 0 : (editorBase->IsPlaintextEditor() ||
974 0 : (!types.Contains(NS_LITERAL_STRING(kHTMLMime)) &&
975 0 : !types.Contains(NS_LITERAL_STRING(kFileMime))))) {
976 0 : return false;
977 : }
978 :
979 : // If there is no source node, this is probably an external drag and the
980 : // drop is allowed. The later checks rely on checking if the drag target
981 : // is the same as the drag source.
982 0 : nsCOMPtr<nsIDOMNode> sourceNode;
983 0 : dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode));
984 0 : if (!sourceNode) {
985 0 : return true;
986 : }
987 :
988 : // There is a source node, so compare the source documents and this document.
989 : // Disallow drops on the same document.
990 :
991 0 : nsCOMPtr<nsIDOMDocument> domdoc = editorBase->GetDOMDocument();
992 0 : NS_ENSURE_TRUE(domdoc, false);
993 :
994 0 : nsCOMPtr<nsIDOMDocument> sourceDoc;
995 0 : nsresult rv = sourceNode->GetOwnerDocument(getter_AddRefs(sourceDoc));
996 0 : NS_ENSURE_SUCCESS(rv, false);
997 :
998 : // If the source and the dest are not same document, allow to drop it always.
999 0 : if (domdoc != sourceDoc) {
1000 0 : return true;
1001 : }
1002 :
1003 : // If the source node is a remote browser, treat this as coming from a
1004 : // different document and allow the drop.
1005 0 : nsCOMPtr<nsIContent> sourceContent = do_QueryInterface(sourceNode);
1006 0 : TabParent* tp = TabParent::GetFrom(sourceContent);
1007 0 : if (tp) {
1008 0 : return true;
1009 : }
1010 :
1011 0 : RefPtr<Selection> selection = editorBase->GetSelection();
1012 0 : if (!selection) {
1013 0 : return false;
1014 : }
1015 :
1016 : // If selection is collapsed, allow to drop it always.
1017 0 : if (selection->Collapsed()) {
1018 0 : return true;
1019 : }
1020 :
1021 0 : nsCOMPtr<nsIDOMNode> parent;
1022 0 : rv = aEvent->GetRangeParent(getter_AddRefs(parent));
1023 0 : if (NS_FAILED(rv) || !parent) {
1024 0 : return false;
1025 : }
1026 :
1027 0 : int32_t offset = 0;
1028 0 : rv = aEvent->GetRangeOffset(&offset);
1029 0 : NS_ENSURE_SUCCESS(rv, false);
1030 :
1031 : int32_t rangeCount;
1032 0 : rv = selection->GetRangeCount(&rangeCount);
1033 0 : NS_ENSURE_SUCCESS(rv, false);
1034 :
1035 0 : for (int32_t i = 0; i < rangeCount; i++) {
1036 0 : RefPtr<nsRange> range = selection->GetRangeAt(i);
1037 0 : if (!range) {
1038 : // Don't bail yet, iterate through them all
1039 0 : continue;
1040 : }
1041 :
1042 0 : bool inRange = true;
1043 0 : range->IsPointInRange(parent, offset, &inRange);
1044 0 : if (inRange) {
1045 : // Okay, now you can bail, we are over the orginal selection
1046 0 : return false;
1047 : }
1048 : }
1049 0 : return true;
1050 : }
1051 :
1052 : nsresult
1053 0 : EditorEventListener::HandleStartComposition(
1054 : WidgetCompositionEvent* aCompositionStartEvent)
1055 : {
1056 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1057 0 : if (DetachedFromEditor() ||
1058 0 : !editorBase->IsAcceptableInputEvent(aCompositionStartEvent)) {
1059 0 : return NS_OK;
1060 : }
1061 : // Although, "compositionstart" should be cancelable, but currently,
1062 : // eCompositionStart event coming from widget is not cancelable.
1063 0 : MOZ_ASSERT(!aCompositionStartEvent->DefaultPrevented(),
1064 : "eCompositionStart shouldn't be cancelable");
1065 0 : return editorBase->BeginIMEComposition(aCompositionStartEvent);
1066 : }
1067 :
1068 : void
1069 0 : EditorEventListener::HandleEndComposition(
1070 : WidgetCompositionEvent* aCompositionEndEvent)
1071 : {
1072 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1073 0 : if (DetachedFromEditor() ||
1074 0 : !editorBase->IsAcceptableInputEvent(aCompositionEndEvent)) {
1075 0 : return;
1076 : }
1077 0 : MOZ_ASSERT(!aCompositionEndEvent->DefaultPrevented(),
1078 : "eCompositionEnd shouldn't be cancelable");
1079 0 : editorBase->EndIMEComposition();
1080 : }
1081 :
1082 : nsresult
1083 0 : EditorEventListener::Focus(InternalFocusEvent* aFocusEvent)
1084 : {
1085 0 : if (NS_WARN_IF(!aFocusEvent) || DetachedFromEditor()) {
1086 0 : return NS_OK;
1087 : }
1088 :
1089 : // Don't turn on selection and caret when the editor is disabled.
1090 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1091 0 : if (editorBase->IsDisabled()) {
1092 0 : return NS_OK;
1093 : }
1094 :
1095 : // Spell check a textarea the first time that it is focused.
1096 0 : SpellCheckIfNeeded();
1097 0 : if (!editorBase) {
1098 : // In e10s, this can cause us to flush notifications, which can destroy
1099 : // the node we're about to focus.
1100 0 : return NS_OK;
1101 : }
1102 :
1103 0 : nsCOMPtr<nsIDOMEventTarget> target = aFocusEvent->GetDOMEventTarget();
1104 0 : nsCOMPtr<nsINode> node = do_QueryInterface(target);
1105 0 : NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);
1106 :
1107 : // If the target is a document node but it's not editable, we should ignore
1108 : // it because actual focused element's event is going to come.
1109 0 : if (node->IsNodeOfType(nsINode::eDOCUMENT) &&
1110 0 : !node->HasFlag(NODE_IS_EDITABLE)) {
1111 0 : return NS_OK;
1112 : }
1113 :
1114 0 : if (node->IsNodeOfType(nsINode::eCONTENT)) {
1115 : // XXX If the focus event target is a form control in contenteditable
1116 : // element, perhaps, the parent HTML editor should do nothing by this
1117 : // handler. However, FindSelectionRoot() returns the root element of the
1118 : // contenteditable editor. So, the editableRoot value is invalid for
1119 : // the plain text editor, and it will be set to the wrong limiter of
1120 : // the selection. However, fortunately, actual bugs are not found yet.
1121 0 : nsCOMPtr<nsIContent> editableRoot = editorBase->FindSelectionRoot(node);
1122 :
1123 : // make sure that the element is really focused in case an earlier
1124 : // listener in the chain changed the focus.
1125 0 : if (editableRoot) {
1126 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
1127 0 : NS_ENSURE_TRUE(fm, NS_OK);
1128 :
1129 0 : nsCOMPtr<nsIDOMElement> element;
1130 0 : fm->GetFocusedElement(getter_AddRefs(element));
1131 0 : if (!element) {
1132 0 : return NS_OK;
1133 : }
1134 :
1135 : nsCOMPtr<nsIDOMEventTarget> originalTarget =
1136 0 : aFocusEvent->GetOriginalDOMEventTarget();
1137 :
1138 : nsCOMPtr<nsIContent> originalTargetAsContent =
1139 0 : do_QueryInterface(originalTarget);
1140 : nsCOMPtr<nsIContent> focusedElementAsContent =
1141 0 : do_QueryInterface(element);
1142 :
1143 0 : if (!SameCOMIdentity(
1144 0 : focusedElementAsContent->FindFirstNonChromeOnlyAccessContent(),
1145 0 : originalTargetAsContent->FindFirstNonChromeOnlyAccessContent())) {
1146 0 : return NS_OK;
1147 : }
1148 : }
1149 : }
1150 :
1151 0 : editorBase->OnFocus(target);
1152 0 : if (DetachedFromEditorOrDefaultPrevented(aFocusEvent)) {
1153 0 : return NS_OK;
1154 : }
1155 :
1156 0 : nsCOMPtr<nsIPresShell> ps = GetPresShell();
1157 0 : NS_ENSURE_TRUE(ps, NS_OK);
1158 0 : nsCOMPtr<nsIContent> focusedContent = editorBase->GetFocusedContentForIME();
1159 0 : IMEStateManager::OnFocusInEditor(ps->GetPresContext(), focusedContent,
1160 0 : *editorBase);
1161 :
1162 0 : return NS_OK;
1163 : }
1164 :
1165 : nsresult
1166 0 : EditorEventListener::Blur(InternalFocusEvent* aBlurEvent)
1167 : {
1168 0 : if (NS_WARN_IF(!aBlurEvent) || DetachedFromEditor()) {
1169 0 : return NS_OK;
1170 : }
1171 :
1172 : // check if something else is focused. If another element is focused, then
1173 : // we should not change the selection.
1174 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
1175 0 : NS_ENSURE_TRUE(fm, NS_OK);
1176 :
1177 0 : nsCOMPtr<nsIDOMElement> element;
1178 0 : fm->GetFocusedElement(getter_AddRefs(element));
1179 0 : if (!element) {
1180 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1181 0 : editorBase->FinalizeSelection();
1182 : }
1183 0 : return NS_OK;
1184 : }
1185 :
1186 : void
1187 0 : EditorEventListener::SpellCheckIfNeeded()
1188 : {
1189 0 : MOZ_ASSERT(!DetachedFromEditor());
1190 :
1191 : // If the spell check skip flag is still enabled from creation time,
1192 : // disable it because focused editors are allowed to spell check.
1193 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1194 0 : uint32_t currentFlags = 0;
1195 0 : editorBase->GetFlags(¤tFlags);
1196 0 : if(currentFlags & nsIPlaintextEditor::eEditorSkipSpellCheck) {
1197 0 : currentFlags ^= nsIPlaintextEditor::eEditorSkipSpellCheck;
1198 0 : editorBase->SetFlags(currentFlags);
1199 : }
1200 0 : }
1201 :
1202 : bool
1203 0 : EditorEventListener::IsFileControlTextBox()
1204 : {
1205 0 : MOZ_ASSERT(!DetachedFromEditor());
1206 :
1207 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1208 0 : Element* root = editorBase->GetRoot();
1209 0 : if (!root || !root->ChromeOnlyAccess()) {
1210 0 : return false;
1211 : }
1212 0 : nsIContent* parent = root->FindFirstNonChromeOnlyAccessContent();
1213 0 : if (!parent || !parent->IsHTMLElement(nsGkAtoms::input)) {
1214 0 : return false;
1215 : }
1216 0 : nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(parent);
1217 0 : return formControl->ControlType() == NS_FORM_INPUT_FILE;
1218 : }
1219 :
1220 : bool
1221 0 : EditorEventListener::ShouldHandleNativeKeyBindings(
1222 : WidgetKeyboardEvent* aKeyboardEvent)
1223 : {
1224 0 : MOZ_ASSERT(!DetachedFromEditor());
1225 :
1226 : // Only return true if the target of the event is a desendant of the active
1227 : // editing host in order to match the similar decision made in
1228 : // nsXBLWindowKeyHandler.
1229 : // Note that IsAcceptableInputEvent doesn't check for the active editing
1230 : // host for keyboard events, otherwise this check would have been
1231 : // unnecessary. IsAcceptableInputEvent currently makes a similar check for
1232 : // mouse events.
1233 :
1234 0 : nsCOMPtr<nsIDOMEventTarget> target = aKeyboardEvent->GetDOMEventTarget();
1235 0 : nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
1236 0 : if (!targetContent) {
1237 0 : return false;
1238 : }
1239 :
1240 0 : RefPtr<EditorBase> editorBase(mEditorBase);
1241 0 : HTMLEditor* htmlEditor = editorBase->AsHTMLEditor();
1242 0 : if (!htmlEditor) {
1243 0 : return false;
1244 : }
1245 :
1246 0 : nsCOMPtr<nsIDocument> doc = editorBase->GetDocument();
1247 0 : if (doc->HasFlag(NODE_IS_EDITABLE)) {
1248 : // Don't need to perform any checks in designMode documents.
1249 0 : return true;
1250 : }
1251 :
1252 0 : nsIContent* editingHost = htmlEditor->GetActiveEditingHost();
1253 0 : if (!editingHost) {
1254 0 : return false;
1255 : }
1256 :
1257 0 : return nsContentUtils::ContentIsDescendantOf(targetContent, editingHost);
1258 : }
1259 :
1260 : } // namespace mozilla
|