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"
9 #include "nsXBLPrototypeHandler.h"
10 #include "nsXBLPrototypeBinding.h"
11 #include "nsContentUtils.h"
12 #include "nsGlobalWindow.h"
13 #include "nsIContent.h"
15 #include "nsIDOMKeyEvent.h"
16 #include "nsIDOMMouseEvent.h"
17 #include "nsNameSpaceManager.h"
18 #include "nsIDocument.h"
19 #include "nsIController.h"
20 #include "nsIControllers.h"
21 #include "nsIDOMXULElement.h"
23 #include "nsIDOMHTMLTextAreaElement.h"
24 #include "nsIDOMHTMLInputElement.h"
25 #include "nsFocusManager.h"
26 #include "nsIDOMEventListener.h"
27 #include "nsPIDOMWindow.h"
28 #include "nsPIWindowRoot.h"
29 #include "nsIDOMWindow.h"
30 #include "nsIServiceManager.h"
31 #include "nsIScriptError.h"
32 #include "nsXPIDLString.h"
33 #include "nsReadableUtils.h"
34 #include "nsGkAtoms.h"
35 #include "nsIXPConnect.h"
36 #include "nsIDOMScriptObjectFactory.h"
37 #include "mozilla/AddonPathService.h"
39 #include "nsUnicharUtils.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/Element.h"
48 #include "mozilla/dom/EventHandlerBinding.h"
49 #include "mozilla/dom/ScriptSettings.h"
50 #include "xpcpublic.h"
52 using namespace mozilla
;
53 using namespace mozilla::dom
;
55 uint32_t nsXBLPrototypeHandler::gRefCnt
= 0;
57 int32_t nsXBLPrototypeHandler::kMenuAccessKey
= -1;
59 const int32_t nsXBLPrototypeHandler::cShift
= (1<<0);
60 const int32_t nsXBLPrototypeHandler::cAlt
= (1<<1);
61 const int32_t nsXBLPrototypeHandler::cControl
= (1<<2);
62 const int32_t nsXBLPrototypeHandler::cMeta
= (1<<3);
63 const int32_t nsXBLPrototypeHandler::cOS
= (1<<4);
65 const int32_t nsXBLPrototypeHandler::cShiftMask
= (1<<5);
66 const int32_t nsXBLPrototypeHandler::cAltMask
= (1<<6);
67 const int32_t nsXBLPrototypeHandler::cControlMask
= (1<<7);
68 const int32_t nsXBLPrototypeHandler::cMetaMask
= (1<<8);
69 const int32_t nsXBLPrototypeHandler::cOSMask
= (1<<9);
71 const int32_t nsXBLPrototypeHandler::cAllModifiers
=
72 cShiftMask
| cAltMask
| cControlMask
| cMetaMask
| cOSMask
;
74 nsXBLPrototypeHandler::nsXBLPrototypeHandler(const char16_t
* aEvent
,
75 const char16_t
* aPhase
,
76 const char16_t
* aAction
,
77 const char16_t
* aCommand
,
78 const char16_t
* aKeyCode
,
79 const char16_t
* aCharCode
,
80 const char16_t
* aModifiers
,
81 const char16_t
* aButton
,
82 const char16_t
* aClickCount
,
83 const char16_t
* aGroup
,
84 const char16_t
* aPreventDefault
,
85 const char16_t
* aAllowUntrusted
,
86 nsXBLPrototypeBinding
* aBinding
,
88 : mHandlerText(nullptr),
89 mLineNumber(aLineNumber
),
90 mNextHandler(nullptr),
91 mPrototypeBinding(aBinding
)
95 ConstructPrototype(nullptr, aEvent
, aPhase
, aAction
, aCommand
, aKeyCode
,
96 aCharCode
, aModifiers
, aButton
, aClickCount
,
97 aGroup
, aPreventDefault
, aAllowUntrusted
);
100 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent
* aHandlerElement
)
101 : mHandlerElement(nullptr),
103 mNextHandler(nullptr),
104 mPrototypeBinding(nullptr)
108 // Make sure our prototype is initialized.
109 ConstructPrototype(aHandlerElement
);
112 nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding
* aBinding
)
113 : mHandlerText(nullptr),
115 mNextHandler(nullptr),
116 mPrototypeBinding(aBinding
)
121 nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
124 if (mType
& NS_HANDLER_TYPE_XUL
) {
125 NS_IF_RELEASE(mHandlerElement
);
126 } else if (mHandlerText
) {
127 nsMemory::Free(mHandlerText
);
130 // We own the next handler in the chain, so delete it now.
131 NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler
, this, mNextHandler
);
134 already_AddRefed
<nsIContent
>
135 nsXBLPrototypeHandler::GetHandlerElement()
137 if (mType
& NS_HANDLER_TYPE_XUL
) {
138 nsCOMPtr
<nsIContent
> element
= do_QueryReferent(mHandlerElement
);
139 return element
.forget();
146 nsXBLPrototypeHandler::AppendHandlerText(const nsAString
& aText
)
149 // Append our text to the existing text.
150 char16_t
* temp
= mHandlerText
;
151 mHandlerText
= ToNewUnicode(nsDependentString(temp
) + aText
);
152 nsMemory::Free(temp
);
155 mHandlerText
= ToNewUnicode(aText
);
159 /////////////////////////////////////////////////////////////////////////////
160 // Get the menu access key from prefs.
161 // XXX Eventually pick up using CSS3 key-equivalent property or somesuch
163 nsXBLPrototypeHandler::InitAccessKeys()
165 if (kMenuAccessKey
>= 0) {
169 // Compiled-in defaults, in case we can't get the pref --
170 // mac doesn't have menu shortcuts, other platforms use alt.
174 kMenuAccessKey
= nsIDOMKeyEvent::DOM_VK_ALT
;
177 // Get the menu access key value from prefs, overriding the default:
179 Preferences::GetInt("ui.key.menuAccessKey", kMenuAccessKey
);
183 nsXBLPrototypeHandler::ExecuteHandler(EventTarget
* aTarget
,
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
196 if (!mHandlerElement
) // This works for both types of handlers. In both cases, the union's var should be defined.
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
207 if (isXULKey
|| isXBLCommand
) {
208 bool trustedEvent
= false;
209 aEvent
->GetIsTrusted(&trustedEvent
);
216 return DispatchXBLCommand(aTarget
, aEvent
);
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.
223 return DispatchXULKeyCommand(aEvent
);
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
;
237 window
= winRoot
->GetWindow();
241 window
= window
->GetCurrentInnerWindow();
242 NS_ENSURE_TRUE(window
, NS_ERROR_UNEXPECTED
);
244 boundGlobal
= do_QueryInterface(window
->GetPrivateRoot());
246 else boundGlobal
= do_QueryInterface(aTarget
);
249 nsCOMPtr
<nsIDocument
> boundDocument(do_QueryInterface(aTarget
));
250 if (!boundDocument
) {
251 // We must be an element.
252 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aTarget
));
255 boundDocument
= content
->OwnerDoc();
258 boundGlobal
= do_QueryInterface(boundDocument
->GetScopeObject());
264 nsISupports
*scriptTarget
;
267 scriptTarget
= boundGlobal
;
269 scriptTarget
= aTarget
;
272 // We're about to create a new JSEventHandler, which means that we need to
273 // Initiatize an AutoJSAPI with aTarget's bound global to make sure any errors
274 // are reported to the correct place.
276 if (NS_WARN_IF(!jsapi
.Init(boundGlobal
))) {
279 jsapi
.TakeOwnershipOfErrorReporting();
280 JSContext
* cx
= jsapi
.cx();
281 JS::Rooted
<JSObject
*> handler(cx
);
283 rv
= EnsureEventHandler(jsapi
, onEventAtom
, &handler
);
284 NS_ENSURE_SUCCESS(rv
, rv
);
286 JSAddonId
* addonId
= MapURIToAddonID(mPrototypeBinding
->DocURI());
288 JS::Rooted
<JSObject
*> globalObject(cx
, boundGlobal
->GetGlobalJSObject());
289 JS::Rooted
<JSObject
*> scopeObject(cx
, xpc::GetScopeForXBLExecution(cx
, globalObject
, addonId
));
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 // Build a scope chain in the XBL scope.
305 nsRefPtr
<Element
> targetElement
= do_QueryObject(scriptTarget
);
306 JS::AutoObjectVector
scopeChain(cx
);
307 ok
= nsJSUtils::GetScopeChainForElement(cx
, targetElement
, scopeChain
);
308 NS_ENSURE_TRUE(ok
, NS_ERROR_OUT_OF_MEMORY
);
310 // Next, clone the generic handler with our desired scope chain.
311 JS::Rooted
<JSObject
*> bound(cx
, JS::CloneFunctionObject(cx
, genericHandler
,
313 NS_ENSURE_TRUE(bound
, NS_ERROR_FAILURE
);
315 nsRefPtr
<EventHandlerNonNull
> handlerCallback
=
316 new EventHandlerNonNull(bound
, /* aIncumbentGlobal = */ nullptr);
318 TypedEventHandler
typedHandler(handlerCallback
);
321 nsCOMPtr
<JSEventHandler
> jsEventHandler
;
322 rv
= NS_NewJSEventHandler(scriptTarget
, onEventAtom
,
324 getter_AddRefs(jsEventHandler
));
325 NS_ENSURE_SUCCESS(rv
, rv
);
328 jsEventHandler
->HandleEvent(aEvent
);
329 jsEventHandler
->Disconnect();
334 nsXBLPrototypeHandler::EnsureEventHandler(AutoJSAPI
& jsapi
, nsIAtom
* aName
,
335 JS::MutableHandle
<JSObject
*> aHandler
)
337 JSContext
* cx
= jsapi
.cx();
339 // Check to see if we've already compiled this
340 JS::Rooted
<JSObject
*> globalObject(cx
, JS::CurrentGlobalOrNull(cx
));
341 nsCOMPtr
<nsPIDOMWindow
> pWindow
= xpc::WindowOrNull(globalObject
);
343 JS::Rooted
<JSObject
*> cachedHandler(cx
, pWindow
->GetCachedXBLPrototypeHandler(this));
345 JS::ExposeObjectToActiveJS(cachedHandler
);
346 aHandler
.set(cachedHandler
);
347 NS_ENSURE_TRUE(aHandler
, NS_ERROR_FAILURE
);
352 // Ensure that we have something to compile
353 nsDependentString
handlerText(mHandlerText
);
354 NS_ENSURE_TRUE(!handlerText
.IsEmpty(), NS_ERROR_FAILURE
);
356 JSAddonId
* addonId
= MapURIToAddonID(mPrototypeBinding
->DocURI());
358 JS::Rooted
<JSObject
*> scopeObject(cx
, xpc::GetScopeForXBLExecution(cx
, globalObject
, addonId
));
359 NS_ENSURE_TRUE(scopeObject
, NS_ERROR_OUT_OF_MEMORY
);
361 nsAutoCString bindingURI
;
362 mPrototypeBinding
->DocURI()->GetSpec(bindingURI
);
365 const char **argNames
;
366 nsContentUtils::GetEventArgNames(kNameSpaceID_XBL
, aName
, false, &argCount
,
369 // Compile the event handler in the xbl scope.
370 JSAutoCompartment
ac(cx
, scopeObject
);
371 JS::CompileOptions
options(cx
);
372 options
.setFileAndLine(bindingURI
.get(), mLineNumber
)
373 .setVersion(JSVERSION_LATEST
);
375 JS::Rooted
<JSObject
*> handlerFun(cx
);
376 JS::AutoObjectVector
emptyVector(cx
);
377 nsresult rv
= nsJSUtils::CompileFunction(jsapi
, emptyVector
, options
,
378 nsAtomCString(aName
), argCount
,
379 argNames
, handlerText
,
380 handlerFun
.address());
381 NS_ENSURE_SUCCESS(rv
, rv
);
382 NS_ENSURE_TRUE(handlerFun
, NS_ERROR_FAILURE
);
384 // Wrap the handler into the content scope, since we're about to stash it
385 // on the DOM window and such.
386 JSAutoCompartment
ac2(cx
, globalObject
);
387 bool ok
= JS_WrapObject(cx
, &handlerFun
);
388 NS_ENSURE_TRUE(ok
, NS_ERROR_OUT_OF_MEMORY
);
389 aHandler
.set(handlerFun
);
390 NS_ENSURE_TRUE(aHandler
, NS_ERROR_FAILURE
);
393 pWindow
->CacheXBLPrototypeHandler(this, aHandler
);
400 nsXBLPrototypeHandler::DispatchXBLCommand(EventTarget
* aTarget
, nsIDOMEvent
* aEvent
)
402 // This is a special-case optimization to make command handling fast.
403 // It isn't really a part of XBL, but it helps speed things up.
406 // See if preventDefault has been set. If so, don't execute.
407 bool preventDefault
= false;
408 aEvent
->GetDefaultPrevented(&preventDefault
);
409 if (preventDefault
) {
412 bool dispatchStopped
= aEvent
->IsDispatchStopped();
413 if (dispatchStopped
) {
418 // Instead of executing JS, let's get the controller for the bound
419 // element and call doCommand on it.
420 nsCOMPtr
<nsIController
> controller
;
422 nsCOMPtr
<nsPIDOMWindow
> privateWindow
;
423 nsCOMPtr
<nsPIWindowRoot
> windowRoot(do_QueryInterface(aTarget
));
425 privateWindow
= windowRoot
->GetWindow();
428 privateWindow
= do_QueryInterface(aTarget
);
429 if (!privateWindow
) {
430 nsCOMPtr
<nsIContent
> elt(do_QueryInterface(aTarget
));
431 nsCOMPtr
<nsIDocument
> doc
;
432 // XXXbz sXBL/XBL2 issue -- this should be the "scope doc" or
433 // something... whatever we use when wrapping DOM nodes
434 // normally. It's not clear that the owner doc is the right
437 doc
= elt
->OwnerDoc();
440 doc
= do_QueryInterface(aTarget
);
443 return NS_ERROR_FAILURE
;
445 privateWindow
= doc
->GetWindow();
447 return NS_ERROR_FAILURE
;
450 windowRoot
= privateWindow
->GetTopWindowRoot();
453 NS_LossyConvertUTF16toASCII
command(mHandlerText
);
455 windowRoot
->GetControllerForCommand(command
.get(), getter_AddRefs(controller
));
457 controller
= GetController(aTarget
); // We're attached to the receiver possibly.
459 if (mEventName
== nsGkAtoms::keypress
&&
460 mDetail
== nsIDOMKeyEvent::DOM_VK_SPACE
&&
462 // get the focused element so that we can pageDown only at
465 nsCOMPtr
<nsPIDOMWindow
> windowToCheck
;
467 windowToCheck
= windowRoot
->GetWindow();
469 windowToCheck
= privateWindow
->GetPrivateRoot();
471 nsCOMPtr
<nsIContent
> focusedContent
;
473 nsCOMPtr
<nsPIDOMWindow
> focusedWindow
;
475 nsFocusManager::GetFocusedDescendant(windowToCheck
, true, getter_AddRefs(focusedWindow
));
479 nsIContent
*content
= focusedContent
;
481 // if the focused element is a link then we do want space to
482 // scroll down. The focused element may be an element in a link,
483 // we need to check the parent node too. Only do this check if an
484 // element is focused and has a parent.
485 if (focusedContent
&& focusedContent
->GetParent()) {
487 if (content
->Tag() == nsGkAtoms::a
&& content
->IsHTML()) {
492 if (content
->HasAttr(kNameSpaceID_XLink
, nsGkAtoms::type
)) {
493 isLink
= content
->AttrValueIs(kNameSpaceID_XLink
, nsGkAtoms::type
,
494 nsGkAtoms::simple
, eCaseMatters
);
501 content
= content
->GetParent();
509 // We are the default action for this command.
510 // Stop any other default action from executing.
511 aEvent
->PreventDefault();
514 controller
->DoCommand(command
.get());
520 nsXBLPrototypeHandler::DispatchXULKeyCommand(nsIDOMEvent
* aEvent
)
522 nsCOMPtr
<nsIContent
> handlerElement
= GetHandlerElement();
523 NS_ENSURE_STATE(handlerElement
);
524 if (handlerElement
->AttrValueIs(kNameSpaceID_None
,
528 // Don't dispatch command events for disabled keys.
532 aEvent
->PreventDefault();
534 // Copy the modifiers from the key event.
535 nsCOMPtr
<nsIDOMKeyEvent
> keyEvent
= do_QueryInterface(aEvent
);
537 NS_ERROR("Trying to execute a key handler for a non-key event!");
538 return NS_ERROR_FAILURE
;
541 // XXX We should use mozilla::Modifiers for supporting all modifiers.
544 bool isControl
= false;
545 bool isShift
= false;
547 keyEvent
->GetAltKey(&isAlt
);
548 keyEvent
->GetCtrlKey(&isControl
);
549 keyEvent
->GetShiftKey(&isShift
);
550 keyEvent
->GetMetaKey(&isMeta
);
552 nsContentUtils::DispatchXULCommand(handlerElement
, true,
554 isControl
, isAlt
, isShift
, isMeta
);
558 already_AddRefed
<nsIAtom
>
559 nsXBLPrototypeHandler::GetEventName()
561 nsCOMPtr
<nsIAtom
> eventName
= mEventName
;
562 return eventName
.forget();
565 already_AddRefed
<nsIController
>
566 nsXBLPrototypeHandler::GetController(EventTarget
* aTarget
)
568 // XXX Fix this so there's a generic interface that describes controllers,
569 // This code should have no special knowledge of what objects might have controllers.
570 nsCOMPtr
<nsIControllers
> controllers
;
572 nsCOMPtr
<nsIDOMXULElement
> xulElement(do_QueryInterface(aTarget
));
574 xulElement
->GetControllers(getter_AddRefs(controllers
));
577 nsCOMPtr
<nsIDOMHTMLTextAreaElement
> htmlTextArea(do_QueryInterface(aTarget
));
579 htmlTextArea
->GetControllers(getter_AddRefs(controllers
));
583 nsCOMPtr
<nsIDOMHTMLInputElement
> htmlInputElement(do_QueryInterface(aTarget
));
584 if (htmlInputElement
)
585 htmlInputElement
->GetControllers(getter_AddRefs(controllers
));
589 nsCOMPtr
<nsIDOMWindow
> domWindow(do_QueryInterface(aTarget
));
591 domWindow
->GetControllers(getter_AddRefs(controllers
));
594 // Return the first controller.
595 // XXX This code should be checking the command name and using supportscommand and
597 nsCOMPtr
<nsIController
> controller
;
599 controllers
->GetControllerAt(0, getter_AddRefs(controller
));
602 return controller
.forget();
606 nsXBLPrototypeHandler::KeyEventMatched(
607 nsIDOMKeyEvent
* aKeyEvent
,
609 const IgnoreModifierState
& aIgnoreModifierState
)
612 // Get the keycode or charcode of the key event.
619 aKeyEvent
->GetCharCode(&code
);
621 code
= ToLowerCase(char16_t(code
));
624 aKeyEvent
->GetKeyCode(&code
);
626 if (code
!= uint32_t(mDetail
))
630 return ModifiersMatchMask(aKeyEvent
, aIgnoreModifierState
);
634 nsXBLPrototypeHandler::MouseEventMatched(nsIDOMMouseEvent
* aMouseEvent
)
636 if (mDetail
== -1 && mMisc
== 0 && (mKeyMask
& cAllModifiers
) == 0)
637 return true; // No filters set up. It's generic.
640 aMouseEvent
->GetButton(&button
);
641 if (mDetail
!= -1 && (button
!= mDetail
))
645 aMouseEvent
->GetDetail(&clickcount
);
646 if (mMisc
!= 0 && (clickcount
!= mMisc
))
649 return ModifiersMatchMask(aMouseEvent
, IgnoreModifierState());
658 // All of these must be uppercase, since the function below does
659 // case-insensitive comparison by converting to uppercase.
660 // XXX: be sure to check this periodically for new symbol additions!
661 static const keyCodeData gKeyCodes
[] = {
663 #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
664 { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode }
665 #include "mozilla/VirtualKeyCodeList.h"
669 int32_t nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString
& aKeyName
)
671 nsAutoCString keyName
;
672 keyName
.AssignWithConversion(aKeyName
);
673 ToUpperCase(keyName
); // We want case-insensitive comparison with data
674 // stored as uppercase.
676 uint32_t keyNameLength
= keyName
.Length();
677 const char* keyNameStr
= keyName
.get();
678 for (uint16_t i
= 0; i
< (sizeof(gKeyCodes
) / sizeof(gKeyCodes
[0])); ++i
)
679 if (keyNameLength
== gKeyCodes
[i
].strlength
&&
680 !nsCRT::strcmp(gKeyCodes
[i
].str
, keyNameStr
))
681 return gKeyCodes
[i
].keycode
;
686 int32_t nsXBLPrototypeHandler::KeyToMask(int32_t key
)
690 case nsIDOMKeyEvent::DOM_VK_META
:
691 return cMeta
| cMetaMask
;
693 case nsIDOMKeyEvent::DOM_VK_WIN
:
694 return cOS
| cOSMask
;
696 case nsIDOMKeyEvent::DOM_VK_ALT
:
697 return cAlt
| cAltMask
;
699 case nsIDOMKeyEvent::DOM_VK_CONTROL
:
701 return cControl
| cControlMask
;
703 return cControl
| cControlMask
; // for warning avoidance
708 nsXBLPrototypeHandler::AccelKeyMask()
710 switch (WidgetInputEvent::AccelModifier()) {
712 return KeyToMask(nsIDOMKeyEvent::DOM_VK_ALT
);
713 case MODIFIER_CONTROL
:
714 return KeyToMask(nsIDOMKeyEvent::DOM_VK_CONTROL
);
716 return KeyToMask(nsIDOMKeyEvent::DOM_VK_META
);
718 return KeyToMask(nsIDOMKeyEvent::DOM_VK_WIN
);
720 MOZ_CRASH("Handle the new result of WidgetInputEvent::AccelModifier()");
726 nsXBLPrototypeHandler::GetEventType(nsAString
& aEvent
)
728 nsCOMPtr
<nsIContent
> handlerElement
= GetHandlerElement();
729 if (!handlerElement
) {
733 handlerElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::event
, aEvent
);
735 if (aEvent
.IsEmpty() && (mType
& NS_HANDLER_TYPE_XUL
))
736 // If no type is specified for a XUL <key> element, let's assume that we're "keypress".
737 aEvent
.AssignLiteral("keypress");
741 nsXBLPrototypeHandler::ConstructPrototype(nsIContent
* aKeyElement
,
742 const char16_t
* aEvent
,
743 const char16_t
* aPhase
,
744 const char16_t
* aAction
,
745 const char16_t
* aCommand
,
746 const char16_t
* aKeyCode
,
747 const char16_t
* aCharCode
,
748 const char16_t
* aModifiers
,
749 const char16_t
* aButton
,
750 const char16_t
* aClickCount
,
751 const char16_t
* aGroup
,
752 const char16_t
* aPreventDefault
,
753 const char16_t
* aAllowUntrusted
)
758 mType
|= NS_HANDLER_TYPE_XUL
;
759 nsCOMPtr
<nsIWeakReference
> weak
= do_GetWeakReference(aKeyElement
);
763 weak
.swap(mHandlerElement
);
766 mType
|= aCommand
? NS_HANDLER_TYPE_XBL_COMMAND
: NS_HANDLER_TYPE_XBL_JS
;
767 mHandlerText
= nullptr;
773 mPhase
= NS_PHASE_BUBBLING
;
776 mHandlerText
= ToNewUnicode(nsDependentString(aAction
));
778 mHandlerText
= ToNewUnicode(nsDependentString(aCommand
));
780 nsAutoString
event(aEvent
);
781 if (event
.IsEmpty()) {
782 if (mType
& NS_HANDLER_TYPE_XUL
)
788 mEventName
= do_GetAtom(event
);
791 const nsDependentString
phase(aPhase
);
792 if (phase
.EqualsLiteral("capturing"))
793 mPhase
= NS_PHASE_CAPTURING
;
794 else if (phase
.EqualsLiteral("target"))
795 mPhase
= NS_PHASE_TARGET
;
798 // Button and clickcount apply only to XBL handlers and don't apply to XUL key
800 if (aButton
&& *aButton
)
801 mDetail
= *aButton
- '0';
803 if (aClickCount
&& *aClickCount
)
804 mMisc
= *aClickCount
- '0';
806 // Modifiers are supported by both types of handlers (XUL and XBL).
807 nsAutoString
modifiers(aModifiers
);
808 if (mType
& NS_HANDLER_TYPE_XUL
)
809 aKeyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::modifiers
, modifiers
);
811 if (!modifiers
.IsEmpty()) {
812 mKeyMask
= cAllModifiers
;
813 char* str
= ToNewCString(modifiers
);
815 char* token
= nsCRT::strtok( str
, ", \t", &newStr
);
816 while( token
!= nullptr ) {
817 if (PL_strcmp(token
, "shift") == 0)
818 mKeyMask
|= cShift
| cShiftMask
;
819 else if (PL_strcmp(token
, "alt") == 0)
820 mKeyMask
|= cAlt
| cAltMask
;
821 else if (PL_strcmp(token
, "meta") == 0)
822 mKeyMask
|= cMeta
| cMetaMask
;
823 else if (PL_strcmp(token
, "os") == 0)
824 mKeyMask
|= cOS
| cOSMask
;
825 else if (PL_strcmp(token
, "control") == 0)
826 mKeyMask
|= cControl
| cControlMask
;
827 else if (PL_strcmp(token
, "accel") == 0)
828 mKeyMask
|= AccelKeyMask();
829 else if (PL_strcmp(token
, "access") == 0)
830 mKeyMask
|= KeyToMask(kMenuAccessKey
);
831 else if (PL_strcmp(token
, "any") == 0)
832 mKeyMask
&= ~(mKeyMask
<< 5);
834 token
= nsCRT::strtok( newStr
, ", \t", &newStr
);
840 nsAutoString
key(aCharCode
);
842 if (mType
& NS_HANDLER_TYPE_XUL
) {
843 aKeyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::key
, key
);
845 aKeyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::charcode
, key
);
849 if (!key
.IsEmpty()) {
851 mKeyMask
= cAllModifiers
;
854 // We have a charcode.
857 const uint8_t GTK2Modifiers
= cShift
| cControl
| cShiftMask
| cControlMask
;
858 if ((mKeyMask
& GTK2Modifiers
) == GTK2Modifiers
&&
859 modifiers
.First() != char16_t(',') &&
860 (mDetail
== 'u' || mDetail
== 'U'))
861 ReportKeyConflict(key
.get(), modifiers
.get(), aKeyElement
, "GTK2Conflict");
862 const uint8_t WinModifiers
= cControl
| cAlt
| cControlMask
| cAltMask
;
863 if ((mKeyMask
& WinModifiers
) == WinModifiers
&&
864 modifiers
.First() != char16_t(',') &&
865 (('A' <= mDetail
&& mDetail
<= 'Z') ||
866 ('a' <= mDetail
&& mDetail
<= 'z')))
867 ReportKeyConflict(key
.get(), modifiers
.get(), aKeyElement
, "WinConflict");
870 key
.Assign(aKeyCode
);
871 if (mType
& NS_HANDLER_TYPE_XUL
)
872 aKeyElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::keycode
, key
);
874 if (!key
.IsEmpty()) {
876 mKeyMask
= cAllModifiers
;
877 mDetail
= GetMatchingKeyCode(key
);
881 if (aGroup
&& nsDependentString(aGroup
).EqualsLiteral("system"))
882 mType
|= NS_HANDLER_TYPE_SYSTEM
;
884 if (aPreventDefault
&&
885 nsDependentString(aPreventDefault
).EqualsLiteral("true"))
886 mType
|= NS_HANDLER_TYPE_PREVENTDEFAULT
;
888 if (aAllowUntrusted
) {
889 mType
|= NS_HANDLER_HAS_ALLOW_UNTRUSTED_ATTR
;
890 if (nsDependentString(aAllowUntrusted
).EqualsLiteral("true")) {
891 mType
|= NS_HANDLER_ALLOW_UNTRUSTED
;
893 mType
&= ~NS_HANDLER_ALLOW_UNTRUSTED
;
899 nsXBLPrototypeHandler::ReportKeyConflict(const char16_t
* aKey
, const char16_t
* aModifiers
, nsIContent
* aKeyElement
, const char *aMessageName
)
901 nsCOMPtr
<nsIDocument
> doc
;
902 if (mPrototypeBinding
) {
903 nsXBLDocumentInfo
* docInfo
= mPrototypeBinding
->XBLDocumentInfo();
905 doc
= docInfo
->GetDocument();
907 } else if (aKeyElement
) {
908 doc
= aKeyElement
->OwnerDoc();
911 const char16_t
* params
[] = { aKey
, aModifiers
};
912 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
913 NS_LITERAL_CSTRING("XBL Prototype Handler"), doc
,
914 nsContentUtils::eXBL_PROPERTIES
,
916 params
, ArrayLength(params
),
917 nullptr, EmptyString(), mLineNumber
);
921 nsXBLPrototypeHandler::ModifiersMatchMask(
922 nsIDOMUIEvent
* aEvent
,
923 const IgnoreModifierState
& aIgnoreModifierState
)
925 WidgetInputEvent
* inputEvent
= aEvent
->GetInternalNSEvent()->AsInputEvent();
926 NS_ENSURE_TRUE(inputEvent
, false);
928 if (mKeyMask
& cMetaMask
) {
929 if (inputEvent
->IsMeta() != ((mKeyMask
& cMeta
) != 0)) {
934 if ((mKeyMask
& cOSMask
) && !aIgnoreModifierState
.mOS
) {
935 if (inputEvent
->IsOS() != ((mKeyMask
& cOS
) != 0)) {
940 if (mKeyMask
& cShiftMask
&& !aIgnoreModifierState
.mShift
) {
941 if (inputEvent
->IsShift() != ((mKeyMask
& cShift
) != 0)) {
946 if (mKeyMask
& cAltMask
) {
947 if (inputEvent
->IsAlt() != ((mKeyMask
& cAlt
) != 0)) {
952 if (mKeyMask
& cControlMask
) {
953 if (inputEvent
->IsControl() != ((mKeyMask
& cControl
) != 0)) {
962 nsXBLPrototypeHandler::Read(nsIObjectInputStream
* aStream
)
964 AssertInCompilationScope();
965 nsresult rv
= aStream
->Read8(&mPhase
);
966 NS_ENSURE_SUCCESS(rv
, rv
);
967 rv
= aStream
->Read8(&mType
);
968 NS_ENSURE_SUCCESS(rv
, rv
);
969 rv
= aStream
->Read8(&mMisc
);
970 NS_ENSURE_SUCCESS(rv
, rv
);
972 rv
= aStream
->Read32(reinterpret_cast<uint32_t*>(&mKeyMask
));
973 NS_ENSURE_SUCCESS(rv
, rv
);
975 rv
= aStream
->Read32(&detail
);
976 NS_ENSURE_SUCCESS(rv
, rv
);
980 rv
= aStream
->ReadString(name
);
981 NS_ENSURE_SUCCESS(rv
, rv
);
982 mEventName
= do_GetAtom(name
);
984 rv
= aStream
->Read32(&mLineNumber
);
985 NS_ENSURE_SUCCESS(rv
, rv
);
987 nsAutoString handlerText
;
988 rv
= aStream
->ReadString(handlerText
);
989 NS_ENSURE_SUCCESS(rv
, rv
);
990 if (!handlerText
.IsEmpty())
991 mHandlerText
= ToNewUnicode(handlerText
);
997 nsXBLPrototypeHandler::Write(nsIObjectOutputStream
* aStream
)
999 AssertInCompilationScope();
1000 // Make sure we don't write out NS_HANDLER_TYPE_XUL types, as they are used
1001 // for <keyset> elements.
1002 if ((mType
& NS_HANDLER_TYPE_XUL
) || !mEventName
)
1005 XBLBindingSerializeDetails type
= XBLBinding_Serialize_Handler
;
1007 nsresult rv
= aStream
->Write8(type
);
1008 rv
= aStream
->Write8(mPhase
);
1009 NS_ENSURE_SUCCESS(rv
, rv
);
1010 rv
= aStream
->Write8(mType
);
1011 NS_ENSURE_SUCCESS(rv
, rv
);
1012 rv
= aStream
->Write8(mMisc
);
1013 NS_ENSURE_SUCCESS(rv
, rv
);
1014 rv
= aStream
->Write32(static_cast<uint32_t>(mKeyMask
));
1015 NS_ENSURE_SUCCESS(rv
, rv
);
1016 rv
= aStream
->Write32(mDetail
);
1017 NS_ENSURE_SUCCESS(rv
, rv
);
1019 rv
= aStream
->WriteWStringZ(nsDependentAtomString(mEventName
).get());
1020 NS_ENSURE_SUCCESS(rv
, rv
);
1022 rv
= aStream
->Write32(mLineNumber
);
1023 NS_ENSURE_SUCCESS(rv
, rv
);
1024 return aStream
->WriteWStringZ(mHandlerText
? mHandlerText
: MOZ_UTF16(""));