Skip to content

Commit

Permalink
Avoid layout roots in subtrees skipped for style recalc
Browse files Browse the repository at this point in the history
Layout roots are laid out from inner to outer in LocalFrameView. DOM
mutations may have added layout roots inside size container subtrees
before style recalc. If we decide to postpone style recalc until layout
of the size container, it means we may try to layout a root inside a
subtree skipped for style recalc. That causes a DCHECK and possibly
other issues.

This also fixes the use-after-poison issue 1365330.

Bug: 1371820, 1365330
Change-Id: Ia48890c08aacfe7b9a3e660817702abce0570564
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3934847
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1055853}
  • Loading branch information
Rune Lillesveen authored and Chromium LUCI CQ committed Oct 6, 2022
1 parent cc21ed7 commit 0f0f1e9
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 0 deletions.
14 changes: 14 additions & 0 deletions third_party/blink/renderer/core/css/style_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2756,6 +2756,7 @@ void StyleEngine::RecalcStyle(StyleRecalcChange change,
const StyleRecalcContext& style_recalc_context) {
DCHECK(GetDocument().documentElement());
ScriptForbiddenScope forbid_script;
SkipStyleRecalcScope skip_scope(*this);
CheckPseudoHasCacheScope check_pseudo_has_cache_scope(&GetDocument());
Element& root_element = style_recalc_root_.RootElement();
Element* parent = FlatTreeTraversal::ParentElement(root_element);
Expand Down Expand Up @@ -3454,4 +3455,17 @@ void StyleEngine::ReportUseOfLegacyLayoutWithContainerQueries() {
GetDocument().AddConsoleMessage(console_message);
}

bool StyleEngine::AllowSkipStyleRecalcForScope() const {
if (InContainerQueryStyleRecalc())
return true;
if (LocalFrameView* view = GetDocument().View()) {
// Existing layout roots before starting style recalc may end up being
// inside skipped subtrees if we allowed skipping. If we start out with an
// empty list, any added ones will be a result of an element style recalc,
// which means the will not be inside a skipped subtree.
return !view->IsSubtreeLayout();
}
return true;
}

} // namespace blink
24 changes: 24 additions & 0 deletions third_party/blink/renderer/core/css/style_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,20 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
base::AutoReset<bool> allow_marking_;
};

// Set up the condition for allowing to skip style recalc before starting
// RecalcStyle().
class SkipStyleRecalcScope {
STACK_ALLOCATED();

public:
explicit SkipStyleRecalcScope(StyleEngine& engine)
: allow_skip_(&engine.allow_skip_style_recalc_,
engine.AllowSkipStyleRecalcForScope()) {}

private:
base::AutoReset<bool> allow_skip_;
};

explicit StyleEngine(Document&);
~StyleEngine() override;

Expand Down Expand Up @@ -362,6 +376,10 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
bool MarkReattachAllowed() const;
bool MarkStyleDirtyAllowed() const;

// Returns true if we can skip style recalc for a size container subtree and
// resume it during layout.
bool SkipStyleRecalcAllowed() const { return allow_skip_style_recalc_; }

CSSFontSelector* GetFontSelector() { return font_selector_; }

void RemoveFontFaceRules(const HeapVector<Member<const StyleRuleFontFace>>&);
Expand Down Expand Up @@ -747,6 +765,9 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
Element& changed_element,
bool for_pseudo_change);

// Initialization value for SkipStyleRecalcScope.
bool AllowSkipStyleRecalcForScope() const;

Member<Document> document_;

// Tracks the number of currently loading top-level stylesheets. Sheets loaded
Expand Down Expand Up @@ -815,6 +836,9 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
// AllowMarkStyleDirtyFromRecalcScope.
bool allow_mark_for_reattach_from_rebuild_layout_tree_{false};

// Set to true if we are allowed to skip recalc for a size container subtree.
bool allow_skip_style_recalc_{false};

// Set to true if we have detected an element which is a size query container
// rendered in legacy layout.
bool legacy_layout_query_container_{false};
Expand Down
4 changes: 4 additions & 0 deletions third_party/blink/renderer/core/dom/element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3846,6 +3846,10 @@ bool Element::SkipStyleRecalcForContainer(
const ComputedStyle& style,
const StyleRecalcChange& child_change) {
DCHECK(RuntimeEnabledFeatures::CSSContainerSkipStyleRecalcEnabled());

if (!GetDocument().GetStyleEngine().SkipStyleRecalcAllowed())
return false;

if (!child_change.TraversePseudoElements(*this)) {
// If none of the children or pseudo elements need to be traversed for style
// recalc, there is no point in marking the subtree as skipped.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html class="reftest-wait">
<link rel="help" href="https://crbug.com/1371820">
<style>
body, div, img { container-type: size; }
</style>
<p>Pass if no crash.</p>
<div id="div"><img id="img" alt="a"></div>
<script>
requestAnimationFrame(() => requestAnimationFrame(() => {
// Adds a layout root inside the div size container.
img.alt = img.src = "b";
// Marks div size container for layout which skips style recalc for the sub-tree.
div.style.width = "500px";
document.documentElement.classList.remove("reftest-wait");
}));
</script>

0 comments on commit 0f0f1e9

Please sign in to comment.