void InlineFlowBoxPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& cullRect) { ASSERT(paintInfo.phase == PaintPhaseForeground); if (m_inlineFlowBox.lineLayoutItem().style()->visibility() != VISIBLE) return; // You can use p::first-line to specify a background. If so, the root line boxes for // a line may actually have to paint a background. LayoutObject* inlineFlowBoxLayoutObject = LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.lineLayoutItem()); const ComputedStyle* styleToUse = m_inlineFlowBox.lineLayoutItem().style(m_inlineFlowBox.isFirstLineStyle()); bool shouldPaintBoxDecorationBackground; if (m_inlineFlowBox.parent()) shouldPaintBoxDecorationBackground = inlineFlowBoxLayoutObject->hasBoxDecorationBackground(); else shouldPaintBoxDecorationBackground = m_inlineFlowBox.isFirstLineStyle() && styleToUse != m_inlineFlowBox.lineLayoutItem().style(); if (!shouldPaintBoxDecorationBackground) return; if (DrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_inlineFlowBox, DisplayItem::BoxDecorationBackground)) return; DrawingRecorder recorder(paintInfo.context, m_inlineFlowBox, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(cullRect)); LayoutRect frameRect = frameRectClampedToLineTopAndBottomIfNeeded(); // Move x/y to our coordinates. LayoutRect localRect(frameRect); m_inlineFlowBox.flipForWritingMode(localRect); LayoutPoint adjustedPaintOffset = paintOffset + localRect.location(); LayoutRect adjustedFrameRect = LayoutRect(adjustedPaintOffset, frameRect.size()); IntRect adjustedClipRect; BorderPaintingType borderPaintingType = getBorderPaintType(adjustedFrameRect, adjustedClipRect); // Shadow comes first and is behind the background and border. if (!m_inlineFlowBox.boxModelObject().boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, &m_inlineFlowBox)) paintBoxShadow(paintInfo, *styleToUse, Normal, adjustedFrameRect); Color backgroundColor = inlineFlowBoxLayoutObject->resolveColor(*styleToUse, CSSPropertyBackgroundColor); paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(), adjustedFrameRect); paintBoxShadow(paintInfo, *styleToUse, Inset, adjustedFrameRect); switch (borderPaintingType) { case DontPaintBorders: break; case PaintBordersWithoutClip: BoxPainter::paintBorder(*toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())), paintInfo, adjustedFrameRect, m_inlineFlowBox.lineLayoutItem().styleRef(m_inlineFlowBox.isFirstLineStyle()), BackgroundBleedNone, m_inlineFlowBox.includeLogicalLeftEdge(), m_inlineFlowBox.includeLogicalRightEdge()); break; case PaintBordersWithClip: // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, // but it isn't even clear how this should work at all. LayoutRect imageStripPaintRect = paintRectForImageStrip(adjustedPaintOffset, frameRect.size(), LTR); GraphicsContextStateSaver stateSaver(paintInfo.context); paintInfo.context.clip(adjustedClipRect); BoxPainter::paintBorder(*toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())), paintInfo, imageStripPaintRect, m_inlineFlowBox.lineLayoutItem().styleRef(m_inlineFlowBox.isFirstLineStyle())); break; } }
TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) { RuntimeEnabledFeatures::setCompositeOpaqueScrollersEnabled(true); setBodyInnerHTML( "<style>" "#scroller { overflow: scroll; height: 200px; width: 200px; background: " "rgba(255,255,255,0.5) local content-box; }" "#scrolled { height: 300px; }" "</style>" "<div id=\"scroller\"><div id=\"scrolled\"></div></div>"); document().view()->updateAllLifecyclePhases(); EXPECT_TRUE(RuntimeEnabledFeatures::compositeOpaqueScrollersEnabled()); Element* scroller = document().getElementById("scroller"); PaintLayer* paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer(); ASSERT_TRUE(paintLayer); EXPECT_FALSE(paintLayer->needsCompositedScrolling()); // Change the background to transparent scroller->setAttribute(HTMLNames::styleAttr, "background: white local content-box;"); document().view()->updateAllLifecyclePhases(); paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer(); ASSERT_TRUE(paintLayer); EXPECT_TRUE(paintLayer->needsCompositedScrolling()); EXPECT_TRUE(paintLayer->graphicsLayerBacking()); ASSERT_TRUE(paintLayer->graphicsLayerBackingForScrolling()); EXPECT_TRUE(paintLayer->graphicsLayerBackingForScrolling()->contentsOpaque()); }
PaintLayer& PaintInvalidationState::enclosingSelfPaintingLayer(const LayoutObject& layoutObject) const { if (layoutObject.hasLayer() && toLayoutBoxModelObject(layoutObject).hasSelfPaintingLayer()) return *toLayoutBoxModelObject(layoutObject).layer(); return m_enclosingSelfPaintingLayer; }
TEST_P(BoxPaintInvalidatorTest, CompositedBackgroundAttachmentLocalGradientResize) { enableCompositing(); Element* target = document().getElementById("target"); target->setAttribute(HTMLNames::classAttr, "border local-background gradient"); target->setAttribute(HTMLNames::styleAttr, "will-change: transform"); target->setInnerHTML( "<div id='child' style='width: 500px; height: 500px'></div>", ASSERT_NO_EXCEPTION); Element* child = document().getElementById("child"); document().view()->updateAllLifecyclePhases(); // Resize the content. document().view()->setTracksPaintInvalidations(true); child->setAttribute(HTMLNames::styleAttr, "width: 500px; height: 1000px"); document().view()->updateAllLifecyclePhases(); GraphicsLayer* containerLayer = toLayoutBoxModelObject(target->layoutObject()) ->layer() ->graphicsLayerBacking(); GraphicsLayer* contentsLayer = toLayoutBoxModelObject(target->layoutObject()) ->layer() ->graphicsLayerBackingForScrolling(); // No invalidation on the container layer. EXPECT_FALSE(containerLayer->getRasterInvalidationTracking()); // Full invalidation of background on contents layer because the gradient // background is resized. const auto& contentsRasterInvalidations = contentsLayer->getRasterInvalidationTracking() ->trackedRasterInvalidations; ASSERT_EQ(1u, contentsRasterInvalidations.size()); EXPECT_EQ(IntRect(0, 0, 500, 1000), contentsRasterInvalidations[0].rect); EXPECT_EQ(static_cast<const DisplayItemClient*>(target->layoutObject()), contentsRasterInvalidations[0].client); EXPECT_EQ(PaintInvalidationBackgroundOnScrollingContentsLayer, contentsRasterInvalidations[0].reason); document().view()->setTracksPaintInvalidations(false); // Resize the container. document().view()->setTracksPaintInvalidations(true); target->setAttribute(HTMLNames::styleAttr, "will-change: transform; height: 200px"); document().view()->updateAllLifecyclePhases(); EXPECT_FALSE(contentsLayer->getRasterInvalidationTracking()); // Full invalidation on the container layer. const auto& containerRasterInvalidations = containerLayer->getRasterInvalidationTracking() ->trackedRasterInvalidations; ASSERT_EQ(1u, containerRasterInvalidations.size()); EXPECT_EQ(IntRect(0, 0, 70, 240), containerRasterInvalidations[0].rect); EXPECT_EQ(static_cast<const DisplayItemClient*>(target->layoutObject()), containerRasterInvalidations[0].client); EXPECT_EQ(PaintInvalidationBorderBoxChange, containerRasterInvalidations[0].reason); document().view()->setTracksPaintInvalidations(false); }
// Test that opacity applied to the scroller or an ancestor will cause the // scrolling contents layer to not be promoted. TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) { ScopedCompositeOpaqueScrollersForTest compositeOpaqueScrollers(true); setBodyInnerHTML( "<style>" "#scroller { overflow: scroll; height: 200px; width: 200px; background: " "white local content-box; }" "#scrolled { height: 300px; }" "</style>" "<div id=\"parent\">" " <div id=\"scroller\"><div id=\"scrolled\"></div></div>" "</div>"); document().view()->updateAllLifecyclePhases(); EXPECT_TRUE(RuntimeEnabledFeatures::compositeOpaqueScrollersEnabled()); Element* parent = document().getElementById("parent"); Element* scroller = document().getElementById("scroller"); PaintLayer* paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer(); ASSERT_TRUE(paintLayer); EXPECT_TRUE(paintLayer->needsCompositedScrolling()); EXPECT_TRUE(paintLayer->graphicsLayerBacking()); ASSERT_TRUE(paintLayer->graphicsLayerBackingForScrolling()); EXPECT_TRUE(paintLayer->graphicsLayerBackingForScrolling()->contentsOpaque()); // Change the parent to be partially translucent. parent->setAttribute(HTMLNames::styleAttr, "opacity: 0.5;"); document().view()->updateAllLifecyclePhases(); paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer(); ASSERT_TRUE(paintLayer); EXPECT_FALSE(paintLayer->needsCompositedScrolling()); EXPECT_FALSE(paintLayer->graphicsLayerBacking()); // Change the parent to be opaque again. parent->setAttribute(HTMLNames::styleAttr, "opacity: 1;"); document().view()->updateAllLifecyclePhases(); paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer(); ASSERT_TRUE(paintLayer); EXPECT_TRUE(paintLayer->needsCompositedScrolling()); EXPECT_TRUE(paintLayer->graphicsLayerBacking()); ASSERT_TRUE(paintLayer->graphicsLayerBackingForScrolling()); EXPECT_TRUE(paintLayer->graphicsLayerBackingForScrolling()->contentsOpaque()); // Make the scroller translucent. scroller->setAttribute(HTMLNames::styleAttr, "opacity: 0.5"); document().view()->updateAllLifecyclePhases(); paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer(); ASSERT_TRUE(paintLayer); EXPECT_FALSE(paintLayer->needsCompositedScrolling()); EXPECT_FALSE(paintLayer->graphicsLayerBacking()); }
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 LayoutBoxModelObject* enclosingBoxModelObject(LayoutObject* object) { while (object && !object->isBoxModelObject()) object = object->parent(); if (!object) return nullptr; return toLayoutBoxModelObject(object); }
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (object.isBoxModelObject()) { // TODO(trchen): Eliminate PaintLayer dependency. PaintLayer* layer = toLayoutBoxModelObject(object).layer(); if (!layer || !layer->paintsWithTransform(GlobalPaintNormalPhase)) return; } if (context.paintOffset == LayoutPoint()) return; // We should use the same subpixel paint offset values for snapping regardless of whether a // transform is present. If there is a transform we round the paint offset but keep around // the residual fractional component for the transformed content to paint with. // In spv1 this was called "subpixel accumulation". For more information, see // PaintLayer::subpixelAccumulation() and PaintLayerPainter::paintFragmentByApplyingTransform. IntPoint roundedPaintOffset = roundedIntPoint(context.paintOffset); LayoutPoint fractionalPaintOffset = LayoutPoint(context.paintOffset - roundedPaintOffset); RefPtr<TransformPaintPropertyNode> paintOffsetTranslation = TransformPaintPropertyNode::create( TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()), FloatPoint3D(), context.currentTransform); context.currentTransform = paintOffsetTranslation.get(); context.paintOffset = fractionalPaintOffset; object.getMutableForPainting().ensureObjectPaintProperties().setPaintOffsetTranslation(paintOffsetTranslation.release()); }
bool CompositorAnimations::startAnimationOnCompositor(const Element& element, int group, double startTime, double timeOffset, const Timing& timing, const AnimationPlayer& player, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate) { ASSERT(startedAnimationIds.isEmpty()); ASSERT(isCandidateForAnimationOnCompositor(timing, element, &player, effect, playerPlaybackRate)); ASSERT(canStartAnimationOnCompositor(element)); const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer(); ASSERT(layer); Vector<OwnPtr<WebCompositorAnimation>> animations; CompositorAnimationsImpl::getAnimationOnCompositor(timing, group, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate); ASSERT(!animations.isEmpty()); for (auto& animation : animations) { int id = animation->id(); if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) { WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer(); ASSERT(compositorPlayer); compositorPlayer->addAnimation(animation.leakPtr()); } else if (!layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->addAnimation(animation.release())) { // FIXME: We should know ahead of time whether these animations can be started. for (int startedAnimationId : startedAnimationIds) cancelAnimationOnCompositor(element, player, startedAnimationId); startedAnimationIds.clear(); return false; } startedAnimationIds.append(id); } ASSERT(!startedAnimationIds.isEmpty()); return true; }
TEST_F(PaintControllerPaintTestForSlimmingPaintV2, CompositingFold) { setBodyInnerHTML( "<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>" " <div style='width: 100px; height: 100px; background-color: " "blue'></div>" "</div>"); PaintLayer& htmlLayer = *toLayoutBoxModelObject(document().documentElement()->layoutObject()) ->layer(); LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div")); LayoutObject& subDiv = *div.firstChild(); EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 8, TestDisplayItem(layoutView(), DisplayItem::kClipFrameToVisibleContentRect), TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence), TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(htmlLayer, DisplayItem::kSubsequence), // The begin and end compositing display items have been folded into this // one. TestDisplayItem(subDiv, backgroundType), TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence), TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence), TestDisplayItem(layoutView(), DisplayItem::clipTypeToEndClipType( DisplayItem::kClipFrameToVisibleContentRect))); }
// static bool InspectorHighlight::getBoxModel(Node* node, RefPtr<TypeBuilder::DOM::BoxModel>& model) { LayoutObject* layoutObject = node->layoutObject(); FrameView* view = node->document().view(); if (!layoutObject || !view) return false; FloatQuad content, padding, border, margin; if (!buildNodeQuads(node, &content, &padding, &border, &margin)) return false; IntRect boundingBox = view->contentsToRootFrame(layoutObject->absoluteBoundingBoxRect()); LayoutBoxModelObject* modelObject = layoutObject->isBoxModelObject() ? toLayoutBoxModelObject(layoutObject) : nullptr; model = TypeBuilder::DOM::BoxModel::create() .setContent(buildArrayForQuad(content)) .setPadding(buildArrayForQuad(padding)) .setBorder(buildArrayForQuad(border)) .setMargin(buildArrayForQuad(margin)) .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width()) .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height()); Shape::DisplayPaths paths; FloatQuad boundsQuad; if (const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad)) { RefPtr<TypeBuilder::DOM::ShapeOutsideInfo> shapeTypeBuilder = TypeBuilder::DOM::ShapeOutsideInfo::create() .setBounds(buildArrayForQuad(boundsQuad)) .setShape(ShapePathBuilder::buildPath(*view, *layoutObject, *shapeOutsideInfo, paths.shape)) .setMarginShape(ShapePathBuilder::buildPath(*view, *layoutObject, *shapeOutsideInfo, paths.marginShape)); model->setShapeOutside(shapeTypeBuilder); } return true; }
TEST_F(PaintLayerClipperTest, NestedContainPaintClip) { setBodyInnerHTML( "<div style='contain: paint; width: 200px; height: 200px; overflow: " "auto'>" " <div id='target' style='contain: paint; height: 400px'>" " </div>" "</div>"); LayoutRect infiniteRect(LayoutRect::infiniteIntRect()); PaintLayer* layer = toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer(); ClipRectsContext context(layer->parent(), PaintingClipRectsIgnoringOverflowClip); LayoutRect layerBounds; ClipRect backgroundRect, foregroundRect; layer->clipper().calculateRects(context, infiniteRect, layerBounds, backgroundRect, foregroundRect); EXPECT_EQ(LayoutRect(0, 0, 200, 400), backgroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 400), foregroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 400), layerBounds); ClipRectsContext contextClip(layer->parent(), PaintingClipRects); layer->clipper().calculateRects(contextClip, infiniteRect, layerBounds, backgroundRect, foregroundRect); EXPECT_EQ(LayoutRect(0, 0, 200, 200), backgroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 200), foregroundRect.rect()); EXPECT_EQ(LayoutRect(0, 0, 200, 400), layerBounds); }
static void addPDFURLRectsForInlineChildrenRecursively(const LayoutObject& layoutObject, const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { for (LayoutObject* child = layoutObject.slowFirstChild(); child; child = child->nextSibling()) { if (!child->isLayoutInline() || toLayoutBoxModelObject(child)->hasSelfPaintingLayer()) continue; ObjectPainter(*child).addPDFURLRectIfNeeded(paintInfo, paintOffset); addPDFURLRectsForInlineChildrenRecursively(*child, paintInfo, paintOffset); } }
void IntersectionObservation::initializeGeometry(IntersectionGeometry& geometry) { ASSERT(m_target); LayoutObject* targetLayoutObject = target()->layoutObject(); if (targetLayoutObject->isBoxModelObject()) geometry.targetRect = toLayoutBoxModelObject(targetLayoutObject)->visualOverflowRect(); else geometry.targetRect = toLayoutText(targetLayoutObject)->visualOverflowRect(); geometry.intersectionRect = geometry.targetRect; }
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { bool usesPaintOffsetTranslation = false; if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() && object.isLayoutView()) { // Root layer scrolling always creates a translation node for LayoutView to // ensure fixed and absolute contexts use the correct transform space. usesPaintOffsetTranslation = true; } else if (object.isBoxModelObject() && context.current.paintOffset != LayoutPoint()) { // TODO(trchen): Eliminate PaintLayer dependency. PaintLayer* layer = toLayoutBoxModelObject(object).layer(); if (layer && layer->paintsWithTransform(GlobalPaintNormalPhase)) usesPaintOffsetTranslation = true; } // We should use the same subpixel paint offset values for snapping // regardless of whether a transform is present. If there is a transform // we round the paint offset but keep around the residual fractional // component for the transformed content to paint with. In spv1 this was // called "subpixel accumulation". For more information, see // PaintLayer::subpixelAccumulation() and // PaintLayerPainter::paintFragmentByApplyingTransform. IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset); LayoutPoint fractionalPaintOffset = LayoutPoint(context.current.paintOffset - roundedPaintOffset); if (usesPaintOffsetTranslation) { object.getMutableForPainting() .ensurePaintProperties() .updatePaintOffsetTranslation( context.current.transform, TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()), FloatPoint3D(), context.current.shouldFlattenInheritedTransform, context.current.renderingContextID); } else { if (auto* properties = object.getMutableForPainting().paintProperties()) properties->clearPaintOffsetTranslation(); } const auto* properties = object.paintProperties(); if (properties && properties->paintOffsetTranslation()) { context.current.transform = properties->paintOffsetTranslation(); context.current.paintOffset = fractionalPaintOffset; if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() && object.isLayoutView()) { context.absolutePosition.transform = properties->paintOffsetTranslation(); context.fixedPosition.transform = properties->paintOffsetTranslation(); context.absolutePosition.paintOffset = LayoutPoint(); context.fixedPosition.paintOffset = LayoutPoint(); } } }
TEST_F(CompositedLayerMappingTest, RotatedInterestRect) { setBodyInnerHTML( "<div id='target' style='width: 200px; height: 200px; will-change: transform; transform: rotateZ(45deg)'></div>"); document().view()->updateAllLifecyclePhases(); Element* element = document().getElementById("target"); PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer(); ASSERT_TRUE(!!paintLayer->graphicsLayerBacking()); EXPECT_RECT_EQ(IntRect(0, 0, 200, 200), recomputeInterestRect(paintLayer->graphicsLayerBacking())); }
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()); } } }
TEST_F(CompositedLayerMappingTest, TallLayerInterestRect) { setBodyInnerHTML("<div id='target' style='width: 200px; height: 10000px; will-change: transform'></div>"); document().view()->updateAllLifecyclePhases(); Element* element = document().getElementById("target"); PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer(); ASSERT_TRUE(paintLayer->graphicsLayerBacking()); // Screen-space visible content rect is [8, 8, 200, 600]. Mapping back to local, adding 4000px in all directions, then // clipping, yields this rect. EXPECT_RECT_EQ(IntRect(0, 0, 200, 4592), recomputeInterestRect(paintLayer->graphicsLayerBacking())); }
void PaintLayerStackingNode::rebuildZOrderLists() { #if DCHECK_IS_ON() DCHECK(m_layerListMutationAllowed); #endif DCHECK(isDirtyStackingContext()); for (PaintLayer* child = layer()->firstChild(); child; child = child->nextSibling()) child->stackingNode()->collectLayers(m_posZOrderList, m_negZOrderList); // Sort the two lists. if (m_posZOrderList) std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); if (m_negZOrderList) std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); // Append layers for top layer elements after normal layer collection, to // ensure they are on top regardless of z-indexes. The layoutObjects of top // layer elements are children of the view, sorted in top layer stacking // order. if (layer()->isRootLayer()) { LayoutBlockFlow* rootBlock = layoutObject()->view(); // If the viewport is paginated, everything (including "top-layer" elements) // gets redirected to the flow thread. So that's where we have to look, in // that case. if (LayoutBlockFlow* multiColumnFlowThread = rootBlock->multiColumnFlowThread()) rootBlock = multiColumnFlowThread; for (LayoutObject* child = rootBlock->firstChild(); child; child = child->nextSibling()) { Element* childElement = (child->node() && child->node()->isElementNode()) ? toElement(child->node()) : 0; if (childElement && childElement->isInTopLayer()) { PaintLayer* layer = toLayoutBoxModelObject(child)->layer(); // Create the buffer if it doesn't exist yet. if (!m_posZOrderList) m_posZOrderList = wrapUnique(new Vector<PaintLayerStackingNode*>); m_posZOrderList->append(layer->stackingNode()); } } } #if ENABLE(ASSERT) updateStackingParentForZOrderLists(this); #endif m_zOrderListsDirty = false; }
static CompositedLayerMapping* mappingFromElement(Element* element) { if (!element) return nullptr; LayoutObject* layoutObject = element->layoutObject(); if (!layoutObject || !layoutObject->isBoxModelObject()) return nullptr; PaintLayer* layer = toLayoutBoxModelObject(layoutObject)->layer(); if (!layer) return nullptr; if (!layer->hasCompositedLayerMapping()) return nullptr; return layer->compositedLayerMapping(); }
TEST_F(PaintLayerTest, PaintingExtentReflectionWithTransform) { setBodyInnerHTML( "<div id='target' style='background-color: blue; position: absolute;" " width: 110px; height: 120px; top: 40px; left: 60px;" " -webkit-box-reflect: below 3px; transform: translateX(30px)'>" "</div>"); PaintLayer* layer = toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer(); EXPECT_EQ( LayoutRect(90, 40, 110, 243), layer->paintingExtent(document().layoutView()->layer(), LayoutSize(), 0)); }
void CompositorAnimations::attachCompositedLayers(const Element& element, const AnimationPlayer& player) { ASSERT(element.layoutObject()); DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer(); ASSERT(layer); WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer(); ASSERT(compositorPlayer); ASSERT(layer->compositedDeprecatedPaintLayerMapping()); compositorPlayer->attachLayer(layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->platformLayer()); }
TEST_F(PaintLayerClipperTest, LocalClipRectFixedUnderTransform) { setBodyInnerHTML( "<div id='transformed'" " style='will-change: transform; width: 100px; height: 100px;" " overflow: hidden'>" " <div id='fixed' " " style='position: fixed; width: 100px; height: 100px;" " top: -50px'>" " </div>" "</div>"); LayoutRect infiniteRect(LayoutRect::infiniteIntRect()); PaintLayer* transformed = toLayoutBoxModelObject(getLayoutObjectByElementId("transformed")) ->layer(); PaintLayer* fixed = toLayoutBoxModelObject(getLayoutObjectByElementId("fixed"))->layer(); EXPECT_EQ(LayoutRect(0, 0, 100, 100), transformed->clipper().localClipRect(transformed)); EXPECT_EQ(LayoutRect(0, 50, 100, 100), fixed->clipper().localClipRect(transformed)); }
TEST_F(CompositedLayerMappingTest, TallLayerWholeDocumentInterestRect) { setBodyInnerHTML("<div id='target' style='width: 200px; height: 10000px; will-change: transform'></div>"); document().settings()->setMainFrameClipsContent(false); document().view()->updateAllLifecyclePhases(); Element* element = document().getElementById("target"); PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer(); ASSERT_TRUE(paintLayer->graphicsLayerBacking()); ASSERT_TRUE(paintLayer->compositedLayerMapping()); // recomputeInterestRect computes the interest rect; computeInterestRect applies the extra setting to paint everything. EXPECT_RECT_EQ(IntRect(0, 0, 200, 4592), recomputeInterestRect(paintLayer->graphicsLayerBacking())); EXPECT_RECT_EQ(IntRect(0, 0, 200, 10000), computeInterestRect(paintLayer->compositedLayerMapping(), paintLayer->graphicsLayerBacking(), IntRect())); }
void FrameCaret::invalidateCaretRect(bool forceInvalidation) { if (!m_caretRectDirty) return; m_caretRectDirty = false; DCHECK(caretPositionIsValidForDocument(*m_frame->document())); LayoutObject* layoutObject = nullptr; LayoutRect newRect; PositionWithAffinity currentCaretPosition = caretPosition(); if (isActive()) newRect = localCaretRectOfPosition(currentCaretPosition, layoutObject); Node* newNode = layoutObject ? layoutObject->node() : nullptr; // The current selected node |newNode| could be a child multiple levels below // its associated "anchor node" ancestor, so we reference and keep around the // anchor node for checking editability. // TODO(wkorman): Consider storing previous Position, rather than Node, and // making use of EditingUtilies::isEditablePosition() directly. Node* newAnchorNode = currentCaretPosition.position().parentAnchoredEquivalent().anchorNode(); if (newNode && newAnchorNode && newNode != newAnchorNode && newAnchorNode->layoutObject() && newAnchorNode->layoutObject()->isBox()) { newNode->layoutObject()->mapToVisualRectInAncestorSpace( toLayoutBoxModelObject(newAnchorNode->layoutObject()), newRect); } // It's possible for the timer to be inactive even though we want to // invalidate the caret. For example, when running as a layout test the // caret blink interval could be zero and thus |m_caretBlinkTimer| will // never be started. We provide |forceInvalidation| for use by paint // invalidation internals where we need to invalidate the caret regardless // of timer state. if (!forceInvalidation && !m_caretBlinkTimer.isActive() && newNode == m_previousCaretNode && newRect == m_previousCaretRect && m_caretVisibility == m_previousCaretVisibility) return; if (m_previousCaretAnchorNode && shouldRepaintCaret(*m_previousCaretAnchorNode)) { invalidateLocalCaretRect(m_previousCaretAnchorNode.get(), m_previousCaretRect); } if (newAnchorNode && shouldRepaintCaret(*newAnchorNode)) invalidateLocalCaretRect(newAnchorNode, newRect); m_previousCaretNode = newNode; m_previousCaretAnchorNode = newAnchorNode; m_previousCaretRect = newRect; m_previousCaretVisibility = m_caretVisibility; }
TEST_F(PaintControllerPaintTestForSlimmingPaintV2, ChunkIdClientCacheFlag) { setBodyInnerHTML( "<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>" " <div style='width: 100px; height: 100px; background-color: " "blue'></div>" " <div style='width: 100px; height: 100px; background-color: " "blue'></div>" "</div>"); PaintLayer& htmlLayer = *toLayoutBoxModelObject(document().documentElement()->layoutObject()) ->layer(); LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div")); LayoutObject& subDiv = *div.firstChild(); LayoutObject& subDiv2 = *subDiv.nextSibling(); EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 11, TestDisplayItem(layoutView(), DisplayItem::kClipFrameToVisibleContentRect), TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence), TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(htmlLayer, DisplayItem::kSubsequence), TestDisplayItem(div, DisplayItem::kBeginCompositing), TestDisplayItem(subDiv, backgroundType), TestDisplayItem(subDiv2, backgroundType), TestDisplayItem(div, DisplayItem::kEndCompositing), TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence), TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence), TestDisplayItem(layoutView(), DisplayItem::clipTypeToEndClipType( DisplayItem::kClipFrameToVisibleContentRect))); const PaintChunk& backgroundChunk = rootPaintController().paintChunks()[0]; EXPECT_TRUE(backgroundChunk.properties.scroll->isRoot()); const EffectPaintPropertyNode* effectNode = div.paintProperties()->effect(); EXPECT_EQ(0.5f, effectNode->opacity()); const PaintChunk& chunk = rootPaintController().paintChunks()[1]; EXPECT_EQ(*div.layer(), chunk.id->client); EXPECT_EQ(effectNode, chunk.properties.effect.get()); EXPECT_FALSE(div.layer()->isJustCreated()); // Client used by only paint chunks and non-cachaeable display items but not // by any cacheable display items won't be marked as validly cached. EXPECT_FALSE(rootPaintController().clientCacheIsValid(*div.layer())); EXPECT_FALSE(rootPaintController().clientCacheIsValid(div)); EXPECT_TRUE(rootPaintController().clientCacheIsValid(subDiv)); }
TEST_P(CompositedLayerMappingTest, RotatedInterestRectNear90Degrees) { setBodyInnerHTML( "<div id='target' style='width: 10000px; height: 200px; will-change: " "transform; transform: rotateY(89.9999deg)'></div>"); document().view()->updateAllLifecyclePhases(); Element* element = document().getElementById("target"); PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer(); ASSERT_TRUE(!!paintLayer->graphicsLayerBacking()); // Because the layer is rotated to almost 90 degrees, floating-point error // leads to a reverse-projected rect that is much much larger than the // original layer size in certain dimensions. In such cases, we often fall // back to the 4000px interest rect padding amount. EXPECT_RECT_EQ(IntRect(0, 0, 4000, 200), recomputeInterestRect(paintLayer->graphicsLayerBacking())); }
static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { if (!object.isBoxModelObject() || !object.hasOverflowClip()) return nullptr; PaintLayer* layer = toLayoutBoxModelObject(object).layer(); ASSERT(layer); DoubleSize scrollOffset = layer->scrollableArea()->scrollOffset(); if (scrollOffset.isZero() && !layer->scrollsOverflow()) return nullptr; RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = TransformPaintPropertyNode::create( TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()), FloatPoint3D(), context.currentTransform); context.currentTransform = newTransformNodeForScrollTranslation.get(); return newTransformNodeForScrollTranslation.release(); }
void IntersectionObservation::clipToRoot(LayoutRect& rect) { // Map and clip rect into root element coordinates. // TODO(szager): the writing mode flipping needs a test. ASSERT(m_target); LayoutObject* rootLayoutObject = m_observer->rootLayoutObject(); LayoutObject* targetLayoutObject = target()->layoutObject(); targetLayoutObject->mapToVisibleRectInAncestorSpace(toLayoutBoxModelObject(rootLayoutObject), rect, nullptr); if (rootLayoutObject->hasOverflowClip()) { LayoutBox* rootLayoutBox = toLayoutBox(rootLayoutObject); LayoutRect clipRect(LayoutPoint(), LayoutSize(rootLayoutBox->layer()->size())); m_observer->applyRootMargin(clipRect); rootLayoutBox->flipForWritingMode(rect); rect.intersect(clipRect); rootLayoutBox->flipForWritingMode(rect); } }
void MouseRelatedEvent::computeRelativePosition() { Node* targetNode = target() ? target()->toNode() : nullptr; if (!targetNode) return; // Compute coordinates that are based on the target. m_layerLocation = m_pageLocation; m_offsetLocation = m_pageLocation; // Must have an updated layout tree for this math to work correctly. targetNode->document().updateLayoutIgnorePendingStylesheets(); // Adjust offsetLocation to be relative to the target's padding box. if (LayoutObject* r = targetNode->layoutObject()) { FloatPoint localPos = r->absoluteToLocal(FloatPoint(absoluteLocation()), UseTransforms); // Adding this here to address crbug.com/570666. Basically we'd like to // find the local coordinates relative to the padding box not the border box. if (r->isBoxModelObject()) { LayoutBoxModelObject* layoutBox = toLayoutBoxModelObject(r); localPos.move(-layoutBox->borderLeft(), -layoutBox->borderTop()); } m_offsetLocation = roundedLayoutPoint(localPos); float scaleFactor = 1 / pageZoomFactor(this); if (scaleFactor != 1.0f) m_offsetLocation.scale(scaleFactor, scaleFactor); } // Adjust layerLocation to be relative to the layer. // FIXME: event.layerX and event.layerY are poorly defined, // and probably don't always correspond to PaintLayer offsets. // https://bugs.webkit.org/show_bug.cgi?id=21868 Node* n = targetNode; while (n && !n->layoutObject()) n = n->parentNode(); if (n) { // FIXME: This logic is a wrong implementation of convertToLayerCoords. for (PaintLayer* layer = n->layoutObject()->enclosingLayer(); layer; layer = layer->parent()) m_layerLocation -= toLayoutSize(layer->location()); } m_hasCachedRelativePosition = true; }