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"
9 #include "EventTarget.h"
13 #include "mozilla/EventListenerManager.h"
14 #include "mozilla/EventStateManager.h"
15 #include "mozilla/HTMLEditor.h"
16 #include "mozilla/KeyEventHandler.h"
17 #include "mozilla/NativeKeyBindingsType.h"
18 #include "mozilla/Preferences.h"
19 #include "mozilla/ShortcutKeys.h"
20 #include "mozilla/StaticPtr.h"
21 #include "mozilla/TextEvents.h"
22 #include "mozilla/dom/Element.h"
23 #include "mozilla/dom/Event.h"
24 #include "mozilla/dom/EventBinding.h"
25 #include "mozilla/dom/KeyboardEvent.h"
26 #include "mozilla/widget/IMEData.h"
29 #include "nsContentUtils.h"
30 #include "nsFocusManager.h"
31 #include "nsGkAtoms.h"
32 #include "nsIContent.h"
33 #include "nsIContentInlines.h"
34 #include "nsIDocShell.h"
35 #include "nsIWidget.h"
36 #include "nsNetUtil.h"
37 #include "nsPIDOMWindow.h"
41 using namespace mozilla::layers
;
43 GlobalKeyListener::GlobalKeyListener(dom::EventTarget
* aTarget
)
44 : mTarget(aTarget
), mHandler(nullptr) {}
46 NS_IMPL_ISUPPORTS(GlobalKeyListener
, nsIDOMEventListener
)
48 static void BuildHandlerChain(nsIContent
* aContent
, KeyEventHandler
** aResult
) {
51 // Since we chain each handler onto the next handler,
52 // we'll enumerate them here in reverse so that when we
53 // walk the chain they'll come out in the original order
54 for (nsIContent
* key
= aContent
->GetLastChild(); key
;
55 key
= key
->GetPreviousSibling()) {
56 if (!key
->NodeInfo()->Equals(nsGkAtoms::key
, kNameSpaceID_XUL
)) {
60 dom::Element
* keyElement
= key
->AsElement();
61 // Check whether the key element has empty value at key/char attribute.
62 // Such element is used by localizers for alternative shortcut key
63 // definition on the locale. See bug 426501.
64 nsAutoString valKey
, valCharCode
, valKeyCode
;
65 // Hopefully at least one of the attributes is set:
66 keyElement
->GetAttr(nsGkAtoms::key
, valKey
) ||
67 keyElement
->GetAttr(nsGkAtoms::charcode
, valCharCode
) ||
68 keyElement
->GetAttr(nsGkAtoms::keycode
, valKeyCode
);
69 // If not, ignore this key element.
70 if (valKey
.IsEmpty() && valCharCode
.IsEmpty() && valKeyCode
.IsEmpty()) {
74 // reserved="pref" is the default for <key> elements.
75 ReservedKey reserved
= ReservedKey_Unset
;
76 if (keyElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::reserved
,
77 nsGkAtoms::_true
, eCaseMatters
)) {
78 reserved
= ReservedKey_True
;
79 } else if (keyElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::reserved
,
80 nsGkAtoms::_false
, eCaseMatters
)) {
81 reserved
= ReservedKey_False
;
84 KeyEventHandler
* handler
= new KeyEventHandler(keyElement
, reserved
);
86 handler
->SetNextHandler(*aResult
);
91 void GlobalKeyListener::WalkHandlers(dom::KeyboardEvent
* aKeyEvent
) {
92 if (aKeyEvent
->DefaultPrevented()) {
96 // Don't process the event if it was not dispatched from a trusted source
97 if (!aKeyEvent
->IsTrusted()) {
103 // skip keysets that are disabled
108 WalkHandlersInternal(Purpose::ExecuteCommand
, aKeyEvent
);
111 void GlobalKeyListener::InstallKeyboardEventListenersTo(
112 EventListenerManager
* aEventListenerManager
) {
113 // For marking each keyboard event as if it's reserved by chrome,
114 // GlobalKeyListeners need to listen each keyboard events before
116 aEventListenerManager
->AddEventListenerByType(this, u
"keydown"_ns
,
117 TrustedEventsAtCapture());
118 aEventListenerManager
->AddEventListenerByType(this, u
"keyup"_ns
,
119 TrustedEventsAtCapture());
120 aEventListenerManager
->AddEventListenerByType(this, u
"keypress"_ns
,
121 TrustedEventsAtCapture());
123 // For reducing the IPC cost, preventing to dispatch reserved keyboard
124 // events into the content process.
125 aEventListenerManager
->AddEventListenerByType(
126 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupCapture());
127 aEventListenerManager
->AddEventListenerByType(
128 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupCapture());
129 aEventListenerManager
->AddEventListenerByType(
130 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupCapture());
132 // Handle keyboard events in bubbling phase of the system event group.
133 aEventListenerManager
->AddEventListenerByType(
134 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupBubble());
135 aEventListenerManager
->AddEventListenerByType(
136 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupBubble());
137 aEventListenerManager
->AddEventListenerByType(
138 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupBubble());
139 // mozaccesskeynotfound event is fired when modifiers of keypress event
140 // matches with modifier of content access key but it's not consumed by
142 aEventListenerManager
->AddEventListenerByType(
143 this, u
"mozaccesskeynotfound"_ns
, TrustedEventsAtSystemGroupBubble());
146 void GlobalKeyListener::RemoveKeyboardEventListenersFrom(
147 EventListenerManager
* aEventListenerManager
) {
148 aEventListenerManager
->RemoveEventListenerByType(this, u
"keydown"_ns
,
149 TrustedEventsAtCapture());
150 aEventListenerManager
->RemoveEventListenerByType(this, u
"keyup"_ns
,
151 TrustedEventsAtCapture());
152 aEventListenerManager
->RemoveEventListenerByType(this, u
"keypress"_ns
,
153 TrustedEventsAtCapture());
155 aEventListenerManager
->RemoveEventListenerByType(
156 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupCapture());
157 aEventListenerManager
->RemoveEventListenerByType(
158 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupCapture());
159 aEventListenerManager
->RemoveEventListenerByType(
160 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupCapture());
162 aEventListenerManager
->RemoveEventListenerByType(
163 this, u
"keydown"_ns
, TrustedEventsAtSystemGroupBubble());
164 aEventListenerManager
->RemoveEventListenerByType(
165 this, u
"keyup"_ns
, TrustedEventsAtSystemGroupBubble());
166 aEventListenerManager
->RemoveEventListenerByType(
167 this, u
"keypress"_ns
, TrustedEventsAtSystemGroupBubble());
168 aEventListenerManager
->RemoveEventListenerByType(
169 this, u
"mozaccesskeynotfound"_ns
, TrustedEventsAtSystemGroupBubble());
173 GlobalKeyListener::HandleEvent(dom::Event
* aEvent
) {
174 RefPtr
<dom::KeyboardEvent
> keyEvent
= aEvent
->AsKeyboardEvent();
175 NS_ENSURE_TRUE(keyEvent
, NS_ERROR_INVALID_ARG
);
177 if (aEvent
->EventPhase() == dom::Event_Binding::CAPTURING_PHASE
) {
178 if (aEvent
->WidgetEventPtr()->mFlags
.mInSystemGroup
) {
179 HandleEventOnCaptureInSystemEventGroup(keyEvent
);
181 HandleEventOnCaptureInDefaultEventGroup(keyEvent
);
186 // If this event was handled by APZ then don't do the default action, and
187 // preventDefault to prevent any other listeners from handling the event.
188 if (aEvent
->WidgetEventPtr()->mFlags
.mHandledByAPZ
) {
189 aEvent
->PreventDefault();
193 WalkHandlers(keyEvent
);
197 void GlobalKeyListener::HandleEventOnCaptureInDefaultEventGroup(
198 dom::KeyboardEvent
* aEvent
) {
199 WidgetKeyboardEvent
* widgetKeyboardEvent
=
200 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
202 if (widgetKeyboardEvent
->IsReservedByChrome()) {
206 if (HasHandlerForEvent(aEvent
).mReservedHandlerForChromeFound
) {
207 widgetKeyboardEvent
->MarkAsReservedByChrome();
211 void GlobalKeyListener::HandleEventOnCaptureInSystemEventGroup(
212 dom::KeyboardEvent
* aEvent
) {
213 WidgetKeyboardEvent
* widgetEvent
=
214 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
216 // If the event won't be sent to remote process, this listener needs to do
217 // nothing. Note that even if mOnlySystemGroupDispatchInContent is true,
218 // we need to send the event to remote process and check reply event
219 // before matching it with registered shortcut keys because event listeners
220 // in the system event group may want to handle the event before registered
221 // shortcut key handlers.
222 if (!widgetEvent
->WillBeSentToRemoteProcess()) {
226 if (!HasHandlerForEvent(aEvent
).mMeaningfulHandlerFound
) {
230 // If this event wasn't marked as IsCrossProcessForwardingStopped,
231 // yet, it means it wasn't processed by content. We'll not call any
232 // of the handlers at this moment, and will wait the reply event.
233 // So, stop immediate propagation in this event first, then, mark it as
234 // waiting reply from remote process. Finally, when this process receives
235 // a reply from the remote process, it should be dispatched into this
237 widgetEvent
->StopImmediatePropagation();
238 widgetEvent
->MarkAsWaitingReplyFromRemoteProcess();
242 // WalkHandlersInternal and WalkHandlersAndExecute
244 // Given a particular DOM event and a pointer to the first handler in the list,
245 // scan through the list to find something to handle the event. If aPurpose =
246 // Purpose::ExecuteHandler, the handler will be executed; otherwise just return
247 // an answer telling if a handler for that event was found.
249 GlobalKeyListener::WalkHandlersResult
GlobalKeyListener::WalkHandlersInternal(
250 Purpose aPurpose
, dom::KeyboardEvent
* aKeyEvent
) {
251 WidgetKeyboardEvent
* nativeKeyboardEvent
=
252 aKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
253 MOZ_ASSERT(nativeKeyboardEvent
);
255 AutoShortcutKeyCandidateArray shortcutKeys
;
256 nativeKeyboardEvent
->GetShortcutKeyCandidates(shortcutKeys
);
258 if (shortcutKeys
.IsEmpty()) {
259 return WalkHandlersAndExecute(aPurpose
, aKeyEvent
, 0,
260 IgnoreModifierState());
263 bool foundDisabledHandler
= false;
264 for (const ShortcutKeyCandidate
& key
: shortcutKeys
) {
265 const bool skipIfEarlierHandlerDisabled
=
266 key
.mSkipIfEarlierHandlerDisabled
==
267 ShortcutKeyCandidate::SkipIfEarlierHandlerDisabled::Yes
;
268 if (foundDisabledHandler
&& skipIfEarlierHandlerDisabled
) {
271 IgnoreModifierState ignoreModifierState
;
272 ignoreModifierState
.mShift
=
273 key
.mShiftState
== ShortcutKeyCandidate::ShiftState::Ignorable
;
274 WalkHandlersResult result
= WalkHandlersAndExecute(
275 aPurpose
, aKeyEvent
, key
.mCharCode
, ignoreModifierState
);
276 if (result
.mMeaningfulHandlerFound
) {
279 // Note that if the candidate should not match if an earlier handler is
280 // disabled, the char code of the candidate is a char which may be
281 // introduced with different shift state. In this case, we do NOT find a
282 // disabled handler which **exactly** matches with the keyboard event.
283 // This avoids to override a higher priority handler with a disabled lower
285 if (!skipIfEarlierHandlerDisabled
&& !foundDisabledHandler
) {
286 foundDisabledHandler
= result
.mDisabledHandlerFound
;
292 GlobalKeyListener::WalkHandlersResult
GlobalKeyListener::WalkHandlersAndExecute(
293 Purpose aPurpose
, dom::KeyboardEvent
* aKeyEvent
, uint32_t aCharCode
,
294 const IgnoreModifierState
& aIgnoreModifierState
) {
295 WidgetKeyboardEvent
* widgetKeyboardEvent
=
296 aKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
297 if (NS_WARN_IF(!widgetKeyboardEvent
)) {
302 ShortcutKeys::ConvertEventToDOMEventType(widgetKeyboardEvent
);
304 // Try all of the handlers until we find one that matches the event.
305 bool foundDisabledHandler
= false;
306 for (KeyEventHandler
* handler
= mHandler
; handler
;
307 handler
= handler
->GetNextHandler()) {
308 bool stopped
= aKeyEvent
->IsDispatchStopped();
310 // The event is finished, don't execute any more handlers
314 if (aPurpose
== Purpose::ExecuteCommand
) {
315 if (!handler
->EventTypeEquals(eventType
)) {
319 if (handler
->EventTypeEquals(nsGkAtoms::keypress
)) {
320 // If the handler is a keypress event handler, we also need to check
321 // if coming keydown event is a preceding event of reserved key
322 // combination because if default action of a keydown event is
323 // prevented, following keypress event won't be fired. However, if
324 // following keypress event is reserved, we shouldn't allow web
325 // contents to prevent the default of the preceding keydown event.
326 if (eventType
!= nsGkAtoms::keydown
&&
327 eventType
!= nsGkAtoms::keypress
) {
330 } else if (!handler
->EventTypeEquals(eventType
)) {
331 // Otherwise, eventType should exactly be matched.
336 // Check if the keyboard event *may* execute the handler.
337 if (!handler
->KeyEventMatched(aKeyEvent
, aCharCode
, aIgnoreModifierState
)) {
338 continue; // try the next one
341 // Before executing this handler, check that it's not disabled,
342 // and that it has something to do (oncommand of the <key> or its
343 // <command> is non-empty).
344 if (!CanHandle(handler
, aPurpose
== Purpose::ExecuteCommand
)) {
345 foundDisabledHandler
= true;
349 if (aPurpose
== Purpose::LookForCommand
) {
350 if (handler
->EventTypeEquals(eventType
)) {
351 WalkHandlersResult result
;
352 result
.mMeaningfulHandlerFound
= true;
353 result
.mReservedHandlerForChromeFound
=
354 IsReservedKey(widgetKeyboardEvent
, handler
);
358 // If the command is reserved and the event is keydown, check also if
359 // the handler is for keypress because if following keypress event is
360 // reserved, we shouldn't dispatch the event into web contents.
361 if (eventType
== nsGkAtoms::keydown
&&
362 handler
->EventTypeEquals(nsGkAtoms::keypress
)) {
363 if (IsReservedKey(widgetKeyboardEvent
, handler
)) {
364 WalkHandlersResult result
;
365 result
.mMeaningfulHandlerFound
= true;
366 result
.mReservedHandlerForChromeFound
= true;
370 // Otherwise, we've not found a handler for the event yet.
374 nsCOMPtr
<dom::EventTarget
> target
= GetHandlerTarget(handler
);
376 // XXX Do we execute only one handler even if the handler neither stops
377 // propagation nor prevents default of the event?
378 nsresult rv
= handler
->ExecuteHandler(target
, aKeyEvent
);
379 if (NS_SUCCEEDED(rv
)) {
380 WalkHandlersResult result
;
381 result
.mMeaningfulHandlerFound
= true;
382 result
.mReservedHandlerForChromeFound
=
383 IsReservedKey(widgetKeyboardEvent
, handler
);
384 result
.mDisabledHandlerFound
= (rv
== NS_SUCCESS_DOM_NO_OPERATION
);
390 // Windows native applications ignore Windows-Logo key state when checking
391 // shortcut keys even if the key is pressed. Therefore, if there is no
392 // shortcut key which exactly matches current modifier state, we should
393 // retry to look for a shortcut key without the Windows-Logo key press.
394 if (!aIgnoreModifierState
.mMeta
&& widgetKeyboardEvent
->IsMeta()) {
395 IgnoreModifierState
ignoreModifierState(aIgnoreModifierState
);
396 ignoreModifierState
.mMeta
= true;
397 return WalkHandlersAndExecute(aPurpose
, aKeyEvent
, aCharCode
,
398 ignoreModifierState
);
402 WalkHandlersResult result
;
403 result
.mDisabledHandlerFound
= foundDisabledHandler
;
407 bool GlobalKeyListener::IsReservedKey(WidgetKeyboardEvent
* aKeyEvent
,
408 KeyEventHandler
* aHandler
) {
409 ReservedKey reserved
= aHandler
->GetIsReserved();
410 // reserved="true" means that the key is always reserved. reserved="false"
411 // means that the key is never reserved. Otherwise, we check site-specific
413 if (reserved
== ReservedKey_False
) {
417 if (reserved
!= ReservedKey_True
&&
418 !nsContentUtils::ShouldBlockReservedKeys(aKeyEvent
)) {
422 // Okay, the key handler is reserved, but if the key combination is mapped to
423 // an edit command or a selection navigation command, we should not treat it
424 // as reserved since user wants to do the mapped thing(s) in editor.
425 if (MOZ_UNLIKELY(!aKeyEvent
->IsTrusted() || !aKeyEvent
->mWidget
)) {
428 widget::InputContext inputContext
= aKeyEvent
->mWidget
->GetInputContext();
429 if (!inputContext
.mIMEState
.IsEditable()) {
432 return MOZ_UNLIKELY(!aKeyEvent
->IsEditCommandsInitialized(
433 inputContext
.GetNativeKeyBindingsType())) ||
435 ->EditCommandsConstRef(inputContext
.GetNativeKeyBindingsType())
439 GlobalKeyListener::WalkHandlersResult
GlobalKeyListener::HasHandlerForEvent(
440 dom::KeyboardEvent
* aEvent
) {
441 WidgetKeyboardEvent
* widgetKeyboardEvent
=
442 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
443 if (NS_WARN_IF(!widgetKeyboardEvent
) || !widgetKeyboardEvent
->IsTrusted()) {
453 return WalkHandlersInternal(Purpose::LookForCommand
, aEvent
);
457 // AttachGlobalKeyHandler
459 // Creates a new key handler and prepares to listen to key events on the given
460 // event receiver (either a document or an content node). If the receiver is
461 // content, then extra work needs to be done to hook it up to the document (XXX
464 void XULKeySetGlobalKeyListener::AttachKeyHandler(
465 dom::Element
* aElementTarget
) {
466 // Only attach if we're really in a document
467 nsCOMPtr
<dom::Document
> doc
= aElementTarget
->GetUncomposedDoc();
472 EventListenerManager
* manager
= doc
->GetOrCreateListenerManager();
477 // the listener already exists, so skip this
478 if (aElementTarget
->GetProperty(nsGkAtoms::listener
)) {
482 // Create the key handler
483 RefPtr
<XULKeySetGlobalKeyListener
> handler
=
484 new XULKeySetGlobalKeyListener(aElementTarget
, doc
);
486 handler
->InstallKeyboardEventListenersTo(manager
);
488 aElementTarget
->SetProperty(nsGkAtoms::listener
, handler
.forget().take(),
489 nsPropertyTable::SupportsDtorFunc
, true);
493 // DetachGlobalKeyHandler
495 // Removes a key handler added by AttachKeyHandler.
497 void XULKeySetGlobalKeyListener::DetachKeyHandler(
498 dom::Element
* aElementTarget
) {
499 // Only attach if we're really in a document
500 nsCOMPtr
<dom::Document
> doc
= aElementTarget
->GetUncomposedDoc();
505 EventListenerManager
* manager
= doc
->GetOrCreateListenerManager();
510 nsIDOMEventListener
* handler
= static_cast<nsIDOMEventListener
*>(
511 aElementTarget
->GetProperty(nsGkAtoms::listener
));
516 static_cast<XULKeySetGlobalKeyListener
*>(handler
)
517 ->RemoveKeyboardEventListenersFrom(manager
);
518 aElementTarget
->RemoveProperty(nsGkAtoms::listener
);
521 XULKeySetGlobalKeyListener::XULKeySetGlobalKeyListener(
522 dom::Element
* aElement
, dom::EventTarget
* aTarget
)
523 : GlobalKeyListener(aTarget
) {
524 mWeakPtrForElement
= do_GetWeakReference(aElement
);
527 dom::Element
* XULKeySetGlobalKeyListener::GetElement(bool* aIsDisabled
) const {
528 RefPtr
<dom::Element
> element
= do_QueryReferent(mWeakPtrForElement
);
529 if (element
&& aIsDisabled
) {
530 *aIsDisabled
= element
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
531 nsGkAtoms::_true
, eCaseMatters
);
533 return element
.get();
536 XULKeySetGlobalKeyListener::~XULKeySetGlobalKeyListener() {
537 if (mWeakPtrForElement
) {
542 void XULKeySetGlobalKeyListener::EnsureHandlers() {
547 dom::Element
* element
= GetElement();
552 BuildHandlerChain(element
, &mHandler
);
555 bool XULKeySetGlobalKeyListener::IsDisabled() const {
557 dom::Element
* element
= GetElement(&isDisabled
);
558 return element
&& isDisabled
;
561 bool XULKeySetGlobalKeyListener::GetElementForHandler(
562 KeyEventHandler
* aHandler
, dom::Element
** aElementForHandler
) const {
563 MOZ_ASSERT(aElementForHandler
);
564 *aElementForHandler
= nullptr;
566 RefPtr
<dom::Element
> keyElement
= aHandler
->GetHandlerElement();
568 // This should only be the case where the <key> element that generated the
569 // handler has been destroyed. Not sure why we return true here...
573 nsCOMPtr
<dom::Element
> chromeHandlerElement
= GetElement();
574 if (!chromeHandlerElement
) {
575 NS_WARNING_ASSERTION(keyElement
->IsInUncomposedDoc(), "uncomposed");
576 keyElement
.swap(*aElementForHandler
);
580 // We are in a XUL doc. Obtain our command attribute.
581 nsAutoString command
;
582 keyElement
->GetAttr(nsGkAtoms::command
, command
);
583 if (command
.IsEmpty()) {
584 // There is no command element associated with the key element.
585 NS_WARNING_ASSERTION(keyElement
->IsInUncomposedDoc(), "uncomposed");
586 keyElement
.swap(*aElementForHandler
);
590 // XXX Shouldn't we check this earlier?
591 dom::Document
* doc
= keyElement
->GetUncomposedDoc();
592 if (NS_WARN_IF(!doc
)) {
596 nsCOMPtr
<dom::Element
> commandElement
= doc
->GetElementById(command
);
597 if (!commandElement
) {
599 "A XUL <key> is observing a command that doesn't exist. "
600 "Unable to execute key binding!");
604 commandElement
.swap(*aElementForHandler
);
608 bool XULKeySetGlobalKeyListener::IsExecutableElement(
609 dom::Element
* aElement
) const {
615 aElement
->GetAttr(nsGkAtoms::disabled
, value
);
616 if (value
.EqualsLiteral("true")) {
620 // Internal keys are defined as <key> elements so that the menu label
621 // and disabled state can be updated properly, but the command is executed
622 // by some other means. This will typically be because the key is defined
623 // as a shortcut defined in ShortcutKeyDefinitions.cpp instead, or on Mac,
624 // some special system defined keys.
625 return !aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::internal
,
626 nsGkAtoms::_true
, eCaseMatters
);
629 already_AddRefed
<dom::EventTarget
> XULKeySetGlobalKeyListener::GetHandlerTarget(
630 KeyEventHandler
* aHandler
) {
631 nsCOMPtr
<dom::Element
> commandElement
;
632 if (!GetElementForHandler(aHandler
, getter_AddRefs(commandElement
))) {
636 return commandElement
.forget();
639 bool XULKeySetGlobalKeyListener::CanHandle(KeyEventHandler
* aHandler
,
640 bool aWillExecute
) const {
641 // If the <key> element itself is disabled, ignore it.
642 if (aHandler
->KeyElementIsDisabled()) {
646 nsCOMPtr
<dom::Element
> commandElement
;
647 if (!GetElementForHandler(aHandler
, getter_AddRefs(commandElement
))) {
651 // The only case where commandElement can be null here is where the <key>
652 // element for the handler is already destroyed. I'm not sure why we continue
654 if (!commandElement
) {
658 // If we're not actually going to execute here bypass the execution check.
659 return !aWillExecute
|| IsExecutableElement(commandElement
);
663 layers::KeyboardMap
RootWindowGlobalKeyListener::CollectKeyboardShortcuts() {
664 KeyEventHandler
* handlers
= ShortcutKeys::GetHandlers(HandlerType::eBrowser
);
666 // Convert the handlers into keyboard shortcuts, using an AutoTArray with
667 // the maximum amount of shortcuts used on any platform to minimize
669 AutoTArray
<KeyboardShortcut
, 48> shortcuts
;
671 // Append keyboard shortcuts for hardcoded actions like tab
672 KeyboardShortcut::AppendHardcodedShortcuts(shortcuts
);
674 for (KeyEventHandler
* handler
= handlers
; handler
;
675 handler
= handler
->GetNextHandler()) {
676 KeyboardShortcut shortcut
;
677 if (handler
->TryConvertToKeyboardShortcut(&shortcut
)) {
678 shortcuts
.AppendElement(shortcut
);
682 return layers::KeyboardMap(std::move(shortcuts
));
686 // AttachGlobalKeyHandler
688 // Creates a new key handler and prepares to listen to key events on the given
689 // event receiver (either a document or an content node). If the receiver is
690 // content, then extra work needs to be done to hook it up to the document (XXX
693 void RootWindowGlobalKeyListener::AttachKeyHandler(dom::EventTarget
* aTarget
) {
694 EventListenerManager
* manager
= aTarget
->GetOrCreateListenerManager();
699 // Create the key handler
700 RefPtr
<RootWindowGlobalKeyListener
> handler
=
701 new RootWindowGlobalKeyListener(aTarget
);
703 // This registers handler with the manager so the manager will keep handler
704 // alive past this point.
705 handler
->InstallKeyboardEventListenersTo(manager
);
708 RootWindowGlobalKeyListener::RootWindowGlobalKeyListener(
709 dom::EventTarget
* aTarget
)
710 : GlobalKeyListener(aTarget
) {}
713 bool RootWindowGlobalKeyListener::IsHTMLEditorFocused() {
714 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
719 nsCOMPtr
<mozIDOMWindowProxy
> focusedWindow
;
720 fm
->GetFocusedWindow(getter_AddRefs(focusedWindow
));
721 if (!focusedWindow
) {
725 auto* piwin
= nsPIDOMWindowOuter::From(focusedWindow
);
726 nsIDocShell
* docShell
= piwin
->GetDocShell();
731 HTMLEditor
* htmlEditor
= docShell
->GetHTMLEditor();
736 if (htmlEditor
->IsInDesignMode()) {
737 // Don't need to perform any checks in designMode documents.
741 nsINode
* focusedNode
= fm
->GetFocusedElement();
742 if (focusedNode
&& focusedNode
->IsElement()) {
743 // If there is a focused element, make sure it's in the active editing host.
744 // Note that ComputeEditingHost finds the current editing host based on
745 // the document's selection. Even though the document selection is usually
746 // collapsed to where the focus is, but the page may modify the selection
747 // without our knowledge, in which case this check will do something useful.
748 dom::Element
* editingHost
= htmlEditor
->ComputeEditingHost();
752 return focusedNode
->IsInclusiveDescendantOf(editingHost
);
758 void RootWindowGlobalKeyListener::EnsureHandlers() {
759 if (IsHTMLEditorFocused()) {
760 mHandler
= ShortcutKeys::GetHandlers(HandlerType::eEditor
);
762 mHandler
= ShortcutKeys::GetHandlers(HandlerType::eBrowser
);
766 } // namespace mozilla