Issue #595 - Implement window.event

This MSIE extension is still technically part of the standard*, although its use is discouraged. This API will also likely never go away based on some comments at this issue on MDN content**.

Note that this uses a different approach for getting the inner window.

* https://dom.spec.whatwg.org/#interface-window-extensions
** https://github.com/mdn/content/issues/21848

Spec PR: https://github.com/whatwg/dom/pull/407
Spec discussion: https://github.com/whatwg/dom/issues/334

Partially based on https://bugzilla.mozilla.org/show_bug.cgi?id=218415
This commit is contained in:
FranklinDM
2023-03-26 18:34:04 +08:00
committed by roytam1
parent 8e6d730463
commit 31283d993b
10 changed files with 110 additions and 6 deletions
+4
View File
@@ -824,6 +824,10 @@ nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
aVisitor.mCanHandle = true;
aVisitor.mMayHaveListenerManager = HasListenerManager();
if (IsInShadowTree()) {
aVisitor.mItemInShadowTree = true;
}
// Don't propagate mouseover and mouseout events when mouse is moving
// inside chrome access only content.
bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
+12 -1
View File
@@ -909,7 +909,8 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow)
mOuterWindow(aOuterWindow),
// Make sure no actual window ends up with mWindowID == 0
mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false),
mEvent(nullptr)
{}
template<class T>
@@ -5245,6 +5246,16 @@ nsGlobalWindow::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
SetOpenerWindow(outer, false);
}
void
nsGlobalWindow::GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval)
{
if (mEvent) {
Unused << nsContentUtils::WrapNative(aCx, mEvent, aRetval);
} else {
aRetval.setUndefined();
}
}
void
nsGlobalWindow::GetStatusOuter(nsAString& aStatus)
{
+1
View File
@@ -846,6 +846,7 @@ public:
already_AddRefed<nsPIDOMWindowOuter> GetOpener() override;
void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
mozilla::ErrorResult& aError);
void GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
already_AddRefed<nsPIDOMWindowOuter> GetParentOuter();
already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
already_AddRefed<nsPIDOMWindowOuter> GetParent() override;
+15
View File
@@ -194,6 +194,10 @@ protected:
// we have what it takes to do so.
void MaybeCreateDoc();
// The event dispatch code sets and unsets this while keeping
// the event object alive.
nsIDOMEvent* mEvent;
public:
inline bool IsLoadingOrRunningTimeout() const;
@@ -789,6 +793,17 @@ public:
return mInnerObjectsFreed;
}
// Sets the event for window.event. Does NOT take ownership, so
// the caller is responsible for clearing the event before the
// event gets deallocated. Pass nullptr to set window.event to
// undefined. Returns the previous value.
nsIDOMEvent* SetEvent(nsIDOMEvent* aEvent)
{
nsIDOMEvent* old = mEvent;
mEvent = aEvent;
return old;
}
/**
* Check whether this window is a secure context.
*/
+14 -1
View File
@@ -264,6 +264,16 @@ public:
return mFlags.mRootOfClosedTree;
}
void SetItemInShadowTree(bool aSet)
{
mFlags.mItemInShadowTree = aSet;
}
bool IsItemInShadowTree()
{
return mFlags.mItemInShadowTree;
}
void SetIsSlotInClosedTree(bool aSet)
{
mFlags.mIsSlotInClosedTree = aSet;
@@ -351,7 +361,8 @@ public:
mManager->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent,
&aVisitor.mDOMEvent,
CurrentTarget(),
&aVisitor.mEventStatus);
&aVisitor.mEventStatus,
IsItemInShadowTree());
NS_ASSERTION(aVisitor.mEvent->mCurrentTarget == nullptr,
"CurrentTarget should be null!");
}
@@ -384,6 +395,7 @@ private:
bool mWantsPreHandleEvent : 1;
bool mPreHandleEventOnly : 1;
bool mRootOfClosedTree : 1;
bool mItemInShadowTree : 1;
bool mIsSlotInClosedTree : 1;
bool mIsChromeHandler : 1;
private:
@@ -433,6 +445,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
SetItemInShadowTree(aVisitor.mItemInShadowTree);
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
mItemFlags = aVisitor.mItemFlags;
mItemData = aVisitor.mItemData;
+8
View File
@@ -125,6 +125,7 @@ public:
, mMayHaveListenerManager(true)
, mWantsPreHandleEvent(false)
, mRootOfClosedTree(false)
, mItemInShadowTree(false)
, mParentIsSlotInClosedTree(false)
, mParentIsChromeHandler(false)
, mRelatedTargetRetargetedInCurrentScope(false)
@@ -147,6 +148,7 @@ public:
mMayHaveListenerManager = true;
mWantsPreHandleEvent = false;
mRootOfClosedTree = false;
mItemInShadowTree = false;
mParentIsSlotInClosedTree = false;
mParentIsChromeHandler = false;
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
@@ -236,6 +238,12 @@ public:
*/
bool mRootOfClosedTree;
/**
* If target is node and its root is a shadow root.
* https://dom.spec.whatwg.org/#event-path-item-in-shadow-tree
*/
bool mItemInShadowTree;
/**
* True if mParentTarget is HTMLSlotElement in a closed shadow tree and the
* current target is assigned to that slot.
+43 -1
View File
@@ -1120,6 +1120,38 @@ EventListenerManager::GetLegacyEventMessage(EventMessage aEventMessage) const
}
}
already_AddRefed<nsPIDOMWindowInner>
EventListenerManager::WindowFromListener(Listener* aListener,
bool aItemInShadowTree)
{
nsCOMPtr<nsPIDOMWindowInner> innerWindow;
if (!aItemInShadowTree) {
if (aListener->mListener.HasWebIDLCallback()) {
CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
nsGlobalWindow* win;
if (callback) {
// Find the real underlying callback.
JSObject* realCallback =
js::UncheckedUnwrap(callback->CallbackPreserveColor());
// Get the global for this callback.
win = mIsMainThreadELM ?
xpc::WindowGlobalOrNull(realCallback) :
nullptr;
}
if (win && win->IsInnerWindow()) {
innerWindow = win->AsInner(); // Can be nullptr
}
} else {
// Can't get the global from
// listener->mListener.GetXPCOMCallback().
// In most cases, it would be the same as for
// the target, so let's do that.
innerWindow = GetInnerWindowForTarget(); // Can be nullptr
}
}
return innerWindow.forget();
}
/**
* Causes a check for event listeners and processing by them if they exist.
* @param an event listener
@@ -1130,7 +1162,8 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus)
nsEventStatus* aEventStatus,
bool aItemInShadowTree)
{
//Set the value of the internal PreventDefault flag properly based on aEventStatus
if (!aEvent->DefaultPrevented() &&
@@ -1222,9 +1255,18 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
listener = listenerHolder.ptr();
hasRemovedListener = true;
}
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
WindowFromListener(listener, aItemInShadowTree);
nsIDOMEvent* oldWindowEvent = nullptr;
if (innerWindow) {
oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
}
if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, aCurrentTarget))) {
aEvent->mFlags.mExceptionWasRaised = true;
}
if (innerWindow) {
Unused << innerWindow->SetEvent(oldWindowEvent);
}
aEvent->mFlags.mInPassiveListener = false;
if (needsEndEventMarker) {
+9 -3
View File
@@ -350,7 +350,8 @@ public:
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
dom::EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus)
nsEventStatus* aEventStatus,
bool aItemInShadowTree)
{
if (mListeners.IsEmpty() || aEvent->PropagationStopped()) {
return;
@@ -371,7 +372,7 @@ public:
return;
}
HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget,
aEventStatus);
aEventStatus, aItemInShadowTree);
}
/**
@@ -482,7 +483,8 @@ protected:
WidgetEvent* aEvent,
nsIDOMEvent** aDOMEvent,
dom::EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus);
nsEventStatus* aEventStatus,
bool aItemInShadowTree);
nsresult HandleEventSubType(Listener* aListener,
nsIDOMEvent* aDOMEvent,
@@ -573,6 +575,10 @@ public:
return typedHandler ? typedHandler->OnBeforeUnloadEventHandler() : nullptr;
}
private:
already_AddRefed<nsPIDOMWindowInner> WindowFromListener(Listener* aListener,
bool aItemInShadowTree);
protected:
/**
* Helper method for implementing the various Get*EventHandler above. Will
+1
View File
@@ -50,6 +50,7 @@ interface nsIDOMCrypto;
[Throws] void stop();
[Throws, CrossOriginCallable, UnsafeInPrerendering] void focus();
[Throws, CrossOriginCallable] void blur();
[Replaceable, Pref="dom.window.event.enabled"] readonly attribute any event;
// other browsing contexts
[Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;
+3
View File
@@ -5244,6 +5244,9 @@ pref("prompts.content_handling_dialog_modal.enabled", false);
// Whether module scripts (<script type="module">) are enabled for content.
pref("dom.moduleScripts.enabled", true);
// Whether read-only Window property event is enabled for content.
pref("dom.window.event.enabled", false);
// Report details when a media source error occurs?
// Enabled by default in debug builds, otherwise should be explicitly enabled
// by the user to prevent XO leaking of the response status (CVE-2020-15666)