void RenderFlowThread::paintFlowThreadPortionInRegion(PaintInfo& paintInfo, RenderRegion* region, LayoutRect flowThreadPortionRect, LayoutRect flowThreadPortionOverflowRect, const LayoutPoint& paintOffset) const { GraphicsContext* context = paintInfo.context; if (!context) return; // Adjust the clipping rect for the region. // paintOffset contains the offset where the painting should occur // adjusted with the region padding and border. LayoutRect regionClippingRect = computeRegionClippingRect(paintOffset, flowThreadPortionRect, flowThreadPortionOverflowRect); PaintInfo info(paintInfo); info.rect.intersect(pixelSnappedIntRect(regionClippingRect)); if (!info.rect.isEmpty()) { context->save(); context->clip(regionClippingRect); // RenderFlowThread should start painting its content in a position that is offset // from the region rect's current position. The amount of offset is equal to the location of // the flow thread portion in the flow thread's local coordinates. IntPoint renderFlowThreadOffset; if (style()->isFlippedBlocksWritingMode()) { LayoutRect flippedFlowThreadPortionRect(flowThreadPortionRect); flipForWritingMode(flippedFlowThreadPortionRect); renderFlowThreadOffset = roundedIntPoint(paintOffset - flippedFlowThreadPortionRect.location()); } else renderFlowThreadOffset = roundedIntPoint(paintOffset - flowThreadPortionRect.location()); context->translate(renderFlowThreadOffset.x(), renderFlowThreadOffset.y()); info.rect.moveBy(-renderFlowThreadOffset); layer()->paint(context, info.rect, 0, 0, region, RenderLayer::PaintLayerTemporaryClipRects); context->restore(); } }
static void mapCaretRectToCaretPainter(LayoutItem caretLayoutItem, LayoutBlockItem caretPainterItem, LayoutRect& caretRect) { // FIXME: This shouldn't be called on un-rooted subtrees. // FIXME: This should probably just use mapLocalToAncestor. // Compute an offset between the caretLayoutItem and the caretPainterItem. DCHECK(caretLayoutItem.isDescendantOf(caretPainterItem)); bool unrooted = false; while (caretLayoutItem != caretPainterItem) { LayoutItem containerItem = caretLayoutItem.container(); if (containerItem.isNull()) { unrooted = true; break; } caretRect.move(caretLayoutItem.offsetFromContainer(containerItem)); caretLayoutItem = containerItem; } if (unrooted) caretRect = LayoutRect(); }
void RenderMultiColumnSet::repaintFlowThreadContent(const LayoutRect& repaintRect, bool immediate) const { // Figure out the start and end columns and only check within that range so that we don't walk the // entire column set. Put the repaint rect into flow thread coordinates by flipping it first. LayoutRect flowThreadRepaintRect(repaintRect); flowThread()->flipForWritingMode(flowThreadRepaintRect); // Now we can compare this rect with the flow thread portions owned by each column. First let's // just see if the repaint rect intersects our flow thread portion at all. LayoutRect clippedRect(flowThreadRepaintRect); clippedRect.intersect(RenderRegion::flowThreadPortionOverflowRect()); if (clippedRect.isEmpty()) return; // Now we know we intersect at least one column. Let's figure out the logical top and logical // bottom of the area we're repainting. LayoutUnit repaintLogicalTop = isHorizontalWritingMode() ? flowThreadRepaintRect.y() : flowThreadRepaintRect.x(); LayoutUnit repaintLogicalBottom = (isHorizontalWritingMode() ? flowThreadRepaintRect.maxY() : flowThreadRepaintRect.maxX()) - 1; unsigned startColumn = columnIndexAtOffset(repaintLogicalTop); unsigned endColumn = columnIndexAtOffset(repaintLogicalBottom); LayoutUnit colGap = columnGap(); unsigned colCount = columnCount(); for (unsigned i = startColumn; i <= endColumn; i++) { LayoutRect colRect = columnRectAt(i); // Get the portion of the flow thread that corresponds to this column. LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); // Now get the overflow rect that corresponds to the column. LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); // Do a repaint for this specific column. repaintFlowThreadContentRectangle(repaintRect, immediate, flowThreadPortion, flowThreadOverflowPortion, colRect.location()); } }
void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { RenderTextControl::paint(paintInfo, paintOffset); if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) { LayoutRect contentsRect = contentBoxRect(); // Center in the block progression direction. if (isHorizontalWritingMode()) contentsRect.setY((height() - contentsRect.height()) / 2); else contentsRect.setX((width() - contentsRect.width()) / 2); // Convert the rect into the coords used for painting the content contentsRect.moveBy(paintOffset + location()); theme().paintCapsLockIndicator(*this, paintInfo, snappedIntRect(contentsRect)); } }
static FloatRect localQuadForTextBox(const InlineTextBox& box, unsigned start, unsigned end, bool useSelectionHeight) { unsigned realEnd = std::min(box.end() + 1, end); LayoutRect boxSelectionRect = box.localSelectionRect(start, realEnd); if (!boxSelectionRect.height()) return FloatRect(); if (useSelectionHeight) return boxSelectionRect; // Change the height and y position (or width and x for vertical text) // because selectionRect uses selection-specific values. if (box.isHorizontal()) { boxSelectionRect.setHeight(box.height()); boxSelectionRect.setY(box.y()); } else { boxSelectionRect.setWidth(box.width()); boxSelectionRect.setX(box.x()); } return boxSelectionRect; }
LayoutRect RootFrameViewport::scrollIntoView(const LayoutRect& rectInContent, const ScrollAlignment& alignX, const ScrollAlignment& alignY) { // We want to move the rect into the viewport that excludes the scrollbars so we intersect // the visual viewport with the scrollbar-excluded frameView content rect. However, we don't // use visibleContentRect directly since it floors the scroll position. Instead, we use // FrameView::scrollPositionDouble and construct a LayoutRect from that (the FrameView size // is always integer sized. LayoutRect frameRectInContent = LayoutRect( layoutViewport().scrollPositionDouble(), layoutViewport().visibleContentRect().size()); LayoutRect visualRectInContent = LayoutRect( layoutViewport().scrollPositionDouble() + toDoubleSize(visualViewport().scrollPositionDouble()), visualViewport().visibleContentRect().size()); LayoutRect viewRectInContent = intersection(visualRectInContent, frameRectInContent); LayoutRect targetViewport = ScrollAlignment::getRectToExpose(viewRectInContent, rectInContent, alignX, alignY); // visualViewport.scrollIntoView will attempt to center the given rect within the viewport // so to prevent it from adjusting r's coordinates the rect must match the viewport's size // i.e. add the subtracted scrollbars from above back in. // FIXME: This is hacky and required because getRectToExpose doesn't naturally account // for the two viewports. crbug.com/449340. targetViewport.setSize(LayoutSize(visualViewport().visibleContentRect().size())); // Snap the visible rect to layout units to match the calculated target viewport rect. FloatRect visible = LayoutRect(visualViewport().scrollPositionDouble(), visualViewport().visibleContentRect().size()); float centeringOffsetX = (visible.width() - targetViewport.width()) / 2; float centeringOffsetY = (visible.height() - targetViewport.height()) / 2; DoublePoint targetOffset( targetViewport.x() - centeringOffsetX, targetViewport.y() - centeringOffsetY); setScrollPosition(targetOffset, ProgrammaticScroll); // RootFrameViewport only changes the viewport relative to the document so we can't change the input // rect's location relative to the document origin. return rectInContent; }
static void adjustBubblePosition(const LayoutRect& hostRect, HTMLElement* bubble) { ASSERT(bubble); if (hostRect.isEmpty()) return; double hostX = hostRect.x(); double hostY = hostRect.y(); if (RenderBox* container = bubble->renderer()->containingBlock()) { FloatPoint containerLocation = container->localToAbsolute(); hostX -= containerLocation.x() + container->borderLeft(); hostY -= containerLocation.y() + container->borderTop(); } bubble->getInlineStyleDecl()->setProperty(CSSPropertyTop, hostY + hostRect.height(), CSSPrimitiveValue::CSS_PX); // The 'left' value of ::-webkit-validation-bubble-arrow. const int bubbleArrowTopOffset = 32; double bubbleX = hostX; if (hostRect.width() / 2 < bubbleArrowTopOffset) bubbleX = max(hostX + hostRect.width() / 2 - bubbleArrowTopOffset, 0.0); bubble->getInlineStyleDecl()->setProperty(CSSPropertyLeft, bubbleX, CSSPrimitiveValue::CSS_PX); }
void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { MediaPlayer* mediaPlayer = videoElement().player(); bool displayingPoster = videoElement().shouldDisplayPosterImage(); Page* page = frame().page(); if (!displayingPoster && !mediaPlayer) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } LayoutRect rect = videoBox(); if (rect.isEmpty()) { if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantUnpaintedObject(this, visualOverflowRect()); return; } rect.moveBy(paintOffset); if (page && paintInfo.phase == PaintPhaseForeground) page->addRelevantRepaintedObject(this, rect); LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); GraphicsContext& context = paintInfo.context(); bool clip = !contentRect.contains(rect); GraphicsContextStateSaver stateSaver(context, clip); if (clip) context.clip(contentRect); if (displayingPoster) paintIntoRect(context, rect); else if (!videoElement().isFullscreen() || !mediaPlayer->supportsAcceleratedRendering()) { if (view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers) mediaPlayer->paintCurrentFrameInContext(context, rect); else mediaPlayer->paint(context, rect); } }
void BlockFlowPainter::paintSelection(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { ASSERT(paintInfo.phase == PaintPhaseForeground); if (!m_layoutBlockFlow.shouldPaintSelectionGaps()) return; LayoutUnit lastTop = 0; LayoutUnit lastLeft = m_layoutBlockFlow.logicalLeftSelectionOffset(&m_layoutBlockFlow, lastTop); LayoutUnit lastRight = m_layoutBlockFlow.logicalRightSelectionOffset(&m_layoutBlockFlow, lastTop); LayoutRect bounds = m_layoutBlockFlow.visualOverflowRect(); bounds.moveBy(paintOffset); // Only create a DrawingRecorder and ClipScope if skipRecording is false. This logic is needed // because selectionGaps(...) needs to be called even when we do not record. bool skipRecording = LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutBlockFlow, DisplayItem::SelectionGap, paintOffset); Optional<LayoutObjectDrawingRecorder> drawingRecorder; Optional<ClipScope> clipScope; if (!skipRecording) { drawingRecorder.emplace(paintInfo.context, m_layoutBlockFlow, DisplayItem::SelectionGap, FloatRect(bounds), paintOffset); clipScope.emplace(paintInfo.context); } LayoutRect gapRectsBounds = m_layoutBlockFlow.selectionGaps(&m_layoutBlockFlow, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, skipRecording ? nullptr : &paintInfo, skipRecording ? nullptr : &(*clipScope)); // TODO(wkorman): Rework below to process paint invalidation rects during layout rather than paint. if (!gapRectsBounds.isEmpty()) { PaintLayer* layer = m_layoutBlockFlow.enclosingLayer(); gapRectsBounds.moveBy(-paintOffset); if (!m_layoutBlockFlow.hasLayer()) { LayoutRect localBounds(gapRectsBounds); m_layoutBlockFlow.flipForWritingMode(localBounds); gapRectsBounds = LayoutRect(m_layoutBlockFlow.localToAncestorQuad(FloatRect(localBounds), layer->layoutObject()).enclosingBoundingBox()); if (layer->layoutObject()->hasOverflowClip()) gapRectsBounds.move(layer->layoutBox()->scrolledContentOffset()); } layer->addBlockSelectionGapsBounds(gapRectsBounds); } }
LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) { int sPos = max(startPos - m_start, 0); int ePos = min(endPos - m_start, (int)m_len); if (sPos > ePos) return LayoutRect(); FontCachePurgePreventer fontCachePurgePreventer; LayoutUnit selTop = selectionTop(); LayoutUnit selHeight = selectionHeight(); RenderStyle* styleToUse = textRenderer().style(isFirstLineStyle()); const Font& font = styleToUse->font(); StringBuilder charactersWithHyphen; bool respectHyphen = ePos == m_len && hasHyphen(); TextRun textRun = constructTextRun(styleToUse, font, respectHyphen ? &charactersWithHyphen : 0); FloatPoint startingPoint = FloatPoint(logicalLeft(), selTop.toFloat()); LayoutRect r; if (sPos || ePos != static_cast<int>(m_len)) r = enclosingIntRect(font.selectionRectForText(textRun, startingPoint, selHeight, sPos, ePos)); else // Avoid computing the font width when the entire line box is selected as an optimization. r = enclosingIntRect(FloatRect(startingPoint, FloatSize(m_logicalWidth, selHeight.toFloat()))); LayoutUnit logicalWidth = r.width(); if (r.x() > logicalRight()) logicalWidth = 0; else if (r.maxX() > logicalRight()) logicalWidth = logicalRight() - r.x(); LayoutPoint topPoint = isHorizontal() ? LayoutPoint(r.x(), selTop) : LayoutPoint(selTop, r.x()); LayoutUnit width = isHorizontal() ? logicalWidth : selHeight; LayoutUnit height = isHorizontal() ? selHeight : logicalWidth; return LayoutRect(topPoint, LayoutSize(width, height)); }
void LayoutTextControlSingleLine::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) const { LayoutTextControl::paint(paintInfo, paintOffset); if (shouldPaintSelfBlockBackground(paintInfo.phase) && m_shouldDrawCapsLockIndicator) { if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, *this, paintInfo.phase, paintOffset)) return; LayoutRect contentsRect = contentBoxRect(); // Center in the block progression direction. if (isHorizontalWritingMode()) contentsRect.setY((size().height() - contentsRect.height()) / 2); else contentsRect.setX((size().width() - contentsRect.width()) / 2); // Convert the rect into the coords used for painting the content contentsRect.moveBy(paintOffset + location()); IntRect snappedRect = pixelSnappedIntRect(contentsRect); LayoutObjectDrawingRecorder recorder(paintInfo.context, *this, paintInfo.phase, snappedRect, paintOffset); LayoutTheme::theme().painter().paintCapsLockIndicator(*this, paintInfo, snappedRect); } }
static void buildRendererHighlight(RenderObject* renderer, RenderRegion* region, const HighlightConfig& highlightConfig, Highlight* highlight, InspectorOverlay::CoordinateSystem coordinateSystem) { Frame* containingFrame = renderer->document().frame(); if (!containingFrame) return; highlight->setDataFromConfig(highlightConfig); FrameView* containingView = containingFrame->view(); FrameView* mainView = containingFrame->page()->mainFrame().view(); // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads(). bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot(); if (isSVGRenderer) { highlight->type = HighlightTypeRects; renderer->absoluteQuads(highlight->quads); for (size_t i = 0; i < highlight->quads.size(); ++i) contentsQuadToCoordinateSystem(mainView, containingView, highlight->quads[i], coordinateSystem); } else if (renderer->isBox() || renderer->isRenderInline()) { LayoutRect contentBox; LayoutRect paddingBox; LayoutRect borderBox; LayoutRect marginBox; if (renderer->isBox()) { RenderBox* renderBox = toRenderBox(renderer); LayoutBoxExtent margins(renderBox->marginTop(), renderBox->marginRight(), renderBox->marginBottom(), renderBox->marginLeft()); if (!renderBox->isOutOfFlowPositioned() && region) { RenderBox::LogicalExtentComputedValues computedValues; renderBox->computeLogicalWidthInRegion(computedValues, region); margins.mutableLogicalLeft(renderBox->style().writingMode()) = computedValues.m_margins.m_start; margins.mutableLogicalRight(renderBox->style().writingMode()) = computedValues.m_margins.m_end; } paddingBox = renderBox->clientBoxRectInRegion(region); contentBox = LayoutRect(paddingBox.x() + renderBox->paddingLeft(), paddingBox.y() + renderBox->paddingTop(), paddingBox.width() - renderBox->paddingLeft() - renderBox->paddingRight(), paddingBox.height() - renderBox->paddingTop() - renderBox->paddingBottom()); borderBox = LayoutRect(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(), paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom()); marginBox = LayoutRect(borderBox.x() - margins.left(), borderBox.y() - margins.top(), borderBox.width() + margins.left() + margins.right(), borderBox.height() + margins.top() + margins.bottom()); } else { RenderInline* renderInline = toRenderInline(renderer); // RenderInline's bounding box includes paddings and borders, excludes margins. borderBox = renderInline->linesBoundingBox(); paddingBox = LayoutRect(borderBox.x() + renderInline->borderLeft(), borderBox.y() + renderInline->borderTop(), borderBox.width() - renderInline->borderLeft() - renderInline->borderRight(), borderBox.height() - renderInline->borderTop() - renderInline->borderBottom()); contentBox = LayoutRect(paddingBox.x() + renderInline->paddingLeft(), paddingBox.y() + renderInline->paddingTop(), paddingBox.width() - renderInline->paddingLeft() - renderInline->paddingRight(), paddingBox.height() - renderInline->paddingTop() - renderInline->paddingBottom()); // Ignore marginTop and marginBottom for inlines. marginBox = LayoutRect(borderBox.x() - renderInline->marginLeft(), borderBox.y(), borderBox.width() + renderInline->horizontalMarginExtent(), borderBox.height()); } FloatQuad absContentQuad; FloatQuad absPaddingQuad; FloatQuad absBorderQuad; FloatQuad absMarginQuad; if (region) { RenderFlowThread* flowThread = region->flowThread(); // Figure out the quads in the space of the RenderFlowThread. absContentQuad = renderer->localToContainerQuad(FloatRect(contentBox), flowThread); absPaddingQuad = renderer->localToContainerQuad(FloatRect(paddingBox), flowThread); absBorderQuad = renderer->localToContainerQuad(FloatRect(borderBox), flowThread); absMarginQuad = renderer->localToContainerQuad(FloatRect(marginBox), flowThread); // Move the quad relative to the space of the current region. LayoutRect flippedRegionRect(region->flowThreadPortionRect()); flowThread->flipForWritingMode(flippedRegionRect); FloatSize delta = region->contentBoxRect().location() - flippedRegionRect.location(); absContentQuad.move(delta); absPaddingQuad.move(delta); absBorderQuad.move(delta); absMarginQuad.move(delta); // Resolve the absolute quads starting from the current region. absContentQuad = region->localToAbsoluteQuad(absContentQuad); absPaddingQuad = region->localToAbsoluteQuad(absPaddingQuad); absBorderQuad = region->localToAbsoluteQuad(absBorderQuad); absMarginQuad = region->localToAbsoluteQuad(absMarginQuad); } else { absContentQuad = renderer->localToAbsoluteQuad(FloatRect(contentBox)); absPaddingQuad = renderer->localToAbsoluteQuad(FloatRect(paddingBox)); absBorderQuad = renderer->localToAbsoluteQuad(FloatRect(borderBox)); absMarginQuad = renderer->localToAbsoluteQuad(FloatRect(marginBox)); } contentsQuadToCoordinateSystem(mainView, containingView, absContentQuad, coordinateSystem); contentsQuadToCoordinateSystem(mainView, containingView, absPaddingQuad, coordinateSystem); contentsQuadToCoordinateSystem(mainView, containingView, absBorderQuad, coordinateSystem); contentsQuadToCoordinateSystem(mainView, containingView, absMarginQuad, coordinateSystem); highlight->type = HighlightTypeNode; highlight->quads.append(absMarginQuad); highlight->quads.append(absBorderQuad); highlight->quads.append(absPaddingQuad); highlight->quads.append(absContentQuad); } }
static PassRefPtr<InspectorObject> buildObjectForRegionHighlight(FrameView* mainView, RenderRegion* region) { FrameView* containingView = region->frame().view(); if (!containingView) return nullptr; RenderBlockFlow* regionContainer = toRenderBlockFlow(region->parent()); LayoutRect borderBox = regionContainer->borderBoxRect(); borderBox.setWidth(borderBox.width() + regionContainer->verticalScrollbarWidth()); borderBox.setHeight(borderBox.height() + regionContainer->horizontalScrollbarHeight()); // Create incoming and outgoing boxes that we use to chain the regions toghether. const LayoutSize linkBoxSize(10, 10); const LayoutSize linkBoxMidpoint(linkBoxSize.width() / 2, linkBoxSize.height() / 2); LayoutRect incomingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint, linkBoxSize); LayoutRect outgoingRectBox = LayoutRect(borderBox.location() - linkBoxMidpoint + borderBox.size(), linkBoxSize); // Move the link boxes slightly inside the region border box. LayoutUnit maxUsableHeight = std::max(LayoutUnit(), borderBox.height() - linkBoxMidpoint.height()); LayoutUnit linkBoxVerticalOffset = std::min(LayoutUnit::fromPixel(15), maxUsableHeight); incomingRectBox.move(0, linkBoxVerticalOffset); outgoingRectBox.move(0, -linkBoxVerticalOffset); FloatQuad borderRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(borderBox)); FloatQuad incomingRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(incomingRectBox)); FloatQuad outgoingRectQuad = regionContainer->localToAbsoluteQuad(FloatRect(outgoingRectBox)); contentsQuadToPage(mainView, containingView, borderRectQuad); contentsQuadToPage(mainView, containingView, incomingRectQuad); contentsQuadToPage(mainView, containingView, outgoingRectQuad); RefPtr<InspectorObject> regionObject = InspectorObject::create(); regionObject->setArray("borderQuad", buildArrayForQuad(borderRectQuad)); regionObject->setArray("incomingQuad", buildArrayForQuad(incomingRectQuad)); regionObject->setArray("outgoingQuad", buildArrayForQuad(outgoingRectQuad)); return regionObject.release(); }
LayoutRect LayoutMultiColumnSet::overflowRectForFlowThreadPortion(const LayoutRect& flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const { if (hasOverflowClip()) return flowThreadPortionRect; LayoutRect flowThreadOverflow = m_flowThread->visualOverflowRect(); // Only clip along the flow thread axis. LayoutRect clipRect; if (m_flowThread->isHorizontalWritingMode()) { LayoutUnit minY = isFirstPortion ? flowThreadOverflow.y() : flowThreadPortionRect.y(); LayoutUnit maxY = isLastPortion ? std::max(flowThreadPortionRect.maxY(), flowThreadOverflow.maxY()) : flowThreadPortionRect.maxY(); LayoutUnit minX = std::min(flowThreadPortionRect.x(), flowThreadOverflow.x()); LayoutUnit maxX = std::max(flowThreadPortionRect.maxX(), flowThreadOverflow.maxX()); clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY); } else { LayoutUnit minX = isFirstPortion ? flowThreadOverflow.x() : flowThreadPortionRect.x(); LayoutUnit maxX = isLastPortion ? std::max(flowThreadPortionRect.maxX(), flowThreadOverflow.maxX()) : flowThreadPortionRect.maxX(); LayoutUnit minY = std::min(flowThreadPortionRect.y(), (flowThreadOverflow.y())); LayoutUnit maxY = std::max(flowThreadPortionRect.y(), (flowThreadOverflow.maxY())); clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY); } return clipRect; }
LayoutRect RenderReplaced::replacedContentRect(const LayoutSize& intrinsicSize) const { LayoutRect contentRect = contentBoxRect(); ObjectFit objectFit = style().objectFit(); if (objectFit == ObjectFitFill) return contentRect; if (!intrinsicSize.width() || !intrinsicSize.height()) return contentRect; LayoutRect finalRect = contentRect; switch (objectFit) { case ObjectFitContain: case ObjectFitScaleDown: case ObjectFitCover: finalRect.setSize(finalRect.size().fitToAspectRatio(intrinsicSize, objectFit == ObjectFitCover ? AspectRatioFitGrow : AspectRatioFitShrink)); if (objectFit != ObjectFitScaleDown || finalRect.width() <= intrinsicSize.width()) break; // fall through case ObjectFitNone: finalRect.setSize(intrinsicSize); break; case ObjectFitFill: ASSERT_NOT_REACHED(); } // FIXME: This is where object-position should be taken into account, but since it's not // implemented yet, assume the initial value of "50% 50%". LayoutUnit xOffset = (contentRect.width() - finalRect.width()) / 2; LayoutUnit yOffset = (contentRect.height() - finalRect.height()) / 2; finalRect.move(xOffset, yOffset); return finalRect; }
IntRect::IntRect(const LayoutRect& r) : m_location(r.x(), r.y()) , m_size(r.width(), r.height()) { }
void IntersectionObserver::applyRootMargin(LayoutRect& rect) const { // TODO(szager): Make sure the spec is clear that left/right margins are resolved against // width and not height. LayoutUnit topMargin = computeMargin(m_topMargin, rect.height()); LayoutUnit rightMargin = computeMargin(m_rightMargin, rect.width()); LayoutUnit bottomMargin = computeMargin(m_bottomMargin, rect.height()); LayoutUnit leftMargin = computeMargin(m_leftMargin, rect.width()); rect.setX(rect.x() - leftMargin); rect.setWidth(rect.width() + leftMargin + rightMargin); rect.setY(rect.y() - topMargin); rect.setHeight(rect.height() + topMargin + bottomMargin); }
LayoutRect RenderNamedFlowFragment::flowThreadPortionRectForClipping(bool isFirstRegionInRange, bool isLastRegionInRange) const { // Elements flowed into a region should not be painted past the region's content box // if they continue to flow into another region in that direction. // If they do not continue into another region in that direction, they should be // painted all the way to the region's border box. // Regions with overflow:hidden will apply clip at the border box, not the content box. LayoutRect clippingRect = flowThreadPortionRect(); RenderBlockFlow& container = fragmentContainer(); if (container.style().hasPadding()) { if (isFirstRegionInRange) { if (flowThread()->isHorizontalWritingMode()) { clippingRect.move(0, -container.paddingBefore()); clippingRect.expand(0, container.paddingBefore()); } else { clippingRect.move(-container.paddingBefore(), 0); clippingRect.expand(container.paddingBefore(), 0); } } if (isLastRegionInRange) { if (flowThread()->isHorizontalWritingMode()) clippingRect.expand(0, container.paddingAfter()); else clippingRect.expand(container.paddingAfter(), 0); } if (flowThread()->isHorizontalWritingMode()) { clippingRect.move(-container.paddingStart(), 0); clippingRect.expand(container.paddingStart() + container.paddingEnd(), 0); } else { clippingRect.move(0, -container.paddingStart()); clippingRect.expand(0, container.paddingStart() + container.paddingEnd()); } } return clippingRect; }
void RenderListItem::positionListMarker() { if (m_marker && m_marker->parent()->isBox() && !m_marker->isInside() && m_marker->inlineBoxWrapper()) { LayoutUnit markerOldLogicalLeft = m_marker->logicalLeft(); LayoutUnit blockOffset = 0; LayoutUnit lineOffset = 0; for (RenderBox* o = m_marker->parentBox(); o != this; o = o->parentBox()) { blockOffset += o->logicalTop(); lineOffset += o->logicalLeft(); } bool adjustOverflow = false; LayoutUnit markerLogicalLeft; RootInlineBox& root = m_marker->inlineBoxWrapper()->root(); bool hitSelfPaintingLayer = false; LayoutUnit lineTop = root.lineTop(); LayoutUnit lineBottom = root.lineBottom(); // FIXME: Need to account for relative positioning in the layout overflow. if (style()->isLeftToRightDirection()) { LayoutUnit leftLineOffset = logicalLeftOffsetForLine(blockOffset, logicalLeftOffsetForLine(blockOffset, false), false); markerLogicalLeft = leftLineOffset - lineOffset - paddingStart() - borderStart() + m_marker->marginStart(); m_marker->inlineBoxWrapper()->adjustLineDirectionPosition((markerLogicalLeft - markerOldLogicalLeft).toFloat()); for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) { LayoutRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect(lineTop, lineBottom); LayoutRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect(lineTop, lineBottom); if (markerLogicalLeft < newLogicalVisualOverflowRect.x() && !hitSelfPaintingLayer) { newLogicalVisualOverflowRect.setWidth(newLogicalVisualOverflowRect.maxX() - markerLogicalLeft); newLogicalVisualOverflowRect.setX(markerLogicalLeft); if (box == root) adjustOverflow = true; } if (markerLogicalLeft < newLogicalLayoutOverflowRect.x()) { newLogicalLayoutOverflowRect.setWidth(newLogicalLayoutOverflowRect.maxX() - markerLogicalLeft); newLogicalLayoutOverflowRect.setX(markerLogicalLeft); if (box == root) adjustOverflow = true; } box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect, lineTop, lineBottom); if (box->boxModelObject()->hasSelfPaintingLayer()) hitSelfPaintingLayer = true; } } else { LayoutUnit rightLineOffset = logicalRightOffsetForLine(blockOffset, logicalRightOffsetForLine(blockOffset, false), false); markerLogicalLeft = rightLineOffset - lineOffset + paddingStart() + borderStart() + m_marker->marginEnd(); m_marker->inlineBoxWrapper()->adjustLineDirectionPosition((markerLogicalLeft - markerOldLogicalLeft).toFloat()); for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) { LayoutRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect(lineTop, lineBottom); LayoutRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect(lineTop, lineBottom); if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalVisualOverflowRect.maxX() && !hitSelfPaintingLayer) { newLogicalVisualOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - newLogicalVisualOverflowRect.x()); if (box == root) adjustOverflow = true; } if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalLayoutOverflowRect.maxX()) { newLogicalLayoutOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - newLogicalLayoutOverflowRect.x()); if (box == root) adjustOverflow = true; } box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect, lineTop, lineBottom); if (box->boxModelObject()->hasSelfPaintingLayer()) hitSelfPaintingLayer = true; } } if (adjustOverflow) { LayoutRect markerRect(LayoutPoint(markerLogicalLeft + lineOffset, blockOffset), m_marker->size()); if (!style()->isHorizontalWritingMode()) markerRect = markerRect.transposedRect(); RenderBox* o = m_marker; bool propagateVisualOverflow = true; bool propagateLayoutOverflow = true; do { o = o->parentBox(); if (o->isRenderBlock()) { if (propagateVisualOverflow) toRenderBlock(o)->addContentsVisualOverflow(markerRect); if (propagateLayoutOverflow) toRenderBlock(o)->addLayoutOverflow(markerRect); } if (o->hasOverflowClip()) { propagateLayoutOverflow = false; propagateVisualOverflow = false; } if (o->hasSelfPaintingLayer()) propagateVisualOverflow = false; markerRect.moveBy(-o->location()); } while (o != this && propagateVisualOverflow && propagateLayoutOverflow); } } }
// Widgets are always placed on integer boundaries, so rounding the size is actually // the desired behavior. This function is here because it's otherwise seldom what we // want to do with a LayoutRect. static inline IntRect roundedIntRect(const LayoutRect& rect) { return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size())); }
TEST_F(VisualRectMappingTest, ContainerFlippedWritingModeAndOverflowScroll) { 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='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(); // -40 = -box_shadow_offset_x(40) (with target's top-right corner as the // origin) // 140 = width(100) + box_shadow_offset_x(40) // 110 = height(90) + box_shadow_offset_y(20) EXPECT_EQ(LayoutRect(-40, 0, 140, 110), targetVisualRect); LayoutRect rect = targetVisualRect; target->flipForWritingMode(rect); EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(target, rect)); // This rect is in physical coordinates of target. EXPECT_EQ(LayoutRect(0, 0, 140, 110), rect); rect = targetVisualRect; target->flipForWritingMode(rect); 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 clipped by container's overflow clip because of overflow:scroll. EXPECT_EQ(LayoutRect(-2, 3, 140, 110), rect); rect = targetVisualRect; target->flipForWritingMode(rect); EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect)); // (-2, 3, 140, 100) is first clipped by container's overflow clip, to // (40, 10, 50, 80), then is added by container's offset in LayoutView // (111, 222). // TODO(crbug.com/600039): rect.x() should be 262 (left + border-left), but is // offset // by extra horizontal border-widths because of layout error. EXPECT_EQ(LayoutRect(322, 121, 50, 80), rect); checkPaintInvalidationStateRectMapping(rect, targetVisualRect, *target, layoutView(), layoutView()); LayoutRect containerVisualRect = container->localVisualRect(); // Because container has overflow clip, its visual overflow doesn't include // overflow from children. // 110 = width(50) + border_left_width(40) + border_right_width(20) // 120 = height(80) + border_top_width(10) + border_bottom_width(30) EXPECT_EQ(LayoutRect(0, 0, 110, 120), containerVisualRect); rect = containerVisualRect; container->flipForWritingMode(rect); EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(container, rect)); EXPECT_EQ(LayoutRect(0, 0, 110, 120), rect); rect = containerVisualRect; container->flipForWritingMode(rect); EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(&layoutView(), rect)); // TODO(crbug.com/600039): rect.x() should be 222 (left), but is offset by // extra horizontal // border-widths because of layout error. EXPECT_EQ(LayoutRect(282, 111, 110, 120), rect); checkPaintInvalidationStateRectMapping(rect, containerVisualRect, *container, layoutView(), layoutView()); }
TEST_F(VisualRectMappingTest, ContainerOverflowScroll) { setBodyInnerHTML( "<div id='container' style='position: absolute; top: 111px; left: 222px;" " border: 10px solid red; overflow: scroll; 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(7)); container->setScrollLeft(LayoutUnit(8)); 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.move(-container->scrolledContentOffset()); // 2 = target_x(0) + container_border_left(10) - scroll_left(8) // 3 = target_y(0) + container_border_top(10) - scroll_top(7) // Rect is not clipped by container's overflow clip because of // overflow:scroll. EXPECT_EQ(LayoutRect(2, 3, 140, 110), rect); rect = targetVisualRect; EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect)); // (2, 3, 140, 100) is first clipped by container's overflow clip, to // (10, 10, 50, 80), then is by added container's offset in LayoutView // (111, 222). EXPECT_EQ(LayoutRect(232, 121, 50, 80), rect); checkPaintInvalidationStateRectMapping(rect, targetVisualRect, *target, layoutView(), layoutView()); LayoutRect containerVisualRect = container->localVisualRect(); // Because container has overflow clip, its visual overflow doesn't include // overflow from children. // 70 = width(50) + border_left_width(10) + border_right_width(10) // 100 = height(80) + border_top_width(10) + border_bottom_width(10) EXPECT_EQ(LayoutRect(0, 0, 70, 100), containerVisualRect); rect = containerVisualRect; EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(container, rect)); // Container should not apply overflow clip on its own overflow rect. EXPECT_EQ(LayoutRect(0, 0, 70, 100), rect); rect = containerVisualRect; EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(&layoutView(), rect)); EXPECT_EQ(LayoutRect(222, 111, 70, 100), rect); checkPaintInvalidationStateRectMapping(rect, containerVisualRect, *container, layoutView(), layoutView()); }
IntPoint AccessibilityObject::clickPoint() { LayoutRect rect = elementRect(); return roundedIntPoint(LayoutPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2)); }
LayoutRect LayoutTextControlSingleLine::controlClipRect( const LayoutPoint& additionalOffset) const { LayoutRect clipRect = paddingBoxRect(); clipRect.moveBy(additionalOffset); return clipRect; }
void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!shouldPaint(paintInfo, paintOffset)) return; #ifndef NDEBUG SetLayoutNeededForbiddenScope scope(this); #endif LayoutPoint adjustedPaintOffset = paintOffset + location(); if (hasVisibleBoxDecorations() && paintInfo.phase == PaintPhaseForeground) paintBoxDecorations(paintInfo, adjustedPaintOffset); if (paintInfo.phase == PaintPhaseMask) { paintMask(paintInfo, adjustedPaintOffset); return; } LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size()); if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style().outlineWidth()) paintOutline(paintInfo, paintRect); if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !canHaveChildren()) return; if (!paintInfo.shouldPaintWithinRoot(*this)) return; bool drawSelectionTint = shouldDrawSelectionTint(); if (paintInfo.phase == PaintPhaseSelection) { if (selectionState() == SelectionNone) return; drawSelectionTint = false; } bool completelyClippedOut = false; if (style().hasBorderRadius()) { LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size()); if (borderRect.isEmpty()) completelyClippedOut = true; else { // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. paintInfo.context().save(); FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(paintRect, paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true)); clipRoundedInnerRect(paintInfo.context(), paintRect, roundedInnerRect); } } if (!completelyClippedOut) { paintReplaced(paintInfo, adjustedPaintOffset); if (style().hasBorderRadius()) paintInfo.context().restore(); } // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of // surrounding content. if (drawSelectionTint) { LayoutRect selectionPaintingRect = localSelectionRect(); selectionPaintingRect.moveBy(adjustedPaintOffset); paintInfo.context().fillRect(snappedIntRect(selectionPaintingRect), selectionBackgroundColor()); } }
LayoutUnit RenderRegion::logicalBottomOfFlowThreadContentRect(const LayoutRect& rect) const { ASSERT(isValid()); return flowThread()->isHorizontalWritingMode() ? rect.maxY() : rect.maxX(); }
void ImagePainter::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_renderImage.contentWidth(); LayoutUnit cHeight = m_renderImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_renderImage.imageResource()->hasImage() || m_renderImage.imageResource()->errorOccurred()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { const int borderWidth = 1; LayoutUnit leftBorder = m_renderImage.borderLeft(); LayoutUnit topBorder = m_renderImage.borderTop(); LayoutUnit leftPad = m_renderImage.paddingLeft(); LayoutUnit topPad = m_renderImage.paddingTop(); // Draw an outline rect where the image should be. IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight)); DrawingRecorder recorder(context, &m_renderImage, paintInfo.phase, paintRect); context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(paintRect); bool errorPictureDrawn = false; LayoutSize imageOffset; // When calculating the usable dimensions, exclude the pixels of // the ouline rect so the error image/alt text doesn't draw on it. LayoutUnit usableWidth = cWidth - 2 * borderWidth; LayoutUnit usableHeight = cHeight - 2 * borderWidth; RefPtr<Image> image = m_renderImage.imageResource()->image(); if (m_renderImage.imageResource()->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { float deviceScaleFactor = blink::deviceScaleFactor(m_renderImage.frame()); // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution. pair<Image*, float> brokenImageAndImageScaleFactor = ImageResource::brokenImage(deviceScaleFactor); image = brokenImageAndImageScaleFactor.first; IntSize imageSize = image->size(); imageSize.scale(1 / brokenImageAndImageScaleFactor.second); // Center the error image, accounting for border and padding. LayoutUnit centerX = (usableWidth - imageSize.width()) / 2; if (centerX < 0) centerX = 0; LayoutUnit centerY = (usableHeight - imageSize.height()) / 2; if (centerY < 0) centerY = 0; imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth); context->drawImage(image.get(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, m_renderImage.shouldRespectImageOrientation()); errorPictureDrawn = true; } if (!m_renderImage.altText().isEmpty()) { const Font& font = m_renderImage.style()->font(); const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint textRectOrigin = paintOffset; textRectOrigin.move(leftBorder + leftPad + (RenderImage::paddingWidth / 2) - borderWidth, topBorder + topPad + (RenderImage::paddingHeight / 2) - borderWidth); LayoutPoint textOrigin(textRectOrigin.x(), textRectOrigin.y() + ascent); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. TextRun textRun = constructTextRun(&m_renderImage, font, m_renderImage.altText(), m_renderImage.style(), TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, DefaultTextRunFlags | RespectDirection); float textWidth = font.width(textRun); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = FloatRect(textRectOrigin, FloatSize(textWidth, fontMetrics.height())); context->setFillColor(m_renderImage.resolveColor(CSSPropertyColor)); if (textRun.direction() == RTL) { int availableWidth = cWidth - static_cast<int>(RenderImage::paddingWidth); textOrigin.move(availableWidth - ceilf(textWidth), 0); } if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context->drawBidiText(font, textRunPaintInfo, textOrigin); } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) { context->drawBidiText(font, textRunPaintInfo, textOrigin); } } } } else if (m_renderImage.imageResource()->hasImage() && cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_renderImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_renderImage.replacedContentRect(); paintRect.moveBy(paintOffset); DrawingRecorder recorder(context, &m_renderImage, paintInfo.phase, contentRect); bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }
LayoutRect RenderRegion::overflowRectForFlowThreadPortion(LayoutRect flowThreadPortionRect, bool isFirstPortion, bool isLastPortion) const { ASSERT(isValid()); // FIXME: Would like to just use hasOverflowClip() but we aren't a block yet. When RenderRegion is eliminated and // folded into RenderBlock, switch to hasOverflowClip(). bool clipX = style()->overflowX() != OVISIBLE; bool clipY = style()->overflowY() != OVISIBLE; bool isLastRegionWithRegionOverflowBreak = (isLastPortion && (style()->regionOverflow() == BreakRegionOverflow)); if ((clipX && clipY) || isLastRegionWithRegionOverflowBreak) return flowThreadPortionRect; LayoutRect flowThreadOverflow = m_flowThread->visualOverflowRect(); // Only clip along the flow thread axis. LayoutUnit outlineSize = maximalOutlineSize(PaintPhaseOutline); LayoutRect clipRect; if (m_flowThread->isHorizontalWritingMode()) { LayoutUnit minY = isFirstPortion ? (flowThreadOverflow.y() - outlineSize) : flowThreadPortionRect.y(); LayoutUnit maxY = isLastPortion ? max(flowThreadPortionRect.maxY(), flowThreadOverflow.maxY()) + outlineSize : flowThreadPortionRect.maxY(); LayoutUnit minX = clipX ? flowThreadPortionRect.x() : min(flowThreadPortionRect.x(), flowThreadOverflow.x() - outlineSize); LayoutUnit maxX = clipX ? flowThreadPortionRect.maxX() : max(flowThreadPortionRect.maxX(), (flowThreadOverflow.maxX() + outlineSize)); clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY); } else { LayoutUnit minX = isFirstPortion ? (flowThreadOverflow.x() - outlineSize) : flowThreadPortionRect.x(); LayoutUnit maxX = isLastPortion ? max(flowThreadPortionRect.maxX(), flowThreadOverflow.maxX()) + outlineSize : flowThreadPortionRect.maxX(); LayoutUnit minY = clipY ? flowThreadPortionRect.y() : min(flowThreadPortionRect.y(), (flowThreadOverflow.y() - outlineSize)); LayoutUnit maxY = clipY ? flowThreadPortionRect.maxY() : max(flowThreadPortionRect.y(), (flowThreadOverflow.maxY() + outlineSize)); clipRect = LayoutRect(minX, minY, maxX - minX, maxY - minY); } return clipRect; }
void LayoutMedia::layout() { LayoutSize oldSize = contentBoxRect().size(); LayoutImage::layout(); LayoutRect newRect = contentBoxRect(); LayoutState state(*this); Optional<LayoutUnit> newPanelWidth; // Iterate the children in reverse order so that the media controls are laid // out before the text track container. This is to ensure that the text // track rendering has an up-to-date position of the media controls for // overlap checking, see LayoutVTTCue. #if ENABLE(ASSERT) bool seenTextTrackContainer = false; #endif for (LayoutObject* child = m_children.lastChild(); child; child = child->previousSibling()) { #if ENABLE(ASSERT) if (child->node()->isMediaControls()) ASSERT(!seenTextTrackContainer); else if (child->node()->isTextTrackContainer()) seenTextTrackContainer = true; else ASSERT_NOT_REACHED(); #endif // TODO(mlamouri): we miss some layouts because needsLayout returns false in // some cases where we want to change the width of the controls because the // visible viewport has changed for example. if (newRect.size() == oldSize && !child->needsLayout()) continue; LayoutUnit width = newRect.width(); if (child->node()->isMediaControls()) { width = computePanelWidth(newRect); if (width != oldSize.width()) newPanelWidth = width; } LayoutBox* layoutBox = toLayoutBox(child); layoutBox->setLocation(newRect.location()); // TODO(foolip): Remove the mutableStyleRef() and depend on CSS // width/height: inherit to match the media element size. layoutBox->mutableStyleRef().setHeight(Length(newRect.height(), Fixed)); layoutBox->mutableStyleRef().setWidth(Length(width, Fixed)); layoutBox->forceLayout(); } clearNeedsLayout(); // Notify our MediaControls that a layout has happened. if (mediaElement() && mediaElement()->mediaControls() && newPanelWidth.has_value()) { mediaElement()->mediaControls()->notifyPanelWidthChanged( newPanelWidth.value()); } }
bool LayoutRect::contains(const LayoutRect& other) const { return x() <= other.x() && maxX() >= other.maxX() && y() <= other.y() && maxY() >= other.maxY(); }