1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "GlobalKeyListener.h"
8 #include "EventTarget.h"
12 #include "mozilla/EventListenerManager.h"
13 #include "mozilla/EventStateManager.h"
14 #include "mozilla/HTMLEditor.h"
15 #include "mozilla/KeyEventHandler.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/ShortcutKeys.h"
18 #include "mozilla/StaticPtr.h"
19 #include "mozilla/TextEvents.h"
20 #include "mozilla/dom/Element.h"
21 #include "mozilla/dom/Event.h"
22 #include "mozilla/dom/EventBinding.h"
23 #include "mozilla/dom/KeyboardEvent.h"
26 #include "nsContentUtils.h"
27 #include "nsFocusManager.h"
28 #include "nsGkAtoms.h"
29 #include "nsIContent.h"
30 #include "nsIDocShell.h"
31 #include "nsNetUtil.h"
32 #include "nsPIDOMWindow.h"
36 using namespace mozilla::layers
;
38 GlobalKeyListener::GlobalKeyListener(dom::EventTarget
* aTarget
)
39 : mTarget(aTarget
), mHandler(nullptr) {}
41 NS_IMPL_ISUPPORTS(GlobalKeyListener
, nsIDOMEventListener
)
43 static void BuildHandlerChain(nsIContent
* aContent
, KeyEventHandler
** aResult
) {
46 // Since we chain each handler onto the next handler,
47 // we'll enumerate them here in reverse so that when we
48 // walk the chain they'll come out in the original order
49 for (nsIContent
* key
= aContent
->GetLastChild(); key
;
50 key
= key
->GetPreviousSibling()) {
51 if (!key
->NodeInfo()->Equals(nsGkAtoms::key
, kNameSpaceID_XUL
)) {
55 dom::Element
* keyElement
= key
->AsElement();
56 // Check whether the key element has empty value at key/char attribute.
57 // Such element is used by localizers for alternative shortcut key
58 // definition on the locale. See bug 426501.
59 nsAutoString valKey
, valCharCode
, valKeyCode
;
60 // Hopefully at least one of the attributes is set:
61 keyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::key
, valKey
) ||
62 keyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::charcode
,
64 keyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::keycode
, valKeyCode
);
65 // If not, ignore this key element.
66 if (valKey
.IsEmpty() && valCharCode
.IsEmpty() && valKeyCode
.IsEmpty()) {
70 // reserved="pref" is the default for <key> elements.
71 ReservedKey reserved
= ReservedKey_Unset
;
72 if (keyElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::reserved
,
73 nsGkAtoms::_true
, eCaseMatters
)) {
74 reserved
= ReservedKey_True
;
75 } else if (keyElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::reserved
,
76 nsGkAtoms::_false
, eCaseMatters
)) {
77 reserved
= ReservedKey_False
;
80 KeyEventHandler
* handler
= new KeyEventHandler(keyElement
, reserved
);
82 handler
->SetNextHandler(*aResult
);
87 void GlobalKeyListener::WalkHandlers(dom::KeyboardEvent
* aKeyEvent
) {
88 if (aKeyEvent
->DefaultPrevented()) {
92 // Don't process the event if it was not dispatched from a trusted source
93 if (!aKeyEvent
->IsTrusted()) {
99 // skip keysets that are disabled
104 WalkHandlersInternal(aKeyEvent
, true);
107 void GlobalKeyListener::InstallKeyboardEventListenersTo(
108 EventListenerManager
* aEventListenerManager
) {
109 // For marking each keyboard event as if it's reserved by chrome,
110 // GlobalKeyListeners need to listen each keyboard events before
112 aEventListenerManager
->AddEventListenerByType(this, u
"keydown"_ns
,
113 TrustedEventsAtCapture());
114 aEventListenerManager
->AddEventListenerByType(this, u
"keyup"_ns
,
115 TrustedEventsAtCapture());
116 aEventListenerManager
->AddEventListenerByType(this, u
"keypress"_ns
,
117 TrustedEventsAtCapture());
119 // For reducing the IPC cost, preventing to dispatch reserved keyboard
120 // events into the content process.
121 aEventListenerManager
->AddEventListenerByType(
122 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupCapture());
123 aEventListenerManager
->AddEventListenerByType(
124 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupCapture());
125 aEventListenerManager
->AddEventListenerByType(
126 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupCapture());
128 // Handle keyboard events in bubbling phase of the system event group.
129 aEventListenerManager
->AddEventListenerByType(
130 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupBubble());
131 aEventListenerManager
->AddEventListenerByType(
132 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupBubble());
133 aEventListenerManager
->AddEventListenerByType(
134 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupBubble());
135 // mozaccesskeynotfound event is fired when modifiers of keypress event
136 // matches with modifier of content access key but it's not consumed by
138 aEventListenerManager
->AddEventListenerByType(
139 this, u
"mozaccesskeynotfound"_ns
, TrustedEventsAtSystemGroupBubble());
142 void GlobalKeyListener::RemoveKeyboardEventListenersFrom(
143 EventListenerManager
* aEventListenerManager
) {
144 aEventListenerManager
->RemoveEventListenerByType(this, u
"keydown"_ns
,
145 TrustedEventsAtCapture());
146 aEventListenerManager
->RemoveEventListenerByType(this, u
"keyup"_ns
,
147 TrustedEventsAtCapture());
148 aEventListenerManager
->RemoveEventListenerByType(this, u
"keypress"_ns
,
149 TrustedEventsAtCapture());
151 aEventListenerManager
->RemoveEventListenerByType(
152 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupCapture());
153 aEventListenerManager
->RemoveEventListenerByType(
154 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupCapture());
155 aEventListenerManager
->RemoveEventListenerByType(
156 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupCapture());
158 aEventListenerManager
->RemoveEventListenerByType(
159 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupBubble());
160 aEventListenerManager
->RemoveEventListenerByType(
161 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupBubble());
162 aEventListenerManager
->RemoveEventListenerByType(
163 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupBubble());
164 aEventListenerManager
->RemoveEventListenerByType(
165 this, u
"mozaccesskeynotfound"_ns
, TrustedEventsAtSystemGroupBubble());
169 GlobalKeyListener::HandleEvent(dom::Event
* aEvent
) {
170 RefPtr
<dom::KeyboardEvent
> keyEvent
= aEvent
->AsKeyboardEvent();
171 NS_ENSURE_TRUE(keyEvent
, NS_ERROR_INVALID_ARG
);
173 if (aEvent
->EventPhase() == dom::Event_Binding::CAPTURING_PHASE
) {
174 if (aEvent
->WidgetEventPtr()->mFlags
.mInSystemGroup
) {
175 HandleEventOnCaptureInSystemEventGroup(keyEvent
);
177 HandleEventOnCaptureInDefaultEventGroup(keyEvent
);
182 // If this event was handled by APZ then don't do the default action, and
183 // preventDefault to prevent any other listeners from handling the event.
184 if (aEvent
->WidgetEventPtr()->mFlags
.mHandledByAPZ
) {
185 aEvent
->PreventDefault();
189 WalkHandlers(keyEvent
);
193 void GlobalKeyListener::HandleEventOnCaptureInDefaultEventGroup(
194 dom::KeyboardEvent
* aEvent
) {
195 WidgetKeyboardEvent
* widgetKeyboardEvent
=
196 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
198 if (widgetKeyboardEvent
->IsReservedByChrome()) {
202 bool isReserved
= false;
203 if (HasHandlerForEvent(aEvent
, &isReserved
) && isReserved
) {
204 widgetKeyboardEvent
->MarkAsReservedByChrome();
208 void GlobalKeyListener::HandleEventOnCaptureInSystemEventGroup(
209 dom::KeyboardEvent
* aEvent
) {
210 WidgetKeyboardEvent
* widgetEvent
=
211 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
213 // If the event won't be sent to remote process, this listener needs to do
214 // nothing. Note that even if mOnlySystemGroupDispatchInContent is true,
215 // we need to send the event to remote process and check reply event
216 // before matching it with registered shortcut keys because event listeners
217 // in the system event group may want to handle the event before registered
218 // shortcut key handlers.
219 if (!widgetEvent
->WillBeSentToRemoteProcess()) {
223 if (!HasHandlerForEvent(aEvent
)) {
227 // If this event wasn't marked as IsCrossProcessForwardingStopped,
228 // yet, it means it wasn't processed by content. We'll not call any
229 // of the handlers at this moment, and will wait the reply event.
230 // So, stop immediate propagation in this event first, then, mark it as
231 // waiting reply from remote process. Finally, when this process receives
232 // a reply from the remote process, it should be dispatched into this
234 widgetEvent
->StopImmediatePropagation();
235 widgetEvent
->MarkAsWaitingReplyFromRemoteProcess();
239 // WalkHandlersInternal and WalkHandlersAndExecute
241 // Given a particular DOM event and a pointer to the first handler in the list,
242 // scan through the list to find something to handle the event. If aExecute =
243 // true, the handler will be executed; otherwise just return an answer telling
244 // if a handler for that event was found.
246 bool GlobalKeyListener::WalkHandlersInternal(dom::KeyboardEvent
* aKeyEvent
,
248 bool* aOutReservedForChrome
) {
249 WidgetKeyboardEvent
* nativeKeyboardEvent
=
250 aKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
251 MOZ_ASSERT(nativeKeyboardEvent
);
253 AutoShortcutKeyCandidateArray shortcutKeys
;
254 nativeKeyboardEvent
->GetShortcutKeyCandidates(shortcutKeys
);
256 if (shortcutKeys
.IsEmpty()) {
257 return WalkHandlersAndExecute(aKeyEvent
, 0, IgnoreModifierState(), aExecute
,
258 aOutReservedForChrome
);
261 for (unsigned long i
= 0; i
< shortcutKeys
.Length(); ++i
) {
262 ShortcutKeyCandidate
& key
= shortcutKeys
[i
];
263 IgnoreModifierState ignoreModifierState
;
264 ignoreModifierState
.mShift
= key
.mIgnoreShift
;
265 if (WalkHandlersAndExecute(aKeyEvent
, key
.mCharCode
, ignoreModifierState
,
266 aExecute
, aOutReservedForChrome
)) {
273 bool GlobalKeyListener::WalkHandlersAndExecute(
274 dom::KeyboardEvent
* aKeyEvent
, uint32_t aCharCode
,
275 const IgnoreModifierState
& aIgnoreModifierState
, bool aExecute
,
276 bool* aOutReservedForChrome
) {
277 if (aOutReservedForChrome
) {
278 *aOutReservedForChrome
= false;
281 WidgetKeyboardEvent
* widgetKeyboardEvent
=
282 aKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
283 if (NS_WARN_IF(!widgetKeyboardEvent
)) {
288 ShortcutKeys::ConvertEventToDOMEventType(widgetKeyboardEvent
);
290 // Try all of the handlers until we find one that matches the event.
291 for (KeyEventHandler
* handler
= mHandler
; handler
;
292 handler
= handler
->GetNextHandler()) {
293 bool stopped
= aKeyEvent
->IsDispatchStopped();
295 // The event is finished, don't execute any more handlers
300 if (!handler
->EventTypeEquals(eventType
)) {
304 if (handler
->EventTypeEquals(nsGkAtoms::keypress
)) {
305 // If the handler is a keypress event handler, we also need to check
306 // if coming keydown event is a preceding event of reserved key
307 // combination because if default action of a keydown event is
308 // prevented, following keypress event won't be fired. However, if
309 // following keypress event is reserved, we shouldn't allow web
310 // contents to prevent the default of the preceding keydown event.
311 if (eventType
!= nsGkAtoms::keydown
&&
312 eventType
!= nsGkAtoms::keypress
) {
315 } else if (!handler
->EventTypeEquals(eventType
)) {
316 // Otherwise, eventType should exactly be matched.
321 // Check if the keyboard event *may* execute the handler.
322 if (!handler
->KeyEventMatched(aKeyEvent
, aCharCode
, aIgnoreModifierState
)) {
323 continue; // try the next one
326 // Before executing this handler, check that it's not disabled,
327 // and that it has something to do (oncommand of the <key> or its
328 // <command> is non-empty).
329 if (!CanHandle(handler
, aExecute
)) {
334 if (handler
->EventTypeEquals(eventType
)) {
335 if (aOutReservedForChrome
) {
336 *aOutReservedForChrome
= IsReservedKey(widgetKeyboardEvent
, handler
);
342 // If the command is reserved and the event is keydown, check also if
343 // the handler is for keypress because if following keypress event is
344 // reserved, we shouldn't dispatch the event into web contents.
345 if (eventType
== nsGkAtoms::keydown
&&
346 handler
->EventTypeEquals(nsGkAtoms::keypress
)) {
347 if (IsReservedKey(widgetKeyboardEvent
, handler
)) {
348 if (aOutReservedForChrome
) {
349 *aOutReservedForChrome
= true;
355 // Otherwise, we've not found a handler for the event yet.
359 // This should only be assigned when aExecute is false.
360 MOZ_ASSERT(!aOutReservedForChrome
);
362 nsCOMPtr
<dom::EventTarget
> target
= GetHandlerTarget(handler
);
364 // XXX Do we execute only one handler even if the handler neither stops
365 // propagation nor prevents default of the event?
366 nsresult rv
= handler
->ExecuteHandler(target
, aKeyEvent
);
367 if (NS_SUCCEEDED(rv
)) {
373 // Windows native applications ignore Windows-Logo key state when checking
374 // shortcut keys even if the key is pressed. Therefore, if there is no
375 // shortcut key which exactly matches current modifier state, we should
376 // retry to look for a shortcut key without the Windows-Logo key press.
377 if (!aIgnoreModifierState
.mOS
&& widgetKeyboardEvent
->IsOS()) {
378 IgnoreModifierState
ignoreModifierState(aIgnoreModifierState
);
379 ignoreModifierState
.mOS
= true;
380 return WalkHandlersAndExecute(aKeyEvent
, aCharCode
, ignoreModifierState
,
388 bool GlobalKeyListener::IsReservedKey(WidgetKeyboardEvent
* aKeyEvent
,
389 KeyEventHandler
* aHandler
) {
390 ReservedKey reserved
= aHandler
->GetIsReserved();
391 // reserved="true" means that the key is always reserved. reserved="false"
392 // means that the key is never reserved. Otherwise, we check site-specific
394 if (reserved
== ReservedKey_False
) {
398 if (reserved
== ReservedKey_True
) {
402 return nsContentUtils::ShouldBlockReservedKeys(aKeyEvent
);
405 bool GlobalKeyListener::HasHandlerForEvent(dom::KeyboardEvent
* aEvent
,
406 bool* aOutReservedForChrome
) {
407 WidgetKeyboardEvent
* widgetKeyboardEvent
=
408 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
409 if (NS_WARN_IF(!widgetKeyboardEvent
) || !widgetKeyboardEvent
->IsTrusted()) {
419 return WalkHandlersInternal(aEvent
, false, aOutReservedForChrome
);
423 // AttachGlobalKeyHandler
425 // Creates a new key handler and prepares to listen to key events on the given
426 // event receiver (either a document or an content node). If the receiver is
427 // content, then extra work needs to be done to hook it up to the document (XXX
430 void XULKeySetGlobalKeyListener::AttachKeyHandler(
431 dom::Element
* aElementTarget
) {
432 // Only attach if we're really in a document
433 nsCOMPtr
<dom::Document
> doc
= aElementTarget
->GetUncomposedDoc();
438 EventListenerManager
* manager
= doc
->GetOrCreateListenerManager();
443 // the listener already exists, so skip this
444 if (aElementTarget
->GetProperty(nsGkAtoms::listener
)) {
448 // Create the key handler
449 RefPtr
<XULKeySetGlobalKeyListener
> handler
=
450 new XULKeySetGlobalKeyListener(aElementTarget
, doc
);
452 handler
->InstallKeyboardEventListenersTo(manager
);
454 aElementTarget
->SetProperty(nsGkAtoms::listener
, handler
.forget().take(),
455 nsPropertyTable::SupportsDtorFunc
, true);
459 // DetachGlobalKeyHandler
461 // Removes a key handler added by AttachKeyHandler.
463 void XULKeySetGlobalKeyListener::DetachKeyHandler(
464 dom::Element
* aElementTarget
) {
465 // Only attach if we're really in a document
466 nsCOMPtr
<dom::Document
> doc
= aElementTarget
->GetUncomposedDoc();
471 EventListenerManager
* manager
= doc
->GetOrCreateListenerManager();
476 nsIDOMEventListener
* handler
= static_cast<nsIDOMEventListener
*>(
477 aElementTarget
->GetProperty(nsGkAtoms::listener
));
482 static_cast<XULKeySetGlobalKeyListener
*>(handler
)
483 ->RemoveKeyboardEventListenersFrom(manager
);
484 aElementTarget
->RemoveProperty(nsGkAtoms::listener
);
487 XULKeySetGlobalKeyListener::XULKeySetGlobalKeyListener(
488 dom::Element
* aElement
, dom::EventTarget
* aTarget
)
489 : GlobalKeyListener(aTarget
) {
490 mWeakPtrForElement
= do_GetWeakReference(aElement
);
493 dom::Element
* XULKeySetGlobalKeyListener::GetElement(bool* aIsDisabled
) const {
494 RefPtr
<dom::Element
> element
= do_QueryReferent(mWeakPtrForElement
);
495 if (element
&& aIsDisabled
) {
496 *aIsDisabled
= element
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
497 nsGkAtoms::_true
, eCaseMatters
);
499 return element
.get();
502 XULKeySetGlobalKeyListener::~XULKeySetGlobalKeyListener() {
503 if (mWeakPtrForElement
) {
508 void XULKeySetGlobalKeyListener::EnsureHandlers() {
513 dom::Element
* element
= GetElement();
518 BuildHandlerChain(element
, &mHandler
);
521 bool XULKeySetGlobalKeyListener::IsDisabled() const {
523 dom::Element
* element
= GetElement(&isDisabled
);
524 return element
&& isDisabled
;
527 bool XULKeySetGlobalKeyListener::GetElementForHandler(
528 KeyEventHandler
* aHandler
, dom::Element
** aElementForHandler
) const {
529 MOZ_ASSERT(aElementForHandler
);
530 *aElementForHandler
= nullptr;
532 RefPtr
<dom::Element
> keyElement
= aHandler
->GetHandlerElement();
534 // This should only be the case where the <key> element that generated the
535 // handler has been destroyed. Not sure why we return true here...
539 nsCOMPtr
<dom::Element
> chromeHandlerElement
= GetElement();
540 if (!chromeHandlerElement
) {
541 NS_WARNING_ASSERTION(keyElement
->IsInUncomposedDoc(), "uncomposed");
542 keyElement
.swap(*aElementForHandler
);
546 // We are in a XUL doc. Obtain our command attribute.
547 nsAutoString command
;
548 keyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, command
);
549 if (command
.IsEmpty()) {
550 // There is no command element associated with the key element.
551 NS_WARNING_ASSERTION(keyElement
->IsInUncomposedDoc(), "uncomposed");
552 keyElement
.swap(*aElementForHandler
);
556 // XXX Shouldn't we check this earlier?
557 dom::Document
* doc
= keyElement
->GetUncomposedDoc();
558 if (NS_WARN_IF(!doc
)) {
562 nsCOMPtr
<dom::Element
> commandElement
= doc
->GetElementById(command
);
563 if (!commandElement
) {
565 "A XUL <key> is observing a command that doesn't exist. "
566 "Unable to execute key binding!");
570 commandElement
.swap(*aElementForHandler
);
574 bool XULKeySetGlobalKeyListener::IsExecutableElement(
575 dom::Element
* aElement
) const {
581 aElement
->GetAttr(nsGkAtoms::disabled
, value
);
582 if (value
.EqualsLiteral("true")) {
586 aElement
->GetAttr(nsGkAtoms::oncommand
, value
);
587 return !value
.IsEmpty();
590 already_AddRefed
<dom::EventTarget
> XULKeySetGlobalKeyListener::GetHandlerTarget(
591 KeyEventHandler
* aHandler
) {
592 nsCOMPtr
<dom::Element
> commandElement
;
593 if (!GetElementForHandler(aHandler
, getter_AddRefs(commandElement
))) {
597 return commandElement
.forget();
600 bool XULKeySetGlobalKeyListener::CanHandle(KeyEventHandler
* aHandler
,
601 bool aWillExecute
) const {
602 nsCOMPtr
<dom::Element
> commandElement
;
603 if (!GetElementForHandler(aHandler
, getter_AddRefs(commandElement
))) {
607 // The only case where commandElement can be null here is where the <key>
608 // element for the handler is already destroyed. I'm not sure why we continue
610 if (!commandElement
) {
614 // If we're not actually going to execute here bypass the execution check.
615 return !aWillExecute
|| IsExecutableElement(commandElement
);
619 layers::KeyboardMap
RootWindowGlobalKeyListener::CollectKeyboardShortcuts() {
620 KeyEventHandler
* handlers
= ShortcutKeys::GetHandlers(HandlerType::eBrowser
);
622 // Convert the handlers into keyboard shortcuts, using an AutoTArray with
623 // the maximum amount of shortcuts used on any platform to minimize
625 AutoTArray
<KeyboardShortcut
, 48> shortcuts
;
627 // Append keyboard shortcuts for hardcoded actions like tab
628 KeyboardShortcut::AppendHardcodedShortcuts(shortcuts
);
630 for (KeyEventHandler
* handler
= handlers
; handler
;
631 handler
= handler
->GetNextHandler()) {
632 KeyboardShortcut shortcut
;
633 if (handler
->TryConvertToKeyboardShortcut(&shortcut
)) {
634 shortcuts
.AppendElement(shortcut
);
638 return layers::KeyboardMap(std::move(shortcuts
));
642 // AttachGlobalKeyHandler
644 // Creates a new key handler and prepares to listen to key events on the given
645 // event receiver (either a document or an content node). If the receiver is
646 // content, then extra work needs to be done to hook it up to the document (XXX
649 void RootWindowGlobalKeyListener::AttachKeyHandler(dom::EventTarget
* aTarget
) {
650 EventListenerManager
* manager
= aTarget
->GetOrCreateListenerManager();
655 // Create the key handler
656 RefPtr
<RootWindowGlobalKeyListener
> handler
=
657 new RootWindowGlobalKeyListener(aTarget
);
659 // This registers handler with the manager so the manager will keep handler
660 // alive past this point.
661 handler
->InstallKeyboardEventListenersTo(manager
);
664 RootWindowGlobalKeyListener::RootWindowGlobalKeyListener(
665 dom::EventTarget
* aTarget
)
666 : GlobalKeyListener(aTarget
) {}
669 bool RootWindowGlobalKeyListener::IsHTMLEditorFocused() {
670 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
675 nsCOMPtr
<mozIDOMWindowProxy
> focusedWindow
;
676 fm
->GetFocusedWindow(getter_AddRefs(focusedWindow
));
677 if (!focusedWindow
) {
681 auto* piwin
= nsPIDOMWindowOuter::From(focusedWindow
);
682 nsIDocShell
* docShell
= piwin
->GetDocShell();
687 HTMLEditor
* htmlEditor
= docShell
->GetHTMLEditor();
692 dom::Document
* doc
= htmlEditor
->GetDocument();
693 if (doc
->HasFlag(NODE_IS_EDITABLE
)) {
694 // Don't need to perform any checks in designMode documents.
698 nsINode
* focusedNode
= fm
->GetFocusedElement();
699 if (focusedNode
&& focusedNode
->IsElement()) {
700 // If there is a focused element, make sure it's in the active editing host.
701 // Note that GetActiveEditingHost finds the current editing host based on
702 // the document's selection. Even though the document selection is usually
703 // collapsed to where the focus is, but the page may modify the selection
704 // without our knowledge, in which case this check will do something useful.
705 nsCOMPtr
<dom::Element
> activeEditingHost
=
706 htmlEditor
->GetActiveEditingHost();
707 if (!activeEditingHost
) {
710 return focusedNode
->IsInclusiveDescendantOf(activeEditingHost
);
716 void RootWindowGlobalKeyListener::EnsureHandlers() {
717 if (IsHTMLEditorFocused()) {
718 mHandler
= ShortcutKeys::GetHandlers(HandlerType::eEditor
);
720 mHandler
= ShortcutKeys::GetHandlers(HandlerType::eBrowser
);
724 } // namespace mozilla