Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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();
}