Back to index

lightning-sunbird  0.9+nobinonly
nsXBLPrototypeHandler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Original Author: David W. Hyatt (hyatt@netscape.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsXBLPrototypeHandler.h"
00041 #include "nsXBLPrototypeBinding.h"
00042 #include "nsContentUtils.h"
00043 #include "nsIContent.h"
00044 #include "nsIAtom.h"
00045 #include "nsIDOMKeyEvent.h"
00046 #include "nsIDOMMouseEvent.h"
00047 #include "nsINameSpaceManager.h"
00048 #include "nsIScriptContext.h"
00049 #include "nsIScriptGlobalObject.h"
00050 #include "nsIDocument.h"
00051 #include "nsIDOMDocument.h"
00052 #include "nsIJSEventListener.h"
00053 #include "nsIController.h"
00054 #include "nsIControllers.h"
00055 #include "nsIDOMXULElement.h"
00056 #include "nsIDOMNSUIEvent.h"
00057 #include "nsIURI.h"
00058 #include "nsIDOMNSHTMLTextAreaElement.h"
00059 #include "nsIDOMNSHTMLInputElement.h"
00060 #include "nsIDOMText.h"
00061 #include "nsIFocusController.h"
00062 #include "nsIEventListenerManager.h"
00063 #include "nsIDOMEventReceiver.h"
00064 #include "nsIDOMEventListener.h"
00065 #include "nsIPrivateDOMEvent.h"
00066 #include "nsIDOMNSEvent.h"
00067 #include "nsPIDOMWindow.h"
00068 #include "nsPIWindowRoot.h"
00069 #include "nsIDOMWindowInternal.h"
00070 #include "nsIServiceManager.h"
00071 #include "nsXPIDLString.h"
00072 #include "nsReadableUtils.h"
00073 #include "nsXULAtoms.h"
00074 #include "nsXBLAtoms.h"
00075 #include "nsLayoutAtoms.h"
00076 #include "nsGUIEvent.h"
00077 #include "nsIXPConnect.h"
00078 #include "nsIDOMScriptObjectFactory.h"
00079 #include "nsDOMCID.h"
00080 #include "nsUnicharUtils.h"
00081 #include "nsReadableUtils.h"
00082 #include "nsCRT.h"
00083 #include "nsXBLEventHandler.h"
00084 #include "nsHTMLAtoms.h"
00085 #include "nsIBoxObject.h"
00086 #include "nsIDOMNSDocument.h"
00087 
00088 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
00089                      NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
00090 
00091 PRUint32 nsXBLPrototypeHandler::gRefCnt = 0;
00092 
00093 PRInt32 nsXBLPrototypeHandler::kMenuAccessKey = -1;
00094 PRInt32 nsXBLPrototypeHandler::kAccelKey = -1;
00095 
00096 const PRInt32 nsXBLPrototypeHandler::cShift = (1<<0);
00097 const PRInt32 nsXBLPrototypeHandler::cAlt = (1<<1);
00098 const PRInt32 nsXBLPrototypeHandler::cControl = (1<<2);
00099 const PRInt32 nsXBLPrototypeHandler::cMeta = (1<<3);
00100 
00101 const PRInt32 nsXBLPrototypeHandler::cShiftMask = (1<<4);
00102 const PRInt32 nsXBLPrototypeHandler::cAltMask = (1<<5);
00103 const PRInt32 nsXBLPrototypeHandler::cControlMask = (1<<6);
00104 const PRInt32 nsXBLPrototypeHandler::cMetaMask = (1<<7);
00105 
00106 const PRInt32 nsXBLPrototypeHandler::cAllModifiers = cShiftMask | cAltMask | cControlMask | cMetaMask;
00107 
00108 nsXBLPrototypeHandler::nsXBLPrototypeHandler(const PRUnichar* aEvent,
00109                                              const PRUnichar* aPhase,
00110                                              const PRUnichar* aAction,
00111                                              const PRUnichar* aCommand,
00112                                              const PRUnichar* aKeyCode,
00113                                              const PRUnichar* aCharCode,
00114                                              const PRUnichar* aModifiers,
00115                                              const PRUnichar* aButton,
00116                                              const PRUnichar* aClickCount,
00117                                              const PRUnichar* aGroup,
00118                                              const PRUnichar* aPreventDefault,
00119                                              const PRUnichar* aAllowUntrusted,
00120                                              nsXBLPrototypeBinding* aBinding)
00121   : mHandlerText(nsnull),
00122     mLineNumber(0),
00123     mNextHandler(nsnull),
00124     mPrototypeBinding(aBinding)
00125 {
00126   ++gRefCnt;
00127   if (gRefCnt == 1)
00128     // Get the primary accelerator key.
00129     InitAccessKeys();
00130 
00131   ConstructPrototype(nsnull, aEvent, aPhase, aAction, aCommand, aKeyCode,
00132                      aCharCode, aModifiers, aButton, aClickCount,
00133                      aGroup, aPreventDefault, aAllowUntrusted);
00134 }
00135 
00136 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement)
00137   : mBoxObjectForHandlerElement(nsnull),
00138     mLineNumber(0),
00139     mNextHandler(nsnull),
00140     mPrototypeBinding(nsnull)
00141 {
00142   ++gRefCnt;
00143   if (gRefCnt == 1)
00144     // Get the primary accelerator key.
00145     InitAccessKeys();
00146 
00147   // Make sure our prototype is initialized.
00148   ConstructPrototype(aHandlerElement);
00149 }
00150 
00151 nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
00152 {
00153   --gRefCnt;
00154   if (mType & NS_HANDLER_TYPE_XUL) {
00155     NS_IF_RELEASE(mBoxObjectForHandlerElement);
00156   } else if (mHandlerText) {
00157     nsMemory::Free(mHandlerText);
00158   }
00159 
00160   // We own the next handler in the chain, so delete it now.
00161   delete mNextHandler;
00162 }
00163 
00164 already_AddRefed<nsIContent>
00165 nsXBLPrototypeHandler::GetHandlerElement()
00166 {
00167   if (!mBoxObjectForHandlerElement || !(mType & NS_HANDLER_TYPE_XUL)) {
00168     return nsnull;
00169   }
00170   nsCOMPtr<nsIDOMElement> element;
00171   mBoxObjectForHandlerElement->GetElement(getter_AddRefs(element));
00172   nsCOMPtr<nsIContent> content = do_QueryInterface(element);
00173   nsIContent* handler = nsnull;
00174   content.swap(handler);
00175   return handler;
00176 }
00177 
00178 void
00179 nsXBLPrototypeHandler::AppendHandlerText(const nsAString& aText) 
00180 {
00181   if (mHandlerText) {
00182     // Append our text to the existing text.
00183     PRUnichar* temp = mHandlerText;
00184     mHandlerText = ToNewUnicode(nsDependentString(temp) + aText);
00185     nsMemory::Free(temp);
00186   }
00187   else
00188     mHandlerText = ToNewUnicode(aText);
00189 }
00190 
00192 // Get the menu access key from prefs.
00193 // XXX Eventually pick up using CSS3 key-equivalent property or somesuch
00194 void
00195 nsXBLPrototypeHandler::InitAccessKeys()
00196 {
00197   if (kAccelKey >= 0 && kMenuAccessKey >= 0)
00198     return;
00199 
00200   // Compiled-in defaults, in case we can't get the pref --
00201   // mac doesn't have menu shortcuts, other platforms use alt.
00202 #if defined(XP_MAC) || defined(XP_MACOSX)
00203   kMenuAccessKey = 0;
00204   kAccelKey = nsIDOMKeyEvent::DOM_VK_META;
00205 #else
00206   kMenuAccessKey = nsIDOMKeyEvent::DOM_VK_ALT;
00207   kAccelKey = nsIDOMKeyEvent::DOM_VK_CONTROL;
00208 #endif
00209 
00210   // Get the menu access key value from prefs, overriding the default:
00211   kMenuAccessKey =
00212     nsContentUtils::GetIntPref("ui.key.menuAccessKey", kMenuAccessKey);
00213   kAccelKey = nsContentUtils::GetIntPref("ui.key.accelKey", kAccelKey);
00214 }
00215 
00216 nsresult
00217 nsXBLPrototypeHandler::ExecuteHandler(nsIDOMEventReceiver* aReceiver,
00218                                       nsIDOMEvent* aEvent)
00219 {
00220   nsresult rv = NS_ERROR_FAILURE;
00221 
00222   // Prevent default action?
00223   if (mType & NS_HANDLER_TYPE_PREVENTDEFAULT) {
00224     aEvent->PreventDefault();
00225     // If we prevent default, then it's okay for
00226     // mBoxObjectForHandlerElement and mHandlerText to be null
00227     rv = NS_OK;
00228   }
00229 
00230   if (!mBoxObjectForHandlerElement) // This works for both types of handlers.  In both cases, the union's var should be defined.
00231     return rv;
00232 
00233   // See if our event receiver is a content node (and not us).
00234   PRBool isXULKey = (mType & NS_HANDLER_TYPE_XUL);
00235   PRBool isXBLCommand = (mType & NS_HANDLER_TYPE_XBL_COMMAND);
00236   NS_ASSERTION(!(isXULKey && isXBLCommand),
00237                "can't be both a key and xbl command handler");
00238 
00239   // XUL handlers and commands shouldn't be triggered by non-trusted
00240   // events.
00241   if (isXULKey || isXBLCommand) {
00242     nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aEvent);
00243     PRBool trustedEvent = PR_FALSE;
00244     if (domNSEvent) {
00245       domNSEvent->GetIsTrusted(&trustedEvent);
00246     }
00247 
00248     if (!trustedEvent)
00249       return NS_OK;
00250   }
00251     
00252   if (isXBLCommand) {
00253     // This is a special-case optimization to make command handling fast.
00254     // It isn't really a part of XBL, but it helps speed things up.
00255 
00256     // See if preventDefault has been set.  If so, don't execute.
00257     PRBool preventDefault = PR_FALSE;
00258     nsCOMPtr<nsIDOMNSUIEvent> nsUIEvent(do_QueryInterface(aEvent));
00259     if (nsUIEvent)
00260       nsUIEvent->GetPreventDefault(&preventDefault);
00261 
00262     if (preventDefault)
00263       return NS_OK;
00264 
00265     nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(aEvent);
00266     if (privateEvent) {
00267       PRBool dispatchStopped;
00268       privateEvent->IsDispatchStopped(&dispatchStopped);
00269       if (dispatchStopped)
00270         return NS_OK;
00271     }
00272 
00273     // Instead of executing JS, let's get the controller for the bound
00274     // element and call doCommand on it.
00275     nsCOMPtr<nsIController> controller;
00276     nsCOMPtr<nsIFocusController> focusController;
00277 
00278     nsCOMPtr<nsPIWindowRoot> windowRoot(do_QueryInterface(aReceiver));
00279     if (windowRoot) {
00280       windowRoot->GetFocusController(getter_AddRefs(focusController));
00281     }
00282     else {
00283       nsCOMPtr<nsPIDOMWindow> privateWindow(do_QueryInterface(aReceiver));
00284       if (!privateWindow) {
00285         nsCOMPtr<nsIContent> elt(do_QueryInterface(aReceiver));
00286         nsCOMPtr<nsIDocument> doc;
00287         if (elt)
00288           doc = elt->GetOwnerDoc();
00289 
00290         if (!doc)
00291           doc = do_QueryInterface(aReceiver);
00292 
00293         if (!doc)
00294           return NS_ERROR_FAILURE;
00295 
00296         privateWindow = do_QueryInterface(doc->GetScriptGlobalObject());
00297         if (!privateWindow)
00298           return NS_ERROR_FAILURE;
00299       }
00300 
00301       focusController = privateWindow->GetRootFocusController();
00302     }
00303 
00304     NS_LossyConvertUCS2toASCII command(mHandlerText);
00305     if (focusController)
00306       focusController->GetControllerForCommand(command.get(), getter_AddRefs(controller));
00307     else
00308       controller = GetController(aReceiver); // We're attached to the receiver possibly.
00309 
00310     nsAutoString type;
00311     mEventName->ToString(type);
00312 
00313     if (type.EqualsLiteral("keypress") &&
00314         mDetail == nsIDOMKeyEvent::DOM_VK_SPACE &&
00315         mMisc == 1) {
00316       // get the focused element so that we can pageDown only at
00317       // certain times.
00318       nsCOMPtr<nsIDOMElement> focusedElement;
00319       focusController->GetFocusedElement(getter_AddRefs(focusedElement));
00320       PRBool isLink = PR_FALSE;
00321       nsCOMPtr<nsIContent> focusedContent = do_QueryInterface(focusedElement);
00322       nsIContent *content = focusedContent;
00323 
00324       // if the focused element is a link then we do want space to 
00325       // scroll down. focused element may be an element in a link,
00326       // we need to check the parent node too.
00327       if (focusedContent) {
00328         while (content) {
00329           if (content->Tag() == nsHTMLAtoms::a &&
00330               content->IsContentOfType(nsIContent::eHTML)) {
00331             isLink = PR_TRUE;
00332             break;
00333           }
00334 
00335           if (content->HasAttr(kNameSpaceID_XLink, nsHTMLAtoms::type)) {
00336             nsAutoString type;
00337             content->GetAttr(kNameSpaceID_XLink, nsHTMLAtoms::type, type);
00338 
00339             isLink = type.EqualsLiteral("simple");
00340 
00341             if (isLink) {
00342               break;
00343             }
00344           }
00345 
00346           content = content->GetParent();
00347         }
00348 
00349         if (!isLink)
00350           return NS_OK;
00351       }
00352     }
00353 
00354     // We are the default action for this command.
00355     // Stop any other default action from executing.
00356     aEvent->PreventDefault();
00357     
00358     if (controller)
00359       controller->DoCommand(command.get());
00360 
00361     return NS_OK;
00362   }
00363 
00364   // If we're executing on a XUL key element, just dispatch a command
00365   // event at the element.  It will take care of retargeting it to its
00366   // command element, if applicable, and executing the event handler.
00367   if (isXULKey) {
00368     aEvent->PreventDefault();
00369 
00370     nsEventStatus status = nsEventStatus_eIgnore;
00371     nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
00372 
00373     // Copy the modifiers from the key event.
00374     nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
00375     if (!keyEvent) {
00376       NS_ERROR("Trying to execute a key handler for a non-key event!");
00377       return NS_ERROR_FAILURE;
00378     }
00379 
00380     keyEvent->GetAltKey(&event.isAlt);
00381     keyEvent->GetCtrlKey(&event.isControl);
00382     keyEvent->GetShiftKey(&event.isShift);
00383     keyEvent->GetMetaKey(&event.isMeta);
00384     
00385     nsCOMPtr<nsIContent> handlerElement = GetHandlerElement();
00386     NS_ENSURE_STATE(handlerElement);
00387     nsPresContext *pc = nsnull;
00388     nsIDocument *doc = handlerElement->GetCurrentDoc();
00389     if (doc) {
00390       nsIPresShell *shell = doc->GetShellAt(0);
00391       if (shell) {
00392         pc = shell->GetPresContext();
00393       }
00394     }
00395 
00396     handlerElement->HandleDOMEvent(pc, &event, nsnull,
00397                                    NS_EVENT_FLAG_INIT, &status);
00398     return NS_OK;
00399   }
00400 
00401   // Look for a compiled handler on the element. 
00402   // Should be compiled and bound with "on" in front of the name.
00403   nsAutoString onEvent(NS_LITERAL_STRING("onxbl"));
00404   nsAutoString str;
00405   mEventName->ToString(str);
00406   onEvent += str;
00407   nsCOMPtr<nsIAtom> onEventAtom = do_GetAtom(onEvent);
00408 
00409   void* handler = nsnull;
00410   
00411   // Compile the handler and bind it to the element.
00412   nsCOMPtr<nsIScriptGlobalObject> boundGlobal;
00413   nsCOMPtr<nsPIWindowRoot> winRoot(do_QueryInterface(aReceiver));
00414   nsCOMPtr<nsIDOMWindowInternal> focusedWin;
00415 
00416   if (winRoot) {
00417     nsCOMPtr<nsIFocusController> focusController;
00418     winRoot->GetFocusController(getter_AddRefs(focusController));
00419     focusController->GetFocusedWindow(getter_AddRefs(focusedWin));
00420   }
00421 
00422   // if the focused window was found get our script global object from
00423   // that.
00424   if (focusedWin) {
00425     NS_ASSERTION(isXULKey, "We should only use the focused window for "
00426                  "XUL key handlers!");
00427     nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(focusedWin));
00428 
00429     if (piWin) {
00430       piWin = piWin->GetCurrentInnerWindow();
00431       NS_ENSURE_TRUE(piWin, NS_ERROR_UNEXPECTED);
00432     }
00433 
00434     boundGlobal = do_QueryInterface(piWin->GetPrivateRoot());
00435   }
00436   else boundGlobal = do_QueryInterface(aReceiver);
00437 
00438   if (!boundGlobal) {
00439     nsCOMPtr<nsIDocument> boundDocument(do_QueryInterface(aReceiver));
00440     if (!boundDocument) {
00441       // We must be an element.
00442       nsCOMPtr<nsIContent> content(do_QueryInterface(aReceiver));
00443       if (!content)
00444         return NS_OK;
00445       boundDocument = content->GetOwnerDoc();
00446       if (!boundDocument)
00447         return NS_OK;
00448     }
00449 
00450     boundGlobal = boundDocument->GetScriptGlobalObject();
00451   }
00452 
00453   // If we still don't have a 'boundGlobal', we're doomed. bug 95465.
00454   NS_ASSERTION(boundGlobal, "failed to get the nsIScriptGlobalObject. bug 95465?");
00455   if (!boundGlobal)
00456     return NS_OK;
00457 
00458   nsIScriptContext *boundContext = boundGlobal->GetContext();
00459   if (!boundContext) return NS_OK;
00460 
00461   JSObject* scriptObject = nsnull;
00462 
00463   // strong ref to a GC root we'll need to protect scriptObject in the case
00464   // where it is not the global object (!winRoot).
00465   nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
00466 
00467   if (winRoot) {
00468     scriptObject = boundGlobal->GetGlobalJSObject();
00469   } else {
00470     JSObject *global = boundGlobal->GetGlobalJSObject();
00471     JSContext *cx = (JSContext *)boundContext->GetNativeContext();
00472 
00473     // XXX: Don't use the global object!
00474     rv = nsContentUtils::XPConnect()->WrapNative(cx, global, aReceiver,
00475                                                  NS_GET_IID(nsISupports),
00476                                                  getter_AddRefs(wrapper));
00477     NS_ENSURE_SUCCESS(rv, rv);
00478 
00479     rv = wrapper->GetJSObject(&scriptObject);
00480     NS_ENSURE_SUCCESS(rv, rv);
00481   }
00482 
00483   const char *eventName = nsContentUtils::GetEventArgName(kNameSpaceID_XBL);
00484 
00485   nsDependentString handlerText(mHandlerText);
00486   if (handlerText.IsEmpty())
00487     return NS_ERROR_FAILURE;
00488   
00489   nsCAutoString bindingURI;
00490   mPrototypeBinding->DocURI()->GetSpec(bindingURI);
00491 
00492   rv = boundContext->CompileEventHandler(scriptObject, onEventAtom, eventName,
00493                                          handlerText, bindingURI.get(),
00494                                          mLineNumber, PR_TRUE, &handler);
00495   NS_ENSURE_SUCCESS(rv, rv);
00496 
00497   nsAutoGCRoot root(&handler, &rv);
00498   NS_ENSURE_SUCCESS(rv, rv);
00499 
00500   // Temporarily bind it to the bound element
00501   boundContext->BindCompiledEventHandler(scriptObject, onEventAtom, handler);
00502 
00503   // Execute it.
00504   nsCOMPtr<nsIDOMEventListener> eventListener;
00505   NS_NewJSEventListener(boundContext, boundGlobal->GetGlobalJSObject(),
00506                         aReceiver, getter_AddRefs(eventListener));
00507 
00508   nsCOMPtr<nsIJSEventListener> jsListener(do_QueryInterface(eventListener));
00509   jsListener->SetEventName(onEventAtom);
00510   
00511   // Handle the event.
00512   eventListener->HandleEvent(aEvent);
00513   return NS_OK;
00514 }
00515 
00516 already_AddRefed<nsIAtom>
00517 nsXBLPrototypeHandler::GetEventName()
00518 {
00519   nsIAtom* eventName = mEventName;
00520   NS_IF_ADDREF(eventName);
00521   return eventName;
00522 }
00523 
00524 already_AddRefed<nsIController>
00525 nsXBLPrototypeHandler::GetController(nsIDOMEventReceiver* aReceiver)
00526 {
00527   // XXX Fix this so there's a generic interface that describes controllers, 
00528   // This code should have no special knowledge of what objects might have controllers.
00529   nsCOMPtr<nsIControllers> controllers;
00530 
00531   nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aReceiver));
00532   if (xulElement)
00533     xulElement->GetControllers(getter_AddRefs(controllers));
00534 
00535   if (!controllers) {
00536     nsCOMPtr<nsIDOMNSHTMLTextAreaElement> htmlTextArea(do_QueryInterface(aReceiver));
00537     if (htmlTextArea)
00538       htmlTextArea->GetControllers(getter_AddRefs(controllers));
00539   }
00540 
00541   if (!controllers) {
00542     nsCOMPtr<nsIDOMNSHTMLInputElement> htmlInputElement(do_QueryInterface(aReceiver));
00543     if (htmlInputElement)
00544       htmlInputElement->GetControllers(getter_AddRefs(controllers));
00545   }
00546 
00547   if (!controllers) {
00548     nsCOMPtr<nsIDOMWindowInternal> domWindow(do_QueryInterface(aReceiver));
00549     if (domWindow)
00550       domWindow->GetControllers(getter_AddRefs(controllers));
00551   }
00552 
00553   // Return the first controller.
00554   // XXX This code should be checking the command name and using supportscommand and
00555   // iscommandenabled.
00556   nsIController* controller;
00557   if (controllers) {
00558     controllers->GetControllerAt(0, &controller);  // return reference
00559   }
00560   else controller = nsnull;
00561 
00562   return controller;
00563 }
00564 
00565 PRBool
00566 nsXBLPrototypeHandler::KeyEventMatched(nsIDOMKeyEvent* aKeyEvent)
00567 {
00568   if (mDetail == -1)
00569     return PR_TRUE; // No filters set up. It's generic.
00570 
00571   // Get the keycode or charcode of the key event.
00572   PRUint32 code;
00573   if (mMisc)
00574     aKeyEvent->GetCharCode(&code);
00575   else
00576     aKeyEvent->GetKeyCode(&code);
00577 
00578   if (code != PRUint32(mDetail))
00579     return PR_FALSE;
00580 
00581   return ModifiersMatchMask(aKeyEvent);
00582 }
00583 
00584 PRBool
00585 nsXBLPrototypeHandler::MouseEventMatched(nsIDOMMouseEvent* aMouseEvent)
00586 {
00587   if (mDetail == -1 && mMisc == 0 && (mKeyMask & cAllModifiers) == 0)
00588     return PR_TRUE; // No filters set up. It's generic.
00589 
00590   unsigned short button;
00591   aMouseEvent->GetButton(&button);
00592   if (mDetail != -1 && (button != mDetail))
00593     return PR_FALSE;
00594 
00595   PRInt32 clickcount;
00596   aMouseEvent->GetDetail(&clickcount);
00597   if (mMisc != 0 && (clickcount != mMisc))
00598     return PR_FALSE;
00599 
00600   return ModifiersMatchMask(aMouseEvent);
00601 }
00602 
00603 struct keyCodeData {
00604   const char* str;
00605   size_t strlength;
00606   PRUint32 keycode;
00607 };
00608 
00609 // All of these must be uppercase, since the function below does
00610 // case-insensitive comparison by converting to uppercase.
00611 // XXX: be sure to check this periodically for new symbol additions!
00612 static const keyCodeData gKeyCodes[] = {
00613 
00614 #define KEYCODE_ENTRY(str) {#str, sizeof(#str) - 1, nsIDOMKeyEvent::DOM_##str}
00615 #define KEYCODE_ENTRY2(str, code) {str, sizeof(str) - 1, code}
00616 
00617   KEYCODE_ENTRY(VK_CANCEL),
00618   KEYCODE_ENTRY2("VK_BACK", nsIDOMKeyEvent::DOM_VK_BACK_SPACE),
00619   KEYCODE_ENTRY(VK_TAB),
00620   KEYCODE_ENTRY(VK_CLEAR),
00621   KEYCODE_ENTRY(VK_RETURN),
00622   KEYCODE_ENTRY(VK_ENTER),
00623   KEYCODE_ENTRY(VK_SHIFT),
00624   KEYCODE_ENTRY(VK_CONTROL),
00625   KEYCODE_ENTRY(VK_ALT),
00626   KEYCODE_ENTRY(VK_PAUSE),
00627   KEYCODE_ENTRY(VK_CAPS_LOCK),
00628   KEYCODE_ENTRY(VK_ESCAPE),
00629   KEYCODE_ENTRY(VK_SPACE),
00630   KEYCODE_ENTRY(VK_PAGE_UP),
00631   KEYCODE_ENTRY(VK_PAGE_DOWN),
00632   KEYCODE_ENTRY(VK_END),
00633   KEYCODE_ENTRY(VK_HOME),
00634   KEYCODE_ENTRY(VK_LEFT),
00635   KEYCODE_ENTRY(VK_UP),
00636   KEYCODE_ENTRY(VK_RIGHT),
00637   KEYCODE_ENTRY(VK_DOWN),
00638   KEYCODE_ENTRY(VK_PRINTSCREEN),
00639   KEYCODE_ENTRY(VK_INSERT),
00640   KEYCODE_ENTRY(VK_HELP),
00641   KEYCODE_ENTRY(VK_DELETE),
00642   KEYCODE_ENTRY(VK_0),
00643   KEYCODE_ENTRY(VK_1),
00644   KEYCODE_ENTRY(VK_2),
00645   KEYCODE_ENTRY(VK_3),
00646   KEYCODE_ENTRY(VK_4),
00647   KEYCODE_ENTRY(VK_5),
00648   KEYCODE_ENTRY(VK_6),
00649   KEYCODE_ENTRY(VK_7),
00650   KEYCODE_ENTRY(VK_8),
00651   KEYCODE_ENTRY(VK_9),
00652   KEYCODE_ENTRY(VK_SEMICOLON),
00653   KEYCODE_ENTRY(VK_EQUALS),
00654   KEYCODE_ENTRY(VK_A),
00655   KEYCODE_ENTRY(VK_B),
00656   KEYCODE_ENTRY(VK_C),
00657   KEYCODE_ENTRY(VK_D),
00658   KEYCODE_ENTRY(VK_E),
00659   KEYCODE_ENTRY(VK_F),
00660   KEYCODE_ENTRY(VK_G),
00661   KEYCODE_ENTRY(VK_H),
00662   KEYCODE_ENTRY(VK_I),
00663   KEYCODE_ENTRY(VK_J),
00664   KEYCODE_ENTRY(VK_K),
00665   KEYCODE_ENTRY(VK_L),
00666   KEYCODE_ENTRY(VK_M),
00667   KEYCODE_ENTRY(VK_N),
00668   KEYCODE_ENTRY(VK_O),
00669   KEYCODE_ENTRY(VK_P),
00670   KEYCODE_ENTRY(VK_Q),
00671   KEYCODE_ENTRY(VK_R),
00672   KEYCODE_ENTRY(VK_S),
00673   KEYCODE_ENTRY(VK_T),
00674   KEYCODE_ENTRY(VK_U),
00675   KEYCODE_ENTRY(VK_V),
00676   KEYCODE_ENTRY(VK_W),
00677   KEYCODE_ENTRY(VK_X),
00678   KEYCODE_ENTRY(VK_Y),
00679   KEYCODE_ENTRY(VK_Z),
00680   KEYCODE_ENTRY(VK_NUMPAD0),
00681   KEYCODE_ENTRY(VK_NUMPAD1),
00682   KEYCODE_ENTRY(VK_NUMPAD2),
00683   KEYCODE_ENTRY(VK_NUMPAD3),
00684   KEYCODE_ENTRY(VK_NUMPAD4),
00685   KEYCODE_ENTRY(VK_NUMPAD5),
00686   KEYCODE_ENTRY(VK_NUMPAD6),
00687   KEYCODE_ENTRY(VK_NUMPAD7),
00688   KEYCODE_ENTRY(VK_NUMPAD8),
00689   KEYCODE_ENTRY(VK_NUMPAD9),
00690   KEYCODE_ENTRY(VK_MULTIPLY),
00691   KEYCODE_ENTRY(VK_ADD),
00692   KEYCODE_ENTRY(VK_SEPARATOR),
00693   KEYCODE_ENTRY(VK_SUBTRACT),
00694   KEYCODE_ENTRY(VK_DECIMAL),
00695   KEYCODE_ENTRY(VK_DIVIDE),
00696   KEYCODE_ENTRY(VK_F1),
00697   KEYCODE_ENTRY(VK_F2),
00698   KEYCODE_ENTRY(VK_F3),
00699   KEYCODE_ENTRY(VK_F4),
00700   KEYCODE_ENTRY(VK_F5),
00701   KEYCODE_ENTRY(VK_F6),
00702   KEYCODE_ENTRY(VK_F7),
00703   KEYCODE_ENTRY(VK_F8),
00704   KEYCODE_ENTRY(VK_F9),
00705   KEYCODE_ENTRY(VK_F10),
00706   KEYCODE_ENTRY(VK_F11),
00707   KEYCODE_ENTRY(VK_F12),
00708   KEYCODE_ENTRY(VK_F13),
00709   KEYCODE_ENTRY(VK_F14),
00710   KEYCODE_ENTRY(VK_F15),
00711   KEYCODE_ENTRY(VK_F16),
00712   KEYCODE_ENTRY(VK_F17),
00713   KEYCODE_ENTRY(VK_F18),
00714   KEYCODE_ENTRY(VK_F19),
00715   KEYCODE_ENTRY(VK_F20),
00716   KEYCODE_ENTRY(VK_F21),
00717   KEYCODE_ENTRY(VK_F22),
00718   KEYCODE_ENTRY(VK_F23),
00719   KEYCODE_ENTRY(VK_F24),
00720   KEYCODE_ENTRY(VK_NUM_LOCK),
00721   KEYCODE_ENTRY(VK_SCROLL_LOCK),
00722   KEYCODE_ENTRY(VK_COMMA),
00723   KEYCODE_ENTRY(VK_PERIOD),
00724   KEYCODE_ENTRY(VK_SLASH),
00725   KEYCODE_ENTRY(VK_BACK_QUOTE),
00726   KEYCODE_ENTRY(VK_OPEN_BRACKET),
00727   KEYCODE_ENTRY(VK_BACK_SLASH),
00728   KEYCODE_ENTRY(VK_CLOSE_BRACKET),
00729   KEYCODE_ENTRY(VK_QUOTE)
00730 
00731 #undef KEYCODE_ENTRY
00732 #undef KEYCODE_ENTRY2
00733 
00734 };
00735 
00736 PRInt32 nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString& aKeyName)
00737 {
00738   nsCAutoString keyName;
00739   keyName.AssignWithConversion(aKeyName);
00740   ToUpperCase(keyName); // We want case-insensitive comparison with data
00741                         // stored as uppercase.
00742 
00743   PRUint32 keyNameLength = keyName.Length();
00744   const char* keyNameStr = keyName.get();
00745   for (PRUint16 i = 0; i < (sizeof(gKeyCodes) / sizeof(gKeyCodes[0])); ++i)
00746     if (keyNameLength == gKeyCodes[i].strlength &&
00747         !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr))
00748       return gKeyCodes[i].keycode;
00749 
00750   return 0;
00751 }
00752 
00753 PRInt32 nsXBLPrototypeHandler::KeyToMask(PRInt32 key)
00754 {
00755   switch (key)
00756   {
00757     case nsIDOMKeyEvent::DOM_VK_META:
00758       return cMeta | cMetaMask;
00759       break;
00760 
00761     case nsIDOMKeyEvent::DOM_VK_ALT:
00762       return cAlt | cAltMask;
00763       break;
00764 
00765     case nsIDOMKeyEvent::DOM_VK_CONTROL:
00766     default:
00767       return cControl | cControlMask;
00768   }
00769   return cControl | cControlMask;  // for warning avoidance
00770 }
00771 
00772 void
00773 nsXBLPrototypeHandler::GetEventType(nsAString& aEvent)
00774 {
00775   nsCOMPtr<nsIContent> handlerElement = GetHandlerElement();
00776   if (!handlerElement) {
00777     aEvent.Truncate();
00778     return;
00779   }
00780   handlerElement->GetAttr(kNameSpaceID_None, nsXBLAtoms::event, aEvent);
00781   
00782   if (aEvent.IsEmpty() && (mType & NS_HANDLER_TYPE_XUL))
00783     // If no type is specified for a XUL <key> element, let's assume that we're "keypress".
00784     aEvent.AssignLiteral("keypress");
00785 }
00786 
00787 void
00788 nsXBLPrototypeHandler::ConstructPrototype(nsIContent* aKeyElement, 
00789                                           const PRUnichar* aEvent,
00790                                           const PRUnichar* aPhase,
00791                                           const PRUnichar* aAction,
00792                                           const PRUnichar* aCommand,
00793                                           const PRUnichar* aKeyCode,
00794                                           const PRUnichar* aCharCode,
00795                                           const PRUnichar* aModifiers,
00796                                           const PRUnichar* aButton,
00797                                           const PRUnichar* aClickCount,
00798                                           const PRUnichar* aGroup,
00799                                           const PRUnichar* aPreventDefault,
00800                                           const PRUnichar* aAllowUntrusted)
00801 {
00802   mType = 0;
00803 
00804   if (aKeyElement) {
00805     mType |= NS_HANDLER_TYPE_XUL;
00806 
00807     nsCOMPtr<nsIDOMNSDocument> nsDomDoc =
00808       do_QueryInterface(aKeyElement->GetOwnerDoc());
00809     if (nsDomDoc) {
00810       nsCOMPtr<nsIDOMElement> el = do_QueryInterface(aKeyElement);
00811       nsCOMPtr<nsIBoxObject> box;
00812       nsDomDoc->GetBoxObjectFor(el, getter_AddRefs(box));
00813       box.swap(mBoxObjectForHandlerElement);
00814       NS_WARN_IF_FALSE(mBoxObjectForHandlerElement,
00815                        "Couldn't get a box object for the key element!");
00816     }
00817   }
00818   else {
00819     mType |= aCommand ? NS_HANDLER_TYPE_XBL_COMMAND : NS_HANDLER_TYPE_XBL_JS;
00820     mHandlerText = nsnull;
00821 
00822     if (mPrototypeBinding && !mPrototypeBinding->IsChrome()) {
00823       mType |= NS_HANDLER_ALLOW_UNTRUSTED;
00824     }
00825   }
00826 
00827   mDetail = -1;
00828   mMisc = 0;
00829   mKeyMask = 0;
00830   mPhase = NS_PHASE_BUBBLING;
00831 
00832   if (aAction)
00833     mHandlerText = ToNewUnicode(nsDependentString(aAction));
00834   else if (aCommand)
00835     mHandlerText = ToNewUnicode(nsDependentString(aCommand));
00836 
00837   nsAutoString event(aEvent);
00838   if (event.IsEmpty()) {
00839     if (mType & NS_HANDLER_TYPE_XUL)
00840       GetEventType(event);
00841     if (event.IsEmpty())
00842       return;
00843   }
00844 
00845   mEventName = do_GetAtom(event);
00846 
00847   if (aPhase) {
00848     const nsDependentString phase(aPhase);
00849     if (phase.EqualsLiteral("capturing"))
00850       mPhase = NS_PHASE_CAPTURING;
00851     else if (phase.EqualsLiteral("target"))
00852       mPhase = NS_PHASE_TARGET;
00853   }
00854 
00855   // Button and clickcount apply only to XBL handlers and don't apply to XUL key
00856   // handlers.  
00857   if (aButton && *aButton)
00858     mDetail = *aButton - '0';
00859 
00860   if (aClickCount && *aClickCount)
00861     mMisc = *aClickCount - '0';
00862 
00863   // Modifiers are supported by both types of handlers (XUL and XBL).  
00864   nsAutoString modifiers(aModifiers);
00865   if (mType & NS_HANDLER_TYPE_XUL)
00866     aKeyElement->GetAttr(kNameSpaceID_None, nsXBLAtoms::modifiers, modifiers);
00867   
00868   if (!modifiers.IsEmpty()) {
00869     mKeyMask = cAllModifiers;
00870     char* str = ToNewCString(modifiers);
00871     char* newStr;
00872     char* token = nsCRT::strtok( str, ", ", &newStr );
00873     while( token != NULL ) {
00874       if (PL_strcmp(token, "shift") == 0)
00875         mKeyMask |= cShift | cShiftMask;
00876       else if (PL_strcmp(token, "alt") == 0)
00877         mKeyMask |= cAlt | cAltMask;
00878       else if (PL_strcmp(token, "meta") == 0)
00879         mKeyMask |= cMeta | cMetaMask;
00880       else if (PL_strcmp(token, "control") == 0)
00881         mKeyMask |= cControl | cControlMask;
00882       else if (PL_strcmp(token, "accel") == 0)
00883         mKeyMask |= KeyToMask(kAccelKey);
00884       else if (PL_strcmp(token, "access") == 0)
00885         mKeyMask |= KeyToMask(kMenuAccessKey);
00886       else if (PL_strcmp(token, "any") == 0)
00887         mKeyMask &= ~(mKeyMask << 4);
00888     
00889       token = nsCRT::strtok( newStr, ", ", &newStr );
00890     }
00891 
00892     nsMemory::Free(str);
00893   }
00894 
00895   nsAutoString key(aCharCode);
00896   if (key.IsEmpty()) {
00897     if (mType & NS_HANDLER_TYPE_XUL) {
00898       aKeyElement->GetAttr(kNameSpaceID_None, nsXBLAtoms::key, key);
00899       if (key.IsEmpty()) 
00900         aKeyElement->GetAttr(kNameSpaceID_None, nsXBLAtoms::charcode, key);
00901     }
00902   }
00903 
00904   if (!key.IsEmpty()) {
00905     if (mKeyMask == 0)
00906       mKeyMask = cAllModifiers;
00907     if (!(mKeyMask & cShift))
00908       mKeyMask &= ~cShiftMask;
00909     if ((mKeyMask & cShift) != 0)
00910       ToUpperCase(key);
00911     else
00912       ToLowerCase(key);
00913 
00914     // We have a charcode.
00915     mMisc = 1;
00916     mDetail = key[0];
00917   }
00918   else {
00919     key.Assign(aKeyCode);
00920     if (mType & NS_HANDLER_TYPE_XUL)
00921       aKeyElement->GetAttr(kNameSpaceID_None, nsXBLAtoms::keycode, key);
00922     
00923     if (!key.IsEmpty()) {
00924       if (mKeyMask == 0)
00925         mKeyMask = cAllModifiers;
00926       mDetail = GetMatchingKeyCode(key);
00927     }
00928   }
00929 
00930   if (aGroup && nsDependentString(aGroup).EqualsLiteral("system"))
00931     mType |= NS_HANDLER_TYPE_SYSTEM;
00932 
00933   nsAutoString preventDefault(aPreventDefault);
00934   if (preventDefault.EqualsLiteral("true"))
00935     mType |= NS_HANDLER_TYPE_PREVENTDEFAULT;
00936 
00937   if (aAllowUntrusted) {
00938     if (nsDependentString(aAllowUntrusted).EqualsLiteral("true")) {
00939       mType |= NS_HANDLER_ALLOW_UNTRUSTED;
00940     } else {
00941       mType &= ~NS_HANDLER_ALLOW_UNTRUSTED;
00942     }
00943   }
00944 }
00945 
00946 PRBool
00947 nsXBLPrototypeHandler::ModifiersMatchMask(nsIDOMUIEvent* aEvent)
00948 {
00949   nsCOMPtr<nsIDOMKeyEvent> key(do_QueryInterface(aEvent));
00950   nsCOMPtr<nsIDOMMouseEvent> mouse(do_QueryInterface(aEvent));
00951 
00952   PRBool keyPresent;
00953   if (mKeyMask & cMetaMask) {
00954     key ? key->GetMetaKey(&keyPresent) : mouse->GetMetaKey(&keyPresent);
00955     if (keyPresent != ((mKeyMask & cMeta) != 0))
00956       return PR_FALSE;
00957   }
00958 
00959   if (mKeyMask & cShiftMask) {
00960     key ? key->GetShiftKey(&keyPresent) : mouse->GetShiftKey(&keyPresent);
00961     if (keyPresent != ((mKeyMask & cShift) != 0))
00962       return PR_FALSE;
00963   }
00964 
00965   if (mKeyMask & cAltMask) {
00966     key ? key->GetAltKey(&keyPresent) : mouse->GetAltKey(&keyPresent);
00967     if (keyPresent != ((mKeyMask & cAlt) != 0))
00968       return PR_FALSE;
00969   }
00970 
00971   if (mKeyMask & cControlMask) {
00972     key ? key->GetCtrlKey(&keyPresent) : mouse->GetCtrlKey(&keyPresent);
00973     if (keyPresent != ((mKeyMask & cControl) != 0))
00974       return PR_FALSE;
00975   }
00976 
00977   return PR_TRUE;
00978 }