Bug 218415 - Add window.event. r=smaug.

window.event is set on the wrong window when the target and the
callback are from different realms and the callback is an XPCOM
callback.

MozReview-Commit-ID: HXeUIicdMuT

--HG--
extra : rebase_source : 5852093c015844cf3cc49dcd7fe71c9ea881eef9
This commit is contained in:
Henri Sivonen 2018-04-04 15:57:17 +03:00
parent 53fddbfeb5
commit 82805f2778
15 changed files with 122 additions and 49 deletions

View File

@ -878,6 +878,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();

View File

@ -918,8 +918,9 @@ nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter *aOuterWindow)
mCanSkipCCGeneration(0),
mBeforeUnloadListenerCount(0)
{
AssertIsOnMainThread();
mIsInnerWindow = true;
AssertIsOnMainThread();
nsLayoutStatics::AddRef();
// Initialize the PRCList (this).
@ -3287,6 +3288,16 @@ nsGlobalWindowInner::SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
SetOpenerWindow(outer, false);
}
void
nsGlobalWindowInner::GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval)
{
if (mEvent) {
Unused << nsContentUtils::WrapNative(aCx, mEvent, aRetval);
} else {
aRetval.setUndefined();
}
}
void
nsGlobalWindowInner::GetStatus(nsAString& aStatus, ErrorResult& aError)
{
@ -8207,7 +8218,8 @@ nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter *aOuterWindow)
mMarkedCCGeneration(0),
mHasTriedToCacheTopInnerWindow(false),
mNumOfIndexedDBDatabases(0),
mNumOfOpenWebSockets(0)
mNumOfOpenWebSockets(0),
mEvent(nullptr)
{
MOZ_ASSERT(aOuterWindow);
}

View File

@ -671,6 +671,7 @@ public:
mozilla::ErrorResult& aError);
void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
mozilla::ErrorResult& aError);
void GetEvent(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
nsPIDOMWindowOuter* GetScriptableParent() override;
nsPIDOMWindowOuter* GetScriptableParentOrNull() override;

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/ServiceWorkerRegistration.h"
#include "nsContentUtils.h"
#include "nsThreadUtils.h"
#include "nsGlobalWindowInner.h"
using mozilla::MallocSizeOf;
using mozilla::Maybe;
@ -221,6 +222,15 @@ nsIGlobalObject::GetOrCreateServiceWorkerRegistration(const ServiceWorkerRegistr
return nullptr;
}
nsPIDOMWindowInner*
nsIGlobalObject::AsInnerWindow()
{
if (MOZ_LIKELY(mIsInnerWindow)) {
return static_cast<nsPIDOMWindowInner*>(static_cast<nsGlobalWindowInner*>(this));
}
return nullptr;
}
size_t
nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const
{

View File

@ -26,6 +26,7 @@
class nsCycleCollectionTraversalCallback;
class nsIPrincipal;
class nsPIDOMWindowInner;
namespace mozilla {
class DOMEventTargetHelper;
@ -48,8 +49,12 @@ class nsIGlobalObject : public nsISupports,
bool mIsDying;
protected:
bool mIsInnerWindow;
nsIGlobalObject()
: mIsDying(false)
, mIsInnerWindow(false)
{}
public:
@ -130,6 +135,8 @@ public:
virtual RefPtr<mozilla::dom::ServiceWorkerRegistration>
GetOrCreateServiceWorkerRegistration(const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor);
// Returns a pointer to this object as an inner window if this is one or nullptr otherwise.
nsPIDOMWindowInner* AsInnerWindow();
protected:
virtual ~nsIGlobalObject();

View File

@ -251,6 +251,17 @@ public:
mMayHavePointerEnterLeaveEventListener = true;
}
// 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.
mozilla::dom::Event* SetEvent(mozilla::dom::Event* aEvent)
{
mozilla::dom::Event* old = mEvent;
mEvent = aEvent;
return old;
}
/**
* Check whether this window is a secure context.
*/
@ -705,6 +716,10 @@ protected:
// play audible media, or we've already been granted permission by the
// user, this is non-null, and encapsulates the request.
RefPtr<mozilla::AutoplayRequest> mAutoplayRequest;
// The event dispatch code sets and unsets this while keeping
// the event object alive.
mozilla::dom::Event* mEvent;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)

View File

@ -321,6 +321,16 @@ public:
return mFlags.mRootOfClosedTree;
}
void SetItemInShadowTree(bool aSet)
{
mFlags.mItemInShadowTree = aSet;
}
bool IsItemInShadowTree()
{
return mFlags.mItemInShadowTree;
}
void SetIsSlotInClosedTree(bool aSet)
{
mFlags.mIsSlotInClosedTree = aSet;
@ -408,7 +418,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!");
}
@ -443,6 +454,7 @@ private:
bool mWantsPreHandleEvent : 1;
bool mPreHandleEventOnly : 1;
bool mRootOfClosedTree : 1;
bool mItemInShadowTree : 1;
bool mIsSlotInClosedTree : 1;
bool mIsChromeHandler : 1;
private:
@ -485,6 +497,7 @@ EventTargetChainItem::GetEventTargetParent(EventChainPreVisitor& aVisitor)
SetWantsPreHandleEvent(aVisitor.mWantsPreHandleEvent);
SetPreHandleEventOnly(aVisitor.mWantsPreHandleEvent && !aVisitor.mCanHandle);
SetRootOfClosedTree(aVisitor.mRootOfClosedTree);
SetItemInShadowTree(aVisitor.mItemInShadowTree);
SetRetargetedRelatedTarget(aVisitor.mRetargetedRelatedTarget);
SetRetargetedTouchTarget(std::move(aVisitor.mRetargetedTouchTargets));
mItemFlags = aVisitor.mItemFlags;

View File

@ -128,6 +128,7 @@ public:
, mMayHaveListenerManager(true)
, mWantsPreHandleEvent(false)
, mRootOfClosedTree(false)
, mItemInShadowTree(false)
, mParentIsSlotInClosedTree(false)
, mParentIsChromeHandler(false)
, mRelatedTargetRetargetedInCurrentScope(false)
@ -149,6 +150,7 @@ public:
mMayHaveListenerManager = true;
mWantsPreHandleEvent = false;
mRootOfClosedTree = false;
mItemInShadowTree = false;
mParentIsSlotInClosedTree = false;
mParentIsChromeHandler = false;
// Note, we don't clear mRelatedTargetRetargetedInCurrentScope explicitly,
@ -237,6 +239,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.

View File

@ -1161,6 +1161,32 @@ 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();
nsIGlobalObject* global = nullptr;
if (callback) {
global = callback->IncumbentGlobalOrNull();
}
if (global) {
innerWindow = global->AsInnerWindow(); // 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
@ -1171,7 +1197,8 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
WidgetEvent* aEvent,
Event** aDOMEvent,
EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus)
nsEventStatus* aEventStatus,
bool aItemInShadowTree)
{
//Set the value of the internal PreventDefault flag properly based on aEventStatus
if (!aEvent->DefaultPrevented() &&
@ -1264,6 +1291,12 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
}
nsresult rv = NS_OK;
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
WindowFromListener(listener, aItemInShadowTree);
mozilla::dom::Event* oldWindowEvent = nullptr;
if (innerWindow) {
oldWindowEvent = innerWindow->SetEvent(*aDOMEvent);
}
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
// Add a profiler label and a profiler marker for the actual
@ -1297,6 +1330,9 @@ EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
{
rv = HandleEventSubType(listener, *aDOMEvent, aCurrentTarget);
}
if (innerWindow) {
Unused << innerWindow->SetEvent(oldWindowEvent);
}
if (NS_FAILED(rv)) {
aEvent->mFlags.mExceptionWasRaised = true;

View File

@ -369,7 +369,8 @@ public:
WidgetEvent* aEvent,
dom::Event** aDOMEvent,
dom::EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus)
nsEventStatus* aEventStatus,
bool aItemInShadowTree)
{
if (mListeners.IsEmpty() || aEvent->PropagationStopped()) {
return;
@ -390,7 +391,7 @@ public:
return;
}
HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget,
aEventStatus);
aEventStatus, aItemInShadowTree);
}
/**
@ -499,7 +500,8 @@ protected:
WidgetEvent* aEvent,
dom::Event** aDOMEvent,
dom::EventTarget* aCurrentTarget,
nsEventStatus* aEventStatus);
nsEventStatus* aEventStatus,
bool aItemInShadowTree);
nsresult HandleEventSubType(Listener* aListener,
dom::Event* aDOMEvent,
@ -590,6 +592,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

View File

@ -51,6 +51,7 @@ interface XULControllers;
[Throws] void stop();
[Throws, CrossOriginCallable] void focus();
[Throws, CrossOriginCallable] void blur();
[Replaceable] readonly attribute any event;
// other browsing contexts
[Replaceable, Throws, CrossOriginReadable] readonly attribute WindowProxy frames;

View File

@ -1,25 +1,8 @@
[event-global-extra.window.html]
[window.event for constructors from another global: function EventTarget() {\n [native code\]\n}]
expected: FAIL
[window.event for constructors from another global: function XMLHttpRequest() {\n [native code\]\n}]
expected: FAIL
[window.event and element from another document]
expected: FAIL
[window.event and moving an element post-dispatch]
expected: FAIL
prefs: [dom.webcomponents.shadowdom.enabled:true]
[window.event should not be affected by nodes moving post-dispatch]
expected: FAIL
[Listener from a different global]
expected: FAIL
[window.event for constructors from another global: EventTarget]
expected: FAIL
[window.event for constructors from another global: XMLHttpRequest]
expected: FAIL

View File

@ -1,19 +1,2 @@
[event-global.html]
[event exists on window, which is initially set to undefined]
expected: FAIL
[window.event is only defined during dispatch]
expected: FAIL
[window.event is undefined if the target is in a shadow tree (event dispatched outside shadow tree)]
expected: FAIL
[window.event is undefined if the target is in a shadow tree (event dispatched inside shadow tree)]
expected: FAIL
[window.event is set to the current event during dispatch]
expected: FAIL
[window.event is set to the current event, which is the event passed to dispatch]
expected: FAIL
prefs: [dom.webcomponents.shadowdom.enabled:true]

View File

@ -99,6 +99,3 @@
[Event interface: new CustomEvent("foo") must inherit property "returnValue" with the proper type]
expected: FAIL
[Window interface: attribute event]
expected: FAIL

View File

@ -1,7 +1,4 @@
[track-remove-track.html]
[Tests that the 'removetrack' event is fired when an out-of-band TextTrack is removed.]
expected: FAIL
[Tests that the 'removetrack' event is NOT fired for inband TextTrack on a failed load.]
expected: FAIL