Closed Bug 2041756 Opened 10 days ago Closed 2 days ago

Assertion failure: false (MOZ_ASSERT_UNREACHABLE: Internal raw update events shouldn't be dispatched to the DOM), at checkouts/gecko/widget/WidgetEventImpl.cpp:180

Categories

(Core :: DOM: Events, defect)

defect

Tracking

()

RESOLVED FIXED
153 Branch
Tracking Status
firefox-esr140 --- unaffected
firefox151 --- unaffected
firefox152 --- unaffected
firefox153 --- fixed

People

(Reporter: tsmith, Assigned: masayuki)

References

(Blocks 1 open bug, Regression, )

Details

(Keywords: assertion, pernosco, regression)

Attachments

(1 file)

Found with m-c 20260521-b754e06486cf (--enable-debug)

This was found by visiting a live website with a debug build.

STR:

  • Launch browser and visit site

This issue was triggered by visiting http://josbank.com/. A Pernosco session is available here: https://pernos.co/debug/V33tNvf2_d9LgWjn3zKY6Q/index.html

Assertion failure: false (MOZ_ASSERT_UNREACHABLE: Internal raw update events shouldn't be dispatched to the DOM), at checkouts/gecko/widget/WidgetEventImpl.cpp:180

0|0|libxul.so|mozilla::IsForbiddenDispatchingToNonElementContent(mozilla::EventMessage)|git:github.com/mozilla-firefox/firefox:widget/WidgetEventImpl.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|179|0x55
0|1|libxul.so|nsContentUtils::GetEventTargetContent(nsIContent*, mozilla::WidgetEvent const*)|git:github.com/mozilla-firefox/firefox:dom/base/nsContentUtils.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|5657|0x23
0|2|libxul.so|mozilla::PresShell::EventHandler::EventTargetData::ComputeElementFromFrame(mozilla::WidgetGUIEvent*)|git:github.com/mozilla-firefox/firefox:layout/base/PresShell.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|12729|0x49
0|3|libxul.so|mozilla::PresShell::EventHandler::HandleEventUsingCoordinates(AutoWeakFrame&, mozilla::WidgetGUIEvent*, nsEventStatus*, bool)|git:github.com/mozilla-firefox/firefox:layout/base/PresShell.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|7645|0x1cc
0|4|libxul.so|mozilla::PresShell::EventHandler::HandleEvent(AutoWeakFrame&, mozilla::WidgetGUIEvent*, bool, nsEventStatus*)|git:github.com/mozilla-firefox/firefox:layout/base/PresShell.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|7536|0x197
0|5|libxul.so|mozilla::PresShell::EnsurePrecedingPointerRawUpdate(AutoWeakFrame&, mozilla::WidgetGUIEvent const&, bool)|git:github.com/mozilla-firefox/firefox:layout/base/PresShell.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|7414|0x17e
0|6|libxul.so|mozilla::PresShell::HandleEvent(nsIFrame*, mozilla::WidgetGUIEvent*, bool, nsEventStatus*)|git:github.com/mozilla-firefox/firefox:layout/base/PresShell.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|7345|0x3ff
0|7|libxul.so|mozilla::PresShellWidgetListener::HandleEvent(mozilla::WidgetGUIEvent*)|git:github.com/mozilla-firefox/firefox:layout/base/PresShellWidgetListener.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|244|0xb3
0|8|libxul.so|nsContentUtils::SynthesizeMouseEvent(mozilla::PresShell*, nsIWidget*, nsTSubstring<char16_t> const&, mozilla::gfx::IntPointTyped<mozilla::LayoutDevicePixel>&, mozilla::dom::SynthesizeMouseEventData const&, mozilla::dom::SynthesizeMouseEventOptions const&, mozilla::dom::Optional<mozilla::OwningNonNull<mozilla::dom::VoidFunction> > const&)|git:github.com/mozilla-firefox/firefox:dom/base/nsContentUtils.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|9911|0x71b
0|9|libxul.so|nsGlobalWindowInner::SynthesizeMouseEvent(nsTSubstring<char16_t> const&, float, float, mozilla::dom::SynthesizeMouseEventData const&, mozilla::dom::SynthesizeMouseEventOptions const&, mozilla::dom::Optional<mozilla::OwningNonNull<mozilla::dom::VoidFunction> > const&, mozilla::ErrorResult&)|git:github.com/mozilla-firefox/firefox:dom/base/nsGlobalWindowInner.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|2824|0x108
0|10|libxul.so|mozilla::dom::Window_Binding::synthesizeMouseEvent(JSContext*, JS::Handle<JSObject*>, void*, JSJitMethodCallArgs const&)|s3:gecko-generated-sources:5c0e19f4a5f47f8dcd5ffe9d395934400510df90b75ab59d1ccee385759e039396c729b01a99162b6dbae54a74de54eedf642a4c19b882c5ab2af3c4826a29e3/dom/bindings/WindowBinding.cpp:|8269|0x467
0|11|libxul.so|mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::MaybeCrossOriginObjectThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*)|git:github.com/mozilla-firefox/firefox:dom/bindings/BindingUtils.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|3217|0x25b
0|12|libxul.so|CallJSNative(JSContext*, bool (*)(JSContext*, unsigned int, JS::Value*), js::CallReason, JS::CallArgs const&)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|498|0xf4
0|13|libxul.so|js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|594|0x2bf
0|14|libxul.so|js::Interpret(JSContext*, js::RunState&)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|3280|0xa8e6
0|15|libxul.so|MaybeEnterInterpreterTrampoline(JSContext*, js::RunState&)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|392|0x23f
0|16|libxul.so|js::RunScript(JSContext*, js::RunState&)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|468|0x3da
0|17|libxul.so|js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|626|0x2e5
0|18|libxul.so|js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason)|git:github.com/mozilla-firefox/firefox:js/src/vm/Interpreter.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|693|0x12c
0|19|libxul.so|js::jit::InvokeFunction(JSContext*, JS::Handle<JSObject*>, bool, bool, unsigned int, JS::Value*, JS::MutableHandle<JS::Value>)|git:github.com/mozilla-firefox/firefox:js/src/jit/VMFunctions.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|565|0x7c4
0|20|libxul.so|js::jit::InvokeFromInterpreterStub(JSContext*, js::jit::InterpreterStubExitFrameLayout*)|git:github.com/mozilla-firefox/firefox:js/src/jit/VMFunctions.cpp:1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1|589|0x10c

Moving this over to Core :: DOM: Events, since the assertion is in widget/ but the logic that appears to be triggering it is in the new pointerrawupdate dispatch path rather than in widget code.

I am not sufficiently familiar with this area to make progress on this myself, but I asked Claude to take a look and the analysis below seemed reasonable as a starting point. Posting it here in case it is useful.


The path appears to be:

  1. Some chrome JS (here, the site-scout harness) calls window.synthesizeMouseEvent, which is [ChromeOnly] in dom/webidl/Window.webidl around line 640.
  2. The event flows through nsContentUtils::SynthesizeMouseEvent (dom/base/nsContentUtils.cpp:9911) with toWindow: true, which dispatches via PresShell::HandleEvent.
  3. PresShell::HandleEvent calls EnsurePrecedingPointerRawUpdate (layout/base/PresShell.cpp:7414).
  4. That function early-returns on mouseEvent->IsSynthesized(), but WidgetMouseEvent::IsSynthesized() is documented (widget/MouseEvents.h:452-457) to mean "synthesized for scroll or layout change" only, with an explicit note "Do not confuse this with a synthesized event for tests." Events from nsContentUtils::SynthesizeMouseEvent set mFlags.mIsSynthesizedForTests instead, so the gate does not catch them.
  5. The function then constructs a fresh eMouseRawUpdate WidgetMouseEvent and calls eventHandler.HandleEvent(...) on it.
  6. That runs through HandleEventUsingCoordinates then EventTargetData::ComputeElementFromFrame then nsContentUtils::GetEventTargetContent, which calls IsForbiddenDispatchingToNonElementContent(eMouseRawUpdate).
  7. The MOZ_ASSERT_UNREACHABLE at widget/WidgetEventImpl.cpp:178-180 fires.

The assertion message reads "Internal raw update events shouldn't be dispatched to the DOM", but GetEventTargetContent is called during target computation (as part of normal hit-testing in ComputeElementFromFrame), not at DOM-listener dispatch time. Reaching this function does not by itself mean the event is being dispatched to the DOM. The commit message for the regressor explicitly states the expectation that DispatchPrecedingPointerEvent will refuse the internal raw-update event and the caller will stop, but ComputeElementFromFrame runs before DispatchPrecedingPointerEvent and so the "stop before DOM dispatch" guarantee does not hold at this point.

In release builds the code falls through and returns true, which causes GetEventTargetContent to walk up to the inclusive ancestor element. That looks like the same target adjustment the other mouse messages get, so the runtime behavior in opt builds appears to be sensible. The mismatch seems to be limited to the debug assertion.

Trigger conditions for reaching this assertion, as best I can tell, are roughly:

  • convertToPointer and convertToPointerRawUpdate both true on the source mouse event.
  • BrowserChild::HasPointerRawUpdateEventListeners() returns true (some window in the BrowserChild has a pointerrawupdate listener).
  • The hit-test target frame's content is a non-element node, e.g. a text node.
  • The source event is not IsSynthesized() in the scroll/layout-synth sense.

This is not specific to synthesizeMouseEvent. Real user input should be able to reach the same path in principle, given a pointerrawupdate listener and a hit on bare text content, though it appears to be much rarer in practice because listeners are usually attached on container elements that hit-test as elements.

The regressor appears to be bug 1550462 part 2 (PresShell::HandleEvent dispatch preceding pointerrawupdate event), which introduced both the assertion in IsForbiddenDispatchingToNonElementContent and the new EnsurePrecedingPointerRawUpdate dispatch path that reaches it.

Possible directions for a fix, with the caveat that I have not validated any of these against the broader pointer-events design:

a) Remove the MOZ_ASSERT_UNREACHABLE for eMouseRawUpdate and eTouchRawUpdate in IsForbiddenDispatchingToNonElementContent and keep the return true;, on the basis that target adjustment is not DOM dispatch.

b) Also early-return in EnsurePrecedingPointerRawUpdate when mouseEvent->mFlags.mIsSynthesizedForTests is set. This would silence the assertion for the test-synthesis case but would not address the underlying assertion-vs-design mismatch for real input.

c) Arrange for the internal raw-update event to be filtered out before reaching ComputeElementFromFrame / GetEventTargetContent, which seems likely to be more invasive.

Component: Widget → DOM: Events
Flags: needinfo?(masayuki)
Keywords: regression
Regressed by: 1550462

Set release status flags based on info from the regressing bug 1550462

Ah, yeah, asserting it in IsForbiddenDispatchingToNonElementContent must be wrong since it's a utility method. And this is a regression of bug 2035992, not bug 1550462 since nsContentUtils::GetEventTargetContent was added recently.

Assignee: nobody → masayuki
Severity: -- → S3
Status: NEW → ASSIGNED
Flags: needinfo?(masayuki)
OS: Unspecified → All
Regressed by: 2035992
No longer regressed by: 1550462
Hardware: Unspecified → All

eMouseRawUpdate and eTouchRawUpdate are internal event messages to
dispatch ePointerRawUpdate and
IsForbiddenDispatchingToNonElementContent() is just a utility method
to help to compute the event target. So, callers may want to know the
ePointerRawUpdate event target with eMouseRawUpdate or
eTouchRawUpdate event message. Therefore, the method should not assert
it and instead, EventDispatcher::Dispatch should check it since it's
the only one dispatcher of WidgetEvent.

Pushed by masayuki@d-toybox.com: https://github.com/mozilla-firefox/firefox/commit/e0cdd748e5ee https://hg.mozilla.org/integration/autoland/rev/19b9912afa67 Make `IsForbiddenDispatchingToNonElementContent()` not assert whether the message can be dispatched to the DOM r=smaug
Status: ASSIGNED → RESOLVED
Closed: 2 days ago
Resolution: --- → FIXED
Target Milestone: --- → 153 Branch
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: