LayoutState::LayoutState(LayoutBox& layoutObject, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged, bool containingBlockLogicalWidthChanged)
    : m_containingBlockLogicalWidthChanged(containingBlockLogicalWidthChanged)
    , m_next(layoutObject.view()->layoutState())
    , m_layoutObject(layoutObject)
{
    if (layoutObject.isLayoutFlowThread())
        m_flowThread = toLayoutFlowThread(&layoutObject);
    else if (!layoutObject.isOutOfFlowPositioned() && !layoutObject.isColumnSpanAll())
        m_flowThread = m_next->flowThread();
    else
        m_flowThread = nullptr;
    layoutObject.view()->pushLayoutState(*this);
    bool fixed = layoutObject.isOutOfFlowPositioned() && layoutObject.style()->position() == FixedPosition;
    if (fixed) {
        // FIXME: This doesn't work correctly with transforms.
        FloatPoint fixedOffset = layoutObject.view()->localToAbsolute(FloatPoint(), IsFixed);
        m_layoutOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset;
    } else {
        m_layoutOffset = m_next->m_layoutOffset + offset;
    }

    if (layoutObject.isOutOfFlowPositioned() && !fixed) {
        if (LayoutObject* container = layoutObject.container()) {
            if (container->style()->hasInFlowPosition() && container->isLayoutInline())
                m_layoutOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(layoutObject);
        }
    }
    // If we establish a new page height, then cache the offset to the top of the first page.
    // We can compare this later on to figure out what part of the page we're actually on,
    if (pageLogicalHeight || layoutObject.isLayoutFlowThread()) {
        m_pageLogicalHeight = pageLogicalHeight;
        bool isFlipped = layoutObject.style()->isFlippedBlocksWritingMode();
        m_pageOffset = LayoutSize(m_layoutOffset.width() + (!isFlipped ? layoutObject.borderLeft() + layoutObject.paddingLeft() : layoutObject.borderRight() + layoutObject.paddingRight()),
            m_layoutOffset.height() + (!isFlipped ? layoutObject.borderTop() + layoutObject.paddingTop() : layoutObject.borderBottom() + layoutObject.paddingBottom()));
        m_pageLogicalHeightChanged = pageLogicalHeightChanged;
        m_isPaginated = true;
    } else if (m_layoutObject.isSVG() && !m_layoutObject.isSVGRoot()) {
        // Pagination inside SVG is not allowed.
        m_flowThread = nullptr;
        m_pageLogicalHeightChanged = false;
        m_isPaginated = false;
    } else {
        // If we don't establish a new page height, then propagate the old page height and offset down.
        m_pageLogicalHeight = m_next->m_pageLogicalHeight;
        m_pageLogicalHeightChanged = m_next->m_pageLogicalHeightChanged;
        m_pageOffset = m_next->m_pageOffset;

        // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and
        // writing mode roots.
        if (layoutObject.isUnsplittableForPagination()) {
            m_flowThread = nullptr;
            m_pageLogicalHeight = 0;
            m_isPaginated = false;
        } else {
            m_isPaginated = m_pageLogicalHeight || m_flowThread;
        }
    }

    // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present.
}
Example #2
0
static inline InlineFlowBox* flowBoxForLayoutObject(LayoutObject* layoutObject)
{
    if (!layoutObject)
        return nullptr;

    if (layoutObject->isLayoutBlock()) {
        // If we're given a block element, it has to be a LayoutSVGText.
        ASSERT(layoutObject->isSVGText());
        LayoutBlockFlow* layoutBlockFlow = toLayoutBlockFlow(layoutObject);

        // LayoutSVGText only ever contains a single line box.
        InlineFlowBox* flowBox = layoutBlockFlow->firstLineBox();
        ASSERT(flowBox == layoutBlockFlow->lastLineBox());
        return flowBox;
    }

    if (layoutObject->isLayoutInline()) {
        // We're given a LayoutSVGInline or objects that derive from it (LayoutSVGTSpan / LayoutSVGTextPath)
        LayoutInline* layoutInline = toLayoutInline(layoutObject);

        // LayoutSVGInline only ever contains a single line box.
        InlineFlowBox* flowBox = layoutInline->firstLineBox();
        ASSERT(flowBox == layoutInline->lastLineBox());
        return flowBox;
    }

    ASSERT_NOT_REACHED();
    return nullptr;
}
PaintInvalidationState::PaintInvalidationState(PaintInvalidationState& next, LayoutBoxModelObject& layoutObject, const LayoutBoxModelObject& paintInvalidationContainer)
    : m_clipped(false)
    , m_cachedOffsetsEnabled(true)
    , m_forcedSubtreeInvalidationWithinContainer(next.m_forcedSubtreeInvalidationWithinContainer)
    , m_forcedSubtreeInvalidationRectUpdateWithinContainer(next.m_forcedSubtreeInvalidationRectUpdateWithinContainer)
    , m_viewClippingAndScrollOffsetDisabled(false)
    , m_paintInvalidationContainer(paintInvalidationContainer)
    , m_pendingDelayedPaintInvalidations(next.pendingDelayedPaintInvalidationTargets())
    , m_enclosingSelfPaintingLayer(next.enclosingSelfPaintingLayer(layoutObject))
{
    // FIXME: SVG could probably benefit from a stack-based optimization like html does. crbug.com/391054
    bool establishesPaintInvalidationContainer = layoutObject == m_paintInvalidationContainer;
    bool fixed = layoutObject.style()->position() == FixedPosition;

    if (!layoutObject.supportsPaintInvalidationStateCachedOffsets() || !next.m_cachedOffsetsEnabled)
        m_cachedOffsetsEnabled = false;
    if (establishesPaintInvalidationContainer) {
        // When we hit a new paint invalidation container, we don't need to
        // continue forcing a check for paint invalidation, since we're
        // descending into a different invalidation container. (For instance if
        // our parents were moved, the entire container will just move.)
        m_forcedSubtreeInvalidationWithinContainer = false;
    } else {
        if (m_cachedOffsetsEnabled) {
            if (fixed) {
                FloatPoint fixedOffset = layoutObject.localToAncestorPoint(FloatPoint(), &m_paintInvalidationContainer, TraverseDocumentBoundaries);
                m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y());
            } else {
                LayoutSize offset = layoutObject.isBox() && !layoutObject.isTableRow() ? toLayoutBox(layoutObject).locationOffset() : LayoutSize();
                m_paintOffset = next.m_paintOffset + offset;
            }

            if (layoutObject.isOutOfFlowPositioned() && !fixed) {
                if (LayoutObject* container = layoutObject.container()) {
                    if (container->style()->hasInFlowPosition() && container->isLayoutInline())
                        m_paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(toLayoutBox(layoutObject));
                }
            }

            if (layoutObject.style()->hasInFlowPosition() && layoutObject.hasLayer())
                m_paintOffset += layoutObject.layer()->offsetForInFlowPosition();
        }

        m_clipped = !fixed && next.m_clipped;
        if (m_clipped)
            m_clipRect = next.m_clipRect;
    }

    if (m_cachedOffsetsEnabled && layoutObject.isSVGRoot()) {
        const LayoutSVGRoot& svgRoot = toLayoutSVGRoot(layoutObject);
        m_svgTransform = AffineTransform(svgRoot.localToBorderBoxTransform());
        if (svgRoot.shouldApplyViewportClip())
            addClipRectRelativeToPaintOffset(LayoutSize(svgRoot.pixelSnappedSize()));
    }

    applyClipIfNeeded(layoutObject);

    // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present.
}
Example #4
0
LayoutState::LayoutState(LayoutBox& layoutObject,
                         bool containingBlockLogicalWidthChanged)
    : m_containingBlockLogicalWidthChanged(containingBlockLogicalWidthChanged),
      m_next(layoutObject.view()->layoutState()),
      m_layoutObject(layoutObject) {
  if (layoutObject.isLayoutFlowThread())
    m_flowThread = toLayoutFlowThread(&layoutObject);
  else
    m_flowThread = m_next->flowThread();
  m_paginationStateChanged = m_next->m_paginationStateChanged;
  layoutObject.view()->pushLayoutState(*this);
  m_heightOffsetForTableHeaders = m_next->heightOffsetForTableHeaders();

  if (layoutObject.isLayoutFlowThread()) {
    // Entering a new pagination context.
    m_paginationOffset = LayoutSize();
    m_isPaginated = true;
    return;
  }

  // Disable pagination for objects we don't support. For now this includes
  // overflow:scroll/auto, inline blocks and writing mode roots. Additionally,
  // pagination inside SVG is not allowed.
  if (layoutObject.getPaginationBreakability() == LayoutBox::ForbidBreaks ||
      (m_layoutObject.isSVG() && !m_layoutObject.isSVGRoot())) {
    m_flowThread = nullptr;
    m_isPaginated = false;
    return;
  }

  m_isPaginated = m_next->m_isPaginated;
  if (!m_isPaginated)
    return;

  // Now adjust the pagination offset, so that we can easily figure out how far
  // away we are from the start of the pagination context.
  m_paginationOffset = m_next->m_paginationOffset;
  bool fixed = layoutObject.isOutOfFlowPositioned() &&
               layoutObject.style()->position() == FixedPosition;
  if (fixed)
    return;
  m_paginationOffset =
      m_next->m_paginationOffset + layoutObject.locationOffset();
  if (!layoutObject.isOutOfFlowPositioned())
    return;
  if (LayoutObject* container = layoutObject.container()) {
    if (container->style()->hasInFlowPosition() &&
        container->isLayoutInline()) {
      m_paginationOffset +=
          toLayoutInline(container)->offsetForInFlowPositionedInline(
              layoutObject);
    }
  }

  // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if
  // present.
}
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;
}
TracedLayoutObject::TracedLayoutObject(const LayoutObject& object)
    : m_address((unsigned long) &object)
    , m_isAnonymous(object.isAnonymous())
    , m_isPositioned(object.isOutOfFlowPositioned())
    , m_isRelPositioned(object.isRelPositioned())
    , m_isStickyPositioned(object.isStickyPositioned())
    , m_isFloating(object.isFloating())
    , m_selfNeeds(object.selfNeedsLayout())
    , m_positionedMovement(object.needsPositionedMovementLayout())
    , m_childNeeds(object.normalChildNeedsLayout())
    , m_posChildNeeds(object.posChildNeedsLayout())
    , m_isTableCell(object.isTableCell())
    , m_name(String(object.name()).isolatedCopy())
    , m_absRect(object.absoluteBoundingBoxRect())
{
    if (Node* node = object.node()) {
        m_tag = String(node->nodeName()).isolatedCopy();
        if (node->isElementNode()) {
            Element& element = toElement(*node);
            if (element.hasID())
                m_id = String(element.getIdAttribute()).isolatedCopy();
            if (element.hasClass()) {
                for (size_t i = 0; i < element.classNames().size(); ++i) {
                    m_classNames.append(
                        String(element.classNames()[i]).isolatedCopy());
                }
            }
        }
    }

    // FIXME: When the fixmes in LayoutTreeAsText::writeLayoutObject() are
    // fixed, deduplicate it with this.
    if (object.isText()) {
        m_rect = LayoutRect(toLayoutText(object).linesBoundingBox());
    } else if (object.isLayoutInline()) {
        m_rect = LayoutRect(toLayoutInline(object).linesBoundingBox());
    } else if (object.isBox()) {
        m_rect = toLayoutBox(&object)->frameRect();
    }

    if (m_isTableCell) {
        const LayoutTableCell& c = toLayoutTableCell(object);
        if (c.row() && c.row()->rowIndexWasSet() && c.hasCol()
            && (!c.row()->section() || !c.row()->section()->needsCellRecalc())) {
            m_row = c.rowIndex();
            m_col = c.col();
            m_rowSpan = c.rowSpan();
            m_colSpan = c.colSpan();
        }
    }

    for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) {
        m_children.append(adoptRef(new TracedLayoutObject(*child)));
    }
}
static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBoxModelObject())
        return;

    const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);

    switch (object.styleRef().position()) {
    case StaticPosition:
        break;
    case RelativePosition:
        context.paintOffset += boxModelObject.offsetForInFlowPosition();
        break;
    case AbsolutePosition: {
        context.currentTransform = context.transformForAbsolutePosition;
        context.paintOffset = context.paintOffsetForAbsolutePosition;

        // Absolutely positioned content in an inline should be positioned relative to the inline.
        const LayoutObject* container = context.containerForAbsolutePosition;
        if (container && container->isInFlowPositioned() && container->isLayoutInline()) {
            DCHECK(object.isBox());
            context.paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(toLayoutBox(object));
        }

        context.currentClip = context.clipForAbsolutePosition;
        break;
    }
    case StickyPosition:
        context.paintOffset += boxModelObject.offsetForInFlowPosition();
        break;
    case FixedPosition:
        context.currentTransform = context.transformForFixedPosition;
        context.paintOffset = context.paintOffsetForFixedPosition;
        context.currentClip = context.clipForFixedPosition;
        break;
    default:
        ASSERT_NOT_REACHED();
    }
    if (boxModelObject.isBox() && (!boxModelObject.isSVG() || boxModelObject.isSVGRoot())) {
        // TODO(pdr): Several calls in this function walk back up the tree to calculate containers
        // (e.g., topLeftLocation, offsetForInFlowPosition*). The containing block and other
        // containers can be stored on PaintPropertyTreeBuilderContext instead of recomputing them.
        context.paintOffset.moveBy(toLayoutBox(boxModelObject).topLeftLocation());
        // This is a weird quirk that table cells paint as children of table rows,
        // but their location have the row's location baked-in.
        // Similar adjustment is done in LayoutTableCell::offsetFromContainer().
        if (boxModelObject.isTableCell()) {
            LayoutObject* parentRow = boxModelObject.parent();
            ASSERT(parentRow && parentRow->isTableRow());
            context.paintOffset.moveBy(-toLayoutBox(parentRow)->topLeftLocation());
        }
    }
}
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 void deriveBorderBoxFromContainerContext(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  if (!object.isBoxModelObject())
    return;

  const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);

  switch (object.styleRef().position()) {
    case StaticPosition:
      break;
    case RelativePosition:
      context.current.paintOffset += boxModelObject.offsetForInFlowPosition();
      break;
    case AbsolutePosition: {
      if (context.isUnderMultiColumnSpanner) {
        const LayoutObject* container = boxModelObject.container();
        if (container != context.containerForAbsolutePosition) {
          // The container of the absolute-position is not in the normal tree-
          // walk order.
          context.containerForAbsolutePosition =
              toLayoutBoxModelObject(container);
          // The container is never a LayoutInline. In the example above
          // overrideContaineringBlockContextFromRealContainingBlock(), if we
          // change the container to an inline, there will be an anonymous
          // blocks created because the spanner is always a block.
          overrideContaineringBlockContextFromRealContainingBlock(
              toLayoutBlock(*container), context.current);
        }
      } else {
        DCHECK(context.containerForAbsolutePosition ==
               boxModelObject.container());
        context.current = context.absolutePosition;
      }

      // Absolutely positioned content in an inline should be positioned
      // relative to the inline.
      const LayoutObject* container = context.containerForAbsolutePosition;
      if (container && container->isInFlowPositioned() &&
          container->isLayoutInline()) {
        DCHECK(object.isBox());
        context.current.paintOffset +=
            toLayoutInline(container)->offsetForInFlowPositionedInline(
                toLayoutBox(object));
      }
      break;
    }
    case StickyPosition:
      context.current.paintOffset += boxModelObject.offsetForInFlowPosition();
      break;
    case FixedPosition:
      if (context.isUnderMultiColumnSpanner) {
        // The container of the fixed-position object may or may not be in the
        // normal tree-walk order.
        overrideContaineringBlockContextFromRealContainingBlock(
            toLayoutBlock(*boxModelObject.container()), context.current);
      } else {
        context.current = context.fixedPosition;
      }
      break;
    default:
      ASSERT_NOT_REACHED();
  }

  // SVGForeignObject needs paint offset because its viewport offset is baked
  // into its location(), while its localSVGTransform() doesn't contain the
  // offset.
  if (boxModelObject.isBox() &&
      (!boxModelObject.isSVG() || boxModelObject.isSVGRoot() ||
       boxModelObject.isSVGForeignObject())) {
    // TODO(pdr): Several calls in this function walk back up the tree to
    // calculate containers (e.g., topLeftLocation, offsetForInFlowPosition*).
    // The containing block and other containers can be stored on
    // PaintPropertyTreeBuilderContext instead of recomputing them.
    context.current.paintOffset.moveBy(
        toLayoutBox(boxModelObject).topLeftLocation());
    // This is a weird quirk that table cells paint as children of table rows,
    // but their location have the row's location baked-in.
    // Similar adjustment is done in LayoutTableCell::offsetFromContainer().
    if (boxModelObject.isTableCell()) {
      LayoutObject* parentRow = boxModelObject.parent();
      ASSERT(parentRow && parentRow->isTableRow());
      context.current.paintOffset.moveBy(
          -toLayoutBox(parentRow)->topLeftLocation());
    }
  }
}
Example #10
0
void LineBoxList::dirtyLinesFromChangedChild(LayoutObject* container, LayoutObject* child)
{
    if (!container->parent() || (container->isLayoutBlock() && (container->selfNeedsLayout() || !container->isLayoutBlockFlow())))
        return;

    LayoutInline* inlineContainer = container->isLayoutInline() ? toLayoutInline(container) : 0;
    InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox();

    // If we have no first line box, then just bail early.
    if (!firstBox) {
        // For an empty inline, go ahead and propagate the check up to our parent, unless the parent
        // is already dirty.
        if (container->isInline() && !container->ancestorLineBoxDirty()) {
            container->parent()->dirtyLinesFromChangedChild(container);
            container->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree.
        }
        return;
    }

    // Try to figure out which line box we belong in.  First try to find a previous
    // line box by examining our siblings.  If we didn't find a line box, then use our
    // parent's first line box.
    RootInlineBox* box = 0;
    LayoutObject* curr = 0;
    ListHashSet<LayoutObject*, 16> potentialLineBreakObjects;
    potentialLineBreakObjects.add(child);
    for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
        potentialLineBreakObjects.add(curr);

        if (curr->isFloatingOrOutOfFlowPositioned())
            continue;

        if (curr->isReplaced()) {
            InlineBox* wrapper = toLayoutBox(curr)->inlineBoxWrapper();
            if (wrapper)
                box = &wrapper->root();
        } else if (curr->isText()) {
            InlineTextBox* textBox = toLayoutText(curr)->lastTextBox();
            if (textBox)
                box = &textBox->root();
        } else if (curr->isLayoutInline()) {
            InlineBox* lastSiblingBox = toLayoutInline(curr)->lastLineBoxIncludingCulling();
            if (lastSiblingBox)
                box = &lastSiblingBox->root();
        }

        if (box)
            break;
    }
    if (!box) {
        if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) {
            // https://bugs.webkit.org/show_bug.cgi?id=60778
            // We may have just removed a <br> with no line box that was our first child. In this case
            // we won't find a previous sibling, but firstBox can be pointing to a following sibling.
            // This isn't good enough, since we won't locate the root line box that encloses the removed
            // <br>. We have to just over-invalidate a bit and go up to our parent.
            if (!inlineContainer->ancestorLineBoxDirty()) {
                inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer);
                inlineContainer->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree.
            }
            return;
        }
        box = &firstBox->root();
    }

    // If we found a line box, then dirty it.
    if (box) {
        RootInlineBox* adjacentBox;
        box->markDirty();

        // dirty the adjacent lines that might be affected
        // NOTE: we dirty the previous line because RootInlineBox objects cache
        // the address of the first object on the next line after a BR, which we may be
        // invalidating here.  For more info, see how LayoutBlock::layoutInlineChildren
        // calls setLineBreakInfo with the result of findNextLineBreak.  findNextLineBreak,
        // despite the name, actually returns the first LayoutObject after the BR.
        // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize."
        adjacentBox = box->prevRootBox();
        if (adjacentBox)
            adjacentBox->markDirty();
        adjacentBox = box->nextRootBox();
        // If |child| or any of its immediately previous siblings with culled lineboxes is the object after a line-break in |box| or the linebox after it
        // then that means |child| actually sits on the linebox after |box| (or is its line-break object) and so we need to dirty it as well.
        if (adjacentBox && (potentialLineBreakObjects.contains(box->lineBreakObj()) || potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || child->isBR() || isIsolated(container->style()->unicodeBidi())))
            adjacentBox->markDirty();
    }
}
bool InspectorHighlight::buildNodeQuads(Node* node, FloatQuad* content, FloatQuad* padding, FloatQuad* border, FloatQuad* margin)
{
    LayoutObject* layoutObject = node->layoutObject();
    if (!layoutObject)
        return false;

    FrameView* containingView = layoutObject->frameView();
    if (!containingView)
        return false;
    if (!layoutObject->isBox() && !layoutObject->isLayoutInline())
        return false;

    LayoutRect contentBox;
    LayoutRect paddingBox;
    LayoutRect borderBox;
    LayoutRect marginBox;

    if (layoutObject->isBox()) {
        LayoutBox* layoutBox = toLayoutBox(layoutObject);

        // LayoutBox returns the "pure" content area box, exclusive of the scrollbars (if present), which also count towards the content area in CSS.
        const int verticalScrollbarWidth = layoutBox->verticalScrollbarWidth();
        const int horizontalScrollbarHeight = layoutBox->horizontalScrollbarHeight();
        contentBox = layoutBox->contentBoxRect();
        contentBox.setWidth(contentBox.width() + verticalScrollbarWidth);
        contentBox.setHeight(contentBox.height() + horizontalScrollbarHeight);

        paddingBox = layoutBox->paddingBoxRect();
        paddingBox.setWidth(paddingBox.width() + verticalScrollbarWidth);
        paddingBox.setHeight(paddingBox.height() + horizontalScrollbarHeight);

        borderBox = layoutBox->borderBoxRect();

        marginBox = LayoutRect(borderBox.x() - layoutBox->marginLeft(), borderBox.y() - layoutBox->marginTop(),
            borderBox.width() + layoutBox->marginWidth(), borderBox.height() + layoutBox->marginHeight());
    } else {
        LayoutInline* layoutInline = toLayoutInline(layoutObject);

        // LayoutInline's bounding box includes paddings and borders, excludes margins.
        borderBox = LayoutRect(layoutInline->linesBoundingBox());
        paddingBox = LayoutRect(borderBox.x() + layoutInline->borderLeft(), borderBox.y() + layoutInline->borderTop(),
            borderBox.width() - layoutInline->borderLeft() - layoutInline->borderRight(), borderBox.height() - layoutInline->borderTop() - layoutInline->borderBottom());
        contentBox = LayoutRect(paddingBox.x() + layoutInline->paddingLeft(), paddingBox.y() + layoutInline->paddingTop(),
            paddingBox.width() - layoutInline->paddingLeft() - layoutInline->paddingRight(), paddingBox.height() - layoutInline->paddingTop() - layoutInline->paddingBottom());
        // Ignore marginTop and marginBottom for inlines.
        marginBox = LayoutRect(borderBox.x() - layoutInline->marginLeft(), borderBox.y(),
            borderBox.width() + layoutInline->marginWidth(), borderBox.height());
    }

    *content = layoutObject->localToAbsoluteQuad(FloatRect(contentBox));
    *padding = layoutObject->localToAbsoluteQuad(FloatRect(paddingBox));
    *border = layoutObject->localToAbsoluteQuad(FloatRect(borderBox));
    *margin = layoutObject->localToAbsoluteQuad(FloatRect(marginBox));

    contentsQuadToViewport(containingView, *content);
    contentsQuadToViewport(containingView, *padding);
    contentsQuadToViewport(containingView, *border);
    contentsQuadToViewport(containingView, *margin);

    return true;
}
void InlineFlowBoxPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutUnit lineTop, const LayoutUnit lineBottom)
{
    LayoutRect overflowRect(m_inlineFlowBox.visualOverflowRect(lineTop, lineBottom));
    m_inlineFlowBox.flipForWritingMode(overflowRect);
    overflowRect.moveBy(paintOffset);

    if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
        return;

    if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) {
        // Add ourselves to the paint info struct's list of inlines that need to paint their
        // outlines.
        if (m_inlineFlowBox.layoutObject().style()->visibility() == VISIBLE && m_inlineFlowBox.layoutObject().style()->hasOutline() && !m_inlineFlowBox.isRootInlineBox()) {
            LayoutInline& inlineFlow = toLayoutInline(m_inlineFlowBox.layoutObject());

            LayoutBlock* cb = 0;
            bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation();
            if (containingBlockPaintsContinuationOutline) {
                // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations
                // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by
                // anonymous blocks. In this case, it is better to bail out and paint it ourself.
                LayoutBlock* enclosingAnonymousBlock = m_inlineFlowBox.layoutObject().containingBlock();
                if (!enclosingAnonymousBlock->isAnonymousBlock()) {
                    containingBlockPaintsContinuationOutline = false;
                } else {
                    cb = enclosingAnonymousBlock->containingBlock();
                    for (LayoutBoxModelObject* box = m_inlineFlowBox.boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) {
                        if (box->hasSelfPaintingLayer()) {
                            containingBlockPaintsContinuationOutline = false;
                            break;
                        }
                    }
                }
            }

            if (containingBlockPaintsContinuationOutline) {
                // Add ourselves to the containing block of the entire continuation so that it can
                // paint us atomically.
                cb->addContinuationWithOutline(toLayoutInline(m_inlineFlowBox.layoutObject().node()->layoutObject()));
            } else if (!inlineFlow.isInlineElementContinuation()) {
                paintInfo.outlineObjects()->add(&inlineFlow);
            }
        }
    } else if (paintInfo.phase == PaintPhaseMask) {
        DrawingRecorder recorder(*paintInfo.context, m_inlineFlowBox, DisplayItem::paintPhaseToDrawingType(paintInfo.phase), pixelSnappedIntRect(overflowRect));
        if (!recorder.canUseCachedDrawing())
            paintMask(paintInfo, paintOffset);
        return;
    } else if (paintInfo.phase == PaintPhaseForeground) {
        // Paint our background, border and box-shadow.
        paintBoxDecorationBackground(paintInfo, paintOffset);
    }

    // Paint our children.
    if (paintInfo.phase != PaintPhaseSelfOutline) {
        PaintInfo childInfo(paintInfo);
        childInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;

        if (childInfo.paintingRoot && childInfo.paintingRoot->isDescendantOf(&m_inlineFlowBox.layoutObject()))
            childInfo.paintingRoot = 0;
        else
            childInfo.updatePaintingRootForChildren(&m_inlineFlowBox.layoutObject());

        for (InlineBox* curr = m_inlineFlowBox.firstChild(); curr; curr = curr->nextOnLine()) {
            if (curr->layoutObject().isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
                curr->paint(childInfo, paintOffset, lineTop, lineBottom);
        }
    }
}
Example #13
0
void LayoutReplaced::computePositionedLogicalWidth(
    LogicalExtentComputedValues& computedValues) const {
    // The following is based off of the W3C Working Draft from April 11, 2006 of
    // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
    // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
    // (block-style-comments in this function correspond to text from the spec and
    // the numbers correspond to numbers in spec).

    // We don't use containingBlock(), since we may be positioned by an enclosing
    // relative positioned inline.
    const LayoutBoxModelObject* containerBlock =
        toLayoutBoxModelObject(container());

    const LayoutUnit containerLogicalWidth =
        containingBlockLogicalWidthForPositioned(containerBlock);
    const LayoutUnit containerRelativeLogicalWidth =
        containingBlockLogicalWidthForPositioned(containerBlock, false);

    // To match WinIE, in quirks mode use the parent's 'direction' property
    // instead of the the container block's.
    TextDirection containerDirection = containerBlock->style()->direction();

    // Variables to solve.
    bool isHorizontal = isHorizontalWritingMode();
    Length logicalLeft = style()->logicalLeft();
    Length logicalRight = style()->logicalRight();
    Length marginLogicalLeft =
        isHorizontal ? style()->marginLeft() : style()->marginTop();
    Length marginLogicalRight =
        isHorizontal ? style()->marginRight() : style()->marginBottom();
    LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection()
                                         ? computedValues.m_margins.m_start
                                         : computedValues.m_margins.m_end;
    LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection()
                                          ? computedValues.m_margins.m_end
                                          : computedValues.m_margins.m_start;

    // ---------------------------------------------------------------------------
    // 1. The used value of 'width' is determined as for inline replaced
    //  elements.
    // ---------------------------------------------------------------------------
    // NOTE: This value of width is final in that the min/max width calculations
    // are dealt with in computeReplacedWidth().  This means that the steps to
    // produce correct max/min in the non-replaced version, are not necessary.
    computedValues.m_extent =
        computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth();

    const LayoutUnit availableSpace =
        containerLogicalWidth - computedValues.m_extent;

    // ---------------------------------------------------------------------------
    // 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
    //    of the containing block is 'ltr', set 'left' to the static position;
    //    else if 'direction' is 'rtl', set 'right' to the static position.
    // ---------------------------------------------------------------------------
    // see FIXME 1
    computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock,
                                containerLogicalWidth);

    // ---------------------------------------------------------------------------
    // 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
    //    or 'margin-right' with '0'.
    // ---------------------------------------------------------------------------
    if (logicalLeft.isAuto() || logicalRight.isAuto()) {
        if (marginLogicalLeft.isAuto())
            marginLogicalLeft.setValue(Fixed, 0);
        if (marginLogicalRight.isAuto())
            marginLogicalRight.setValue(Fixed, 0);
    }

    // ---------------------------------------------------------------------------
    // 4. If at this point both 'margin-left' and 'margin-right' are still 'auto',
    //    solve the equation under the extra constraint that the two margins must
    //    get equal values, unless this would make them negative, in which case
    //    when the direction of the containing block is 'ltr' ('rtl'), set
    //    'margin-left' ('margin-right') to zero and solve for 'margin-right'
    //    ('margin-left').
    // ---------------------------------------------------------------------------
    LayoutUnit logicalLeftValue;
    LayoutUnit logicalRightValue;

    if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
        // 'left' and 'right' cannot be 'auto' due to step 3
        ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));

        logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
        logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);

        LayoutUnit difference =
            availableSpace - (logicalLeftValue + logicalRightValue);
        if (difference > LayoutUnit()) {
            marginLogicalLeftAlias = difference / 2;  // split the difference
            marginLogicalRightAlias =
                difference -
                marginLogicalLeftAlias;  // account for odd valued differences
        } else {
            // Use the containing block's direction rather than the parent block's
            // per CSS 2.1 reference test abspos-replaced-width-margin-000.
            if (containerDirection == LTR) {
                marginLogicalLeftAlias = LayoutUnit();
                marginLogicalRightAlias = difference;  // will be negative
            } else {
                marginLogicalLeftAlias = difference;  // will be negative
                marginLogicalRightAlias = LayoutUnit();
            }
        }

        // -------------------------------------------------------------------------
        // 5. If at this point there is an 'auto' left, solve the equation for that
        //    value.
        // -------------------------------------------------------------------------
    } else if (logicalLeft.isAuto()) {
        marginLogicalLeftAlias =
            valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
        marginLogicalRightAlias =
            valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
        logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);

        // Solve for 'left'
        logicalLeftValue =
            availableSpace -
            (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias);
    } else if (logicalRight.isAuto()) {
        marginLogicalLeftAlias =
            valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
        marginLogicalRightAlias =
            valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
        logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);

        // Solve for 'right'
        logicalRightValue =
            availableSpace -
            (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias);
    } else if (marginLogicalLeft.isAuto()) {
        marginLogicalRightAlias =
            valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
        logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
        logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);

        // Solve for 'margin-left'
        marginLogicalLeftAlias =
            availableSpace -
            (logicalLeftValue + logicalRightValue + marginLogicalRightAlias);
    } else if (marginLogicalRight.isAuto()) {
        marginLogicalLeftAlias =
            valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
        logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
        logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);

        // Solve for 'margin-right'
        marginLogicalRightAlias =
            availableSpace -
            (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias);
    } else {
        // Nothing is 'auto', just calculate the values.
        marginLogicalLeftAlias =
            valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
        marginLogicalRightAlias =
            valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
        logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
        logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
        // If the containing block is right-to-left, then push the left position as
        // far to the right as possible
        if (containerDirection == RTL) {
            int totalLogicalWidth =
                (computedValues.m_extent + logicalLeftValue + logicalRightValue +
                 marginLogicalLeftAlias + marginLogicalRightAlias)
                .toInt();
            logicalLeftValue =
                containerLogicalWidth - (totalLogicalWidth - logicalLeftValue);
        }
    }

    // ---------------------------------------------------------------------------
    // 6. If at this point the values are over-constrained, ignore the value for
    //    either 'left' (in case the 'direction' property of the containing block
    //    is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that
    //    value.
    // ---------------------------------------------------------------------------
    // NOTE: Constraints imposed by the width of the containing block and its
    // content have already been accounted for above.
    //
    // FIXME: Deal with differing writing modes here.  Our offset needs to be in
    // the containing block's coordinate space, so that
    // can make the result here rather complicated to compute.
    //
    // Use computed values to calculate the horizontal position.
    //
    // FIXME: This hack is needed to calculate the logical left position for a
    // 'rtl' relatively positioned, inline containing block because right now, it
    // is using the logical left position of the first line box when really it
    // should use the last line box. When this is fixed elsewhere, this block
    // should be removed.
    if (containerBlock->isLayoutInline() &&
            !containerBlock->style()->isLeftToRightDirection()) {
        const LayoutInline* flow = toLayoutInline(containerBlock);
        InlineFlowBox* firstLine = flow->firstLineBox();
        InlineFlowBox* lastLine = flow->lastLineBox();
        if (firstLine && lastLine && firstLine != lastLine) {
            computedValues.m_position =
                logicalLeftValue + marginLogicalLeftAlias +
                lastLine->borderLogicalLeft() +
                (lastLine->logicalLeft() - firstLine->logicalLeft());
            return;
        }
    }

    LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias;
    computeLogicalLeftPositionedOffset(logicalLeftPos, this,
                                       computedValues.m_extent, containerBlock,
                                       containerLogicalWidth);
    computedValues.m_position = logicalLeftPos;
}
Example #14
0
InlineFlowBox* SnapToLinesLayouter::findFirstLineBox() const
{
    if (!m_cueBox.firstChild()->isLayoutInline())
        return nullptr;
    return toLayoutInline(m_cueBox.firstChild())->firstLineBox();
}