diff options
Diffstat (limited to 'dom/events/UIEvent.cpp')
-rw-r--r-- | dom/events/UIEvent.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/dom/events/UIEvent.cpp b/dom/events/UIEvent.cpp new file mode 100644 index 0000000000..ce43eb08f5 --- /dev/null +++ b/dom/events/UIEvent.cpp @@ -0,0 +1,335 @@ +/* -*- 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 "base/basictypes.h" +#include "ipc/IPCMessageUtils.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/dom/UIEvent.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/ContentEvents.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/PointerLockManager.h" +#include "mozilla/PresShell.h" +#include "mozilla/TextEvents.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsIContent.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIDocShell.h" +#include "nsIFrame.h" +#include "nsLayoutUtils.h" +#include "prtime.h" + +namespace mozilla::dom { + +UIEvent::UIEvent(EventTarget* aOwner, nsPresContext* aPresContext, + WidgetGUIEvent* aEvent) + : Event(aOwner, aPresContext, + aEvent ? aEvent : new InternalUIEvent(false, eVoidEvent, nullptr)), + mDefaultClientPoint(0, 0), + mLayerPoint(0, 0), + mPagePoint(0, 0), + mMovementPoint(0, 0) { + if (aEvent) { + mEventIsInternal = false; + } else { + mEventIsInternal = true; + } + + // Fill mDetail and mView according to the mEvent (widget-generated + // event) we've got + switch (mEvent->mClass) { + case eUIEventClass: { + mDetail = mEvent->AsUIEvent()->mDetail; + break; + } + + case eScrollPortEventClass: { + InternalScrollPortEvent* scrollEvent = mEvent->AsScrollPortEvent(); + mDetail = static_cast<int32_t>(scrollEvent->mOrient); + break; + } + + default: + mDetail = 0; + break; + } + + mView = nullptr; + if (mPresContext) { + nsIDocShell* docShell = mPresContext->GetDocShell(); + if (docShell) { + mView = docShell->GetWindow(); + } + } +} + +// static +already_AddRefed<UIEvent> UIEvent::Constructor(const GlobalObject& aGlobal, + const nsAString& aType, + const UIEventInit& aParam) { + nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); + RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr); + bool trusted = e->Init(t); + e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView, + aParam.mDetail); + e->SetTrusted(trusted); + e->SetComposed(aParam.mComposed); + return e.forget(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event, mView) + +NS_IMPL_ADDREF_INHERITED(UIEvent, Event) +NS_IMPL_RELEASE_INHERITED(UIEvent, Event) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UIEvent) +NS_INTERFACE_MAP_END_INHERITING(Event) + +static nsIntPoint DevPixelsToCSSPixels(const LayoutDeviceIntPoint& aPoint, + nsPresContext* aContext) { + return nsIntPoint(aContext->DevPixelsToIntCSSPixels(aPoint.x), + aContext->DevPixelsToIntCSSPixels(aPoint.y)); +} + +nsIntPoint UIEvent::GetMovementPoint() { + if (mEvent->mFlags.mIsPositionless) { + return nsIntPoint(0, 0); + } + + if (mPrivateDataDuplicated || mEventIsInternal) { + return mMovementPoint; + } + + if (!mEvent || !mEvent->AsGUIEvent()->mWidget || + (mEvent->mMessage != eMouseMove && mEvent->mMessage != ePointerMove)) { + // Pointer Lock spec defines that movementX/Y must be zero for all mouse + // events except mousemove. + return nsIntPoint(0, 0); + } + + // Calculate the delta between the last screen point and the current one. + nsIntPoint current = DevPixelsToCSSPixels(mEvent->mRefPoint, mPresContext); + nsIntPoint last = DevPixelsToCSSPixels(mEvent->mLastRefPoint, mPresContext); + return current - last; +} + +void UIEvent::InitUIEvent(const nsAString& typeArg, bool canBubbleArg, + bool cancelableArg, nsGlobalWindowInner* viewArg, + int32_t detailArg) { + if (NS_WARN_IF(mEvent->mFlags.mIsBeingDispatched)) { + return; + } + + Event::InitEvent(typeArg, canBubbleArg, cancelableArg); + + mDetail = detailArg; + mView = viewArg ? viewArg->GetOuterWindow() : nullptr; +} + +already_AddRefed<nsIContent> UIEvent::GetRangeParentContentAndOffset( + int32_t* aOffset) const { + if (NS_WARN_IF(!mPresContext)) { + return nullptr; + } + RefPtr<PresShell> presShell = mPresContext->GetPresShell(); + if (NS_WARN_IF(!presShell)) { + return nullptr; + } + nsCOMPtr<nsIContent> container; + nsLayoutUtils::GetContainerAndOffsetAtEvent( + presShell, mEvent, getter_AddRefs(container), aOffset); + return container.forget(); +} + +int32_t UIEvent::RangeOffset() const { + if (NS_WARN_IF(!mPresContext)) { + return 0; + } + RefPtr<PresShell> presShell = mPresContext->GetPresShell(); + if (NS_WARN_IF(!presShell)) { + return 0; + } + int32_t offset = 0; + nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent, nullptr, + &offset); + return offset; +} + +nsIntPoint UIEvent::GetLayerPoint() const { + if (mEvent->mFlags.mIsPositionless) { + return nsIntPoint(0, 0); + } + + if (!mEvent || + (mEvent->mClass != eMouseEventClass && + mEvent->mClass != eMouseScrollEventClass && + mEvent->mClass != eWheelEventClass && + mEvent->mClass != ePointerEventClass && + mEvent->mClass != eTouchEventClass && + mEvent->mClass != eDragEventClass && + mEvent->mClass != eSimpleGestureEventClass) || + !mPresContext || mEventIsInternal) { + return mLayerPoint; + } + // XXX I'm not really sure this is correct; it's my best shot, though + nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget(); + if (!targetFrame) return mLayerPoint; + nsIFrame* layer = nsLayoutUtils::GetClosestLayer(targetFrame); + nsPoint pt( + nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent, RelativeTo{layer})); + return nsIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x), + nsPresContext::AppUnitsToIntCSSPixels(pt.y)); +} + +void UIEvent::DuplicatePrivateData() { + mDefaultClientPoint = Event::GetClientCoords( + mPresContext, mEvent, mEvent->mRefPoint, mDefaultClientPoint); + mMovementPoint = GetMovementPoint(); + mLayerPoint = GetLayerPoint(); + mPagePoint = Event::GetPageCoords(mPresContext, mEvent, mEvent->mRefPoint, + mDefaultClientPoint); + // GetScreenPoint converts mEvent->mRefPoint to right coordinates. + CSSIntPoint screenPoint = + Event::GetScreenCoords(mPresContext, mEvent, mEvent->mRefPoint) + .valueOr(CSSIntPoint{0, 0}); + + Event::DuplicatePrivateData(); + + CSSToLayoutDeviceScale scale = mPresContext + ? mPresContext->CSSToDevPixelScale() + : CSSToLayoutDeviceScale(1); + mEvent->mRefPoint = RoundedToInt(screenPoint * scale); +} + +void UIEvent::Serialize(IPC::MessageWriter* aWriter, + bool aSerializeInterfaceType) { + if (aSerializeInterfaceType) { + IPC::WriteParam(aWriter, u"uievent"_ns); + } + + Event::Serialize(aWriter, false); + + IPC::WriteParam(aWriter, Detail()); +} + +bool UIEvent::Deserialize(IPC::MessageReader* aReader) { + NS_ENSURE_TRUE(Event::Deserialize(aReader), false); + NS_ENSURE_TRUE(IPC::ReadParam(aReader, &mDetail), false); + return true; +} + +// XXX Following struct and array are used only in +// UIEvent::ComputeModifierState(), but if we define them in it, +// we fail to build on Mac at calling mozilla::ArrayLength(). +struct ModifierPair { + Modifier modifier; + const char* name; +}; +static const ModifierPair kPairs[] = { + // clang-format off + { MODIFIER_ALT, NS_DOM_KEYNAME_ALT }, + { MODIFIER_ALTGRAPH, NS_DOM_KEYNAME_ALTGRAPH }, + { MODIFIER_CAPSLOCK, NS_DOM_KEYNAME_CAPSLOCK }, + { MODIFIER_CONTROL, NS_DOM_KEYNAME_CONTROL }, + { MODIFIER_FN, NS_DOM_KEYNAME_FN }, + { MODIFIER_FNLOCK, NS_DOM_KEYNAME_FNLOCK }, + { MODIFIER_META, NS_DOM_KEYNAME_META }, + { MODIFIER_NUMLOCK, NS_DOM_KEYNAME_NUMLOCK }, + { MODIFIER_SCROLLLOCK, NS_DOM_KEYNAME_SCROLLLOCK }, + { MODIFIER_SHIFT, NS_DOM_KEYNAME_SHIFT }, + { MODIFIER_SYMBOL, NS_DOM_KEYNAME_SYMBOL }, + { MODIFIER_SYMBOLLOCK, NS_DOM_KEYNAME_SYMBOLLOCK }, + // clang-format on +}; + +// static +Modifiers UIEvent::ComputeModifierState(const nsAString& aModifiersList) { + if (aModifiersList.IsEmpty()) { + return 0; + } + + // Be careful about the performance. If aModifiersList is too long, + // parsing it needs too long time. + // XXX Should we abort if aModifiersList is too long? + + Modifiers modifiers = 0; + + nsAString::const_iterator listStart, listEnd; + aModifiersList.BeginReading(listStart); + aModifiersList.EndReading(listEnd); + + for (uint32_t i = 0; i < ArrayLength(kPairs); i++) { + nsAString::const_iterator start(listStart), end(listEnd); + if (!FindInReadable(NS_ConvertASCIItoUTF16(kPairs[i].name), start, end)) { + continue; + } + + if ((start != listStart && !NS_IsAsciiWhitespace(*(--start))) || + (end != listEnd && !NS_IsAsciiWhitespace(*(end)))) { + continue; + } + modifiers |= kPairs[i].modifier; + } + + return modifiers; +} + +bool UIEvent::GetModifierStateInternal(const nsAString& aKey) { + WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); + MOZ_ASSERT(inputEvent, "mEvent must be WidgetInputEvent or derived class"); + return ((inputEvent->mModifiers & WidgetInputEvent::GetModifier(aKey)) != 0); +} + +static Modifiers ConvertToModifiers(const EventModifierInit& aParam) { + Modifiers bits = MODIFIER_NONE; + +#define SET_MODIFIER(aName, aValue) bits |= aParam.m##aName ? (aValue) : 0; + + SET_MODIFIER(CtrlKey, MODIFIER_CONTROL) + SET_MODIFIER(ShiftKey, MODIFIER_SHIFT) + SET_MODIFIER(AltKey, MODIFIER_ALT) + SET_MODIFIER(MetaKey, MODIFIER_META) + SET_MODIFIER(ModifierAltGraph, MODIFIER_ALTGRAPH) + SET_MODIFIER(ModifierCapsLock, MODIFIER_CAPSLOCK) + SET_MODIFIER(ModifierFn, MODIFIER_FN) + SET_MODIFIER(ModifierFnLock, MODIFIER_FNLOCK) + SET_MODIFIER(ModifierNumLock, MODIFIER_NUMLOCK) + SET_MODIFIER(ModifierScrollLock, MODIFIER_SCROLLLOCK) + SET_MODIFIER(ModifierSymbol, MODIFIER_SYMBOL) + SET_MODIFIER(ModifierSymbolLock, MODIFIER_SYMBOLLOCK) + +#undef SET_MODIFIER + + return bits; +} + +void UIEvent::InitModifiers(const EventModifierInit& aParam) { + if (NS_WARN_IF(!mEvent)) { + return; + } + WidgetInputEvent* inputEvent = mEvent->AsInputEvent(); + MOZ_ASSERT(inputEvent, + "This method shouldn't be called if it doesn't have modifiers"); + if (NS_WARN_IF(!inputEvent)) { + return; + } + + inputEvent->mModifiers = ConvertToModifiers(aParam); +} + +} // namespace mozilla::dom + +using namespace mozilla; +using namespace mozilla::dom; + +already_AddRefed<UIEvent> NS_NewDOMUIEvent(EventTarget* aOwner, + nsPresContext* aPresContext, + WidgetGUIEvent* aEvent) { + RefPtr<UIEvent> it = new UIEvent(aOwner, aPresContext, aEvent); + return it.forget(); +} |