示例#1
0
int LayoutTextControl::firstLineBoxBaseline() const {
    int result = LayoutBlock::firstLineBoxBaseline();
    if (result != -1)
        return result;

    // When the text is empty, |LayoutBlock::firstLineBoxBaseline()| cannot
    // compute the baseline because lineboxes do not exist.
    Element* innerEditor = innerEditorElement();
    if (!innerEditor || !innerEditor->layoutObject())
        return -1;

    LayoutBlock* innerEditorLayoutObject =
        toLayoutBlock(innerEditor->layoutObject());
    const SimpleFontData* fontData =
        innerEditorLayoutObject->style(true)->font().primaryFont();
    DCHECK(fontData);
    if (!fontData)
        return -1;

    LayoutUnit baseline(fontData->getFontMetrics().ascent(AlphabeticBaseline));
    for (LayoutObject* box = innerEditorLayoutObject; box && box != this;
            box = box->parent()) {
        if (box->isBox())
            baseline += toLayoutBox(box)->logicalTop();
    }
    return baseline.toInt();
}
示例#2
0
TEST_F(VisualRectMappingTest, LayoutInline) {
  document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
  setBodyInnerHTML(
      "<style>body { margin: 0; }</style>"
      "<div id='container' style='overflow: scroll; width: 50px; height: 50px'>"
      "  <span><img style='width: 20px; height: 100px'></span>"
      "  <span id=leaf></span></div>");

  LayoutBlock* container =
      toLayoutBlock(getLayoutObjectByElementId("container"));
  LayoutObject* leaf = container->lastChild();

  container->setScrollTop(LayoutUnit(50));
  LayoutRect originalRect(0, 60, 20, 80);
  LayoutRect rect = originalRect;
  EXPECT_TRUE(leaf->mapToVisualRectInAncestorSpace(container, rect));
  rect.move(-container->scrolledContentOffset());
  EXPECT_EQ(rect, LayoutRect(0, 10, 20, 80));

  rect = originalRect;
  EXPECT_TRUE(leaf->mapToVisualRectInAncestorSpace(&layoutView(), rect));
  EXPECT_EQ(rect, LayoutRect(0, 10, 20, 40));
  checkPaintInvalidationStateRectMapping(rect, originalRect, *leaf,
                                         layoutView(), layoutView());

  rect = LayoutRect(0, 60, 80, 0);
  EXPECT_TRUE(
      leaf->mapToVisualRectInAncestorSpace(container, rect, EdgeInclusive));
  rect.move(-container->scrolledContentOffset());
  EXPECT_EQ(rect, LayoutRect(0, 10, 80, 0));
}
示例#3
0
LayoutUnit SnapToLinesLayouter::computeInitialPositionAdjustment(LayoutUnit& step) const
{
    // 6. Let line position be the text track cue computed line position.
    // 7. Round line position to an integer by adding 0.5 and then flooring it.
    LayoutUnit linePosition = floorf(m_linePosition + 0.5f);

    WritingMode writingMode = m_cueBox.style()->writingMode();
    // 8. Vertical Growing Left: Add one to line position then negate it.
    if (writingMode == RightToLeftWritingMode)
        linePosition = -(linePosition + 1);

    // 9. Let position be the result of multiplying step and line position.
    LayoutUnit position = step * linePosition;

    // 10. Vertical Growing Left: Decrease position by the width of the
    // bounding box of the boxes in boxes, then increase position by step.
    if (writingMode == RightToLeftWritingMode) {
        position -= m_cueBox.size().width();
        position += step;
    }

    // 11. If line position is less than zero...
    if (linePosition < 0) {
        LayoutBlock* parentBlock = m_cueBox.containingBlock();

        // Horizontal / Vertical: ... then increase position by the
        // height / width of the video's rendering area ...
        position += blink::isHorizontalWritingMode(writingMode) ? parentBlock->size().height() : parentBlock->size().width();

        // ... and negate step.
        step = -step;
    }
    return position;
}
示例#4
0
TEST_F(VisualRectMappingTest, LayoutText) {
  setBodyInnerHTML(
      "<style>body { margin: 0; }</style>"
      "<div id='container' style='overflow: scroll; width: 50px; height: 50px'>"
      "  <span><img style='width: 20px; height: 100px'></span>"
      "  text text text text text text text"
      "</div>");

  LayoutBlock* container =
      toLayoutBlock(getLayoutObjectByElementId("container"));
  LayoutText* text = toLayoutText(container->lastChild());

  container->setScrollTop(LayoutUnit(50));
  LayoutRect originalRect(0, 60, 20, 80);
  LayoutRect rect = originalRect;
  EXPECT_TRUE(text->mapToVisualRectInAncestorSpace(container, rect));
  rect.move(-container->scrolledContentOffset());
  EXPECT_EQ(rect, LayoutRect(0, 10, 20, 80));

  rect = originalRect;
  EXPECT_TRUE(text->mapToVisualRectInAncestorSpace(&layoutView(), rect));
  EXPECT_EQ(rect, LayoutRect(0, 10, 20, 40));
  checkPaintInvalidationStateRectMapping(rect, originalRect, *text,
                                         layoutView(), layoutView());

  rect = LayoutRect(0, 60, 80, 0);
  EXPECT_TRUE(
      text->mapToVisualRectInAncestorSpace(container, rect, EdgeInclusive));
  rect.move(-container->scrolledContentOffset());
  EXPECT_EQ(rect, LayoutRect(0, 10, 80, 0));
}
示例#5
0
void findGoodTouchTargets(const IntRect& touchBoxInRootFrame, LocalFrame* mainFrame, Vector<IntRect>& goodTargets, WillBeHeapVector<RawPtrWillBeMember<Node>>& highlightNodes)
{
    goodTargets.clear();

    int touchPointPadding = ceil(std::max(touchBoxInRootFrame.width(), touchBoxInRootFrame.height()) * 0.5);

    IntPoint touchPoint = touchBoxInRootFrame.center();
    IntPoint contentsPoint = mainFrame->view()->rootFrameToContents(touchPoint);

    HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint(contentsPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ListBased, LayoutSize(touchPointPadding, touchPointPadding));
    const WillBeHeapListHashSet<RefPtrWillBeMember<Node>>& hitResults = result.listBasedTestResult();

    // Blacklist nodes that are container of disambiguated nodes.
    // It is not uncommon to have a clickable <div> that contains other clickable objects.
    // This heuristic avoids excessive disambiguation in that case.
    WillBeHeapHashSet<RawPtrWillBeMember<Node>> blackList;
    for (const auto& hitResult : hitResults) {
        // Ignore any Nodes that can't be clicked on.
        LayoutObject* layoutObject = hitResult.get()->layoutObject();
        if (!layoutObject || !hitResult.get()->willRespondToMouseClickEvents())
            continue;

        // Blacklist all of the Node's containers.
        for (LayoutBlock* container = layoutObject->containingBlock(); container; container = container->containingBlock()) {
            Node* containerNode = container->node();
            if (!containerNode)
                continue;
            if (!blackList.add(containerNode).isNewEntry)
                break;
        }
    }

    WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData> touchTargets;
    float bestScore = 0;
    for (const auto& hitResult : hitResults) {
        for (Node* node = hitResult.get(); node; node = node->parentNode()) {
            if (blackList.contains(node))
                continue;
            if (node->isDocumentNode() || isHTMLHtmlElement(*node) || isHTMLBodyElement(*node))
                break;
            if (node->willRespondToMouseClickEvents()) {
                TouchTargetData& targetData = touchTargets.add(node, TouchTargetData()).storedValue->value;
                targetData.windowBoundingBox = boundingBoxForEventNodes(node);
                targetData.score = scoreTouchTarget(touchPoint, touchPointPadding, targetData.windowBoundingBox);
                bestScore = std::max(bestScore, targetData.score);
                break;
            }
        }
    }

    for (const auto& touchTarget : touchTargets) {
        // Currently the scoring function uses the overlap area with the fat point as the score.
        // We ignore the candidates that has less than 1/2 overlap (we consider not really ambiguous enough) than the best candidate to avoid excessive popups.
        if (touchTarget.value.score < bestScore * 0.5)
            continue;
        goodTargets.append(touchTarget.value.windowBoundingBox);
        highlightNodes.append(touchTarget.key);
    }
}
IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
{
    LayoutBlock* caretPainter = caretLayoutObject(node);
    if (!caretPainter)
        return IntRect();

    LayoutRect localRect(rect);
    caretPainter->flipForWritingMode(localRect);
    return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
示例#7
0
TEST_F(VisualRectMappingTest, ContainerOverflowHidden) {
  setBodyInnerHTML(
      "<div id='container' style='position: absolute; top: 111px; left: 222px;"
      "    border: 10px solid red; overflow: hidden; width: 50px; height: "
      "80px;'>"
      "    <div id='target' style='box-shadow: 40px 20px black; width: 100px; "
      "height: 90px'></div>"
      "</div>");

  LayoutBlock* container =
      toLayoutBlock(getLayoutObjectByElementId("container"));
  EXPECT_EQ(LayoutUnit(), container->scrollTop());
  EXPECT_EQ(LayoutUnit(), container->scrollLeft());
  container->setScrollTop(LayoutUnit(27));
  container->setScrollLeft(LayoutUnit(28));
  document().view()->updateAllLifecyclePhases();

  LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
  LayoutRect targetVisualRect = target->localVisualRect();
  // 140 = width(100) + box_shadow_offset_x(40)
  // 110 = height(90) + box_shadow_offset_y(20)
  EXPECT_EQ(LayoutRect(0, 0, 140, 110), targetVisualRect);
  LayoutRect rect = targetVisualRect;
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(target, rect));
  EXPECT_EQ(LayoutRect(0, 0, 140, 110), rect);

  rect = targetVisualRect;
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(container, rect));
  // Rect is not clipped by container's overflow clip.
  EXPECT_EQ(LayoutRect(10, 10, 140, 110), rect);
}
PositionWithAffinity LayoutSVGInlineText::positionForPoint(const LayoutPoint& point)
{
    if (!firstTextBox() || !textLength())
        return createPositionWithAffinity(0, DOWNSTREAM);

    ASSERT(m_scalingFactor);
    float baseline = m_scaledFont.fontMetrics().floatAscent() / m_scalingFactor;

    LayoutBlock* containingBlock = this->containingBlock();
    ASSERT(containingBlock);

    // Map local point to absolute point, as the character origins stored in the text fragments use absolute coordinates.
    FloatPoint absolutePoint(point);
    absolutePoint.moveBy(containingBlock->location());

    float closestDistance = std::numeric_limits<float>::max();
    float closestDistancePosition = 0;
    const SVGTextFragment* closestDistanceFragment = 0;
    SVGInlineTextBox* closestDistanceBox = 0;

    AffineTransform fragmentTransform;
    for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
        if (!box->isSVGInlineTextBox())
            continue;

        SVGInlineTextBox* textBox = toSVGInlineTextBox(box);
        Vector<SVGTextFragment>& fragments = textBox->textFragments();

        unsigned textFragmentsSize = fragments.size();
        for (unsigned i = 0; i < textFragmentsSize; ++i) {
            const SVGTextFragment& fragment = fragments.at(i);
            FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height);
            fragment.buildFragmentTransform(fragmentTransform);
            if (!fragmentTransform.isIdentity())
                fragmentRect = fragmentTransform.mapRect(fragmentRect);

            float distance = 0;
            if (!fragmentRect.contains(absolutePoint))
                distance = squaredDistanceToClosestPoint(fragmentRect, absolutePoint);

            if (distance <= closestDistance) {
                closestDistance = distance;
                closestDistanceBox = textBox;
                closestDistanceFragment = &fragment;
                closestDistancePosition = fragmentRect.x();
            }
        }
    }

    if (!closestDistanceFragment)
        return createPositionWithAffinity(0, DOWNSTREAM);

    int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanceFragment, absolutePoint.x() - closestDistancePosition, true);
    return createPositionWithAffinity(offset + closestDistanceBox->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
}
void LayoutRubyRun::addChild(LayoutObject* child, LayoutObject* beforeChild)
{
    ASSERT(child);

    if (child->isRubyText()) {
        if (!beforeChild) {
            // LayoutRuby has already ascertained that we can add the child here.
            ASSERT(!hasRubyText());
            // prepend ruby texts as first child
            LayoutBlockFlow::addChild(child, firstChild());
        }  else if (beforeChild->isRubyText()) {
            // New text is inserted just before another.
            // In this case the new text takes the place of the old one, and
            // the old text goes into a new run that is inserted as next sibling.
            ASSERT(beforeChild->parent() == this);
            LayoutObject* ruby = parent();
            ASSERT(ruby->isRuby());
            LayoutBlock* newRun = staticCreateRubyRun(ruby);
            ruby->addChild(newRun, nextSibling());
            // Add the new ruby text and move the old one to the new run
            // Note: Doing it in this order and not using LayoutRubyRun's methods,
            // in order to avoid automatic removal of the ruby run in case there is no
            // other child besides the old ruby text.
            LayoutBlockFlow::addChild(child, beforeChild);
            LayoutBlockFlow::removeChild(beforeChild);
            newRun->addChild(beforeChild);
        } else if (hasRubyBase()) {
            // Insertion before a ruby base object.
            // In this case we need insert a new run before the current one and split the base.
            LayoutObject* ruby = parent();
            LayoutRubyRun* newRun = staticCreateRubyRun(ruby);
            ruby->addChild(newRun, this);
            newRun->addChild(child);

            // Make sure we don't leave anything in the percentage descendant
            // map before moving the children to the new base.
            if (hasPercentHeightDescendants())
                clearPercentHeightDescendants();
            rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
        }
    } else {
        // child is not a text -> insert it into the base
        // (append it instead if beforeChild is the ruby text)
        LayoutRubyBase* base = rubyBaseSafe();
        if (beforeChild == base)
            beforeChild = base->firstChild();
        if (beforeChild && beforeChild->isRubyText())
            beforeChild = 0;
        ASSERT(!beforeChild || beforeChild->isDescendantOf(base));
        base->addChild(child, beforeChild);
    }
}
示例#10
0
TEST_F(LayoutObjectTest, PaintingLayerOfOverflowClipLayerUnderColumnSpanAll) {
  setBodyInnerHTML(
      "<div id='columns' style='columns: 3'>"
      "  <div style='column-span: all'>"
      "    <div id='overflow-clip-layer' style='height: 100px; overflow: "
      "hidden'></div>"
      "  </div>"
      "</div>");

  LayoutObject* overflowClipObject =
      getLayoutObjectByElementId("overflow-clip-layer");
  LayoutBlock* columns = toLayoutBlock(getLayoutObjectByElementId("columns"));
  EXPECT_EQ(columns->layer(), overflowClipObject->paintingLayer());
}
TEST_F(LayoutObjectTest, MapToVisibleRectInContainerSpace)
{
    setBodyInnerHTML(
        "<div id='container' style='overflow: scroll; will-change: transform; width: 50px; height: 50px'>"
        "  <span><img style='width: 20px; height: 100px'></span>"
        "  text text text text text text text"
        "</div>");
    LayoutBlock* container = toLayoutBlock(document().getElementById("container")->layoutObject());
    LayoutText* text = toLayoutText(container->lastChild());

    container->setScrollTop(50);
    LayoutRect rect(0, 60, 20, 20);
    text->mapToVisibleRectInAncestorSpace(container, rect, nullptr);
    EXPECT_TRUE(rect == LayoutRect(0, 10, 20, 20));
}
示例#12
0
void BlockPainter::paintContinuationOutlines(const PaintInfo& info, const LayoutPoint& paintOffset)
{
    LayoutInline* inlineCont = m_layoutBlock.inlineElementContinuation();
    if (inlineCont && inlineCont->style()->hasOutline() && inlineCont->style()->visibility() == VISIBLE) {
        LayoutInline* inlineLayoutObject = toLayoutInline(inlineCont->node()->layoutObject());
        LayoutBlock* cb = m_layoutBlock.containingBlock();

        bool inlineEnclosedInSelfPaintingLayer = false;
        for (LayoutBoxModelObject* box = inlineLayoutObject; box != cb; box = box->parent()->enclosingBoxModelObject()) {
            if (box->hasSelfPaintingLayer()) {
                inlineEnclosedInSelfPaintingLayer = true;
                break;
            }
        }

        // Do not add continuations for outline painting by our containing block if we are a relative positioned
        // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on layoutObjects in its continuation table being
        // in the same layer.
        if (!inlineEnclosedInSelfPaintingLayer && !m_layoutBlock.hasLayer()) {
            cb->addContinuationWithOutline(inlineLayoutObject);
        } else if (!inlineLayoutObject->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && m_layoutBlock.hasLayer())) {
            // The outline might be painted multiple times if multiple blocks have the same inline element continuation, and the inline has a self-painting layer.
            ScopeRecorder scopeRecorder(*info.context);
            InlinePainter(*inlineLayoutObject).paintOutline(info, paintOffset - m_layoutBlock.locationOffset() + inlineLayoutObject->containingBlock()->location());
        }
    }

    ContinuationOutlineTableMap* table = continuationOutlineTable();
    if (table->isEmpty())
        return;

    OwnPtr<ListHashSet<LayoutInline*>> continuations = table->take(&m_layoutBlock);
    if (!continuations)
        return;

    LayoutPoint accumulatedPaintOffset = paintOffset;
    // Paint each continuation outline.
    ListHashSet<LayoutInline*>::iterator end = continuations->end();
    for (ListHashSet<LayoutInline*>::iterator it = continuations->begin(); it != end; ++it) {
        // Need to add in the coordinates of the intervening blocks.
        LayoutInline* flow = *it;
        LayoutBlock* block = flow->containingBlock();
        for ( ; block && block != &m_layoutBlock; block = block->containingBlock())
            accumulatedPaintOffset.moveBy(block->location());
        ASSERT(block);
        InlinePainter(*flow).paintOutline(info, accumulatedPaintOffset);
    }
}
static bool isIndependentDescendant(const LayoutBlock* layoutObject)
{
    ASSERT(isPotentialClusterRoot(layoutObject));

    LayoutBlock* containingBlock = layoutObject->containingBlock();
    return layoutObject->isLayoutView()
        || layoutObject->isFloating()
        || layoutObject->isOutOfFlowPositioned()
        || layoutObject->isTableCell()
        || layoutObject->isTableCaption()
        || layoutObject->isFlexibleBoxIncludingDeprecated()
        || (containingBlock && containingBlock->isHorizontalWritingMode() != layoutObject->isHorizontalWritingMode())
        || layoutObject->style()->isDisplayReplacedType()
        || layoutObject->isTextArea()
        || layoutObject->style()->userModify() != READ_ONLY;
}
示例#14
0
PositionWithAffinity LayoutSVGInlineText::positionForPoint(const LayoutPoint& point)
{
    if (!hasTextBoxes() || !textLength())
        return createPositionWithAffinity(0);

    ASSERT(m_scalingFactor);
    float baseline = m_scaledFont.getFontMetrics().floatAscent() / m_scalingFactor;

    LayoutBlock* containingBlock = this->containingBlock();
    ASSERT(containingBlock);

    // Map local point to absolute point, as the character origins stored in the text fragments use absolute coordinates.
    FloatPoint absolutePoint(point);
    absolutePoint.moveBy(containingBlock->location());

    float closestDistance = std::numeric_limits<float>::max();
    float closestDistancePosition = 0;
    const SVGTextFragment* closestDistanceFragment = nullptr;
    SVGInlineTextBox* closestDistanceBox = nullptr;

    for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
        if (!box->isSVGInlineTextBox())
            continue;

        SVGInlineTextBox* textBox = toSVGInlineTextBox(box);
        for (const SVGTextFragment& fragment : textBox->textFragments()) {
            FloatRect fragmentRect = fragment.boundingBox(baseline);

            float distance = 0;
            if (!fragmentRect.contains(absolutePoint))
                distance = fragmentRect.squaredDistanceTo(absolutePoint);

            if (distance <= closestDistance) {
                closestDistance = distance;
                closestDistanceBox = textBox;
                closestDistanceFragment = &fragment;
                closestDistancePosition = fragmentRect.x();
            }
        }
    }

    if (!closestDistanceFragment)
        return createPositionWithAffinity(0);

    int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanceFragment, LayoutUnit(absolutePoint.x() - closestDistancePosition), true);
    return createPositionWithAffinity(offset + closestDistanceBox->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : TextAffinity::Downstream);
}
示例#15
0
static inline LayoutRect visualOverflowRectWithPaintOffset(const LayoutBlock& layoutBox, const LayoutPoint& paintOffset)
{
    if (!RuntimeEnabledFeatures::slimmingPaintEnabled())
        return LayoutRect();
    LayoutRect bounds = layoutBox.visualOverflowRect();
    bounds.moveBy(paintOffset);
    return bounds;
}
void LayoutTextControl::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle)
{
    LayoutBlockFlow::styleDidChange(diff, oldStyle);
    Element* innerEditor = innerEditorElement();
    if (!innerEditor)
        return;
    LayoutBlock* innerEditorLayoutObject = toLayoutBlock(innerEditor->layoutObject());
    if (innerEditorLayoutObject) {
        // We may have set the width and the height in the old style in layout().
        // Reset them now to avoid getting a spurious layout hint.
        innerEditorLayoutObject->mutableStyleRef().setHeight(Length());
        innerEditorLayoutObject->mutableStyleRef().setWidth(Length());
        innerEditorLayoutObject->setStyle(createInnerEditorStyle(styleRef()));
        innerEditor->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Control));
    }
    textFormControlElement()->updatePlaceholderVisibility(false);
}
示例#17
0
void CaretBase::invalidateLocalCaretRect(Node* node, const LayoutRect& rect)
{
    LayoutBlock* caretPainter = caretLayoutObject(node);
    if (!caretPainter)
        return;

    // FIXME: Need to over-paint 1 pixel to workaround some rounding problems.
    // https://bugs.webkit.org/show_bug.cgi?id=108283
    LayoutRect inflatedRect = rect;
    inflatedRect.inflate(1);

    // FIXME: We should use mapLocalToContainer() since we know we're not un-rooted.
    mapCaretRectToCaretPainter(node->layoutObject(), caretPainter, inflatedRect);

    // FIXME: We should not allow paint invalidation out of paint invalidation state. crbug.com/457415
    DisablePaintInvalidationStateAsserts disabler;
    caretPainter->invalidatePaintRectangle(inflatedRect);
}
示例#18
0
TEST_F(VisualRectMappingTest, ContainerAndTargetDifferentFlippedWritingMode) {
  setBodyInnerHTML(
      "<div id='container' style='writing-mode: vertical-rl; position: "
      "absolute; top: 111px; left: 222px;"
      "    border: solid red; border-width: 10px 20px 30px 40px;"
      "    overflow: scroll; width: 50px; height: 80px'>"
      "    <div id='target' style='writing-mode: vertical-lr; box-shadow: 40px "
      "20px black; width: 100px; height: 90px'></div>"
      "    <div style='width: 100px; height: 100px'></div>"
      "</div>");

  LayoutBlock* container =
      toLayoutBlock(getLayoutObjectByElementId("container"));
  EXPECT_EQ(LayoutUnit(), container->scrollTop());
  // The initial scroll offset is to the left-most because of flipped blocks
  // writing mode.
  // 150 = total_layout_overflow(100 + 100) - width(50)
  EXPECT_EQ(LayoutUnit(150), container->scrollLeft());
  container->setScrollTop(LayoutUnit(7));
  container->setScrollLeft(
      LayoutUnit(142));  // Scroll to the right by 8 pixels.
  document().view()->updateAllLifecyclePhases();

  LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
  LayoutRect targetVisualRect = target->localVisualRect();
  // 140 = width(100) + box_shadow_offset_x(40)
  // 110 = height(90) + box_shadow_offset_y(20)
  EXPECT_EQ(LayoutRect(0, 0, 140, 110), targetVisualRect);

  LayoutRect rect = targetVisualRect;
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(target, rect));
  // This rect is in physical coordinates of target.
  EXPECT_EQ(LayoutRect(0, 0, 140, 110), rect);

  rect = targetVisualRect;
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(container, rect));
  rect.move(-container->scrolledContentOffset());
  // -2 = target_physical_x(100) + container_border_left(40) - scroll_left(142)
  // 3 = target_y(0) + container_border_top(10) - scroll_top(7)
  // Rect is not clipped by container's overflow clip.
  EXPECT_EQ(LayoutRect(-2, 3, 140, 110), rect);
}
void LayoutMultiColumnFlowThread::calculateColumnCountAndWidth(LayoutUnit& width, unsigned& count) const
{
    LayoutBlock* columnBlock = multiColumnBlockFlow();
    const ComputedStyle* columnStyle = columnBlock->style();
    LayoutUnit availableWidth = columnBlock->contentLogicalWidth();
    LayoutUnit columnGap = columnBlock->columnGap();
    LayoutUnit computedColumnWidth = max<LayoutUnit>(1, LayoutUnit(columnStyle->columnWidth()));
    unsigned computedColumnCount = max<int>(1, columnStyle->columnCount());

    ASSERT(!columnStyle->hasAutoColumnCount() || !columnStyle->hasAutoColumnWidth());
    if (columnStyle->hasAutoColumnWidth() && !columnStyle->hasAutoColumnCount()) {
        count = computedColumnCount;
        width = std::max<LayoutUnit>(0, (availableWidth - ((count - 1) * columnGap)) / count);
    } else if (!columnStyle->hasAutoColumnWidth() && columnStyle->hasAutoColumnCount()) {
        count = std::max<LayoutUnit>(1, (availableWidth + columnGap) / (computedColumnWidth + columnGap));
        width = ((availableWidth + columnGap) / count) - columnGap;
    } else {
        count = std::max<LayoutUnit>(std::min<LayoutUnit>(computedColumnCount, (availableWidth + columnGap) / (computedColumnWidth + columnGap)), 1);
        width = ((availableWidth + columnGap) / count) - columnGap;
    }
}
void LayoutRubyRun::removeChild(LayoutObject* child)
{
    // If the child is a ruby text, then merge the ruby base with the base of
    // the right sibling run, if possible.
    if (!beingDestroyed() && !documentBeingDestroyed() && child->isRubyText()) {
        LayoutRubyBase* base = rubyBase();
        LayoutObject* rightNeighbour = nextSibling();
        if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
            // Ruby run without a base can happen only at the first run.
            LayoutRubyRun* rightRun = toLayoutRubyRun(rightNeighbour);
            if (rightRun->hasRubyBase()) {
                LayoutRubyBase* rightBase = rightRun->rubyBaseSafe();
                // Collect all children in a single base, then swap the bases.
                rightBase->moveChildren(base);
                moveChildTo(rightRun, base);
                rightRun->moveChildTo(this, rightBase);
                // The now empty ruby base will be removed below.
                ASSERT(!rubyBase()->firstChild());
            }
        }
    }

    LayoutBlockFlow::removeChild(child);

    if (!beingDestroyed() && !documentBeingDestroyed()) {
        // Check if our base (if any) is now empty. If so, destroy it.
        LayoutBlock* base = rubyBase();
        if (base && !base->firstChild()) {
            LayoutBlockFlow::removeChild(base);
            base->deleteLineBoxTree();
            base->destroy();
        }

        // If any of the above leaves the run empty, destroy it as well.
        if (!hasRubyText() && !hasRubyBase()) {
            deleteLineBoxTree();
            destroy();
        }
    }
}
示例#21
0
TEST_F(VisualRectMappingTest, SelfFlippedWritingMode) {
  setBodyInnerHTML(
      "<div id='target' style='writing-mode: vertical-rl; box-shadow: 40px "
      "20px black;"
      "    width: 100px; height: 50px; position: absolute; top: 111px; left: "
      "222px'>"
      "</div>");

  LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
  LayoutRect visualRect = target->localVisualRect();
  // -40 = -box_shadow_offset_x(40) (with target's top-right corner as the
  // origin)
  // 140 = width(100) + box_shadow_offset_x(40)
  // 70 = height(50) + box_shadow_offset_y(20)
  EXPECT_EQ(LayoutRect(-40, 0, 140, 70), visualRect);

  LayoutRect rect = visualRect;
  // TODO(wkorman): The calls to flipForWritingMode() here and in other test
  // cases below are necessary because mapToVisualRectInAncestorSpace()
  // currently expects the input rect to be in "physical coordinates" (*not*
  // "physical coordinates with flipped block-flow direction"), see
  // LayoutBoxModelObject.h.
  target->flipForWritingMode(rect);
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(target, rect));
  // This rect is in physical coordinates of target.
  EXPECT_EQ(LayoutRect(0, 0, 140, 70), rect);

  rect = visualRect;
  target->flipForWritingMode(rect);
  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
  EXPECT_EQ(LayoutRect(222, 111, 140, 70), rect);
  checkPaintInvalidationStateRectMapping(rect, visualRect, *target,
                                         layoutView(), layoutView());
}
示例#22
0
void LayoutRubyBase::moveBlockChildren(LayoutRubyBase* toBase, LayoutObject* beforeChild)
{
    ASSERT(!childrenInline());
    ASSERT_ARG(toBase, toBase);

    if (!firstChild())
        return;

    if (toBase->childrenInline())
        toBase->makeChildrenNonInline();

    // If an anonymous block would be put next to another such block, then merge those.
    LayoutObject* firstChildHere = firstChild();
    LayoutObject* lastChildThere = toBase->lastChild();
    if (firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline()
        && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) {
        LayoutBlock* anonBlockHere = toLayoutBlock(firstChildHere);
        LayoutBlock* anonBlockThere = toLayoutBlock(lastChildThere);
        anonBlockHere->moveAllChildrenTo(anonBlockThere, anonBlockThere->children());
        anonBlockHere->deleteLineBoxTree();
        anonBlockHere->destroy();
    }
    // Move all remaining children normally.
    moveChildrenTo(toBase, firstChild(), beforeChild);
}
示例#23
0
TEST_F(VisualRectMappingTest, LayoutView) {
  document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
  setBodyInnerHTML(
      "<style>body { margin: 0; }</style>"
      "<div id=frameContainer>"
      "  <iframe src='http://test.com' width='50' height='50' "
      "frameBorder='0'></iframe>"
      "</div>");
  setChildFrameHTML(
      "<style>body { margin: 0; }</style><span><img style='width: 20px; "
      "height: 100px'></span>text text text");

  document().view()->updateAllLifecyclePhases();

  LayoutBlock* frameContainer =
      toLayoutBlock(getLayoutObjectByElementId("frameContainer"));
  LayoutBlock* frameBody =
      toLayoutBlock(childDocument().body()->layoutObject());
  LayoutText* frameText = toLayoutText(frameBody->lastChild());

  // This case involves clipping: frame height is 50, y-coordinate of result
  // rect is 13, so height should be clipped to (50 - 13) == 37.
  childDocument().view()->setScrollOffset(ScrollOffset(0, 47),
                                          ProgrammaticScroll);
  LayoutRect originalRect(4, 60, 20, 80);
  LayoutRect rect = originalRect;
  EXPECT_TRUE(frameText->mapToVisualRectInAncestorSpace(frameContainer, rect));
  EXPECT_EQ(rect, LayoutRect(4, 13, 20, 37));

  rect = originalRect;
  EXPECT_TRUE(frameText->mapToVisualRectInAncestorSpace(&layoutView(), rect));
  EXPECT_EQ(rect, LayoutRect(4, 13, 20, 37));
  checkPaintInvalidationStateRectMapping(rect, originalRect, *frameText,
                                         layoutView(), layoutView());

  rect = LayoutRect(4, 60, 0, 80);
  EXPECT_TRUE(frameText->mapToVisualRectInAncestorSpace(frameContainer, rect,
                                                        EdgeInclusive));
  EXPECT_EQ(rect, LayoutRect(4, 13, 0, 37));
}
示例#24
0
LayoutObject* LayoutFullScreen::wrapLayoutObject(LayoutObject* object, LayoutObject* parent, Document* document)
{
    // FIXME: We should not modify the structure of the layout tree during
    // layout. crbug.com/370459
    DeprecatedDisableModifyLayoutTreeStructureAsserts disabler;

    LayoutFullScreen* fullscreenLayoutObject = LayoutFullScreen::createAnonymous(document);
    fullscreenLayoutObject->updateStyle();
    if (parent && !parent->isChildAllowed(fullscreenLayoutObject, fullscreenLayoutObject->styleRef())) {
        fullscreenLayoutObject->destroy();
        return nullptr;
    }
    if (object) {
        // |object->parent()| can be null if the object is not yet attached
        // to |parent|.
        if (LayoutObject* parent = object->parent()) {
            LayoutBlock* containingBlock = object->containingBlock();
            ASSERT(containingBlock);
            // Since we are moving the |object| to a new parent |fullscreenLayoutObject|,
            // the line box tree underneath our |containingBlock| is not longer valid.
            containingBlock->deleteLineBoxTree();

            parent->addChild(fullscreenLayoutObject, object);
            object->remove();

            // Always just do a full layout to ensure that line boxes get deleted properly.
            // Because objects moved from |parent| to |fullscreenLayoutObject|, we want to
            // make new line boxes instead of leaving the old ones around.
            parent->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::Fullscreen);
            containingBlock->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::Fullscreen);
        }
        fullscreenLayoutObject->addChild(object);
        fullscreenLayoutObject->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::Fullscreen);
    }

    ASSERT(document);
    Fullscreen::from(*document).setFullScreenLayoutObject(fullscreenLayoutObject);
    return fullscreenLayoutObject;
}
示例#25
0
TEST_F(VisualRectMappingTest, LayoutViewDisplayNone) {
  document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
  setBodyInnerHTML(
      "<style>body { margin: 0; }</style>"
      "<div id=frameContainer>"
      "  <iframe id='frame' src='http://test.com' width='50' height='50' "
      "frameBorder='0'></iframe>"
      "</div>");
  setChildFrameHTML(
      "<style>body { margin: 0; }</style><div "
      "style='width:100px;height:100px;'></div>");

  document().view()->updateAllLifecyclePhases();

  LayoutBlock* frameContainer =
      toLayoutBlock(getLayoutObjectByElementId("frameContainer"));
  LayoutBlock* frameBody =
      toLayoutBlock(childDocument().body()->layoutObject());
  LayoutBlock* frameDiv = toLayoutBlock(frameBody->lastChild());

  // This part is copied from the LayoutView test, just to ensure that the
  // mapped rect is valid before display:none is set on the iframe.
  childDocument().view()->setScrollOffset(ScrollOffset(0, 47),
                                          ProgrammaticScroll);
  LayoutRect originalRect(4, 60, 20, 80);
  LayoutRect rect = originalRect;
  EXPECT_TRUE(frameDiv->mapToVisualRectInAncestorSpace(frameContainer, rect));
  EXPECT_EQ(rect, LayoutRect(4, 13, 20, 37));

  Element* frameElement = document().getElementById("frame");
  frameElement->setInlineStyleProperty(CSSPropertyDisplay, "none");
  document().view()->updateAllLifecyclePhases();

  rect = originalRect;
  EXPECT_FALSE(frameDiv->mapToVisualRectInAncestorSpace(&layoutView(), rect));
  EXPECT_EQ(rect, LayoutRect());
}
示例#26
0
TEST_F(VisualRectMappingTest,
       ContainerOfAbsoluteAbovePaintInvalidationContainer) {
  enableCompositing();
  document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);

  setBodyInnerHTML(
      "<div id='container' style='position: absolute; top: 88px; left: 99px'>"
      "    <div style='height: 222px'></div>"
      // This div makes stacking-context composited.
      "    <div style='position: absolute; width: 1px; height: 1px; "
      "background:yellow; will-change: transform'></div>"
      // This stacking context is paintInvalidationContainer of the absolute
      // child, but not a container of it.
      "    <div id='stacking-context' style='opacity: 0.9'>"
      "        <div id='absolute' style='position: absolute; top: 50px; left: "
      "50px; width: 50px; height: 50px; background: green'></div>"
      "    </div>"
      "</div>");

  LayoutBlock* stackingContext =
      toLayoutBlock(getLayoutObjectByElementId("stacking-context"));
  LayoutBlock* absolute = toLayoutBlock(getLayoutObjectByElementId("absolute"));
  LayoutBlock* container =
      toLayoutBlock(getLayoutObjectByElementId("container"));
  EXPECT_EQ(stackingContext, &absolute->containerForPaintInvalidation());
  EXPECT_EQ(container, absolute->container());

  LayoutRect absoluteVisualRect = absolute->localVisualRect();
  EXPECT_EQ(LayoutRect(0, 0, 50, 50), absoluteVisualRect);
  LayoutRect rect = absoluteVisualRect;
  EXPECT_TRUE(absolute->mapToVisualRectInAncestorSpace(stackingContext, rect));
  // -172 = top(50) - y_offset_of_stacking_context(222)
  EXPECT_EQ(LayoutRect(50, -172, 50, 50), rect);
  checkPaintInvalidationStateRectMapping(rect, absoluteVisualRect, *absolute,
                                         layoutView(), *stackingContext);
}
// Override ContainingBlockContext based on the properties of a containing block
// that was previously walked in a subtree other than the current subtree being
// walked. Used for out-of-flow positioned descendants of multi-column spanner
// when the containing block is not in the normal tree walk order.
// For example:
// <div id="columns" style="columns: 2">
//   <div id="relative" style="position: relative">
//     <div id="spanner" style="column-span: all">
//       <div id="absolute" style="position: absolute"></div>
//     </div>
//   </div>
// <div>
// The real containing block of "absolute" is "relative" which is not in the
// tree-walk order of "columns" -> spanner placeholder -> spanner -> absolute.
// Here we rebuild a ContainingBlockContext based on the properties of
// "relative" for "absolute".
static void overrideContaineringBlockContextFromRealContainingBlock(
    const LayoutBlock& containingBlock,
    PaintPropertyTreeBuilderContext::ContainingBlockContext& context) {
  const auto* properties =
      containingBlock.paintProperties()->localBorderBoxProperties();
  DCHECK(properties);

  context.transform = properties->propertyTreeState.transform();
  context.paintOffset = properties->paintOffset;
  context.shouldFlattenInheritedTransform =
      context.transform && context.transform->flattensInheritedTransform();
  context.renderingContextID =
      context.transform ? context.transform->renderingContextID() : 0;
  context.clip = properties->propertyTreeState.clip();
  context.scroll = const_cast<ScrollPaintPropertyNode*>(
      properties->propertyTreeState.scroll());
}
示例#28
0
// We need to balance the benefit of subtree optimization and the cost of subtree display items.
// Only output subtree information if the block has multiple children or multiple line boxes.
static bool needsSubtreeRecorder(const LayoutBlock& layoutBlock)
{
    return (layoutBlock.firstChild() && layoutBlock.firstChild()->nextSibling())
        || (layoutBlock.isLayoutBlockFlow() && toLayoutBlockFlow(layoutBlock).firstLineBox() && toLayoutBlockFlow(layoutBlock).firstLineBox()->nextLineBox());
}
LayoutAnalyzer::BlockScope::BlockScope(const LayoutBlock& block)
    : m_block(block)
    , m_width(block.frameRect().width())
    , m_height(block.frameRect().height())
{
}
示例#30
0
LayoutUnit LayoutTableCaption::containingBlockLogicalWidthForContent() const
{
    LayoutBlock* cb = containingBlock();
    return cb->logicalWidth();
}