// Hit Testing bool RenderRegion::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { if (!isValid() || action != HitTestForeground) return false; LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); boundsRect.moveBy(accumulatedOffset); if (visibleToHitTesting() && locationInContainer.intersects(boundsRect)) { if (m_flowThread->hitTestFlowThreadPortionInRegion(this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), request, result, locationInContainer, LayoutPoint(accumulatedOffset.x() + borderLeft() + paddingLeft(), accumulatedOffset.y() + borderTop() + paddingTop()))) return true; } return false; }
MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, EventTarget* relatedTarget, RawPtr<AbstractView> abstractView, int detail, const IntPoint& screenLocation, const IntPoint& rootFrameLocation, const IntPoint& movementDelta, PlatformEvent::Modifiers modifiers, double platformTimeStamp, PositionType positionType, InputDeviceCapabilities* sourceCapabilities) : UIEventWithKeyState(eventType, canBubble, cancelable, relatedTarget, abstractView, detail, modifiers, platformTimeStamp, sourceCapabilities) , m_screenLocation(screenLocation) , m_movementDelta(movementDelta) , m_positionType(positionType) { LayoutPoint adjustedPageLocation; LayoutPoint scrollPosition; LocalFrame* frame = view() && view()->isLocalDOMWindow() ? toLocalDOMWindow(view())->frame() : nullptr; if (frame && hasPosition()) { if (FrameView* frameView = frame->view()) { scrollPosition = frameView->scrollPosition(); adjustedPageLocation = frameView->rootFrameToContents(rootFrameLocation); float scaleFactor = 1 / frame->pageZoomFactor(); if (scaleFactor != 1.0f) { adjustedPageLocation.scale(scaleFactor, scaleFactor); scrollPosition.scale(scaleFactor, scaleFactor); } } } m_clientLocation = adjustedPageLocation - toLayoutSize(scrollPosition); m_pageLocation = adjustedPageLocation; // Set up initial values for coordinates. // Correct values are computed lazily, see computeRelativePosition. m_layerLocation = m_pageLocation; m_offsetLocation = m_pageLocation; computePageLocation(); m_hasCachedRelativePosition = false; }
bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { LayoutPoint adjustedLocation = accumulatedOffset + roundedLayoutPoint(topLeft()); // Hit test the markup box. if (InlineBox* markupBox = this->markupBox()) { RenderStyle* style = m_renderer->style(isFirstLineStyle()); LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x(); LayoutUnit mty = adjustedLocation.y() + style->fontMetrics().ascent() - (markupBox->y() + markupBox->renderer()->style(isFirstLineStyle())->fontMetrics().ascent()); if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom)) { renderer()->updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty)); return true; } } LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height)); if (visibleToHitTestRequest(request) && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) { renderer()->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(renderer()->node(), request, locationInContainer, boundsRect)) return true; } return false; }
RenderRegion* RenderFlowThread::mapFromFlowToRegion(TransformState& transformState) const { if (!hasValidRegionInfo()) return 0; LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox(); flipForWritingMode(boxRect); // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions, // for now we just take the center of the mapped enclosing box and map it to a region. // Note: Using the center in order to avoid rounding errors. LayoutPoint center = boxRect.center(); RenderRegion* renderRegion = regionAtBlockOffset(isHorizontalWritingMode() ? center.y() : center.x(), true); if (!renderRegion) return 0; LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect()); flipForWritingMode(flippedRegionRect); transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location()); return renderRegion; }
VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents, const RenderRegion* region) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = downcast<SVGRootInlineBox>(*rootBox).closestLeafChildForPosition(pointInContents); if (!closestBox) return createVisiblePosition(0, DOWNSTREAM); return closestBox->renderer().positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()), region); }
PositionWithAffinity RenderSVGText::positionForPoint(const LayoutPoint& pointInContents) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createPositionWithAffinity(0, DOWNSTREAM); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = toSVGRootInlineBox(rootBox)->closestLeafChildForPosition(pointInContents); if (!closestBox) return createPositionWithAffinity(0, DOWNSTREAM); return closestBox->renderer().positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y())); }
void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point) { HTMLInputElement* input = hostInput(); if (!input->renderer() || !renderer()) return; LayoutPoint offset = roundedLayoutPoint(input->renderer()->absoluteToLocal(point, false, true)); bool isVertical = hasVerticalAppearance(input); LayoutUnit trackSize; LayoutUnit position; LayoutUnit currentPosition; // We need to calculate currentPosition from absolute points becaue the // renderer for this node is usually on a layer and renderBox()->x() and // y() are unusable. LayoutPoint absoluteThumbOrigin = renderBox()->absoluteBoundingBoxRect().location(); LayoutPoint absoluteSliderContentOrigin = roundedLayoutPoint(input->renderer()->localToAbsolute()); if (isVertical) { trackSize = input->renderBox()->contentHeight() - renderBox()->height(); position = offset.y() - renderBox()->height() / 2; currentPosition = absoluteThumbOrigin.y() - absoluteSliderContentOrigin.y(); } else { trackSize = input->renderBox()->contentWidth() - renderBox()->width(); position = offset.x() - renderBox()->width() / 2; currentPosition = absoluteThumbOrigin.x() - absoluteSliderContentOrigin.x(); } position = max<LayoutUnit>(0, min(position, trackSize)); if (position == currentPosition) return; StepRange range(input); double fraction = static_cast<double>(position) / trackSize; if (isVertical || !renderBox()->style()->isLeftToRightDirection()) fraction = 1 - fraction; double value = range.clampValue(range.valueFromProportion(fraction)); // FIXME: This is no longer being set from renderer. Consider updating the method name. input->setValueFromRenderer(serializeForNumberType(value)); renderer()->setNeedsLayout(true); input->dispatchFormControlChangeEvent(); }
bool RenderLineBoxList::rangeIntersectsRect(RenderBoxModelObject* renderer, LayoutUnit logicalTop, LayoutUnit logicalBottom, const LayoutRect& rect, const LayoutPoint& offset) const { RenderBox* block; if (renderer->isBox()) block = toRenderBox(renderer); else block = renderer->containingBlock(); LayoutUnit physicalStart = block->flipForWritingMode(logicalTop); LayoutUnit physicalEnd = block->flipForWritingMode(logicalBottom); LayoutUnit physicalExtent = absoluteValue(physicalEnd - physicalStart); physicalStart = min(physicalStart, physicalEnd); if (renderer->style()->isHorizontalWritingMode()) { physicalStart += offset.y(); if (physicalStart >= rect.maxY() || physicalStart + physicalExtent <= rect.y()) return false; } else { physicalStart += offset.x(); if (physicalStart >= rect.maxX() || physicalStart + physicalExtent <= rect.x()) return false; } return true; }
void RenderEmbeddedObject::paintSnapshotImage(PaintInfo& paintInfo, const LayoutPoint& paintOffset, Image* image) { LayoutUnit cWidth = contentWidth(); LayoutUnit cHeight = contentHeight(); if (!cWidth || !cHeight) return; GraphicsContext* context = paintInfo.context; LayoutSize contentSize(cWidth, cHeight); LayoutPoint contentLocation = location() + paintOffset; contentLocation.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); LayoutRect rect(contentLocation, contentSize); IntRect alignedRect = snappedIntRect(rect); if (alignedRect.width() <= 0 || alignedRect.height() <= 0) return; bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, image, alignedRect.size()); ImageOrientationDescription orientationDescription(shouldRespectImageOrientation()); #if ENABLE(CSS_IMAGE_ORIENTATION) orientationDescription.setImageOrientationEnum(style().imageOrientation()); #endif context->drawImage(image, style().colorSpace(), alignedRect, ImagePaintingOptions(orientationDescription, useLowQualityScaling)); }
void RenderFrameSet::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderObject* child = firstChild(); if (!child) return; LayoutPoint adjustedPaintOffset = paintOffset + location(); size_t rows = m_rows.m_sizes.size(); size_t cols = m_cols.m_sizes.size(); LayoutUnit borderThickness = frameSet()->border(); LayoutUnit yPos = 0; for (size_t r = 0; r < rows; r++) { LayoutUnit xPos = 0; for (size_t c = 0; c < cols; c++) { child->paint(paintInfo, adjustedPaintOffset); xPos += m_cols.m_sizes[c]; if (borderThickness && m_cols.m_allowBorder[c + 1]) { paintColumnBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x() + xPos, adjustedPaintOffset.y() + yPos, borderThickness, height()))); xPos += borderThickness; } child = child->nextSibling(); if (!child) return; } yPos += m_rows.m_sizes[r]; if (borderThickness && m_rows.m_allowBorder[r + 1]) { paintRowBorder(paintInfo, pixelSnappedIntRect(LayoutRect(adjustedPaintOffset.x(), adjustedPaintOffset.y() + yPos, width(), borderThickness))); yPos += borderThickness; } } }
bool LineBoxList::hitTest(LineLayoutBoxModel layoutObject, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) const { if (hitTestAction != HitTestForeground) return false; ASSERT(layoutObject.isLayoutBlock() || (layoutObject.isLayoutInline() && layoutObject.hasLayer())); // The only way an inline could hit test like this is if it has a layer. // If we have no lines then we have no work to do. if (!firstLineBox()) return false; LayoutPoint point = locationInContainer.point(); CullRect cullRect(firstLineBox()->isHorizontal() ? IntRect(point.x(), point.y() - locationInContainer.topPadding(), 1, locationInContainer.topPadding() + locationInContainer.bottomPadding() + 1) : IntRect(point.x() - locationInContainer.leftPadding(), point.y(), locationInContainer.rightPadding() + locationInContainer.leftPadding() + 1, 1)); if (!anyLineIntersectsRect(layoutObject, cullRect, accumulatedOffset)) return false; // See if our root lines contain the point. If so, then we hit test // them further. Note that boxes can easily overlap, so we can't make any assumptions // based off positions of our first line box or our last line box. for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevLineBox()) { RootInlineBox& root = curr->root(); if (rangeIntersectsRect(layoutObject, curr->logicalTopVisualOverflow(root.lineTop()), curr->logicalBottomVisualOverflow(root.lineBottom()), cullRect, accumulatedOffset)) { bool inside = curr->nodeAtPoint(result, locationInContainer, accumulatedOffset, root.lineTop(), root.lineBottom()); if (inside) { layoutObject.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); return true; } } } return false; }
void RenderRegion::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // Delegate painting of content in region to RenderFlowThread. if (!m_flowThread || !isValid()) return; if (Frame* frame = this->frame()) { if (Page* page = frame->page()) page->addRelevantRepaintedObject(this, paintInfo.rect); } setRegionBoxesRegionStyle(); m_flowThread->paintIntoRegion(paintInfo, this, LayoutPoint(paintOffset.x() + borderLeft() + paddingLeft(), paintOffset.y() + borderTop() + paddingTop())); restoreRegionBoxesOriginalStyle(); }
VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents, const RenderRegion* region) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); ASSERT_WITH_SECURITY_IMPLICATION(rootBox->isSVGRootInlineBox()); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = toSVGRootInlineBox(rootBox)->closestLeafChildForPosition(pointInContents); if (!closestBox) return createVisiblePosition(0, DOWNSTREAM); return closestBox->renderer().positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y()), region); }
VisiblePosition RenderSVGText::positionForPoint(const LayoutPoint& pointInContents) { RootInlineBox* rootBox = firstRootBox(); if (!rootBox) return createVisiblePosition(0, DOWNSTREAM); ASSERT(rootBox->isSVGRootInlineBox()); ASSERT(!rootBox->nextRootBox()); ASSERT(childrenInline()); InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents); if (!closestBox) return createVisiblePosition(0, DOWNSTREAM); return closestBox->renderer()->positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y())); }
void LayoutSliderContainer::layout() { HTMLInputElement* input = toHTMLInputElement(node()->shadowHost()); bool isVertical = hasVerticalAppearance(input); mutableStyleRef().setFlexDirection(isVertical ? FlowColumn : FlowRow); TextDirection oldTextDirection = style()->direction(); if (isVertical) { // FIXME: Work around rounding issues in RTL vertical sliders. We want them to // render identically to LTR vertical sliders. We can remove this work around when // subpixel rendering is enabled on all ports. mutableStyleRef().setDirection(LTR); } Element* thumbElement = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderThumb()); Element* trackElement = input->userAgentShadowRoot()->getElementById(ShadowElementNames::sliderTrack()); LayoutBox* thumb = thumbElement ? thumbElement->layoutBox() : 0; LayoutBox* track = trackElement ? trackElement->layoutBox() : 0; SubtreeLayoutScope layoutScope(*this); // Force a layout to reset the position of the thumb so the code below doesn't move the thumb to the wrong place. // FIXME: Make a custom layout class for the track and move the thumb positioning code there. if (track) layoutScope.setChildNeedsLayout(track); LayoutFlexibleBox::layout(); mutableStyleRef().setDirection(oldTextDirection); // These should always exist, unless someone mutates the shadow DOM (e.g., in the inspector). if (!thumb || !track) return; double percentageOffset = sliderPosition(input).toDouble(); LayoutUnit availableExtent = isVertical ? track->contentHeight() : track->contentWidth(); availableExtent -= isVertical ? thumb->size().height() : thumb->size().width(); LayoutUnit offset = percentageOffset * availableExtent; LayoutPoint thumbLocation = thumb->location(); if (isVertical) thumbLocation.setY(thumbLocation.y() + track->contentHeight() - thumb->size().height() - offset); else if (style()->isLeftToRightDirection()) thumbLocation.setX(thumbLocation.x() + offset); else thumbLocation.setX(thumbLocation.x() - offset); thumb->setLocation(thumbLocation); // We need one-off invalidation code here because painting of the timeline element does not go through style. // Instead it has a custom implementation in C++ code. // Therefore the style system cannot understand when it needs to be paint invalidated. setShouldDoFullPaintInvalidation(); }
LayoutPoint InlineBox::logicalPositionToPhysicalPoint(const LayoutPoint& point, const LayoutSize& size) const { if (!UNLIKELY(lineLayoutItem().hasFlippedBlocksWritingMode())) return LayoutPoint(point.x(), point.y()); LayoutBlockFlow& block = root().block(); if (block.style()->isHorizontalWritingMode()) return LayoutPoint(point.x(), block.size().height() - size.height() - point.y()); return LayoutPoint(block.size().width() - size.width() - point.x(), point.y()); }
void distanceDataForNode(FocusType type, const FocusCandidate& current, FocusCandidate& candidate) { if (areElementsOnSameLine(current, candidate)) { if ((type == FocusTypeUp && current.rect.y() > candidate.rect.y()) || (type == FocusTypeDown && candidate.rect.y() > current.rect.y())) { candidate.distance = 0; candidate.alignment = Full; return; } } LayoutRect nodeRect = candidate.rect; LayoutRect currentRect = current.rect; deflateIfOverlapped(currentRect, nodeRect); if (!isRectInDirection(type, currentRect, nodeRect)) return; LayoutPoint exitPoint; LayoutPoint entryPoint; entryAndExitPointsForDirection(type, currentRect, nodeRect, exitPoint, entryPoint); LayoutUnit xAxis = exitPoint.x() - entryPoint.x(); LayoutUnit yAxis = exitPoint.y() - entryPoint.y(); LayoutUnit navigationAxisDistance; LayoutUnit orthogonalAxisDistance; switch (type) { case FocusTypeLeft: case FocusTypeRight: navigationAxisDistance = xAxis.abs(); orthogonalAxisDistance = yAxis.abs(); break; case FocusTypeUp: case FocusTypeDown: navigationAxisDistance = yAxis.abs(); orthogonalAxisDistance = xAxis.abs(); break; default: ASSERT_NOT_REACHED(); return; } double euclidianDistancePow2 = (xAxis * xAxis + yAxis * yAxis).toDouble(); LayoutRect intersectionRect = intersection(currentRect, nodeRect); double overlap = (intersectionRect.width() * intersectionRect.height()).toDouble(); // Distance calculation is based on http://www.w3.org/TR/WICD/#focus-handling candidate.distance = sqrt(euclidianDistancePow2) + navigationAxisDistance+ orthogonalAxisDistance * 2 - sqrt(overlap); LayoutSize viewSize = candidate.visibleNode->document().page()->deprecatedLocalMainFrame()->view()->visibleContentRect().size(); candidate.alignment = alignmentForRects(type, currentRect, nodeRect, viewSize); }
void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& adjustedPaintOffset) { // An empty viewport disables rendering. if (borderBoxRect().isEmpty()) return; // Don't paint, if the context explicitely disabled it. if (paintInfo.context->paintingDisabled()) return; // Don't paint if we don't have kids, except if we have filters we should paint those. if (!firstChild()) { SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); if (!resources || !resources->filter()) return; } if (Frame* frame = this->frame()) { if (Page* page = frame->page()) page->addRelevantRepaintedObject(this, paintInfo.rect); } // Make a copy of the PaintInfo because applyTransform will modify the damage rect. PaintInfo childPaintInfo(paintInfo); childPaintInfo.context->save(); // Apply initial viewport clip - not affected by overflow handling childPaintInfo.context->clip(overflowClipRect(adjustedPaintOffset, paintInfo.renderRegion)); // Convert from container offsets (html renderers) to a relative transform (svg renderers). // Transform from our paint container's coordinate system to our local coords. childPaintInfo.applyTransform(AffineTransform::translation(adjustedPaintOffset.x() - x(), adjustedPaintOffset.y() - y()) * localToParentTransform()); bool continueRendering = true; if (childPaintInfo.phase == PaintPhaseForeground) continueRendering = SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo); if (continueRendering) RenderBox::paint(childPaintInfo, LayoutPoint()); if (childPaintInfo.phase == PaintPhaseForeground) SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, paintInfo.context); childPaintInfo.context->restore(); }
void RenderRegion::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // Delegate painting of content in region to RenderFlowThread. if (!m_flowThread || !isValid()) return; #ifndef NDEBUG m_insideRegionPaint = true; #endif setRegionBoxesRegionStyle(); m_flowThread->paintIntoRegion(paintInfo, this, LayoutPoint(paintOffset.x() + borderLeft() + paddingLeft(), paintOffset.y() + borderTop() + paddingTop())); restoreRegionBoxesOriginalStyle(); #ifndef NDEBUG m_insideRegionPaint = false; #endif }
void ImagePainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_layoutImage.contentWidth(); LayoutUnit cHeight = m_layoutImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_layoutImage.imageResource()->hasImage()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { // Draw an outline rect where the image should be. IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + m_layoutImage.borderLeft() + m_layoutImage.paddingLeft(), paintOffset.y() + m_layoutImage.borderTop() + m_layoutImage.paddingTop(), cWidth, cHeight)); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, paintRect); if (drawingRecorder.canUseCachedDrawing()) return; context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(paintRect); } } else if (cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_layoutImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_layoutImage.replacedContentRect(); paintRect.moveBy(paintOffset); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, contentRect); if (drawingRecorder.canUseCachedDrawing()) return; bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }
PositionWithAffinity RenderReplaced::positionForPoint(const LayoutPoint& point) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = point.y() + y(); if (blockDirectionPosition < top) return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); // coordinates are below return RenderBox::positionForPoint(point); }
// Hit Testing bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { if (!isValid()) return false; LayoutPoint adjustedLocation = accumulatedOffset + location(); // Check our bounds next. For this purpose always assume that we can only be hit in the // foreground phase (which is true for replaced elements like images). LayoutRect boundsRect(adjustedLocation, size()); if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(pointInContainer))) { // Check the contents of the RenderFlowThread. if (m_flowThread && m_flowThread->hitTestRegion(this, request, result, pointInContainer, LayoutPoint(adjustedLocation.x() + borderLeft() + paddingLeft(), adjustedLocation.y() + borderTop() + paddingTop()))) return true; updateHitTestResult(result, pointInContainer - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(node(), pointInContainer, boundsRect)) return true; } return false; }
void ImagePainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_layoutImage.contentWidth(); LayoutUnit cHeight = m_layoutImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_layoutImage.imageResource()->hasImage()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, m_layoutImage, paintInfo.phase, paintOffset)) return; // Draw an outline rect where the image should be. IntRect paintRect = pixelSnappedIntRect(LayoutRect(paintOffset.x() + m_layoutImage.borderLeft() + m_layoutImage.paddingLeft(), paintOffset.y() + m_layoutImage.borderTop() + m_layoutImage.paddingTop(), cWidth, cHeight)); LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, paintRect, paintOffset); context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(paintRect); } } else if (cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_layoutImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_layoutImage.replacedContentRect(); paintRect.moveBy(paintOffset); Optional<ClipRecorder> clipRecorder; if (!contentRect.contains(paintRect)) { // TODO(fmalita): can we get rid of this clip and adjust the image src/dst rect instead? clipRecorder.emplace(*context, m_layoutImage, paintInfo.displayItemTypeForClipping(), contentRect); } if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*context, m_layoutImage, paintInfo.phase, paintOffset)) return; LayoutObjectDrawingRecorder drawingRecorder(*context, m_layoutImage, paintInfo.phase, contentRect, paintOffset); paintIntoRect(context, paintRect); } }
void RenderSliderContainer::layout() { ASSERT(element()->shadowHost()); auto& input = downcast<HTMLInputElement>(*element()->shadowHost()); bool isVertical = hasVerticalAppearance(input); mutableStyle().setFlexDirection(isVertical ? FlowColumn : FlowRow); TextDirection oldTextDirection = style().direction(); if (isVertical) { // FIXME: Work around rounding issues in RTL vertical sliders. We want them to // render identically to LTR vertical sliders. We can remove this work around when // subpixel rendering is enabled on all ports. mutableStyle().setDirection(LTR); } RenderBox* thumb = input.sliderThumbElement() ? input.sliderThumbElement()->renderBox() : nullptr; RenderBox* track = input.sliderTrackElement() ? input.sliderTrackElement()->renderBox() : nullptr; // Force a layout to reset the position of the thumb so the code below doesn't move the thumb to the wrong place. // FIXME: Make a custom Render class for the track and move the thumb positioning code there. if (track) track->setChildNeedsLayout(MarkOnlyThis); RenderFlexibleBox::layout(); mutableStyle().setDirection(oldTextDirection); // These should always exist, unless someone mutates the shadow DOM (e.g., in the inspector). if (!thumb || !track) return; double percentageOffset = sliderPosition(input).toDouble(); LayoutUnit availableExtent = isVertical ? track->contentHeight() : track->contentWidth(); availableExtent -= isVertical ? thumb->height() : thumb->width(); LayoutUnit offset = percentageOffset * availableExtent; LayoutPoint thumbLocation = thumb->location(); if (isVertical) thumbLocation.setY(thumbLocation.y() + track->contentHeight() - thumb->height() - offset); else if (style().isLeftToRightDirection()) thumbLocation.setX(thumbLocation.x() + offset); else thumbLocation.setX(thumbLocation.x() - offset); thumb->setLocation(thumbLocation); thumb->repaint(); }
LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const LayoutPoint& startPoint) const { // If the element is the HTML body element or doesn't have a parent // return 0 and stop this algorithm. if (isBody() || !parent()) return LayoutPoint(); LayoutPoint referencePoint = startPoint; referencePoint.move(parent()->columnOffset(referencePoint)); // If the offsetParent of the element is null, or is the HTML body element, // return the distance between the canvas origin and the left border edge // of the element and stop this algorithm. Element* element = offsetParent(); if (!element) return referencePoint; if (const RenderBoxModelObject* offsetParent = element->renderBoxModelObject()) { if (offsetParent->isBox() && !offsetParent->isBody()) referencePoint.move(-toRenderBox(offsetParent)->borderLeft(), -toRenderBox(offsetParent)->borderTop()); if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) { if (isRelPositioned()) referencePoint.move(relativePositionOffset()); RenderObject* current; for (current = parent(); current != offsetParent && current->parent(); current = current->parent()) { // FIXME: What are we supposed to do inside SVG content? if (!isOutOfFlowPositioned()) { if (current->isBox() && !current->isTableRow()) referencePoint.moveBy(toRenderBox(current)->topLeftLocation()); referencePoint.move(current->parent()->columnOffset(referencePoint)); } } if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned()) referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation()); } } return referencePoint; }
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE) return; bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled(); GraphicsContext& context = paintInfo.context(); const FontCascade& font = style.fontCascade(); TextPaintStyle textPaintStyle = computeTextPaintStyle(flow.frame(), style, paintInfo); GraphicsContextStateSaver stateSaver(context, textPaintStyle.strokeWidth > 0); updateGraphicsContext(context, textPaintStyle); LayoutRect paintRect = paintInfo.rect; paintRect.moveBy(-paintOffset); auto resolver = runResolver(flow, layout); float strokeOverflow = ceilf(flow.style().textStrokeWidth()); float deviceScaleFactor = flow.document().deviceScaleFactor(); for (const auto& run : resolver.rangeForRect(paintRect)) { FloatRect rect = run.rect(); rect.inflate(strokeOverflow); if (!rect.intersects(paintRect) || run.start() == run.end()) continue; TextRun textRun(run.text()); textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout. textRun.setXPos(0); FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor)); context.drawText(font, textRun, textOrigin); if (debugBordersEnabled) paintDebugBorders(context, LayoutRect(run.rect()), paintOffset); } }
LayoutRect RenderMathMLOperator::paintCharacter(PaintInfo& info, UChar character, const LayoutPoint& origin, CharacterPaintTrimming trim) { GlyphData data = style()->font().glyphDataForCharacter(character, false); FloatRect glyphBounds = data.fontData->boundsForGlyph(data.glyph); LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height())); glyphPaintRect.setY(origin.y() + glyphBounds.y()); // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less // than full coverage. These edge pixels can introduce small seams between connected glyphs FloatRect clipBounds = info.rect; switch (trim) { case TrimTop: glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1); clipBounds.shiftYEdgeTo(glyphPaintRect.y()); break; case TrimBottom: glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1); clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY()); break; case TrimTopAndBottom: LayoutUnit temp = glyphPaintRect.y() + 1; glyphPaintRect.shiftYEdgeTo(temp.ceil()); glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1); clipBounds.shiftYEdgeTo(glyphPaintRect.y()); clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY()); break; } // Clipping the enclosing IntRect avoids any potential issues at joined edges. GraphicsContextStateSaver stateSaver(*info.context); info.context->clip(clipBounds); info.context->drawText(style()->font(), TextRun(&character, 1), origin); return glyphPaintRect; }
// Hit Testing bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { if (!isValid()) return false; LayoutPoint adjustedLocation = accumulatedOffset + location(); // Check our bounds next. For this purpose always assume that we can only be hit in the // foreground phase (which is true for replaced elements like images). // FIXME: Once we support overflow, we need to intersect with that and not with the bounds rect. LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); boundsRect.moveBy(adjustedLocation); if (visibleToHitTestRequest(request) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) { // Check the contents of the RenderFlowThread. if (m_flowThread->hitTestFlowThreadPortionInRegion(this, flowThreadPortionRect(), flowThreadPortionOverflowRect(), request, result, locationInContainer, LayoutPoint(adjustedLocation.x() + borderLeft() + paddingLeft(), adjustedLocation.y() + borderTop() + paddingTop()))) return true; updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(generatingNode(), request, locationInContainer, boundsRect)) return true; } return false; }
void RenderMultiColumnSet::adjustRegionBoundsFromFlowThreadPortionRect(const LayoutPoint& layerOffset, LayoutRect& regionBounds) { LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerOffset.y() : layerOffset.x(); unsigned startColumn = columnIndexAtOffset(layerLogicalTop); LayoutUnit colGap = columnGap(); LayoutUnit colLogicalWidth = computedColumnWidth(); LayoutRect flowThreadPortion = flowThreadPortionRectAt(startColumn); LayoutPoint translationOffset; RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionIsInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); LayoutUnit initialBlockOffset = initialBlockOffsetForPainting(); LayoutUnit inlineOffset = progressionIsInline ? startColumn * (colLogicalWidth + colGap) : LayoutUnit(); bool leftToRight = style().isLeftToRightDirection() ^ progressionReversed; if (!leftToRight) { inlineOffset = -inlineOffset; if (progressionReversed) inlineOffset += contentLogicalWidth() - colLogicalWidth; } translationOffset.setX(inlineOffset); LayoutUnit blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x()); if (!progressionIsInline) { if (!progressionReversed) blockOffset = startColumn * colGap; else blockOffset -= startColumn * (computedColumnHeight() + colGap); } if (isFlippedBlocksWritingMode(style().writingMode())) blockOffset = -blockOffset; translationOffset.setY(blockOffset); if (!isHorizontalWritingMode()) translationOffset = translationOffset.transposedPoint(); // FIXME: The translation needs to include the multicolumn set's content offset within the // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets. regionBounds.moveBy(roundedIntPoint(-translationOffset)); }
VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const { if (!m_first || !renderer.textLength()) return renderer.createVisiblePosition(0, DOWNSTREAM); LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y(); LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x(); bool blocksAreFlipped = renderer.style().isFlippedBlocksWritingMode(); InlineTextBox* lastBox = nullptr; for (auto box = m_first; box; box = box->nextTextBox()) { if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak()) box = box->nextTextBox(); auto& rootBox = box->root(); LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop()); if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) { LayoutUnit bottom = rootBox.selectionBottom(); if (rootBox.nextRootBox()) bottom = std::min(bottom, rootBox.nextRootBox()->lineTop()); if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) { ShouldAffinityBeDownstream shouldAffinityBeDownstream; #if PLATFORM(IOS) if (pointLineDirection != box->logicalLeft() && point.x() < box->x() + box->logicalWidth()) { int half = box->x() + box->logicalWidth() / 2; EAffinity affinity = point.x() < half ? DOWNSTREAM : VP_UPSTREAM_IF_POSSIBLE; return renderer.createVisiblePosition(box->offsetForPosition(pointLineDirection) + box->start(), affinity); } #endif if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream)) return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream); } } lastBox = box; } if (lastBox) { ShouldAffinityBeDownstream shouldAffinityBeDownstream; lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream); return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream); } return renderer.createVisiblePosition(0, DOWNSTREAM); }