зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1569512 - Make `PresShell` ignore synthesized `mousemove` events coming from another process if the child process stores mouse location of synthesized mouse events for tests r=smaug
The reason of intermittent failure of `test_bug656379-2.html` is, synthesized `mousemove` event coming from the parent process causes `mouseout` and `mouseleave` events of the last synthesized `mousemove` in the test. The reason is, synthesized `mousemove` for tests makes `PresShell` in the content process record the cursor location, but won't make it `PresShell` in the parent process do it. Therefore, parent process may synthesize `mousemove` event for the system cursor position which does not match with the synthesized mouse location in the content process. Therefore, `:hover` state may be updated unexpectedly. This patch makes `WidgetEvent::mFlags` have a flag to indicate whether it came from another process. Then, makes `PresShell::HandleEvent()` ignore synthesized `mousemove` events coming from another process only when the recorded mouse location was set by a mouse event synthesized for tests. Differential Revision: https://phabricator.services.mozilla.com/D65282 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
7bd48e79b8
Коммит
c90d6c80b3
|
@ -28,9 +28,38 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=656379
|
|||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
function log(aEvent) {
|
||||
function getPath() {
|
||||
if (!aEvent.target) {
|
||||
return "(null)";
|
||||
}
|
||||
function getNodeName(aNode) {
|
||||
if (aNode.id) {
|
||||
return `${aNode.nodeName}#${aNode.id}`;
|
||||
}
|
||||
return aNode.nodeName;
|
||||
}
|
||||
let path = getNodeName(aEvent.target);
|
||||
for (let parent = aEvent.target.parentElement;
|
||||
parent && document.body != parent;
|
||||
parent = parent.parentElement) {
|
||||
path = `${getNodeName(parent)} > ${path}`;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
info(`${aEvent.type} on ${getPath()}`);
|
||||
}
|
||||
|
||||
window.addEventListener("mousemove", log, {capture: true});
|
||||
window.addEventListener("mouseenter", log, {capture: true});
|
||||
window.addEventListener("mouseleave", log, {capture: true});
|
||||
window.addEventListener("mouseover", log, {capture: true});
|
||||
window.addEventListener("mouseout", log, {capture: true});
|
||||
|
||||
/** Test for Bug 656379 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
function* tests() {
|
||||
info("Synthesizing mousemove on label1...");
|
||||
synthesizeMouseAtCenter($("label1"), { type: "mousemove" });
|
||||
yield undefined;
|
||||
is($("button1").matches(":hover"), true,
|
||||
|
@ -41,6 +70,7 @@ function* tests() {
|
|||
"Button 2 should not be hovered after mousemove over label1");
|
||||
is($("label2").matches(":hover"), false,
|
||||
"Label 2 should not be hovered after mousemove over label1");
|
||||
info("Synthesizing mousemove on button2...");
|
||||
synthesizeMouseAtCenter($("button2"), { type: "mousemove" });
|
||||
yield undefined;
|
||||
is($("button1").matches(":hover"), false,
|
||||
|
@ -51,6 +81,7 @@ function* tests() {
|
|||
"Button 2 should be hovered after mousemove over button2");
|
||||
is($("label2").matches(":hover"), false,
|
||||
"Label 2 should not be hovered after mousemove over label2");
|
||||
info("Synthesizing mousemove on label2...");
|
||||
synthesizeMouseAtCenter($("label2"), { type: "mousemove" });
|
||||
yield undefined;
|
||||
is($("button1").matches(":hover"), false,
|
||||
|
|
|
@ -842,7 +842,8 @@ PresShell::PresShell(Document* aDocument)
|
|||
mForceUseLegacyKeyCodeAndCharCodeValues(false),
|
||||
mInitializedWithKeyPressEventDispatchingBlacklist(false),
|
||||
mForceUseLegacyNonPrimaryDispatch(false),
|
||||
mInitializedWithClickEventDispatchingBlacklist(false) {
|
||||
mInitializedWithClickEventDispatchingBlacklist(false),
|
||||
mMouseLocationWasSetBySynthesizedMouseEventForTests(false) {
|
||||
MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
|
||||
MOZ_ASSERT(aDocument);
|
||||
|
||||
|
@ -5072,8 +5073,7 @@ nscolor PresShell::GetDefaultBackgroundColorToDraw() {
|
|||
// content JS.
|
||||
Document* doc = GetDocument();
|
||||
BrowsingContext* bc = doc->GetBrowsingContext();
|
||||
if (bc && bc->IsTop() && !bc->HasOpener() &&
|
||||
doc->GetDocumentURI() &&
|
||||
if (bc && bc->IsTop() && !bc->HasOpener() && doc->GetDocumentURI() &&
|
||||
NS_IsAboutBlank(doc->GetDocumentURI()) &&
|
||||
doc->PrefersColorScheme() == StylePrefersColorScheme::Dark) {
|
||||
// Use --in-content-page-background for prefers-color-scheme: dark.
|
||||
|
@ -6289,6 +6289,18 @@ void PresShell::DisableNonTestMouseEvents(bool aDisable) {
|
|||
sDisableNonTestMouseEvents = aDisable;
|
||||
}
|
||||
|
||||
bool PresShell::MouseLocationWasSetBySynthesizedMouseEventForTests() const {
|
||||
if (!mPresContext) {
|
||||
return false;
|
||||
}
|
||||
if (mPresContext->IsRoot()) {
|
||||
return mMouseLocationWasSetBySynthesizedMouseEventForTests;
|
||||
}
|
||||
PresShell* rootPresShell = GetRootPresShell();
|
||||
return rootPresShell &&
|
||||
rootPresShell->mMouseLocationWasSetBySynthesizedMouseEventForTests;
|
||||
}
|
||||
|
||||
void PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) {
|
||||
if (!mPresContext) return;
|
||||
|
||||
|
@ -6315,6 +6327,8 @@ void PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) {
|
|||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
|
||||
mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
|
||||
}
|
||||
mMouseLocationWasSetBySynthesizedMouseEventForTests =
|
||||
aEvent->mFlags.mIsSynthesizedForTests;
|
||||
#ifdef DEBUG_MOUSE_LOCATION
|
||||
if (aEvent->mMessage == eMouseEnterIntoWidget) {
|
||||
printf("[ps=%p]got mouse enter for %p\n", this, aEvent->mWidget);
|
||||
|
@ -6333,6 +6347,8 @@ void PresShell::RecordMouseLocation(WidgetGUIEvent* aEvent) {
|
|||
// the mouse exit when the mouse moves from one of our widgets into another.
|
||||
mMouseLocation = nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
||||
mMouseEventTargetGuid = InputAPZContext::GetTargetLayerGuid();
|
||||
mMouseLocationWasSetBySynthesizedMouseEventForTests =
|
||||
aEvent->mFlags.mIsSynthesizedForTests;
|
||||
#ifdef DEBUG_MOUSE_LOCATION
|
||||
printf("[ps=%p]got mouse exit for %p\n", this, aEvent->mWidget);
|
||||
printf("[ps=%p]clearing mouse location\n", this);
|
||||
|
@ -6439,6 +6455,17 @@ nsresult PresShell::HandleEvent(nsIFrame* aFrameForPresShell,
|
|||
bool aDontRetargetEvents,
|
||||
nsEventStatus* aEventStatus) {
|
||||
MOZ_ASSERT(aGUIEvent);
|
||||
// If it's synthesized in the parent process and our mouse location was set
|
||||
// by a mouse event which was synthesized for tests because the test does not
|
||||
// want to change `:hover` state with the synthesized mouse event for native
|
||||
// mouse cursor position.
|
||||
if (aGUIEvent->mMessage == eMouseMove &&
|
||||
aGUIEvent->CameFromAnotherProcess() && XRE_IsContentProcess() &&
|
||||
!aGUIEvent->mFlags.mIsSynthesizedForTests &&
|
||||
MouseLocationWasSetBySynthesizedMouseEventForTests() &&
|
||||
aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) {
|
||||
return NS_OK;
|
||||
}
|
||||
EventHandler eventHandler(*this);
|
||||
return eventHandler.HandleEvent(aFrameForPresShell, aGUIEvent,
|
||||
aDontRetargetEvents, aEventStatus);
|
||||
|
@ -10633,7 +10660,7 @@ nsresult PresShell::UpdateImageLockingState() {
|
|||
return rv;
|
||||
}
|
||||
|
||||
PresShell* PresShell::GetRootPresShell() {
|
||||
PresShell* PresShell::GetRootPresShell() const {
|
||||
if (mPresContext) {
|
||||
nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
|
||||
if (rootPresContext) {
|
||||
|
|
|
@ -1939,6 +1939,7 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
// Check if aEvent is a mouse event and record the mouse location for later
|
||||
// synth mouse moves.
|
||||
void RecordMouseLocation(WidgetGUIEvent* aEvent);
|
||||
inline bool MouseLocationWasSetBySynthesizedMouseEventForTests() const;
|
||||
class nsSynthMouseMoveEvent final : public nsARefreshObserver {
|
||||
public:
|
||||
nsSynthMouseMoveEvent(PresShell* aPresShell, bool aFromScroll)
|
||||
|
@ -2758,7 +2759,7 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
static StaticRefPtr<dom::Element> sLastKeyDownEventTargetElement;
|
||||
};
|
||||
|
||||
PresShell* GetRootPresShell();
|
||||
PresShell* GetRootPresShell() const;
|
||||
|
||||
nscolor GetDefaultBackgroundColorToDraw();
|
||||
|
||||
|
@ -3160,6 +3161,10 @@ class PresShell final : public nsStubDocumentObserver,
|
|||
// Whether mForceUseLegacyNonPrimaryDispatch is initialised.
|
||||
bool mInitializedWithClickEventDispatchingBlacklist : 1;
|
||||
|
||||
// Set to true if mMouseLocation is set by a mouse event which is synthesized
|
||||
// for tests.
|
||||
bool mMouseLocationWasSetBySynthesizedMouseEventForTests : 1;
|
||||
|
||||
struct CapturingContentInfo final {
|
||||
CapturingContentInfo()
|
||||
: mAllowed(false),
|
||||
|
|
|
@ -793,7 +793,7 @@ void nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet) {
|
|||
}
|
||||
}
|
||||
|
||||
nsPresContext* nsPresContext::GetParentPresContext() {
|
||||
nsPresContext* nsPresContext::GetParentPresContext() const {
|
||||
mozilla::PresShell* presShell = GetPresShell();
|
||||
if (presShell) {
|
||||
nsViewManager* viewManager = presShell->GetViewManager();
|
||||
|
@ -848,8 +848,8 @@ nsIWidget* nsPresContext::GetRootWidget() const {
|
|||
|
||||
// We may want to replace this with something faster, maybe caching the root
|
||||
// prescontext
|
||||
nsRootPresContext* nsPresContext::GetRootPresContext() {
|
||||
nsPresContext* pc = this;
|
||||
nsRootPresContext* nsPresContext::GetRootPresContext() const {
|
||||
nsPresContext* pc = const_cast<nsPresContext*>(this);
|
||||
for (;;) {
|
||||
nsPresContext* parent = pc->GetParentPresContext();
|
||||
if (!parent) break;
|
||||
|
|
|
@ -181,7 +181,7 @@ class nsPresContext : public nsISupports,
|
|||
* Returns the parent prescontext for this one. Returns null if this is a
|
||||
* root.
|
||||
*/
|
||||
nsPresContext* GetParentPresContext();
|
||||
nsPresContext* GetParentPresContext() const;
|
||||
|
||||
/**
|
||||
* Returns the prescontext of the toplevel content document that contains
|
||||
|
@ -218,7 +218,7 @@ class nsPresContext : public nsISupports,
|
|||
* hierarchy that contains this presentation context, or nullptr if it can't
|
||||
* be found (e.g. it's detached).
|
||||
*/
|
||||
nsRootPresContext* GetRootPresContext();
|
||||
nsRootPresContext* GetRootPresContext() const;
|
||||
|
||||
virtual bool IsRoot() { return false; }
|
||||
|
||||
|
|
|
@ -176,6 +176,8 @@ struct BaseEventFlags {
|
|||
// remote process (but it's not handled yet if it's not a duplicated event
|
||||
// instance).
|
||||
bool mPostedToRemoteProcess : 1;
|
||||
// If mCameFromAnotherProcess is true, the event came from another process.
|
||||
bool mCameFromAnotherProcess : 1;
|
||||
|
||||
// At lease one of the event in the event path had non privileged click
|
||||
// listener.
|
||||
|
@ -337,6 +339,16 @@ struct BaseEventFlags {
|
|||
inline bool HasBeenPostedToRemoteProcess() const {
|
||||
return mPostedToRemoteProcess;
|
||||
}
|
||||
/**
|
||||
* Return true if the event came from another process.
|
||||
*/
|
||||
inline bool CameFromAnotherProcess() const { return mCameFromAnotherProcess; }
|
||||
/**
|
||||
* Mark the event as coming from another process.
|
||||
*/
|
||||
inline void MarkAsComingFromAnotherProcess() {
|
||||
mCameFromAnotherProcess = true;
|
||||
}
|
||||
/**
|
||||
* Mark the event is reserved by chrome. I.e., shouldn't be dispatched to
|
||||
* content because it shouldn't be cancelable.
|
||||
|
@ -700,6 +712,18 @@ class WidgetEvent : public WidgetEventTime {
|
|||
inline bool HasBeenPostedToRemoteProcess() const {
|
||||
return mFlags.HasBeenPostedToRemoteProcess();
|
||||
}
|
||||
/**
|
||||
* Return true if the event came from another process.
|
||||
*/
|
||||
inline bool CameFromAnotherProcess() const {
|
||||
return mFlags.CameFromAnotherProcess();
|
||||
}
|
||||
/**
|
||||
* Mark the event as coming from another process.
|
||||
*/
|
||||
inline void MarkAsComingFromAnotherProcess() {
|
||||
mFlags.MarkAsComingFromAnotherProcess();
|
||||
}
|
||||
/**
|
||||
* Mark the event is reserved by chrome. I.e., shouldn't be dispatched to
|
||||
* content because it shouldn't be cancelable.
|
||||
|
|
|
@ -74,6 +74,8 @@ struct ParamTraits<mozilla::WidgetEvent> {
|
|||
// Reset cross process dispatching state here because the event has not
|
||||
// been dispatched to different process from current process.
|
||||
aResult->ResetCrossProcessDispatchingState();
|
||||
// Mark the event comes from another process.
|
||||
aResult->MarkAsComingFromAnotherProcess();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче