Example #1
0
bool Text::textLayoutObjectIsNeeded(const ComputedStyle& style, const LayoutObject& parent)
{
    if (!parent.canHaveChildren())
        return false;

    if (isEditingText())
        return true;

    if (!length())
        return false;

    if (style.display() == NONE)
        return false;

    if (!containsOnlyWhitespace())
        return true;

    if (!canHaveWhitespaceChildren(parent, this))
        return false;

    // pre-wrap in SVG never makes layoutObject.
    if (style.whiteSpace() == PRE_WRAP && parent.isSVG())
        return false;

    // pre/pre-wrap/-bb-pre-wrap-text/pre-line always make layoutObjects.
    if (style.preserveNewline())
        return true;

    // childNeedsDistributionRecalc() here is rare, only happens JS calling surroundContents() etc. from DOMNodeInsertedIntoDocument etc.
    if (document().childNeedsDistributionRecalc())
        return true;

    const LayoutObject* prev = LayoutTreeBuilderTraversal::previousSiblingLayoutObject(*this);
    if (prev && prev->isBR()) // <span><br/> <br/></span>
        return false;

    if (parent.isLayoutInline()) {
        // <span><div/> <div/></span>
        if (prev && !prev->isInline() && !prev->isOutOfFlowPositioned())
            return false;
    } else {
        if (parent.isLayoutBlock() && !parent.childrenInline() && (!prev || !prev->isInline()))
            return false;

        // Avoiding creation of a layoutObject for the text node is a non-essential memory optimization.
        // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit.
        unsigned maxSiblingsToVisit = 50;

        LayoutObject* first = parent.slowFirstChild();
        while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--)
            first = first->nextSibling();
        if (!first || first == layoutObject() || LayoutTreeBuilderTraversal::nextSiblingLayoutObject(*this) == first) {
            // Whitespace at the start of a block just goes away.  Don't even
            // make a layout object for this text.
            return false;
        }
    }
    return true;
}
float TextAutosizer::inflate(LayoutObject* parent, InflateBehavior behavior, float multiplier)
{
    Cluster* cluster = currentCluster();
    bool hasTextChild = false;

    LayoutObject* child = nullptr;
    if (parent->isLayoutBlock() && (parent->childrenInline() || behavior == DescendToInnerBlocks))
        child = toLayoutBlock(parent)->firstChild();
    else if (parent->isLayoutInline())
        child = toLayoutInline(parent)->firstChild();

    while (child) {
        if (child->isText()) {
            hasTextChild = true;
            // We only calculate this multiplier on-demand to ensure the parent block of this text
            // has entered layout.
            if (!multiplier)
                multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster);
            applyMultiplier(child, multiplier);

            // FIXME: Investigate why MarkOnlyThis is sufficient.
            if (parent->isLayoutInline())
                child->setPreferredLogicalWidthsDirty(MarkOnlyThis);
        } else if (child->isLayoutInline()) {
            multiplier = inflate(child, behavior, multiplier);
        } else if (child->isLayoutBlock() && behavior == DescendToInnerBlocks
            && !classifyBlock(child, INDEPENDENT | EXPLICIT_WIDTH | SUPPRESSING)) {
            multiplier = inflate(child, behavior, multiplier);
        }
        child = child->nextSibling();
    }

    if (hasTextChild) {
        applyMultiplier(parent, multiplier); // Parent handles line spacing.
    } else if (!parent->isListItem()) {
        // For consistency, a block with no immediate text child should always have a
        // multiplier of 1.
        applyMultiplier(parent, 1);
    }

    if (parent->isListItem()) {
        float multiplier = clusterMultiplier(cluster);
        applyMultiplier(parent, multiplier);

        // The list item has to be treated special because we can have a tree such that you have
        // a list item for a form inside it. The list marker then ends up inside the form and when
        // we try to get the clusterMultiplier we have the wrong cluster root to work from and get
        // the wrong value.
        LayoutListItem* item = toLayoutListItem(parent);
        if (LayoutListMarker* marker = item->marker()) {
            applyMultiplier(marker, multiplier);
            marker->setPreferredLogicalWidthsDirty(MarkOnlyThis);
        }
    }

    return multiplier;
}
TEST_F(PaintContainmentTest, InlinePaintContainment)
{
    setBodyInnerHTML("<div><span id='test' style='contain: paint'>Foo</span></div>");
    Element* span = document().getElementById(AtomicString("test"));
    ASSERT(span);
    // The inline should have been coerced into a block in StyleAdjuster.
    LayoutObject* obj = span->layoutObject();
    ASSERT(obj && obj->isLayoutBlock());
    LayoutBlock& layoutBlock = toLayoutBlock(*obj);
    checkIsClippingStackingContextAndContainer(layoutBlock);
}
TEST_F(PaintContainmentTest, BlockPaintContainment)
{
    setBodyInnerHTML("<div id='div' style='contain: paint'></div>");
    Element* div = document().getElementById(AtomicString("div"));
    ASSERT(div);
    LayoutObject* obj = div->layoutObject();
    ASSERT(obj && obj->isLayoutBlock());
    LayoutBlock& block = toLayoutBlock(*obj);
    EXPECT_TRUE(block.createsNewFormattingContext());
    EXPECT_FALSE(block.canBeScrolledAndHasScrollableArea());
    checkIsClippingStackingContextAndContainer(block);
}
LayoutBlock* CaretBase::caretLayoutObject(Node* node)
{
    if (!node)
        return nullptr;

    LayoutObject* layoutObject = node->layoutObject();
    if (!layoutObject)
        return nullptr;

    // if caretNode is a block and caret is inside it then caret should be painted by that block
    bool paintedByBlock = layoutObject->isLayoutBlock() && caretRendersInsideNode(node);
    return paintedByBlock ? toLayoutBlock(layoutObject) : layoutObject->containingBlock();
}
bool TextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const LayoutBlock* widthProvider)
{
    if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText)
        return cluster->m_hasEnoughTextToAutosize == HasEnoughText;

    const LayoutBlock* root = cluster->m_root;
    if (!widthProvider)
        widthProvider = clusterWidthProvider(root);

    // TextAreas and user-modifiable areas get a free pass to autosize regardless of text content.
    if (root->isTextArea() || (root->style() && root->style()->userModify() != READ_ONLY)) {
        cluster->m_hasEnoughTextToAutosize = HasEnoughText;
        return true;
    }

    if (cluster->m_flags & SUPPRESSING) {
        cluster->m_hasEnoughTextToAutosize = NotEnoughText;
        return false;
    }

    // 4 lines of text is considered enough to autosize.
    float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4;

    float length = 0;
    LayoutObject* descendant = root->firstChild();
    while (descendant) {
        if (descendant->isLayoutBlock()) {
            if (classifyBlock(descendant, INDEPENDENT | SUPPRESSING)) {
                descendant = descendant->nextInPreOrderAfterChildren(root);
                continue;
            }
        } else if (descendant->isText()) {
            // Note: Using text().stripWhiteSpace().length() instead of resolvedTextLength() because
            // the lineboxes will not be built until layout. These values can be different.
            // Note: This is an approximation assuming each character is 1em wide.
            length += toLayoutText(descendant)->text().stripWhiteSpace().length() * descendant->style()->specifiedFontSize();

            if (length >= minimumTextLengthToAutosize) {
                cluster->m_hasEnoughTextToAutosize = HasEnoughText;
                return true;
            }
        }
        descendant = descendant->nextInPreOrder(root);
    }

    cluster->m_hasEnoughTextToAutosize = NotEnoughText;
    return false;
}
Example #7
0
LayoutBlock* CaretBase::caretLayoutObject(Node* node) {
    if (!node)
        return nullptr;

    LayoutObject* layoutObject = node->layoutObject();
    if (!layoutObject)
        return nullptr;

    // if caretNode is a block and caret is inside it then caret should be painted
    // by that block
    bool paintedByBlock =
        layoutObject->isLayoutBlock() && caretRendersInsideNode(node);
    // TODO(yoichio): This function is called at least
    // DocumentLifeCycle::LayoutClean but caretRendersInsideNode above can
    // layout. Thus |node->layoutObject()| can be changed then this is bad
    // design. We should make caret painting algorithm clean.
    CHECK_EQ(layoutObject, node->layoutObject())
            << "Layout tree should not changed";
    return paintedByBlock ? toLayoutBlock(layoutObject)
           : layoutObject->containingBlock();
}