bool RenderView::hitTest(const HitTestRequest& request, const HitTestLocation& location, HitTestResult& result) { TRACE_EVENT0("blink", "RenderView::hitTest"); m_hitTestCount++; if (!m_frameView->visibleContentRect().contains(location.roundedPoint())) return false; // We have to recursively update layout/style here because otherwise, when the hit test recurses // into a child document, it could trigger a layout on the parent document, which can destroy RenderLayers // that are higher up in the call stack, leading to crashes. // Note that Document::updateLayout calls its parent's updateLayout. // FIXME: It should be the caller's responsibility to ensure an up-to-date layout. frameView()->updateLayoutAndStyleIfNeededRecursive(); // RenderView should make sure to update layout before entering hit testing ASSERT(!frame()->view()->layoutPending()); ASSERT(!document().renderView()->needsLayout()); // TODO(ojan): Does any of this intersection stuff make sense for Sky? LayoutRect hitTestArea = view()->documentRect(); if (!request.ignoreClipping()) hitTestArea.intersect(frame()->view()->visibleContentRect()); bool insideLayer = hitTestLayer(layer(), 0, request, result, hitTestArea, location); if (!insideLayer) { // TODO(ojan): Is this code needed for Sky? // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, // return ourselves. We do this so mouse events continue getting delivered after a drag has // exited the WebView, and so hit testing over a scrollbar hits the content document. if (request.active() || request.release()) { updateHitTestResult(result, location.point()); insideLayer = true; } } return insideLayer; }
// 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; }
bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) { // We only draw in the forground phase, so we only hit-test then. if (hitTestAction != HitTestForeground) return false; PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents()); bool isVisible = (style()->visibility() == VISIBLE); if (isVisible || !hitRules.requireVisible) { FloatPoint localPoint; if (!SVGRenderSupport::transformToUserSpaceAndCheckClipping(this, localToParentTransform(), pointInParent, localPoint)) return false; if (hitRules.canHitFill || hitRules.canHitBoundingBox) { if (m_objectBoundingBox.contains(localPoint)) { updateHitTestResult(result, roundedLayoutPoint(localPoint)); return true; } } } return false; }
bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction) { // We only draw in the forground phase, so we only hit-test then. if (hitTestAction != HitTestForeground) return false; PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents()); bool isVisible = (style()->visibility() == VISIBLE); if (isVisible || !hitRules.requireVisible) { double localX, localY; absoluteTransform().inverse().map(_x, _y, localX, localY); if (hitRules.canHitFill) { if (m_localBounds.contains(narrowPrecisionToFloat(localX), narrowPrecisionToFloat(localY))) { updateHitTestResult(result, IntPoint(_x, _y)); return true; } } } return false; }
bool RenderSVGViewportContainer::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) { if (!viewport().isEmpty() && style()->overflowX() == OHIDDEN && style()->overflowY() == OHIDDEN) { // Check if we need to do anything at all. IntRect overflowBox = IntRect(0, 0, width(), height()); overflowBox.move(_tx, _ty); TransformationMatrix ctm = RenderObject::absoluteTransform(); ctm.translate(viewport().x(), viewport().y()); double localX, localY; ctm.inverse().map(_x - _tx, _y - _ty, &localX, &localY); if (!overflowBox.contains((int)localX, (int)localY)) return false; } int sx = 0; int sy = 0; // Respect parent translation offset for non-outermost <svg> elements. // Outermost <svg> element is handled by RenderSVGRoot. if (element()->hasTagName(SVGNames::svgTag)) { sx = _tx; sy = _ty; } for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { if (child->nodeAtPoint(request, result, _x - sx, _y - sy, _tx, _ty, hitTestAction)) { updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty)); return true; } } // Spec: Only graphical elements can be targeted by the mouse, period. // 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; }
bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) { // We only draw in the forground phase, so we only hit-test then. if (hitTestAction != HitTestForeground) return false; PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents()); bool isVisible = (style()->visibility() == VISIBLE); if (isVisible || !hitRules.requireVisible) { FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); if (!SVGRenderSupport::pointInClippingArea(this, localPoint)) return false; if (hitRules.canHitFill) { if (m_localBounds.contains(localPoint)) { updateHitTestResult(result, roundedIntPoint(localPoint)); return true; } } } return false; }
void WebWidget::showContextMenu(const QPoint &position) { m_contextMenuReason = QContextMenuEvent::Other; const bool hasSelection = (this->hasSelection() && !getSelectedText().trimmed().isEmpty()); if (m_ignoreContextMenu || (position.isNull() && (!hasSelection || m_clickPosition.isNull()))) { return; } const QPoint hitPosition = (position.isNull() ? m_clickPosition : position); if (isScrollBar(hitPosition) || (SettingsManager::getValue(QLatin1String("Browser/JavaScriptCanDisableContextMenu")).toBool() && !canShowContextMenu(hitPosition))) { return; } updateHitTestResult(hitPosition); updateEditActions(); QStringList flags; if (m_hitResult.flags.testFlag(IsFormTest)) { flags.append(QLatin1String("form")); } if (!m_hitResult.imageUrl.isValid() && m_hitResult.flags.testFlag(IsSelectedTest) && hasSelection) { flags.append(QLatin1String("selection")); } if (m_hitResult.linkUrl.isValid()) { if (m_hitResult.linkUrl.scheme() == QLatin1String("mailto")) { flags.append(QLatin1String("mail")); } else { flags.append(QLatin1String("link")); } } if (!m_hitResult.imageUrl.isEmpty()) { flags.append(QLatin1String("image")); } if (m_hitResult.mediaUrl.isValid()) { flags.append(QLatin1String("media")); } if (m_hitResult.flags.testFlag(IsContentEditableTest)) { flags.append(QLatin1String("edit")); } if (flags.isEmpty() || (flags.size() == 1 && flags.first() == QLatin1String("form"))) { flags.append(QLatin1String("standard")); if (m_hitResult.frameUrl.isValid()) { flags.append(QLatin1String("frame")); } } if (flags.isEmpty()) { return; } Menu menu(Menu::NoMenuRole, this); menu.load(QLatin1String("menu/webWidget.json"), flags); menu.exec(mapToGlobal(hitPosition)); }