summaryrefslogtreecommitdiffstats
path: root/dom/xbl/nsXBLPrototypeHandler.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/xbl/nsXBLPrototypeHandler.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/xbl/nsXBLPrototypeHandler.cpp')
-rw-r--r--dom/xbl/nsXBLPrototypeHandler.cpp1016
1 files changed, 1016 insertions, 0 deletions
diff --git a/dom/xbl/nsXBLPrototypeHandler.cpp b/dom/xbl/nsXBLPrototypeHandler.cpp
new file mode 100644
index 000000000..2591a72fd
--- /dev/null
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -0,0 +1,1016 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsQueryObject.h"
+#include "nsXBLPrototypeHandler.h"
+#include "nsXBLPrototypeBinding.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsIContent.h"
+#include "nsIAtom.h"
+#include "nsIDOMKeyEvent.h"
+#include "nsIDOMMouseEvent.h"
+#include "nsNameSpaceManager.h"
+#include "nsIDocument.h"
+#include "nsIController.h"
+#include "nsIControllers.h"
+#include "nsIDOMXULElement.h"
+#include "nsIURI.h"
+#include "nsIDOMHTMLTextAreaElement.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsFocusManager.h"
+#include "nsIFormControl.h"
+#include "nsIDOMEventListener.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIWindowRoot.h"
+#include "nsIDOMWindow.h"
+#include "nsIServiceManager.h"
+#include "nsIScriptError.h"
+#include "nsXPIDLString.h"
+#include "nsReadableUtils.h"
+#include "nsGkAtoms.h"
+#include "nsIXPConnect.h"
+#include "mozilla/AddonPathService.h"
+#include "nsDOMCID.h"
+#include "nsUnicharUtils.h"
+#include "nsCRT.h"
+#include "nsXBLEventHandler.h"
+#include "nsXBLSerialize.h"
+#include "nsJSUtils.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/JSEventHandler.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/EventHandlerBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "xpcpublic.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+uint32_t nsXBLPrototypeHandler::gRefCnt = 0;
+
+int32_t nsXBLPrototypeHandler::kMenuAccessKey = -1;
+
+const int32_t nsXBLPrototypeHandler::cShift = (1<<0);
+const int32_t nsXBLPrototypeHandler::cAlt = (1<<1);
+const int32_t nsXBLPrototypeHandler::cControl = (1<<2);
+const int32_t nsXBLPrototypeHandler::cMeta = (1<<3);
+const int32_t nsXBLPrototypeHandler::cOS = (1<<4);
+
+const int32_t nsXBLPrototypeHandler::cShiftMask = (1<<5);
+const int32_t nsXBLPrototypeHandler::cAltMask = (1<<6);
+const int32_t nsXBLPrototypeHandler::cControlMask = (1<<7);
+const int32_t nsXBLPrototypeHandler::cMetaMask = (1<<8);
+const int32_t nsXBLPrototypeHandler::cOSMask = (1<<9);
+
+const int32_t nsXBLPrototypeHandler::cAllModifiers =
+ cShiftMask | cAltMask | cControlMask | cMetaMask | cOSMask;
+
+nsXBLPrototypeHandler::nsXBLPrototypeHandler(const char16_t* aEvent,
+ const char16_t* aPhase,
+ const char16_t* aAction,
+ const char16_t* aCommand,
+ const char16_t* aKeyCode,
+ const char16_t* aCharCode,
+ const char16_t* aModifiers,
+ const char16_t* aButton,
+ const char16_t* aClickCount,
+ const char16_t* aGroup,
+ const char16_t* aPreventDefault,
+ const char16_t* aAllowUntrusted,
+ nsXBLPrototypeBinding* aBinding,
+ uint32_t aLineNumber)
+ : mHandlerText(nullptr),
+ mLineNumber(aLineNumber),
+ mNextHandler(nullptr),
+ mPrototypeBinding(aBinding)
+{
+ Init();
+
+ ConstructPrototype(nullptr, aEvent, aPhase, aAction, aCommand, aKeyCode,
+ aCharCode, aModifiers, aButton, aClickCount,
+ aGroup, aPreventDefault, aAllowUntrusted);
+}
+
+nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsIContent* aHandlerElement)
+ : mHandlerElement(nullptr),
+ mLineNumber(0),
+ mNextHandler(nullptr),
+ mPrototypeBinding(nullptr)
+{
+ Init();
+
+ // Make sure our prototype is initialized.
+ ConstructPrototype(aHandlerElement);
+}
+
+nsXBLPrototypeHandler::nsXBLPrototypeHandler(nsXBLPrototypeBinding* aBinding)
+ : mHandlerText(nullptr),
+ mLineNumber(0),
+ mNextHandler(nullptr),
+ mPrototypeBinding(aBinding)
+{
+ Init();
+}
+
+nsXBLPrototypeHandler::~nsXBLPrototypeHandler()
+{
+ --gRefCnt;
+ if (mType & NS_HANDLER_TYPE_XUL) {
+ NS_IF_RELEASE(mHandlerElement);
+ } else if (mHandlerText) {
+ free(mHandlerText);
+ }
+
+ // We own the next handler in the chain, so delete it now.
+ NS_CONTENT_DELETE_LIST_MEMBER(nsXBLPrototypeHandler, this, mNextHandler);
+}
+
+already_AddRefed<nsIContent>
+nsXBLPrototypeHandler::GetHandlerElement()
+{
+ if (mType & NS_HANDLER_TYPE_XUL) {
+ nsCOMPtr<nsIContent> element = do_QueryReferent(mHandlerElement);
+ return element.forget();
+ }
+
+ return nullptr;
+}
+
+void
+nsXBLPrototypeHandler::AppendHandlerText(const nsAString& aText)
+{
+ if (mHandlerText) {
+ // Append our text to the existing text.
+ char16_t* temp = mHandlerText;
+ mHandlerText = ToNewUnicode(nsDependentString(temp) + aText);
+ free(temp);
+ }
+ else {
+ mHandlerText = ToNewUnicode(aText);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Get the menu access key from prefs.
+// XXX Eventually pick up using CSS3 key-equivalent property or somesuch
+void
+nsXBLPrototypeHandler::InitAccessKeys()
+{
+ if (kMenuAccessKey >= 0) {
+ return;
+ }
+
+ // Compiled-in defaults, in case we can't get the pref --
+ // mac doesn't have menu shortcuts, other platforms use alt.
+#ifdef XP_MACOSX
+ kMenuAccessKey = 0;
+#else
+ kMenuAccessKey = nsIDOMKeyEvent::DOM_VK_ALT;
+#endif
+
+ // Get the menu access key value from prefs, overriding the default:
+ kMenuAccessKey =
+ Preferences::GetInt("ui.key.menuAccessKey", kMenuAccessKey);
+}
+
+nsresult
+nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
+ nsIDOMEvent* aEvent)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ // Prevent default action?
+ if (mType & NS_HANDLER_TYPE_PREVENTDEFAULT) {
+ aEvent->PreventDefault();
+ // If we prevent default, then it's okay for
+ // mHandlerElement and mHandlerText to be null
+ rv = NS_OK;
+ }
+
+ if (!mHandlerElement) // This works for both types of handlers. In both cases, the union's var should be defined.
+ return rv;
+
+ // See if our event receiver is a content node (and not us).
+ bool isXULKey = !!(mType & NS_HANDLER_TYPE_XUL);
+ bool isXBLCommand = !!(mType & NS_HANDLER_TYPE_XBL_COMMAND);
+ NS_ASSERTION(!(isXULKey && isXBLCommand),
+ "can't be both a key and xbl command handler");
+
+ // XUL handlers and commands shouldn't be triggered by non-trusted
+ // events.
+ if (isXULKey || isXBLCommand) {
+ bool trustedEvent = false;
+ aEvent->GetIsTrusted(&trustedEvent);
+
+ if (!trustedEvent)
+ return NS_OK;
+ }
+
+ if (isXBLCommand) {
+ return DispatchXBLCommand(aTarget, aEvent);
+ }
+
+ // If we're executing on a XUL key element, just dispatch a command
+ // event at the element. It will take care of retargeting it to its
+ // command element, if applicable, and executing the event handler.
+ if (isXULKey) {
+ return DispatchXULKeyCommand(aEvent);
+ }
+
+ // Look for a compiled handler on the element.
+ // Should be compiled and bound with "on" in front of the name.
+ nsCOMPtr<nsIAtom> onEventAtom = NS_Atomize(NS_LITERAL_STRING("onxbl") +
+ nsDependentAtomString(mEventName));
+
+ // Compile the handler and bind it to the element.
+ nsCOMPtr<nsIScriptGlobalObject> boundGlobal;
+ nsCOMPtr<nsPIWindowRoot> winRoot = do_QueryInterface(aTarget);
+ if (winRoot) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = winRoot->GetWindow()) {
+ nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
+ NS_ENSURE_TRUE(innerWindow, NS_ERROR_UNEXPECTED);
+
+ boundGlobal = do_QueryInterface(innerWindow->GetPrivateRoot());
+ }
+ }
+ else boundGlobal = do_QueryInterface(aTarget);
+
+ if (!boundGlobal) {
+ nsCOMPtr<nsIDocument> boundDocument(do_QueryInterface(aTarget));
+ if (!boundDocument) {
+ // We must be an element.
+ nsCOMPtr<nsIContent> content(do_QueryInterface(aTarget));
+ if (!content)
+ return NS_OK;
+ boundDocument = content->OwnerDoc();
+ }
+
+ boundGlobal = do_QueryInterface(boundDocument->GetScopeObject());
+ }
+
+ if (!boundGlobal)
+ return NS_OK;
+
+ nsISupports *scriptTarget;
+
+ if (winRoot) {
+ scriptTarget = boundGlobal;
+ } else {
+ scriptTarget = aTarget;
+ }
+
+ // We're about to create a new JSEventHandler, which means that we need to
+ // Initiatize an AutoJSAPI with aTarget's bound global to make sure any errors
+ // are reported to the correct place.
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(boundGlobal))) {
+ return NS_OK;
+ }
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JSObject*> handler(cx);
+
+ rv = EnsureEventHandler(jsapi, onEventAtom, &handler);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSAddonId* addonId = MapURIToAddonID(mPrototypeBinding->DocURI());
+
+ JS::Rooted<JSObject*> globalObject(cx, boundGlobal->GetGlobalJSObject());
+ JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, addonId));
+ NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
+
+ // Bind it to the bound element. Note that if we're using a separate XBL scope,
+ // we'll actually be binding the event handler to a cross-compartment wrapper
+ // to the bound element's reflector.
+
+ // First, enter our XBL scope. This is where the generic handler should have
+ // been compiled, above.
+ JSAutoCompartment ac(cx, scopeObject);
+ JS::Rooted<JSObject*> genericHandler(cx, handler.get());
+ bool ok = JS_WrapObject(cx, &genericHandler);
+ NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+ MOZ_ASSERT(!js::IsCrossCompartmentWrapper(genericHandler));
+
+ // Build a scope chain in the XBL scope.
+ RefPtr<Element> targetElement = do_QueryObject(scriptTarget);
+ JS::AutoObjectVector scopeChain(cx);
+ ok = nsJSUtils::GetScopeChainForElement(cx, targetElement, scopeChain);
+ NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+
+ // Next, clone the generic handler with our desired scope chain.
+ JS::Rooted<JSObject*> bound(cx, JS::CloneFunctionObject(cx, genericHandler,
+ scopeChain));
+ NS_ENSURE_TRUE(bound, NS_ERROR_FAILURE);
+
+ RefPtr<EventHandlerNonNull> handlerCallback =
+ new EventHandlerNonNull(nullptr, bound, /* aIncumbentGlobal = */ nullptr);
+
+ TypedEventHandler typedHandler(handlerCallback);
+
+ // Execute it.
+ nsCOMPtr<JSEventHandler> jsEventHandler;
+ rv = NS_NewJSEventHandler(scriptTarget, onEventAtom,
+ typedHandler,
+ getter_AddRefs(jsEventHandler));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Handle the event.
+ jsEventHandler->HandleEvent(aEvent);
+ jsEventHandler->Disconnect();
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeHandler::EnsureEventHandler(AutoJSAPI& jsapi, nsIAtom* aName,
+ JS::MutableHandle<JSObject*> aHandler)
+{
+ JSContext* cx = jsapi.cx();
+
+ // Check to see if we've already compiled this
+ JS::Rooted<JSObject*> globalObject(cx, JS::CurrentGlobalOrNull(cx));
+ nsCOMPtr<nsPIDOMWindowInner> pWindow = xpc::WindowOrNull(globalObject)->AsInner();
+ if (pWindow) {
+ JS::Rooted<JSObject*> cachedHandler(cx, pWindow->GetCachedXBLPrototypeHandler(this));
+ if (cachedHandler) {
+ JS::ExposeObjectToActiveJS(cachedHandler);
+ aHandler.set(cachedHandler);
+ NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE);
+ return NS_OK;
+ }
+ }
+
+ // Ensure that we have something to compile
+ nsDependentString handlerText(mHandlerText);
+ NS_ENSURE_TRUE(!handlerText.IsEmpty(), NS_ERROR_FAILURE);
+
+ JSAddonId* addonId = MapURIToAddonID(mPrototypeBinding->DocURI());
+
+ JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, addonId));
+ NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
+
+ nsAutoCString bindingURI;
+ nsresult rv = mPrototypeBinding->DocURI()->GetSpec(bindingURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t argCount;
+ const char **argNames;
+ nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, false, &argCount,
+ &argNames);
+
+ // Compile the event handler in the xbl scope.
+ JSAutoCompartment ac(cx, scopeObject);
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(bindingURI.get(), mLineNumber)
+ .setVersion(JSVERSION_LATEST);
+
+ JS::Rooted<JSObject*> handlerFun(cx);
+ JS::AutoObjectVector emptyVector(cx);
+ rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options,
+ nsAtomCString(aName), argCount,
+ argNames, handlerText,
+ handlerFun.address());
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(handlerFun, NS_ERROR_FAILURE);
+
+ // Wrap the handler into the content scope, since we're about to stash it
+ // on the DOM window and such.
+ JSAutoCompartment ac2(cx, globalObject);
+ bool ok = JS_WrapObject(cx, &handlerFun);
+ NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+ aHandler.set(handlerFun);
+ NS_ENSURE_TRUE(aHandler, NS_ERROR_FAILURE);
+
+ if (pWindow) {
+ pWindow->CacheXBLPrototypeHandler(this, aHandler);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeHandler::DispatchXBLCommand(EventTarget* aTarget, nsIDOMEvent* aEvent)
+{
+ // This is a special-case optimization to make command handling fast.
+ // It isn't really a part of XBL, but it helps speed things up.
+
+ if (aEvent) {
+ // See if preventDefault has been set. If so, don't execute.
+ bool preventDefault = false;
+ aEvent->GetDefaultPrevented(&preventDefault);
+ if (preventDefault) {
+ return NS_OK;
+ }
+ bool dispatchStopped = aEvent->IsDispatchStopped();
+ if (dispatchStopped) {
+ return NS_OK;
+ }
+ }
+
+ // Instead of executing JS, let's get the controller for the bound
+ // element and call doCommand on it.
+ nsCOMPtr<nsIController> controller;
+
+ nsCOMPtr<nsPIDOMWindowOuter> privateWindow;
+ nsCOMPtr<nsPIWindowRoot> windowRoot = do_QueryInterface(aTarget);
+ if (windowRoot) {
+ privateWindow = windowRoot->GetWindow();
+ }
+ else {
+ privateWindow = do_QueryInterface(aTarget);
+ if (!privateWindow) {
+ nsCOMPtr<nsIContent> elt(do_QueryInterface(aTarget));
+ nsCOMPtr<nsIDocument> doc;
+ // XXXbz sXBL/XBL2 issue -- this should be the "scope doc" or
+ // something... whatever we use when wrapping DOM nodes
+ // normally. It's not clear that the owner doc is the right
+ // thing.
+ if (elt)
+ doc = elt->OwnerDoc();
+
+ if (!doc)
+ doc = do_QueryInterface(aTarget);
+
+ if (!doc)
+ return NS_ERROR_FAILURE;
+
+ privateWindow = doc->GetWindow();
+ if (!privateWindow)
+ return NS_ERROR_FAILURE;
+ }
+
+ windowRoot = privateWindow->GetTopWindowRoot();
+ }
+
+ NS_LossyConvertUTF16toASCII command(mHandlerText);
+ if (windowRoot)
+ windowRoot->GetControllerForCommand(command.get(), getter_AddRefs(controller));
+ else
+ controller = GetController(aTarget); // We're attached to the receiver possibly.
+
+ // We are the default action for this command.
+ // Stop any other default action from executing.
+ aEvent->PreventDefault();
+
+ if (mEventName == nsGkAtoms::keypress &&
+ mDetail == nsIDOMKeyEvent::DOM_VK_SPACE &&
+ mMisc == 1) {
+ // get the focused element so that we can pageDown only at
+ // certain times.
+
+ nsCOMPtr<nsPIDOMWindowOuter> windowToCheck;
+ if (windowRoot)
+ windowToCheck = windowRoot->GetWindow();
+ else
+ windowToCheck = privateWindow->GetPrivateRoot();
+
+ nsCOMPtr<nsIContent> focusedContent;
+ if (windowToCheck) {
+ nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+ focusedContent =
+ nsFocusManager::GetFocusedDescendant(windowToCheck, true, getter_AddRefs(focusedWindow));
+ }
+
+ // If the focus is in an editable region, don't scroll.
+ if (focusedContent && focusedContent->IsEditable()) {
+ return NS_OK;
+ }
+
+ // If the focus is in a form control, don't scroll.
+ for (nsIContent* c = focusedContent; c; c = c->GetParent()) {
+ nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(c);
+ if (formControl) {
+ return NS_OK;
+ }
+ }
+ }
+
+ if (controller)
+ controller->DoCommand(command.get());
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeHandler::DispatchXULKeyCommand(nsIDOMEvent* aEvent)
+{
+ nsCOMPtr<nsIContent> handlerElement = GetHandlerElement();
+ NS_ENSURE_STATE(handlerElement);
+ if (handlerElement->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::disabled,
+ nsGkAtoms::_true,
+ eCaseMatters)) {
+ // Don't dispatch command events for disabled keys.
+ return NS_OK;
+ }
+
+ aEvent->PreventDefault();
+
+ // Copy the modifiers from the key event.
+ nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
+ if (!keyEvent) {
+ NS_ERROR("Trying to execute a key handler for a non-key event!");
+ return NS_ERROR_FAILURE;
+ }
+
+ // XXX We should use mozilla::Modifiers for supporting all modifiers.
+
+ bool isAlt = false;
+ bool isControl = false;
+ bool isShift = false;
+ bool isMeta = false;
+ keyEvent->GetAltKey(&isAlt);
+ keyEvent->GetCtrlKey(&isControl);
+ keyEvent->GetShiftKey(&isShift);
+ keyEvent->GetMetaKey(&isMeta);
+
+ nsContentUtils::DispatchXULCommand(handlerElement, true,
+ nullptr, nullptr,
+ isControl, isAlt, isShift, isMeta);
+ return NS_OK;
+}
+
+already_AddRefed<nsIAtom>
+nsXBLPrototypeHandler::GetEventName()
+{
+ nsCOMPtr<nsIAtom> eventName = mEventName;
+ return eventName.forget();
+}
+
+already_AddRefed<nsIController>
+nsXBLPrototypeHandler::GetController(EventTarget* aTarget)
+{
+ // XXX Fix this so there's a generic interface that describes controllers,
+ // This code should have no special knowledge of what objects might have controllers.
+ nsCOMPtr<nsIControllers> controllers;
+
+ nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aTarget));
+ if (xulElement)
+ xulElement->GetControllers(getter_AddRefs(controllers));
+
+ if (!controllers) {
+ nsCOMPtr<nsIDOMHTMLTextAreaElement> htmlTextArea(do_QueryInterface(aTarget));
+ if (htmlTextArea)
+ htmlTextArea->GetControllers(getter_AddRefs(controllers));
+ }
+
+ if (!controllers) {
+ nsCOMPtr<nsIDOMHTMLInputElement> htmlInputElement(do_QueryInterface(aTarget));
+ if (htmlInputElement)
+ htmlInputElement->GetControllers(getter_AddRefs(controllers));
+ }
+
+ if (!controllers) {
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow(do_QueryInterface(aTarget));
+ if (domWindow) {
+ domWindow->GetControllers(getter_AddRefs(controllers));
+ }
+ }
+
+ // Return the first controller.
+ // XXX This code should be checking the command name and using supportscommand and
+ // iscommandenabled.
+ nsCOMPtr<nsIController> controller;
+ if (controllers) {
+ controllers->GetControllerAt(0, getter_AddRefs(controller));
+ }
+
+ return controller.forget();
+}
+
+bool
+nsXBLPrototypeHandler::KeyEventMatched(
+ nsIDOMKeyEvent* aKeyEvent,
+ uint32_t aCharCode,
+ const IgnoreModifierState& aIgnoreModifierState)
+{
+ if (mDetail != -1) {
+ // Get the keycode or charcode of the key event.
+ uint32_t code;
+
+ if (mMisc) {
+ if (aCharCode)
+ code = aCharCode;
+ else
+ aKeyEvent->GetCharCode(&code);
+ if (IS_IN_BMP(code))
+ code = ToLowerCase(char16_t(code));
+ }
+ else
+ aKeyEvent->GetKeyCode(&code);
+
+ if (code != uint32_t(mDetail))
+ return false;
+ }
+
+ return ModifiersMatchMask(aKeyEvent, aIgnoreModifierState);
+}
+
+bool
+nsXBLPrototypeHandler::MouseEventMatched(nsIDOMMouseEvent* aMouseEvent)
+{
+ if (mDetail == -1 && mMisc == 0 && (mKeyMask & cAllModifiers) == 0)
+ return true; // No filters set up. It's generic.
+
+ int16_t button;
+ aMouseEvent->GetButton(&button);
+ if (mDetail != -1 && (button != mDetail))
+ return false;
+
+ int32_t clickcount;
+ aMouseEvent->GetDetail(&clickcount);
+ if (mMisc != 0 && (clickcount != mMisc))
+ return false;
+
+ return ModifiersMatchMask(aMouseEvent, IgnoreModifierState());
+}
+
+struct keyCodeData {
+ const char* str;
+ uint16_t strlength;
+ uint16_t keycode;
+};
+
+// All of these must be uppercase, since the function below does
+// case-insensitive comparison by converting to uppercase.
+// XXX: be sure to check this periodically for new symbol additions!
+static const keyCodeData gKeyCodes[] = {
+
+#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
+ { #aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode },
+#include "mozilla/VirtualKeyCodeList.h"
+#undef NS_DEFINE_VK
+
+ { nullptr, 0, 0 }
+};
+
+int32_t nsXBLPrototypeHandler::GetMatchingKeyCode(const nsAString& aKeyName)
+{
+ nsAutoCString keyName;
+ keyName.AssignWithConversion(aKeyName);
+ ToUpperCase(keyName); // We want case-insensitive comparison with data
+ // stored as uppercase.
+
+ uint32_t keyNameLength = keyName.Length();
+ const char* keyNameStr = keyName.get();
+ for (uint16_t i = 0; i < ArrayLength(gKeyCodes) - 1; ++i) {
+ if (keyNameLength == gKeyCodes[i].strlength &&
+ !nsCRT::strcmp(gKeyCodes[i].str, keyNameStr)) {
+ return gKeyCodes[i].keycode;
+ }
+ }
+
+ return 0;
+}
+
+int32_t nsXBLPrototypeHandler::KeyToMask(int32_t key)
+{
+ switch (key)
+ {
+ case nsIDOMKeyEvent::DOM_VK_META:
+ return cMeta | cMetaMask;
+
+ case nsIDOMKeyEvent::DOM_VK_WIN:
+ return cOS | cOSMask;
+
+ case nsIDOMKeyEvent::DOM_VK_ALT:
+ return cAlt | cAltMask;
+
+ case nsIDOMKeyEvent::DOM_VK_CONTROL:
+ default:
+ return cControl | cControlMask;
+ }
+ return cControl | cControlMask; // for warning avoidance
+}
+
+// static
+int32_t
+nsXBLPrototypeHandler::AccelKeyMask()
+{
+ switch (WidgetInputEvent::AccelModifier()) {
+ case MODIFIER_ALT:
+ return KeyToMask(nsIDOMKeyEvent::DOM_VK_ALT);
+ case MODIFIER_CONTROL:
+ return KeyToMask(nsIDOMKeyEvent::DOM_VK_CONTROL);
+ case MODIFIER_META:
+ return KeyToMask(nsIDOMKeyEvent::DOM_VK_META);
+ case MODIFIER_OS:
+ return KeyToMask(nsIDOMKeyEvent::DOM_VK_WIN);
+ default:
+ MOZ_CRASH("Handle the new result of WidgetInputEvent::AccelModifier()");
+ return 0;
+ }
+}
+
+void
+nsXBLPrototypeHandler::GetEventType(nsAString& aEvent)
+{
+ nsCOMPtr<nsIContent> handlerElement = GetHandlerElement();
+ if (!handlerElement) {
+ aEvent.Truncate();
+ return;
+ }
+ handlerElement->GetAttr(kNameSpaceID_None, nsGkAtoms::event, aEvent);
+
+ if (aEvent.IsEmpty() && (mType & NS_HANDLER_TYPE_XUL))
+ // If no type is specified for a XUL <key> element, let's assume that we're "keypress".
+ aEvent.AssignLiteral("keypress");
+}
+
+void
+nsXBLPrototypeHandler::ConstructPrototype(nsIContent* aKeyElement,
+ const char16_t* aEvent,
+ const char16_t* aPhase,
+ const char16_t* aAction,
+ const char16_t* aCommand,
+ const char16_t* aKeyCode,
+ const char16_t* aCharCode,
+ const char16_t* aModifiers,
+ const char16_t* aButton,
+ const char16_t* aClickCount,
+ const char16_t* aGroup,
+ const char16_t* aPreventDefault,
+ const char16_t* aAllowUntrusted)
+{
+ mType = 0;
+
+ if (aKeyElement) {
+ mType |= NS_HANDLER_TYPE_XUL;
+ MOZ_ASSERT(!mPrototypeBinding);
+ nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(aKeyElement);
+ if (!weak) {
+ return;
+ }
+ weak.swap(mHandlerElement);
+ }
+ else {
+ mType |= aCommand ? NS_HANDLER_TYPE_XBL_COMMAND : NS_HANDLER_TYPE_XBL_JS;
+ mHandlerText = nullptr;
+ }
+
+ mDetail = -1;
+ mMisc = 0;
+ mKeyMask = 0;
+ mPhase = NS_PHASE_BUBBLING;
+
+ if (aAction)
+ mHandlerText = ToNewUnicode(nsDependentString(aAction));
+ else if (aCommand)
+ mHandlerText = ToNewUnicode(nsDependentString(aCommand));
+
+ nsAutoString event(aEvent);
+ if (event.IsEmpty()) {
+ if (mType & NS_HANDLER_TYPE_XUL)
+ GetEventType(event);
+ if (event.IsEmpty())
+ return;
+ }
+
+ mEventName = NS_Atomize(event);
+
+ if (aPhase) {
+ const nsDependentString phase(aPhase);
+ if (phase.EqualsLiteral("capturing"))
+ mPhase = NS_PHASE_CAPTURING;
+ else if (phase.EqualsLiteral("target"))
+ mPhase = NS_PHASE_TARGET;
+ }
+
+ // Button and clickcount apply only to XBL handlers and don't apply to XUL key
+ // handlers.
+ if (aButton && *aButton)
+ mDetail = *aButton - '0';
+
+ if (aClickCount && *aClickCount)
+ mMisc = *aClickCount - '0';
+
+ // Modifiers are supported by both types of handlers (XUL and XBL).
+ nsAutoString modifiers(aModifiers);
+ if (mType & NS_HANDLER_TYPE_XUL)
+ aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiers);
+
+ if (!modifiers.IsEmpty()) {
+ mKeyMask = cAllModifiers;
+ char* str = ToNewCString(modifiers);
+ char* newStr;
+ char* token = nsCRT::strtok( str, ", \t", &newStr );
+ while( token != nullptr ) {
+ if (PL_strcmp(token, "shift") == 0)
+ mKeyMask |= cShift | cShiftMask;
+ else if (PL_strcmp(token, "alt") == 0)
+ mKeyMask |= cAlt | cAltMask;
+ else if (PL_strcmp(token, "meta") == 0)
+ mKeyMask |= cMeta | cMetaMask;
+ else if (PL_strcmp(token, "os") == 0)
+ mKeyMask |= cOS | cOSMask;
+ else if (PL_strcmp(token, "control") == 0)
+ mKeyMask |= cControl | cControlMask;
+ else if (PL_strcmp(token, "accel") == 0)
+ mKeyMask |= AccelKeyMask();
+ else if (PL_strcmp(token, "access") == 0)
+ mKeyMask |= KeyToMask(kMenuAccessKey);
+ else if (PL_strcmp(token, "any") == 0)
+ mKeyMask &= ~(mKeyMask << 5);
+
+ token = nsCRT::strtok( newStr, ", \t", &newStr );
+ }
+
+ free(str);
+ }
+
+ nsAutoString key(aCharCode);
+ if (key.IsEmpty()) {
+ if (mType & NS_HANDLER_TYPE_XUL) {
+ aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::key, key);
+ if (key.IsEmpty())
+ aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::charcode, key);
+ }
+ }
+
+ if (!key.IsEmpty()) {
+ if (mKeyMask == 0)
+ mKeyMask = cAllModifiers;
+ ToLowerCase(key);
+
+ // We have a charcode.
+ mMisc = 1;
+ mDetail = key[0];
+ const uint8_t GTK2Modifiers = cShift | cControl | cShiftMask | cControlMask;
+ if ((mType & NS_HANDLER_TYPE_XUL) &&
+ (mKeyMask & GTK2Modifiers) == GTK2Modifiers &&
+ modifiers.First() != char16_t(',') &&
+ (mDetail == 'u' || mDetail == 'U'))
+ ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "GTK2Conflict2");
+ const uint8_t WinModifiers = cControl | cAlt | cControlMask | cAltMask;
+ if ((mType & NS_HANDLER_TYPE_XUL) &&
+ (mKeyMask & WinModifiers) == WinModifiers &&
+ modifiers.First() != char16_t(',') &&
+ (('A' <= mDetail && mDetail <= 'Z') ||
+ ('a' <= mDetail && mDetail <= 'z')))
+ ReportKeyConflict(key.get(), modifiers.get(), aKeyElement, "WinConflict2");
+ }
+ else {
+ key.Assign(aKeyCode);
+ if (mType & NS_HANDLER_TYPE_XUL)
+ aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, key);
+
+ if (!key.IsEmpty()) {
+ if (mKeyMask == 0)
+ mKeyMask = cAllModifiers;
+ mDetail = GetMatchingKeyCode(key);
+ }
+ }
+
+ if (aGroup && nsDependentString(aGroup).EqualsLiteral("system"))
+ mType |= NS_HANDLER_TYPE_SYSTEM;
+
+ if (aPreventDefault &&
+ nsDependentString(aPreventDefault).EqualsLiteral("true"))
+ mType |= NS_HANDLER_TYPE_PREVENTDEFAULT;
+
+ if (aAllowUntrusted) {
+ mType |= NS_HANDLER_HAS_ALLOW_UNTRUSTED_ATTR;
+ if (nsDependentString(aAllowUntrusted).EqualsLiteral("true")) {
+ mType |= NS_HANDLER_ALLOW_UNTRUSTED;
+ } else {
+ mType &= ~NS_HANDLER_ALLOW_UNTRUSTED;
+ }
+ }
+}
+
+void
+nsXBLPrototypeHandler::ReportKeyConflict(const char16_t* aKey, const char16_t* aModifiers, nsIContent* aKeyElement, const char *aMessageName)
+{
+ nsCOMPtr<nsIDocument> doc;
+ if (mPrototypeBinding) {
+ nsXBLDocumentInfo* docInfo = mPrototypeBinding->XBLDocumentInfo();
+ if (docInfo) {
+ doc = docInfo->GetDocument();
+ }
+ } else {
+ doc = aKeyElement->OwnerDoc();
+ }
+
+ nsAutoString id;
+ aKeyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
+ const char16_t* params[] = { aKey, aModifiers, id.get() };
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("XBL Prototype Handler"), doc,
+ nsContentUtils::eXBL_PROPERTIES,
+ aMessageName,
+ params, ArrayLength(params),
+ nullptr, EmptyString(), mLineNumber);
+}
+
+bool
+nsXBLPrototypeHandler::ModifiersMatchMask(
+ nsIDOMUIEvent* aEvent,
+ const IgnoreModifierState& aIgnoreModifierState)
+{
+ WidgetInputEvent* inputEvent = aEvent->AsEvent()->WidgetEventPtr()->AsInputEvent();
+ NS_ENSURE_TRUE(inputEvent, false);
+
+ if (mKeyMask & cMetaMask) {
+ if (inputEvent->IsMeta() != ((mKeyMask & cMeta) != 0)) {
+ return false;
+ }
+ }
+
+ if ((mKeyMask & cOSMask) && !aIgnoreModifierState.mOS) {
+ if (inputEvent->IsOS() != ((mKeyMask & cOS) != 0)) {
+ return false;
+ }
+ }
+
+ if (mKeyMask & cShiftMask && !aIgnoreModifierState.mShift) {
+ if (inputEvent->IsShift() != ((mKeyMask & cShift) != 0)) {
+ return false;
+ }
+ }
+
+ if (mKeyMask & cAltMask) {
+ if (inputEvent->IsAlt() != ((mKeyMask & cAlt) != 0)) {
+ return false;
+ }
+ }
+
+ if (mKeyMask & cControlMask) {
+ if (inputEvent->IsControl() != ((mKeyMask & cControl) != 0)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult
+nsXBLPrototypeHandler::Read(nsIObjectInputStream* aStream)
+{
+ AssertInCompilationScope();
+ nsresult rv = aStream->Read8(&mPhase);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Read8(&mType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Read8(&mMisc);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->Read32(reinterpret_cast<uint32_t*>(&mKeyMask));
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t detail;
+ rv = aStream->Read32(&detail);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mDetail = detail;
+
+ nsAutoString name;
+ rv = aStream->ReadString(name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mEventName = NS_Atomize(name);
+
+ rv = aStream->Read32(&mLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString handlerText;
+ rv = aStream->ReadString(handlerText);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!handlerText.IsEmpty())
+ mHandlerText = ToNewUnicode(handlerText);
+
+ return NS_OK;
+}
+
+nsresult
+nsXBLPrototypeHandler::Write(nsIObjectOutputStream* aStream)
+{
+ AssertInCompilationScope();
+ // Make sure we don't write out NS_HANDLER_TYPE_XUL types, as they are used
+ // for <keyset> elements.
+ if ((mType & NS_HANDLER_TYPE_XUL) || !mEventName)
+ return NS_OK;
+
+ XBLBindingSerializeDetails type = XBLBinding_Serialize_Handler;
+
+ nsresult rv = aStream->Write8(type);
+ rv = aStream->Write8(mPhase);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write8(mType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write8(mMisc);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write32(static_cast<uint32_t>(mKeyMask));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = aStream->Write32(mDetail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->WriteWStringZ(nsDependentAtomString(mEventName).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aStream->Write32(mLineNumber);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return aStream->WriteWStringZ(mHandlerText ? mHandlerText : u"");
+}