Bug 1878805 - Fix a crash which a caret frame misses a MarkNeedsDisplayItemRebuild() call r=emilio

Generally, we mark a caret frame for display first, and then
nsCaret tracks this frame in nsCaret::SchedulePaint to call
MarkNeedsDisplayItemRebuild() accordingly. However, it's possible
for nsCaret::SchedulePaint fails to find the caret frame (i.e, selection changes),
so we end up not calling MarkNeedsDisplayItemRebuild() on this frame.

This patch improves this case by manually setting this caret frame
to nsCaret, so that it's always tracked.

Differential Revision: https://phabricator.services.mozilla.com/D200880
This commit is contained in:
Sean Feng 2024-02-08 14:45:24 +00:00
parent ce3defc7e2
commit e4f25caa82
5 changed files with 49 additions and 2 deletions

View File

@ -413,7 +413,7 @@ void nsCaret::SchedulePaint(Selection* aSelection) {
nsIFrame* frame = GetFrameAndOffset(selection, mOverrideContent,
mOverrideOffset, &frameOffset);
if (!frame) {
mLastCaretFrame = nullptr;
SetLastCaretFrame(nullptr);
return;
}
@ -421,7 +421,7 @@ void nsCaret::SchedulePaint(Selection* aSelection) {
frame = cb;
}
mLastCaretFrame = frame;
SetLastCaretFrame(frame);
frame->SchedulePaint();
}

View File

@ -197,6 +197,10 @@ class nsCaret final : public nsISelectionListener {
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void SetLastCaretFrame(nsIFrame* aLastCaretFrame) {
mLastCaretFrame = aLastCaretFrame;
}
protected:
static void CaretBlinkCallback(nsITimer* aTimer, void* aClosure);

View File

@ -0,0 +1,34 @@
<html class="reftest-wait">
<style>
* { translate: 0px }
</style>
<script>
addEventListener("MozReftestInvalidate", () => {
o.setAttribute("volume", "-68")
document.designMode = "on"
document.execCommand("selectAll", false)
document.execCommand("insertText", false, "AAA")
requestAnimationFrame(function() {
requestAnimationFrame(function() {
document.documentElement.classList.remove("reftest-wait");
});
});
})
function go() {
window.find("A")
let a = new MutationObserver(() => {
document.designMode = "off"
window.requestAnimationFrame(() => {
window.parent.document.designMode = "on"
document.execCommand("indent", false)
document.designMode = "off"
})
document.onselectionchange = go
})
a.observe(o, {attributeOldValue: false})
}
</script>
<body>
<input autofocus="" onfocusin="go()">
<bgsound id="o">
</html>

View File

@ -823,3 +823,4 @@ load 1816574.html
load 1821603.html
load 1822118.html
load 1825434.html
load 1878805.html

View File

@ -1119,6 +1119,14 @@ void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame,
// instead.
if (state->mCaretFrame) {
MOZ_ASSERT(state->mCaretFrame->PresShell() == state->mPresShell);
// Generally, nsCaret sets the last caret frame in
// nsCaret::SchedulePaint to call MarkNeedsDisplayItemRebuild()
// on the frame accordingly, so we shouldn't need do to this manually.
// However, it's possible for nsCaret::SchedulePaint fails to find
// the caret frame (i.e, selection changes), we end up not calling
// MarkNeedsDisplayItemRebuild() on this frame. This is not good,
// so we are manually setting the last caret frame here.
caret->SetLastCaretFrame(state->mCaretFrame);
MarkFrameForDisplay(state->mCaretFrame, aReferenceFrame);
}
}