Exemplo n.º 1
0
TEST_F(LayoutObjectTest, ContainingBlockAbsoluteLayoutObjectShouldBeNonStaticallyPositionedBlockAncestor)
{
    setBodyInnerHTML("<div style='position:relative'><bar style='position:absolute'></bar></div>");
    LayoutObject* containingBlocklayoutObject = document().body()->layoutObject()->slowFirstChild();
    LayoutObject* layoutObject = containingBlocklayoutObject->slowFirstChild();
    EXPECT_EQ(layoutObject->containingBlock(), containingBlocklayoutObject);
}
// Find the next layout object that has the multicol container in its containing block chain, skipping nested multicol containers.
static LayoutObject* nextInPreOrderAfterChildrenSkippingOutOfFlow(LayoutMultiColumnFlowThread* flowThread, LayoutObject* descendant)
{
    ASSERT(descendant->isDescendantOf(flowThread));
    LayoutObject* object = descendant->nextInPreOrderAfterChildren(flowThread);
    while (object) {
        // Walk through the siblings and find the first one which is either in-flow or has this
        // flow thread as its containing block flow thread.
        if (!object->isOutOfFlowPositioned())
            break;
        if (object->containingBlock()->flowThreadContainingBlock() == flowThread) {
            // This out-of-flow object is still part of the flow thread, because its containing
            // block (probably relatively positioned) is part of the flow thread.
            break;
        }
        object = object->nextInPreOrderAfterChildren(flowThread);
    }
    if (!object)
        return nullptr;
#if ENABLE(ASSERT)
    // Make sure that we didn't stumble into an inner multicol container.
    for (LayoutObject* walker = object->parent(); walker && walker != flowThread; walker = walker->parent())
        ASSERT(!isMultiColumnContainer(*walker));
#endif
    return object;
}
Exemplo n.º 3
0
TEST_F(LayoutObjectTest, ContainingBlockStaticLayoutObjectShouldBeParent)
{
    setBodyInnerHTML("<foo style='position:static'></foo>");
    LayoutObject* bodyLayoutObject = document().body()->layoutObject();
    LayoutObject* layoutObject = bodyLayoutObject->slowFirstChild();
    EXPECT_EQ(layoutObject->containingBlock(), bodyLayoutObject);
}
void LayoutMultiColumnFlowThread::skipColumnSpanner(LayoutBox* layoutObject, LayoutUnit logicalTopInFlowThread)
{
    ASSERT(layoutObject->isColumnSpanAll());
    LayoutMultiColumnSpannerPlaceholder* placeholder = layoutObject->spannerPlaceholder();
    LayoutBox* previousColumnBox = placeholder->previousSiblingMultiColumnBox();
    if (previousColumnBox && previousColumnBox->isLayoutMultiColumnSet()) {
        LayoutMultiColumnSet* columnSet = toLayoutMultiColumnSet(previousColumnBox);
        if (logicalTopInFlowThread < columnSet->logicalTopInFlowThread())
            logicalTopInFlowThread = columnSet->logicalTopInFlowThread(); // Negative margins may cause this.
        columnSet->endFlow(logicalTopInFlowThread);
    }
    LayoutBox* nextColumnBox = placeholder->nextSiblingMultiColumnBox();
    if (nextColumnBox && nextColumnBox->isLayoutMultiColumnSet()) {
        LayoutMultiColumnSet* nextSet = toLayoutMultiColumnSet(nextColumnBox);
        m_lastSetWorkedOn = nextSet;
        nextSet->beginFlow(logicalTopInFlowThread);
    }

    // We'll lay out of spanners after flow thread layout has finished (during layout of the spanner
    // placeholders). There may be containing blocks for out-of-flow positioned descendants of the
    // spanner in the flow thread, so that out-of-flow objects inside the spanner will be laid out
    // as part of flow thread layout (even if the spanner itself won't). We need to add such
    // out-of-flow positioned objects to their containing blocks now, or they'll never get laid
    // out. Since it's non-trivial to determine if we need this, and where such out-of-flow objects
    // might be, just go through the whole subtree.
    for (LayoutObject* descendant = layoutObject->slowFirstChild(); descendant; descendant = descendant->nextInPreOrder()) {
        if (descendant->isBox() && descendant->isOutOfFlowPositioned())
            descendant->containingBlock()->insertPositionedObject(toLayoutBox(descendant));
    }
}
Exemplo n.º 5
0
TEST_F(LayoutObjectTest,
       ContainingBlockAbsoluteLayoutObjectShouldBeLayoutView) {
  setBodyInnerHTML("<foo style='position:absolute'></foo>");
  LayoutObject* layoutObject =
      document().body()->layoutObject()->slowFirstChild();
  EXPECT_EQ(layoutObject->containingBlock(), layoutView());
}
Exemplo n.º 6
0
// Abs x/y position of the caret ignoring transforms.
// TODO(yosin) navigation with transforms should be smarter.
static LayoutUnit lineDirectionPointForBlockDirectionNavigationOf(
    const VisiblePosition& visiblePosition) {
  if (visiblePosition.isNull())
    return LayoutUnit();

  LayoutObject* layoutObject;
  LayoutRect localRect = localCaretRectOfPosition(
      visiblePosition.toPositionWithAffinity(), layoutObject);
  if (localRect.isEmpty() || !layoutObject)
    return LayoutUnit();

  // This ignores transforms on purpose, for now. Vertical navigation is done
  // without consulting transforms, so that 'up' in transformed text is 'up'
  // relative to the text, not absolute 'up'.
  FloatPoint caretPoint =
      layoutObject->localToAbsolute(FloatPoint(localRect.location()));
  LayoutObject* containingBlock = layoutObject->containingBlock();
  if (!containingBlock) {
    // Just use ourselves to determine the writing mode if we have no containing
    // block.
    containingBlock = layoutObject;
  }
  return LayoutUnit(containingBlock->isHorizontalWritingMode()
                        ? caretPoint.x()
                        : caretPoint.y());
}
static const DeprecatedPaintLayer* findParentLayerOnClippingContainerChain(const DeprecatedPaintLayer* layer)
{
    LayoutObject* current = layer->layoutObject();
    while (current) {
        if (current->style()->position() == FixedPosition) {
            for (current = current->parent(); current && !current->canContainFixedPositionObjects(); current = current->parent()) {
                // All types of clips apply to fixed-position descendants of other fixed-position elements.
                // Note: it's unclear whether this is what the spec says. Firefox does not clip, but Chrome does.
                if (current->style()->position() == FixedPosition && current->hasClipOrOverflowClip()) {
                    ASSERT(current->hasLayer());
                    return static_cast<const LayoutBoxModelObject*>(current)->layer();
                }

                // CSS clip applies to fixed position elements even for ancestors that are not what the
                // fixed element is positioned with respect to.
                if (current->hasClip()) {
                    ASSERT(current->hasLayer());
                    return static_cast<const LayoutBoxModelObject*>(current)->layer();
                }
            }
        } else {
            current = current->containingBlock();
        }

        if (current->hasLayer())
            return static_cast<const LayoutBoxModelObject*>(current)->layer();
        // Having clip or overflow clip forces the LayoutObject to become a layer.
        ASSERT(!current->hasClipOrOverflowClip());
    }
    ASSERT_NOT_REACHED();
    return nullptr;
}
Exemplo n.º 8
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);
    }
}
Exemplo n.º 9
0
TEST_F(LayoutObjectTest, ContainingBlockAbsoluteLayoutObjectShouldNotBeNonStaticlyPositionedInlineAncestor)
{
    setBodyInnerHTML("<span style='position:relative'><bar style='position:absolute'></bar></span>");
    LayoutObject* bodyLayoutObject = document().body()->layoutObject();
    LayoutObject* layoutObject = bodyLayoutObject->slowFirstChild()->slowFirstChild();

    // Sanity check: Make sure we don't generate anonymous objects.
    EXPECT_EQ(nullptr, bodyLayoutObject->slowFirstChild()->nextSibling());
    EXPECT_EQ(nullptr, layoutObject->slowFirstChild());
    EXPECT_EQ(nullptr, layoutObject->nextSibling());

    EXPECT_EQ(layoutObject->containingBlock(), bodyLayoutObject);
}
Exemplo n.º 10
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);
    return paintedByBlock ? toLayoutBlock(layoutObject) : layoutObject->containingBlock();
}
Exemplo n.º 11
0
int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const
{
    LayoutObject* layoutObject;
    LayoutRect localRect = localCaretRect(layoutObject);
    if (localRect.isEmpty() || !layoutObject)
        return 0;

    // This ignores transforms on purpose, for now. Vertical navigation is done
    // without consulting transforms, so that 'up' in transformed text is 'up'
    // relative to the text, not absolute 'up'.
    FloatPoint caretPoint = layoutObject->localToAbsolute(FloatPoint(localRect.location()));
    LayoutObject* containingBlock = layoutObject->containingBlock();
    if (!containingBlock)
        containingBlock = layoutObject; // Just use ourselves to determine the writing mode if we have no containing block.
    return containingBlock->isHorizontalWritingMode() ? caretPoint.x() : caretPoint.y();
}
Exemplo n.º 12
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();
}
// When processing layout objects to remove or when processing layout objects that have just been
// inserted, certain types of objects should be skipped.
static bool shouldSkipInsertedOrRemovedChild(LayoutMultiColumnFlowThread* flowThread, const LayoutObject& child)
{
    if (child.isSVG() && !child.isSVGRoot()) {
        // Don't descend into SVG objects. What's in there is of no interest, and there might even
        // be a foreignObject there with column-span:all, which doesn't apply to us.
        return true;
    }
    if (child.isLayoutFlowThread()) {
        // Found an inner flow thread. We need to skip it and its descendants.
        return true;
    }
    if (child.isLayoutMultiColumnSet() || child.isLayoutMultiColumnSpannerPlaceholder()) {
        // Column sets and spanner placeholders in a child multicol context don't affect the parent
        // flow thread.
        return true;
    }
    if (child.isOutOfFlowPositioned() && child.containingBlock()->flowThreadContainingBlock() != flowThread) {
        // Out-of-flow with its containing block on the outside of the multicol container.
        return true;
    }
    return false;
}