dom/xbl/nsXBLPrototypeHandler.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:de8a46f89d3c
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/. */
5
6 #include "mozilla/ArrayUtils.h"
7
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"
48
49 using namespace mozilla;
50 using namespace mozilla::dom;
51
52 uint32_t nsXBLPrototypeHandler::gRefCnt = 0;
53
54 int32_t nsXBLPrototypeHandler::kMenuAccessKey = -1;
55 int32_t nsXBLPrototypeHandler::kAccelKey = -1;
56
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);
62
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);
68
69 const int32_t nsXBLPrototypeHandler::cAllModifiers =
70 cShiftMask | cAltMask | cControlMask | cMetaMask | cOSMask;
71
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();
92
93 ConstructPrototype(nullptr, aEvent, aPhase, aAction, aCommand, aKeyCode,
94 aCharCode, aModifiers, aButton, aClickCount,
95 aGroup, aPreventDefault, aAllowUntrusted);
96 }
97
98 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement)
99 : mHandlerElement(nullptr),
100 mLineNumber(0),
101 mNextHandler(nullptr),
102 mPrototypeBinding(nullptr)
103 {
104 Init();
105
106 // Make sure our prototype is initialized.
107 ConstructPrototype(aHandlerElement);
108 }
109
110 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
111 : mHandlerText(nullptr),
112 mLineNumber(0),
113 mNextHandler(nullptr),
114 mPrototypeBinding(aBinding)
115 {
116 Init();
117 }
118
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 }
127
128 // We own the next handler in the chain, so delete it now.
129 NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler);
130 }
131
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 }
139
140 return nullptr;
141 }
142
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 }
156
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;
165
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
175
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 }
181
182 nsresult
183 nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
184 nsIDOMEvent* aEvent)
185 {
186 nsresult rv = NS_ERROR_FAILURE;
187
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 }
195
196 if (!mHandlerElement) // This works for both types of handlers. In both cases, the union's var should be defined.
197 return rv;
198
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");
204
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);
210
211 if (!trustedEvent)
212 return NS_OK;
213 }
214
215 if (isXBLCommand) {
216 return DispatchXBLCommand(aTarget, aEvent);
217 }
218
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 }
225
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));
230
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;
235
236 if (winRoot) {
237 window = winRoot->GetWindow();
238 }
239
240 if (window) {
241 window = window->GetCurrentInnerWindow();
242 NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED);
243
244 boundGlobal = do_QueryInterface(window->GetPrivateRoot());
245 }
246 else boundGlobal = do_QueryInterface(aTarget);
247
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 }
257
258 boundGlobal = do_QueryInterface(boundDocument->GetScopeObject());
259 }
260
261 if (!boundGlobal)
262 return NS_OK;
263
264 nsIScriptContext *boundContext = boundGlobal->GetScriptContext();
265 if (!boundContext)
266 return NS_OK;
267
268 nsISupports *scriptTarget;
269
270 if (winRoot) {
271 scriptTarget = boundGlobal;
272 } else {
273 scriptTarget = aTarget;
274 }
275
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));
281
282 AutoPushJSContext cx(boundContext->GetNativeContext());
283 JS::Rooted<JSObject*> handler(cx);
284
285 rv = EnsureEventHandler(boundGlobal, boundContext, onEventAtom, &handler);
286 NS_ENSURE_SUCCESS(rv, rv);
287
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);
291
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.
295
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));
303
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);
310
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);
315
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 }
321
322 nsRefPtr<EventHandlerNonNull> handlerCallback =
323 new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr);
324
325 TypedEventHandler typedHandler(handlerCallback);
326
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);
333
334 // Handle the event.
335 jsEventHandler->HandleEvent(aEvent);
336 jsEventHandler->Disconnect();
337 return NS_OK;
338 }
339
340 nsresult
341 nsXBLPrototypeHandler::EnsureEventHandler(nsIScriptGlobalObject* aGlobal,
342 nsIScriptContext *aBoundContext,
343 nsIAtom *aName,
344 JS::MutableHandle<JSObject*> aHandler)
345 {
346 AutoPushJSContext cx(aBoundContext->GetNativeContext());
347
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 }
359
360 // Ensure that we have something to compile
361 nsDependentString handlerText(mHandlerText);
362 NS_ENSURE_TRUE(!handlerText.IsEmpty(), NS_ERROR_FAILURE);
363
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);
367
368 nsAutoCString bindingURI;
369 mPrototypeBinding->DocURI()->GetSpec(bindingURI);
370
371 uint32_t argCount;
372 const char **argNames;
373 nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, &argCount,
374 &argNames);
375
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);
381
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);
389
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);
397
398 if (pWindow) {
399 pWindow->CacheXBLPrototypeHandler(this, aHandler);
400 }
401
402 return NS_OK;
403 }
404
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.
410
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 }
423
424 // Instead of executing JS, let's get the controller for the bound
425 // element and call doCommand on it.
426 nsCOMPtr<nsIController> controller;
427
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();
444
445 if (!doc)
446 doc = do_QueryInterface(aTarget);
447
448 if (!doc)
449 return NS_ERROR_FAILURE;
450
451 privateWindow = doc->GetWindow();
452 if (!privateWindow)
453 return NS_ERROR_FAILURE;
454 }
455
456 windowRoot = privateWindow->GetTopWindowRoot();
457 }
458
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.
464
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.
470
471 nsCOMPtr<nsPIDOMWindow> windowToCheck;
472 if (windowRoot)
473 windowToCheck = windowRoot->GetWindow();
474 else
475 windowToCheck = privateWindow->GetPrivateRoot();
476
477 nsCOMPtr<nsIContent> focusedContent;
478 if (windowToCheck) {
479 nsCOMPtr<nsPIDOMWindow> focusedWindow;
480 focusedContent =
481 nsFocusManager::GetFocusedDescendant(windowToCheck, true, getter_AddRefs(focusedWindow));
482 }
483
484 bool isLink = false;
485 nsIContent *content = focusedContent;
486
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 }
497
498 if (content->HasAttr(kNameSpaceID_XLink, nsGkAtoms::type)) {
499 isLink = content->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
500 nsGkAtoms::simple, eCaseMatters);
501
502 if (isLink) {
503 break;
504 }
505 }
506
507 content = content->GetParent();
508 }
509
510 if (!isLink)
511 return NS_OK;
512 }
513 }
514
515 // We are the default action for this command.
516 // Stop any other default action from executing.
517 aEvent->PreventDefault();
518
519 if (controller)
520 controller->DoCommand(command.get());
521
522 return NS_OK;
523 }
524
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 }
537
538 aEvent->PreventDefault();
539
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 }
546
547 // XXX We should use mozilla::Modifiers for supporting all modifiers.
548
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);
557
558 nsContentUtils::DispatchXULCommand(handlerElement, true,
559 nullptr, nullptr,
560 isControl, isAlt, isShift, isMeta);
561 return NS_OK;
562 }
563
564 already_AddRefed<nsIAtom>
565 nsXBLPrototypeHandler::GetEventName()
566 {
567 nsCOMPtr<nsIAtom> eventName = mEventName;
568 return eventName.forget();
569 }
570
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;
577
578 nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aTarget));
579 if (xulElement)
580 xulElement->GetControllers(getter_AddRefs(controllers));
581
582 if (!controllers) {
583 nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea(do_QueryInterface(aTarget));
584 if (htmlTextArea)
585 htmlTextArea->GetControllers(getter_AddRefs(controllers));
586 }
587
588 if (!controllers) {
589 nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement(do_QueryInterface(aTarget));
590 if (htmlInputElement)
591 htmlInputElement->GetControllers(getter_AddRefs(controllers));
592 }
593
594 if (!controllers) {
595 nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(aTarget));
596 if (domWindow)
597 domWindow->GetControllers(getter_AddRefs(controllers));
598 }
599
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 }
607
608 return controller.forget();
609 }
610
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;
619
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);
630
631 if (code != uint32_t(mDetail))
632 return false;
633 }
634
635 return ModifiersMatchMask(aKeyEvent, aIgnoreShiftKey);
636 }
637
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.
643
644 int16_t button;
645 aMouseEvent->GetButton(&button);
646 if (mDetail != -1 && (button != mDetail))
647 return false;
648
649 int32_t clickcount;
650 aMouseEvent->GetDetail(&clickcount);
651 if (mMisc != 0 && (clickcount != mMisc))
652 return false;
653
654 return ModifiersMatchMask(aMouseEvent);
655 }
656
657 struct keyCodeData {
658 const char* str;
659 size_t strlength;
660 uint32_t keycode;
661 };
662
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[] = {
667
668 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
669 { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode }
670 #include "mozilla/VirtualKeyCodeList.h"
671 #undef NS_DEFINE_VK
672 };
673
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.
680
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;
687
688 return 0;
689 }
690
691 int32_t nsXBLPrototypeHandler::KeyToMask(int32_t key)
692 {
693 switch (key)
694 {
695 case nsIDOMKeyEvent::DOM_VK_META:
696 return cMeta | cMetaMask;
697
698 case nsIDOMKeyEvent::DOM_VK_WIN:
699 return cOS | cOSMask;
700
701 case nsIDOMKeyEvent::DOM_VK_ALT:
702 return cAlt | cAltMask;
703
704 case nsIDOMKeyEvent::DOM_VK_CONTROL:
705 default:
706 return cControl | cControlMask;
707 }
708 return cControl | cControlMask; // for warning avoidance
709 }
710
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);
720
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 }
725
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;
742
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 }
755
756 mDetail = -1;
757 mMisc = 0;
758 mKeyMask = 0;
759 mPhase = NS_PHASE_BUBBLING;
760
761 if (aAction)
762 mHandlerText = ToNewUnicode(nsDependentString(aAction));
763 else if (aCommand)
764 mHandlerText = ToNewUnicode(nsDependentString(aCommand));
765
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 }
773
774 mEventName = do_GetAtom(event);
775
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 }
783
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';
788
789 if (aClickCount && *aClickCount)
790 mMisc = *aClickCount - '0';
791
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);
796
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);
819
820 token = nsCRT::strtok( newStr, ", \t", &newStr );
821 }
822
823 nsMemory::Free(str);
824 }
825
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 }
834
835 if (!key.IsEmpty()) {
836 if (mKeyMask == 0)
837 mKeyMask = cAllModifiers;
838 ToLowerCase(key);
839
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);
859
860 if (!key.IsEmpty()) {
861 if (mKeyMask == 0)
862 mKeyMask = cAllModifiers;
863 mDetail = GetMatchingKeyCode(key);
864 }
865 }
866
867 if (aGroup && nsDependentString(aGroup).EqualsLiteral("system"))
868 mType |= NS_HANDLER_TYPE_SYSTEM;
869
870 if (aPreventDefault &&
871 nsDependentString(aPreventDefault).EqualsLiteral("true"))
872 mType |= NS_HANDLER_TYPE_PREVENTDEFAULT;
873
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 }
883
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 }
896
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 }
905
906 bool
907 nsXBLPrototypeHandler::ModifiersMatchMask(nsIDOMUIEvent* aEvent,
908 bool aIgnoreShiftKey)
909 {
910 WidgetInputEvent* inputEvent = aEvent->GetInternalNSEvent()->AsInputEvent();
911 NS_ENSURE_TRUE(inputEvent, false);
912
913 if (mKeyMask & cMetaMask) {
914 if (inputEvent->IsMeta() != ((mKeyMask & cMeta) != 0)) {
915 return false;
916 }
917 }
918
919 if (mKeyMask & cOSMask) {
920 if (inputEvent->IsOS() != ((mKeyMask & cOS) != 0)) {
921 return false;
922 }
923 }
924
925 if (mKeyMask & cShiftMask && !aIgnoreShiftKey) {
926 if (inputEvent->IsShift() != ((mKeyMask & cShift) != 0)) {
927 return false;
928 }
929 }
930
931 if (mKeyMask & cAltMask) {
932 if (inputEvent->IsAlt() != ((mKeyMask & cAlt) != 0)) {
933 return false;
934 }
935 }
936
937 if (mKeyMask & cControlMask) {
938 if (inputEvent->IsControl() != ((mKeyMask & cControl) != 0)) {
939 return false;
940 }
941 }
942
943 return true;
944 }
945
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);
956
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;
963
964 nsAutoString name;
965 rv = aStream->ReadString(name);
966 NS_ENSURE_SUCCESS(rv, rv);
967 mEventName = do_GetAtom(name);
968
969 rv = aStream->Read32(&mLineNumber);
970 NS_ENSURE_SUCCESS(rv, rv);
971
972 nsAutoString handlerText;
973 rv = aStream->ReadString(handlerText);
974 NS_ENSURE_SUCCESS(rv, rv);
975 if (!handlerText.IsEmpty())
976 mHandlerText = ToNewUnicode(handlerText);
977
978 return NS_OK;
979 }
980
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;
989
990 XBLBindingSerializeDetails type = XBLBinding_Serialize_Handler;
991
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);
1003
1004 rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
1005 NS_ENSURE_SUCCESS(rv, rv);
1006
1007 rv = aStream->Write32(mLineNumber);
1008 NS_ENSURE_SUCCESS(rv, rv);
1009 return aStream->WriteWStringZ(mHandlerText ? mHandlerText : MOZ_UTF16(""));
1010 }

mercurial