bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, const HitTestRequest& request, HitTestResult& result, const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) const { if (hitTestAction != HitTestForeground) return false; ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && 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 = pointInContainer.point(); LayoutRect rect = firstLineBox()->isHorizontal() ? IntRect(point.x(), point.y() - pointInContainer.topPadding(), 1, pointInContainer.topPadding() + pointInContainer.bottomPadding() + 1) : IntRect(point.x() - pointInContainer.leftPadding(), point.y(), pointInContainer.rightPadding() + pointInContainer.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(request, result, pointInContainer, accumulatedOffset, root->lineTop(), root->lineBottom()); if (inside) { renderer->updateHitTestResult(result, pointInContainer.point() - toLayoutSize(accumulatedOffset)); return true; } } } return false; }
bool HitTestResult::addNodeToRectBasedTestResult(Node* node, const HitTestPoint& pointInContainer, const FloatRect& rect) { // If it is not a rect-based hit test, this method has to be no-op. // Return false, so the hit test stops. if (!isRectBasedTest()) return false; // If node is null, return true so the hit test can continue. if (!node) return true; if (m_shadowContentFilterPolicy == DoNotAllowShadowContent) node = node->shadowAncestorNode(); mutableRectBasedTestResult().add(node); bool regionFilled = rect.contains(rectForPoint(pointInContainer.point())); // FIXME: This code (incorrectly) attempts to correct for culled inline nodes. See https://bugs.webkit.org/show_bug.cgi?id=85849. if (node->renderer()->isInline() && !regionFilled) { for (RenderObject* curr = node->renderer()->parent(); curr; curr = curr->parent()) { if (!curr->isRenderInline()) break; // We need to make sure the nodes for culled inlines get included. RenderInline* currInline = toRenderInline(curr); if (currInline->alwaysCreateLineBoxes()) break; if (currInline->visibleToHitTesting() && currInline->node()) mutableRectBasedTestResult().add(currInline->node()->shadowAncestorNode()); } } return !regionFilled; }
// Hit Testing bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) { // Table rows cannot ever be hit tested. Effectively they do not exist. // Just forward to our children always. for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { // FIXME: We have to skip over inline flows, since they can show up inside table rows // at the moment (a demoted inline <form> for example). If we ever implement a // table-specific hit-test method (which we should do for performance reasons anyway), // then we can remove this check. if (child->isTableCell() && !toRenderBox(child)->hasSelfPaintingLayer()) { LayoutPoint cellPoint = flipForWritingModeForChild(toRenderTableCell(child), accumulatedOffset); if (child->nodeAtPoint(request, result, pointInContainer, cellPoint, action)) { updateHitTestResult(result, pointInContainer.point() - toLayoutSize(cellPoint)); return true; } } } return false; }
// Hit Testing bool RenderRegion::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestPoint& 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 = borderBoxRectInRegion(pointInContainer.region()); boundsRect.moveBy(adjustedLocation); if (visibleToHitTesting() && action == HitTestForeground && pointInContainer.intersects(boundsRect)) { // 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.point() - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(node(), pointInContainer, boundsRect)) return true; } return false; }