Closed Bug 1663722 Opened 4 years ago Closed 2 years ago

Assertion failure: mPresContext->GetVisibleArea().height != nscoord((1 << 30) - 1) (height should not be NS_UNCONSTRAINEDSIZE after reflow), at /builds/worker/checkouts/gecko/layout/base/PresShell.cpp:2107

Categories

(Core :: Layout, defect)

defect

Tracking

()

VERIFIED FIXED
98 Branch
Tracking Status
firefox-esr91 --- wontfix
firefox82 --- wontfix
firefox93 --- wontfix
firefox94 --- wontfix
firefox95 --- wontfix
firefox96 --- wontfix
firefox97 --- wontfix
firefox98 --- verified

People

(Reporter: jkratzer, Assigned: dholbert)

References

(Blocks 1 open bug)

Details

(Keywords: testcase, Whiteboard: [bugmon:confirmed][fuzzblocker])

Attachments

(2 files, 1 obsolete file)

Attached file testcase.zip (obsolete) —

Testcase found while fuzzing mozilla-central rev fb9c01b719fa (built with --enable-debug). Testcase must be served over HTTP.

Assertion failure: mPresContext->GetVisibleArea().height != nscoord((1 << 30) - 1) (height should not be NS_UNCONSTRAINEDSIZE after reflow), at /builds/worker/checkouts/gecko/layout/base/PresShell.cpp:2107

    #0 0x7f2d49147fca in mozilla::PresShell::ResizeReflowIgnoreOverride(int, int, mozilla::ResizeReflowOptions) /gecko/layout/base/PresShell.cpp:2105:3
    #1 0x7f2d4924dc1d in nsDocumentViewer::GetContentSizeInternal(int*, int*, int, int) /gecko/layout/base/nsDocumentViewer.cpp:2850:18
    #2 0x7f2d4924e178 in nsDocumentViewer::GetContentSize(int*, int*) /gecko/layout/base/nsDocumentViewer.cpp:2881:10
    #3 0x7f2d44534340 in nsGlobalWindowOuter::SizeToContentOuter(mozilla::dom::CallerType, mozilla::ErrorResult&) /gecko/dom/base/nsGlobalWindowOuter.cpp:5670:16
    #4 0x7f2d45c620c9 in mozilla::dom::Window_Binding::sizeToContent(JSContext*, JS::Handle<JSObject*>, void*, JSJitMethodCallArgs const&) /builds/worker/workspace/obj-build/dom/bindings/WindowBinding.cpp:5786:24
    #5 0x7f2d4648f188 in bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::MaybeCrossOriginObjectThisPolicy, mozilla::dom::binding_detail::ThrowExceptions>(JSContext*, unsigned int, JS::Value*) /gecko/dom/bindings/BindingUtils.cpp:3227:13
    #6 0x7f2d4cb58248 in CallJSNative /gecko/js/src/vm/Interpreter.cpp:507:13
    #7 0x7f2d4cb58248 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:599:12
    #8 0x7f2d4cb5a56b in InternalCall(JSContext*, js::AnyInvokeArgs const&, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:664:10
    #9 0x7f2d4cb5a8f0 in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:681:8
    #10 0x7f2d4cd89479 in js::ForwardingProxyHandler::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const /gecko/js/src/proxy/Wrapper.cpp:163:10
    #11 0x7f2d4cd5c8d5 in js::CrossCompartmentWrapper::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) const /gecko/js/src/proxy/CrossCompartmentWrapper.cpp:239:19
    #12 0x7f2d4cd6e836 in js::Proxy::call(JSContext*, JS::Handle<JSObject*>, JS::CallArgs const&) /gecko/js/src/proxy/Proxy.cpp:641:19
    #13 0x7f2d4cb588a9 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:573:14
    #14 0x7f2d4cb5a56b in InternalCall(JSContext*, js::AnyInvokeArgs const&, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:664:10
    #15 0x7f2d4cb412e1 in CallFromStack /gecko/js/src/vm/Interpreter.cpp:668:10
    #16 0x7f2d4cb412e1 in Interpret(JSContext*, js::RunState&) /gecko/js/src/vm/Interpreter.cpp:3336:16
    #17 0x7f2d4cb21eb0 in js::RunScript(JSContext*, js::RunState&) /gecko/js/src/vm/Interpreter.cpp:468:13
    #18 0x7f2d4cb583d9 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:636:13
    #19 0x7f2d4cb5a56b in InternalCall(JSContext*, js::AnyInvokeArgs const&, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:664:10
    #20 0x7f2d4cb5a8f0 in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:681:8
    #21 0x7f2d4d1bb2c4 in js::CallSelfHostedFunction(JSContext*, JS::Handle<js::PropertyName*>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>) /gecko/js/src/vm/SelfHosting.cpp:1694:10
    #22 0x7f2d4cdce759 in AsyncFunctionResume(JSContext*, JS::Handle<js::AsyncFunctionGeneratorObject*>, ResumeKind, JS::Handle<JS::Value>) /gecko/js/src/vm/AsyncFunction.cpp:128:8
    #23 0x7f2d4cf246a9 in AsyncFunctionPromiseReactionJob /gecko/js/src/builtin/Promise.cpp:1700:10
    #24 0x7f2d4cf246a9 in PromiseReactionJob(JSContext*, unsigned int, JS::Value*) /gecko/js/src/builtin/Promise.cpp:1852:12
    #25 0x7f2d4cb58248 in CallJSNative /gecko/js/src/vm/Interpreter.cpp:507:13
    #26 0x7f2d4cb58248 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:599:12
    #27 0x7f2d4cb5a56b in InternalCall(JSContext*, js::AnyInvokeArgs const&, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:664:10
    #28 0x7f2d4cb5a8f0 in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) /gecko/js/src/vm/Interpreter.cpp:681:8
    #29 0x7f2d4cce99c2 in JS::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::HandleValueArray const&, JS::MutableHandle<JS::Value>) /gecko/js/src/jsapi.cpp:2831:10
    #30 0x7f2d4525380f in mozilla::dom::PromiseJobCallback::Call(mozilla::dom::BindingCallContext&, JS::Handle<JS::Value>, mozilla::ErrorResult&) /builds/worker/workspace/obj-build/dom/bindings/PromiseBinding.cpp:28:8
    #31 0x7f2d40af1ddc in Call /builds/worker/workspace/obj-build/dist/include/mozilla/dom/PromiseBinding.h:91:12
    #32 0x7f2d40af1ddc in Call /builds/worker/workspace/obj-build/dist/include/mozilla/dom/PromiseBinding.h:104:12
    #33 0x7f2d40af1ddc in mozilla::PromiseJobRunnable::Run(mozilla::AutoSlowOperation&) /gecko/xpcom/base/CycleCollectedJSContext.cpp:211:18
    #34 0x7f2d40ad235b in mozilla::CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool) /gecko/xpcom/base/CycleCollectedJSContext.cpp:646:17
    #35 0x7f2d464b0095 in LeaveMicroTask /builds/worker/workspace/obj-build/dist/include/mozilla/CycleCollectedJSContext.h:232:7
    #36 0x7f2d464b0095 in mozilla::dom::CallbackObject::CallSetup::~CallSetup() /gecko/dom/bindings/CallbackObject.cpp:393:11
    #37 0x7f2d448b07c3 in void mozilla::dom::Function::Call<nsCOMPtr<nsIGlobalObject> >(nsCOMPtr<nsIGlobalObject> const&, nsTArray<JS::Value> const&, JS::MutableHandle<JS::Value>, mozilla::ErrorResult&, char const*, mozilla::dom::CallbackObject::ExceptionHandling, JS::Realm*) /builds/worker/workspace/obj-build/dist/include/mozilla/dom/FunctionBinding.h:74:3
    #38 0x7f2d448b03a3 in mozilla::dom::CallbackTimeoutHandler::Call(char const*) /gecko/dom/base/TimeoutHandler.cpp:167:29
    #39 0x7f2d444eac79 in nsGlobalWindowInner::RunTimeoutHandler(mozilla::dom::Timeout*, nsIScriptContext*) /gecko/dom/base/nsGlobalWindowInner.cpp:6083:38
    #40 0x7f2d448ab82a in mozilla::dom::TimeoutManager::RunTimeout(mozilla::TimeStamp const&, mozilla::TimeStamp const&, bool) /gecko/dom/base/TimeoutManager.cpp:915:44
    #41 0x7f2d448aa3b5 in mozilla::dom::TimeoutExecutor::MaybeExecute() /gecko/dom/base/TimeoutExecutor.cpp:179:11
    #42 0x7f2d448adf36 in Notify /gecko/dom/base/TimeoutExecutor.cpp:246:5
    #43 0x7f2d448adf36 in non-virtual thunk to mozilla::dom::TimeoutExecutor::Notify(nsITimer*) /gecko/dom/base/TimeoutExecutor.cpp
    #44 0x7f2d40cc9fe9 in nsTimerImpl::Fire(int) /gecko/xpcom/threads/nsTimerImpl.cpp:565:39
    #45 0x7f2d40cc97cd in nsTimerEvent::Run() /gecko/xpcom/threads/TimerThread.cpp:251:11
    #46 0x7f2d40d06113 in mozilla::ThrottledEventQueue::Inner::ExecuteRunnable() /gecko/xpcom/threads/ThrottledEventQueue.cpp:254:22
    #47 0x7f2d40cf9fdf in mozilla::ThrottledEventQueue::Inner::Executor::Run() /gecko/xpcom/threads/ThrottledEventQueue.cpp:81:15
    #48 0x7f2d40cad309 in mozilla::RunnableTask::Run() /gecko/xpcom/threads/TaskController.cpp:242:16
    #49 0x7f2d40ca9817 in mozilla::TaskController::DoExecuteNextTaskOnlyMainThreadInternal(mozilla::detail::BaseAutoLock<mozilla::Mutex&> const&) /gecko/xpcom/threads/TaskController.cpp:512:26
    #50 0x7f2d40ca76b7 in mozilla::TaskController::ExecuteNextTaskOnlyMainThreadInternal(mozilla::detail::BaseAutoLock<mozilla::Mutex&> const&) /gecko/xpcom/threads/TaskController.cpp:371:15
    #51 0x7f2d40ca7b0d in mozilla::TaskController::ProcessPendingMTTask(bool) /gecko/xpcom/threads/TaskController.cpp:168:36
    #52 0x7f2d40cb9144 in operator() /gecko/xpcom/threads/TaskController.cpp:86:37
    #53 0x7f2d40cb9144 in mozilla::detail::RunnableFunction<mozilla::TaskController::InitializeInternal()::$_5>::Run() /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:577:5
    #54 0x7f2d40cdd164 in nsThread::ProcessNextEvent(bool, bool*) /gecko/xpcom/threads/nsThread.cpp:1234:14
    #55 0x7f2d40ce78ec in NS_ProcessNextEvent(nsIThread*, bool) /gecko/xpcom/threads/nsThreadUtils.cpp:513:10
    #56 0x7f2d41fbaba4 in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) /gecko/ipc/glue/MessagePump.cpp:109:5
    #57 0x7f2d41ebf5a1 in RunInternal /gecko/ipc/chromium/src/base/message_loop.cc:334:10
    #58 0x7f2d41ebf5a1 in RunHandler /gecko/ipc/chromium/src/base/message_loop.cc:327:3
    #59 0x7f2d41ebf5a1 in MessageLoop::Run() /gecko/ipc/chromium/src/base/message_loop.cc:309:3
    #60 0x7f2d48c22b47 in nsBaseAppShell::Run() /gecko/widget/nsBaseAppShell.cpp:137:27
    #61 0x7f2d4c8ed30f in XRE_RunAppShell() /gecko/toolkit/xre/nsEmbedFunctions.cpp:913:20
    #62 0x7f2d41ebf5a1 in RunInternal /gecko/ipc/chromium/src/base/message_loop.cc:334:10
    #63 0x7f2d41ebf5a1 in RunHandler /gecko/ipc/chromium/src/base/message_loop.cc:327:3
    #64 0x7f2d41ebf5a1 in MessageLoop::Run() /gecko/ipc/chromium/src/base/message_loop.cc:309:3
    #65 0x7f2d4c8ec8ac in XRE_InitChildProcess(int, char**, XREChildData const*) /gecko/toolkit/xre/nsEmbedFunctions.cpp:744:34
    #66 0x557fd79c88dd in content_process_main(mozilla::Bootstrap*, int, char**) /gecko/browser/app/../../ipc/contentproc/plugin-container.cpp:56:28
    #67 0x557fd79c8d17 in main /gecko/browser/app/nsBrowserApp.cpp:303:18
    #68 0x7f2d5d1d70b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
    #69 0x557fd791c279 in _start (/home/worker/builds/m-c-20200905093724-fuzzing-asan-opt/firefox+0x5c279)
Flags: in-testsuite?
Keywords: bugmon
Whiteboard: [bugmon:confirm] → [bugmon:confirmed]
Bugmon Analysis:
Unable to reproduce bug using the following builds:
> mozilla-central 20200908215255-dc90a7a18c07
> mozilla-central 20200908030802-80ac8d8c74d5
Removing bugmon keyword as no further action possible.
Please review the bug and re-add the keyword for further analysis.

The fuzzers have been tripping over this for a while and it is triggered frequently. Marking as fuzzblocker.

Whiteboard: [bugmon:confirmed] → [bugmon:confirmed][fuzzblocker]
Attached file testcase.html
Attachment #9174438 - Attachment is obsolete: true
Keywords: bugmon

:dholbert, due to the simplicity of the attached testcase, this issue is still occurring very frequently. Is this actually an issue or could we relax the assertion?

Flags: needinfo?(dholbert)

This is the sort of assertion that I would expect might trip from bogus values like e.g. <html style="height:nscoord_MAX"> or similar. If that was what was causing that, then I'd suggest relaxing the assertion.

However, neither of the attached testcases seem to have bogus huge values, so this is likely indicative of a real bug (probably something to do with our printPreview timing/logic, given the printPreview() invocation in both of the testcases).

So: this is probably pointing at something real that we need to fix. I can try to take a closer look in the next day or so; in the meantime, if it'd be possible for you or Tyson to generate a pernosco trace with the new testcase, that would help a lot.

(I tried to repro locally but I'm getting JS error self.printPreview is not a function so I think there's one or several pref-flips that I'm missing.)

Severity: normal → S2

ni? myself to remember to get a Pernosco trace.

Flags: needinfo?(twsmith)

A Pernosco session is available here: https://pernos.co/debug/37Pltfy0paWx9ogK_hESAA/index.html

The Pernosco session in comment 3 is from the initial test case.

Flags: needinfo?(twsmith)

Thanks.

From a quick look so far, some notes:
(1) We enter PresShell::ResizeReflowIgnoreOverride
(2) That function sets the visible area to have unconstrained height:
https://searchfox.org/mozilla-central/rev/d107bc8aeadcc816ba85cb21c1a6a1aac1d4ef9f/layout/base/PresShell.cpp#2086-2089
(3) We do the reflow
(4) We fail the assert because the reflow failed to update the visible area's unconstrained height (it's still unconstrained):
https://searchfox.org/mozilla-central/rev/d107bc8aeadcc816ba85cb21c1a6a1aac1d4ef9f/layout/base/PresShell.cpp#2129-2131

(Need to dig in a bit more, just dropping what I've got so far)

(In reply to Daniel Holbert [:dholbert] from comment #9)

(4) We fail the assert because the reflow failed to update the visible area's unconstrained height (it's still unconstrained):
https://searchfox.org/mozilla-central/rev/d107bc8aeadcc816ba85cb21c1a6a1aac1d4ef9f/layout/base/PresShell.cpp#2129-2131

Actually, we do attempt to update the visible area's unconstrained height, here:


  if (isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
    mPresContext->SetVisibleArea(boundsRelativeToTarget);
  }

https://searchfox.org/mozilla-central/rev/5ce782034b736293836a2850ab698e149ca04657/layout/base/PresShell.cpp#9670-9671

...but boundsRelativeToTarget has NS_UNCONSTRAINEDSIZE (i.e. nscoord_MAX) as its height, this doesn't end up helping.

boundsRelativeToTarget comes from the frame tree, and it's an indication that we have frames with nscoord_MAX as their height. Indeed, at this point, our frame tree looks like this (note 1073741823 is nscoord_MAX):

Viewport(-1)@75454a06baa0 (x=0, y=0, w=0, h=1073741823)
[...]
    Canvas(html)(-1) (x=0, y=0, w=0, h=1073741823)
      PageSequence(html)(-1) (x=0, y=0, w=34560, h=1073741823)
        PrintedSheet(-1) (x=1440, y=720, w=48960, h=63360)
          Page(-1) (x=0, y=0, w=48960, h=63360)
            [...]

The top few levels of frames have bogus sizes here -- width=0 and height=nscoord_MAX. The frames inside (PrintedSheet and lower) have finite/meaningful sizes, but we don't actually traverse them during this reflow; we bail out early since this is an incremental reflow and nsPageSequenceFrame doesn't attempt to do incremental reflows right now. We take this early-return:
https://searchfox.org/mozilla-central/rev/5ce782034b736293836a2850ab698e149ca04657/layout/generic/nsPageSequenceFrame.cpp#281-285
...which sets our ReflowOutput (the desired-size that ends up in our frame rect) based on the mComputedSize that was passed in via the ReflowInput.

This early-return apparently assumes that we'll have a definite computed height in our ReflowInput -- but in this particular case we clearly do not. In this case, our aReflowInput (in nsPageSequenceFrame::Reflow) has mComputedSize=(0,nscoord_MAX) (and all of the ancestor frames have that in their own aReflowInput as well). So that's what we end up putting in the ReflowOutput here.

Usually, our the ViewportFrame isn't given an unconstrained height (BSize) in its ReflowInput -- it's given a height based on the viewport size which is a physical thing on-screen with a known size.

But it can happen, per https://searchfox.org/mozilla-central/rev/5ce782034b736293836a2850ab698e149ca04657/layout/base/PresShell.cpp#9593 , if we're called from sizeToContent (which we are in this case, and which can be seen in the testcase).

So: essentially, I think this is a case where nsPageSequenceFrame's early-return needs to detect and handle this unconstrained-BSize scenario (by e.g. returning the sum of its children's frame-sizes, plus whatever additional padding/etc. space that it requires). If nsPageSequenceFrame handles this gracefully, then I think its parent frames will respect the desired size that it requests, and they'll use that for their own calculations as well. And we'll end up getting reasonable sizes in our frame tree and thus in boundsRelativeToTarget and thus in SetVisibleArea, and we'll avoid tripping this assert.

Flags: needinfo?(dholbert)

I think the simplest solution here is just to amend nsPageSequenceFrame::PopulateReflowOutput to not grow-to-fill-the-available-space (which it currently does) if ComputedBSize() is unconstrained.

nsPageSequenceFrame does a thing where it grows its desired size to fit the
AvailableISize and ComputedBSize (under the assumption that those are the
actual dimensions of our scrollport, which it wants to make maximal use of).

This behavior causes trouble when it's reflowed under the privileged
'sizeToContent' JS API. That API makes us reflow with nscoord_MAX as the
viewport's ComputedBSize(), and this nscoord_MAX value gets passed down to be
the nsPageSequenceFrame's ComputedBSize as well. When we reach the code in
question, we dutifully grow the desired size to that bogus huge value, which is
clearly wrong.

This patch addresses this issue by simply declining to grow the desired size in
the scenario where ComputedBSize() is unconstrained. This leaves us with
reasonable values for our desired size (which are actually based on the
content, which makes it the right thing to do for the purpose of a
SizeToContent() call).

Assignee: nobody → dholbert
Status: NEW → ASSIGNED
Pushed by dholbert@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/08d254139525
Make nsPageSequenceFrame gracefully handle SizeToContent calls. r=emilio
Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → 98 Branch

Bugmon Analysis
Verified bug as fixed on rev mozilla-central 20220113093336-08d254139525.
Removing bugmon keyword as no further action possible. Please review the bug and re-add the keyword for further analysis.

Status: RESOLVED → VERIFIED
Keywords: bugmon

:dholbert, since this bug contains a bisection range, could you fill (if possible) the regressed_by field?
For more information, please visit auto_nag documentation.

Flags: needinfo?(dholbert)

Sorry, bug in the bot.

Flags: needinfo?(dholbert)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: