summaryrefslogtreecommitdiffstats
path: root/dom/events/Event.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/events/Event.cpp')
-rw-r--r--dom/events/Event.cpp889
1 files changed, 889 insertions, 0 deletions
diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp
new file mode 100644
index 0000000000..c7763a17aa
--- /dev/null
+++ b/dom/events/Event.cpp
@@ -0,0 +1,889 @@
+/* -*- 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 "AccessCheck.h"
+#include "base/basictypes.h"
+#include "ipc/IPCMessageUtils.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ContentEvents.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/InternalMutationEvent.h"
+#include "mozilla/dom/Performance.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/PointerLockManager.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/ViewportUtils.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/DocumentInlines.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/SVGUtils.h"
+#include "mozilla/SVGOuterSVGFrame.h"
+#include "nsContentUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDeviceContext.h"
+#include "nsError.h"
+#include "nsGlobalWindow.h"
+#include "nsIFrame.h"
+#include "nsIContent.h"
+#include "nsIContentInlines.h"
+#include "nsIScrollableFrame.h"
+#include "nsJSEnvironment.h"
+#include "nsLayoutUtils.h"
+#include "nsPIWindowRoot.h"
+#include "nsRFPService.h"
+
+namespace mozilla::dom {
+
+Event::Event(EventTarget* aOwner, nsPresContext* aPresContext,
+ WidgetEvent* aEvent) {
+ ConstructorInit(aOwner, aPresContext, aEvent);
+}
+
+Event::Event(nsPIDOMWindowInner* aParent) {
+ ConstructorInit(nsGlobalWindowInner::Cast(aParent), nullptr, nullptr);
+}
+
+void Event::ConstructorInit(EventTarget* aOwner, nsPresContext* aPresContext,
+ WidgetEvent* aEvent) {
+ SetOwner(aOwner);
+ mIsMainThreadEvent = NS_IsMainThread();
+
+ mPrivateDataDuplicated = false;
+ mWantsPopupControlCheck = false;
+
+ if (aEvent) {
+ mEvent = aEvent;
+ mEventIsInternal = false;
+ } else {
+ mEventIsInternal = true;
+ /*
+ A derived class might want to allocate its own type of aEvent
+ (derived from WidgetEvent). To do this, it should take care to pass
+ a non-nullptr aEvent to this ctor, e.g.:
+
+ FooEvent::FooEvent(..., WidgetEvent* aEvent)
+ : Event(..., aEvent ? aEvent : new WidgetEvent())
+
+ Then, to override the mEventIsInternal assignments done by the
+ base ctor, it should do this in its own ctor:
+
+ FooEvent::FooEvent(..., WidgetEvent* aEvent)
+ ...
+ {
+ ...
+ if (aEvent) {
+ mEventIsInternal = false;
+ }
+ else {
+ mEventIsInternal = true;
+ }
+ ...
+ }
+ */
+ mEvent = new WidgetEvent(false, eVoidEvent);
+ }
+
+ InitPresContextData(aPresContext);
+}
+
+void Event::InitPresContextData(nsPresContext* aPresContext) {
+ mPresContext = aPresContext;
+ // Get the explicit original target (if it's anonymous make it null)
+ {
+ nsCOMPtr<nsIContent> content = GetTargetFromFrame();
+ mExplicitOriginalTarget = content;
+ if (content && content->IsInNativeAnonymousSubtree()) {
+ mExplicitOriginalTarget = nullptr;
+ }
+ }
+}
+
+Event::~Event() {
+ NS_ASSERT_OWNINGTHREAD(Event);
+
+ if (mEventIsInternal && mEvent) {
+ delete mEvent;
+ }
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Event)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(Event)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Event)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Event)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Event)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Event)
+ if (tmp->mEventIsInternal) {
+ tmp->mEvent->mTarget = nullptr;
+ tmp->mEvent->mCurrentTarget = nullptr;
+ tmp->mEvent->mOriginalTarget = nullptr;
+ tmp->mEvent->mRelatedTarget = nullptr;
+ tmp->mEvent->mOriginalRelatedTarget = nullptr;
+ switch (tmp->mEvent->mClass) {
+ case eDragEventClass: {
+ WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
+ dragEvent->mDataTransfer = nullptr;
+ break;
+ }
+ case eClipboardEventClass:
+ tmp->mEvent->AsClipboardEvent()->mClipboardData = nullptr;
+ break;
+ case eEditorInputEventClass: {
+ InternalEditorInputEvent* inputEvent =
+ tmp->mEvent->AsEditorInputEvent();
+ inputEvent->mDataTransfer = nullptr;
+ inputEvent->mTargetRanges.Clear();
+ break;
+ }
+ case eMutationEventClass:
+ tmp->mEvent->AsMutationEvent()->mRelatedNode = nullptr;
+ break;
+ default:
+ break;
+ }
+
+ if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) {
+ mouseEvent->mClickTarget = nullptr;
+ }
+ }
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mPresContext);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExplicitOriginalTarget);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
+ if (tmp->mEventIsInternal) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mCurrentTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mRelatedTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvent->mOriginalRelatedTarget);
+ switch (tmp->mEvent->mClass) {
+ case eDragEventClass: {
+ WidgetDragEvent* dragEvent = tmp->mEvent->AsDragEvent();
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer");
+ cb.NoteXPCOMChild(dragEvent->mDataTransfer);
+ break;
+ }
+ case eClipboardEventClass:
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClipboardData");
+ cb.NoteXPCOMChild(tmp->mEvent->AsClipboardEvent()->mClipboardData);
+ break;
+ case eEditorInputEventClass:
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mDataTransfer");
+ cb.NoteXPCOMChild(tmp->mEvent->AsEditorInputEvent()->mDataTransfer);
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
+ mEvent->AsEditorInputEvent()->mTargetRanges);
+ break;
+ case eMutationEventClass:
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mRelatedNode");
+ cb.NoteXPCOMChild(tmp->mEvent->AsMutationEvent()->mRelatedNode);
+ break;
+ default:
+ break;
+ }
+
+ if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClickTarget");
+ cb.NoteXPCOMChild(mouseEvent->mClickTarget);
+ }
+ }
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+JSObject* Event::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return WrapObjectInternal(aCx, aGivenProto);
+}
+
+JSObject* Event::WrapObjectInternal(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return Event_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void Event::GetType(nsAString& aType) const {
+ GetWidgetEventType(mEvent, aType);
+}
+
+EventTarget* Event::GetTarget() const { return mEvent->GetDOMEventTarget(); }
+
+already_AddRefed<Document> Event::GetDocument() const {
+ nsCOMPtr<EventTarget> eventTarget = GetTarget();
+
+ if (!eventTarget) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> win =
+ do_QueryInterface(eventTarget->GetOwnerGlobal());
+
+ if (!win) {
+ return nullptr;
+ }
+
+ nsCOMPtr<Document> doc;
+ doc = win->GetExtantDoc();
+
+ return doc.forget();
+}
+
+EventTarget* Event::GetCurrentTarget() const {
+ return mEvent->GetCurrentDOMEventTarget();
+}
+
+void Event::ComposedPath(nsTArray<RefPtr<EventTarget>>& aPath) {
+ EventDispatcher::GetComposedPathFor(mEvent, aPath);
+}
+
+//
+// Get the actual event target node (may have been retargeted for mouse events)
+//
+already_AddRefed<nsIContent> Event::GetTargetFromFrame() {
+ if (!mPresContext) {
+ return nullptr;
+ }
+
+ // Get the mTarget frame (have to get the ESM first)
+ nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
+ if (!targetFrame) {
+ return nullptr;
+ }
+
+ // get the real content
+ nsCOMPtr<nsIContent> realEventContent;
+ targetFrame->GetContentForEvent(mEvent, getter_AddRefs(realEventContent));
+ return realEventContent.forget();
+}
+
+EventTarget* Event::GetExplicitOriginalTarget() const {
+ if (mExplicitOriginalTarget) {
+ return mExplicitOriginalTarget;
+ }
+ return GetTarget();
+}
+
+EventTarget* Event::GetOriginalTarget() const {
+ return mEvent->GetOriginalDOMEventTarget();
+}
+
+EventTarget* Event::GetComposedTarget() const {
+ EventTarget* et = GetOriginalTarget();
+ nsCOMPtr<nsIContent> content = do_QueryInterface(et);
+ if (!content) {
+ return et;
+ }
+ nsIContent* nonChrome = content->FindFirstNonChromeOnlyAccessContent();
+ return nonChrome ? static_cast<EventTarget*>(nonChrome)
+ : static_cast<EventTarget*>(content->GetComposedDoc());
+}
+
+void Event::SetTrusted(bool aTrusted) { mEvent->mFlags.mIsTrusted = aTrusted; }
+
+bool Event::ShouldIgnoreChromeEventTargetListener() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!XRE_IsParentProcess()) {
+ return false;
+ }
+ if (EventTarget* currentTarget = GetCurrentTarget();
+ NS_WARN_IF(!currentTarget) || !currentTarget->IsRootWindow()) {
+ return false;
+ }
+ EventTarget* et = GetOriginalTarget();
+ if (NS_WARN_IF(!et)) {
+ return false;
+ }
+ nsIGlobalObject* global = et->GetOwnerGlobal();
+ if (NS_WARN_IF(!global)) {
+ return false;
+ }
+ nsPIDOMWindowInner* win = global->AsInnerWindow();
+ if (NS_WARN_IF(!win)) {
+ return false;
+ }
+ BrowsingContext* bc = win->GetBrowsingContext();
+ if (NS_WARN_IF(!bc)) {
+ return false;
+ }
+ // If this is a content event on an nsWindowRoot, then we also handle this in
+ // InProcessBrowserChildMessageManager, so we can ignore this event.
+ return bc->IsContent();
+}
+
+bool Event::Init(mozilla::dom::EventTarget* aGlobal) {
+ if (!mIsMainThreadEvent) {
+ return IsCurrentThreadRunningChromeWorker();
+ }
+ bool trusted = false;
+ nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(aGlobal);
+ if (w) {
+ nsCOMPtr<Document> d = w->GetExtantDoc();
+ if (d) {
+ trusted = nsContentUtils::IsChromeDoc(d);
+ nsPresContext* presContext = d->GetPresContext();
+ if (presContext) {
+ InitPresContextData(presContext);
+ }
+ }
+ }
+ return trusted;
+}
+
+// static
+already_AddRefed<Event> Event::Constructor(const GlobalObject& aGlobal,
+ const nsAString& aType,
+ const EventInit& aParam) {
+ nsCOMPtr<mozilla::dom::EventTarget> t =
+ do_QueryInterface(aGlobal.GetAsSupports());
+ return Constructor(t, aType, aParam);
+}
+
+// static
+already_AddRefed<Event> Event::Constructor(EventTarget* aEventTarget,
+ const nsAString& aType,
+ const EventInit& aParam) {
+ RefPtr<Event> e = new Event(aEventTarget, nullptr, nullptr);
+ bool trusted = e->Init(aEventTarget);
+ e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
+ e->SetTrusted(trusted);
+ e->SetComposed(aParam.mComposed);
+ return e.forget();
+}
+
+uint16_t Event::EventPhase() const {
+ // Note, remember to check that this works also
+ // if or when Bug 235441 is fixed.
+ if ((mEvent->mCurrentTarget && mEvent->mCurrentTarget == mEvent->mTarget) ||
+ mEvent->mFlags.InTargetPhase()) {
+ return Event_Binding::AT_TARGET;
+ }
+ if (mEvent->mFlags.mInCapturePhase) {
+ return Event_Binding::CAPTURING_PHASE;
+ }
+ if (mEvent->mFlags.mInBubblingPhase) {
+ return Event_Binding::BUBBLING_PHASE;
+ }
+ return Event_Binding::NONE;
+}
+
+void Event::StopPropagation() { mEvent->StopPropagation(); }
+
+void Event::StopImmediatePropagation() { mEvent->StopImmediatePropagation(); }
+
+void Event::StopCrossProcessForwarding() {
+ mEvent->StopCrossProcessForwarding();
+}
+
+void Event::PreventDefault() {
+ // This method is called only from C++ code which must handle default action
+ // of this event. So, pass true always.
+ PreventDefaultInternal(true);
+}
+
+void Event::PreventDefault(JSContext* aCx, CallerType aCallerType) {
+ // Note that at handling default action, another event may be dispatched.
+ // Then, JS in content mey be call preventDefault()
+ // even in the event is in system event group. Therefore, don't refer
+ // mInSystemGroup here.
+ nsIPrincipal* principal =
+ mIsMainThreadEvent ? nsContentUtils::SubjectPrincipal(aCx) : nullptr;
+
+ PreventDefaultInternal(aCallerType == CallerType::System, principal);
+}
+
+void Event::PreventDefaultInternal(bool aCalledByDefaultHandler,
+ nsIPrincipal* aPrincipal) {
+ if (!mEvent->mFlags.mCancelable) {
+ return;
+ }
+ if (mEvent->mFlags.mInPassiveListener) {
+ nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(mOwner));
+ if (win) {
+ if (Document* doc = win->GetExtantDoc()) {
+ if (!doc->HasWarnedAbout(
+ Document::ePreventDefaultFromPassiveListener)) {
+ AutoTArray<nsString, 1> params;
+ GetType(*params.AppendElement());
+ doc->WarnOnceAbout(Document::ePreventDefaultFromPassiveListener,
+ false, params);
+ }
+ }
+ }
+ return;
+ }
+
+ mEvent->PreventDefault(aCalledByDefaultHandler, aPrincipal);
+
+ if (!IsTrusted()) {
+ return;
+ }
+
+ WidgetDragEvent* dragEvent = mEvent->AsDragEvent();
+ if (!dragEvent) {
+ return;
+ }
+
+ nsIPrincipal* principal = nullptr;
+ nsCOMPtr<nsINode> node =
+ nsINode::FromEventTargetOrNull(mEvent->mCurrentTarget);
+ if (node) {
+ principal = node->NodePrincipal();
+ } else {
+ nsCOMPtr<nsIScriptObjectPrincipal> sop =
+ do_QueryInterface(mEvent->mCurrentTarget);
+ if (sop) {
+ principal = sop->GetPrincipal();
+ }
+ }
+ if (principal && !principal->IsSystemPrincipal()) {
+ dragEvent->mDefaultPreventedOnContent = true;
+ }
+}
+
+void Event::SetEventType(const nsAString& aEventTypeArg) {
+ mEvent->mSpecifiedEventTypeString.Truncate();
+ if (mIsMainThreadEvent) {
+ mEvent->mSpecifiedEventType = nsContentUtils::GetEventMessageAndAtom(
+ aEventTypeArg, mEvent->mClass, &(mEvent->mMessage));
+ mEvent->SetDefaultComposed();
+ } else {
+ mEvent->mSpecifiedEventType = NS_Atomize(u"on"_ns + aEventTypeArg);
+ mEvent->mMessage = eUnidentifiedEvent;
+ mEvent->SetComposed(aEventTypeArg);
+ }
+ mEvent->SetDefaultComposedInNativeAnonymousContent();
+}
+
+already_AddRefed<EventTarget> Event::EnsureWebAccessibleRelatedTarget(
+ EventTarget* aRelatedTarget) {
+ nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget;
+ if (relatedTarget) {
+ nsIContent* content = nsIContent::FromEventTarget(relatedTarget);
+ if (content && content->ChromeOnlyAccess() &&
+ !nsContentUtils::CanAccessNativeAnon()) {
+ content = content->FindFirstNonChromeOnlyAccessContent();
+ relatedTarget = content;
+ }
+
+ if (relatedTarget) {
+ relatedTarget = relatedTarget->GetTargetForDOMEvent();
+ }
+ }
+ return relatedTarget.forget();
+}
+
+void Event::InitEvent(const nsAString& aEventTypeArg,
+ mozilla::CanBubble aCanBubbleArg,
+ mozilla::Cancelable aCancelableArg,
+ mozilla::Composed aComposedArg) {
+ // Make sure this event isn't already being dispatched.
+ NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
+
+ if (IsTrusted()) {
+ // Ensure the caller is permitted to dispatch trusted DOM events.
+ if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
+ SetTrusted(false);
+ }
+ }
+
+ SetEventType(aEventTypeArg);
+
+ mEvent->mFlags.mBubbles = aCanBubbleArg == CanBubble::eYes;
+ mEvent->mFlags.mCancelable = aCancelableArg == Cancelable::eYes;
+ if (aComposedArg != Composed::eDefault) {
+ mEvent->mFlags.mComposed = aComposedArg == Composed::eYes;
+ }
+
+ mEvent->mFlags.mDefaultPrevented = false;
+ mEvent->mFlags.mDefaultPreventedByContent = false;
+ mEvent->mFlags.mDefaultPreventedByChrome = false;
+ mEvent->mFlags.mPropagationStopped = false;
+ mEvent->mFlags.mImmediatePropagationStopped = false;
+
+ // Clearing the old targets, so that the event is targeted correctly when
+ // re-dispatching it.
+ mEvent->mTarget = nullptr;
+ mEvent->mOriginalTarget = nullptr;
+}
+
+void Event::DuplicatePrivateData() {
+ NS_ASSERTION(mEvent, "No WidgetEvent for Event duplication!");
+ if (mEventIsInternal) {
+ return;
+ }
+
+ mEvent = mEvent->Duplicate();
+ mPresContext = nullptr;
+ mEventIsInternal = true;
+ mPrivateDataDuplicated = true;
+}
+
+void Event::SetTarget(EventTarget* aTarget) { mEvent->mTarget = aTarget; }
+
+bool Event::IsDispatchStopped() { return mEvent->PropagationStopped(); }
+
+WidgetEvent* Event::WidgetEventPtr() { return mEvent; }
+
+// static
+Maybe<CSSIntPoint> Event::GetScreenCoords(nsPresContext* aPresContext,
+ WidgetEvent* aEvent,
+ LayoutDeviceIntPoint aPoint) {
+ if (PointerLockManager::IsLocked()) {
+ return Some(EventStateManager::sLastScreenPoint);
+ }
+
+ if (!aEvent || (aEvent->mClass != eMouseEventClass &&
+ aEvent->mClass != eMouseScrollEventClass &&
+ aEvent->mClass != eWheelEventClass &&
+ aEvent->mClass != ePointerEventClass &&
+ aEvent->mClass != eTouchEventClass &&
+ aEvent->mClass != eDragEventClass &&
+ aEvent->mClass != eSimpleGestureEventClass)) {
+ return Nothing();
+ }
+
+ // Doing a straight conversion from LayoutDeviceIntPoint to CSSIntPoint
+ // seem incorrect, but it is needed to maintain legacy functionality.
+ WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent();
+ if (!aPresContext || !(guiEvent && guiEvent->mWidget)) {
+ return Some(CSSIntPoint(aPoint.x, aPoint.y));
+ }
+
+ // (Potentially) transform the point from the coordinate space of an
+ // out-of-process iframe to the coordinate space of the native
+ // window. The transform can only be applied to a point whose components
+ // are floating-point values, so convert the integer point first, then
+ // transform, and then round the result back to an integer point.
+ LayoutDevicePoint floatPoint(aPoint);
+ LayoutDevicePoint topLevelPoint =
+ guiEvent->mWidget->WidgetToTopLevelWidgetTransform().TransformPoint(
+ floatPoint);
+ LayoutDeviceIntPoint rounded = RoundedToInt(topLevelPoint);
+
+ nsPoint pt = LayoutDevicePixel::ToAppUnits(
+ rounded, aPresContext->DeviceContext()->AppUnitsPerDevPixel());
+
+ pt += LayoutDevicePixel::ToAppUnits(
+ guiEvent->mWidget->TopLevelWidgetToScreenOffset(),
+ aPresContext->DeviceContext()->AppUnitsPerDevPixel());
+
+ return Some(CSSPixel::FromAppUnitsRounded(pt));
+}
+
+// static
+CSSIntPoint Event::GetPageCoords(nsPresContext* aPresContext,
+ WidgetEvent* aEvent,
+ LayoutDeviceIntPoint aPoint,
+ CSSIntPoint aDefaultPoint) {
+ CSSIntPoint pagePoint =
+ Event::GetClientCoords(aPresContext, aEvent, aPoint, aDefaultPoint);
+
+ // If there is some scrolling, add scroll info to client point.
+ if (aPresContext && aPresContext->GetPresShell()) {
+ PresShell* presShell = aPresContext->PresShell();
+ nsIScrollableFrame* scrollframe =
+ presShell->GetRootScrollFrameAsScrollable();
+ if (scrollframe) {
+ pagePoint +=
+ CSSIntPoint::FromAppUnitsRounded(scrollframe->GetScrollPosition());
+ }
+ }
+
+ return pagePoint;
+}
+
+// static
+CSSIntPoint Event::GetClientCoords(nsPresContext* aPresContext,
+ WidgetEvent* aEvent,
+ LayoutDeviceIntPoint aPoint,
+ CSSIntPoint aDefaultPoint) {
+ if (PointerLockManager::IsLocked()) {
+ return EventStateManager::sLastClientPoint;
+ }
+
+ if (!aEvent ||
+ (aEvent->mClass != eMouseEventClass &&
+ aEvent->mClass != eMouseScrollEventClass &&
+ aEvent->mClass != eWheelEventClass &&
+ aEvent->mClass != eTouchEventClass &&
+ aEvent->mClass != eDragEventClass &&
+ aEvent->mClass != ePointerEventClass &&
+ aEvent->mClass != eSimpleGestureEventClass) ||
+ !aPresContext || !aEvent->AsGUIEvent()->mWidget) {
+ return aDefaultPoint;
+ }
+
+ PresShell* presShell = aPresContext->GetPresShell();
+ if (!presShell) {
+ return CSSIntPoint(0, 0);
+ }
+ nsIFrame* rootFrame = presShell->GetRootFrame();
+ if (!rootFrame) {
+ return CSSIntPoint(0, 0);
+ }
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
+ aEvent, aPoint, RelativeTo{rootFrame});
+
+ return CSSIntPoint::FromAppUnitsRounded(pt);
+}
+
+// static
+CSSIntPoint Event::GetOffsetCoords(nsPresContext* aPresContext,
+ WidgetEvent* aEvent,
+ LayoutDeviceIntPoint aPoint,
+ CSSIntPoint aDefaultPoint) {
+ if (!aEvent->mTarget) {
+ return GetPageCoords(aPresContext, aEvent, aPoint, aDefaultPoint);
+ }
+ nsCOMPtr<nsIContent> content = nsIContent::FromEventTarget(aEvent->mTarget);
+ if (!content || !aPresContext) {
+ return CSSIntPoint();
+ }
+ RefPtr<PresShell> presShell = aPresContext->GetPresShell();
+ if (!presShell) {
+ return CSSIntPoint();
+ }
+ presShell->FlushPendingNotifications(FlushType::Layout);
+ nsIFrame* frame = content->GetPrimaryFrame();
+ if (!frame) {
+ return CSSIntPoint();
+ }
+ // For compat, see https://github.com/w3c/csswg-drafts/issues/1508. In SVG we
+ // just return the coordinates of the outer SVG box. This is all kinda
+ // unfortunate.
+ if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) &&
+ StaticPrefs::dom_events_offset_in_svg_relative_to_svg_root()) {
+ frame = SVGUtils::GetOuterSVGFrame(frame);
+ if (!frame) {
+ return CSSIntPoint();
+ }
+ }
+ nsIFrame* rootFrame = presShell->GetRootFrame();
+ if (!rootFrame) {
+ return CSSIntPoint();
+ }
+ CSSIntPoint clientCoords =
+ GetClientCoords(aPresContext, aEvent, aPoint, aDefaultPoint);
+ nsPoint pt = CSSPixel::ToAppUnits(clientCoords);
+ if (nsLayoutUtils::TransformPoint(RelativeTo{rootFrame}, RelativeTo{frame},
+ pt) == nsLayoutUtils::TRANSFORM_SUCCEEDED) {
+ pt -= frame->GetPaddingRectRelativeToSelf().TopLeft();
+ return CSSPixel::FromAppUnitsRounded(pt);
+ }
+ return CSSIntPoint();
+}
+
+// To be called ONLY by Event::GetType (which has the additional
+// logic for handling user-defined events).
+// static
+const char16_t* Event::GetEventName(EventMessage aEventType) {
+ switch (aEventType) {
+#define MESSAGE_TO_EVENT(name_, _message, _type, _struct) \
+ case _message: \
+ return u"" #name_;
+#include "mozilla/EventNameList.h"
+#undef MESSAGE_TO_EVENT
+ default:
+ break;
+ }
+ // XXXldb We can hit this case for WidgetEvent objects that we didn't
+ // create and that are not user defined events since this function and
+ // SetEventType are incomplete. (But fixing that requires fixing the
+ // arrays in nsEventListenerManager too, since the events for which
+ // this is a problem generally *are* created by Event.)
+ return nullptr;
+}
+
+bool Event::DefaultPrevented(CallerType aCallerType) const {
+ NS_ENSURE_TRUE(mEvent, false);
+
+ // If preventDefault() has never been called, just return false.
+ if (!mEvent->DefaultPrevented()) {
+ return false;
+ }
+
+ // If preventDefault() has been called by content, return true. Otherwise,
+ // i.e., preventDefault() has been called by chrome, return true only when
+ // this is called by chrome.
+ return mEvent->DefaultPreventedByContent() ||
+ aCallerType == CallerType::System;
+}
+
+bool Event::ReturnValue(CallerType aCallerType) const {
+ return !DefaultPrevented(aCallerType);
+}
+
+void Event::SetReturnValue(bool aReturnValue, CallerType aCallerType) {
+ if (!aReturnValue) {
+ PreventDefaultInternal(aCallerType == CallerType::System);
+ }
+}
+
+double Event::TimeStamp() {
+ if (mEvent->mTimeStamp.IsNull()) {
+ return 0.0;
+ }
+
+ if (mIsMainThreadEvent) {
+ if (NS_WARN_IF(!mOwner)) {
+ return 0.0;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(mOwner);
+ if (NS_WARN_IF(!win)) {
+ return 0.0;
+ }
+
+ Performance* perf = win->GetPerformance();
+ if (NS_WARN_IF(!perf)) {
+ return 0.0;
+ }
+
+ double ret =
+ perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->mTimeStamp);
+ MOZ_ASSERT(mOwner->PrincipalOrNull());
+
+ return nsRFPService::ReduceTimePrecisionAsMSecs(
+ ret, perf->GetRandomTimelineSeed(), perf->GetRTPCallerType());
+ }
+
+ WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+
+ double ret = workerPrivate->TimeStampToDOMHighRes(mEvent->mTimeStamp);
+
+ return nsRFPService::ReduceTimePrecisionAsMSecs(
+ ret, workerPrivate->GetRandomTimelineSeed(),
+ workerPrivate->GlobalScope()->GetRTPCallerType());
+}
+
+void Event::Serialize(IPC::MessageWriter* aWriter,
+ bool aSerializeInterfaceType) {
+ if (aSerializeInterfaceType) {
+ IPC::WriteParam(aWriter, u"event"_ns);
+ }
+
+ nsString type;
+ GetType(type);
+ IPC::WriteParam(aWriter, type);
+
+ IPC::WriteParam(aWriter, Bubbles());
+ IPC::WriteParam(aWriter, Cancelable());
+ IPC::WriteParam(aWriter, IsTrusted());
+ IPC::WriteParam(aWriter, Composed());
+
+ // No timestamp serialization for now!
+}
+
+bool Event::Deserialize(IPC::MessageReader* aReader) {
+ nsString type;
+ NS_ENSURE_TRUE(IPC::ReadParam(aReader, &type), false);
+
+ bool bubbles = false;
+ NS_ENSURE_TRUE(IPC::ReadParam(aReader, &bubbles), false);
+
+ bool cancelable = false;
+ NS_ENSURE_TRUE(IPC::ReadParam(aReader, &cancelable), false);
+
+ bool trusted = false;
+ NS_ENSURE_TRUE(IPC::ReadParam(aReader, &trusted), false);
+
+ bool composed = false;
+ NS_ENSURE_TRUE(IPC::ReadParam(aReader, &composed), false);
+
+ InitEvent(type, bubbles, cancelable);
+ SetTrusted(trusted);
+ SetComposed(composed);
+
+ return true;
+}
+
+void Event::SetOwner(EventTarget* aOwner) {
+ mOwner = nullptr;
+
+ if (!aOwner) {
+ return;
+ }
+
+ nsCOMPtr<nsINode> n = do_QueryInterface(aOwner);
+ if (n) {
+ mOwner = n->OwnerDoc()->GetScopeObject();
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(aOwner);
+ if (w) {
+ mOwner = do_QueryInterface(w);
+ return;
+ }
+
+ nsCOMPtr<DOMEventTargetHelper> eth = do_QueryInterface(aOwner);
+ if (eth) {
+ mOwner = eth->GetParentObject();
+ return;
+ }
+
+#ifdef DEBUG
+ nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(aOwner);
+ MOZ_ASSERT(root, "Unexpected EventTarget!");
+#endif
+}
+
+void Event::GetWidgetEventType(WidgetEvent* aEvent, nsAString& aType) {
+ if (!aEvent->mSpecifiedEventTypeString.IsEmpty()) {
+ aType = aEvent->mSpecifiedEventTypeString;
+ return;
+ }
+
+ const char16_t* name = GetEventName(aEvent->mMessage);
+
+ if (name) {
+ aType.AssignLiteral(name, nsString::char_traits::length(name));
+ return;
+ } else if (aEvent->mMessage == eUnidentifiedEvent &&
+ aEvent->mSpecifiedEventType) {
+ // Remove "on"
+ aType = Substring(nsDependentAtomString(aEvent->mSpecifiedEventType), 2);
+ aEvent->mSpecifiedEventTypeString = aType;
+ return;
+ }
+
+ aType.Truncate();
+}
+
+bool Event::IsDragExitEnabled(JSContext* aCx, JSObject* aGlobal) {
+ return StaticPrefs::dom_event_dragexit_enabled() ||
+ nsContentUtils::IsSystemCaller(aCx);
+}
+
+} // namespace mozilla::dom
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+already_AddRefed<Event> NS_NewDOMEvent(EventTarget* aOwner,
+ nsPresContext* aPresContext,
+ WidgetEvent* aEvent) {
+ RefPtr<Event> it = new Event(aOwner, aPresContext, aEvent);
+ return it.forget();
+}