void PaintPropertyTreeBuilder::updateTransform(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (object.isSVG() && !object.isSVGRoot()) { // SVG does not use paint offset internally. DCHECK(context.paintOffset == LayoutPoint()); // FIXME(pdr): Check for the presence of a transform instead of the value. Checking for an // identity matrix will cause the property tree structure to change during animations if // the animation passes through the identity matrix. // FIXME(pdr): Refactor this so all non-root SVG objects use the same transform function. const AffineTransform& transform = object.isSVGForeignObject() ? object.localSVGTransform() : object.localToSVGParentTransform(); if (transform.isIdentity()) return; // The origin is included in the local transform, so use an empty origin. RefPtr<TransformPaintPropertyNode> svgTransform = TransformPaintPropertyNode::create( transform, FloatPoint3D(0, 0, 0), context.currentTransform); context.currentTransform = svgTransform.get(); object.getMutableForPainting().ensureObjectPaintProperties().setTransform(svgTransform.release()); return; } const ComputedStyle& style = object.styleRef(); if (!object.isBox() || !style.hasTransform()) return; TransformationMatrix matrix; style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); RefPtr<TransformPaintPropertyNode> transformNode = TransformPaintPropertyNode::create( matrix, transformOrigin(toLayoutBox(object)), context.currentTransform); context.currentTransform = transformNode.get(); object.getMutableForPainting().ensureObjectPaintProperties().setTransform(transformNode.release()); }
static void applyClipRects(const ClipRectsContext& context, const LayoutObject& layoutObject, LayoutPoint offset, ClipRects& clipRects) { ASSERT(layoutObject.hasOverflowClip() || layoutObject.hasClip() || layoutObject.style()->containsPaint()); LayoutView* view = layoutObject.view(); ASSERT(view); if (clipRects.fixed() && context.rootLayer->layoutObject() == view) offset -= toIntSize(view->frameView()->scrollPosition()); if (layoutObject.hasOverflowClip() || layoutObject.style()->containsPaint()) { ClipRect newOverflowClip = toLayoutBox(layoutObject).overflowClipRect(offset, context.scrollbarRelevancy); newOverflowClip.setHasRadius(layoutObject.style()->hasBorderRadius()); clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); if (layoutObject.isPositioned()) clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); if (layoutObject.isLayoutView()) clipRects.setFixedClipRect(intersection(newOverflowClip, clipRects.fixedClipRect())); if (layoutObject.style()->containsPaint()) { clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); clipRects.setFixedClipRect(intersection(newOverflowClip, clipRects.fixedClipRect())); } } if (layoutObject.hasClip()) { LayoutRect newClip = toLayoutBox(layoutObject).clipRect(offset); clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect()).setIsClippedByClipCss()); clipRects.setOverflowClipRect(intersection(newClip, clipRects.overflowClipRect()).setIsClippedByClipCss()); clipRects.setFixedClipRect(intersection(newClip, clipRects.fixedClipRect()).setIsClippedByClipCss()); } }
void AutoscrollController::updateAutoscrollLayoutObject() { if (!m_autoscrollLayoutObject) return; LayoutObject* layoutObject = m_autoscrollLayoutObject; if (RuntimeEnabledFeatures::middleClickAutoscrollEnabled()) { HitTestResult hitTest = layoutObject->frame()->eventHandler().hitTestResultAtPoint( m_middleClickAutoscrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active); if (Node* nodeAtPoint = hitTest.innerNode()) layoutObject = nodeAtPoint->layoutObject(); } while (layoutObject && !(layoutObject->isBox() && toLayoutBox(layoutObject)->canAutoscroll())) layoutObject = layoutObject->parent(); m_autoscrollLayoutObject = layoutObject && layoutObject->isBox() ? toLayoutBox(layoutObject) : nullptr; if (m_autoscrollType != NoAutoscroll && !m_autoscrollLayoutObject) m_autoscrollType = NoAutoscroll; }
bool ExternalPopupMenu::showInternal() { // Blink core reuses the PopupMenu of an element. For simplicity, we do // recreate the actual external popup everytime. if (m_webExternalPopupMenu) { m_webExternalPopupMenu->close(); m_webExternalPopupMenu = 0; } WebPopupMenuInfo info; getPopupMenuInfo(info, *m_ownerElement); if (info.items.isEmpty()) return false; WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(m_localFrame.get()); m_webExternalPopupMenu = webframe->client()->createExternalPopupMenu(info, this); if (m_webExternalPopupMenu) { LayoutObject* layoutObject = m_ownerElement->layoutObject(); if (!layoutObject || !layoutObject->isBox()) return false; FloatQuad quad(toLayoutBox(layoutObject)->localToAbsoluteQuad(FloatQuad(toLayoutBox(layoutObject)->borderBoundingBox()))); IntRect rect(quad.enclosingBoundingBox()); IntRect rectInViewport = m_localFrame->view()->soonToBeRemovedContentsToUnscaledViewport(rect); // TODO(tkent): If the anchor rectangle is not visible, we should not // show a popup. m_webExternalPopupMenu->show(rectInViewport); m_shownDOMTreeVersion = m_ownerElement->document().domTreeVersion(); return true; } else { // The client might refuse to create a popup (when there is already one pending to be shown for example). didCancel(); return false; } }
static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (object.isSVG() && !object.isSVGRoot()) { const AffineTransform& transform = object.localToParentTransform(); if (transform.isIdentity()) return nullptr; // SVG's transform origin is baked into the localToParentTransform. RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformPaintPropertyNode::create( transform, FloatPoint3D(0, 0, 0), context.currentTransform); context.currentTransform = newTransformNodeForTransform.get(); return newTransformNodeForTransform.release(); } const ComputedStyle& style = object.styleRef(); if (!object.isBox() || !style.hasTransform()) return nullptr; ASSERT(context.paintOffset == LayoutPoint()); TransformationMatrix matrix; style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformPaintPropertyNode::create( matrix, transformOrigin(toLayoutBox(object)), context.currentTransform); context.currentTransform = newTransformNodeForTransform.get(); return newTransformNodeForTransform.release(); }
void TablePainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && m_layoutTable.hasBoxDecorationBackground() && m_layoutTable.style()->visibility() == VISIBLE) paintBoxDecorationBackground(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask) { paintMask(paintInfo, paintOffset); return; } // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseBlockBackground) return; // We don't paint our own background, but we do let the kids paint their backgrounds. if (paintPhase == PaintPhaseChildBlockBackgrounds) paintPhase = PaintPhaseChildBlockBackground; PaintInfo info(paintInfo); info.phase = paintPhase; info.updatePaintingRootForChildren(&m_layoutTable); for (LayoutObject* child = m_layoutTable.firstChild(); child; child = child->nextSibling()) { if (child->isBox() && !toLayoutBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { LayoutPoint childPoint = m_layoutTable.flipForWritingModeForChild(toLayoutBox(child), paintOffset); child->paint(info, childPoint); } } if (m_layoutTable.collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && m_layoutTable.style()->visibility() == VISIBLE) { // Using our cached sorted styles, we then do individual passes, // painting each style of border from lowest precedence to highest precedence. info.phase = PaintPhaseCollapsedTableBorders; LayoutTable::CollapsedBorderValues collapsedBorders = m_layoutTable.collapsedBorders(); size_t count = collapsedBorders.size(); for (size_t i = 0; i < count; ++i) { // FIXME: pass this value into children rather than storing temporarily on the LayoutTable object. m_layoutTable.setCurrentBorderValue(&collapsedBorders[i]); for (LayoutTableSection* section = m_layoutTable.bottomSection(); section; section = m_layoutTable.sectionAbove(section)) { LayoutPoint childPoint = m_layoutTable.flipForWritingModeForChild(section, paintOffset); section->paint(info, childPoint); } } m_layoutTable.setCurrentBorderValue(0); } // Paint outline. if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_layoutTable.style()->hasOutline() && m_layoutTable.style()->visibility() == VISIBLE) { LayoutRect overflowRect(m_layoutTable.visualOverflowRect()); overflowRect.moveBy(paintOffset); ObjectPainter(m_layoutTable).paintOutline(paintInfo, LayoutRect(paintOffset, m_layoutTable.size()), overflowRect); } }
TEST_F(ScrollingCoordinatorTest, overflowHidden) { registerMockedHttpURLLoad("overflow-hidden.html"); navigateTo(m_baseURL + "overflow-hidden.html"); forceFullCompositingUpdate(); // Verify the properties of the accelerated scrolling element starting from the LayoutObject // all the way to the WebLayer. Element* overflowElement = frame()->document()->getElementById("unscrollable-y"); ASSERT(overflowElement); LayoutObject* layoutObject = overflowElement->layoutObject(); ASSERT_TRUE(layoutObject->isBox()); ASSERT_TRUE(layoutObject->hasLayer()); LayoutBox* box = toLayoutBox(layoutObject); ASSERT_TRUE(box->usesCompositedScrolling()); ASSERT_EQ(PaintsIntoOwnBacking, box->layer()->compositingState()); CompositedLayerMapping* compositedLayerMapping = box->layer()->compositedLayerMapping(); ASSERT_TRUE(compositedLayerMapping->hasScrollingLayer()); ASSERT(compositedLayerMapping->scrollingContentsLayer()); GraphicsLayer* graphicsLayer = compositedLayerMapping->scrollingContentsLayer(); ASSERT_EQ(box->layer()->scrollableArea(), graphicsLayer->scrollableArea()); WebLayer* webScrollLayer = compositedLayerMapping->scrollingContentsLayer()->platformLayer(); ASSERT_TRUE(webScrollLayer->scrollable()); ASSERT_TRUE(webScrollLayer->userScrollableHorizontal()); ASSERT_FALSE(webScrollLayer->userScrollableVertical()); overflowElement = frame()->document()->getElementById("unscrollable-x"); ASSERT(overflowElement); layoutObject = overflowElement->layoutObject(); ASSERT_TRUE(layoutObject->isBox()); ASSERT_TRUE(layoutObject->hasLayer()); box = toLayoutBox(layoutObject); ASSERT_TRUE(box->scrollableArea()->usesCompositedScrolling()); ASSERT_EQ(PaintsIntoOwnBacking, box->layer()->compositingState()); compositedLayerMapping = box->layer()->compositedLayerMapping(); ASSERT_TRUE(compositedLayerMapping->hasScrollingLayer()); ASSERT(compositedLayerMapping->scrollingContentsLayer()); graphicsLayer = compositedLayerMapping->scrollingContentsLayer(); ASSERT_EQ(box->layer()->scrollableArea(), graphicsLayer->scrollableArea()); webScrollLayer = compositedLayerMapping->scrollingContentsLayer()->platformLayer(); ASSERT_TRUE(webScrollLayer->scrollable()); ASSERT_FALSE(webScrollLayer->userScrollableHorizontal()); ASSERT_TRUE(webScrollLayer->userScrollableVertical()); }
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 TablePainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { PaintPhase paintPhase = paintInfo.phase; if (shouldPaintSelfBlockBackground(paintPhase)) { paintBoxDecorationBackground(paintInfo, paintOffset); if (paintPhase == PaintPhaseSelfBlockBackgroundOnly) return; } if (paintPhase == PaintPhaseMask) { paintMask(paintInfo, paintOffset); return; } if (paintPhase != PaintPhaseSelfOutlineOnly) { PaintInfo paintInfoForDescendants = paintInfo.forDescendants(); for (LayoutObject* child = m_layoutTable.firstChild(); child; child = child->nextSibling()) { if (child->isBox() && !toLayoutBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) { LayoutPoint childPoint = m_layoutTable.flipForWritingModeForChild( toLayoutBox(child), paintOffset); child->paint(paintInfoForDescendants, childPoint); } } if (m_layoutTable.collapseBorders() && shouldPaintDescendantBlockBackgrounds(paintPhase) && m_layoutTable.style()->visibility() == EVisibility::Visible) { // Using our cached sorted styles, we then do individual passes, // painting each style of border from lowest precedence to highest // precedence. LayoutTable::CollapsedBorderValues collapsedBorders = m_layoutTable.collapsedBorders(); size_t count = collapsedBorders.size(); for (size_t i = 0; i < count; ++i) { for (LayoutTableSection* section = m_layoutTable.bottomSection(); section; section = m_layoutTable.sectionAbove(section)) { LayoutPoint childPoint = m_layoutTable.flipForWritingModeForChild(section, paintOffset); TableSectionPainter(*section).paintCollapsedBorders( paintInfoForDescendants, childPoint, collapsedBorders[i]); } } } } if (shouldPaintSelfOutline(paintPhase)) ObjectPainter(m_layoutTable).paintOutline(paintInfo, paintOffset); }
void PaintLayerClipper::calculateRects(const ClipRectsContext& context, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, ClipRect& backgroundRect, ClipRect& foregroundRect, const LayoutPoint* offsetFromRoot) const { bool isClippingRoot = m_layoutObject.layer() == context.rootLayer; if (!isClippingRoot && m_layoutObject.layer()->parent()) { backgroundRect = backgroundClipRect(context); backgroundRect.move(context.subPixelAccumulation); backgroundRect.intersect(paintDirtyRect); } else { backgroundRect = paintDirtyRect; } foregroundRect = backgroundRect; LayoutPoint offset; if (offsetFromRoot) offset = *offsetFromRoot; else m_layoutObject.layer()->convertToLayerCoords(context.rootLayer, offset); layerBounds = LayoutRect(offset, LayoutSize(m_layoutObject.layer()->size())); // Update the clip rects that will be passed to child layers. if (m_layoutObject.hasOverflowClip() && shouldRespectOverflowClip(context)) { foregroundRect.intersect(toLayoutBox(m_layoutObject).overflowClipRect(offset, context.scrollbarRelevancy)); if (m_layoutObject.style()->hasBorderRadius()) foregroundRect.setHasRadius(true); // FIXME: Does not do the right thing with columns yet, since we don't yet factor in the // individual column boxes as overflow. // The LayoutView is special since its overflow clipping rect may be larger than its box rect (crbug.com/492871). LayoutRect layerBoundsWithVisualOverflow = m_layoutObject.isLayoutView() ? toLayoutView(m_layoutObject).viewRect() : toLayoutBox(m_layoutObject).visualOverflowRect(); toLayoutBox(m_layoutObject).flipForWritingMode(layerBoundsWithVisualOverflow); // PaintLayer are in physical coordinates, so the overflow has to be flipped. layerBoundsWithVisualOverflow.moveBy(offset); backgroundRect.intersect(layerBoundsWithVisualOverflow); } // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box. if (m_layoutObject.hasClip()) { // Clip applies to *us* as well, so go ahead and update the damageRect. LayoutRect newPosClip = toLayoutBox(m_layoutObject).clipRect(offset); backgroundRect.intersect(newPosClip); backgroundRect.setIsClippedByClipCss(); foregroundRect.intersect(newPosClip); foregroundRect.setIsClippedByClipCss(); } }
void PaintPropertyTreeBuilder::updatePerspective( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { const ComputedStyle& style = object.styleRef(); if (object.isBox() && style.hasPerspective()) { // The perspective node must not flatten (else nothing will get // perspective), but it should still extend the rendering context as // most transform nodes do. TransformationMatrix matrix = TransformationMatrix().applyPerspective(style.perspective()); FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.current.paintOffset); object.getMutableForPainting().ensurePaintProperties().updatePerspective( context.current.transform, matrix, origin, context.current.shouldFlattenInheritedTransform, context.current.renderingContextID); } else { if (auto* properties = object.getMutableForPainting().paintProperties()) properties->clearPerspective(); } const auto* properties = object.paintProperties(); if (properties && properties->perspective()) { context.current.transform = properties->perspective(); context.current.shouldFlattenInheritedTransform = false; } }
bool ThemePainterDefault::paintMenuList(const LayoutObject& o, const PaintInfo& i, const IntRect& rect) { if (!o.isBox()) return false; WebThemeEngine::ExtraParams extraParams; const LayoutBox& box = toLayoutBox(o); // Match Chromium Win behaviour of showing all borders if any are shown. extraParams.menuList.hasBorder = box.borderRight() || box.borderLeft() || box.borderTop() || box.borderBottom(); extraParams.menuList.hasBorderRadius = o.styleRef().hasBorderRadius(); // Fallback to transparent if the specified color object is invalid. Color backgroundColor(Color::transparent); if (o.styleRef().hasBackground()) backgroundColor = o.resolveColor(CSSPropertyBackgroundColor); extraParams.menuList.backgroundColor = backgroundColor.rgb(); // If we have a background image, don't fill the content area to expose the // parent's background. Also, we shouldn't fill the content area if the // alpha of the color is 0. The API of Windows GDI ignores the alpha. // FIXME: the normal Aura theme doesn't care about this, so we should // investigate if we really need fillContentArea. extraParams.menuList.fillContentArea = !o.styleRef().hasBackgroundImage() && backgroundColor.alpha(); setupMenuListArrow(box, rect, extraParams); WebCanvas* canvas = i.context.canvas(); Platform::current()->themeEngine()->paint( canvas, WebThemeEngine::PartMenuList, getWebThemeState(o), WebRect(rect), &extraParams); return false; }
TEST_F(ScrollingCoordinatorTest, fastFractionalScrollingDiv) { registerMockedHttpURLLoad("fractional-scroll-div.html"); navigateTo(m_baseURL + "fractional-scroll-div.html"); forceFullCompositingUpdate(); Document* document = frame()->document(); Element* scrollableElement = document->getElementById("scroller"); ASSERT(scrollableElement); scrollableElement->setScrollTop(1.0); scrollableElement->setScrollLeft(1.0); forceFullCompositingUpdate(); // Make sure the fractional scroll offset change 1.0 -> 1.2 gets propagated // to compositor. scrollableElement->setScrollTop(1.2); scrollableElement->setScrollLeft(1.2); forceFullCompositingUpdate(); LayoutObject* layoutObject = scrollableElement->layoutObject(); ASSERT_TRUE(layoutObject->isBox()); LayoutBox* box = toLayoutBox(layoutObject); ASSERT_TRUE(box->usesCompositedScrolling()); CompositedLayerMapping* compositedLayerMapping = box->layer()->compositedLayerMapping(); ASSERT_TRUE(compositedLayerMapping->hasScrollingLayer()); ASSERT(compositedLayerMapping->scrollingContentsLayer()); WebLayer* webScrollLayer = compositedLayerMapping->scrollingContentsLayer()->platformLayer(); ASSERT_TRUE(webScrollLayer); ASSERT_NEAR(1.2, webScrollLayer->scrollPositionDouble().x, 0.01); ASSERT_NEAR(1.2, webScrollLayer->scrollPositionDouble().y, 0.01); }
void PaintPropertyTreeBuilder::updateOverflowClip(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (!object.isBox()) return; const LayoutBox& box = toLayoutBox(object); // The <input> elements can't have contents thus CSS overflow property doesn't apply. // However for layout purposes we do generate child layout objects for them, e.g. button label. // We should clip the overflow from those children. This is called control clip and we // technically treat them like overflow clip. LayoutRect clipRect; if (box.hasControlClip()) clipRect = box.controlClipRect(context.paintOffset); else if (box.hasOverflowClip()) clipRect = box.overflowClipRect(context.paintOffset); else return; RefPtr<ClipPaintPropertyNode> borderRadiusClip; if (box.styleRef().hasBorderRadius()) { auto innerBorder = box.styleRef().getRoundedInnerBorderFor( LayoutRect(context.paintOffset, box.size())); borderRadiusClip = ClipPaintPropertyNode::create( context.currentTransform, innerBorder, context.currentClip); } RefPtr<ClipPaintPropertyNode> overflowClip = ClipPaintPropertyNode::create( context.currentTransform, FloatRoundedRect(FloatRect(clipRect)), borderRadiusClip ? borderRadiusClip.release() : context.currentClip); context.currentClip = overflowClip.get(); object.getMutableForPainting().ensureObjectPaintProperties().setOverflowClip(overflowClip.release()); }
void BlockPainter::paintInlineBox(const InlineBox& inlineBox, const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) return; // Text clips are painted only for the direct inline children of the object // that has a text clip style on it, not block children. DCHECK(paintInfo.phase != PaintPhaseTextClip); LayoutPoint childPoint = paintOffset; if (inlineBox.parent() ->getLineLayoutItem() .style() ->isFlippedBlocksWritingMode()) { // Faster than calling containingBlock(). childPoint = LineLayoutAPIShim::layoutObjectFrom(inlineBox.getLineLayoutItem()) ->containingBlock() ->flipForWritingModeForChild( toLayoutBox(LineLayoutAPIShim::layoutObjectFrom( inlineBox.getLineLayoutItem())), childPoint); } ObjectPainter( *LineLayoutAPIShim::constLayoutObjectFrom(inlineBox.getLineLayoutItem())) .paintAllPhasesAtomically(paintInfo, childPoint); }
LayoutBox* SVGImage::embeddedContentBox() const { SVGSVGElement* rootElement = svgRootElement(m_page.get()); if (!rootElement) return nullptr; return toLayoutBox(rootElement->layoutObject()); }
void InlineBox::move(const LayoutSize& delta) { m_topLeft.move(delta); if (lineLayoutItem().isReplaced()) toLayoutBox(layoutObject()).move(delta.width(), delta.height()); }
static PassRefPtr<ClipPaintPropertyNode> createOverflowClipIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (!object.isBox()) return nullptr; const LayoutBox& box = toLayoutBox(object); // The <input> elements can't have contents thus CSS overflow property doesn't apply. // However for layout purposes we do generate child layout objects for them, e.g. button label. // We should clip the overflow from those children. This is called control clip and we // technically treat them like overflow clip. LayoutRect clipRect; if (box.hasControlClip()) clipRect = box.controlClipRect(context.paintOffset); else if (box.hasOverflowClip()) clipRect = box.overflowClipRect(context.paintOffset); else return nullptr; RefPtr<ClipPaintPropertyNode> newClipNodeForBorderRadiusClip; const ComputedStyle& style = box.styleRef(); if (style.hasBorderRadius()) { newClipNodeForBorderRadiusClip = ClipPaintPropertyNode::create( context.currentTransform, style.getRoundedInnerBorderFor(LayoutRect(context.paintOffset, box.size())), context.currentClip); } RefPtr<ClipPaintPropertyNode> newClipNodeForOverflowClip = ClipPaintPropertyNode::create( context.currentTransform, FloatRoundedRect(FloatRect(clipRect)), newClipNodeForBorderRadiusClip ? newClipNodeForBorderRadiusClip.release() : context.currentClip); context.currentClip = newClipNodeForOverflowClip.get(); return newClipNodeForOverflowClip.release(); }
void InlineBox::adjustPosition(FloatWillBeLayoutUnit dx, FloatWillBeLayoutUnit dy) { m_topLeft.move(dx, dy); if (layoutObject().isReplaced()) toLayoutBox(layoutObject()).move(dx, dy); }
int LayoutTextControl::firstLineBoxBaseline() const { int result = LayoutBlock::firstLineBoxBaseline(); if (result != -1) return result; // When the text is empty, |LayoutBlock::firstLineBoxBaseline()| cannot // compute the baseline because lineboxes do not exist. Element* innerEditor = innerEditorElement(); if (!innerEditor || !innerEditor->layoutObject()) return -1; LayoutBlock* innerEditorLayoutObject = toLayoutBlock(innerEditor->layoutObject()); const SimpleFontData* fontData = innerEditorLayoutObject->style(true)->font().primaryFont(); DCHECK(fontData); if (!fontData) return -1; LayoutUnit baseline(fontData->getFontMetrics().ascent(AlphabeticBaseline)); for (LayoutObject* box = innerEditorLayoutObject; box && box != this; box = box->parent()) { if (box->isBox()) baseline += toLayoutBox(box)->logicalTop(); } return baseline.toInt(); }
static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (!object.isBoxModelObject()) return; const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object); // TODO(trchen): There is some insanity going on with tables. Double check results. switch (object.styleRef().position()) { case StaticPosition: break; case RelativePosition: context.paintOffset += boxModelObject.offsetForInFlowPosition(); break; case AbsolutePosition: context.currentTransform = context.transformForOutOfFlowPositioned; context.paintOffset = context.paintOffsetForOutOfFlowPositioned; context.currentClip = context.clipForOutOfFlowPositioned; break; case StickyPosition: context.paintOffset += boxModelObject.offsetForInFlowPosition(); break; case FixedPosition: context.currentTransform = context.transformForFixedPositioned; context.paintOffset = context.paintOffsetForFixedPositioned; context.currentClip = context.clipForFixedPositioned; break; default: ASSERT_NOT_REACHED(); } if (boxModelObject.isBox()) context.paintOffset += toLayoutBox(boxModelObject).locationOffset(); }
static inline bool fullyClipsContents(Node* node) { LayoutObject* renderer = node->layoutObject(); if (!renderer || !renderer->isBox() || !renderer->hasOverflowClip()) return false; return toLayoutBox(renderer)->size().isEmpty(); }
void LayoutMultiColumnFlowThread::skipColumnSpanner(LayoutBox* layoutObject, LayoutUnit logicalTopInFlowThread) { ASSERT(layoutObject->isColumnSpanAll()); LayoutMultiColumnSpannerPlaceholder* placeholder = layoutObject->spannerPlaceholder(); LayoutBox* previousColumnBox = placeholder->previousSiblingMultiColumnBox(); if (previousColumnBox && previousColumnBox->isLayoutMultiColumnSet()) { LayoutMultiColumnSet* columnSet = toLayoutMultiColumnSet(previousColumnBox); if (logicalTopInFlowThread < columnSet->logicalTopInFlowThread()) logicalTopInFlowThread = columnSet->logicalTopInFlowThread(); // Negative margins may cause this. columnSet->endFlow(logicalTopInFlowThread); } LayoutBox* nextColumnBox = placeholder->nextSiblingMultiColumnBox(); if (nextColumnBox && nextColumnBox->isLayoutMultiColumnSet()) { LayoutMultiColumnSet* nextSet = toLayoutMultiColumnSet(nextColumnBox); m_lastSetWorkedOn = nextSet; nextSet->beginFlow(logicalTopInFlowThread); } // We'll lay out of spanners after flow thread layout has finished (during layout of the spanner // placeholders). There may be containing blocks for out-of-flow positioned descendants of the // spanner in the flow thread, so that out-of-flow objects inside the spanner will be laid out // as part of flow thread layout (even if the spanner itself won't). We need to add such // out-of-flow positioned objects to their containing blocks now, or they'll never get laid // out. Since it's non-trivial to determine if we need this, and where such out-of-flow objects // might be, just go through the whole subtree. for (LayoutObject* descendant = layoutObject->slowFirstChild(); descendant; descendant = descendant->nextInPreOrder()) { if (descendant->isBox() && descendant->isOutOfFlowPositioned()) descendant->containingBlock()->insertPositionedObject(toLayoutBox(descendant)); } }
static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context) { const ComputedStyle& style = object.styleRef(); if (!object.isBox() || !style.hasTransform()) return nullptr; ASSERT(context.paintOffset == LayoutPoint()); TransformationMatrix matrix; style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformPaintPropertyNode::create( matrix, transformOrigin(toLayoutBox(object)), context.currentTransform); context.currentTransform = newTransformNodeForTransform.get(); return newTransformNodeForTransform.release(); }
LayoutBox* LayoutScrollbar::owningLayoutObject() const { if (m_owningFrame) return toLayoutBox( LayoutAPIShim::layoutObjectFrom(m_owningFrame->ownerLayoutItem())); return m_owner && m_owner->layoutObject() ? m_owner->layoutObject()->enclosingBox() : 0; }
void Fullscreen::didEnterFullscreenForElement(Element* element) { DCHECK(element); if (!document()->isActive()) return; if (m_fullScreenLayoutObject) m_fullScreenLayoutObject->unwrapLayoutObject(); m_currentFullScreenElement = element; // Create a placeholder block for a the full-screen element, to keep the page // from reflowing when the element is removed from the normal flow. Only do // this for a LayoutBox, as only a box will have a frameRect. The placeholder // will be created in setFullScreenLayoutObject() during layout. LayoutObject* layoutObject = m_currentFullScreenElement->layoutObject(); bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox(); if (shouldCreatePlaceholder) { m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect(); m_savedPlaceholderComputedStyle = ComputedStyle::clone(layoutObject->styleRef()); } // TODO(alexmos): When |m_forCrossProcessDescendant| is true, some of // this layout work has already been done in another process, so it should // not be necessary to repeat it here. if (m_currentFullScreenElement != document()->documentElement()) LayoutFullScreen::wrapLayoutObject( layoutObject, layoutObject ? layoutObject->parent() : 0, document()); // When |m_forCrossProcessDescendant| is true, m_currentFullScreenElement // corresponds to the HTMLFrameOwnerElement for the out-of-process iframe // that contains the actual fullscreen element. Hence, it must also set // the ContainsFullScreenElement flag (so that it gains the // -webkit-full-screen-ancestor style). if (m_forCrossProcessDescendant) { DCHECK(m_currentFullScreenElement->isFrameOwnerElement()); DCHECK(toHTMLFrameOwnerElement(m_currentFullScreenElement) ->contentFrame() ->isRemoteFrame()); m_currentFullScreenElement->setContainsFullScreenElement(true); } m_currentFullScreenElement ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true); document()->styleEngine().ensureFullscreenUAStyle(); m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); // FIXME: This should not call updateStyleAndLayoutTree. document()->updateStyleAndLayoutTree(); m_currentFullScreenElement->didBecomeFullscreenElement(); if (document()->frame()) document()->frame()->eventHandler().scheduleHoverStateUpdate(); m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); }
static void applyClipRects(const ClipRectsContext& context, const LayoutBoxModelObject& layoutObject, LayoutPoint offset, ClipRects& clipRects) { DCHECK(layoutObject.hasClipRelatedProperty() || (layoutObject.isSVGRoot() && toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip())); LayoutView* view = layoutObject.view(); DCHECK(view); if (clipRects.fixed() && context.rootLayer->layoutObject() == view) offset -= LayoutSize(view->frameView()->scrollOffset()); if (layoutObject.hasOverflowClip() || (layoutObject.isSVGRoot() && toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip()) || (layoutObject.styleRef().containsPaint() && layoutObject.isBox())) { ClipRect newOverflowClip = toLayoutBox(layoutObject) .overflowClipRect(offset, context.overlayScrollbarClipBehavior); newOverflowClip.setHasRadius(layoutObject.styleRef().hasBorderRadius()); clipRects.setOverflowClipRect( intersection(newOverflowClip, clipRects.overflowClipRect())); if (layoutObject.isPositioned()) clipRects.setPosClipRect( intersection(newOverflowClip, clipRects.posClipRect())); if (layoutObject.isLayoutView()) clipRects.setFixedClipRect( intersection(newOverflowClip, clipRects.fixedClipRect())); if (layoutObject.styleRef().containsPaint()) { clipRects.setPosClipRect( intersection(newOverflowClip, clipRects.posClipRect())); clipRects.setFixedClipRect( intersection(newOverflowClip, clipRects.fixedClipRect())); } } if (layoutObject.hasClip()) { LayoutRect newClip = toLayoutBox(layoutObject).clipRect(offset); clipRects.setPosClipRect( intersection(newClip, clipRects.posClipRect()).setIsClippedByClipCss()); clipRects.setOverflowClipRect( intersection(newClip, clipRects.overflowClipRect()) .setIsClippedByClipCss()); clipRects.setFixedClipRect(intersection(newClip, clipRects.fixedClipRect()) .setIsClippedByClipCss()); } }
void PaintPropertyTreeBuilder::updateTransform( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (object.isSVG() && !object.isSVGRoot()) { updateTransformForNonRootSVG(object, context); return; } const ComputedStyle& style = object.styleRef(); if (object.isBox() && (style.hasTransform() || style.preserves3D())) { TransformationMatrix matrix; style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin, ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties); // TODO(trchen): transform-style should only be respected if a PaintLayer // is created. // If a node with transform-style: preserve-3d does not exist in an // existing rendering context, it establishes a new one. unsigned renderingContextID = context.current.renderingContextID; if (style.preserves3D() && !renderingContextID) renderingContextID = PtrHash<const LayoutObject>::hash(&object); object.getMutableForPainting().ensurePaintProperties().updateTransform( context.current.transform, matrix, transformOrigin(toLayoutBox(object)), context.current.shouldFlattenInheritedTransform, renderingContextID); } else { if (auto* properties = object.getMutableForPainting().paintProperties()) properties->clearTransform(); } const auto* properties = object.paintProperties(); if (properties && properties->transform()) { context.current.transform = properties->transform(); if (object.styleRef().preserves3D()) { context.current.renderingContextID = properties->transform()->renderingContextID(); context.current.shouldFlattenInheritedTransform = false; } else { context.current.renderingContextID = 0; context.current.shouldFlattenInheritedTransform = true; } } }
LayoutUnit InlineBox::logicalHeight() const { if (hasVirtualLogicalHeight()) return virtualLogicalHeight(); if (lineLayoutItem().isText()) return m_bitfields.isText() ? LayoutUnit(lineLayoutItem().style(isFirstLineStyle())->fontMetrics().height()) : LayoutUnit(); if (lineLayoutItem().isBox() && parent()) return isHorizontal() ? toLayoutBox(layoutObject()).size().height() : toLayoutBox(layoutObject()).size().width(); ASSERT(isInlineFlowBox()); LineLayoutBoxModel flowObject = boxModelObject(); const FontMetrics& fontMetrics = lineLayoutItem().style(isFirstLineStyle())->fontMetrics(); LayoutUnit result = fontMetrics.height(); if (parent()) result += flowObject.borderAndPaddingLogicalHeight(); return result; }
void AutoscrollController::updateAutoscrollLayoutObject() { if (!m_autoscrollLayoutObject) return; LayoutObject* layoutObject = m_autoscrollLayoutObject; #if OS(WIN) HitTestResult hitTest = layoutObject->frame()->eventHandler().hitTestResultAtPoint(m_panScrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active); if (Node* nodeAtPoint = hitTest.innerNode()) layoutObject = nodeAtPoint->layoutObject(); #endif while (layoutObject && !(layoutObject->isBox() && toLayoutBox(layoutObject)->canAutoscroll())) layoutObject = layoutObject->parent(); m_autoscrollLayoutObject = layoutObject && layoutObject->isBox() ? toLayoutBox(layoutObject) : nullptr; }