blob: 2c2b427c1b83424539a95fb11f20ea816a6262fd [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_WHITESPACE_ATTACHER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_WHITESPACE_ATTACHER_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/heap/member.h"
namespace blink {
class Element;
class LayoutObject;
class Text;
// The WhitespaceAttacher is used during the layout tree rebuild to lazily re-
// attach whitespace LayoutObjects when necessary. For more details about white-
// space LayoutObjects, see the WhitespaceLayoutObjects.md file in this
// directory.
//
// As RebuildLayoutTree walks from last to first child, we track the last text
// node, or the last skipped display:contents element we have seen. These are
// reset to null as soon as we encounter an in-flow element.
//
// If the tracked text node needed a (re-)attach, we call
// ReattachWhitespaceSiblings once we visit or re-attach the first preceding
// in-flow.
//
// If we re-attach a preceding in-flow, we also call ReattachWhitespaceSiblings
// since the need for a succeeding whitespace LayoutObject may change.
class CORE_EXPORT WhitespaceAttacher {
STACK_ALLOCATED();
public:
WhitespaceAttacher() = default;
~WhitespaceAttacher();
void DidVisitText(Text*);
void DidReattachText(Text*);
void DidVisitElement(Element*);
void DidReattachElement(Element*, LayoutObject*);
bool LastTextNodeNeedsReattach() const {
return last_text_node_needs_reattach_;
}
void SetReattachAllWhitespaceNodes() {
reattach_all_whitespace_nodes_ = true;
}
bool TraverseIntoDisplayContents() const {
return last_text_node_needs_reattach_ || reattach_all_whitespace_nodes_;
}
private:
void DidReattach(Node*, LayoutObject*);
void ReattachWhitespaceSiblings(LayoutObject* previous_in_flow);
void ForceLastTextNodeNeedsReattach();
void UpdateLastTextNodeFromDisplayContents();
void SetLastTextNode(Text* text) {
last_display_contents_ = nullptr;
last_text_node_ = text;
if (!text)
last_text_node_needs_reattach_ = false;
}
// If we encounter a display:contents, without traversing its flat tree
// children during layout tree rebuild, we store that element and start
// traversing it for text nodes as needed if we re-attach a preceding node
// without encountering a text node or an in-flow element first.
//
// If there is already a text node which needs re-attachment, we traverse into
// the display:contents element instead as we need to find the last in-flow
// descendant of that subtree which is used to check if the re-attached text
// node needs a LayoutText or not.
//
// Invariants:
// DCHECK(!last_display_contents_ || !last_text_node_needs_reattach_)
// DCHECK(last_text_node_ || !last_text_node_needs_reattach_)
Member<Element> last_display_contents_ = nullptr;
// The last text node we've visited during rebuild for this attacher.
Member<Text> last_text_node_ = nullptr;
// Set to true if we need to re-attach last_text_node_ when:
// 1. We visiting a previous in-flow sibling, or
// 2. We get to the start of the sibling list during the rebuild.
bool last_text_node_needs_reattach_ = false;
// Removing a node from the DOM may cause the need for a whitespace
// LayoutObject to be attached or detached. When the display type changes on
// an element, the WhitespaceAttacher keeps track of the last text node seen
// and re-attaches whitespaces as necessary during the tree walk. When
// removing an element from the tree, we can not selectively track which
// whitespace nodes which needs to be checked for re-attachment. Thus, we need
// to check all LayoutText children of the layout tree parent of the removed
// node for re-attachment. Set to true when all whitespace children needs to
// be checked for re-attachement.
bool reattach_all_whitespace_nodes_ = false;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_WHITESPACE_ATTACHER_H_