blob: b1517de56b693f9cdc6cdea6103954946161e34c [file] [log] [blame]
/*
* Copyright (C) 2022 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "InteractionRegion.h"
#include "Document.h"
#include "Frame.h"
#include "FrameSnapshotting.h"
#include "FrameView.h"
#include "GeometryUtilities.h"
#include "HTMLAnchorElement.h"
#include "HTMLFormControlElement.h"
#include "HitTestResult.h"
#include "Page.h"
#include "PathUtilities.h"
#include "PlatformMouseEvent.h"
#include "RenderBox.h"
#include "RenderLayer.h"
#include "RenderLayerBacking.h"
#include "SimpleRange.h"
#include <wtf/NeverDestroyed.h>
namespace WebCore {
InteractionRegion::~InteractionRegion() = default;
static CursorType cursorTypeForElement(Element& element)
{
auto* renderer = element.renderer();
auto* style = renderer ? &renderer->style() : nullptr;
auto cursorType = style ? style->cursor() : CursorType::Auto;
if (cursorType == CursorType::Auto && element.enclosingLinkEventParentOrSelf())
cursorType = CursorType::Pointer;
return cursorType;
}
std::optional<InteractionRegion> interactionRegionForRenderedRegion(RenderObject& regionRenderer, const Region& region)
{
if (!regionRenderer.node())
return std::nullopt;
auto bounds = region.bounds();
if (bounds.isEmpty())
return std::nullopt;
auto& mainFrameView = *regionRenderer.document().frame()->mainFrame().view();
auto layoutArea = mainFrameView.layoutSize().area();
if (bounds.area() > layoutArea / 2)
return std::nullopt;
auto element = dynamicDowncast<Element>(regionRenderer.node());
if (!element)
element = regionRenderer.node()->parentElement();
if (auto* linkElement = element->enclosingLinkEventParentOrSelf())
element = linkElement;
if (!element || !element->renderer())
return std::nullopt;
auto& renderer = *element->renderer();
// FIXME: Consider also allowing elements that only receive touch events.
if (!renderer.style().eventListenerRegionTypes().contains(EventListenerRegionType::MouseClick))
return std::nullopt;
auto cursor = cursorTypeForElement(*element);
if (cursor != CursorType::Pointer && !is<HTMLFormControlElement>(element))
return std::nullopt;
bool isInlineNonBlock = renderer.isInline() && !renderer.isReplacedOrInlineBlock();
if (isInlineNonBlock) {
static constexpr float inlinePadding = 3;
bounds.inflate(inlinePadding);
}
bool hasLightBackground = true;
if (auto linkRange = makeRangeSelectingNode(*element))
hasLightBackground = estimatedBackgroundColorForRange(*linkRange, *element->document().frame()).luminance() > 0.5;
float borderRadius = 0;
if (const auto& renderBox = dynamicDowncast<RenderBox>(renderer))
borderRadius = renderBox->borderRadii().minimumRadius();
Region boundsRegion;
boundsRegion.unite(bounds);
return { {
element->identifier(),
boundsRegion,
hasLightBackground,
borderRadius
} };
}
TextStream& operator<<(TextStream& ts, const InteractionRegion& interactionRegion)
{
ts.dumpProperty("region", interactionRegion.regionInLayerCoordinates);
ts.dumpProperty("hasLightBackground", interactionRegion.hasLightBackground);
ts.dumpProperty("borderRadius", interactionRegion.borderRadius);
return ts;
}
}