dom/xbl/nsXBLPrototypeHandler.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     6 #include "mozilla/ArrayUtils.h"
     8 #include "nsCOMPtr.h"
     9 #include "nsXBLPrototypeHandler.h"
    10 #include "nsXBLPrototypeBinding.h"
    11 #include "nsContentUtils.h"
    12 #include "nsCxPusher.h"
    13 #include "nsIContent.h"
    14 #include "nsIAtom.h"
    15 #include "nsIDOMKeyEvent.h"
    16 #include "nsIDOMMouseEvent.h"
    17 #include "nsNameSpaceManager.h"
    18 #include "nsIScriptContext.h"
    19 #include "nsIDocument.h"
    20 #include "nsIController.h"
    21 #include "nsIControllers.h"
    22 #include "nsIDOMXULElement.h"
    23 #include "nsIURI.h"
    24 #include "nsIDOMHTMLTextAreaElement.h"
    25 #include "nsIDOMHTMLInputElement.h"
    26 #include "nsFocusManager.h"
    27 #include "nsIDOMEventListener.h"
    28 #include "nsPIDOMWindow.h"
    29 #include "nsPIWindowRoot.h"
    30 #include "nsIDOMWindow.h"
    31 #include "nsIServiceManager.h"
    32 #include "nsIScriptError.h"
    33 #include "nsXPIDLString.h"
    34 #include "nsReadableUtils.h"
    35 #include "nsGkAtoms.h"
    36 #include "nsIXPConnect.h"
    37 #include "nsIDOMScriptObjectFactory.h"
    38 #include "nsDOMCID.h"
    39 #include "nsUnicharUtils.h"
    40 #include "nsCRT.h"
    41 #include "nsXBLEventHandler.h"
    42 #include "nsXBLSerialize.h"
    43 #include "nsJSUtils.h"
    44 #include "mozilla/BasicEvents.h"
    45 #include "mozilla/JSEventHandler.h"
    46 #include "mozilla/Preferences.h"
    47 #include "mozilla/dom/EventHandlerBinding.h"
    49 using namespace mozilla;
    50 using namespace mozilla::dom;
    52 uint32_t nsXBLPrototypeHandler::gRefCnt = 0;
    54 int32_t nsXBLPrototypeHandler::kMenuAccessKey = -1;
    55 int32_t nsXBLPrototypeHandler::kAccelKey = -1;
    57 const int32_t nsXBLPrototypeHandler::cShift = (1<<0);
    58 const int32_t nsXBLPrototypeHandler::cAlt = (1<<1);
    59 const int32_t nsXBLPrototypeHandler::cControl = (1<<2);
    60 const int32_t nsXBLPrototypeHandler::cMeta = (1<<3);
    61 const int32_t nsXBLPrototypeHandler::cOS = (1<<4);
    63 const int32_t nsXBLPrototypeHandler::cShiftMask = (1<<5);
    64 const int32_t nsXBLPrototypeHandler::cAltMask = (1<<6);
    65 const int32_t nsXBLPrototypeHandler::cControlMask = (1<<7);
    66 const int32_t nsXBLPrototypeHandler::cMetaMask = (1<<8);
    67 const int32_t nsXBLPrototypeHandler::cOSMask = (1<<9);
    69 const int32_t nsXBLPrototypeHandler::cAllModifiers =
    70   cShiftMask | cAltMask | cControlMask | cMetaMask | cOSMask;
    72 nsXBLPrototypeHandler::nsXBLPrototypeHandler(const char16_t* aEvent,
    73                                              const char16_t* aPhase,
    74                                              const char16_t* aAction,
    75                                              const char16_t* aCommand,
    76                                              const char16_t* aKeyCode,
    77                                              const char16_t* aCharCode,
    78                                              const char16_t* aModifiers,
    79                                              const char16_t* aButton,
    80                                              const char16_t* aClickCount,
    81                                              const char16_t* aGroup,
    82                                              const char16_t* aPreventDefault,
    83                                              const char16_t* aAllowUntrusted,
    84                                              nsXBLPrototypeBinding* aBinding,
    85                                              uint32_t aLineNumber)
    86   : mHandlerText(nullptr),
    87     mLineNumber(aLineNumber),
    88     mNextHandler(nullptr),
    89     mPrototypeBinding(aBinding)
    90 {
    91   Init();
    93   ConstructPrototype(nullptr, aEvent, aPhase, aAction, aCommand, aKeyCode,
    94                      aCharCode, aModifiers, aButton, aClickCount,
    95                      aGroup, aPreventDefault, aAllowUntrusted);
    96 }
    98 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement)
    99   : mHandlerElement(nullptr),
   100     mLineNumber(0),
   101     mNextHandler(nullptr),
   102     mPrototypeBinding(nullptr)
   103 {
   104   Init();
   106   // Make sure our prototype is initialized.
   107   ConstructPrototype(aHandlerElement);
   108 }
   110 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
   111   : mHandlerText(nullptr),
   112     mLineNumber(0),
   113     mNextHandler(nullptr),
   114     mPrototypeBinding(aBinding)
   115 {
   116   Init();
   117 }
   119 nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
   120 {
   121   --gRefCnt;
   122   if (mType & NS_HANDLER_TYPE_XUL) {
   123     NS_IF_RELEASE(mHandlerElement);
   124   } else if (mHandlerText) {
   125     nsMemory::Free(mHandlerText);
   126   }
   128   // We own the next handler in the chain, so delete it now.
   129   NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler);
   130 }
   132 already_AddRefed<nsIContent>
   133 nsXBLPrototypeHandler::GetHandlerElement()
   134 {
   135   if (mType & NS_HANDLER_TYPE_XUL) {
   136     nsCOMPtr<nsIContent> element = do_QueryReferent(mHandlerElement);
   137     return element.forget();
   138   }
   140   return nullptr;
   141 }
   143 void
   144 nsXBLPrototypeHandler::AppendHandlerText(const nsAString& aText) 
   145 {
   146   if (mHandlerText) {
   147     // Append our text to the existing text.
   148     char16_t* temp = mHandlerText;
   149     mHandlerText = ToNewUnicode(nsDependentString(temp) + aText);
   150     nsMemory::Free(temp);
   151   }
   152   else {
   153     mHandlerText = ToNewUnicode(aText);
   154   }
   155 }
   157 /////////////////////////////////////////////////////////////////////////////
   158 // Get the menu access key from prefs.
   159 // XXX Eventually pick up using CSS3 key-equivalent property or somesuch
   160 void
   161 nsXBLPrototypeHandler::InitAccessKeys()
   162 {
   163   if (kAccelKey >= 0 && kMenuAccessKey >= 0)
   164     return;
   166   // Compiled-in defaults, in case we can't get the pref --
   167   // mac doesn't have menu shortcuts, other platforms use alt.
   168 #ifdef XP_MACOSX
   169   kMenuAccessKey = 0;
   170   kAccelKey = nsIDOMKeyEvent::DOM_VK_META;
   171 #else
   172   kMenuAccessKey = nsIDOMKeyEvent::DOM_VK_ALT;
   173   kAccelKey = nsIDOMKeyEvent::DOM_VK_CONTROL;
   174 #endif
   176   // Get the menu access key value from prefs, overriding the default:
   177   kMenuAccessKey =
   178     Preferences::GetInt("ui.key.menuAccessKey", kMenuAccessKey);
   179   kAccelKey = Preferences::GetInt("ui.key.accelKey", kAccelKey);
   180 }
   182 nsresult
   183 nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
   184                                       nsIDOMEvent* aEvent)
   185 {
   186   nsresult rv = NS_ERROR_FAILURE;
   188   // Prevent default action?
   189   if (mType & NS_HANDLER_TYPE_PREVENTDEFAULT) {
   190     aEvent->PreventDefault();
   191     // If we prevent default, then it's okay for
   192     // mHandlerElement and mHandlerText to be null
   193     rv = NS_OK;
   194   }
   196   if (!mHandlerElement) // This works for both types of handlers.  In both cases, the union's var should be defined.
   197     return rv;
   199   // See if our event receiver is a content node (and not us).
   200   bool isXULKey = !!(mType & NS_HANDLER_TYPE_XUL);
   201   bool isXBLCommand = !!(mType & NS_HANDLER_TYPE_XBL_COMMAND);
   202   NS_ASSERTION(!(isXULKey && isXBLCommand),
   203                "can't be both a key and xbl command handler");
   205   // XUL handlers and commands shouldn't be triggered by non-trusted
   206   // events.
   207   if (isXULKey || isXBLCommand) {
   208     bool trustedEvent = false;
   209     aEvent->GetIsTrusted(&trustedEvent);
   211     if (!trustedEvent)
   212       return NS_OK;
   213   }
   215   if (isXBLCommand) {
   216     return DispatchXBLCommand(aTarget, aEvent);
   217   }
   219   // If we're executing on a XUL key element, just dispatch a command
   220   // event at the element.  It will take care of retargeting it to its
   221   // command element, if applicable, and executing the event handler.
   222   if (isXULKey) {
   223     return DispatchXULKeyCommand(aEvent);
   224   }
   226   // Look for a compiled handler on the element. 
   227   // Should be compiled and bound with "on" in front of the name.
   228   nsCOMPtr<nsIAtom> onEventAtom = do_GetAtom(NS_LITERAL_STRING("onxbl") +
   229                                              nsDependentAtomString(mEventName));
   231   // Compile the handler and bind it to the element.
   232   nsCOMPtr<nsIScriptGlobalObject> boundGlobal;
   233   nsCOMPtr<nsPIWindowRoot> winRoot(do_QueryInterface(aTarget));
   234   nsCOMPtr<nsPIDOMWindow> window;
   236   if (winRoot) {
   237     window = winRoot->GetWindow();
   238   }
   240   if (window) {
   241     window = window->GetCurrentInnerWindow();
   242     NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED);
   244     boundGlobal = do_QueryInterface(window->GetPrivateRoot());
   245   }
   246   else boundGlobal = do_QueryInterface(aTarget);
   248   if (!boundGlobal) {
   249     nsCOMPtr<nsIDocument> boundDocument(do_QueryInterface(aTarget));
   250     if (!boundDocument) {
   251       // We must be an element.
   252       nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
   253       if (!content)
   254         return NS_OK;
   255       boundDocument = content->OwnerDoc();
   256     }
   258     boundGlobal = do_QueryInterface(boundDocument->GetScopeObject());
   259   }
   261   if (!boundGlobal)
   262     return NS_OK;
   264   nsIScriptContext *boundContext = boundGlobal->GetScriptContext();
   265   if (!boundContext)
   266     return NS_OK;
   268   nsISupports *scriptTarget;
   270   if (winRoot) {
   271     scriptTarget = boundGlobal;
   272   } else {
   273     scriptTarget = aTarget;
   274   }
   276   // We're about to create a new JSEventHandler, which means that we're
   277   // responsible for pushing the context of the event target. See the similar
   278   // comment in nsEventManagerListener.cpp.
   279   nsCxPusher pusher;
   280   NS_ENSURE_STATE(pusher.Push(aTarget));
   282   AutoPushJSContext cx(boundContext->GetNativeContext());
   283   JS::Rooted<JSObject*> handler(cx);
   285   rv = EnsureEventHandler(boundGlobal, boundContext, onEventAtom, &handler);
   286   NS_ENSURE_SUCCESS(rv, rv);
   288   JS::Rooted<JSObject*> globalObject(cx, boundGlobal->GetGlobalJSObject());
   289   JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
   290   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
   292   // Bind it to the bound element. Note that if we're using a separate XBL scope,
   293   // we'll actually be binding the event handler to a cross-compartment wrapper
   294   // to the bound element's reflector.
   296   // First, enter our XBL scope. This is where the generic handler should have
   297   // been compiled, above.
   298   JSAutoCompartment ac(cx, scopeObject);
   299   JS::Rooted<JSObject*> genericHandler(cx, handler.get());
   300   bool ok = JS_WrapObject(cx, &genericHandler);
   301   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   302   MOZ_ASSERT(!js::IsCrossCompartmentWrapper(genericHandler));
   304   // Wrap the native into the XBL scope. This creates a reflector in the document
   305   // scope if one doesn't already exist, and potentially wraps it cross-
   306   // compartment into our scope (via aAllowWrapping=true).
   307   JS::Rooted<JS::Value> targetV(cx, JS::UndefinedValue());
   308   rv = nsContentUtils::WrapNative(cx, scriptTarget, &targetV);
   309   NS_ENSURE_SUCCESS(rv, rv);
   311   // Next, clone the generic handler to be parented to the target.
   312   JS::Rooted<JSObject*> target(cx, &targetV.toObject());
   313   JS::Rooted<JSObject*> bound(cx, JS_CloneFunctionObject(cx, genericHandler, target));
   314   NS_ENSURE_TRUE(bound, NS_ERROR_FAILURE);
   316   // Now, wrap the bound handler into the content compartment and use it.
   317   JSAutoCompartment ac2(cx, globalObject);
   318   if (!JS_WrapObject(cx, &bound)) {
   319     return NS_ERROR_FAILURE;
   320   }
   322   nsRefPtr<EventHandlerNonNull> handlerCallback =
   323     new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr);
   325   TypedEventHandler typedHandler(handlerCallback);
   327   // Execute it.
   328   nsCOMPtr<JSEventHandler> jsEventHandler;
   329   rv = NS_NewJSEventHandler(scriptTarget, onEventAtom,
   330                             typedHandler,
   331                             getter_AddRefs(jsEventHandler));
   332   NS_ENSURE_SUCCESS(rv, rv);
   334   // Handle the event.
   335   jsEventHandler->HandleEvent(aEvent);
   336   jsEventHandler->Disconnect();
   337   return NS_OK;
   338 }
   340 nsresult
   341 nsXBLPrototypeHandler::EnsureEventHandler(nsIScriptGlobalObject* aGlobal,
   342                                           nsIScriptContext *aBoundContext,
   343                                           nsIAtom *aName,
   344                                           JS::MutableHandle<JSObject*> aHandler)
   345 {
   346   AutoPushJSContext cx(aBoundContext->GetNativeContext());
   348   // Check to see if we've already compiled this
   349   nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aGlobal);
   350   if (pWindow) {
   351     JS::Rooted<JSObject*> cachedHandler(cx, pWindow->GetCachedXBLPrototypeHandler(this));
   352     if (cachedHandler) {
   353       JS::ExposeObjectToActiveJS(cachedHandler);
   354       aHandler.set(cachedHandler);
   355       NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE);
   356       return NS_OK;
   357     }
   358   }
   360   // Ensure that we have something to compile
   361   nsDependentString handlerText(mHandlerText);
   362   NS_ENSURE_TRUE(!handlerText.IsEmpty(), NS_ERROR_FAILURE);
   364   JS::Rooted<JSObject*> globalObject(cx, aGlobal->GetGlobalJSObject());
   365   JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
   366   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
   368   nsAutoCString bindingURI;
   369   mPrototypeBinding->DocURI()->GetSpec(bindingURI);
   371   uint32_t argCount;
   372   const char **argNames;
   373   nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, &argCount,
   374                                    &argNames);
   376   // Compile the event handler in the xbl scope.
   377   JSAutoCompartment ac(cx, scopeObject);
   378   JS::CompileOptions options(cx);
   379   options.setFileAndLine(bindingURI.get(), mLineNumber)
   380          .setVersion(JSVERSION_LATEST);
   382   JS::Rooted<JSObject*> handlerFun(cx);
   383   nsresult rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options,
   384                                            nsAtomCString(aName), argCount,
   385                                            argNames, handlerText,
   386                                            handlerFun.address());
   387   NS_ENSURE_SUCCESS(rv, rv);
   388   NS_ENSURE_TRUE(handlerFun, NS_ERROR_FAILURE);
   390   // Wrap the handler into the content scope, since we're about to stash it
   391   // on the DOM window and such.
   392   JSAutoCompartment ac2(cx, globalObject);
   393   bool ok = JS_WrapObject(cx, &handlerFun);
   394   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   395   aHandler.set(handlerFun);
   396   NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE);
   398   if (pWindow) {
   399     pWindow->CacheXBLPrototypeHandler(this, aHandler);
   400   }
   402   return NS_OK;
   403 }
   405 nsresult
   406 nsXBLPrototypeHandler::DispatchXBLCommand(EventTarget* aTarget, nsIDOMEvent* aEvent)
   407 {
   408   // This is a special-case optimization to make command handling fast.
   409   // It isn't really a part of XBL, but it helps speed things up.
   411   if (aEvent) {
   412     // See if preventDefault has been set.  If so, don't execute.
   413     bool preventDefault = false;
   414     aEvent->GetDefaultPrevented(&preventDefault);
   415     if (preventDefault) {
   416       return NS_OK;
   417     }
   418     bool dispatchStopped = aEvent->IsDispatchStopped();
   419     if (dispatchStopped) {
   420       return NS_OK;
   421     }
   422   }
   424   // Instead of executing JS, let's get the controller for the bound
   425   // element and call doCommand on it.
   426   nsCOMPtr<nsIController> controller;
   428   nsCOMPtr<nsPIDOMWindow> privateWindow;
   429   nsCOMPtr<nsPIWindowRoot> windowRoot(do_QueryInterface(aTarget));
   430   if (windowRoot) {
   431     privateWindow = windowRoot->GetWindow();
   432   }
   433   else {
   434     privateWindow = do_QueryInterface(aTarget);
   435     if (!privateWindow) {
   436       nsCOMPtr<nsIContent> elt(do_QueryInterface(aTarget));
   437       nsCOMPtr<nsIDocument> doc;
   438       // XXXbz sXBL/XBL2 issue -- this should be the "scope doc" or
   439       // something... whatever we use when wrapping DOM nodes
   440       // normally.  It's not clear that the owner doc is the right
   441       // thing.
   442       if (elt)
   443         doc = elt->OwnerDoc();
   445       if (!doc)
   446         doc = do_QueryInterface(aTarget);
   448       if (!doc)
   449         return NS_ERROR_FAILURE;
   451       privateWindow = doc->GetWindow();
   452       if (!privateWindow)
   453         return NS_ERROR_FAILURE;
   454     }
   456     windowRoot = privateWindow->GetTopWindowRoot();
   457   }
   459   NS_LossyConvertUTF16toASCII command(mHandlerText);
   460   if (windowRoot)
   461     windowRoot->GetControllerForCommand(command.get(), getter_AddRefs(controller));
   462   else
   463     controller = GetController(aTarget); // We're attached to the receiver possibly.
   465   if (mEventName == nsGkAtoms::keypress &&
   466       mDetail == nsIDOMKeyEvent::DOM_VK_SPACE &&
   467       mMisc == 1) {
   468     // get the focused element so that we can pageDown only at
   469     // certain times.
   471     nsCOMPtr<nsPIDOMWindow> windowToCheck;
   472     if (windowRoot)
   473       windowToCheck = windowRoot->GetWindow();
   474     else
   475       windowToCheck = privateWindow->GetPrivateRoot();
   477     nsCOMPtr<nsIContent> focusedContent;
   478     if (windowToCheck) {
   479       nsCOMPtr<nsPIDOMWindow> focusedWindow;
   480       focusedContent =
   481         nsFocusManager::GetFocusedDescendant(windowToCheck, true, getter_AddRefs(focusedWindow));
   482     }
   484     bool isLink = false;
   485     nsIContent *content = focusedContent;
   487     // if the focused element is a link then we do want space to 
   488     // scroll down. The focused element may be an element in a link,
   489     // we need to check the parent node too. Only do this check if an
   490     // element is focused and has a parent.
   491     if (focusedContent && focusedContent->GetParent()) {
   492       while (content) {
   493         if (content->Tag() == nsGkAtoms::a && content->IsHTML()) {
   494           isLink = true;
   495           break;
   496         }
   498         if (content->HasAttr(kNameSpaceID_XLink, nsGkAtoms::type)) {
   499           isLink = content->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
   500                                         nsGkAtoms::simple, eCaseMatters);
   502           if (isLink) {
   503             break;
   504           }
   505         }
   507         content = content->GetParent();
   508       }
   510       if (!isLink)
   511         return NS_OK;
   512     }
   513   }
   515   // We are the default action for this command.
   516   // Stop any other default action from executing.
   517   aEvent->PreventDefault();
   519   if (controller)
   520     controller->DoCommand(command.get());
   522   return NS_OK;
   523 }
   525 nsresult
   526 nsXBLPrototypeHandler::DispatchXULKeyCommand(nsIDOMEvent* aEvent)
   527 {
   528   nsCOMPtr<nsIContent> handlerElement = GetHandlerElement();
   529   NS_ENSURE_STATE(handlerElement);
   530   if (handlerElement->AttrValueIs(kNameSpaceID_None,
   531                                   nsGkAtoms::disabled,
   532                                   nsGkAtoms::_true,
   533                                   eCaseMatters)) {
   534     // Don't dispatch command events for disabled keys.
   535     return NS_OK;
   536   }
   538   aEvent->PreventDefault();
   540   // Copy the modifiers from the key event.
   541   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
   542   if (!keyEvent) {
   543     NS_ERROR("Trying to execute a key handler for a non-key event!");
   544     return NS_ERROR_FAILURE;
   545   }
   547   // XXX We should use mozilla::Modifiers for supporting all modifiers.
   549   bool isAlt = false;
   550   bool isControl = false;
   551   bool isShift = false;
   552   bool isMeta = false;
   553   keyEvent->GetAltKey(&isAlt);
   554   keyEvent->GetCtrlKey(&isControl);
   555   keyEvent->GetShiftKey(&isShift);
   556   keyEvent->GetMetaKey(&isMeta);
   558   nsContentUtils::DispatchXULCommand(handlerElement, true,
   559                                      nullptr, nullptr,
   560                                      isControl, isAlt, isShift, isMeta);
   561   return NS_OK;
   562 }
   564 already_AddRefed<nsIAtom>
   565 nsXBLPrototypeHandler::GetEventName()
   566 {
   567   nsCOMPtr<nsIAtom> eventName = mEventName;
   568   return eventName.forget();
   569 }
   571 already_AddRefed<nsIController>
   572 nsXBLPrototypeHandler::GetController(EventTarget* aTarget)
   573 {
   574   // XXX Fix this so there's a generic interface that describes controllers,
   575   // This code should have no special knowledge of what objects might have controllers.
   576   nsCOMPtr<nsIControllers> controllers;
   578   nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aTarget));
   579   if (xulElement)
   580     xulElement->GetControllers(getter_AddRefs(controllers));
   582   if (!controllers) {
   583     nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea(do_QueryInterface(aTarget));
   584     if (htmlTextArea)
   585       htmlTextArea->GetControllers(getter_AddRefs(controllers));
   586   }
   588   if (!controllers) {
   589     nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement(do_QueryInterface(aTarget));
   590     if (htmlInputElement)
   591       htmlInputElement->GetControllers(getter_AddRefs(controllers));
   592   }
   594   if (!controllers) {
   595     nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(aTarget));
   596     if (domWindow)
   597       domWindow->GetControllers(getter_AddRefs(controllers));
   598   }
   600   // Return the first controller.
   601   // XXX This code should be checking the command name and using supportscommand and
   602   // iscommandenabled.
   603   nsCOMPtr<nsIController> controller;
   604   if (controllers) {
   605     controllers->GetControllerAt(0, getter_AddRefs(controller));
   606   }
   608   return controller.forget();
   609 }
   611 bool
   612 nsXBLPrototypeHandler::KeyEventMatched(nsIDOMKeyEvent* aKeyEvent,
   613                                        uint32_t aCharCode,
   614                                        bool aIgnoreShiftKey)
   615 {
   616   if (mDetail != -1) {
   617     // Get the keycode or charcode of the key event.
   618     uint32_t code;
   620     if (mMisc) {
   621       if (aCharCode)
   622         code = aCharCode;
   623       else
   624         aKeyEvent->GetCharCode(&code);
   625       if (IS_IN_BMP(code))
   626         code = ToLowerCase(char16_t(code));
   627     }
   628     else
   629       aKeyEvent->GetKeyCode(&code);
   631     if (code != uint32_t(mDetail))
   632       return false;
   633   }
   635   return ModifiersMatchMask(aKeyEvent, aIgnoreShiftKey);
   636 }
   638 bool
   639 nsXBLPrototypeHandler::MouseEventMatched(nsIDOMMouseEvent* aMouseEvent)
   640 {
   641   if (mDetail == -1 && mMisc == 0 && (mKeyMask & cAllModifiers) == 0)
   642     return true; // No filters set up. It's generic.
   644   int16_t button;
   645   aMouseEvent->GetButton(&button);
   646   if (mDetail != -1 && (button != mDetail))
   647     return false;
   649   int32_t clickcount;
   650   aMouseEvent->GetDetail(&clickcount);
   651   if (mMisc != 0 && (clickcount != mMisc))
   652     return false;
   654   return ModifiersMatchMask(aMouseEvent);
   655 }
   657 struct keyCodeData {
   658   const char* str;
   659   size_t strlength;
   660   uint32_t keycode;
   661 };
   663 // All of these must be uppercase, since the function below does
   664 // case-insensitive comparison by converting to uppercase.
   665 // XXX: be sure to check this periodically for new symbol additions!
   666 static const keyCodeData gKeyCodes[] = {
   668 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
   669   { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode }
   670 #include "mozilla/VirtualKeyCodeList.h"
   671 #undef NS_DEFINE_VK
   672 };
   674 int32_t nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString& aKeyName)
   675 {
   676   nsAutoCString keyName;
   677   keyName.AssignWithConversion(aKeyName);
   678   ToUpperCase(keyName); // We want case-insensitive comparison with data
   679                         // stored as uppercase.
   681   uint32_t keyNameLength = keyName.Length();
   682   const char* keyNameStr = keyName.get();
   683   for (uint16_t i = 0; i < (sizeof(gKeyCodes) / sizeof(gKeyCodes[0])); ++i)
   684     if (keyNameLength == gKeyCodes[i].strlength &&
   685         !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr))
   686       return gKeyCodes[i].keycode;
   688   return 0;
   689 }
   691 int32_t nsXBLPrototypeHandler::KeyToMask(int32_t key)
   692 {
   693   switch (key)
   694   {
   695     case nsIDOMKeyEvent::DOM_VK_META:
   696       return cMeta | cMetaMask;
   698     case nsIDOMKeyEvent::DOM_VK_WIN:
   699       return cOS | cOSMask;
   701     case nsIDOMKeyEvent::DOM_VK_ALT:
   702       return cAlt | cAltMask;
   704     case nsIDOMKeyEvent::DOM_VK_CONTROL:
   705     default:
   706       return cControl | cControlMask;
   707   }
   708   return cControl | cControlMask;  // for warning avoidance
   709 }
   711 void
   712 nsXBLPrototypeHandler::GetEventType(nsAString& aEvent)
   713 {
   714   nsCOMPtr<nsIContent> handlerElement = GetHandlerElement();
   715   if (!handlerElement) {
   716     aEvent.Truncate();
   717     return;
   718   }
   719   handlerElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, aEvent);
   721   if (aEvent.IsEmpty() && (mType & NS_HANDLER_TYPE_XUL))
   722     // If no type is specified for a XUL <key> element, let's assume that we're "keypress".
   723     aEvent.AssignLiteral("keypress");
   724 }
   726 void
   727 nsXBLPrototypeHandler::ConstructPrototype(nsIContent* aKeyElement, 
   728                                           const char16_t* aEvent,
   729                                           const char16_t* aPhase,
   730                                           const char16_t* aAction,
   731                                           const char16_t* aCommand,
   732                                           const char16_t* aKeyCode,
   733                                           const char16_t* aCharCode,
   734                                           const char16_t* aModifiers,
   735                                           const char16_t* aButton,
   736                                           const char16_t* aClickCount,
   737                                           const char16_t* aGroup,
   738                                           const char16_t* aPreventDefault,
   739                                           const char16_t* aAllowUntrusted)
   740 {
   741   mType = 0;
   743   if (aKeyElement) {
   744     mType |= NS_HANDLER_TYPE_XUL;
   745     nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aKeyElement);
   746     if (!weak) {
   747       return;
   748     }
   749     weak.swap(mHandlerElement);
   750   }
   751   else {
   752     mType |= aCommand ? NS_HANDLER_TYPE_XBL_COMMAND : NS_HANDLER_TYPE_XBL_JS;
   753     mHandlerText = nullptr;
   754   }
   756   mDetail = -1;
   757   mMisc = 0;
   758   mKeyMask = 0;
   759   mPhase = NS_PHASE_BUBBLING;
   761   if (aAction)
   762     mHandlerText = ToNewUnicode(nsDependentString(aAction));
   763   else if (aCommand)
   764     mHandlerText = ToNewUnicode(nsDependentString(aCommand));
   766   nsAutoString event(aEvent);
   767   if (event.IsEmpty()) {
   768     if (mType & NS_HANDLER_TYPE_XUL)
   769       GetEventType(event);
   770     if (event.IsEmpty())
   771       return;
   772   }
   774   mEventName = do_GetAtom(event);
   776   if (aPhase) {
   777     const nsDependentString phase(aPhase);
   778     if (phase.EqualsLiteral("capturing"))
   779       mPhase = NS_PHASE_CAPTURING;
   780     else if (phase.EqualsLiteral("target"))
   781       mPhase = NS_PHASE_TARGET;
   782   }
   784   // Button and clickcount apply only to XBL handlers and don't apply to XUL key
   785   // handlers.  
   786   if (aButton && *aButton)
   787     mDetail = *aButton - '0';
   789   if (aClickCount && *aClickCount)
   790     mMisc = *aClickCount - '0';
   792   // Modifiers are supported by both types of handlers (XUL and XBL).  
   793   nsAutoString modifiers(aModifiers);
   794   if (mType & NS_HANDLER_TYPE_XUL)
   795     aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiers);
   797   if (!modifiers.IsEmpty()) {
   798     mKeyMask = cAllModifiers;
   799     char* str = ToNewCString(modifiers);
   800     char* newStr;
   801     char* token = nsCRT::strtok( str, ", \t", &newStr );
   802     while( token != nullptr ) {
   803       if (PL_strcmp(token, "shift") == 0)
   804         mKeyMask |= cShift | cShiftMask;
   805       else if (PL_strcmp(token, "alt") == 0)
   806         mKeyMask |= cAlt | cAltMask;
   807       else if (PL_strcmp(token, "meta") == 0)
   808         mKeyMask |= cMeta | cMetaMask;
   809       else if (PL_strcmp(token, "os") == 0)
   810         mKeyMask |= cOS | cOSMask;
   811       else if (PL_strcmp(token, "control") == 0)
   812         mKeyMask |= cControl | cControlMask;
   813       else if (PL_strcmp(token, "accel") == 0)
   814         mKeyMask |= KeyToMask(kAccelKey);
   815       else if (PL_strcmp(token, "access") == 0)
   816         mKeyMask |= KeyToMask(kMenuAccessKey);
   817       else if (PL_strcmp(token, "any") == 0)
   818         mKeyMask &= ~(mKeyMask << 5);
   820       token = nsCRT::strtok( newStr, ", \t", &newStr );
   821     }
   823     nsMemory::Free(str);
   824   }
   826   nsAutoString key(aCharCode);
   827   if (key.IsEmpty()) {
   828     if (mType & NS_HANDLER_TYPE_XUL) {
   829       aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key);
   830       if (key.IsEmpty()) 
   831         aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, key);
   832     }
   833   }
   835   if (!key.IsEmpty()) {
   836     if (mKeyMask == 0)
   837       mKeyMask = cAllModifiers;
   838     ToLowerCase(key);
   840     // We have a charcode.
   841     mMisc = 1;
   842     mDetail = key[0];
   843     const uint8_t GTK2Modifiers = cShift | cControl | cShiftMask | cControlMask;
   844     if ((mKeyMask & GTK2Modifiers) == GTK2Modifiers &&
   845         modifiers.First() != char16_t(',') &&
   846         (mDetail == 'u' || mDetail == 'U'))
   847       ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "GTK2Conflict");
   848     const uint8_t WinModifiers = cControl | cAlt | cControlMask | cAltMask;
   849     if ((mKeyMask & WinModifiers) == WinModifiers &&
   850         modifiers.First() != char16_t(',') &&
   851         (('A' <= mDetail && mDetail <= 'Z') ||
   852          ('a' <= mDetail && mDetail <= 'z')))
   853       ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "WinConflict");
   854   }
   855   else {
   856     key.Assign(aKeyCode);
   857     if (mType & NS_HANDLER_TYPE_XUL)
   858       aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, key);
   860     if (!key.IsEmpty()) {
   861       if (mKeyMask == 0)
   862         mKeyMask = cAllModifiers;
   863       mDetail = GetMatchingKeyCode(key);
   864     }
   865   }
   867   if (aGroup && nsDependentString(aGroup).EqualsLiteral("system"))
   868     mType |= NS_HANDLER_TYPE_SYSTEM;
   870   if (aPreventDefault &&
   871       nsDependentString(aPreventDefault).EqualsLiteral("true"))
   872     mType |= NS_HANDLER_TYPE_PREVENTDEFAULT;
   874   if (aAllowUntrusted) {
   875     mType |= NS_HANDLER_HAS_ALLOW_UNTRUSTED_ATTR;
   876     if (nsDependentString(aAllowUntrusted).EqualsLiteral("true")) {
   877       mType |= NS_HANDLER_ALLOW_UNTRUSTED;
   878     } else {
   879       mType &= ~NS_HANDLER_ALLOW_UNTRUSTED;
   880     }
   881   }
   882 }
   884 void
   885 nsXBLPrototypeHandler::ReportKeyConflict(const char16_t* aKey, const char16_t* aModifiers, nsIContent* aKeyElement, const char *aMessageName)
   886 {
   887   nsCOMPtr<nsIDocument> doc;
   888   if (mPrototypeBinding) {
   889     nsXBLDocumentInfo* docInfo = mPrototypeBinding->XBLDocumentInfo();
   890     if (docInfo) {
   891       doc = docInfo->GetDocument();
   892     }
   893   } else if (aKeyElement) {
   894     doc = aKeyElement->OwnerDoc();
   895   }
   897   const char16_t* params[] = { aKey, aModifiers };
   898   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   899                                   NS_LITERAL_CSTRING("XBL Prototype Handler"), doc,
   900                                   nsContentUtils::eXBL_PROPERTIES,
   901                                   aMessageName,
   902                                   params, ArrayLength(params),
   903                                   nullptr, EmptyString(), mLineNumber);
   904 }
   906 bool
   907 nsXBLPrototypeHandler::ModifiersMatchMask(nsIDOMUIEvent* aEvent,
   908                                           bool aIgnoreShiftKey)
   909 {
   910   WidgetInputEvent* inputEvent = aEvent->GetInternalNSEvent()->AsInputEvent();
   911   NS_ENSURE_TRUE(inputEvent, false);
   913   if (mKeyMask & cMetaMask) {
   914     if (inputEvent->IsMeta() != ((mKeyMask & cMeta) != 0)) {
   915       return false;
   916     }
   917   }
   919   if (mKeyMask & cOSMask) {
   920     if (inputEvent->IsOS() != ((mKeyMask & cOS) != 0)) {
   921       return false;
   922     }
   923   }
   925   if (mKeyMask & cShiftMask && !aIgnoreShiftKey) {
   926     if (inputEvent->IsShift() != ((mKeyMask & cShift) != 0)) {
   927       return false;
   928     }
   929   }
   931   if (mKeyMask & cAltMask) {
   932     if (inputEvent->IsAlt() != ((mKeyMask & cAlt) != 0)) {
   933       return false;
   934     }
   935   }
   937   if (mKeyMask & cControlMask) {
   938     if (inputEvent->IsControl() != ((mKeyMask & cControl) != 0)) {
   939       return false;
   940     }
   941   }
   943   return true;
   944 }
   946 nsresult
   947 nsXBLPrototypeHandler::Read(nsIObjectInputStream* aStream)
   948 {
   949   AssertInCompilationScope();
   950   nsresult rv = aStream->Read8(&mPhase);
   951   NS_ENSURE_SUCCESS(rv, rv);
   952   rv = aStream->Read8(&mType);
   953   NS_ENSURE_SUCCESS(rv, rv);
   954   rv = aStream->Read8(&mMisc);
   955   NS_ENSURE_SUCCESS(rv, rv);
   957   rv = aStream->Read32(reinterpret_cast<uint32_t*>(&mKeyMask));
   958   NS_ENSURE_SUCCESS(rv, rv);
   959   uint32_t detail; 
   960   rv = aStream->Read32(&detail);
   961   NS_ENSURE_SUCCESS(rv, rv);
   962   mDetail = detail;
   964   nsAutoString name;
   965   rv = aStream->ReadString(name);
   966   NS_ENSURE_SUCCESS(rv, rv);
   967   mEventName = do_GetAtom(name);
   969   rv = aStream->Read32(&mLineNumber);
   970   NS_ENSURE_SUCCESS(rv, rv);
   972   nsAutoString handlerText;
   973   rv = aStream->ReadString(handlerText);
   974   NS_ENSURE_SUCCESS(rv, rv);
   975   if (!handlerText.IsEmpty())
   976     mHandlerText = ToNewUnicode(handlerText);
   978   return NS_OK;
   979 }
   981 nsresult
   982 nsXBLPrototypeHandler::Write(nsIObjectOutputStream* aStream)
   983 {
   984   AssertInCompilationScope();
   985   // Make sure we don't write out NS_HANDLER_TYPE_XUL types, as they are used
   986   // for <keyset> elements.
   987   if ((mType & NS_HANDLER_TYPE_XUL) || !mEventName)
   988     return NS_OK;
   990   XBLBindingSerializeDetails type = XBLBinding_Serialize_Handler;
   992   nsresult rv = aStream->Write8(type);
   993   rv = aStream->Write8(mPhase);
   994   NS_ENSURE_SUCCESS(rv, rv);
   995   rv = aStream->Write8(mType);
   996   NS_ENSURE_SUCCESS(rv, rv);
   997   rv = aStream->Write8(mMisc);
   998   NS_ENSURE_SUCCESS(rv, rv);
   999   rv = aStream->Write32(static_cast<uint32_t>(mKeyMask));
  1000   NS_ENSURE_SUCCESS(rv, rv);
  1001   rv = aStream->Write32(mDetail);
  1002   NS_ENSURE_SUCCESS(rv, rv);
  1004   rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
  1005   NS_ENSURE_SUCCESS(rv, rv);
  1007   rv = aStream->Write32(mLineNumber);
  1008   NS_ENSURE_SUCCESS(rv, rv);
  1009   return aStream->WriteWStringZ(mHandlerText ? mHandlerText : MOZ_UTF16(""));

mercurial