Open Bug 1943411 Opened 10 days ago Updated 2 days ago

On eclecticgames.co.uk, the "drop-down" menu disappears when moving the mouse outside of the top-level item

Categories

(Web Compatibility :: Site Reports, defect, P1)

Tracking

(Webcompat Priority:P1, firefox-esr115 unaffected, firefox-esr128 disabled, firefox134 disabled, firefox135+ affected, firefox136+ affected)

Webcompat Priority P1
Tracking Status
firefox-esr115 --- unaffected
firefox-esr128 --- disabled
firefox134 --- disabled
firefox135 + affected
firefox136 + affected

People

(Reporter: babolivier, Unassigned, NeedInfo)

References

(Regression, )

Details

(6 keywords)

User Story

outreach-assignee:mbalfanz
outreach-contact-date:2025-01-28
platform:windows,mac,linux
impact:workflow-broken
configuration:general
affects:all
branch:release
diagnosis-team:dom
user-impact-score:60

Attachments

(8 files)

On https://www.eclecticgames.co.uk/, users can place their cursor over the category names below the search bar to show a menu dropping down to offer specific categories to explore.

On Firefox (136.0a1 (2025-01-23) (64-bit)), moving the cursor away from the top-level category makes this menu disappear, even if the cursor is still on top of the menu itself.

On Chromium (132.0.6834.83), moving the cursor away from the top-level category does not change the menu's visibility as long as the cursor is still on top of the menu itself.

I've attached a screen recording showing the difference between Firefox and Chromium.

Work around:
Set dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed to false.

Blocks: 1938528

Hmm, the popup menu is not a child of the menu item in the menu bar. So, I've not understood why the mouse boundary event behavior change caused the regression.

According to the Inspector, there is no pointer boundary event listeners. So, this could be caused by edge cases of mouse boundary events which are not defined by the specs.

I'm not sure whether we should keep shipping the new boundary behavior because I'm not sure how much incompatible.

<nav>
  <menu>
    ...
    <li>
      <a href="...">Boardgames & Cardgames</a>
      <div style="display:none">...</div>
    </li>
    ...
  </menu>
</nav>

Note that the <div> under the link is not the popup menu. The popup menu is in a sibling of the <nav>.

When I move from above the link, "Boardgames & Cardgames", to the popup bellow the link, I got:

I/MouseBoundaryEvents NotifyMouseOver: the source event is eMouseMove (IsReal()=true)
I/MouseBoundaryEvents NotifyMouseOut: the source event is eMouseMove (IsReal()=true)
I/MouseBoundaryEvents Dispatching eMouseOut event to menu.nav.body.html.#document (2d96c00af70)
I/MouseBoundaryEvents Dispatching eMouseLeave event to menu.nav.body.html.#document (2d96c00af70) and its ancestors
I/MouseBoundaryEvents Dispatched
I/MouseBoundaryEvents Dispatching eMouseOver event to a.li.menu.nav.body.html.#document (2d96c00b7c0)
I/MouseBoundaryEvents Dispatching eMouseEnter event to a.li.menu.nav.body.html.#document (2d96c00b7c0) and its ancestors
I/MouseBoundaryEvents Dispatched "over" and "enter" events (the original "over" event target was in the document 2d96bc0d8

then,

I/MouseBoundaryEvents NotifyMouseOver: the source event is eMouseMove (IsReal()=true)
I/MouseBoundaryEvents NotifyMouseOut: the source event is eMouseMove (IsReal()=true)
I/MouseBoundaryEvents Dispatching eMouseOut event to a.li.menu.nav.body.html.#document (2d96c00b7c0)
I/MouseBoundaryEvents Dispatching eMouseLeave event to a.li.menu.nav.body.html.#document (2d96c00b7c0) and its ancestors
I/MouseBoundaryEvents Dispatched "out" and/or "leave" events
I/MouseBoundaryEvents Dispatching eMouseOver event to div.div.body.html.#document (2d972e37040)
I/MouseBoundaryEvents The dispatching "over" event target (2d972e37040) is removed
I/MouseBoundaryEvents The last "over" event target (2d972e37040) is removed and now the last deepest enter target becomes div.body.html.#document(2d96c098af0)
I/MouseBoundaryEvents Dispatching eMouseEnter event to div (2d972e37040) and its ancestors
I/MouseBoundaryEvents Dispatched "over" and "enter" events (the original "over" event target was in the document 0, and now in 2d96bc0d800)
D/MouseBoundaryEvents Forgetting the last "over" event target (2d972cd75b0) because it is not reconnected under the deepest enter event target (2d96c098af0)

I'm not sure the last mouseover target <div> is which one. However, it's removed from the DOM by a mouseover event listener. Then, it's not reconnected immediately. So, the website touches the black box of mouse boundary events.

The popup is:

<div class="nav-menu nav-menu_xxx">
  <div class="nav-menuCol">
    <a class="nav-link ...">...</a>
    ...
  </div>
  <div class="nav-menuCol">
    <a class="nav-link ...">...</a>
    ...
  </div>
</div>

When I move over the popup, mouseover is fired on the <div class="nav-menuCol"> and it's removed.

If I disable the pref, I got:

I/MouseBoundaryEvents NotifyMouseOver: the source event is eMouseMove (IsReal()=true)
I/MouseBoundaryEvents NotifyMouseOut: the source event is eMouseMove (IsReal()=true)
I/MouseBoundaryEvents Dispatching eMouseOut event to a[class="nav-topLink is-active"].li[class="nav-topItem"].menu[class="nav-topBar nav-visible"].nav[class="nav"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13ba9a0b7c0)
I/MouseBoundaryEvents Dispatching eMouseLeave event to a[class="nav-topLink is-active"].li[class="nav-topItem"].menu[class="nav-topBar nav-visible"].nav[class="nav"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13ba9a0b7c0) and its ancestors
I/MouseBoundaryEvents Dispatched "out" and/or "leave" events
I/MouseBoundaryEvents Dispatching eMouseOver event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13bb031f0d0)
I/MouseBoundaryEvents The last "over" event target (13bb031f0d0) is removed
I/MouseBoundaryEvents Dispatching eMouseEnter event to div[class="nav-menuCol"] (13bb031f0d0) and its ancestors
I/MouseBoundaryEvents Dispatched "over" and "enter" events (the original "over" event target was in the document 0, and now in 13ba7ad9800)

Ah, okay, next one chunk may be important.

Within the new behavior:

I/MouseBoundaryEvents NotifyMouseOver: the source event is eMouseMove (IsReal()=false)
I/MouseBoundaryEvents NotifyMouseOut: the source event is eMouseMove (IsReal()=false)
I/MouseBoundaryEvents Dispatching eMouseLeave event to div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (193d61a0670) and its ancestors
I/MouseBoundaryEvents Dispatched "out" and/or "leave" events
I/MouseBoundaryEvents Dispatching eMouseOver event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (193dc5460d0)
I/MouseBoundaryEvents Dispatching eMouseEnter event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (193dc5460d0) and its ancestors
I/MouseBoundaryEvents Dispatched "over" and "enter" events (the original "over" event target was in the document 193d43e1800, and now in 193d43e1800)

Within the legacy behavior:

I/MouseBoundaryEvents NotifyMouseOver: the source event is eMouseMove (IsReal()=false)
I/MouseBoundaryEvents Dispatching eMouseOver event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13ba9af04c0)
I/MouseBoundaryEvents Dispatching eMouseEnter event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13ba9af04c0) and its ancestors
I/MouseBoundaryEvents Dispatched "over" and "enter" events (the original "over" event target was in the document 13ba7ad9800, and now in 13ba7ad9800)
I/MouseBoundaryEvents The last "over" event target (13ba9af04c0) is removed

I/MouseBoundaryEvents NotifyMouseOver: the source event is eMouseMove (IsReal()=false)
I/MouseBoundaryEvents Dispatching eMouseOver event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13bb0334dc0)
I/MouseBoundaryEvents Dispatching eMouseEnter event to div[class="nav-menuCol"].div[class="nav-menu nav-menu_3003279 is-visible"].body[class="l-fullHeight pg-home"].html[class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths wf-montserrat-n4-active wf-active"].#document (13bb0334dc0) and its ancestors
I/MouseBoundaryEvents Dispatched "over" and "enter" events (the original "over" event target was in the document 13ba7ad9800, and now in 13ba7ad9800)

So, it seems that the mouseleave on <div class="nav-menu"> causes the issue.

Oh, but there is no mouseleave event listener...

I still don't understand what's going on, and it's hard to debug the website from C++ side due to mouse event boundary events. It'd be really helpful if somebody would create a minimized testcase.

Note that I wrote some tests to check the behavior on Chrome, but their events also odd. So, that makes me harder to debug the website too.

Keywords: testcase-wanted

(In reply to Masayuki Nakano [:masayuki] (he/him)(JST, +0900) from comment #4)

I'm not sure whether we should keep shipping the new boundary behavior because I'm not sure how much incompatible.

We're building the Fx135 RC on Monday. Please let me know ASAP what we want to do here.

Flags: needinfo?(masayuki)
Webcompat Priority: --- → P3

I chatted with smaug, the change should be backed out from the beta channel, and I think that it should be backed out from m-c too because it'll be merged to the next beta soon.

RyanVM: Could you back it out from both beta and m-c?

Flags: needinfo?(masayuki) → needinfo?(ryanvm)

(And keep open this bug since it's reproducible on Nightly and early beta builds even after back it out.)

@masayuki, @smaug, @hsin-yi -- How will backing out the change impact Firefox's final Interop-2024 score? How bad is the possible breakage you're worried about if this ships? Are there any reasonable alternatives to backing out?

IIUC we don't fully understand yet why this site is showing a problem, and we don't know if the problem is unique to this site or if other sites are affected. If other sites are affected, it may be minimal. Since this behavior is all behind a pref, we could pref it off after it goes to Release via Nimbus if there are significant problems. Reading this bug report, I'm uncertain that this problem rises to the level of significant. If you disagree and have additional information that makes you believe this will cause significant problems, please weigh in. Thanks!

Flags: needinfo?(smaug)
Flags: needinfo?(masayuki)
Flags: needinfo?(htsai)

@jgraham - How bad of a webcompat problem is this if we ship vs backout the support for the new mouse/pointer boundary behavior (which is the Pointer and Mouse Events focus area in Interop-2024)? You rated this bug as a P3, which is normally a bug we can live with for a while.

Also, what sites are likely fixed or improved by shipping the new mouse/pointer boundary behavior?

Flags: needinfo?(james)

IIUC we don't fully understand yet why this site is showing a problem, and we don't know if the problem is unique to this site or if other sites are affected.

Yes, debugging from C++ side, I've not found the root cause from the dispatching events. So, it might be caused by the website using different paths for Firefox and the others.

Are there any reasonable alternatives to backing out?

We could back out only about mouse boundary events, but it might cause other issues if websites use both point and mouse boundary events. (Anyway, this requires to write a patch to split the pref like this, however, anyway the score will have damage because the scope includes mouse boundary events.

Reading this bug report, I'm uncertain that this problem rises to the level of significant.

At least for the website, this is serious because of breaking the main UI of their navigation. However, I'm still not sure whether it's our side issue or theirs.

NOTE: Once we back the patch out, we start failing in following Interop2024 scope tests:

  • pointerevent_after_target_appended.html?mouse (3)
  • pointerevent_after_target_removed.html?mouse (4)
  • synthetic-mouse-enter-leave-over-out-button-state-after-target-removed.tentative.html (4)
Flags: needinfo?(masayuki)

Thanks, Masayuki and Smaug. I'm going to ask the webcompat team in slack and matrix to deep-dive on this so we can figure out why Firefox is having problems on this site and Chrome isn't. (Is it bad site code? Is it an error on our side? Is it timing related and just "luck" that it works in Chrome?) Once we know that, we should have a much better idea of what the best next steps are. If you can please be available in #webcompat on slack and "Web Compatiblity" on matrix during your normal business hours, that would be very helpful here. Thanks.

Hmm, so dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed=false would "fix" this, is that less risk than just backing out?

There are a lot of mouse{enter,leave} listeners, fwiw. The relevant ones seem to be mouse{enter,leave} in this section of the code. It seems they use setTimeout(20) in some places, setTimeout(0) in others, and sync toggle() calls...

One tricky thing is that those listeners end up being mouse{over,out} listeners because of jquery...

(Relevant link for the above is https://github.com/jquery/jquery/blob/098591e6fd3222e64b59af92c8849f5d8963d43c/src/event.js#L811)

So the page receives the mouseover on the menu, the page calls toggle() again, which calls showMenu (but async since it's a _.debounce(..)d function). We're open, so we call calcLayout(). this replaces the whole contents of the menu (that's the this.menu.html(this.cachedCalcs[e].html))), and that ends up firing a mouseout, which closes the menu, without a corresponding mouseover again.

So yeah I suspect this kinda depends on the timing of the synthetic mouse events and such, but I don't see why we'd fire a mouseout on the menu as a consequence of those mutations to begin with, and I don't see why we wouldn't fire another mouseover if we did... Masayuki, does the above help somehow?

Flags: needinfo?(masayuki)

In case someone finds it useful to crib from.

(In reply to Emilio Cobos Álvarez (:emilio) from comment #19)

Hmm, so dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed=false would "fix" this, is that less risk than just backing out?

That is what the backout we've been pondering means, backing out bug 1938528

This specific broken site probably isn't a serious webcompat problem; given the site is outside the local/global top 100k and it doesn't affect Android, if we did the triage it would get at most 80 points if we considered the breakage to be a broken workflow, 30 if we considered it a broken feature. Given that you can still access all the functionality by clicking on the main menu item and then using the filters on the subsequent pages it's arguably more like a broken feature, so I"d be inclined to triage at the lower severity.

So I think the main question here is not about this one site (although it's a regression); if necessary I would imagine there's a good chance we could figure out an intervention to fix this specific case. It's whether this is a early warning of more significant problems with the patch / our new behaviour. To figure that out we'll need a proper root cause. I can take a look at this some more tomorrow.

FWIW, with devtools "Pick an element from the page" the menu keeps opening, though I am totally unsure what devtools does, it might be a chance to use webcompat intervention in the same manner what devtools does.

(Sorry, I'm on a PTO today, I'll check it tomorrow.)

(In reply to Emilio Cobos Álvarez (:emilio) from comment #22)

So yeah I suspect this kinda depends on the timing of the synthetic mouse events and such, but I don't see why we'd fire a mouseout on the menu as a consequence of those mutations to begin with, and I don't see why we wouldn't fire another mouseover if we did... Masayuki, does the above help somehow?

Thank you for the investigation. I guess that the temporarily removed mouseover target is reconnected again in a duration which we think enough short. Therefore, we may not treat it as disconnected to fix bug 1886012 (the patch).

Flags: needinfo?(masayuki)

So, accoding to Emilio's investigation, we could fix this if we dispatch eMouseOver again when we restore the temporary removed target. However, it's potentilly risky change because it might cause an infinite loop.

Attached file menu_bug.tgz

Here is a slightly minified and pretty-printed testcase; note that jquery isn't pretty-printed because that somehow seems to break it.

(In reply to Masayuki Nakano [:masayuki] (he/him)(JST, +0900) from comment #29)

So, accoding to Emilio's investigation, we could fix this if we dispatch eMouseOver again when we restore the temporary removed target. However, it's potentilly risky change because it might cause an infinite loop.

I tried to do that, but it does not work on the web site, it may be not enough or I'm just misunderstanding something.

I still don't understand why were sending the mouseout to the menu. The menu is not getting disconnected aiui, just has all their children replaced

(In reply to Emilio Cobos Álvarez (:emilio) from comment #32)

I still don't understand why were sending the mouseout to the menu. The menu is not getting disconnected aiui, just has all their children replaced

Do you mean that "the menu" is the <a> in the menu bar, not in the popup? The popup is not a child of the <a> and anyway, when the element underneath the cursor is changed, mouseout needs to be dispatched.

Ah, the contnt of the popup is replaced with same HTML but different nodes. mouseout should be fired if and only if the last mouseover target is connected and hann't been removed except temporarily. Well, but I've not gotten that which mouseout is odd.

The bug is marked as tracked for firefox135 (beta) and tracked for firefox136 (nightly). We have limited time to fix this, the soft freeze is in 3 days. However, the bug still isn't assigned.

:ksenia, could you please find an assignee for this tracked bug? Given that it is a regression and we know the cause, we could also simply backout the regressor. If you disagree with the tracking decision, please talk with the release managers.

For more information, please visit BugBot documentation.

Flags: needinfo?(kberezina)

(In reply to Emilio Cobos Álvarez (:emilio) from comment #32)

I still don't understand why were sending the mouseout to the menu. The menu is not getting disconnected aiui, just has all their children replaced

I don't think that event is the issue: it is sent after the menu is closed (and I think it is sent because the menu was closed).
The real issue seems to be a mousemove handler on the document that decides to close the menu.

This breakpoint is hit in Firefox but not in Chrome.

I've found a key difference between Nightly and Stable: there is an event that triggers a handler in Stable but not in Nightly.
I've added logpoints to the boundary event handlers attached to this.menu and this.btn and a logpoint to the mousemove handler when it decides the menu should be closed (because it thinks the mouse is outside of it). And I've added event listener logpoints to mouseover, mouseenter and mouseout.
Note that in both cases the jQuery handler is called but in Nightly, jQuery doesn't call the app's handler, so we'll have to figure out what leads to the different jQuery behavior.

This is what I see in Stable.

This is what I see in Nightly.

The mousemove handler sets this.hoverBtn and this.hoverMenu to false and schedules a call to showMenu(). Before showMenu() is called, a mouseover event is handled by this.menu in Stable, which sets this.hoverMenu to a truthy value. When showMenu() is called it decides whether to show or hide the menu based on this.hoverBtn and this.hoverMenu - that's why the menu is closed in Nightly (where this.hoverMenu is still false) but not in Stable.

Furthermore there's a difference between Firefox and Chrome: in Firefox when the mouse moves from the menu button to the menu, a mousemove event is fired with the document as its target but I never see that target in mousemove events in Chrome.

We've decided to build RC1 with things left as-is with the option for an RC2 respin later this week still on the table pending further debugging. LMK if that's the route we decide to go.

Flags: needinfo?(ryanvm)
Severity: -- → S2
User Story: (updated)
Webcompat Priority: P3 → P1
Priority: -- → P1

(In reply to Holger Benl from comment #42)

Furthermore there's a difference between Firefox and Chrome: in Firefox when the mouse moves from the menu button to the menu, a mousemove event is fired with the document as its target but I never see that target in mousemove events in Chrome.

When mouse moves from menu button to menu, a timeout task is scheduled in mouseout event listener of menu button in https://www.eclecticgames.co.uk/js/main.js?v=1.1140

.bind(this)),
this.btn.on("mouseleave", function(t) {
  this.hoverBtn = !1,
  setTimeout(this.toggle.bind(this), 20)
}

I log the event and set a breakpoint on toggle method, and found the timeout handler runs in different timing,


Nightly with dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed=true:

mouseout <a class="nav-topLink is-active" href="c/boardgames-cardgames" ir-navsubbtn="">
mouseover <div class="nav-menuCol"> // <==== This set this.hoverMenu to true
mousemove HTMLDocument https://www.eclecticgames.co.uk/c/boardgames-cardgames // <==== This set this.hoverMenu to false
/// <==== timeout handler runs here, which closes the menu due to this.hoverMenu is false.
mouseover <div class="nav-menuCol">
mousemove <div class="nav-menuCol">
mousemove <div class="nav-menuCol">
mousemove <div class="nav-menuCol">
mouseout <div class="nav-menuCol">


Nightly with dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed=false:

mouseout <a class="nav-topLink is-active" href="c/boardgames-cardgames" ir-navsubbtn="">
mouseover <div class="nav-menuCol">. // <==== This set this.hoverMenu to true
mousemove HTMLDocument https://www.eclecticgames.co.uk/c/boardgames-cardgames. // <=== This set this.hoverMenu to false
mouseover <div class="nav-menuCol"> // <=== This set this.hoverMenu to true again
/// <==== timeout handler runs here
mousemove <div class="nav-menuCol">
mouseover <div class="nav-menuCol">


Chrome:

mouseout <a class=​"nav-topLink is-active" href=​"c/​boardgames-cardgames" ir-navsubbtn>​Boardgames & Cardgames​</a>​
mouseover <div class=​"nav-menuCol">​…​</div>​ // <==== This set this.hoverMenu to true
~mouseover <div class=​"nav-menuCol">​…​</div>~​
//// <==== timeout handler runs here
mousemove <div class=​"nav-menuCol">​…​</div>​
mouseover <div class=​"nav-menuCol">​…​</div>​


Edit: It turns out not related to timing but the app's handler doesn't run for the mouseover after the mousemove HTMLDocument. Please see comment# 46.

Thanks, Edgar -- what happens if we change the timeout to be longer? Does the problem appear to go away?

Flags: needinfo?(echen)

Unlike Edgar, I keep seeing a mouseover <div class="nav-menuCol"> after themousemove HTMLDocument and before the timeout handler runs in both Nightly and Stable / Nightly with dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed=false.
The difference is that in Nightly the mouseover has a relatedTarget whereas in Stable and Nightly with dom.events.mouse-pointer-boundary.keep-enter-targets-after-over-target-removed=false relatedTarget is null and for some reason jQuery only dispatches the event to the app's handler if relatedTarget is null.
It seems to me that the new behavior is correct and Chrome also sets relatedTarget for these events. But Chrome doesn't have an issue because it doesn't fire the mousemove HTMLDocument event.

Thanks Holger - should the mousemove HTMLDocument event be sent? I.e. does Chrome work because it has a bug, and this page is dependent on that bug?

I noticed on the company that builds this package for these sites that their dropdown (which appears similar) does NOT hit this bug; is it possible they updated the package/library but it hasn't been incorporated into already-existing clients? I imagine the best way to find out would be to ask them, though in theory we could find it from examining the code on their page - https://www.intelligentretail.com/

User Story: (updated)

I looked at their site, and it does appear to use the same menu system. I didn't try to compare code between the two beyond noticing the similarities of how it's set up.

Chrome also has relatedTarget set but to a different element: in Firefox it is set to the <div class="nav-menu"> that contains the target <div class="nav-menuCol"> but in Chrome it is set to another <div class="nav-menuCol"> that is disconnected from the DOM (but looks like the the target <div class="nav-menuCol">, so apparently it's an old copy of that element).
jQuery doesn't dispatch this event if the relatedTarget is identical to the element on which the handler is registered, which is the case here (in Firefox).

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #47)

Thanks Holger - should the mousemove HTMLDocument event be sent? I.e. does Chrome work because it has a bug, and this page is dependent on that bug?

I don't know, I'm not familiar enough with the spec.

I noticed on the company that builds this package for these sites that their dropdown (which appears similar) does NOT hit this bug; is it possible they updated the package/library but it hasn't been incorporated into already-existing clients? I imagine the best way to find out would be to ask them, though in theory we could find it from examining the code on their page - https://www.intelligentretail.com/

I'll try to compare their code tomorrow.

Oh, Holger is right, the app's handler isn't run for mouseover after the mousemove HTMLDocument, I thought the event is not yet dispatched. So the change the timeout to be longer doesn't help.

Flags: needinfo?(echen)
Attached file test.html

This test is an extension of the one in comment #23 and should simulate the page behavior.

Thank you very much, Holger and Edgar!!! I'll try to understand what's going on.

Interesting, Chrome dispatches mouseover on replaced element if user moves the cursor again.

Additionally, when mouseover makes the target disconnected, they stops dispatching the source mousemove event. However, we do it on the Document node.

Okay, it fixes the root cause if I makes us stop dispatching the source event of mouse/pointer boundary events (i.e., the mousemove event on the Document in the case). I'll run automated tests with the patch, but I think that this is pretty risky change especially for RC builds. So, anyway the patch is not available in the 135 branch.

@masayuki - Is that change in behavior (to more closely match Chrome's behavior) within the spec? What is the risk of doing this?

Flags: needinfo?(masayuki)

Oh, it seems that Chromium developers want to change something around mousemove event dispatcher.

I wrote WPT for bug 1944191. However, Chrome Canary dispatches mousemove on connected ancestor of removed/replaced mouseover target. However, with running Chrome Canary with --no-enable-experimental, they dispatch mousemove on the disconnected mouseover target. Therefore, it does not run mousemove event listener on the Document node.

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #56)

@masayuki - Is that change in behavior (to more closely match Chrome's behavior) within the spec? What is the risk of doing this?

No, there is no definition. If we can emulate Chrome's behavior completely, the risk is not so high, but it's difficult because Chrome's mouse boundary behavior is not so clear to me and feels odd to me in some cases.

EDIT: See comment 60.

Flags: needinfo?(masayuki)

(I think that the website should not use mousemove to hide the menu due to the stable state of mousemove definition of such edge case.)

(In reply to Masayuki Nakano [:masayuki] (he/him)(JST, +0900) from comment #58)

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #56)

@masayuki - Is that change in behavior (to more closely match Chrome's behavior) within the spec? What is the risk of doing this?

No, there is no definition. If we can emulate Chrome's behavior completely, the risk is not so high, but it's difficult because Chrome's mouse boundary behavior is not so clear to me and feels odd to me in some cases.

Oh, I found this paragraph in "3.4.4. Mouse Event Order":

If the event target (e.g. the target element) is removed from the DOM during the mouse events sequence, the remaining events of the sequence MUST NOT be fired on that element.

However, I'm not 100% sure whether this covers the mouse boundary events, and this does not match with the experimental behavior of Chrome Canary.

Er, if this means that mousemove event should be fired on new target in the composed tree rather than the disconnected mouseover target, Chrome Canary's new behavior matches with this paragraph.

If the experimental feature is disabled, Chromium considers the mousemove event target with the composedPath of preceding pointermove. However, if it's enabled, Chromium considers the target from the deepest mouseenter event target in the tree.
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/input/pointer_event_manager.cc;l=1085-1088;drc=27d34700b83f381c62e3a348de2e6dfdc08364b8

I don't understand the reason why this makes Chrome Canary stop dispatching mousemove after replacing the mouseover target.
https://jsfiddle.net/d_toybox/3tsd72rc/4/

so, to summarize:

  • Chrome's current behavior doesn't match the spec. Canary's new experimental behavior matches a possible interpretation of the spec, but the spec isn't 100% clear here.
  • Our behavior doesn't match the spec, but in a different manner than Chrome
  • The website is relying on a non-spec behavior of Chrome

Are those correct?

Flags: needinfo?(masayuki)

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #63)

so, to summarize:

  • Chrome's current behavior doesn't match the spec. Canary's new experimental behavior matches a possible interpretation of the spec, but the spec isn't 100% clear here.

Right.

  • Our behavior doesn't match the spec, but in a different manner than Chrome

Right.

  • The website is relying on a non-spec behavior of Chrome

It seems that both yes and no. Even if I enable "Boundary Event Dispatch Tracks Node Removal" experimental feature of Chrome Canary (that should dispatch mousemove after , the menu works on Chrome Canary. So, there is another compatible issue at least, but I don't know the details.

Flags: needinfo?(masayuki)

Okay, I got the another cause. If we stop synthesizing mouseover in the next animation frame, the website works.

According to attachment 9462063 [details], when we dispatch mouseover on menu, it tries to show the popup. Then, we dispatch mousemove on the document node which cases setting hoverMenu to false. At this point, we store the deepest mouseenter target is the menu. Then, we dispatch mouseover event on the new popup. Then, the mouseover's relatedTarget is set to menu. Therefore, mouseover event listener of menu does nothing due to skipEvent(menu, e). Finally, toggle may be fired by setTimeout(toggle, 20) in mouseout event listener of link and close the menu due to hoverMenu == false. So, the our bug is, we set wrong relatedTarget.

Hmm... I'm really confused again. Once I enable the experimental feature on Chrome Canary, the website works but starting to fail in attachment 9462063 [details].

I mean, I cannot write a reasonable WPT for bug 1944212. Perhaps, we haven't reached the root cause yet.

Wow, I realized that Chrome Canary works differently if I modified attachment 9462063 [details]:

<nav id="menu"><div class="contents">something<br>something</div></nav>

to:

<nav id="menu">
  <div class="contents">something<br>something</div>
</nav>

Setting innerHTML difference might cause changing the mouseover targetting on Chrome...

Ah,

      $(document).on(
        'mousemove',
        function (t) {
          IR.breakpoint.value >= this.breakpoint &&
          (this.hoverBtn || this.hoverMenu) &&
          $(t.target).closest(this.menu).length < 1 &&
          $(t.target).closest(this.btn).length < 1 &&
          (this.hoverBtn = !1, this.hoverMenu = !1, this.toggle())
        }.bind(this)

$(t.target).closest(this.menu).length < 1 must work for Chrome because their mousemove is fired on parent of the replaced element, i.e., menu.

(In reply to Randell Jesup [:jesup] (needinfo me) from comment #47)

I noticed on the company that builds this package for these sites that their dropdown (which appears similar) does NOT hit this bug;

I've looked at the site: the code is almost identical (the line numbers in the pretty-printed files are off a little so there seems to be a small difference) and the menu sometimes works and sometimes doesn't (showing the same issue as eclecticgames.co.uk). I've also seen the menu work sporadically on eclecticgames.co.uk but on intelligentretail.com it works a lot more often. To reproduce the issue on intelligentretail.com move the mouse slowly from the button to the menu.
So there are other sites having the same issue, confirming the high priority.

A question for the people doing diagnosis: is there a simple change we could suggest to the site authors that would fix this issue (without regressing Chrome or other browsers)? It seems like their code is racy and poking at underdefined parts of the platform, so irrespective of whether we can implement behaviour that aligns with Chrome/others, ultimately the current situation is best fixed by the site making changes.

(In reply to James Graham [:jgraham] from comment #71)

A question for the people doing diagnosis: is there a simple change we could suggest to the site authors that would fix this issue (without regressing Chrome or other browsers)? It seems like their code is racy and poking at underdefined parts of the platform, so irrespective of whether we can implement behaviour that aligns with Chrome/others, ultimately the current situation is best fixed by the site making changes.

I think the most straightforward fix would be to ignore mousemove events whose target is the document in the handler that closes the menu when the mouse is outside of it. I've tried this fix (adding t.target.nodeType !== 9 to the condition in the handler) and it fixed the issue for me.

Note that there seems to be another issue with the same symptoms on intelligentretail.com: the menu also closes sometimes when the mouse moves from the button over it but on that site it also happens in Firefox Stable and Chrome and my proposed fix doesn't fix the issue there.

User Story: (updated)
Flags: needinfo?(james)
User Story: (updated)
Flags: needinfo?(htsai)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: