bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { if (hitTestAction != HitTestForeground) return false; if (!layout.runCount()) return false; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE || style.pointerEvents() == PE_NONE) return false; RenderObject& renderer = *flow.firstChild(); LayoutRect rangeRect = locationInContainer.boundingBox(); rangeRect.moveBy(-accumulatedOffset); auto resolver = lineResolver(flow, layout); for (FloatRect lineRect : resolver.rangeForRect(rangeRect)) { lineRect.moveBy(accumulatedOffset); if (!locationInContainer.intersects(lineRect)) continue; renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); if (!result.addNodeToRectBasedTestResult(renderer.node(), request, locationInContainer, lineRect)) return true; } return false; }
void RenderView::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const { ASSERT_UNUSED(wasFixed, !wasFixed || *wasFixed == static_cast<bool>(mode & IsFixed)); if (!repaintContainer && mode & UseTransforms && shouldUseTransformFromContainer(0)) { TransformationMatrix t; getTransformFromContainer(0, LayoutSize(), t); transformState.applyTransform(t); } if (mode & IsFixed && m_frameView) transformState.move(m_frameView->scrollOffsetForFixedPosition()); if (repaintContainer == this) return; if (mode & TraverseDocumentBoundaries) { if (RenderObject* parentDocRenderer = frame()->ownerRenderer()) { transformState.move(-frame()->view()->scrollOffset()); if (parentDocRenderer->isBox()) transformState.move(toLayoutSize(toRenderBox(parentDocRenderer)->contentBoxRect().location())); parentDocRenderer->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); return; } } // If a container was specified, and was not 0 or the RenderView, // then we should have found it by now. ASSERT_ARG(repaintContainer, !repaintContainer); }
LayoutPoint ScrollableArea::constrainScrollPositionForOverhang(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, int headerHeight, int footerHeight) { // The viewport rect that we're scrolling shouldn't be larger than our document. LayoutSize idealScrollRectSize(std::min(visibleContentRect.width(), totalContentsSize.width()), std::min(visibleContentRect.height(), totalContentsSize.height())); LayoutRect scrollRect(scrollPosition + scrollOrigin - LayoutSize(0, headerHeight), idealScrollRectSize); LayoutRect documentRect(LayoutPoint(), LayoutSize(totalContentsSize.width(), totalContentsSize.height() - headerHeight - footerHeight)); // Use intersection to constrain our ideal scroll rect by the document rect. scrollRect.intersect(documentRect); if (scrollRect.size() != idealScrollRectSize) { // If the rect was clipped, restore its size, effectively pushing it "down" from the top left. scrollRect.setSize(idealScrollRectSize); // If we still clip, push our rect "up" from the bottom right. scrollRect.intersect(documentRect); if (scrollRect.width() < idealScrollRectSize.width()) scrollRect.move(-(idealScrollRectSize.width() - scrollRect.width()), 0); if (scrollRect.height() < idealScrollRectSize.height()) scrollRect.move(0, -(idealScrollRectSize.height() - scrollRect.height())); } return scrollRect.location() - toLayoutSize(scrollOrigin); }
MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubble, bool cancelable, PassRefPtr<AbstractView> abstractView, int detail, const IntPoint& screenLocation, const IntPoint& windowLocation, const IntPoint& movementDelta, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool isSimulated) : UIEventWithKeyState(eventType, canBubble, cancelable, abstractView, detail, ctrlKey, altKey, shiftKey, metaKey) , m_screenLocation(screenLocation) , m_movementDelta(movementDelta) , m_isSimulated(isSimulated) { LayoutPoint adjustedPageLocation; LayoutPoint scrollPosition; LocalFrame* frame = view() ? view()->frame() : 0; if (frame && !isSimulated) { if (FrameView* frameView = frame->view()) { scrollPosition = frameView->scrollPosition(); adjustedPageLocation = frameView->windowToContents(windowLocation); 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; initCoordinates(); }
void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer) { const RenderObject& renderer = layer->renderer(); // We have to visit all the renderers to detect flipped blocks. This might defeat the gains // from mapping via layers. bool canConvertInLayerTree = ancestorLayer ? canMapBetweenRenderers(layer->renderer(), ancestorLayer->renderer()) : false; // fprintf(stderr, "RenderGeometryMap::pushMappingsToAncestor from layer %p to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, canConvertInLayerTree); if (canConvertInLayerTree) { LayoutPoint layerOffset; layer->convertToLayerCoords(ancestorLayer, layerOffset); // The RenderView must be pushed first. if (!m_mapping.size()) { ASSERT(ancestorLayer->renderer().isRenderView()); pushMappingsToAncestor(&ancestorLayer->renderer(), 0); } TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size()); push(&renderer, toLayoutSize(layerOffset), /*accumulatingTransform*/ true, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false); return; } const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? &ancestorLayer->renderer() : 0; pushMappingsToAncestor(&renderer, ancestorRenderer); }
void MouseRelatedEvent::computeRelativePosition() { Node* targetNode = target() ? target()->toNode() : 0; if (!targetNode) return; // Compute coordinates that are based on the target. m_layerLocation = m_pageLocation; m_offsetLocation = m_pageLocation; // Must have an updated render tree for this math to work correctly. targetNode->document().updateLayoutIgnorePendingStylesheets(); // Adjust offsetLocation to be relative to the target's position. if (RenderObject* r = targetNode->renderer()) { FloatPoint localPos = r->absoluteToLocal(absoluteLocation(), UseTransforms); m_offsetLocation = roundedLayoutPoint(localPos); } // Adjust layerLocation to be relative to the layer. // FIXME: event.layerX and event.layerY are poorly defined, // and probably don't always correspond to RenderLayer offsets. // https://bugs.webkit.org/show_bug.cgi?id=21868 Node* n = targetNode; while (n && !n->renderer()) n = n->parentNode(); if (n) { // FIXME: This logic is a wrong implementation of convertToLayerCoords. for (RenderLayer* layer = n->renderer()->enclosingLayer(); layer; layer = layer->parent()) m_layerLocation -= toLayoutSize(layer->location()); } m_hasCachedRelativePosition = true; }
const RenderObject* RenderView::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const { LayoutSize offsetForFixedPosition; LayoutSize offset; RenderObject* container = 0; if (m_frameView) offsetForFixedPosition = m_frameView->scrollOffsetForFixedPosition(); if (geometryMap.mapCoordinatesFlags() & TraverseDocumentBoundaries) { if (RenderPart* parentDocRenderer = frame()->ownerRenderer()) { offset = -m_frameView->scrollOffset(); offset += toLayoutSize(parentDocRenderer->contentBoxRect().location()); container = parentDocRenderer; } } // If a container was specified, and was not 0 or the RenderView, then we // should have found it by now unless we're traversing to a parent document. ASSERT_ARG(ancestorToStopAt, !ancestorToStopAt || ancestorToStopAt == this || container); if ((!ancestorToStopAt || container) && shouldUseTransformFromContainer(container)) { TransformationMatrix t; getTransformFromContainer(container, LayoutSize(), t); geometryMap.push(this, t, false, false, false, true, offsetForFixedPosition); } else { geometryMap.push(this, offset, false, false, false, false, offsetForFixedPosition); } return container; }
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 = 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; } } FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(accumulatedOffset); FloatRect boundsRect(boxOrigin, size()); 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; }
void PaintPropertyTreeBuilder::updatePerspective( const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { const ComputedStyle& style = object.styleRef(); if (object.isBox() && style.hasPerspective()) { // The perspective node must not flatten (else nothing will get // perspective), but it should still extend the rendering context as // most transform nodes do. TransformationMatrix matrix = TransformationMatrix().applyPerspective(style.perspective()); FloatPoint3D origin = perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.current.paintOffset); object.getMutableForPainting().ensurePaintProperties().updatePerspective( context.current.transform, matrix, origin, context.current.shouldFlattenInheritedTransform, context.current.renderingContextID); } else { if (auto* properties = object.getMutableForPainting().paintProperties()) properties->clearPerspective(); } const auto* properties = object.paintProperties(); if (properties && properties->perspective()) { context.current.transform = properties->perspective(); context.current.shouldFlattenInheritedTransform = false; } }
void RenderView::mapLocalToContainer(const RenderLayerModelObject* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed, const PaintInvalidationState* paintInvalidationState) const { ASSERT_UNUSED(wasFixed, !wasFixed || *wasFixed == static_cast<bool>(mode & IsFixed)); if (!paintInvalidationContainer && mode & UseTransforms && shouldUseTransformFromContainer(0)) { TransformationMatrix t; getTransformFromContainer(0, LayoutSize(), t); transformState.applyTransform(t); } if ((mode & IsFixed) && m_frameView) { transformState.move(m_frameView->scrollOffsetForFixedPosition()); // IsFixed flag is only applicable within this RenderView. mode &= ~IsFixed; } if (paintInvalidationContainer == this) return; if (mode & TraverseDocumentBoundaries) { if (RenderObject* parentDocRenderer = frame()->ownerRenderer()) { transformState.move(-frame()->view()->scrollOffset()); if (parentDocRenderer->isBox()) transformState.move(toLayoutSize(toRenderBox(parentDocRenderer)->contentBoxRect().location())); parentDocRenderer->mapLocalToContainer(paintInvalidationContainer, transformState, mode, wasFixed, paintInvalidationState); return; } } }
bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { LayoutPoint pointInParent = pointInContainer - toLayoutSize(accumulatedOffset); LayoutPoint pointInBorderBox(pointInParent.x() - x(), pointInParent.y() - y()); // Note: For now, we're ignoring hits to border and padding for <svg> if (!contentBoxRect().contains(pointInBorderBox)) return false; FloatPoint localPoint = localToParentTransform().inverse().mapPoint(FloatPoint(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, roundedLayoutPoint(localPoint)); return true; } return false; }
bool SVGInlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit) { // FIXME: integrate with InlineTextBox::nodeAtPoint better. ASSERT(!isLineBreak()); PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, renderer().style()->pointerEvents()); bool isVisible = renderer().style()->visibility() == VISIBLE; if (isVisible || !hitRules.requireVisible) { if (hitRules.canHitBoundingBox || (hitRules.canHitStroke && (renderer().style()->svgStyle().hasStroke() || !hitRules.requireStroke)) || (hitRules.canHitFill && (renderer().style()->svgStyle().hasFill() || !hitRules.requireFill))) { FloatPointWillBeLayoutPoint boxOrigin(x(), y()); boxOrigin.moveBy(accumulatedOffset); FloatRectWillBeLayoutRect rect(boxOrigin, size()); // FIXME: both calls to rawValue() below is temporary and should be removed once the transition // to LayoutUnit-based types is complete (crbug.com/321237) if (locationInContainer.intersects(rect.rawValue())) { renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); if (!result.addNodeToRectBasedTestResult(renderer().node(), request, locationInContainer, rect.rawValue())) return true; } } } return false; }
void LayoutGeometryMap::pushMappingsToAncestor(const PaintLayer* layer, const PaintLayer* ancestorLayer) { const LayoutObject* layoutObject = layer->layoutObject(); bool crossDocument = ancestorLayer && layer->layoutObject()->frame() != ancestorLayer->layoutObject()->frame(); ASSERT(!crossDocument || m_mapCoordinatesFlags & TraverseDocumentBoundaries); // We have to visit all the layoutObjects to detect flipped blocks. This might defeat the gains // from mapping via layers. bool canConvertInLayerTree = (ancestorLayer && !crossDocument) ? canMapBetweenLayoutObjects(layer->layoutObject(), ancestorLayer->layoutObject()) : false; LAYOUT_GEOMETRY_MAP_LOG("LayoutGeometryMap::pushMappingsToAncestor from layer %p to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, canConvertInLayerTree); if (canConvertInLayerTree) { LayoutPoint layerOffset; layer->convertToLayerCoords(ancestorLayer, layerOffset); // The LayoutView must be pushed first. if (!m_mapping.size()) { ASSERT(ancestorLayer->layoutObject()->isLayoutView()); pushMappingsToAncestor(ancestorLayer->layoutObject(), 0); } TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size()); bool accumulatingTransform = layer->layoutObject()->style()->preserves3D() || ancestorLayer->layoutObject()->style()->preserves3D(); push(layoutObject, toLayoutSize(layerOffset), accumulatingTransform ? AccumulatingTransform : 0); return; } const LayoutBoxModelObject* ancestorLayoutObject = ancestorLayer ? ancestorLayer->layoutObject() : 0; pushMappingsToAncestor(layoutObject, ancestorLayoutObject); }
bool LineBoxList::hitTest(LayoutBoxModelObject* renderer, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) const { if (hitTestAction != HitTestForeground) return false; ASSERT(renderer->isLayoutBlock() || (renderer->isLayoutInline() && renderer->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(); LayoutRect rect(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(renderer, rect, 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(renderer, curr->logicalTopVisualOverflow(root.lineTop()), curr->logicalBottomVisualOverflow(root.lineBottom()), rect, accumulatedOffset)) { bool inside = curr->nodeAtPoint(result, locationInContainer, accumulatedOffset, root.lineTop(), root.lineBottom()); if (inside) { renderer->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); return true; } } } return false; }
void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) { LayoutPoint adjustedLocation = accumulatedOffset + location(); HTMLElement* innerText = innerTextElement(); result.setInnerNode(innerText); result.setInnerNonSharedNode(innerText); result.setLocalPoint(pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location())); }
bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { if (hasEllipsisBox() && visibleToHitTesting()) { if (ellipsisBox()->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) { renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); return true; } } return InlineFlowBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom); }
static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { const ComputedStyle& style = object.styleRef(); if (!object.isBox() || !style.hasPerspective()) return nullptr; RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = TransformPaintPropertyNode::create( TransformationMatrix().applyPerspective(style.perspective()), perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffset), context.currentTransform); context.currentTransform = newTransformNodeForPerspective.get(); return newTransformNodeForPerspective.release(); }
void LayoutTextControl::hitInnerEditorElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) { HTMLElement* innerEditor = innerEditorElement(); if (!innerEditor->layoutObject()) return; LayoutPoint adjustedLocation = accumulatedOffset + location(); LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerEditor->layoutBox()->location()); if (hasOverflowClip()) localPoint += scrolledContentOffset(); result.setNodeAndPosition(innerEditor, localPoint); }
bool SVGInlineTextBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit) { // FIXME: integrate with InlineTextBox::nodeAtPoint better. ASSERT(!isLineBreak()); PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, result.hitTestRequest(), getLineLayoutItem().style()->pointerEvents()); bool isVisible = getLineLayoutItem().style()->visibility() == EVisibility::Visible; if (isVisible || !hitRules.requireVisible) { if (hitRules.canHitBoundingBox || (hitRules.canHitStroke && (getLineLayoutItem().style()->svgStyle().hasStroke() || !hitRules.requireStroke)) || (hitRules.canHitFill && (getLineLayoutItem().style()->svgStyle().hasFill() || !hitRules.requireFill))) { LayoutRect rect(topLeft(), LayoutSize(logicalWidth(), logicalHeight())); rect.moveBy(accumulatedOffset); if (locationInContainer.intersects(rect)) { LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->getLineLayoutItem()); const SimpleFontData* fontData = lineLayoutItem.scaledFont().primaryFont(); DCHECK(fontData); if (!fontData) return false; DCHECK(lineLayoutItem.scalingFactor()); float baseline = fontData->getFontMetrics().floatAscent() / lineLayoutItem.scalingFactor(); FloatPoint floatLocation = FloatPoint(locationInContainer.point()); for (const SVGTextFragment& fragment : m_textFragments) { FloatQuad fragmentQuad = fragment.boundingQuad(baseline); if (fragmentQuad.containsPoint(floatLocation)) { lineLayoutItem.updateHitTestResult( result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); if (result.addNodeToListBasedTestResult(lineLayoutItem.node(), locationInContainer, rect) == StopHitTesting) return true; } } } } } return false; }
void RenderTextControl::hitInnerTextElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) { TextControlInnerTextElement* innerText = innerTextElement(); if (!innerText->renderer()) return; LayoutPoint adjustedLocation = accumulatedOffset + location(); LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerText->renderBox()->location()) + scrolledContentOffset(); result.setInnerNode(innerText); result.setInnerNonSharedNode(innerText); result.setLocalPoint(localPoint); }
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; }
void PaintPropertyTreeBuilder::updatePerspective(const LayoutObject& object, PaintPropertyTreeBuilderContext& context) { const ComputedStyle& style = object.styleRef(); if (!object.isBox() || !style.hasPerspective()) return; RefPtr<TransformPaintPropertyNode> perspective = TransformPaintPropertyNode::create( TransformationMatrix().applyPerspective(style.perspective()), perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffset), context.currentTransform); context.currentTransform = perspective.get(); object.getMutableForPainting().ensureObjectPaintProperties().setPerspective(perspective.release()); }
void RenderTextControl::hitInnerEditorElement(HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset) { HTMLElement* innerEditor = innerEditorElement(); if (!innerEditor->renderer()) return; LayoutPoint adjustedLocation = accumulatedOffset + location(); LayoutPoint localPoint = pointInContainer - toLayoutSize(adjustedLocation + innerEditor->renderBox()->location()); if (hasOverflowClip()) localPoint += scrolledContentOffset(); result.setInnerNode(innerEditor); result.setInnerNonSharedNode(innerEditor); result.setLocalPoint(localPoint); }
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 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; }
bool EllipsisBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { // FIXME: the call to roundedLayoutPoint() below is temporary and should be removed once // the transition to LayoutUnit-based types is complete (crbug.com/321237) LayoutPoint adjustedLocation = accumulatedOffset + topLeft(); LayoutPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(accumulatedOffset); LayoutRect boundsRect(boxOrigin, size()); if (visibleToHitTestRequest(result.hitTestRequest()) && boundsRect.intersects(LayoutRect(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0)))) { lineLayoutItem().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); if (result.addNodeToListBasedTestResult(lineLayoutItem().node(), locationInContainer, boundsRect) == StopHitTesting) return true; } return false; }
void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer) { const RenderObject* renderer = layer->renderer(); // The simple case can be handled fast in the layer tree. bool canConvertInLayerTree = ancestorLayer ? canMapViaLayer(ancestorLayer) : false; for (const RenderLayer* current = layer; current != ancestorLayer && canConvertInLayerTree; current = current->parent()) canConvertInLayerTree = canMapViaLayer(current); if (canConvertInLayerTree) { TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size()); LayoutPoint layerOffset; layer->convertToLayerCoords(ancestorLayer, layerOffset); push(renderer, toLayoutSize(layerOffset), /*accumulatingTransform*/ true, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false); return; } const RenderBoxModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0; pushMappingsToAncestor(renderer, ancestorRenderer); }
void MouseRelatedEvent::computeRelativePosition() { Node* targetNode = target() ? target()->toNode() : 0; if (!targetNode) return; // Compute coordinates that are based on the target. m_layerLocation = m_pageLocation; m_offsetLocation = m_pageLocation; // Must have an updated render tree for this math to work correctly. targetNode->document()->updateStyleIfNeeded(); // Adjust offsetLocation to be relative to the target's position. if (!isSimulated()) { if (RenderObject* r = targetNode->renderer()) { FloatPoint localPos = r->absoluteToLocal(absoluteLocation(), UseTransforms | SnapOffsetForTransforms); m_offsetLocation = roundedLayoutPoint(localPos); float scaleFactor = 1 / (pageZoomFactor(this) * frameScaleFactor(this)); if (scaleFactor != 1.0f) m_offsetLocation.scale(scaleFactor, scaleFactor); } } // Adjust layerLocation to be relative to the layer. // FIXME: We're pretty sure this is the wrong definition of "layer." // Our RenderLayer is a more modern concept, and layerX/Y is some // other notion about groups of elements (left over from the Netscape 4 days?); // we should test and fix this. Node* n = targetNode; while (n && !n->renderer()) n = n->parentNode(); RenderLayer* layer; if (n && (layer = n->renderer()->enclosingLayer())) { layer->updateLayerPosition(); for (; layer; layer = layer->parent()) { m_layerLocation -= toLayoutSize(layer->location()); } } m_hasCachedRelativePosition = true; }
bool RenderMultiColumnSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { 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 (!visibleToHitTesting() || action != HitTestForeground || !locationInContainer.intersects(boundsRect)) return false; // The point is in one specific column. Since columns can't overlap, we don't ever have to test // multiple columns. Put the // FIXME: It would be nice to jump right to the specific column by just doing math on the point. Since columns // can't overlap, we shouldn't have to walk every column like this. The old column code walked all the columns, though, // so this is no worse. We'd have to watch out for rect-based hit testing, though, which actually could overlap // multiple columns. LayoutUnit colGap = columnGap(); unsigned colCount = columnCount(); for (unsigned i = 0; i < colCount; i++) { // First we get the column rect, which is in our local coordinate space, and we make it physical and apply // the hit test offset to it. That gives us the physical location that we want to paint the column at. LayoutRect colRect = columnRectAt(i); flipForWritingMode(colRect); colRect.moveBy(adjustedLocation); // Next we get the portion of the flow thread that corresponds to this column. LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); // Now get the overflow rect that corresponds to the column. LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); // Do the hit test with the computed rects. if (flowThread()->hitTestFlowThreadPortionInRegion(this, flowThreadPortion, flowThreadOverflowPortion, request, result, locationInContainer, colRect.location())) return true; } updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); return !result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect); }
void RenderGeometryMap::pushMappingsToAncestor( const RenderLayer* layer, const RenderLayer* ancestorLayer) { const RenderObject* renderer = layer->renderer(); bool crossDocument = false; ASSERT(!crossDocument || m_mapCoordinatesFlags & TraverseDocumentBoundaries); // We have to visit all the renderers to detect flipped blocks. This might // defeat the gains from mapping via layers. bool canConvertInLayerTree = (ancestorLayer && !crossDocument) ? canMapBetweenRenderers(layer->renderer(), ancestorLayer->renderer()) : false; // fprintf(stderr, "RenderGeometryMap::pushMappingsToAncestor from layer %p // to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, // canConvertInLayerTree); if (canConvertInLayerTree) { LayoutPoint layerOffset; layer->convertToLayerCoords(ancestorLayer, layerOffset); // The RenderView must be pushed first. if (!m_mapping.size()) { ASSERT(ancestorLayer->renderer()->isRenderView()); pushMappingsToAncestor(ancestorLayer->renderer(), 0); } TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size()); bool accumulatingTransform = layer->renderer()->style()->preserves3D() || ancestorLayer->renderer()->style()->preserves3D(); push(renderer, toLayoutSize(layerOffset), accumulatingTransform, /*isNonUniform*/ false, /*hasTransform*/ false); return; } const RenderBox* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0; pushMappingsToAncestor(renderer, ancestorRenderer); }