1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/xbl/nsXBLPrototypeHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1010 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/ArrayUtils.h" 1.10 + 1.11 +#include "nsCOMPtr.h" 1.12 +#include "nsXBLPrototypeHandler.h" 1.13 +#include "nsXBLPrototypeBinding.h" 1.14 +#include "nsContentUtils.h" 1.15 +#include "nsCxPusher.h" 1.16 +#include "nsIContent.h" 1.17 +#include "nsIAtom.h" 1.18 +#include "nsIDOMKeyEvent.h" 1.19 +#include "nsIDOMMouseEvent.h" 1.20 +#include "nsNameSpaceManager.h" 1.21 +#include "nsIScriptContext.h" 1.22 +#include "nsIDocument.h" 1.23 +#include "nsIController.h" 1.24 +#include "nsIControllers.h" 1.25 +#include "nsIDOMXULElement.h" 1.26 +#include "nsIURI.h" 1.27 +#include "nsIDOMHTMLTextAreaElement.h" 1.28 +#include "nsIDOMHTMLInputElement.h" 1.29 +#include "nsFocusManager.h" 1.30 +#include "nsIDOMEventListener.h" 1.31 +#include "nsPIDOMWindow.h" 1.32 +#include "nsPIWindowRoot.h" 1.33 +#include "nsIDOMWindow.h" 1.34 +#include "nsIServiceManager.h" 1.35 +#include "nsIScriptError.h" 1.36 +#include "nsXPIDLString.h" 1.37 +#include "nsReadableUtils.h" 1.38 +#include "nsGkAtoms.h" 1.39 +#include "nsIXPConnect.h" 1.40 +#include "nsIDOMScriptObjectFactory.h" 1.41 +#include "nsDOMCID.h" 1.42 +#include "nsUnicharUtils.h" 1.43 +#include "nsCRT.h" 1.44 +#include "nsXBLEventHandler.h" 1.45 +#include "nsXBLSerialize.h" 1.46 +#include "nsJSUtils.h" 1.47 +#include "mozilla/BasicEvents.h" 1.48 +#include "mozilla/JSEventHandler.h" 1.49 +#include "mozilla/Preferences.h" 1.50 +#include "mozilla/dom/EventHandlerBinding.h" 1.51 + 1.52 +using namespace mozilla; 1.53 +using namespace mozilla::dom; 1.54 + 1.55 +uint32_t nsXBLPrototypeHandler::gRefCnt = 0; 1.56 + 1.57 +int32_t nsXBLPrototypeHandler::kMenuAccessKey = -1; 1.58 +int32_t nsXBLPrototypeHandler::kAccelKey = -1; 1.59 + 1.60 +const int32_t nsXBLPrototypeHandler::cShift = (1<<0); 1.61 +const int32_t nsXBLPrototypeHandler::cAlt = (1<<1); 1.62 +const int32_t nsXBLPrototypeHandler::cControl = (1<<2); 1.63 +const int32_t nsXBLPrototypeHandler::cMeta = (1<<3); 1.64 +const int32_t nsXBLPrototypeHandler::cOS = (1<<4); 1.65 + 1.66 +const int32_t nsXBLPrototypeHandler::cShiftMask = (1<<5); 1.67 +const int32_t nsXBLPrototypeHandler::cAltMask = (1<<6); 1.68 +const int32_t nsXBLPrototypeHandler::cControlMask = (1<<7); 1.69 +const int32_t nsXBLPrototypeHandler::cMetaMask = (1<<8); 1.70 +const int32_t nsXBLPrototypeHandler::cOSMask = (1<<9); 1.71 + 1.72 +const int32_t nsXBLPrototypeHandler::cAllModifiers = 1.73 + cShiftMask | cAltMask | cControlMask | cMetaMask | cOSMask; 1.74 + 1.75 +nsXBLPrototypeHandler::nsXBLPrototypeHandler(const char16_t* aEvent, 1.76 + const char16_t* aPhase, 1.77 + const char16_t* aAction, 1.78 + const char16_t* aCommand, 1.79 + const char16_t* aKeyCode, 1.80 + const char16_t* aCharCode, 1.81 + const char16_t* aModifiers, 1.82 + const char16_t* aButton, 1.83 + const char16_t* aClickCount, 1.84 + const char16_t* aGroup, 1.85 + const char16_t* aPreventDefault, 1.86 + const char16_t* aAllowUntrusted, 1.87 + nsXBLPrototypeBinding* aBinding, 1.88 + uint32_t aLineNumber) 1.89 + : mHandlerText(nullptr), 1.90 + mLineNumber(aLineNumber), 1.91 + mNextHandler(nullptr), 1.92 + mPrototypeBinding(aBinding) 1.93 +{ 1.94 + Init(); 1.95 + 1.96 + ConstructPrototype(nullptr, aEvent, aPhase, aAction, aCommand, aKeyCode, 1.97 + aCharCode, aModifiers, aButton, aClickCount, 1.98 + aGroup, aPreventDefault, aAllowUntrusted); 1.99 +} 1.100 + 1.101 +nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement) 1.102 + : mHandlerElement(nullptr), 1.103 + mLineNumber(0), 1.104 + mNextHandler(nullptr), 1.105 + mPrototypeBinding(nullptr) 1.106 +{ 1.107 + Init(); 1.108 + 1.109 + // Make sure our prototype is initialized. 1.110 + ConstructPrototype(aHandlerElement); 1.111 +} 1.112 + 1.113 +nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding) 1.114 + : mHandlerText(nullptr), 1.115 + mLineNumber(0), 1.116 + mNextHandler(nullptr), 1.117 + mPrototypeBinding(aBinding) 1.118 +{ 1.119 + Init(); 1.120 +} 1.121 + 1.122 +nsXBLPrototypeHandler::~nsXBLPrototypeHandler() 1.123 +{ 1.124 + --gRefCnt; 1.125 + if (mType & NS_HANDLER_TYPE_XUL) { 1.126 + NS_IF_RELEASE(mHandlerElement); 1.127 + } else if (mHandlerText) { 1.128 + nsMemory::Free(mHandlerText); 1.129 + } 1.130 + 1.131 + // We own the next handler in the chain, so delete it now. 1.132 + NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler); 1.133 +} 1.134 + 1.135 +already_AddRefed<nsIContent> 1.136 +nsXBLPrototypeHandler::GetHandlerElement() 1.137 +{ 1.138 + if (mType & NS_HANDLER_TYPE_XUL) { 1.139 + nsCOMPtr<nsIContent> element = do_QueryReferent(mHandlerElement); 1.140 + return element.forget(); 1.141 + } 1.142 + 1.143 + return nullptr; 1.144 +} 1.145 + 1.146 +void 1.147 +nsXBLPrototypeHandler::AppendHandlerText(const nsAString& aText) 1.148 +{ 1.149 + if (mHandlerText) { 1.150 + // Append our text to the existing text. 1.151 + char16_t* temp = mHandlerText; 1.152 + mHandlerText = ToNewUnicode(nsDependentString(temp) + aText); 1.153 + nsMemory::Free(temp); 1.154 + } 1.155 + else { 1.156 + mHandlerText = ToNewUnicode(aText); 1.157 + } 1.158 +} 1.159 + 1.160 +///////////////////////////////////////////////////////////////////////////// 1.161 +// Get the menu access key from prefs. 1.162 +// XXX Eventually pick up using CSS3 key-equivalent property or somesuch 1.163 +void 1.164 +nsXBLPrototypeHandler::InitAccessKeys() 1.165 +{ 1.166 + if (kAccelKey >= 0 && kMenuAccessKey >= 0) 1.167 + return; 1.168 + 1.169 + // Compiled-in defaults, in case we can't get the pref -- 1.170 + // mac doesn't have menu shortcuts, other platforms use alt. 1.171 +#ifdef XP_MACOSX 1.172 + kMenuAccessKey = 0; 1.173 + kAccelKey = nsIDOMKeyEvent::DOM_VK_META; 1.174 +#else 1.175 + kMenuAccessKey = nsIDOMKeyEvent::DOM_VK_ALT; 1.176 + kAccelKey = nsIDOMKeyEvent::DOM_VK_CONTROL; 1.177 +#endif 1.178 + 1.179 + // Get the menu access key value from prefs, overriding the default: 1.180 + kMenuAccessKey = 1.181 + Preferences::GetInt("ui.key.menuAccessKey", kMenuAccessKey); 1.182 + kAccelKey = Preferences::GetInt("ui.key.accelKey", kAccelKey); 1.183 +} 1.184 + 1.185 +nsresult 1.186 +nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget, 1.187 + nsIDOMEvent* aEvent) 1.188 +{ 1.189 + nsresult rv = NS_ERROR_FAILURE; 1.190 + 1.191 + // Prevent default action? 1.192 + if (mType & NS_HANDLER_TYPE_PREVENTDEFAULT) { 1.193 + aEvent->PreventDefault(); 1.194 + // If we prevent default, then it's okay for 1.195 + // mHandlerElement and mHandlerText to be null 1.196 + rv = NS_OK; 1.197 + } 1.198 + 1.199 + if (!mHandlerElement) // This works for both types of handlers. In both cases, the union's var should be defined. 1.200 + return rv; 1.201 + 1.202 + // See if our event receiver is a content node (and not us). 1.203 + bool isXULKey = !!(mType & NS_HANDLER_TYPE_XUL); 1.204 + bool isXBLCommand = !!(mType & NS_HANDLER_TYPE_XBL_COMMAND); 1.205 + NS_ASSERTION(!(isXULKey && isXBLCommand), 1.206 + "can't be both a key and xbl command handler"); 1.207 + 1.208 + // XUL handlers and commands shouldn't be triggered by non-trusted 1.209 + // events. 1.210 + if (isXULKey || isXBLCommand) { 1.211 + bool trustedEvent = false; 1.212 + aEvent->GetIsTrusted(&trustedEvent); 1.213 + 1.214 + if (!trustedEvent) 1.215 + return NS_OK; 1.216 + } 1.217 + 1.218 + if (isXBLCommand) { 1.219 + return DispatchXBLCommand(aTarget, aEvent); 1.220 + } 1.221 + 1.222 + // If we're executing on a XUL key element, just dispatch a command 1.223 + // event at the element. It will take care of retargeting it to its 1.224 + // command element, if applicable, and executing the event handler. 1.225 + if (isXULKey) { 1.226 + return DispatchXULKeyCommand(aEvent); 1.227 + } 1.228 + 1.229 + // Look for a compiled handler on the element. 1.230 + // Should be compiled and bound with "on" in front of the name. 1.231 + nsCOMPtr<nsIAtom> onEventAtom = do_GetAtom(NS_LITERAL_STRING("onxbl") + 1.232 + nsDependentAtomString(mEventName)); 1.233 + 1.234 + // Compile the handler and bind it to the element. 1.235 + nsCOMPtr<nsIScriptGlobalObject> boundGlobal; 1.236 + nsCOMPtr<nsPIWindowRoot> winRoot(do_QueryInterface(aTarget)); 1.237 + nsCOMPtr<nsPIDOMWindow> window; 1.238 + 1.239 + if (winRoot) { 1.240 + window = winRoot->GetWindow(); 1.241 + } 1.242 + 1.243 + if (window) { 1.244 + window = window->GetCurrentInnerWindow(); 1.245 + NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED); 1.246 + 1.247 + boundGlobal = do_QueryInterface(window->GetPrivateRoot()); 1.248 + } 1.249 + else boundGlobal = do_QueryInterface(aTarget); 1.250 + 1.251 + if (!boundGlobal) { 1.252 + nsCOMPtr<nsIDocument> boundDocument(do_QueryInterface(aTarget)); 1.253 + if (!boundDocument) { 1.254 + // We must be an element. 1.255 + nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget)); 1.256 + if (!content) 1.257 + return NS_OK; 1.258 + boundDocument = content->OwnerDoc(); 1.259 + } 1.260 + 1.261 + boundGlobal = do_QueryInterface(boundDocument->GetScopeObject()); 1.262 + } 1.263 + 1.264 + if (!boundGlobal) 1.265 + return NS_OK; 1.266 + 1.267 + nsIScriptContext *boundContext = boundGlobal->GetScriptContext(); 1.268 + if (!boundContext) 1.269 + return NS_OK; 1.270 + 1.271 + nsISupports *scriptTarget; 1.272 + 1.273 + if (winRoot) { 1.274 + scriptTarget = boundGlobal; 1.275 + } else { 1.276 + scriptTarget = aTarget; 1.277 + } 1.278 + 1.279 + // We're about to create a new JSEventHandler, which means that we're 1.280 + // responsible for pushing the context of the event target. See the similar 1.281 + // comment in nsEventManagerListener.cpp. 1.282 + nsCxPusher pusher; 1.283 + NS_ENSURE_STATE(pusher.Push(aTarget)); 1.284 + 1.285 + AutoPushJSContext cx(boundContext->GetNativeContext()); 1.286 + JS::Rooted<JSObject*> handler(cx); 1.287 + 1.288 + rv = EnsureEventHandler(boundGlobal, boundContext, onEventAtom, &handler); 1.289 + NS_ENSURE_SUCCESS(rv, rv); 1.290 + 1.291 + JS::Rooted<JSObject*> globalObject(cx, boundGlobal->GetGlobalJSObject()); 1.292 + JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject)); 1.293 + NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); 1.294 + 1.295 + // Bind it to the bound element. Note that if we're using a separate XBL scope, 1.296 + // we'll actually be binding the event handler to a cross-compartment wrapper 1.297 + // to the bound element's reflector. 1.298 + 1.299 + // First, enter our XBL scope. This is where the generic handler should have 1.300 + // been compiled, above. 1.301 + JSAutoCompartment ac(cx, scopeObject); 1.302 + JS::Rooted<JSObject*> genericHandler(cx, handler.get()); 1.303 + bool ok = JS_WrapObject(cx, &genericHandler); 1.304 + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 1.305 + MOZ_ASSERT(!js::IsCrossCompartmentWrapper(genericHandler)); 1.306 + 1.307 + // Wrap the native into the XBL scope. This creates a reflector in the document 1.308 + // scope if one doesn't already exist, and potentially wraps it cross- 1.309 + // compartment into our scope (via aAllowWrapping=true). 1.310 + JS::Rooted<JS::Value> targetV(cx, JS::UndefinedValue()); 1.311 + rv = nsContentUtils::WrapNative(cx, scriptTarget, &targetV); 1.312 + NS_ENSURE_SUCCESS(rv, rv); 1.313 + 1.314 + // Next, clone the generic handler to be parented to the target. 1.315 + JS::Rooted<JSObject*> target(cx, &targetV.toObject()); 1.316 + JS::Rooted<JSObject*> bound(cx, JS_CloneFunctionObject(cx, genericHandler, target)); 1.317 + NS_ENSURE_TRUE(bound, NS_ERROR_FAILURE); 1.318 + 1.319 + // Now, wrap the bound handler into the content compartment and use it. 1.320 + JSAutoCompartment ac2(cx, globalObject); 1.321 + if (!JS_WrapObject(cx, &bound)) { 1.322 + return NS_ERROR_FAILURE; 1.323 + } 1.324 + 1.325 + nsRefPtr<EventHandlerNonNull> handlerCallback = 1.326 + new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr); 1.327 + 1.328 + TypedEventHandler typedHandler(handlerCallback); 1.329 + 1.330 + // Execute it. 1.331 + nsCOMPtr<JSEventHandler> jsEventHandler; 1.332 + rv = NS_NewJSEventHandler(scriptTarget, onEventAtom, 1.333 + typedHandler, 1.334 + getter_AddRefs(jsEventHandler)); 1.335 + NS_ENSURE_SUCCESS(rv, rv); 1.336 + 1.337 + // Handle the event. 1.338 + jsEventHandler->HandleEvent(aEvent); 1.339 + jsEventHandler->Disconnect(); 1.340 + return NS_OK; 1.341 +} 1.342 + 1.343 +nsresult 1.344 +nsXBLPrototypeHandler::EnsureEventHandler(nsIScriptGlobalObject* aGlobal, 1.345 + nsIScriptContext *aBoundContext, 1.346 + nsIAtom *aName, 1.347 + JS::MutableHandle<JSObject*> aHandler) 1.348 +{ 1.349 + AutoPushJSContext cx(aBoundContext->GetNativeContext()); 1.350 + 1.351 + // Check to see if we've already compiled this 1.352 + nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aGlobal); 1.353 + if (pWindow) { 1.354 + JS::Rooted<JSObject*> cachedHandler(cx, pWindow->GetCachedXBLPrototypeHandler(this)); 1.355 + if (cachedHandler) { 1.356 + JS::ExposeObjectToActiveJS(cachedHandler); 1.357 + aHandler.set(cachedHandler); 1.358 + NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE); 1.359 + return NS_OK; 1.360 + } 1.361 + } 1.362 + 1.363 + // Ensure that we have something to compile 1.364 + nsDependentString handlerText(mHandlerText); 1.365 + NS_ENSURE_TRUE(!handlerText.IsEmpty(), NS_ERROR_FAILURE); 1.366 + 1.367 + JS::Rooted<JSObject*> globalObject(cx, aGlobal->GetGlobalJSObject()); 1.368 + JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject)); 1.369 + NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY); 1.370 + 1.371 + nsAutoCString bindingURI; 1.372 + mPrototypeBinding->DocURI()->GetSpec(bindingURI); 1.373 + 1.374 + uint32_t argCount; 1.375 + const char **argNames; 1.376 + nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, &argCount, 1.377 + &argNames); 1.378 + 1.379 + // Compile the event handler in the xbl scope. 1.380 + JSAutoCompartment ac(cx, scopeObject); 1.381 + JS::CompileOptions options(cx); 1.382 + options.setFileAndLine(bindingURI.get(), mLineNumber) 1.383 + .setVersion(JSVERSION_LATEST); 1.384 + 1.385 + JS::Rooted<JSObject*> handlerFun(cx); 1.386 + nsresult rv = nsJSUtils::CompileFunction(cx, JS::NullPtr(), options, 1.387 + nsAtomCString(aName), argCount, 1.388 + argNames, handlerText, 1.389 + handlerFun.address()); 1.390 + NS_ENSURE_SUCCESS(rv, rv); 1.391 + NS_ENSURE_TRUE(handlerFun, NS_ERROR_FAILURE); 1.392 + 1.393 + // Wrap the handler into the content scope, since we're about to stash it 1.394 + // on the DOM window and such. 1.395 + JSAutoCompartment ac2(cx, globalObject); 1.396 + bool ok = JS_WrapObject(cx, &handlerFun); 1.397 + NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 1.398 + aHandler.set(handlerFun); 1.399 + NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE); 1.400 + 1.401 + if (pWindow) { 1.402 + pWindow->CacheXBLPrototypeHandler(this, aHandler); 1.403 + } 1.404 + 1.405 + return NS_OK; 1.406 +} 1.407 + 1.408 +nsresult 1.409 +nsXBLPrototypeHandler::DispatchXBLCommand(EventTarget* aTarget, nsIDOMEvent* aEvent) 1.410 +{ 1.411 + // This is a special-case optimization to make command handling fast. 1.412 + // It isn't really a part of XBL, but it helps speed things up. 1.413 + 1.414 + if (aEvent) { 1.415 + // See if preventDefault has been set. If so, don't execute. 1.416 + bool preventDefault = false; 1.417 + aEvent->GetDefaultPrevented(&preventDefault); 1.418 + if (preventDefault) { 1.419 + return NS_OK; 1.420 + } 1.421 + bool dispatchStopped = aEvent->IsDispatchStopped(); 1.422 + if (dispatchStopped) { 1.423 + return NS_OK; 1.424 + } 1.425 + } 1.426 + 1.427 + // Instead of executing JS, let's get the controller for the bound 1.428 + // element and call doCommand on it. 1.429 + nsCOMPtr<nsIController> controller; 1.430 + 1.431 + nsCOMPtr<nsPIDOMWindow> privateWindow; 1.432 + nsCOMPtr<nsPIWindowRoot> windowRoot(do_QueryInterface(aTarget)); 1.433 + if (windowRoot) { 1.434 + privateWindow = windowRoot->GetWindow(); 1.435 + } 1.436 + else { 1.437 + privateWindow = do_QueryInterface(aTarget); 1.438 + if (!privateWindow) { 1.439 + nsCOMPtr<nsIContent> elt(do_QueryInterface(aTarget)); 1.440 + nsCOMPtr<nsIDocument> doc; 1.441 + // XXXbz sXBL/XBL2 issue -- this should be the "scope doc" or 1.442 + // something... whatever we use when wrapping DOM nodes 1.443 + // normally. It's not clear that the owner doc is the right 1.444 + // thing. 1.445 + if (elt) 1.446 + doc = elt->OwnerDoc(); 1.447 + 1.448 + if (!doc) 1.449 + doc = do_QueryInterface(aTarget); 1.450 + 1.451 + if (!doc) 1.452 + return NS_ERROR_FAILURE; 1.453 + 1.454 + privateWindow = doc->GetWindow(); 1.455 + if (!privateWindow) 1.456 + return NS_ERROR_FAILURE; 1.457 + } 1.458 + 1.459 + windowRoot = privateWindow->GetTopWindowRoot(); 1.460 + } 1.461 + 1.462 + NS_LossyConvertUTF16toASCII command(mHandlerText); 1.463 + if (windowRoot) 1.464 + windowRoot->GetControllerForCommand(command.get(), getter_AddRefs(controller)); 1.465 + else 1.466 + controller = GetController(aTarget); // We're attached to the receiver possibly. 1.467 + 1.468 + if (mEventName == nsGkAtoms::keypress && 1.469 + mDetail == nsIDOMKeyEvent::DOM_VK_SPACE && 1.470 + mMisc == 1) { 1.471 + // get the focused element so that we can pageDown only at 1.472 + // certain times. 1.473 + 1.474 + nsCOMPtr<nsPIDOMWindow> windowToCheck; 1.475 + if (windowRoot) 1.476 + windowToCheck = windowRoot->GetWindow(); 1.477 + else 1.478 + windowToCheck = privateWindow->GetPrivateRoot(); 1.479 + 1.480 + nsCOMPtr<nsIContent> focusedContent; 1.481 + if (windowToCheck) { 1.482 + nsCOMPtr<nsPIDOMWindow> focusedWindow; 1.483 + focusedContent = 1.484 + nsFocusManager::GetFocusedDescendant(windowToCheck, true, getter_AddRefs(focusedWindow)); 1.485 + } 1.486 + 1.487 + bool isLink = false; 1.488 + nsIContent *content = focusedContent; 1.489 + 1.490 + // if the focused element is a link then we do want space to 1.491 + // scroll down. The focused element may be an element in a link, 1.492 + // we need to check the parent node too. Only do this check if an 1.493 + // element is focused and has a parent. 1.494 + if (focusedContent && focusedContent->GetParent()) { 1.495 + while (content) { 1.496 + if (content->Tag() == nsGkAtoms::a && content->IsHTML()) { 1.497 + isLink = true; 1.498 + break; 1.499 + } 1.500 + 1.501 + if (content->HasAttr(kNameSpaceID_XLink, nsGkAtoms::type)) { 1.502 + isLink = content->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type, 1.503 + nsGkAtoms::simple, eCaseMatters); 1.504 + 1.505 + if (isLink) { 1.506 + break; 1.507 + } 1.508 + } 1.509 + 1.510 + content = content->GetParent(); 1.511 + } 1.512 + 1.513 + if (!isLink) 1.514 + return NS_OK; 1.515 + } 1.516 + } 1.517 + 1.518 + // We are the default action for this command. 1.519 + // Stop any other default action from executing. 1.520 + aEvent->PreventDefault(); 1.521 + 1.522 + if (controller) 1.523 + controller->DoCommand(command.get()); 1.524 + 1.525 + return NS_OK; 1.526 +} 1.527 + 1.528 +nsresult 1.529 +nsXBLPrototypeHandler::DispatchXULKeyCommand(nsIDOMEvent* aEvent) 1.530 +{ 1.531 + nsCOMPtr<nsIContent> handlerElement = GetHandlerElement(); 1.532 + NS_ENSURE_STATE(handlerElement); 1.533 + if (handlerElement->AttrValueIs(kNameSpaceID_None, 1.534 + nsGkAtoms::disabled, 1.535 + nsGkAtoms::_true, 1.536 + eCaseMatters)) { 1.537 + // Don't dispatch command events for disabled keys. 1.538 + return NS_OK; 1.539 + } 1.540 + 1.541 + aEvent->PreventDefault(); 1.542 + 1.543 + // Copy the modifiers from the key event. 1.544 + nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent); 1.545 + if (!keyEvent) { 1.546 + NS_ERROR("Trying to execute a key handler for a non-key event!"); 1.547 + return NS_ERROR_FAILURE; 1.548 + } 1.549 + 1.550 + // XXX We should use mozilla::Modifiers for supporting all modifiers. 1.551 + 1.552 + bool isAlt = false; 1.553 + bool isControl = false; 1.554 + bool isShift = false; 1.555 + bool isMeta = false; 1.556 + keyEvent->GetAltKey(&isAlt); 1.557 + keyEvent->GetCtrlKey(&isControl); 1.558 + keyEvent->GetShiftKey(&isShift); 1.559 + keyEvent->GetMetaKey(&isMeta); 1.560 + 1.561 + nsContentUtils::DispatchXULCommand(handlerElement, true, 1.562 + nullptr, nullptr, 1.563 + isControl, isAlt, isShift, isMeta); 1.564 + return NS_OK; 1.565 +} 1.566 + 1.567 +already_AddRefed<nsIAtom> 1.568 +nsXBLPrototypeHandler::GetEventName() 1.569 +{ 1.570 + nsCOMPtr<nsIAtom> eventName = mEventName; 1.571 + return eventName.forget(); 1.572 +} 1.573 + 1.574 +already_AddRefed<nsIController> 1.575 +nsXBLPrototypeHandler::GetController(EventTarget* aTarget) 1.576 +{ 1.577 + // XXX Fix this so there's a generic interface that describes controllers, 1.578 + // This code should have no special knowledge of what objects might have controllers. 1.579 + nsCOMPtr<nsIControllers> controllers; 1.580 + 1.581 + nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aTarget)); 1.582 + if (xulElement) 1.583 + xulElement->GetControllers(getter_AddRefs(controllers)); 1.584 + 1.585 + if (!controllers) { 1.586 + nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea(do_QueryInterface(aTarget)); 1.587 + if (htmlTextArea) 1.588 + htmlTextArea->GetControllers(getter_AddRefs(controllers)); 1.589 + } 1.590 + 1.591 + if (!controllers) { 1.592 + nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement(do_QueryInterface(aTarget)); 1.593 + if (htmlInputElement) 1.594 + htmlInputElement->GetControllers(getter_AddRefs(controllers)); 1.595 + } 1.596 + 1.597 + if (!controllers) { 1.598 + nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(aTarget)); 1.599 + if (domWindow) 1.600 + domWindow->GetControllers(getter_AddRefs(controllers)); 1.601 + } 1.602 + 1.603 + // Return the first controller. 1.604 + // XXX This code should be checking the command name and using supportscommand and 1.605 + // iscommandenabled. 1.606 + nsCOMPtr<nsIController> controller; 1.607 + if (controllers) { 1.608 + controllers->GetControllerAt(0, getter_AddRefs(controller)); 1.609 + } 1.610 + 1.611 + return controller.forget(); 1.612 +} 1.613 + 1.614 +bool 1.615 +nsXBLPrototypeHandler::KeyEventMatched(nsIDOMKeyEvent* aKeyEvent, 1.616 + uint32_t aCharCode, 1.617 + bool aIgnoreShiftKey) 1.618 +{ 1.619 + if (mDetail != -1) { 1.620 + // Get the keycode or charcode of the key event. 1.621 + uint32_t code; 1.622 + 1.623 + if (mMisc) { 1.624 + if (aCharCode) 1.625 + code = aCharCode; 1.626 + else 1.627 + aKeyEvent->GetCharCode(&code); 1.628 + if (IS_IN_BMP(code)) 1.629 + code = ToLowerCase(char16_t(code)); 1.630 + } 1.631 + else 1.632 + aKeyEvent->GetKeyCode(&code); 1.633 + 1.634 + if (code != uint32_t(mDetail)) 1.635 + return false; 1.636 + } 1.637 + 1.638 + return ModifiersMatchMask(aKeyEvent, aIgnoreShiftKey); 1.639 +} 1.640 + 1.641 +bool 1.642 +nsXBLPrototypeHandler::MouseEventMatched(nsIDOMMouseEvent* aMouseEvent) 1.643 +{ 1.644 + if (mDetail == -1 && mMisc == 0 && (mKeyMask & cAllModifiers) == 0) 1.645 + return true; // No filters set up. It's generic. 1.646 + 1.647 + int16_t button; 1.648 + aMouseEvent->GetButton(&button); 1.649 + if (mDetail != -1 && (button != mDetail)) 1.650 + return false; 1.651 + 1.652 + int32_t clickcount; 1.653 + aMouseEvent->GetDetail(&clickcount); 1.654 + if (mMisc != 0 && (clickcount != mMisc)) 1.655 + return false; 1.656 + 1.657 + return ModifiersMatchMask(aMouseEvent); 1.658 +} 1.659 + 1.660 +struct keyCodeData { 1.661 + const char* str; 1.662 + size_t strlength; 1.663 + uint32_t keycode; 1.664 +}; 1.665 + 1.666 +// All of these must be uppercase, since the function below does 1.667 +// case-insensitive comparison by converting to uppercase. 1.668 +// XXX: be sure to check this periodically for new symbol additions! 1.669 +static const keyCodeData gKeyCodes[] = { 1.670 + 1.671 +#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \ 1.672 + { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode } 1.673 +#include "mozilla/VirtualKeyCodeList.h" 1.674 +#undef NS_DEFINE_VK 1.675 +}; 1.676 + 1.677 +int32_t nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString& aKeyName) 1.678 +{ 1.679 + nsAutoCString keyName; 1.680 + keyName.AssignWithConversion(aKeyName); 1.681 + ToUpperCase(keyName); // We want case-insensitive comparison with data 1.682 + // stored as uppercase. 1.683 + 1.684 + uint32_t keyNameLength = keyName.Length(); 1.685 + const char* keyNameStr = keyName.get(); 1.686 + for (uint16_t i = 0; i < (sizeof(gKeyCodes) / sizeof(gKeyCodes[0])); ++i) 1.687 + if (keyNameLength == gKeyCodes[i].strlength && 1.688 + !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr)) 1.689 + return gKeyCodes[i].keycode; 1.690 + 1.691 + return 0; 1.692 +} 1.693 + 1.694 +int32_t nsXBLPrototypeHandler::KeyToMask(int32_t key) 1.695 +{ 1.696 + switch (key) 1.697 + { 1.698 + case nsIDOMKeyEvent::DOM_VK_META: 1.699 + return cMeta | cMetaMask; 1.700 + 1.701 + case nsIDOMKeyEvent::DOM_VK_WIN: 1.702 + return cOS | cOSMask; 1.703 + 1.704 + case nsIDOMKeyEvent::DOM_VK_ALT: 1.705 + return cAlt | cAltMask; 1.706 + 1.707 + case nsIDOMKeyEvent::DOM_VK_CONTROL: 1.708 + default: 1.709 + return cControl | cControlMask; 1.710 + } 1.711 + return cControl | cControlMask; // for warning avoidance 1.712 +} 1.713 + 1.714 +void 1.715 +nsXBLPrototypeHandler::GetEventType(nsAString& aEvent) 1.716 +{ 1.717 + nsCOMPtr<nsIContent> handlerElement = GetHandlerElement(); 1.718 + if (!handlerElement) { 1.719 + aEvent.Truncate(); 1.720 + return; 1.721 + } 1.722 + handlerElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, aEvent); 1.723 + 1.724 + if (aEvent.IsEmpty() && (mType & NS_HANDLER_TYPE_XUL)) 1.725 + // If no type is specified for a XUL <key> element, let's assume that we're "keypress". 1.726 + aEvent.AssignLiteral("keypress"); 1.727 +} 1.728 + 1.729 +void 1.730 +nsXBLPrototypeHandler::ConstructPrototype(nsIContent* aKeyElement, 1.731 + const char16_t* aEvent, 1.732 + const char16_t* aPhase, 1.733 + const char16_t* aAction, 1.734 + const char16_t* aCommand, 1.735 + const char16_t* aKeyCode, 1.736 + const char16_t* aCharCode, 1.737 + const char16_t* aModifiers, 1.738 + const char16_t* aButton, 1.739 + const char16_t* aClickCount, 1.740 + const char16_t* aGroup, 1.741 + const char16_t* aPreventDefault, 1.742 + const char16_t* aAllowUntrusted) 1.743 +{ 1.744 + mType = 0; 1.745 + 1.746 + if (aKeyElement) { 1.747 + mType |= NS_HANDLER_TYPE_XUL; 1.748 + nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aKeyElement); 1.749 + if (!weak) { 1.750 + return; 1.751 + } 1.752 + weak.swap(mHandlerElement); 1.753 + } 1.754 + else { 1.755 + mType |= aCommand ? NS_HANDLER_TYPE_XBL_COMMAND : NS_HANDLER_TYPE_XBL_JS; 1.756 + mHandlerText = nullptr; 1.757 + } 1.758 + 1.759 + mDetail = -1; 1.760 + mMisc = 0; 1.761 + mKeyMask = 0; 1.762 + mPhase = NS_PHASE_BUBBLING; 1.763 + 1.764 + if (aAction) 1.765 + mHandlerText = ToNewUnicode(nsDependentString(aAction)); 1.766 + else if (aCommand) 1.767 + mHandlerText = ToNewUnicode(nsDependentString(aCommand)); 1.768 + 1.769 + nsAutoString event(aEvent); 1.770 + if (event.IsEmpty()) { 1.771 + if (mType & NS_HANDLER_TYPE_XUL) 1.772 + GetEventType(event); 1.773 + if (event.IsEmpty()) 1.774 + return; 1.775 + } 1.776 + 1.777 + mEventName = do_GetAtom(event); 1.778 + 1.779 + if (aPhase) { 1.780 + const nsDependentString phase(aPhase); 1.781 + if (phase.EqualsLiteral("capturing")) 1.782 + mPhase = NS_PHASE_CAPTURING; 1.783 + else if (phase.EqualsLiteral("target")) 1.784 + mPhase = NS_PHASE_TARGET; 1.785 + } 1.786 + 1.787 + // Button and clickcount apply only to XBL handlers and don't apply to XUL key 1.788 + // handlers. 1.789 + if (aButton && *aButton) 1.790 + mDetail = *aButton - '0'; 1.791 + 1.792 + if (aClickCount && *aClickCount) 1.793 + mMisc = *aClickCount - '0'; 1.794 + 1.795 + // Modifiers are supported by both types of handlers (XUL and XBL). 1.796 + nsAutoString modifiers(aModifiers); 1.797 + if (mType & NS_HANDLER_TYPE_XUL) 1.798 + aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiers); 1.799 + 1.800 + if (!modifiers.IsEmpty()) { 1.801 + mKeyMask = cAllModifiers; 1.802 + char* str = ToNewCString(modifiers); 1.803 + char* newStr; 1.804 + char* token = nsCRT::strtok( str, ", \t", &newStr ); 1.805 + while( token != nullptr ) { 1.806 + if (PL_strcmp(token, "shift") == 0) 1.807 + mKeyMask |= cShift | cShiftMask; 1.808 + else if (PL_strcmp(token, "alt") == 0) 1.809 + mKeyMask |= cAlt | cAltMask; 1.810 + else if (PL_strcmp(token, "meta") == 0) 1.811 + mKeyMask |= cMeta | cMetaMask; 1.812 + else if (PL_strcmp(token, "os") == 0) 1.813 + mKeyMask |= cOS | cOSMask; 1.814 + else if (PL_strcmp(token, "control") == 0) 1.815 + mKeyMask |= cControl | cControlMask; 1.816 + else if (PL_strcmp(token, "accel") == 0) 1.817 + mKeyMask |= KeyToMask(kAccelKey); 1.818 + else if (PL_strcmp(token, "access") == 0) 1.819 + mKeyMask |= KeyToMask(kMenuAccessKey); 1.820 + else if (PL_strcmp(token, "any") == 0) 1.821 + mKeyMask &= ~(mKeyMask << 5); 1.822 + 1.823 + token = nsCRT::strtok( newStr, ", \t", &newStr ); 1.824 + } 1.825 + 1.826 + nsMemory::Free(str); 1.827 + } 1.828 + 1.829 + nsAutoString key(aCharCode); 1.830 + if (key.IsEmpty()) { 1.831 + if (mType & NS_HANDLER_TYPE_XUL) { 1.832 + aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key); 1.833 + if (key.IsEmpty()) 1.834 + aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, key); 1.835 + } 1.836 + } 1.837 + 1.838 + if (!key.IsEmpty()) { 1.839 + if (mKeyMask == 0) 1.840 + mKeyMask = cAllModifiers; 1.841 + ToLowerCase(key); 1.842 + 1.843 + // We have a charcode. 1.844 + mMisc = 1; 1.845 + mDetail = key[0]; 1.846 + const uint8_t GTK2Modifiers = cShift | cControl | cShiftMask | cControlMask; 1.847 + if ((mKeyMask & GTK2Modifiers) == GTK2Modifiers && 1.848 + modifiers.First() != char16_t(',') && 1.849 + (mDetail == 'u' || mDetail == 'U')) 1.850 + ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "GTK2Conflict"); 1.851 + const uint8_t WinModifiers = cControl | cAlt | cControlMask | cAltMask; 1.852 + if ((mKeyMask & WinModifiers) == WinModifiers && 1.853 + modifiers.First() != char16_t(',') && 1.854 + (('A' <= mDetail && mDetail <= 'Z') || 1.855 + ('a' <= mDetail && mDetail <= 'z'))) 1.856 + ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "WinConflict"); 1.857 + } 1.858 + else { 1.859 + key.Assign(aKeyCode); 1.860 + if (mType & NS_HANDLER_TYPE_XUL) 1.861 + aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, key); 1.862 + 1.863 + if (!key.IsEmpty()) { 1.864 + if (mKeyMask == 0) 1.865 + mKeyMask = cAllModifiers; 1.866 + mDetail = GetMatchingKeyCode(key); 1.867 + } 1.868 + } 1.869 + 1.870 + if (aGroup && nsDependentString(aGroup).EqualsLiteral("system")) 1.871 + mType |= NS_HANDLER_TYPE_SYSTEM; 1.872 + 1.873 + if (aPreventDefault && 1.874 + nsDependentString(aPreventDefault).EqualsLiteral("true")) 1.875 + mType |= NS_HANDLER_TYPE_PREVENTDEFAULT; 1.876 + 1.877 + if (aAllowUntrusted) { 1.878 + mType |= NS_HANDLER_HAS_ALLOW_UNTRUSTED_ATTR; 1.879 + if (nsDependentString(aAllowUntrusted).EqualsLiteral("true")) { 1.880 + mType |= NS_HANDLER_ALLOW_UNTRUSTED; 1.881 + } else { 1.882 + mType &= ~NS_HANDLER_ALLOW_UNTRUSTED; 1.883 + } 1.884 + } 1.885 +} 1.886 + 1.887 +void 1.888 +nsXBLPrototypeHandler::ReportKeyConflict(const char16_t* aKey, const char16_t* aModifiers, nsIContent* aKeyElement, const char *aMessageName) 1.889 +{ 1.890 + nsCOMPtr<nsIDocument> doc; 1.891 + if (mPrototypeBinding) { 1.892 + nsXBLDocumentInfo* docInfo = mPrototypeBinding->XBLDocumentInfo(); 1.893 + if (docInfo) { 1.894 + doc = docInfo->GetDocument(); 1.895 + } 1.896 + } else if (aKeyElement) { 1.897 + doc = aKeyElement->OwnerDoc(); 1.898 + } 1.899 + 1.900 + const char16_t* params[] = { aKey, aModifiers }; 1.901 + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, 1.902 + NS_LITERAL_CSTRING("XBL Prototype Handler"), doc, 1.903 + nsContentUtils::eXBL_PROPERTIES, 1.904 + aMessageName, 1.905 + params, ArrayLength(params), 1.906 + nullptr, EmptyString(), mLineNumber); 1.907 +} 1.908 + 1.909 +bool 1.910 +nsXBLPrototypeHandler::ModifiersMatchMask(nsIDOMUIEvent* aEvent, 1.911 + bool aIgnoreShiftKey) 1.912 +{ 1.913 + WidgetInputEvent* inputEvent = aEvent->GetInternalNSEvent()->AsInputEvent(); 1.914 + NS_ENSURE_TRUE(inputEvent, false); 1.915 + 1.916 + if (mKeyMask & cMetaMask) { 1.917 + if (inputEvent->IsMeta() != ((mKeyMask & cMeta) != 0)) { 1.918 + return false; 1.919 + } 1.920 + } 1.921 + 1.922 + if (mKeyMask & cOSMask) { 1.923 + if (inputEvent->IsOS() != ((mKeyMask & cOS) != 0)) { 1.924 + return false; 1.925 + } 1.926 + } 1.927 + 1.928 + if (mKeyMask & cShiftMask && !aIgnoreShiftKey) { 1.929 + if (inputEvent->IsShift() != ((mKeyMask & cShift) != 0)) { 1.930 + return false; 1.931 + } 1.932 + } 1.933 + 1.934 + if (mKeyMask & cAltMask) { 1.935 + if (inputEvent->IsAlt() != ((mKeyMask & cAlt) != 0)) { 1.936 + return false; 1.937 + } 1.938 + } 1.939 + 1.940 + if (mKeyMask & cControlMask) { 1.941 + if (inputEvent->IsControl() != ((mKeyMask & cControl) != 0)) { 1.942 + return false; 1.943 + } 1.944 + } 1.945 + 1.946 + return true; 1.947 +} 1.948 + 1.949 +nsresult 1.950 +nsXBLPrototypeHandler::Read(nsIObjectInputStream* aStream) 1.951 +{ 1.952 + AssertInCompilationScope(); 1.953 + nsresult rv = aStream->Read8(&mPhase); 1.954 + NS_ENSURE_SUCCESS(rv, rv); 1.955 + rv = aStream->Read8(&mType); 1.956 + NS_ENSURE_SUCCESS(rv, rv); 1.957 + rv = aStream->Read8(&mMisc); 1.958 + NS_ENSURE_SUCCESS(rv, rv); 1.959 + 1.960 + rv = aStream->Read32(reinterpret_cast<uint32_t*>(&mKeyMask)); 1.961 + NS_ENSURE_SUCCESS(rv, rv); 1.962 + uint32_t detail; 1.963 + rv = aStream->Read32(&detail); 1.964 + NS_ENSURE_SUCCESS(rv, rv); 1.965 + mDetail = detail; 1.966 + 1.967 + nsAutoString name; 1.968 + rv = aStream->ReadString(name); 1.969 + NS_ENSURE_SUCCESS(rv, rv); 1.970 + mEventName = do_GetAtom(name); 1.971 + 1.972 + rv = aStream->Read32(&mLineNumber); 1.973 + NS_ENSURE_SUCCESS(rv, rv); 1.974 + 1.975 + nsAutoString handlerText; 1.976 + rv = aStream->ReadString(handlerText); 1.977 + NS_ENSURE_SUCCESS(rv, rv); 1.978 + if (!handlerText.IsEmpty()) 1.979 + mHandlerText = ToNewUnicode(handlerText); 1.980 + 1.981 + return NS_OK; 1.982 +} 1.983 + 1.984 +nsresult 1.985 +nsXBLPrototypeHandler::Write(nsIObjectOutputStream* aStream) 1.986 +{ 1.987 + AssertInCompilationScope(); 1.988 + // Make sure we don't write out NS_HANDLER_TYPE_XUL types, as they are used 1.989 + // for <keyset> elements. 1.990 + if ((mType & NS_HANDLER_TYPE_XUL) || !mEventName) 1.991 + return NS_OK; 1.992 + 1.993 + XBLBindingSerializeDetails type = XBLBinding_Serialize_Handler; 1.994 + 1.995 + nsresult rv = aStream->Write8(type); 1.996 + rv = aStream->Write8(mPhase); 1.997 + NS_ENSURE_SUCCESS(rv, rv); 1.998 + rv = aStream->Write8(mType); 1.999 + NS_ENSURE_SUCCESS(rv, rv); 1.1000 + rv = aStream->Write8(mMisc); 1.1001 + NS_ENSURE_SUCCESS(rv, rv); 1.1002 + rv = aStream->Write32(static_cast<uint32_t>(mKeyMask)); 1.1003 + NS_ENSURE_SUCCESS(rv, rv); 1.1004 + rv = aStream->Write32(mDetail); 1.1005 + NS_ENSURE_SUCCESS(rv, rv); 1.1006 + 1.1007 + rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get()); 1.1008 + NS_ENSURE_SUCCESS(rv, rv); 1.1009 + 1.1010 + rv = aStream->Write32(mLineNumber); 1.1011 + NS_ENSURE_SUCCESS(rv, rv); 1.1012 + return aStream->WriteWStringZ(mHandlerText ? mHandlerText : MOZ_UTF16("")); 1.1013 +}