1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsXBLPrototypeHandler.h"
8 #include "nsXBLWindowKeyHandler.h"
9 #include "nsIContent.h"
11 #include "nsIDOMKeyEvent.h"
12 #include "nsXBLService.h"
13 #include "nsIServiceManager.h"
14 #include "nsGkAtoms.h"
15 #include "nsXBLDocumentInfo.h"
16 #include "nsIDOMElement.h"
17 #include "nsFocusManager.h"
19 #include "nsNetUtil.h"
20 #include "nsContentUtils.h"
21 #include "nsXBLPrototypeBinding.h"
22 #include "nsPIDOMWindow.h"
23 #include "nsIDocShell.h"
24 #include "nsIPresShell.h"
25 #include "mozilla/EventStateManager.h"
26 #include "nsISelectionController.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/TextEvents.h"
29 #include "mozilla/dom/Element.h"
30 #include "mozilla/dom/Event.h"
31 #include "nsIEditor.h"
32 #include "nsIHTMLEditor.h"
33 #include "nsIDOMDocument.h"
35 using namespace mozilla
;
36 using namespace mozilla::dom
;
38 class nsXBLSpecialDocInfo
: public nsIObserver
41 nsRefPtr
<nsXBLDocumentInfo
> mHTMLBindings
;
42 nsRefPtr
<nsXBLDocumentInfo
> mUserHTMLBindings
;
44 static const char sHTMLBindingStr
[];
45 static const char sUserHTMLBindingStr
[];
54 void GetAllHandlers(const char* aType
,
55 nsXBLPrototypeHandler
** handler
,
56 nsXBLPrototypeHandler
** userHandler
);
57 void GetHandlers(nsXBLDocumentInfo
* aInfo
,
58 const nsACString
& aRef
,
59 nsXBLPrototypeHandler
** aResult
);
61 nsXBLSpecialDocInfo() : mInitialized(false) {}
64 virtual ~nsXBLSpecialDocInfo() {}
68 const char nsXBLSpecialDocInfo::sHTMLBindingStr
[] =
69 "chrome://global/content/platformHTMLBindings.xml";
71 NS_IMPL_ISUPPORTS(nsXBLSpecialDocInfo
, nsIObserver
)
74 nsXBLSpecialDocInfo::Observe(nsISupports
* aSubject
,
76 const char16_t
* aData
)
78 MOZ_ASSERT(!strcmp(aTopic
, "xpcom-shutdown"), "wrong topic");
80 // On shutdown, clear our fields to avoid an extra cycle collection.
81 mHTMLBindings
= nullptr;
82 mUserHTMLBindings
= nullptr;
84 nsContentUtils::UnregisterShutdownObserver(this);
89 void nsXBLSpecialDocInfo::LoadDocInfo()
94 nsContentUtils::RegisterShutdownObserver(this);
96 nsXBLService
* xblService
= nsXBLService::GetInstance();
100 // Obtain the platform doc info
101 nsCOMPtr
<nsIURI
> bindingURI
;
102 NS_NewURI(getter_AddRefs(bindingURI
), sHTMLBindingStr
);
106 xblService
->LoadBindingDocumentInfo(nullptr, nullptr,
110 getter_AddRefs(mHTMLBindings
));
112 const nsAdoptingCString
& userHTMLBindingStr
=
113 Preferences::GetCString("dom.userHTMLBindings.uri");
114 if (!userHTMLBindingStr
.IsEmpty()) {
115 NS_NewURI(getter_AddRefs(bindingURI
), userHTMLBindingStr
);
120 xblService
->LoadBindingDocumentInfo(nullptr, nullptr,
124 getter_AddRefs(mUserHTMLBindings
));
133 nsXBLSpecialDocInfo::GetHandlers(nsXBLDocumentInfo
* aInfo
,
134 const nsACString
& aRef
,
135 nsXBLPrototypeHandler
** aResult
)
137 nsXBLPrototypeBinding
* binding
= aInfo
->GetPrototypeBinding(aRef
);
139 NS_ASSERTION(binding
, "No binding found for the XBL window key handler.");
143 *aResult
= binding
->GetPrototypeHandlers();
147 nsXBLSpecialDocInfo::GetAllHandlers(const char* aType
,
148 nsXBLPrototypeHandler
** aHandler
,
149 nsXBLPrototypeHandler
** aUserHandler
)
151 if (mUserHTMLBindings
) {
152 nsAutoCString
type(aType
);
153 type
.AppendLiteral("User");
154 GetHandlers(mUserHTMLBindings
, type
, aUserHandler
);
157 GetHandlers(mHTMLBindings
, nsDependentCString(aType
), aHandler
);
162 nsXBLSpecialDocInfo
* nsXBLWindowKeyHandler::sXBLSpecialDocInfo
= nullptr;
163 uint32_t nsXBLWindowKeyHandler::sRefCnt
= 0;
165 nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement
* aElement
,
166 EventTarget
* aTarget
)
169 mUserHandler(nullptr)
171 mWeakPtrForElement
= do_GetWeakReference(aElement
);
175 nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
177 // If mWeakPtrForElement is non-null, we created a prototype handler.
178 if (mWeakPtrForElement
)
183 NS_IF_RELEASE(sXBLSpecialDocInfo
);
187 NS_IMPL_ISUPPORTS(nsXBLWindowKeyHandler
,
191 BuildHandlerChain(nsIContent
* aContent
, nsXBLPrototypeHandler
** aResult
)
195 // Since we chain each handler onto the next handler,
196 // we'll enumerate them here in reverse so that when we
197 // walk the chain they'll come out in the original order
198 for (nsIContent
* key
= aContent
->GetLastChild();
200 key
= key
->GetPreviousSibling()) {
202 if (key
->NodeInfo()->Equals(nsGkAtoms::key
, kNameSpaceID_XUL
)) {
203 // Check whether the key element has empty value at key/char attribute.
204 // Such element is used by localizers for alternative shortcut key
205 // definition on the locale. See bug 426501.
206 nsAutoString valKey
, valCharCode
, valKeyCode
;
208 key
->GetAttr(kNameSpaceID_None
, nsGkAtoms::key
, valKey
) ||
209 key
->GetAttr(kNameSpaceID_None
, nsGkAtoms::charcode
, valCharCode
) ||
210 key
->GetAttr(kNameSpaceID_None
, nsGkAtoms::keycode
, valKeyCode
);
212 valKey
.IsEmpty() && valCharCode
.IsEmpty() && valKeyCode
.IsEmpty())
215 nsXBLPrototypeHandler
* handler
= new nsXBLPrototypeHandler(key
);
220 handler
->SetNextHandler(*aResult
);
229 // Lazily load the XBL handlers. Overridden to handle being attached
230 // to a particular element rather than the document
233 nsXBLWindowKeyHandler::EnsureHandlers()
235 nsCOMPtr
<Element
> el
= GetElement();
236 NS_ENSURE_STATE(!mWeakPtrForElement
|| el
);
238 // We are actually a XUL <keyset>.
242 nsCOMPtr
<nsIContent
> content(do_QueryInterface(el
));
243 BuildHandlerChain(content
, &mHandler
);
244 } else { // We are an XBL file of handlers.
245 if (!sXBLSpecialDocInfo
) {
246 sXBLSpecialDocInfo
= new nsXBLSpecialDocInfo();
247 NS_ADDREF(sXBLSpecialDocInfo
);
249 sXBLSpecialDocInfo
->LoadDocInfo();
251 // Now determine which handlers we should be using.
252 if (IsHTMLEditableFieldFocused()) {
253 sXBLSpecialDocInfo
->GetAllHandlers("editor", &mHandler
, &mUserHandler
);
256 sXBLSpecialDocInfo
->GetAllHandlers("browser", &mHandler
, &mUserHandler
);
264 nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent
* aKeyEvent
, nsIAtom
* aEventType
)
267 aKeyEvent
->GetDefaultPrevented(&prevent
);
271 bool trustedEvent
= false;
272 // Don't process the event if it was not dispatched from a trusted source
273 aKeyEvent
->GetIsTrusted(&trustedEvent
);
278 nsresult rv
= EnsureHandlers();
279 NS_ENSURE_SUCCESS(rv
, rv
);
282 nsCOMPtr
<Element
> el
= GetElement(&isDisabled
);
285 WalkHandlersInternal(aKeyEvent
, aEventType
, mUserHandler
, true);
286 aKeyEvent
->GetDefaultPrevented(&prevent
);
288 return NS_OK
; // Handled by the user bindings. Our work here is done.
292 // skip keysets that are disabled
293 if (el
&& isDisabled
) {
297 WalkHandlersInternal(aKeyEvent
, aEventType
, mHandler
, true);
303 nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent
* aEvent
)
305 nsCOMPtr
<nsIDOMKeyEvent
> keyEvent(do_QueryInterface(aEvent
));
306 NS_ENSURE_TRUE(keyEvent
, NS_ERROR_INVALID_ARG
);
309 aEvent
->GetEventPhase(&eventPhase
);
310 if (eventPhase
== nsIDOMEvent::CAPTURING_PHASE
) {
311 HandleEventOnCapture(keyEvent
);
315 nsAutoString eventType
;
316 aEvent
->GetType(eventType
);
317 nsCOMPtr
<nsIAtom
> eventTypeAtom
= do_GetAtom(eventType
);
318 NS_ENSURE_TRUE(eventTypeAtom
, NS_ERROR_OUT_OF_MEMORY
);
320 return WalkHandlers(keyEvent
, eventTypeAtom
);
324 nsXBLWindowKeyHandler::HandleEventOnCapture(nsIDOMKeyEvent
* aEvent
)
326 WidgetKeyboardEvent
* widgetEvent
=
327 aEvent
->GetInternalNSEvent()->AsKeyboardEvent();
329 if (widgetEvent
->mFlags
.mNoCrossProcessBoundaryForwarding
) {
333 nsCOMPtr
<mozilla::dom::Element
> originalTarget
=
334 do_QueryInterface(aEvent
->GetInternalNSEvent()->originalTarget
);
335 if (!EventStateManager::IsRemoteTarget(originalTarget
)) {
339 if (!HasHandlerForEvent(aEvent
)) {
343 // If this event hasn't been marked as mNoCrossProcessBoundaryForwarding
344 // yet, it means it wasn't processed by content. We'll not call any
345 // of the handlers at this moment, and will wait for the event to be
346 // redispatched with mNoCrossProcessBoundaryForwarding = 1 to process it.
348 // Inform the child process that this is a event that we want a reply
350 widgetEvent
->mFlags
.mWantReplyFromContentProcess
= 1;
351 aEvent
->StopPropagation();
357 // See if the given handler cares about this particular key event
360 nsXBLWindowKeyHandler::EventMatched(nsXBLPrototypeHandler
* inHandler
,
361 nsIAtom
* inEventType
,
362 nsIDOMKeyEvent
* inEvent
,
363 uint32_t aCharCode
, bool aIgnoreShiftKey
)
365 return inHandler
->KeyEventMatched(inEventType
, inEvent
, aCharCode
,
370 nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
372 nsIFocusManager
* fm
= nsFocusManager::GetFocusManager();
376 nsCOMPtr
<nsIDOMWindow
> focusedWindow
;
377 fm
->GetFocusedWindow(getter_AddRefs(focusedWindow
));
381 nsCOMPtr
<nsPIDOMWindow
> piwin(do_QueryInterface(focusedWindow
));
382 nsIDocShell
*docShell
= piwin
->GetDocShell();
387 nsCOMPtr
<nsIEditor
> editor
;
388 docShell
->GetEditor(getter_AddRefs(editor
));
389 nsCOMPtr
<nsIHTMLEditor
> htmlEditor
= do_QueryInterface(editor
);
394 nsCOMPtr
<nsIDOMDocument
> domDocument
;
395 editor
->GetDocument(getter_AddRefs(domDocument
));
396 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(domDocument
);
397 if (doc
->HasFlag(NODE_IS_EDITABLE
)) {
398 // Don't need to perform any checks in designMode documents.
402 nsCOMPtr
<nsIDOMElement
> focusedElement
;
403 fm
->GetFocusedElement(getter_AddRefs(focusedElement
));
404 nsCOMPtr
<nsINode
> focusedNode
= do_QueryInterface(focusedElement
);
406 // If there is a focused element, make sure it's in the active editing host.
407 // Note that GetActiveEditingHost finds the current editing host based on
408 // the document's selection. Even though the document selection is usually
409 // collapsed to where the focus is, but the page may modify the selection
410 // without our knowledge, in which case this check will do something useful.
411 nsCOMPtr
<Element
> activeEditingHost
= htmlEditor
->GetActiveEditingHost();
412 if (!activeEditingHost
) {
415 return nsContentUtils::ContentIsDescendantOf(focusedNode
, activeEditingHost
);
422 // WalkHandlersInternal and WalkHandlersAndExecute
424 // Given a particular DOM event and a pointer to the first handler in the list,
425 // scan through the list to find something to handle the event. If aExecute = true,
426 // the handler will be executed; otherwise just return an answer telling if a handler
427 // for that event was found.
430 nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent
* aKeyEvent
,
432 nsXBLPrototypeHandler
* aHandler
,
435 nsAutoTArray
<nsShortcutCandidate
, 10> accessKeys
;
436 nsContentUtils::GetAccelKeyCandidates(aKeyEvent
, accessKeys
);
438 if (accessKeys
.IsEmpty()) {
439 return WalkHandlersAndExecute(aKeyEvent
, aEventType
, aHandler
,
443 for (uint32_t i
= 0; i
< accessKeys
.Length(); ++i
) {
444 nsShortcutCandidate
&key
= accessKeys
[i
];
445 if (WalkHandlersAndExecute(aKeyEvent
, aEventType
, aHandler
,
446 key
.mCharCode
, key
.mIgnoreShift
, aExecute
))
453 nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent
* aKeyEvent
,
455 nsXBLPrototypeHandler
* aHandler
,
457 bool aIgnoreShiftKey
,
462 // Try all of the handlers until we find one that matches the event.
463 for (nsXBLPrototypeHandler
*currHandler
= aHandler
; currHandler
;
464 currHandler
= currHandler
->GetNextHandler()) {
465 bool stopped
= aKeyEvent
->IsDispatchStopped();
467 // The event is finished, don't execute any more handlers
471 if (!EventMatched(currHandler
, aEventType
, aKeyEvent
,
472 aCharCode
, aIgnoreShiftKey
))
473 continue; // try the next one
475 // Before executing this handler, check that it's not disabled,
476 // and that it has something to do (oncommand of the <key> or its
477 // <command> is non-empty).
478 nsCOMPtr
<nsIContent
> elt
= currHandler
->GetHandlerElement();
479 nsCOMPtr
<Element
> commandElt
;
481 // See if we're in a XUL doc.
482 nsCOMPtr
<Element
> el
= GetElement();
484 // We are. Obtain our command attribute.
485 nsAutoString command
;
486 elt
->GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, command
);
487 if (!command
.IsEmpty()) {
488 // Locate the command element in question. Note that we
489 // know "elt" is in a doc if we're dealing with it here.
490 NS_ASSERTION(elt
->IsInDoc(), "elt must be in document");
491 nsIDocument
*doc
= elt
->GetCurrentDoc();
493 commandElt
= do_QueryInterface(doc
->GetElementById(command
));
496 NS_ERROR("A XUL <key> is observing a command that doesn't exist. Unable to execute key binding!");
503 commandElt
= do_QueryInterface(elt
);
508 commandElt
->GetAttribute(NS_LITERAL_STRING("disabled"), value
);
509 if (value
.EqualsLiteral("true")) {
510 continue; // this handler is disabled, try the next one
513 // Check that there is an oncommand handler
514 commandElt
->GetAttribute(NS_LITERAL_STRING("oncommand"), value
);
515 if (value
.IsEmpty()) {
516 continue; // nothing to do
520 nsCOMPtr
<EventTarget
> piTarget
;
521 nsCOMPtr
<Element
> element
= GetElement();
523 piTarget
= commandElt
;
532 rv
= currHandler
->ExecuteHandler(piTarget
, aKeyEvent
);
533 if (NS_SUCCEEDED(rv
)) {
542 nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent
* aEvent
)
544 if (!aEvent
->InternalDOMEvent()->IsTrusted()) {
548 nsresult rv
= EnsureHandlers();
549 NS_ENSURE_SUCCESS(rv
, false);
552 nsCOMPtr
<Element
> el
= GetElement(&isDisabled
);
553 if (el
&& isDisabled
) {
557 nsAutoString eventType
;
558 aEvent
->GetType(eventType
);
559 nsCOMPtr
<nsIAtom
> eventTypeAtom
= do_GetAtom(eventType
);
560 NS_ENSURE_TRUE(eventTypeAtom
, false);
562 return WalkHandlersInternal(aEvent
, eventTypeAtom
, mHandler
, false);
565 already_AddRefed
<Element
>
566 nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled
)
568 nsCOMPtr
<Element
> element
= do_QueryReferent(mWeakPtrForElement
);
569 if (element
&& aIsDisabled
) {
570 *aIsDisabled
= element
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
571 nsGkAtoms::_true
, eCaseMatters
);
573 return element
.forget();
576 ///////////////////////////////////////////////////////////////////////////////////
578 already_AddRefed
<nsXBLWindowKeyHandler
>
579 NS_NewXBLWindowKeyHandler(nsIDOMElement
* aElement
, EventTarget
* aTarget
)
581 nsRefPtr
<nsXBLWindowKeyHandler
> result
=
582 new nsXBLWindowKeyHandler(aElement
, aTarget
);
583 return result
.forget();