int RenderSlider::currentPosition() { ASSERT(m_thumb); ASSERT(m_thumb->renderer()); if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) return toRenderBox(m_thumb->renderer())->y() - contentBoxRect().y(); return toRenderBox(m_thumb->renderer())->x() - contentBoxRect().x(); }
void RenderImage::paintInvalidationOrMarkForLayout(const IntRect* rect) { ASSERT(isRooted()); LayoutSize oldIntrinsicSize = intrinsicSize(); LayoutSize newIntrinsicSize = m_imageResource->intrinsicSize(); updateIntrinsicSizeIfNeeded(newIntrinsicSize); bool imageSourceHasChangedSize = oldIntrinsicSize != newIntrinsicSize; if (imageSourceHasChangedSize) setPreferredLogicalWidthsDirty(); // If the actual area occupied by the image has changed and it is not constrained by style then a layout is required. bool imageSizeIsConstrained = style()->logicalWidth().isSpecified() && style()->logicalHeight().isSpecified(); // FIXME: We only need to recompute the containing block's preferred size if the containing block's size // depends on the image's size (i.e., the container uses shrink-to-fit sizing). // There's no easy way to detect that shrink-to-fit is needed, always force a layout. bool containingBlockNeedsToRecomputePreferredSize = style()->logicalWidth().isPercent() || style()->logicalMaxWidth().isPercent() || style()->logicalMinWidth().isPercent(); if (imageSourceHasChangedSize && (!imageSizeIsConstrained || containingBlockNeedsToRecomputePreferredSize)) { setNeedsLayoutAndFullPaintInvalidation(); return; } // The image hasn't changed in size or its style constrains its size, so a paint invalidation will suffice. if (everHadLayout() && !selfNeedsLayout()) { // The inner content rectangle is calculated during layout, but may need an update now // (unless the box has already been scheduled for layout). In order to calculate it, we // may need values from the containing block, though, so make sure that we're not too // early. It may be that layout hasn't even taken place once yet. updateInnerContentRect(); } LayoutRect paintInvalidationRect; if (rect) { // The image changed rect is in source image coordinates, // so map from the bounds of the image to the contentsBox. paintInvalidationRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), m_imageResource->imageSize()), contentBoxRect())); // Guard against too-large changed rects. paintInvalidationRect.intersect(contentBoxRect()); } else { paintInvalidationRect = contentBoxRect(); } { // FIXME: We should not be allowing paint invalidations during layout. crbug.com/339584 AllowPaintInvalidationScope scoper(frameView()); DisableCompositingQueryAsserts disabler; invalidatePaintRectangle(paintInvalidationRect); } // Tell any potential compositing layers that the image needs updating. contentChanged(ImageChanged); }
void RenderSnapshottedPlugIn::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; LayoutSize oldSize = contentBoxRect().size(); RenderEmbeddedObject::layout(); LayoutSize newSize = contentBoxRect().size(); if (newSize == oldSize) return; view().frameView().addEmbeddedObjectToUpdate(*this); }
void RenderWidget::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { IntPoint contentPaintOffset = roundedIntPoint(paintOffset + location() + contentBoxRect().location()); // Tell the widget to paint now. This is the only time the widget is allowed // to paint itself. That way it will composite properly with z-indexed layers. LayoutRect paintRect = paintInfo.rect; IntPoint widgetLocation = m_widget->frameRect().location(); IntSize widgetPaintOffset = contentPaintOffset - widgetLocation; // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer, // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing. if (!widgetPaintOffset.isZero()) { paintInfo.context().translate(widgetPaintOffset); paintRect.move(-widgetPaintOffset); } // FIXME: Remove repaintrect encolsing/integral snapping when RenderWidget becomes device pixel snapped. m_widget->paint(paintInfo.context(), snappedIntRect(paintRect)); if (!widgetPaintOffset.isZero()) paintInfo.context().translate(-widgetPaintOffset); if (is<FrameView>(*m_widget)) { FrameView& frameView = downcast<FrameView>(*m_widget); bool runOverlapTests = !frameView.useSlowRepaintsIfNotOverlapped(); if (paintInfo.overlapTestRequests && runOverlapTests) { ASSERT(!paintInfo.overlapTestRequests->contains(this)); paintInfo.overlapTestRequests->set(this, m_widget->frameRect()); } } }
bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { if (request.allowsChildFrameContent() && widget() && widget()->isFrameView() && toFrameView(widget())->renderView()) { FrameView* childFrameView = toFrameView(widget()); RenderView* childRoot = childFrameView->renderView(); LayoutPoint adjustedLocation = accumulatedOffset + location(); LayoutPoint contentOffset = LayoutPoint(borderLeft() + paddingLeft(), borderTop() + paddingTop()) - childFrameView->scrollOffset(); HitTestLocation newHitTestLocation(locationInContainer, -adjustedLocation - contentOffset); HitTestRequest newHitTestRequest(request.type() | HitTestRequest::ChildFrameHitTest); HitTestResult childFrameResult(newHitTestLocation); bool isInsideChildFrame = childRoot->hitTest(newHitTestRequest, newHitTestLocation, childFrameResult); if (newHitTestLocation.isRectBasedTest()) result.append(childFrameResult); else if (isInsideChildFrame) result = childFrameResult; if (isInsideChildFrame) return true; } bool hadResult = result.innerNode(); bool inside = RenderReplaced::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action); // Check to see if we are really over the widget itself (and not just in the border/padding area). if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == &frameOwnerElement()) result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); return inside; }
bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const IntPoint& pointInContainer, const IntPoint& accumulatedOffset, HitTestAction hitTestAction) { IntPoint pointInParent = pointInContainer - toSize(accumulatedOffset); IntPoint pointInBorderBox = pointInParent - parentOriginToBorderBox(); // Note: For now, we're ignoring hits to border and padding for <svg> IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox(); if (!contentBoxRect().contains(pointInContentBox)) return false; IntPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) { // FIXME: CSS/HTML assumes the local point is relative to the border box, right? updateHitTestResult(result, pointInBorderBox); // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. result.addNodeToRectBasedTestResult(child->node(), pointInContainer); return true; } } // If we didn't early exit above, we've just hit the container <svg> element. Unlike SVG 1.1, 2nd Edition allows container elements to be hit. if (hitTestAction == HitTestBlockBackground && style()->pointerEvents() != PE_NONE) { // Only return true here, if the last hit testing phase 'BlockBackground' is executed. If we'd return true in the 'Foreground' phase, // hit testing would stop immediately. For SVG only trees this doesn't matter. Though when we have a <foreignObject> subtree we need // to be able to detect hits on the background of a <div> element. If we'd return true here in the 'Foreground' phase, we are not able // to detect these hits anymore. updateHitTestResult(result, roundedIntPoint(localPoint)); return true; } return false; }
bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned) const { if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) return false; if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded()) return false; if (!contentBoxRect().contains(localRect)) return false; EFillBox backgroundClip = style()->backgroundClip(); // Background paints under borders. if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground()) return false; // Background shows in padding area. if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding()) return false; // Object-position may leave parts of the content box empty, regardless of the value of object-fit. if (style()->objectPosition() != RenderStyle::initialObjectPosition()) return false; // Object-fit may leave parts of the content box empty. ObjectFit objectFit = style()->objectFit(); if (objectFit != ObjectFitFill && objectFit != ObjectFitCover) return false; // Check for image with alpha. return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this); }
LayoutRect RenderReplaced::replacedContentRect(const LayoutSize& intrinsicSize) const { LayoutRect contentRect = contentBoxRect(); if (intrinsicSize.isEmpty()) return contentRect; ObjectFit objectFit = style().objectFit(); 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; FALLTHROUGH; case ObjectFitNone: finalRect.setSize(intrinsicSize); break; case ObjectFitFill: break; } LengthPoint objectPosition = style().objectPosition(); LayoutUnit xOffset = minimumValueForLength(objectPosition.x(), contentRect.width() - finalRect.width()); LayoutUnit yOffset = minimumValueForLength(objectPosition.y(), contentRect.height() - finalRect.height()); finalRect.move(xOffset, yOffset); return finalRect; }
bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) { IntPoint pointInContainer(_x, _y); IntSize containerToParentOffset(_tx, _ty); IntPoint pointInParent = pointInContainer - containerToParentOffset; IntPoint pointInBorderBox = pointInParent - parentOriginToBorderBox(); // Note: For now, we're ignoring hits to border and padding for <svg> IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox(); if (!contentBoxRect().contains(pointInContentBox)) return false; IntPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) { // FIXME: CSS/HTML assumes the local point is relative to the border box, right? updateHitTestResult(result, pointInBorderBox); return true; } } // Spec: Only graphical elements can be targeted by the mouse, so we don't check self here. // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." return false; }
void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { // Not allowed to overflow the content box. paintInfo.context->save(); paintInfo.context->clip(pixelSnappedIntRect(contentRect)); } // FIXME: InterpolationNone should be used if ImageRenderingOptimizeContrast is set. // See bug for more details: crbug.com/353716. InterpolationQuality interpolationQuality = style()->imageRendering() == ImageRenderingOptimizeContrast ? InterpolationLow : CanvasDefaultInterpolationQuality; HTMLCanvasElement* canvas = toHTMLCanvasElement(node()); LayoutSize layoutSize = contentRect.size(); if (style()->imageRendering() == ImageRenderingPixelated && (layoutSize.width() > canvas->width() || layoutSize.height() > canvas->height() || layoutSize == canvas->size())) { interpolationQuality = InterpolationNone; } InterpolationQuality previousInterpolationQuality = context->imageInterpolationQuality(); context->setImageInterpolationQuality(interpolationQuality); canvas->paint(context, paintRect); context->setImageInterpolationQuality(previousInterpolationQuality); if (clip) context->restore(); }
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 RenderVideo::videoBox() const { if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage()) return IntRect(); LayoutSize elementSize; if (videoElement()->shouldDisplayPosterImage()) elementSize = m_cachedImageSize; else elementSize = intrinsicSize(); IntRect contentRect = pixelSnappedIntRect(contentBoxRect()); if (elementSize.isEmpty() || contentRect.isEmpty()) return IntRect(); LayoutRect renderBox = contentRect; LayoutUnit ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width(); if (ratio > 0) { LayoutUnit newWidth = renderBox.height() * elementSize.width() / elementSize.height(); // Just fill the whole area if the difference is one pixel or less (in both sides) if (renderBox.width() - newWidth > 2) renderBox.setWidth(newWidth); renderBox.move((contentRect.width() - renderBox.width()) / 2, 0); } else if (ratio < 0) { LayoutUnit newHeight = renderBox.width() * elementSize.height() / elementSize.width(); if (renderBox.height() - newHeight > 2) renderBox.setHeight(newHeight); renderBox.move(0, (contentRect.height() - renderBox.height()) / 2); } return pixelSnappedIntRect(renderBox); }
void RenderSnapshottedPlugIn::paintReplacedSnapshotWithLabel(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (contentBoxRect().isEmpty()) return; if (!plugInImageElement()->hovered() && m_showReason == UserMousedOver) return; m_showedLabelOnce = true; LayoutRect rect = contentBoxRect(); LayoutRect labelRect = tryToFitStartLabel(LabelSizeLarge, rect); LabelSize size = NoLabel; if (!labelRect.isEmpty()) size = LabelSizeLarge; else { labelRect = tryToFitStartLabel(LabelSizeSmall, rect); if (!labelRect.isEmpty()) size = LabelSizeSmall; else return; } Image* labelImage = startLabelImage(size); if (!labelImage) return; RefPtr<Image> snapshotImage = m_snapshotResource->image(); if (!snapshotImage || snapshotImage->isNull()) return; #if ENABLE(FILTERS) RefPtr<Image> blurredSnapshotImage = m_snapshotResourceForLabel->image(); if (!blurredSnapshotImage || blurredSnapshotImage->isNull()) { blurredSnapshotImage = snapshottedPluginImageForLabelDisplay(snapshotImage, labelRect); m_snapshotResourceForLabel->setCachedImage(new CachedImage(blurredSnapshotImage.get())); } snapshotImage = blurredSnapshotImage; #endif paintSnapshot(snapshotImage.get(), paintInfo, paintOffset); // Remember that the labelRect includes the label inset, so we need to adjust for it. paintInfo.context->drawImage(labelImage, ColorSpaceDeviceRGB, IntRect(roundedIntPoint(paintOffset + labelRect.location() - IntSize(startLabelInset, startLabelInset)), roundedIntSize(labelRect.size() + IntSize(2 * startLabelInset, 2 * startLabelInset))), labelImage->rect()); }
LayoutSize RenderMultiColumnSet::flowThreadTranslationAtOffset(LayoutUnit blockOffset) const { unsigned columnIndex = columnIndexAtOffset(blockOffset); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flipForWritingMode(portionRect); LayoutRect columnRect(columnRectAt(columnIndex)); flipForWritingMode(columnRect); return contentBoxRect().location() + columnRect.location() - portionRect.location(); }
bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) { bool hadResult = result.innerNode(); bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); // Check to see if we are really over the widget itself (and not just in the border/padding area). if (inside && !hadResult && result.innerNode() == element()) result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); return inside; }
bool LayoutPart::nodeAtPointOverWidget(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { bool hadResult = result.innerNode(); bool inside = LayoutReplaced::nodeAtPoint(result, locationInContainer, accumulatedOffset, action); // Check to see if we are really over the widget itself (and not just in the border/padding area). if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node()) result.setIsOverWidget(contentBoxRect().contains(result.localPoint())); return inside; }
bool RenderWidget::updateWidgetGeometry() { LayoutRect contentBox = contentBoxRect(); LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); if (m_widget->isFrameView()) { contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); } return setWidgetGeometry(absoluteContentBox); }
void RenderMedia::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; LayoutSize oldSize = contentBoxRect().size(); RenderImage::layout(); RenderBox* controlsRenderer = toRenderBox(m_children.firstChild()); if (!controlsRenderer) return; bool controlsNeedLayout = controlsRenderer->needsLayout(); // If the region chain has changed we also need to relayout the controls to update the region box info. // FIXME: We can do better once we compute region box info for RenderReplaced, not only for RenderBlock. const RenderFlowThread* flowThread = flowThreadContainingBlock(); if (flowThread && !controlsNeedLayout) { if (flowThread->pageLogicalSizeChanged()) controlsNeedLayout = true; } LayoutSize newSize = contentBoxRect().size(); if (newSize == oldSize && !controlsNeedLayout) return; // When calling layout() on a child node, a parent must either push a LayoutStateMaintainter, or // instantiate LayoutStateDisabler. Since using a LayoutStateMaintainer is slightly more efficient, // and this method will be called many times per second during playback, use a LayoutStateMaintainer: LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); controlsRenderer->setLocation(LayoutPoint(borderLeft(), borderTop()) + LayoutSize(paddingLeft(), paddingTop())); controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed)); controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed)); controlsRenderer->setNeedsLayout(true, MarkOnlyThis); controlsRenderer->layout(); setChildNeedsLayout(false); statePusher.pop(); }
bool LayoutPart::updateWidgetGeometry() { Widget* widget = this->widget(); ASSERT(widget); LayoutRect contentBox = contentBoxRect(); LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(contentBox)).boundingBox()); if (widget->isFrameView()) { contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); } return setWidgetGeometry(absoluteContentBox); }
void RenderRegion::computeOverflowFromFlowThread() { ASSERT(isValid()); LayoutRect layoutRect = overflowRectForFlowThreadPortion(flowThreadPortionRect(), isFirstRegion(), isLastRegion(), LayoutOverflow); layoutRect.setLocation(contentBoxRect().location() + (layoutRect.location() - m_flowThreadPortionRect.location())); // FIXME: Correctly adjust the layout overflow for writing modes. addLayoutOverflow(layoutRect); updateLayerTransform(); updateScrollInfoAfterLayout(); }
LayoutRect LayoutReplaced::computeObjectFit( const LayoutSize* overriddenIntrinsicSize) const { LayoutRect contentRect = contentBoxRect(); ObjectFit objectFit = style()->getObjectFit(); if (objectFit == ObjectFitFill && style()->objectPosition() == ComputedStyle::initialObjectPosition()) { return contentRect; } // TODO(davve): intrinsicSize doubles as both intrinsic size and intrinsic // ratio. In the case of SVG images this isn't correct since they can have // intrinsic ratio but no intrinsic size. In order to maintain aspect ratio, // the intrinsic size for SVG might be faked from the aspect ratio, // see SVGImage::containerSize(). LayoutSize intrinsicSize = overriddenIntrinsicSize ? *overriddenIntrinsicSize : this->intrinsicSize(); 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: break; default: ASSERT_NOT_REACHED(); } LayoutUnit xOffset = minimumValueForLength( style()->objectPosition().x(), contentRect.width() - finalRect.width()); LayoutUnit yOffset = minimumValueForLength( style()->objectPosition().y(), contentRect.height() - finalRect.height()); finalRect.move(xOffset, yOffset); return finalRect; }
bool LayoutPart::updateWidgetGeometryInternal() { Widget* widget = this->widget(); ASSERT(widget); LayoutRect contentBox = contentBoxRect(); LayoutRect absoluteContentBox(localToAbsoluteQuad(FloatQuad(FloatRect(contentBox))).boundingBox()); if (widget->isFrameView()) { if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) contentBox.setLocation(absoluteContentBox.location()); return setWidgetGeometry(contentBox); } // TODO(chrishtr): why are these widgets using an absolute rect for their frameRect? return setWidgetGeometry(absoluteContentBox); }
LayoutRect LayoutMenuList::controlClipRect( const LayoutPoint& additionalOffset) const { // Clip to the intersection of the content box and the content box for the // inner box. This will leave room for the arrows which sit in the inner box // padding, and if the inner box ever spills out of the outer box, that will // get clipped too. LayoutRect outerBox = contentBoxRect(); outerBox.moveBy(additionalOffset); LayoutRect innerBox( additionalOffset + m_innerBlock->location() + LayoutSize(m_innerBlock->paddingLeft(), m_innerBlock->paddingTop()), m_innerBlock->contentSize()); return intersection(outerBox, innerBox); }
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { GraphicsContext* context = paintInfo.context; if (m_imageResource->hasImage() && contentWidth() > 0 && contentHeight() > 0) { LayoutRect contentRect = contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }
bool LayoutSVGRoot::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { LayoutPoint pointInParent = locationInContainer.point() - toLayoutSize(accumulatedOffset); LayoutPoint pointInBorderBox = pointInParent - toLayoutSize(location()); // Only test SVG content if the point is in our content box, or in case we // don't clip to the viewport, the visual overflow rect. // FIXME: This should be an intersection when rect-based hit tests are supported by nodeAtFloatPoint. if (contentBoxRect().contains(pointInBorderBox) || (!shouldApplyViewportClip() && visualOverflowRect().contains(pointInBorderBox))) { const AffineTransform& localToParentTransform = this->localToParentTransform(); if (localToParentTransform.isInvertible()) { FloatPoint localPoint = localToParentTransform.inverse().mapPoint(FloatPoint(pointInParent)); for (LayoutObject* child = lastChild(); child; child = child->previousSibling()) { // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. if (child->nodeAtFloatPoint(result, localPoint, hitTestAction)) { updateHitTestResult(result, pointInBorderBox); if (result.addNodeToListBasedTestResult(child->node(), locationInContainer) == StopHitTesting) return true; } } } } // If we didn't early exit above, we've just hit the container <svg> element. Unlike SVG 1.1, 2nd Edition allows container elements to be hit. if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && visibleToHitTestRequest(result.hitTestRequest())) { // Only return true here, if the last hit testing phase 'BlockBackground' (or 'ChildBlockBackground' - depending on context) is executed. // If we'd return true in the 'Foreground' phase, hit testing would stop immediately. For SVG only trees this doesn't matter. // Though when we have a <foreignObject> subtree we need to be able to detect hits on the background of a <div> element. // If we'd return true here in the 'Foreground' phase, we are not able to detect these hits anymore. LayoutRect boundsRect(accumulatedOffset + location(), size()); if (locationInContainer.intersects(boundsRect)) { updateHitTestResult(result, pointInBorderBox); if (result.addNodeToListBasedTestResult(node(), locationInContainer, boundsRect) == StopHitTesting) return true; } } return false; }
LayoutRect RenderReplaced::replacedContentRect(const LayoutSize* overriddenIntrinsicSize) const { LayoutRect contentRect = contentBoxRect(); ObjectFit objectFit = style()->objectFit(); if (objectFit == ObjectFitFill && style()->objectPosition() == RenderStyle::initialObjectPosition()) { if (RuntimeEnabledFeatures::objectFitPositionEnabled()) return contentRect; objectFit = ObjectFitContain; } LayoutSize intrinsicSize = overriddenIntrinsicSize ? *overriddenIntrinsicSize : this->intrinsicSize(); 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: break; default: ASSERT_NOT_REACHED(); } LayoutUnit xOffset = minimumValueForLength(style()->objectPosition().x(), contentRect.width() - finalRect.width()); LayoutUnit yOffset = minimumValueForLength(style()->objectPosition().y(), contentRect.height() - finalRect.height()); finalRect.move(xOffset, yOffset); return finalRect; }
void RenderRegion::computeOverflowFromFlowThread() { ASSERT(isValid()); LayoutRect layoutRect; { // When getting the overflow from the flow thread we need to temporarly reset the current flow thread because // we're changing flows. CurrentRenderFlowThreadMaintainer flowThreadMaintainer(m_flowThread); layoutRect = layoutOverflowRectForBox(m_flowThread); } layoutRect.setLocation(contentBoxRect().location() + (layoutRect.location() - m_flowThreadPortionRect.location())); // FIXME: Correctly adjust the layout overflow for writing modes. addLayoutOverflow(layoutRect); RenderFlowThread* enclosingRenderFlowThread = flowThreadContainingBlock(); if (enclosingRenderFlowThread) enclosingRenderFlowThread->addRegionsLayoutOverflow(this, layoutRect); updateLayerTransform(); updateScrollInfoAfterLayout(); }
IntRect RenderSlider::thumbRect() { if (!m_thumb) return IntRect(); IntRect thumbRect; RenderBox* thumb = toRenderBox(m_thumb->renderer()); thumbRect.setWidth(thumb->style()->width().calcMinValue(contentWidth())); thumbRect.setHeight(thumb->style()->height().calcMinValue(contentHeight())); double fraction = sliderPosition(static_cast<HTMLInputElement*>(node())); IntRect contentRect = contentBoxRect(); if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) { thumbRect.setX(contentRect.x() + (contentRect.width() - thumbRect.width()) / 2); thumbRect.setY(contentRect.y() + static_cast<int>(nextafter((contentRect.height() - thumbRect.height()) + 1, 0) * (1 - fraction))); } else { thumbRect.setX(contentRect.x() + static_cast<int>(nextafter((contentRect.width() - thumbRect.width()) + 1, 0) * fraction)); thumbRect.setY(contentRect.y() + (contentRect.height() - thumbRect.height()) / 2); } return thumbRect; }
bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { LayoutPoint pointInParent = locationInContainer.point() - toLayoutSize(accumulatedOffset); LayoutPoint pointInBorderBox = pointInParent - toLayoutSize(location()); // Only test SVG content if the point is in our content box. // FIXME: This should be an intersection when rect-based hit tests are supported by nodeAtFloatPoint. if (contentBoxRect().contains(pointInBorderBox)) { FloatPoint localPoint = localToParentTransform().inverse().mapPoint(FloatPoint(pointInParent)); for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) { updateHitTestResult(result, pointInBorderBox); if (!result.addNodeToRectBasedTestResult(child->node(), request, locationInContainer)) return true; } } } // If we didn't early exit above, we've just hit the container <svg> element. Unlike SVG 1.1, 2nd Edition allows container elements to be hit. if (hitTestAction == HitTestBlockBackground && visibleToHitTesting()) { // Only return true here, if the last hit testing phase 'BlockBackground' is executed. If we'd return true in the 'Foreground' phase, // hit testing would stop immediately. For SVG only trees this doesn't matter. Though when we have a <foreignObject> subtree we need // to be able to detect hits on the background of a <div> element. If we'd return true here in the 'Foreground' phase, we are not able // to detect these hits anymore. LayoutRect boundsRect(accumulatedOffset + location(), size()); if (locationInContainer.intersects(boundsRect)) { updateHitTestResult(result, pointInBorderBox); if (!result.addNodeToRectBasedTestResult(&svgSVGElement(), request, locationInContainer, boundsRect)) return true; } } return false; }
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()); } }