Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / content / xbl / src / nsXBLWindowKeyHandler.cpp
blob2c16d1b0228856c13bda6543eff19089b3ca632f
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
40 #include "nsCOMPtr.h"
41 #include "nsXBLPrototypeHandler.h"
42 #include "nsXBLWindowKeyHandler.h"
43 #include "nsIContent.h"
44 #include "nsIAtom.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"
60 #include "nsIURI.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
79 public:
80 nsCOMPtr<nsIXBLDocumentInfo> mHTMLBindings;
81 nsCOMPtr<nsIXBLDocumentInfo> mUserHTMLBindings;
83 static const char sHTMLBindingStr[];
84 static const char sUserHTMLBindingStr[];
86 PRBool mInitialized;
88 public:
89 void LoadDocInfo();
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()
105 if (mInitialized)
106 return;
107 mInitialized = PR_TRUE;
109 nsresult rv;
110 nsCOMPtr<nsIXBLService> xblService =
111 do_GetService("@mozilla.org/xbl;1", &rv);
112 if (NS_FAILED(rv) || !xblService)
113 return;
115 // Obtain the platform doc info
116 nsCOMPtr<nsIURI> bindingURI;
117 NS_NewURI(getter_AddRefs(bindingURI), sHTMLBindingStr);
118 if (!bindingURI) {
119 return;
121 xblService->LoadBindingDocumentInfo(nsnull, nsnull,
122 bindingURI,
123 nsnull,
124 PR_TRUE,
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);
131 if (!bindingURI) {
132 return;
135 xblService->LoadBindingDocumentInfo(nsnull, nsnull,
136 bindingURI,
137 nsnull,
138 PR_TRUE,
139 getter_AddRefs(mUserHTMLBindings));
144 // GetHandlers
147 void
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.");
156 if (!binding)
157 return;
159 *aResult = binding->GetPrototypeHandlers();
162 void
163 nsXBLSpecialDocInfo::GetAllHandlers(const char* aType,
164 nsXBLPrototypeHandler** aHandler,
165 nsXBLPrototypeHandler** aUserHandler)
167 if (mUserHTMLBindings) {
168 nsCAutoString type(aType);
169 type.Append("User");
170 GetHandlers(mUserHTMLBindings, type, aUserHandler);
172 if (mHTMLBindings) {
173 GetHandlers(mHTMLBindings, nsDependentCString(aType), aHandler);
177 // Init statics
178 nsXBLSpecialDocInfo* nsXBLWindowKeyHandler::sXBLSpecialDocInfo = nsnull;
179 PRUint32 nsXBLWindowKeyHandler::sRefCnt = 0;
181 nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement* aElement,
182 nsPIDOMEventTarget* aTarget)
183 : mTarget(aTarget),
184 mHandler(nsnull),
185 mUserHandler(nsnull)
187 mWeakPtrForElement = do_GetWeakReference(aElement);
188 ++sRefCnt;
191 nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
193 // If mWeakPtrForElement is non-null, we created a prototype handler.
194 if (mWeakPtrForElement)
195 delete mHandler;
197 --sRefCnt;
198 if (!sRefCnt) {
199 delete sXBLSpecialDocInfo;
200 sXBLSpecialDocInfo = nsnull;
204 NS_IMPL_ISUPPORTS2(nsXBLWindowKeyHandler,
205 nsIDOMKeyListener,
206 nsIDOMEventListener)
208 static void
209 BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)
211 *aResult = nsnull;
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;
224 PRBool attrExists =
225 key->GetAttr(kNameSpaceID_None, nsGkAtoms::key, valKey) ||
226 key->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, valCharCode) ||
227 key->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, valKeyCode);
228 if (attrExists &&
229 valKey.IsEmpty() && valCharCode.IsEmpty() && valKeyCode.IsEmpty())
230 continue;
232 nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(key);
234 if (!handler)
235 return;
237 handler->SetNextHandler(*aResult);
238 *aResult = handler;
244 // EnsureHandlers
246 // Lazily load the XBL handlers. Overridden to handle being attached
247 // to a particular element rather than the document
249 nsresult
250 nsXBLWindowKeyHandler::EnsureHandlers(PRBool *aIsEditor)
252 nsCOMPtr<nsIDOMElement> el = GetElement();
253 NS_ENSURE_STATE(!mWeakPtrForElement || el);
254 if (el) {
255 // We are actually a XUL <keyset>.
256 if (aIsEditor)
257 *aIsEditor = PR_FALSE;
259 if (mHandler)
260 return NS_OK;
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) {
268 if (aIsEditor) {
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();
277 if (isEditor) {
278 sXBLSpecialDocInfo->GetAllHandlers("editor", &mHandler, &mUserHandler);
280 else {
281 sXBLSpecialDocInfo->GetAllHandlers("browser", &mHandler, &mUserHandler);
284 if (aIsEditor)
285 *aIsEditor = isEditor;
288 return NS_OK;
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;
307 static void
308 DoCommandCallback(const char *aCommand, void *aData)
310 nsIControllers *controllers = static_cast<nsIControllers*>(aData);
311 if (controllers) {
312 nsCOMPtr<nsIController> controller;
313 controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
314 if (controller) {
315 controller->DoCommand(aCommand);
320 nsresult
321 nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType)
323 nsCOMPtr<nsIDOMNSUIEvent> evt = do_QueryInterface(aKeyEvent);
324 PRBool prevent;
325 evt->GetPreventDefault(&prevent);
326 if (prevent)
327 return NS_OK;
329 nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
330 PRBool trustedEvent = PR_FALSE;
332 if (domNSEvent) {
333 //Don't process the event if it was not dispatched from a trusted source
334 domNSEvent->GetIsTrusted(&trustedEvent);
337 if (!trustedEvent)
338 return NS_OK;
340 PRBool isEditor;
341 nsresult rv = EnsureHandlers(&isEditor);
342 NS_ENSURE_SUCCESS(rv, rv);
344 nsCOMPtr<nsIDOMElement> el = GetElement();
345 if (!el) {
346 if (mUserHandler) {
347 WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler);
348 evt->GetPreventDefault(&prevent);
349 if (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)) {
358 return NS_OK;
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);
369 if (root) {
370 nsCOMPtr<nsIFocusController> fc;
371 root->GetFocusController(getter_AddRefs(fc));
372 if (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);
386 } else {
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);
393 if (handled)
394 aKeyEvent->PreventDefault();
398 return NS_OK;
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);
424 // EventMatched
426 // See if the given handler cares about this particular key event
428 PRBool
429 nsXBLWindowKeyHandler::EventMatched(nsXBLPrototypeHandler* inHandler,
430 nsIAtom* inEventType,
431 nsIDOMKeyEvent* inEvent,
432 PRUint32 aCharCode, PRBool aIgnoreShiftKey)
434 return inHandler->KeyEventMatched(inEventType, inEvent, aCharCode,
435 aIgnoreShiftKey);
438 /* static */ void
439 nsXBLWindowKeyHandler::ShutDown()
441 NS_IF_RELEASE(sNativeEditorBindings);
445 // IsEditor
447 // Determine if the document we're working with is Editor or Browser
449 PRBool
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");
458 return PR_FALSE;
461 nsCOMPtr<nsIDOMWindowInternal> focusedWindow;
462 focusController->GetFocusedWindow(getter_AddRefs(focusedWindow));
463 if (!focusedWindow)
464 return PR_FALSE;
466 nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(focusedWindow));
467 nsIDocShell *docShell = piwin->GetDocShell();
468 nsCOMPtr<nsIPresShell> presShell;
469 if (docShell)
470 docShell->GetPresShell(getter_AddRefs(presShell));
472 if (presShell) {
473 PRInt16 isEditor;
474 presShell->GetSelectionFlags(&isEditor);
475 return isEditor == nsISelectionDisplay::DISPLAY_ALL;
478 return PR_FALSE;
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
486 // so.
488 nsresult
489 nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
490 nsIAtom* aEventType,
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);
498 return NS_OK;
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))
505 return NS_OK;
507 return NS_OK;
510 PRBool
511 nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
512 nsIAtom* aEventType,
513 nsXBLPrototypeHandler* aHandler,
514 PRUint32 aCharCode,
515 PRBool aIgnoreShiftKey)
517 nsresult rv;
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();
524 if (stopped) {
525 // The event is finished, don't execute any more handlers
526 return NS_OK;
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();
541 if (el && elt) {
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()));
551 if (domDoc)
552 domDoc->GetElementById(command, getter_AddRefs(commandElt));
554 if (!commandElt) {
555 NS_ERROR("A XUL <key> is observing a command that doesn't exist. Unable to execute key binding!\n");
556 continue;
561 if (!commandElt) {
562 commandElt = do_QueryInterface(elt);
565 if (commandElt) {
566 nsAutoString value;
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();
581 if (element) {
582 piTarget = do_QueryInterface(commandElt);
583 } else {
584 piTarget = mTarget;
587 rv = currHandler->ExecuteHandler(piTarget, aKeyEvent);
588 if (NS_SUCCEEDED(rv)) {
589 return PR_TRUE;
593 return PR_FALSE;
596 already_AddRefed<nsIDOMElement>
597 nsXBLWindowKeyHandler::GetElement()
599 nsCOMPtr<nsIDOMElement> element = do_QueryReferent(mWeakPtrForElement);
600 nsIDOMElement* el = nsnull;
601 element.swap(el);
602 return el;
605 ///////////////////////////////////////////////////////////////////////////////////
607 nsresult
608 NS_NewXBLWindowKeyHandler(nsIDOMElement* aElement, nsPIDOMEventTarget* aTarget,
609 nsXBLWindowKeyHandler** aResult)
611 *aResult = new nsXBLWindowKeyHandler(aElement, aTarget);
612 if (!*aResult)
613 return NS_ERROR_OUT_OF_MEMORY;
614 NS_ADDREF(*aResult);
615 return NS_OK;