예제 #1
1
// This can return null if an empty document is loaded.
static Element* elementUnderMouse(Document* documentUnderMouse, const IntPoint& p)
{
    Frame* frame = documentUnderMouse->frame();
    float zoomFactor = frame ? frame->pageZoomFactor() : 1;
    LayoutPoint point = roundedLayoutPoint(FloatPoint(p.x() * zoomFactor, p.y() * zoomFactor));

    HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
    HitTestResult result(point);
    documentUnderMouse->renderView()->hitTest(request, result);

    Node* n = result.innerNode();
    while (n && !n->isElementNode())
        n = n->parentNode();
    if (n)
        n = n->shadowAncestorNode();

    return static_cast<Element*>(n);
}
bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
{
    // Give RenderSVGViewportContainer a chance to apply its viewport clip
    if (!pointIsInsideViewportClip(pointInParent))
        return false;

    FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);

    if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
        return false;
                
    for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
        if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
            updateHitTestResult(result, roundedLayoutPoint(localPoint));
            return true;
        }
    }

    // Accessibility wants to return SVG containers, if appropriate.
    if (request.type() & HitTestRequest::AccessibilityHitTest && m_objectBoundingBox.contains(localPoint)) {
        updateHitTestResult(result, roundedLayoutPoint(localPoint));
        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;
}
예제 #3
0
bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
{
    // Give RenderSVGViewportContainer a chance to apply its viewport clip
    if (!pointIsInsideViewportClip(pointInParent))
        return false;

    FloatPoint localPoint;
    if (!SVGRenderSupport::transformToUserSpaceAndCheckClipping(this, localToParentTransform(), pointInParent, localPoint))
        return false;

    for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
        if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
            updateHitTestResult(result, roundedLayoutPoint(localPoint));
            return true;
        }
    }

    // pointer-events=boundingBox makes it possible for containers to be direct targets
    if (style()->pointerEvents() == PE_BOUNDINGBOX) {
        ASSERT(isObjectBoundingBoxValid());
        if (objectBoundingBox().contains(localPoint)) {
            updateHitTestResult(result, roundedLayoutPoint(localPoint));
            return true;
        }
    }
    // 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 SliderThumbElement::setPositionFromPoint(const LayoutPoint& point)
{
    RefPtrWillBeRawPtr<HTMLInputElement> input(hostInput());
    Element* trackElement = input->closedShadowRoot()->getElementById(ShadowElementNames::sliderTrack());

    if (!input->layoutObject() || !layoutBox() || !trackElement->layoutBox())
        return;

    LayoutPoint offset = roundedLayoutPoint(input->layoutObject()->absoluteToLocal(FloatPoint(point), UseTransforms));
    bool isVertical = hasVerticalAppearance(input.get());
    bool isLeftToRightDirection = layoutBox()->style()->isLeftToRightDirection();
    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 layoutBox()->x() and
    // y() are unusable.
    // FIXME: This should probably respect transforms.
    LayoutPoint absoluteThumbOrigin = layoutBox()->absoluteBoundingBoxRectIgnoringTransforms().location();
    LayoutPoint absoluteSliderContentOrigin = roundedLayoutPoint(input->layoutObject()->localToAbsolute());
    IntRect trackBoundingBox = trackElement->layoutObject()->absoluteBoundingBoxRectIgnoringTransforms();
    IntRect inputBoundingBox = input->layoutObject()->absoluteBoundingBoxRectIgnoringTransforms();
    if (isVertical) {
        trackSize = trackElement->layoutBox()->contentHeight() - layoutBox()->size().height();
        position = offset.y() - layoutBox()->size().height() / 2 - trackBoundingBox.y() + inputBoundingBox.y() - layoutBox()->marginBottom();
        currentPosition = absoluteThumbOrigin.y() - absoluteSliderContentOrigin.y();
    } else {
        trackSize = trackElement->layoutBox()->contentWidth() - layoutBox()->size().width();
        position = offset.x() - layoutBox()->size().width() / 2 - trackBoundingBox.x() + inputBoundingBox.x();
        position -= isLeftToRightDirection ? layoutBox()->marginLeft() : layoutBox()->marginRight();
        currentPosition = absoluteThumbOrigin.x() - absoluteSliderContentOrigin.x();
    }
    position = std::max<LayoutUnit>(0, std::min(position, trackSize));
    const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackSize);
    const Decimal fraction = isVertical || !isLeftToRightDirection ? Decimal(1) - ratio : ratio;
    StepRange stepRange(input->createStepRange(RejectAny));
    Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction));

    Decimal closest = input->findClosestTickMarkValue(value);
    if (closest.isFinite()) {
        double closestFraction = stepRange.proportionFromValue(closest).toDouble();
        double closestRatio = isVertical || !isLeftToRightDirection ? 1.0 - closestFraction : closestFraction;
        LayoutUnit closestPosition = trackSize * closestRatio;
        const LayoutUnit snappingThreshold = 5;
        if ((closestPosition - position).abs() <= snappingThreshold)
            value = closest;
    }

    String valueString = serializeForNumberType(value);
    if (valueString == input->value())
        return;

    // FIXME: This is no longer being set from renderer. Consider updating the method name.
    input->setValueFromRenderer(valueString);
    if (layoutObject())
        layoutObject()->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SliderValueChanged);
}
예제 #5
0
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;
}
예제 #6
0
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;
}
예제 #7
0
void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
{
    RenderObject* o = container();
    if (!o)
        return;

    if (o->isRenderFlowThread())
        transformState.move(o->columnOffset(LayoutPoint(transformState.mappedPoint())));

    o->mapAbsoluteToLocalPoint(mode, transformState);

    LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint());

    if (!style()->hasOutOfFlowPosition() && o->hasColumns()) {
        RenderBlock* block = toRenderBlock(o);
        LayoutPoint point(roundedLayoutPoint(transformState.mappedPoint()));
        point -= containerOffset;
        block->adjustForColumnRect(containerOffset, point);
    }

    bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
    if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
        TransformationMatrix t;
        getTransformFromContainer(o, containerOffset, t);
        transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    } else
        transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
}
예제 #8
0
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 LayoutSVGImage::nodeAtFloatPoint(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, result.hitTestRequest(), style()->pointerEvents());
    bool isVisible = (style()->visibility() == VISIBLE);
    if (isVisible || !hitRules.requireVisible) {
        FloatPoint localPoint;
        if (!SVGLayoutSupport::transformToUserSpaceAndCheckClipping(this, localToSVGParentTransform(), pointInParent, localPoint))
            return false;

        if (hitRules.canHitFill || hitRules.canHitBoundingBox) {
            if (m_objectBoundingBox.contains(localPoint)) {
                const LayoutPoint& localLayoutPoint = roundedLayoutPoint(localPoint);
                updateHitTestResult(result, localLayoutPoint);
                if (result.addNodeToListBasedTestResult(element(), localLayoutPoint) == StopHitTesting)
                    return true;
            }
        }
    }

    return false;
}
void RenderMultiColumnFlowThread::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
{
    // First get the transform state's point into the block flow thread's physical coordinate space.
    parent()->mapAbsoluteToLocalPoint(mode, transformState);
    LayoutPoint transformPoint = roundedLayoutPoint(transformState.mappedPoint());

    // Now walk through each region.
    const RenderMultiColumnSet* candidateColumnSet = nullptr;
    LayoutPoint candidatePoint;
    LayoutSize candidateContainerOffset;

    for (const auto& columnSet : childrenOfType<RenderMultiColumnSet>(*parent())) {
        candidateContainerOffset = columnSet.offsetFromContainer(parent(), LayoutPoint());

        candidatePoint = transformPoint - candidateContainerOffset;
        candidateColumnSet = &columnSet;

        // We really have no clue what to do with overflow. We'll just use the closest region to the point in that case.
        LayoutUnit pointOffset = isHorizontalWritingMode() ? candidatePoint.y() : candidatePoint.x();
        LayoutUnit regionOffset = isHorizontalWritingMode() ? columnSet.topLeftLocation().y() : columnSet.topLeftLocation().x();
        if (pointOffset < regionOffset + columnSet.logicalHeight())
            break;
    }

    // Once we have a good guess as to which region we hit tested through (and yes, this was just a heuristic, but it's
    // the best we could do), then we can map from the region into the flow thread.
    LayoutSize translationOffset = physicalTranslationFromRegionToFlow(candidateColumnSet, candidatePoint) + candidateContainerOffset;
    bool preserve3D = mode & UseTransforms && (parent()->style().preserves3D() || style().preserves3D());
    if (mode & UseTransforms && shouldUseTransformFromContainer(parent())) {
        TransformationMatrix t;
        getTransformFromContainer(parent(), translationOffset, t);
        transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    } else
        transformState.move(translationOffset.width(), translationOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
}
예제 #11
0
void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const
{
    bool rootLayerScrolls = m_layoutObject.document().settings() && m_layoutObject.document().settings()->rootLayerScrolls();
    if (!m_layoutObject.layer()->parent() && !rootLayerScrolls) {
        // The root layer's clip rect is always infinite.
        clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
        return;
    }

    bool isClippingRoot = m_layoutObject.layer() == context.rootLayer;

    // For transformed layers, the root layer was shifted to be us, so there is no need to
    // examine the parent. We want to cache clip rects with us as the root.
    PaintLayer* parentLayer = !isClippingRoot ? m_layoutObject.layer()->parent() : 0;
    // Ensure that our parent's clip has been calculated so that we can examine the values.
    if (parentLayer) {
        // FIXME: Why don't we just call getClipRects here?
        if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) {
            clipRects = *parentLayer->clipper().cachedClipRects(context);
        } else {
            parentLayer->clipper().calculateClipRects(context, clipRects);
        }
    } else {
        clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
    }

    adjustClipRectsForChildren(m_layoutObject, clipRects);

    if ((m_layoutObject.hasOverflowClip() && shouldRespectOverflowClip(context)) || m_layoutObject.hasClip()) {
        // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
        // some transformed layer boundary, for example, in the PaintLayerCompositor overlapMap, where
        // clipRects are needed in view space.
        applyClipRects(context, m_layoutObject, roundedLayoutPoint(m_layoutObject.localToContainerPoint(FloatPoint(), context.rootLayer->layoutObject())), clipRects);
    }
}
예제 #12
0
bool RenderSVGForeignObject::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
{
    FloatPoint localPoint = localTransform().inverse().mapPoint(pointInParent);

    // Early exit if local point is not contained in clipped viewport area
    if (SVGRenderSupport::isOverflowHidden(this) && !m_viewport.contains(localPoint))
        return false;

    return RenderBlock::nodeAtPoint(request, result, roundedLayoutPoint(localPoint), LayoutPoint(), hitTestAction);
}
예제 #13
0
bool LayoutSVGContainer::nodeAtFloatPoint(HitTestResult& result,
                                          const FloatPoint& pointInParent,
                                          HitTestAction hitTestAction) {
  // Give LayoutSVGViewportContainer a chance to apply its viewport clip
  if (!pointIsInsideViewportClip(pointInParent))
    return false;

  FloatPoint localPoint;
  if (!SVGLayoutSupport::transformToUserSpaceAndCheckClipping(
          *this, localToSVGParentTransform(), pointInParent, localPoint))
    return false;

  for (LayoutObject* child = lastChild(); child;
       child = child->previousSibling()) {
    if (child->nodeAtFloatPoint(result, localPoint, hitTestAction)) {
      const LayoutPoint& localLayoutPoint = roundedLayoutPoint(localPoint);
      updateHitTestResult(result, localLayoutPoint);
      if (result.addNodeToListBasedTestResult(
              child->node(), localLayoutPoint) == StopHitTesting)
        return true;
    }
  }

  // pointer-events: bounding-box makes it possible for containers to be direct
  // targets.
  if (style()->pointerEvents() == PE_BOUNDINGBOX) {
    // Check for a valid bounding box because it will be invalid for empty
    // containers.
    if (isObjectBoundingBoxValid() &&
        objectBoundingBox().contains(localPoint)) {
      const LayoutPoint& localLayoutPoint = roundedLayoutPoint(localPoint);
      updateHitTestResult(result, localLayoutPoint);
      if (result.addNodeToListBasedTestResult(element(), localLayoutPoint) ==
          StopHitTesting)
        return true;
    }
  }
  // 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;
}
예제 #14
0
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.
    // FIXME: This should probably respect transforms.
    LayoutPoint absoluteThumbOrigin = renderBox()->absoluteBoundingBoxRectIgnoringTransforms().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();
}
예제 #15
0
void RenderLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const
{
    if (!m_renderer.layer()->parent()) {
        // The root layer's clip rect is always infinite.
        clipRects.reset(PaintInfo::infiniteRect());
        return;
    }

    bool isClippingRoot = m_renderer.layer() == context.rootLayer;

    // For transformed layers, the root layer was shifted to be us, so there is no need to
    // examine the parent. We want to cache clip rects with us as the root.
    RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0;

    // Ensure that our parent's clip has been calculated so that we can examine the values.
    if (parentLayer) {
        // FIXME: Why don't we just call getClipRects here?
        if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) {
            clipRects = *parentLayer->clipper().cachedClipRects(context);
        } else {
            parentLayer->clipper().calculateClipRects(context, clipRects);
        }
    } else {
        clipRects.reset(PaintInfo::infiniteRect());
    }

    if (m_renderer.style()->position() == AbsolutePosition) {
        clipRects.setOverflowClipRect(clipRects.posClipRect());
    }

    // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
    // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where
    // clipRects are needed in view space.
    LayoutPoint offset = roundedLayoutPoint(m_renderer.localToContainerPoint(FloatPoint(), context.rootLayer->renderer()));
    if (m_renderer.hasOverflowClip()) {
        ClipRect newOverflowClip = m_renderer.overflowClipRect(offset);
        newOverflowClip.setHasRadius(m_renderer.style()->hasBorderRadius());
        clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
        if (m_renderer.isPositioned())
            clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
    }

    if (m_renderer.hasClip()) {
        LayoutRect newClip = m_renderer.clipRect(offset);
        clipRects.setPosClipRect(intersection(newClip, clipRects.posClipRect()));
        clipRects.setOverflowClipRect(intersection(newClip, clipRects.overflowClipRect()));
    }
}
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;
}
예제 #17
0
bool RenderSVGForeignObject::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
{
    // Embedded content is drawn in the foreground phase.
    if (hitTestAction != HitTestForeground)
        return false;

    FloatPoint localPoint = localTransform().inverse().mapPoint(pointInParent);

    // Early exit if local point is not contained in clipped viewport area
    if (SVGRenderSupport::isOverflowHidden(this) && !m_viewport.contains(localPoint))
        return false;

    // FOs establish a stacking context, so we need to hit-test all layers.
    HitTestLocation hitTestLocation(roundedLayoutPoint(localPoint));
    return RenderBlock::nodeAtPoint(request, result, hitTestLocation, LayoutPoint(), HitTestForeground)
        || RenderBlock::nodeAtPoint(request, result, hitTestLocation, LayoutPoint(), HitTestFloat)
        || RenderBlock::nodeAtPoint(request, result, hitTestLocation, LayoutPoint(), HitTestChildBlockBackgrounds);
}
예제 #18
0
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(), false, true);
            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: 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 -= toSize(layer->location());
        }
    }

    m_hasCachedRelativePosition = true;
}
예제 #19
0
파일: Touch.cpp 프로젝트: CannedFish/webkit
Touch::Touch(Frame* frame, EventTarget* target, unsigned identifier, int screenX, int screenY, int pageX, int pageY, int radiusX, int radiusY, float rotationAngle, float force)
    : m_target(target)
    , m_identifier(identifier)
    , m_clientX(pageX - contentsX(frame))
    , m_clientY(pageY - contentsY(frame))
    , m_screenX(screenX)
    , m_screenY(screenY)
    , m_pageX(pageX)
    , m_pageY(pageY)
    , m_radiusX(radiusX)
    , m_radiusY(radiusY)
    , m_rotationAngle(rotationAngle)
    , m_force(force)
{
    float scaleFactor = frame->pageZoomFactor() * frame->frameScaleFactor();
    float x = pageX * scaleFactor;
    float y = pageY * scaleFactor;
    m_absoluteLocation = roundedLayoutPoint(FloatPoint(x, y));
}
예제 #20
0
VisiblePosition VisibleSelection::visiblePositionRespectingEditingBoundary(const LayoutPoint& localPoint, Node* targetNode) const
{
    if (!targetNode->renderer())
        return VisiblePosition();

    LayoutPoint selectionEndPoint = localPoint;
    Element* editableElement = rootEditableElement();

    if (editableElement && !editableElement->contains(targetNode)) {
        if (!editableElement->renderer())
            return VisiblePosition();

        FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
        selectionEndPoint = roundedLayoutPoint(editableElement->renderer()->absoluteToLocal(absolutePoint));
        targetNode = editableElement;
    }

    return VisiblePosition(targetNode->renderer()->positionForPoint(selectionEndPoint));
}
bool LayoutSVGShape::nodeAtFloatPoint(HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
{
    // We only draw in the foreground phase, so we only hit-test then.
    if (hitTestAction != HitTestForeground)
        return false;

    FloatPoint localPoint;
    if (!SVGLayoutSupport::transformToUserSpaceAndCheckClipping(this, localToParentTransform(), pointInParent, localPoint))
        return false;

    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, result.hitTestRequest(), style()->pointerEvents());
    if (nodeAtFloatPointInternal(result.hitTestRequest(), localPoint, hitRules)) {
        const LayoutPoint& localLayoutPoint = roundedLayoutPoint(localPoint);
        updateHitTestResult(result, localLayoutPoint);
        if (!result.addNodeToListBasedTestResult(element(), localLayoutPoint))
            return true;
    }

    return false;
}
예제 #22
0
void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context,
                                           ClipRects& clipRects) const {
  const LayoutBoxModelObject& layoutObject = *m_layer.layoutObject();
  if (!m_layer.parent() &&
      !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
    // The root layer's clip rect is always infinite.
    clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
    return;
  }

  bool isClippingRoot = &m_layer == context.rootLayer;

  // For transformed layers, the root layer was shifted to be us, so there is no
  // need to examine the parent. We want to cache clip rects with us as the
  // root.
  PaintLayer* parentLayer = !isClippingRoot ? m_layer.parent() : nullptr;
  // Ensure that our parent's clip has been calculated so that we can examine
  // the values.
  if (parentLayer) {
    parentLayer->clipper().getOrCalculateClipRects(context, clipRects);
  } else {
    clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
  }

  adjustClipRectsForChildren(layoutObject, clipRects);

  if (shouldClipOverflow(context) || layoutObject.hasClip() ||
      (layoutObject.isSVGRoot() &&
       toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip())) {
    // This offset cannot use convertToLayerCoords, because sometimes our
    // rootLayer may be across some transformed layer boundary, for example, in
    // the PaintLayerCompositor overlapMap, where clipRects are needed in view
    // space.
    applyClipRects(context, layoutObject,
                   roundedLayoutPoint(layoutObject.localToAncestorPoint(
                       FloatPoint(), context.rootLayer->layoutObject())),
                   clipRects);
  }
}
예제 #23
0
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()) {
        const RenderStyle& lineStyle = this->lineStyle();
        LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x();
        LayoutUnit mty = adjustedLocation.y() + lineStyle.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent());
        if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom)) {
            blockFlow().updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty));
            return true;
        }
    }

    LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height));
    if (visibleToHitTesting() && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) {
        blockFlow().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
        if (!result.addNodeToRectBasedTestResult(blockFlow().element(), request, locationInContainer, boundsRect))
            return true;
    }

    return false;
}
예제 #24
0
void RenderLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const
{
    if (!m_renderer.layer()->parent()) {
        // The root layer's clip rect is always infinite.
        clipRects.reset(PaintInfo::infiniteRect());
        return;
    }

    bool isClippingRoot = m_renderer.layer() == context.rootLayer;

    // For transformed layers, the root layer was shifted to be us, so there is no need to
    // examine the parent. We want to cache clip rects with us as the root.
    RenderLayer* parentLayer = !isClippingRoot ? m_renderer.layer()->parent() : 0;

    // Ensure that our parent's clip has been calculated so that we can examine the values.
    if (parentLayer) {
        // FIXME: Why don't we just call getClipRects here?
        if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) {
            clipRects = *parentLayer->clipper().cachedClipRects(context);
        } else {
            parentLayer->clipper().calculateClipRects(context, clipRects);
        }
    } else {
        clipRects.reset(PaintInfo::infiniteRect());
    }

    adjustClipRectsForChildren(m_renderer, clipRects);

    // FIXME: This logic looks wrong. We'll apply overflow clip rects even if we were told to IgnoreOverflowClip if m_renderer.hasClip().
    if ((m_renderer.hasOverflowClip() && (context.respectOverflowClip == RespectOverflowClip || !isClippingRoot)) || m_renderer.hasClip()) {
        // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
        // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where
        // clipRects are needed in view space.
        applyClipRects(context, m_renderer, roundedLayoutPoint(m_renderer.localToContainerPoint(FloatPoint(), context.rootLayer->renderer())), clipRects);
    }
}
예제 #25
0
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_objectBoundingBox.contains(localPoint)) {
                updateHitTestResult(result, roundedLayoutPoint(localPoint));
                return true;
            }
        }
    }

    return false;
}
예제 #26
0
void MouseRelatedEvent::computePageLocation()
{
    float zoomFactor = pageZoomFactor(this);
    setAbsoluteLocation(roundedLayoutPoint(FloatPoint(pageX() * zoomFactor, pageY() * zoomFactor)));
}
예제 #27
0
void TouchEventManager::updateTargetAndRegionMapsForTouchStarts(
    HeapVector<TouchInfo>& touchInfos)
{
    for (auto& touchInfo : touchInfos) {
        // Touch events implicitly capture to the touched node, and don't change
        // active/hover states themselves (Gesture events do). So we only need
        // to hit-test on touchstart and when the target could be different than
        // the corresponding pointer event target.
        if (touchInfo.point.state() == PlatformTouchPoint::TouchPressed) {
            HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent | HitTestRequest::ReadOnly | HitTestRequest::Active;
            LayoutPoint pagePoint = roundedLayoutPoint(m_frame->view()->rootFrameToContents(touchInfo.point.pos()));
            HitTestResult result;
            if (!m_touchSequenceDocument) {
                result = m_frame->eventHandler().hitTestResultAtPoint(pagePoint, hitType);
            } else if (m_touchSequenceDocument->frame()) {
                LayoutPoint framePoint = roundedLayoutPoint(m_touchSequenceDocument->frame()->view()->rootFrameToContents(touchInfo.point.pos()));
                result = EventHandler::hitTestResultInFrame(m_touchSequenceDocument->frame(), framePoint, hitType);
            } else {
                continue;
            }

            Node* node = result.innerNode();
            if (!node)
                continue;
            if (isHTMLCanvasElement(node)) {
                std::pair<Element*, String> regionInfo = toHTMLCanvasElement(node)->getControlAndIdIfHitRegionExists(result.pointInInnerNodeFrame());
                if (regionInfo.first)
                    node = regionInfo.first;
                touchInfo.region = regionInfo.second;
            }
            // Touch events should not go to text nodes.
            if (node->isTextNode())
                node = FlatTreeTraversal::parent(*node);
            touchInfo.touchNode = node;

            if (!m_touchSequenceDocument) {
                // Keep track of which document should receive all touch events
                // in the active sequence. This must be a single document to
                // ensure we don't leak Nodes between documents.
                m_touchSequenceDocument = &(touchInfo.touchNode->document());
                ASSERT(m_touchSequenceDocument->frame()->view());
            }

            // Ideally we'd ASSERT(!m_targetForTouchID.contains(point.id())
            // since we shouldn't get a touchstart for a touch that's already
            // down. However EventSender allows this to be violated and there's
            // some tests that take advantage of it. There may also be edge
            // cases in the browser where this happens.
            // See http://crbug.com/345372.
            m_targetForTouchID.set(touchInfo.point.id(), touchInfo.touchNode);

            m_regionForTouchID.set(touchInfo.point.id(), touchInfo.region);

            TouchAction effectiveTouchAction =
                TouchActionUtil::computeEffectiveTouchAction(
                    *touchInfo.touchNode);
            if (effectiveTouchAction != TouchActionAuto)
                m_frame->page()->chromeClient().setTouchAction(effectiveTouchAction);
        }
    }
}
예제 #28
0
void RenderLayerClipper::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
{
    if (!m_renderer->layer()->parent()) {
        // The root layer's clip rect is always infinite.
        clipRects.reset(PaintInfo::infiniteRect());
        return;
    }

    ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
    bool useCached = clipRectsType != TemporaryClipRects;

    // For transformed layers, the root layer was shifted to be us, so there is no need to
    // examine the parent. We want to cache clip rects with us as the root.
    RenderLayer* parentLayer = clipRectsContext.rootLayer != m_renderer->layer() ? m_renderer->layer()->parent() : 0;

    // Ensure that our parent's clip has been calculated so that we can examine the values.
    if (parentLayer) {
        if (useCached && parentLayer->clipper().clipRects(clipRectsContext)) {
            clipRects = *parentLayer->clipper().clipRects(clipRectsContext);
        } else {
            ClipRectsContext parentContext(clipRectsContext);
            parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; // FIXME: why?
            parentLayer->clipper().calculateClipRects(parentContext, clipRects);
        }
    } else {
        clipRects.reset(PaintInfo::infiniteRect());
    }

    // A fixed object is essentially the root of its containing block hierarchy, so when
    // we encounter such an object, we reset our clip rects to the fixedClipRect.
    if (m_renderer->style()->position() == FixedPosition) {
        clipRects.setPosClipRect(clipRects.fixedClipRect());
        clipRects.setOverflowClipRect(clipRects.fixedClipRect());
        clipRects.setFixed(true);
    } else if (m_renderer->style()->hasInFlowPosition()) {
        clipRects.setPosClipRect(clipRects.overflowClipRect());
    } else if (m_renderer->style()->position() == AbsolutePosition) {
        clipRects.setOverflowClipRect(clipRects.posClipRect());
    }

    // Update the clip rects that will be passed to child layers.
    if ((m_renderer->hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || m_renderer->layer() != clipRectsContext.rootLayer)) || m_renderer->hasClip()) {
        // This layer establishes a clip of some kind.

        // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
        // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where
        // clipRects are needed in view space.
        LayoutPoint offset;
        offset = roundedLayoutPoint(m_renderer->localToContainerPoint(FloatPoint(), clipRectsContext.rootLayer->renderer()));
        RenderView* view = m_renderer->view();
        ASSERT(view);
        if (view && clipRects.fixed() && clipRectsContext.rootLayer->renderer() == view) {
            offset -= view->frameView()->scrollOffsetForFixedPosition();
        }

        if (m_renderer->hasOverflowClip()) {
            ClipRect newOverflowClip = toRenderBox(m_renderer)->overflowClipRect(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy);
            if (m_renderer->style()->hasBorderRadius())
                newOverflowClip.setHasRadius(true);
            clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
            if (m_renderer->isPositioned())
                clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
        }
        if (m_renderer->hasClip()) {
            LayoutRect newPosClip = toRenderBox(m_renderer)->clipRect(offset, clipRectsContext.region);
            clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect()));
            clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect()));
            clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect()));
        }
    }
}
예제 #29
0
static LayoutPoint positionForEvent(const EventType& event)
{
    return roundedLayoutPoint(FloatPoint(event.x, event.y));
}