1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Original Author: David W. Hyatt (hyatt@netscape.com)
24 * - Mike Pinkerton (pinkerton@netscape.com)
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 #include "nsXBLPrototypeHandler.h"
42 #include "nsXBLWindowKeyHandler.h"
43 #include "nsIContent.h"
45 #include "nsIDOMNSUIEvent.h"
46 #include "nsIDOMKeyEvent.h"
47 #include "nsIDOMEventTarget.h"
48 #include "nsIDOMNSEvent.h"
49 #include "nsXBLService.h"
50 #include "nsIServiceManager.h"
51 #include "nsGkAtoms.h"
52 #include "nsIXBLDocumentInfo.h"
53 #include "nsIDOMElement.h"
54 #include "nsINativeKeyBindings.h"
55 #include "nsIController.h"
56 #include "nsIControllers.h"
57 #include "nsIDOMWindowInternal.h"
58 #include "nsIFocusController.h"
59 #include "nsPIWindowRoot.h"
61 #include "nsNetUtil.h"
62 #include "nsContentUtils.h"
63 #include "nsXBLPrototypeBinding.h"
64 #include "nsIDOMDocument.h"
65 #include "nsIDOMNSDocument.h"
66 #include "nsPIWindowRoot.h"
67 #include "nsPIDOMWindow.h"
68 #include "nsIFocusController.h"
69 #include "nsIDocShell.h"
70 #include "nsIPresShell.h"
71 #include "nsIPrivateDOMEvent.h"
72 #include "nsISelectionController.h"
73 #include "nsGUIEvent.h"
75 static nsINativeKeyBindings
*sNativeEditorBindings
= nsnull
;
77 class nsXBLSpecialDocInfo
80 nsCOMPtr
<nsIXBLDocumentInfo
> mHTMLBindings
;
81 nsCOMPtr
<nsIXBLDocumentInfo
> mUserHTMLBindings
;
83 static const char sHTMLBindingStr
[];
84 static const char sUserHTMLBindingStr
[];
90 void GetAllHandlers(const char* aType
,
91 nsXBLPrototypeHandler
** handler
,
92 nsXBLPrototypeHandler
** userHandler
);
93 void GetHandlers(nsIXBLDocumentInfo
* aInfo
,
94 const nsACString
& aRef
,
95 nsXBLPrototypeHandler
** aResult
);
97 nsXBLSpecialDocInfo() : mInitialized(PR_FALSE
) {}
100 const char nsXBLSpecialDocInfo::sHTMLBindingStr
[] =
101 "chrome://global/content/platformHTMLBindings.xml";
103 void nsXBLSpecialDocInfo::LoadDocInfo()
107 mInitialized
= PR_TRUE
;
110 nsCOMPtr
<nsIXBLService
> xblService
=
111 do_GetService("@mozilla.org/xbl;1", &rv
);
112 if (NS_FAILED(rv
) || !xblService
)
115 // Obtain the platform doc info
116 nsCOMPtr
<nsIURI
> bindingURI
;
117 NS_NewURI(getter_AddRefs(bindingURI
), sHTMLBindingStr
);
121 xblService
->LoadBindingDocumentInfo(nsnull
, nsnull
,
125 getter_AddRefs(mHTMLBindings
));
127 const nsAdoptingCString
& userHTMLBindingStr
=
128 nsContentUtils::GetCharPref("dom.userHTMLBindings.uri");
129 if (!userHTMLBindingStr
.IsEmpty()) {
130 NS_NewURI(getter_AddRefs(bindingURI
), userHTMLBindingStr
);
135 xblService
->LoadBindingDocumentInfo(nsnull
, nsnull
,
139 getter_AddRefs(mUserHTMLBindings
));
148 nsXBLSpecialDocInfo::GetHandlers(nsIXBLDocumentInfo
* aInfo
,
149 const nsACString
& aRef
,
150 nsXBLPrototypeHandler
** aResult
)
152 nsXBLPrototypeBinding
* binding
;
153 aInfo
->GetPrototypeBinding(aRef
, &binding
);
155 NS_ASSERTION(binding
, "No binding found for the XBL window key handler.");
159 *aResult
= binding
->GetPrototypeHandlers();
163 nsXBLSpecialDocInfo::GetAllHandlers(const char* aType
,
164 nsXBLPrototypeHandler
** aHandler
,
165 nsXBLPrototypeHandler
** aUserHandler
)
167 if (mUserHTMLBindings
) {
168 nsCAutoString
type(aType
);
170 GetHandlers(mUserHTMLBindings
, type
, aUserHandler
);
173 GetHandlers(mHTMLBindings
, nsDependentCString(aType
), aHandler
);
178 nsXBLSpecialDocInfo
* nsXBLWindowKeyHandler::sXBLSpecialDocInfo
= nsnull
;
179 PRUint32
nsXBLWindowKeyHandler::sRefCnt
= 0;
181 nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement
* aElement
,
182 nsPIDOMEventTarget
* aTarget
)
187 mWeakPtrForElement
= do_GetWeakReference(aElement
);
191 nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
193 // If mWeakPtrForElement is non-null, we created a prototype handler.
194 if (mWeakPtrForElement
)
199 delete sXBLSpecialDocInfo
;
200 sXBLSpecialDocInfo
= nsnull
;
204 NS_IMPL_ISUPPORTS2(nsXBLWindowKeyHandler
,
209 BuildHandlerChain(nsIContent
* aContent
, nsXBLPrototypeHandler
** aResult
)
213 // Since we chain each handler onto the next handler,
214 // we'll enumerate them here in reverse so that when we
215 // walk the chain they'll come out in the original order
216 for (PRUint32 j
= aContent
->GetChildCount(); j
--; ) {
217 nsIContent
*key
= aContent
->GetChildAt(j
);
219 if (key
->NodeInfo()->Equals(nsGkAtoms::key
, kNameSpaceID_XUL
)) {
220 // Check whether the key element has empty value at key/char attribute.
221 // Such element is used by localizers for alternative shortcut key
222 // definition on the locale. See bug 426501.
223 nsAutoString valKey
, valCharCode
, valKeyCode
;
225 key
->GetAttr(kNameSpaceID_None
, nsGkAtoms::key
, valKey
) ||
226 key
->GetAttr(kNameSpaceID_None
, nsGkAtoms::charcode
, valCharCode
) ||
227 key
->GetAttr(kNameSpaceID_None
, nsGkAtoms::keycode
, valKeyCode
);
229 valKey
.IsEmpty() && valCharCode
.IsEmpty() && valKeyCode
.IsEmpty())
232 nsXBLPrototypeHandler
* handler
= new nsXBLPrototypeHandler(key
);
237 handler
->SetNextHandler(*aResult
);
246 // Lazily load the XBL handlers. Overridden to handle being attached
247 // to a particular element rather than the document
250 nsXBLWindowKeyHandler::EnsureHandlers(PRBool
*aIsEditor
)
252 nsCOMPtr
<nsIDOMElement
> el
= GetElement();
253 NS_ENSURE_STATE(!mWeakPtrForElement
|| el
);
255 // We are actually a XUL <keyset>.
257 *aIsEditor
= PR_FALSE
;
262 nsCOMPtr
<nsIContent
> content(do_QueryInterface(el
));
263 BuildHandlerChain(content
, &mHandler
);
264 } else { // We are an XBL file of handlers.
265 if (!sXBLSpecialDocInfo
)
266 sXBLSpecialDocInfo
= new nsXBLSpecialDocInfo();
267 if (!sXBLSpecialDocInfo
) {
269 *aIsEditor
= PR_FALSE
;
271 return NS_ERROR_OUT_OF_MEMORY
;
273 sXBLSpecialDocInfo
->LoadDocInfo();
275 // Now determine which handlers we should be using.
276 PRBool isEditor
= IsEditor();
278 sXBLSpecialDocInfo
->GetAllHandlers("editor", &mHandler
, &mUserHandler
);
281 sXBLSpecialDocInfo
->GetAllHandlers("browser", &mHandler
, &mUserHandler
);
285 *aIsEditor
= isEditor
;
291 static nsINativeKeyBindings
*
292 GetEditorKeyBindings()
294 static PRBool noBindings
= PR_FALSE
;
295 if (!sNativeEditorBindings
&& !noBindings
) {
296 CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX
"editor",
297 &sNativeEditorBindings
);
299 if (!sNativeEditorBindings
) {
300 noBindings
= PR_TRUE
;
304 return sNativeEditorBindings
;
308 DoCommandCallback(const char *aCommand
, void *aData
)
310 nsIControllers
*controllers
= static_cast<nsIControllers
*>(aData
);
312 nsCOMPtr
<nsIController
> controller
;
313 controllers
->GetControllerForCommand(aCommand
, getter_AddRefs(controller
));
315 controller
->DoCommand(aCommand
);
321 nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent
* aKeyEvent
, nsIAtom
* aEventType
)
323 nsCOMPtr
<nsIDOMNSUIEvent
> evt
= do_QueryInterface(aKeyEvent
);
325 evt
->GetPreventDefault(&prevent
);
329 nsCOMPtr
<nsIDOMNSEvent
> domNSEvent
= do_QueryInterface(aKeyEvent
);
330 PRBool trustedEvent
= PR_FALSE
;
333 //Don't process the event if it was not dispatched from a trusted source
334 domNSEvent
->GetIsTrusted(&trustedEvent
);
341 nsresult rv
= EnsureHandlers(&isEditor
);
342 NS_ENSURE_SUCCESS(rv
, rv
);
344 nsCOMPtr
<nsIDOMElement
> el
= GetElement();
347 WalkHandlersInternal(aKeyEvent
, aEventType
, mUserHandler
);
348 evt
->GetPreventDefault(&prevent
);
350 return NS_OK
; // Handled by the user bindings. Our work here is done.
354 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(el
);
355 // skip keysets that are disabled
356 if (content
&& content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
357 nsGkAtoms::_true
, eCaseMatters
)) {
361 WalkHandlersInternal(aKeyEvent
, aEventType
, mHandler
);
363 nsINativeKeyBindings
*nativeBindings
;
364 if (isEditor
&& (nativeBindings
= GetEditorKeyBindings())) {
365 nsNativeKeyEvent nativeEvent
;
366 // get the DOM window we're attached to
367 nsCOMPtr
<nsIControllers
> controllers
;
368 nsCOMPtr
<nsPIWindowRoot
> root
= do_QueryInterface(mTarget
);
370 nsCOMPtr
<nsIFocusController
> fc
;
371 root
->GetFocusController(getter_AddRefs(fc
));
373 fc
->GetControllers(getter_AddRefs(controllers
));
377 PRBool handled
= PR_FALSE
;
378 if (aEventType
== nsGkAtoms::keypress
) {
379 if (nsContentUtils::DOMEventToNativeKeyEvent(aKeyEvent
, &nativeEvent
, PR_TRUE
))
380 handled
= sNativeEditorBindings
->KeyPress(nativeEvent
,
381 DoCommandCallback
, controllers
);
382 } else if (aEventType
== nsGkAtoms::keyup
) {
383 if (nsContentUtils::DOMEventToNativeKeyEvent(aKeyEvent
, &nativeEvent
, PR_FALSE
))
384 handled
= sNativeEditorBindings
->KeyUp(nativeEvent
,
385 DoCommandCallback
, controllers
);
387 NS_ASSERTION(aEventType
== nsGkAtoms::keydown
, "unknown key event type");
388 if (nsContentUtils::DOMEventToNativeKeyEvent(aKeyEvent
, &nativeEvent
, PR_FALSE
))
389 handled
= sNativeEditorBindings
->KeyDown(nativeEvent
,
390 DoCommandCallback
, controllers
);
394 aKeyEvent
->PreventDefault();
401 nsresult
nsXBLWindowKeyHandler::KeyUp(nsIDOMEvent
* aEvent
)
403 nsCOMPtr
<nsIDOMKeyEvent
> keyEvent(do_QueryInterface(aEvent
));
404 NS_ENSURE_TRUE(keyEvent
, NS_ERROR_INVALID_ARG
);
405 return WalkHandlers(keyEvent
, nsGkAtoms::keyup
);
408 nsresult
nsXBLWindowKeyHandler::KeyDown(nsIDOMEvent
* aEvent
)
410 nsCOMPtr
<nsIDOMKeyEvent
> keyEvent(do_QueryInterface(aEvent
));
411 NS_ENSURE_TRUE(keyEvent
, NS_ERROR_INVALID_ARG
);
412 return WalkHandlers(keyEvent
, nsGkAtoms::keydown
);
415 nsresult
nsXBLWindowKeyHandler::KeyPress(nsIDOMEvent
* aEvent
)
417 nsCOMPtr
<nsIDOMKeyEvent
> keyEvent(do_QueryInterface(aEvent
));
418 NS_ENSURE_TRUE(keyEvent
, NS_ERROR_INVALID_ARG
);
419 return WalkHandlers(keyEvent
, nsGkAtoms::keypress
);
426 // See if the given handler cares about this particular key event
429 nsXBLWindowKeyHandler::EventMatched(nsXBLPrototypeHandler
* inHandler
,
430 nsIAtom
* inEventType
,
431 nsIDOMKeyEvent
* inEvent
,
432 PRUint32 aCharCode
, PRBool aIgnoreShiftKey
)
434 return inHandler
->KeyEventMatched(inEventType
, inEvent
, aCharCode
,
439 nsXBLWindowKeyHandler::ShutDown()
441 NS_IF_RELEASE(sNativeEditorBindings
);
447 // Determine if the document we're working with is Editor or Browser
450 nsXBLWindowKeyHandler::IsEditor()
452 nsCOMPtr
<nsPIWindowRoot
> windowRoot(do_QueryInterface(mTarget
));
453 NS_ENSURE_TRUE(windowRoot
, PR_FALSE
);
454 nsCOMPtr
<nsIFocusController
> focusController
;
455 windowRoot
->GetFocusController(getter_AddRefs(focusController
));
456 if (!focusController
) {
457 NS_WARNING("********* Something went wrong! No focus controller on the root!!!\n");
461 nsCOMPtr
<nsIDOMWindowInternal
> focusedWindow
;
462 focusController
->GetFocusedWindow(getter_AddRefs(focusedWindow
));
466 nsCOMPtr
<nsPIDOMWindow
> piwin(do_QueryInterface(focusedWindow
));
467 nsIDocShell
*docShell
= piwin
->GetDocShell();
468 nsCOMPtr
<nsIPresShell
> presShell
;
470 docShell
->GetPresShell(getter_AddRefs(presShell
));
474 presShell
->GetSelectionFlags(&isEditor
);
475 return isEditor
== nsISelectionDisplay::DISPLAY_ALL
;
482 // WalkHandlersInternal and WalkHandlersAndExecute
484 // Given a particular DOM event and a pointer to the first handler in the list,
485 // scan through the list to find something to handle the event and then make it
489 nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent
* aKeyEvent
,
491 nsXBLPrototypeHandler
* aHandler
)
493 nsAutoTArray
<nsShortcutCandidate
, 10> accessKeys
;
494 nsContentUtils::GetAccelKeyCandidates(aKeyEvent
, accessKeys
);
496 if (accessKeys
.IsEmpty()) {
497 WalkHandlersAndExecute(aKeyEvent
, aEventType
, aHandler
, 0, PR_FALSE
);
501 for (PRUint32 i
= 0; i
< accessKeys
.Length(); ++i
) {
502 nsShortcutCandidate
&key
= accessKeys
[i
];
503 if (WalkHandlersAndExecute(aKeyEvent
, aEventType
, aHandler
,
504 key
.mCharCode
, key
.mIgnoreShift
))
511 nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent
* aKeyEvent
,
513 nsXBLPrototypeHandler
* aHandler
,
515 PRBool aIgnoreShiftKey
)
518 nsCOMPtr
<nsIPrivateDOMEvent
> privateEvent(do_QueryInterface(aKeyEvent
));
520 // Try all of the handlers until we find one that matches the event.
521 for (nsXBLPrototypeHandler
*currHandler
= aHandler
; currHandler
;
522 currHandler
= currHandler
->GetNextHandler()) {
523 PRBool stopped
= privateEvent
->IsDispatchStopped();
525 // The event is finished, don't execute any more handlers
529 if (!EventMatched(currHandler
, aEventType
, aKeyEvent
,
530 aCharCode
, aIgnoreShiftKey
))
531 continue; // try the next one
533 // Before executing this handler, check that it's not disabled,
534 // and that it has something to do (oncommand of the <key> or its
535 // <command> is non-empty).
536 nsCOMPtr
<nsIContent
> elt
= currHandler
->GetHandlerElement();
537 nsCOMPtr
<nsIDOMElement
> commandElt
;
539 // See if we're in a XUL doc.
540 nsCOMPtr
<nsIDOMElement
> el
= GetElement();
542 // We are. Obtain our command attribute.
543 nsAutoString command
;
544 elt
->GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, command
);
545 if (!command
.IsEmpty()) {
546 // Locate the command element in question. Note that we
547 // know "elt" is in a doc if we're dealing with it here.
548 NS_ASSERTION(elt
->IsInDoc(), "elt must be in document");
549 nsCOMPtr
<nsIDOMDocument
> domDoc(
550 do_QueryInterface(elt
->GetCurrentDoc()));
552 domDoc
->GetElementById(command
, getter_AddRefs(commandElt
));
555 NS_ERROR("A XUL <key> is observing a command that doesn't exist. Unable to execute key binding!\n");
562 commandElt
= do_QueryInterface(elt
);
567 commandElt
->GetAttribute(NS_LITERAL_STRING("disabled"), value
);
568 if (value
.EqualsLiteral("true")) {
569 continue; // this handler is disabled, try the next one
572 // Check that there is an oncommand handler
573 commandElt
->GetAttribute(NS_LITERAL_STRING("oncommand"), value
);
574 if (value
.IsEmpty()) {
575 continue; // nothing to do
579 nsCOMPtr
<nsPIDOMEventTarget
> piTarget
;
580 nsCOMPtr
<nsIDOMElement
> element
= GetElement();
582 piTarget
= do_QueryInterface(commandElt
);
587 rv
= currHandler
->ExecuteHandler(piTarget
, aKeyEvent
);
588 if (NS_SUCCEEDED(rv
)) {
596 already_AddRefed
<nsIDOMElement
>
597 nsXBLWindowKeyHandler::GetElement()
599 nsCOMPtr
<nsIDOMElement
> element
= do_QueryReferent(mWeakPtrForElement
);
600 nsIDOMElement
* el
= nsnull
;
605 ///////////////////////////////////////////////////////////////////////////////////
608 NS_NewXBLWindowKeyHandler(nsIDOMElement
* aElement
, nsPIDOMEventTarget
* aTarget
,
609 nsXBLWindowKeyHandler
** aResult
)
611 *aResult
= new nsXBLWindowKeyHandler(aElement
, aTarget
);
613 return NS_ERROR_OUT_OF_MEMORY
;