SelectionHandler.cpp [plain text]
#include "config.h"
#include "SelectionHandler.h"
#include "DOMSupport.h"
#include "Document.h"
#include "FatFingers.h"
#include "FloatQuad.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameSelection.h"
#include "FrameView.h"
#include "HitTestResult.h"
#include "InputHandler.h"
#include "IntRect.h"
#include "Page.h"
#include "RenderLayer.h"
#include "SelectionOverlay.h"
#include "TouchEventHandler.h"
#include "VisibleUnits.h"
#include "WebPageClient.h"
#include "WebPage_p.h"
#include "htmlediting.h"
#include <BlackBerryPlatformKeyboardEvent.h>
#include <BlackBerryPlatformLog.h>
#include <BlackBerryPlatformViewportAccessor.h>
#include <sys/keycodes.h>
#define SHOWDEBUG_SELECTIONHANDLER 0
#define SHOWDEBUG_SELECTIONHANDLER_TIMING 0
using namespace BlackBerry::Platform;
using namespace WebCore;
#if SHOWDEBUG_SELECTIONHANDLER
#define SelectionLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
#else
#define SelectionLog(severity, format, ...)
#endif // SHOWDEBUG_SELECTIONHANDLER
#if SHOWDEBUG_SELECTIONHANDLER_TIMING
#define SelectionTimingLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
#else
#define SelectionTimingLog(severity, format, ...)
#endif // SHOWDEBUG_SELECTIONHANDLER_TIMING
namespace BlackBerry {
namespace WebKit {
SelectionHandler::SelectionHandler(WebPagePrivate* page)
: m_webPage(page)
, m_selectionActive(false)
, m_caretActive(false)
, m_lastUpdatedEndPointIsValid(false)
, m_didSuppressCaretPositionChangedNotification(false)
{
}
SelectionHandler::~SelectionHandler()
{
}
void SelectionHandler::cancelSelection()
{
m_selectionActive = false;
m_lastSelectionRegion = IntRectRegion();
if (m_webPage->m_selectionOverlay)
m_webPage->m_selectionOverlay->hide();
m_webPage->m_client->notifySelectionDetailsChanged(SelectionDetails());
m_webPage->updateSelectionScrollView(0);
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::cancelSelection");
if (m_webPage->m_inputHandler->isInputMode())
m_webPage->m_inputHandler->cancelSelection();
else
m_webPage->focusedOrMainFrame()->selection()->clear();
}
BlackBerry::Platform::String SelectionHandler::selectedText() const
{
return m_webPage->focusedOrMainFrame()->editor().selectedText();
}
WebCore::IntRect SelectionHandler::clippingRectForVisibleContent() const
{
Frame* frame = m_webPage->focusedOrMainFrame();
WebCore::IntRect clipRect = WebCore::IntRect(WebCore::IntPoint(0, 0), frame->view()->contentsSize());
if (frame != m_webPage->mainFrame()) {
clipRect = m_webPage->getRecursiveVisibleWindowRect(frame->view(), true );
clipRect = m_webPage->m_mainFrame->view()->windowToContents(clipRect);
}
WebCore::IntRect inputBoundingBox = m_webPage->m_inputHandler->boundingBoxForInputField();
if (!inputBoundingBox.isEmpty()) {
inputBoundingBox = m_webPage->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(inputBoundingBox));
clipRect.intersect(inputBoundingBox);
}
return clipRect;
}
void SelectionHandler::regionForTextQuads(Vector<FloatQuad> &quadList, IntRectRegion& region, bool shouldClipToVisibleContent) const
{
ASSERT(region.isEmpty());
if (!quadList.isEmpty()) {
FrameView* frameView = m_webPage->focusedOrMainFrame()->view();
WebCore::IntRect frameRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
WebCore::IntPoint framePosition = m_webPage->frameOffset(m_webPage->focusedOrMainFrame());
WebCore::IntRect clippingRect = shouldClipToVisibleContent ? clippingRectForVisibleContent() : WebCore::IntRect(-1, -1, 0, 0);
Platform::IntRect selectionBoundingBox;
std::vector<Platform::IntRect> adjustedIntRects;
for (unsigned i = 0; i < quadList.size(); i++) {
WebCore::IntRect enclosingRect = quadList[i].enclosingBoundingBox();
enclosingRect.intersect(frameRect);
enclosingRect.move(framePosition.x(), framePosition.y());
if (clippingRect.location() != DOMSupport::InvalidPoint)
enclosingRect.intersect(clippingRect);
adjustedIntRects.push_back(enclosingRect);
selectionBoundingBox.unite(enclosingRect);
}
region = IntRectRegion(selectionBoundingBox, adjustedIntRects.size(), adjustedIntRects);
}
}
static VisiblePosition visiblePositionForPointIgnoringClipping(const Frame& frame, const WebCore::IntPoint& framePoint)
{
HitTestResult result = frame.eventHandler()->hitTestResultAtPoint(framePoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping);
Node* node = result.innerNode();
if (!node || node->document() != frame.document())
return VisiblePosition();
RenderObject* renderer = node->renderer();
if (!renderer)
return VisiblePosition();
VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint());
if (visiblePos.isNull())
visiblePos = VisiblePosition(Position(createLegacyEditingPosition(node, 0)));
return visiblePos;
}
static unsigned directionOfPointRelativeToRect(const WebCore::IntPoint& point, const WebCore::IntRect& rect, const bool useTopPadding = true, const bool useBottomPadding = true)
{
ASSERT(!rect.contains(point));
const int verticalPadding = 5;
if (point.y() - (useTopPadding ? verticalPadding : 0) < rect.y())
return KEYCODE_UP;
if (point.y() > rect.maxY() + (useBottomPadding ? verticalPadding : 0))
return KEYCODE_DOWN;
if (point.x() < rect.location().x())
return KEYCODE_LEFT;
if (point.x() > rect.maxX())
return KEYCODE_RIGHT;
return 0;
}
bool SelectionHandler::shouldUpdateSelectionOrCaretForPoint(const WebCore::IntPoint& point, const WebCore::IntRect& caretRect, bool startCaret) const
{
ASSERT(m_webPage->m_inputHandler->isInputMode());
if (point == DOMSupport::InvalidPoint)
return true;
VisibleSelection currentSelection = m_webPage->focusedOrMainFrame()->selection()->selection();
bool aboveCaret = point.y() < caretRect.y();
bool belowCaret = point.y() >= caretRect.maxY();
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::shouldUpdateSelectionOrCaretForPoint multiline = %s above = %s below = %s first line = %s last line = %s start = %s",
m_webPage->m_inputHandler->isMultilineInputMode() ? "true" : "false",
aboveCaret ? "true" : "false",
belowCaret ? "true" : "false",
inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) ? "true" : "false",
inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) ? "true" : "false",
startCaret ? "true" : "false");
if (!m_webPage->m_inputHandler->isMultilineInputMode() && (aboveCaret || belowCaret))
return false;
if (startCaret && inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) && aboveCaret)
return false;
if (!startCaret && inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) && belowCaret)
return false;
return true;
}
void SelectionHandler::setCaretPosition(const WebCore::IntPoint& position)
{
if (!m_webPage->m_inputHandler->isInputMode() || !m_webPage->focusedOrMainFrame()->document()->focusedElement())
return;
m_caretActive = true;
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::setCaretPosition requested point %s",
Platform::IntPoint(position).toString().c_str());
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
FrameSelection* controller = focusedFrame->selection();
WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, position);
WebCore::IntRect currentCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
if (relativePoint == DOMSupport::InvalidPoint || !shouldUpdateSelectionOrCaretForPoint(relativePoint, currentCaretRect)) {
selectionPositionChanged(true );
return;
}
WebCore::IntRect nodeOutlineBounds(m_webPage->m_inputHandler->boundingBoxForInputField());
if (!nodeOutlineBounds.isEmpty() && !nodeOutlineBounds.contains(relativePoint)) {
if (unsigned character = directionOfPointRelativeToRect(relativePoint, currentCaretRect))
m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character));
selectionPositionChanged(true );
return;
}
VisibleSelection newSelection(focusedFrame->visiblePositionForPoint(relativePoint));
if (controller->selection() == newSelection) {
selectionPositionChanged(true );
return;
}
controller->setSelection(newSelection);
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::setCaretPosition point valid, cursor updated");
}
void SelectionHandler::inputHandlerDidFinishProcessingChange()
{
if (m_didSuppressCaretPositionChangedNotification)
notifyCaretPositionChangedIfNeeded(false);
}
static bool shouldExtendSelectionInDirection(const VisibleSelection& selection, unsigned character)
{
FrameSelection tempSelection;
tempSelection.setSelection(selection);
switch (character) {
case KEYCODE_LEFT:
tempSelection.modify(FrameSelection::AlterationExtend, DirectionLeft, CharacterGranularity);
break;
case KEYCODE_RIGHT:
tempSelection.modify(FrameSelection::AlterationExtend, DirectionRight, CharacterGranularity);
break;
case KEYCODE_UP:
tempSelection.modify(FrameSelection::AlterationExtend, DirectionBackward, LineGranularity);
break;
case KEYCODE_DOWN:
tempSelection.modify(FrameSelection::AlterationExtend, DirectionForward, LineGranularity);
break;
default:
break;
}
if ((character == KEYCODE_LEFT || character == KEYCODE_RIGHT)
&& (!inSameLine(selection.visibleStart(), tempSelection.selection().visibleStart())
|| !inSameLine(selection.visibleEnd(), tempSelection.selection().visibleEnd())))
return false;
return tempSelection.selection().selectionType() == VisibleSelection::RangeSelection;
}
static int clamp(const int min, const int value, const int max)
{
return value < min ? min : std::min(value, max);
}
static VisiblePosition directionalVisiblePositionAtExtentOfBox(Frame* frame, const WebCore::IntRect& boundingBox, unsigned direction, const WebCore::IntPoint& basePoint)
{
ASSERT(frame);
if (!frame)
return VisiblePosition();
switch (direction) {
case KEYCODE_LEFT:
return frame->visiblePositionForPoint(WebCore::IntPoint(boundingBox.x(), clamp(boundingBox.y(), basePoint.y(), boundingBox.maxY())));
case KEYCODE_RIGHT:
return frame->visiblePositionForPoint(WebCore::IntPoint(boundingBox.maxX(), clamp(boundingBox.y(), basePoint.y(), boundingBox.maxY())));
case KEYCODE_UP:
return frame->visiblePositionForPoint(WebCore::IntPoint(clamp(boundingBox.x(), basePoint.x(), boundingBox.maxX()), boundingBox.y()));
case KEYCODE_DOWN:
return frame->visiblePositionForPoint(WebCore::IntPoint(clamp(boundingBox.x(), basePoint.x(), boundingBox.maxX()), boundingBox.maxY()));
default:
break;
}
return frame->visiblePositionForPoint(WebCore::IntPoint(basePoint.x(), basePoint.y()));
}
static bool pointIsOutsideOfBoundingBoxInDirection(unsigned direction, const WebCore::IntPoint& selectionPoint, const WebCore::IntRect& boundingBox)
{
if ((direction == KEYCODE_LEFT && selectionPoint.x() < boundingBox.x())
|| (direction == KEYCODE_UP && selectionPoint.y() < boundingBox.y())
|| (direction == KEYCODE_RIGHT && selectionPoint.x() > boundingBox.maxX())
|| (direction == KEYCODE_DOWN && selectionPoint.y() > boundingBox.maxY()))
return true;
return false;
}
unsigned SelectionHandler::extendSelectionToFieldBoundary(bool isStartHandle, const WebCore::IntPoint& selectionPoint, VisibleSelection& newSelection)
{
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
if (!focusedFrame->document()->focusedElement() || !focusedFrame->document()->focusedElement()->renderer())
return 0;
VisibleSelection activeSelection = focusedFrame->selection()->selection();
WebCore::IntRect caretRect = isStartHandle ? activeSelection.visibleStart().absoluteCaretBounds() : activeSelection.visibleEnd().absoluteCaretBounds();
WebCore::IntRect nodeBoundingBox = focusedFrame->document()->focusedElement()->renderer()->absoluteBoundingBoxRect();
nodeBoundingBox.inflate(-1);
unsigned character = directionOfPointRelativeToRect(selectionPoint, caretRect, isStartHandle , !isStartHandle );
if ((isStartHandle && (character == KEYCODE_RIGHT || character == KEYCODE_DOWN))
|| (!isStartHandle && (character == KEYCODE_LEFT || character == KEYCODE_UP)))
character = 0;
VisiblePosition newVisiblePosition = isStartHandle ? activeSelection.extent() : activeSelection.base();
if (pointIsOutsideOfBoundingBoxInDirection(character, selectionPoint, nodeBoundingBox))
newVisiblePosition = directionalVisiblePositionAtExtentOfBox(focusedFrame, nodeBoundingBox, character, selectionPoint);
if (isStartHandle)
newSelection = VisibleSelection(newVisiblePosition, newSelection.extent(), true );
else
newSelection = VisibleSelection(newSelection.base(), newVisiblePosition, true );
if (activeSelection == newSelection)
return character;
return 0;
}
bool SelectionHandler::updateOrHandleInputSelection(VisibleSelection& newSelection, const WebCore::IntPoint& relativeStart, const WebCore::IntPoint& relativeEnd)
{
ASSERT(m_webPage->m_inputHandler->isInputMode());
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
Node* focusedNode = focusedFrame->document()->focusedElement();
if (!focusedNode || !focusedNode->renderer())
return false;
FrameSelection* controller = focusedFrame->selection();
WebCore::IntRect currentStartCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
WebCore::IntRect currentEndCaretRect = controller->selection().visibleEnd().absoluteCaretBounds();
if (!shouldUpdateSelectionOrCaretForPoint(relativeStart, currentStartCaretRect, true )
|| !shouldUpdateSelectionOrCaretForPoint(relativeEnd, currentEndCaretRect, false )) {
selectionPositionChanged(true );
return true;
}
WebCore::IntRect nodeBoundingBox = focusedNode->renderer()->absoluteBoundingBoxRect();
bool startIsOutsideOfField = relativeStart != DOMSupport::InvalidPoint && !nodeBoundingBox.contains(relativeStart);
bool endIsOutsideOfField = relativeEnd != DOMSupport::InvalidPoint && !nodeBoundingBox.contains(relativeEnd);
if (startIsOutsideOfField && endIsOutsideOfField)
return false;
unsigned character = 0;
bool needToInvertDirection = false;
if (startIsOutsideOfField) {
character = extendSelectionToFieldBoundary(true , relativeStart, newSelection);
if (character && controller->selection().isBaseFirst()) {
controller->setSelection(VisibleSelection(controller->selection().end(), controller->selection().start(), true ));
needToInvertDirection = true;
}
} else if (endIsOutsideOfField) {
character = extendSelectionToFieldBoundary(false , relativeEnd, newSelection);
if (character && !controller->selection().isBaseFirst()) {
controller->setSelection(VisibleSelection(controller->selection().start(), controller->selection().end(), true ));
}
}
if (!character)
return false;
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::updateOrHandleInputSelection making selection change attempt using key event %d",
character);
if (shouldExtendSelectionInDirection(controller->selection(), character))
m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character, Platform::KeyboardEvent::KeyDown, KEYMOD_SHIFT));
if (needToInvertDirection)
controller->setSelection(VisibleSelection(controller->selection().extent(), controller->selection().base(), true ));
selectionPositionChanged(true );
return true;
}
void SelectionHandler::setSelection(WebCore::IntPoint start, WebCore::IntPoint end)
{
m_selectionActive = true;
ASSERT(m_webPage);
ASSERT(m_webPage->focusedOrMainFrame());
ASSERT(m_webPage->focusedOrMainFrame()->selection());
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
FrameSelection* controller = focusedFrame->selection();
#if SHOWDEBUG_SELECTIONHANDLER_TIMING
m_timer.start();
#endif
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::setSelection adjusted points %s, %s",
Platform::IntPoint(start).toString().c_str(),
Platform::IntPoint(end).toString().c_str());
bool startIsValid = start != DOMSupport::InvalidPoint;
m_lastUpdatedEndPointIsValid = end != DOMSupport::InvalidPoint;
ASSERT(startIsValid || m_lastUpdatedEndPointIsValid);
if (m_webPage->m_inputHandler->isInputMode() && !m_webPage->m_inputHandler->isMultilineInputMode()) {
WebCore::IntRect caret(startCaretViewportRect(m_webPage->frameOffset(focusedFrame)));
if (!caret.isEmpty()) {
int centerOfCaretY = caret.center().y();
if (startIsValid)
start.setY(centerOfCaretY);
if (m_lastUpdatedEndPointIsValid)
end.setY(centerOfCaretY);
}
}
WebCore::IntPoint relativeStart = start;
WebCore::IntPoint relativeEnd = end;
VisiblePosition newStart = controller->selection().visibleStart();
VisiblePosition newEnd = controller->selection().visibleEnd();
if (startIsValid) {
relativeStart = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, start);
VisiblePosition base = visiblePositionForPointIgnoringClipping(*focusedFrame, clipPointToVisibleContainer(start));
if (base.isNotNull())
newStart = base;
}
if (m_lastUpdatedEndPointIsValid) {
relativeEnd = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, end);
VisiblePosition extent = visiblePositionForPointIgnoringClipping(*focusedFrame, clipPointToVisibleContainer(end));
if (extent.isNotNull())
newEnd = extent;
}
VisibleSelection newSelection(newStart, newEnd, true );
if (!controller->selection().isRange())
m_webPage->updateSelectionScrollView(newSelection.visibleEnd().deepEquivalent().anchorNode());
if (m_webPage->m_inputHandler->isInputMode()) {
if (updateOrHandleInputSelection(newSelection, relativeStart, relativeEnd))
return;
}
if (controller->selection() == newSelection) {
selectionPositionChanged(true );
return;
}
Vector<FloatQuad> quads;
DOMSupport::visibleTextQuads(newSelection, quads);
IntRectRegion unclippedRegion;
regionForTextQuads(quads, unclippedRegion, false );
if (unclippedRegion.isEmpty()) {
selectionPositionChanged(true );
SelectionLog(Platform::LogLevelWarn, "SelectionHandler::setSelection selection points invalid, selection not updated.");
return;
}
if (m_selectionActive && !newSelection.isBaseFirst()) {
m_webPage->m_client->notifySelectionHandlesReversed();
newSelection = VisibleSelection(newSelection.extent(), newSelection.base());
}
controller->setSelection(newSelection);
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::setSelection selection points valid, selection updated.");
}
static Node* enclosingLinkEventParentForNode(Node* node)
{
if (!node)
return 0;
Node* linkNode = node->enclosingLinkEventParentOrSelf();
return linkNode && linkNode->isLink() ? linkNode : 0;
}
TextGranularity textGranularityFromSelectionExpansionType(SelectionExpansionType selectionExpansionType)
{
TextGranularity granularity;
switch (selectionExpansionType) {
case Word:
default:
granularity = WordGranularity;
break;
case Sentence:
granularity = SentenceGranularity;
break;
case Paragraph:
granularity = ParagraphGranularity;
break;
}
return granularity;
}
bool SelectionHandler::selectNodeIfFatFingersResultIsLink(FatFingersResult fatFingersResult)
{
if (!fatFingersResult.isValid())
return false;
Node* targetNode = fatFingersResult.node(FatFingersResult::ShadowContentNotAllowed);
ASSERT(targetNode);
if (Node* link = enclosingLinkEventParentForNode(targetNode)) {
Element* element = fatFingersResult.nodeAsElementIfApplicable();
if (!element)
return false;
m_animationHighlightColor = element->renderStyle()->initialTapHighlightColor();
selectObject(link);
m_webPage->m_client->stopExpandingSelection();
return true;
}
return false;
}
WebCore::IntRect SelectionHandler::startCaretViewportRect(const WebCore::IntPoint& frameOffset) const
{
WebCore::IntRect caretRect;
Frame* frame = m_webPage->focusedOrMainFrame();
if (!frame)
return caretRect;
if (frame->selection()->selectionType() != VisibleSelection::NoSelection) {
caretRect = frame->selection()->selection().visibleStart().absoluteCaretBounds();
caretRect.moveBy(frameOffset);
}
return caretRect;
}
void SelectionHandler::selectAtPoint(const WebCore::IntPoint& location, SelectionExpansionType selectionExpansionType)
{
if (selectionExpansionType == Word) {
m_animationOverlayStartPos = VisiblePosition();
m_animationOverlayEndPos = VisiblePosition();
m_currentAnimationOverlayRegion = IntRectRegion();
m_nextAnimationOverlayRegion = IntRectRegion();
m_selectionSubframeViewportRect = WebCore::IntRect();
}
if (location == DOMSupport::InvalidPoint) {
selectObject(WordGranularity);
return;
}
WebCore::IntPoint targetPosition;
FatFingersResult fatFingersResult = m_webPage->m_touchEventHandler->lastFatFingersResult();
if (selectNodeIfFatFingersResultIsLink(fatFingersResult))
return;
if (!fatFingersResult.resultMatches(location, FatFingers::Text) || !fatFingersResult.positionWasAdjusted() || !fatFingersResult.nodeAsElementIfApplicable()) {
fatFingersResult = FatFingers(m_webPage, location, FatFingers::Text).findBestPoint();
m_webPage->m_touchEventHandler->cacheTextResult(fatFingersResult);
}
if (!fatFingersResult.positionWasAdjusted()) {
if (isSelectionActive())
cancelSelection();
m_webPage->m_client->notifySelectionDetailsChanged(SelectionDetails());
m_webPage->m_touchEventHandler->sendClickAtFatFingersPoint();
return;
}
targetPosition = fatFingersResult.adjustedPosition();
if (selectNodeIfFatFingersResultIsLink(fatFingersResult))
return;
selectObject(targetPosition, textGranularityFromSelectionExpansionType(selectionExpansionType));
}
static bool isInvalidParagraph(const VisiblePosition& pos)
{
return endOfParagraph(pos).isNull() || pos == endOfParagraph(pos);
}
void SelectionHandler::selectNextParagraph()
{
FrameSelection* controller = m_webPage->focusedOrMainFrame()->selection();
VisiblePosition startPos = VisiblePosition(controller->start(), controller->affinity());
if (isStartOfLine(startPos) && isEndOfDocument(startPos))
startPos = startPos.previous(CannotCrossEditingBoundary);
VisiblePosition endPos(controller->end(), controller->affinity()); endPos = endPos.next(CannotCrossEditingBoundary); while (!isEndOfDocument(endPos) && endPos.isNotNull() && isInvalidParagraph(endPos))
endPos = endPos.next(CannotCrossEditingBoundary); endPos = endOfParagraph(endPos);
if (m_currentAnimationOverlayRegion.extents().bottom() >= endPos.absoluteCaretBounds().maxY() && endPos.isNotNull()) {
VisibleSelection selection = VisibleSelection(startPos, endPos);
selection.setAffinity(controller->affinity());
controller->setSelection(selection);
if (isEndOfDocument(endPos))
m_webPage->m_client->stopExpandingSelection();
}
}
void SelectionHandler::drawAnimationOverlay(IntRectRegion overlayRegion, bool isExpandingOverlayAtConstantRate, bool isStartOfSelection)
{
if (isExpandingOverlayAtConstantRate) {
WebCore::IntRect currentOverlayRect = m_currentAnimationOverlayRegion.extents();
WebCore::IntRect nextOverlayRect = m_nextAnimationOverlayRegion.extents();
WebCore::IntRect overlayRect(WebCore::IntRect(nextOverlayRect.location(), WebCore::IntSize(nextOverlayRect.width(), currentOverlayRect.height() + m_overlayExpansionHeight)));
overlayRegion = IntRectRegion(overlayRect);
}
m_webPage->m_selectionHighlight->draw(overlayRegion,
m_animationHighlightColor.red(), m_animationHighlightColor.green(), m_animationHighlightColor.blue(), m_animationHighlightColor.alpha(),
false ,
isStartOfSelection);
m_currentAnimationOverlayRegion = overlayRegion;
}
IntRectRegion SelectionHandler::regionForSelectionQuads(VisibleSelection selection)
{
Vector<FloatQuad> quads;
DOMSupport::visibleTextQuads(selection, quads);
IntRectRegion region;
regionForTextQuads(quads, region);
return region;
}
bool SelectionHandler::findNextAnimationOverlayRegion()
{
if (isEndOfDocument(m_animationOverlayEndPos) || m_animationOverlayEndPos.isNull())
return false;
m_animationOverlayEndPos = m_animationOverlayEndPos.next(CannotCrossEditingBoundary);
while (!isEndOfDocument(m_animationOverlayEndPos) && m_animationOverlayEndPos.isNotNull() && isInvalidParagraph(m_animationOverlayEndPos))
m_animationOverlayEndPos = m_animationOverlayEndPos.next(CannotCrossEditingBoundary); m_animationOverlayEndPos = endOfParagraph(m_animationOverlayEndPos);
VisibleSelection selection(m_animationOverlayStartPos, m_animationOverlayEndPos);
m_nextAnimationOverlayRegion = regionForSelectionQuads(selection);
return true;
}
void SelectionHandler::expandSelection(bool isScrollStarted)
{
if (m_currentAnimationOverlayRegion.isEmpty() || m_nextAnimationOverlayRegion.isEmpty())
return;
WebCore::IntPoint nextOverlayBottomRightPoint = WebCore::IntPoint(m_currentAnimationOverlayRegion.extents().bottomRight()) + WebCore::IntPoint(0, m_overlayExpansionHeight);
if (nextOverlayBottomRightPoint.y() > m_nextAnimationOverlayRegion.extents().bottom())
if (!findNextAnimationOverlayRegion()) {
drawAnimationOverlay(m_nextAnimationOverlayRegion, false);
selectNextParagraph();
return;
}
if (ensureSelectedTextVisible(nextOverlayBottomRightPoint, false ) && m_animationOverlayEndPos.isNotNull())
drawAnimationOverlay(IntRectRegion(), true );
else if (!isScrollStarted) {
m_webPage->m_client->startSelectionScroll();
return;
}
selectNextParagraph();
}
bool SelectionHandler::ensureSelectedTextVisible(const WebCore::IntPoint& point, bool scrollIfNeeded)
{
WebCore::IntRect viewportRect = selectionViewportRect();
if (!scrollIfNeeded)
return viewportRect.maxY() >= m_webPage->contentsSize().height() ? viewportRect.maxY() >= point.y() : viewportRect.maxY() >= point.y() + m_scrollMargin.height();
if (!m_selectionSubframeViewportRect.isEmpty())
return false;
WebCore::IntRect endLocation = m_animationOverlayEndPos.absoluteCaretBounds();
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
Frame* mainFrame = m_webPage->mainFrame();
if (focusedFrame && focusedFrame->view() && mainFrame && mainFrame->view() && focusedFrame != mainFrame)
endLocation = mainFrame->view()->windowToContents(focusedFrame->view()->contentsToWindow(endLocation));
Node* anchorNode = m_animationOverlayEndPos.deepEquivalent().anchorNode();
if (!anchorNode || !anchorNode->renderer())
return false;
RenderLayer* layer = anchorNode->renderer()->enclosingLayer();
if (!layer)
return false;
endLocation.inflateX(m_scrollMargin.width());
endLocation.inflateY(m_scrollMargin.height());
WebCore::IntRect revealRect(layer->getRectToExpose(viewportRect, endLocation, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded));
revealRect.setX(std::min(std::max(revealRect.x(), 0), m_webPage->maximumScrollPosition().x()));
revealRect.setY(std::min(std::max(revealRect.y(), 0), m_webPage->maximumScrollPosition().y()));
m_webPage->animateToScaleAndDocumentScrollPosition(m_webPage->currentScale() , WebCore::FloatPoint(revealRect.x(), revealRect.y()));
return true;
}
WebCore::IntRect SelectionHandler::selectionViewportRect() const
{
if (m_selectionSubframeViewportRect.isEmpty())
return WebCore::IntRect(m_webPage->scrollPosition(), m_selectionViewportSize);
return m_selectionSubframeViewportRect;
}
void SelectionHandler::setParagraphExpansionScrollMargin(const WebCore::IntSize& scrollMargin)
{
m_scrollMargin.setWidth(scrollMargin.width());
m_scrollMargin.setHeight(scrollMargin.height());
}
bool SelectionHandler::expandSelectionToGranularity(Frame* frame, VisibleSelection selection, TextGranularity granularity, bool isInputMode)
{
ASSERT(frame);
ASSERT(frame->selection());
if (!(selection.start().anchorNode() && selection.start().anchorNode()->isTextNode()))
return false;
if (granularity == WordGranularity)
selection = DOMSupport::visibleSelectionForClosestActualWordStart(selection);
selection.expandUsingGranularity(granularity);
selection.setAffinity(frame->selection()->affinity());
if (isInputMode && !frame->selection()->shouldChangeSelection(selection))
return false;
m_animationOverlayStartPos = selection.visibleStart();
m_animationOverlayEndPos = selection.visibleEnd();
if (granularity == WordGranularity) {
m_webPage->updateSelectionScrollView(selection.visibleEnd().deepEquivalent().anchorNode());
Element* element = m_animationOverlayStartPos.deepEquivalent().element();
if (!element)
return false;
m_animationHighlightColor = element->renderStyle()->initialTapHighlightColor();
}
ensureSelectedTextVisible(WebCore::IntPoint(), true );
drawAnimationOverlay(regionForSelectionQuads(selection), false , granularity == WordGranularity );
frame->selection()->setSelection(selection);
if (granularity == ParagraphGranularity)
findNextAnimationOverlayRegion();
return true;
}
void SelectionHandler::selectObject(const WebCore::IntPoint& location, TextGranularity granularity)
{
ASSERT(location.x() >= 0 && location.y() >= 0);
ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::selectObject adjusted points %s",
Platform::IntPoint(location).toString().c_str());
WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, location);
VisiblePosition pointLocation(focusedFrame->visiblePositionForPoint(relativePoint));
VisibleSelection selection = VisibleSelection(pointLocation, pointLocation);
if (!m_webPage->m_inputHandler->boundingBoxForInputField().contains(relativePoint)) {
Node* anchorNode = selection.start().anchorNode();
if (!anchorNode || anchorNode->isElementNode())
m_webPage->m_page->focusController()->setFocusedElement(toElement(anchorNode), focusedFrame);
}
m_selectionActive = expandSelectionToGranularity(focusedFrame, selection, granularity, m_webPage->m_inputHandler->isInputMode());
}
void SelectionHandler::selectObject(TextGranularity granularity)
{
ASSERT(m_webPage && m_webPage->m_inputHandler);
if (!m_webPage->m_inputHandler->isInputMode())
return;
ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::selectObject using current selection");
ASSERT(focusedFrame->selection()->selectionType() != VisibleSelection::NoSelection);
VisibleSelection selectionOrigin = focusedFrame->selection()->selection();
if (m_webPage->m_inputHandler->isCaretAtEndOfText())
selectionOrigin = previousWordPosition(selectionOrigin.start());
m_selectionActive = expandSelectionToGranularity(focusedFrame, selectionOrigin, granularity, true );
}
void SelectionHandler::selectObject(Node* node)
{
if (!node)
return;
if (node != m_webPage->m_inputHandler->currentFocusElement().get())
m_webPage->clearFocusNode();
m_selectionActive = true;
ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
Frame* focusedFrame = m_webPage->focusedOrMainFrame();
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::selectNode");
VisibleSelection selection = VisibleSelection::selectionFromContentsOfNode(node);
drawAnimationOverlay(regionForSelectionQuads(selection), false , true );
focusedFrame->selection()->setSelection(selection);
m_webPage->updateSelectionScrollView(node);
}
static TextDirection directionOfEnclosingBlock(FrameSelection* selection)
{
Node* enclosingBlockNode = enclosingBlock(selection->selection().extent().deprecatedNode());
if (!enclosingBlockNode)
return LTR;
if (RenderObject* renderer = enclosingBlockNode->renderer())
return renderer->style()->direction();
return LTR;
}
static inline int comparePointsToReferencePoint(const WebCore::IntPoint& p1, const WebCore::IntPoint& p2, const WebCore::IntPoint& referencePoint, bool rightGravity)
{
int dy1 = abs(referencePoint.y() - p1.y());
int dy2 = abs(referencePoint.y() - p2.y());
if (dy1 != dy2)
return dy2 - dy1;
if (p1.x() == p2.x())
return 0;
if (p1.x() > p2.x())
return rightGravity ? 1 : -1;
return rightGravity ? -1 : 1;
}
static WebCore::IntPoint minXMinYCorner(const WebCore::IntRect& rect) { return rect.location(); } static WebCore::IntPoint maxXMinYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x() + rect.width() - 1, rect.y()); } static WebCore::IntPoint minXMaxYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x(), rect.y() + rect.height() - 1); } static WebCore::IntPoint maxXMaxYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1); }
static inline bool caretIsOnLeft(bool isStartCaret, bool isRTL)
{
if (isStartCaret)
return !isRTL;
return isRTL;
}
static inline WebCore::IntPoint caretLocationForRect(const WebCore::IntRect& rect, bool isStartCaret, bool isRTL)
{
return caretIsOnLeft(isStartCaret, isRTL) ? minXMinYCorner(rect) : maxXMinYCorner(rect);
}
static inline WebCore::IntPoint caretComparisonPointForRect(const WebCore::IntRect& rect, bool isStartCaret, bool isRTL)
{
if (isStartCaret)
return caretIsOnLeft(isStartCaret, isRTL) ? minXMinYCorner(rect) : maxXMinYCorner(rect);
return caretIsOnLeft(isStartCaret, isRTL) ? minXMaxYCorner(rect) : maxXMaxYCorner(rect);
}
static void adjustCaretRects(WebCore::IntRect& startCaret, bool isStartCaretClippedOut, WebCore::IntRect& endCaret, bool isEndCaretClippedOut,
const std::vector<Platform::IntRect> rectList, const WebCore::IntPoint& startReferencePoint, const WebCore::IntPoint& endReferencePoint, bool isRTL)
{
if (isStartCaretClippedOut)
startCaret.setLocation(DOMSupport::InvalidPoint);
else {
startCaret = rectList[0];
startCaret.setLocation(caretLocationForRect(startCaret, true, isRTL));
startCaret.setWidth(1);
}
if (isEndCaretClippedOut)
endCaret.setLocation(DOMSupport::InvalidPoint);
else {
endCaret = rectList[0];
endCaret.setLocation(caretLocationForRect(endCaret, false, isRTL));
endCaret.setWidth(1);
}
if (isStartCaretClippedOut && isEndCaretClippedOut)
return;
for (unsigned i = 1; i < rectList.size(); i++) {
WebCore::IntRect currentRect(rectList[i]);
if (!isStartCaretClippedOut && comparePointsToReferencePoint(
caretComparisonPointForRect(currentRect, true, isRTL),
caretComparisonPointForRect(startCaret, true, isRTL),
startReferencePoint, isRTL) > 0) {
startCaret.setLocation(caretLocationForRect(currentRect, true, isRTL));
startCaret.setHeight(currentRect.height());
}
if (!isEndCaretClippedOut && comparePointsToReferencePoint(
caretComparisonPointForRect(currentRect, false, isRTL),
caretComparisonPointForRect(endCaret, false, isRTL),
endReferencePoint, !isRTL) > 0) {
endCaret.setLocation(caretLocationForRect(currentRect, false, isRTL));
endCaret.setHeight(currentRect.height());
}
}
}
WebCore::IntPoint SelectionHandler::clipPointToVisibleContainer(const WebCore::IntPoint& point) const
{
ASSERT(m_webPage->m_mainFrame && m_webPage->m_mainFrame->view());
Frame* frame = m_webPage->focusedOrMainFrame();
WebCore::IntPoint clippedPoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), frame, point, true );
if (m_webPage->m_inputHandler->isInputMode()
&& frame->document()->focusedElement()
&& frame->document()->focusedElement()->renderer()) {
WebCore::IntRect boundingBox(frame->document()->focusedElement()->renderer()->absoluteBoundingBoxRect());
boundingBox.inflate(-1);
clippedPoint = WebCore::IntPoint(clamp(boundingBox.x(), clippedPoint.x(), boundingBox.maxX()), clamp(boundingBox.y(), clippedPoint.y(), boundingBox.maxY()));
}
return clippedPoint;
}
static WebCore::IntPoint referencePoint(const VisiblePosition& position, const WebCore::IntRect& boundingRect, const WebCore::IntPoint& framePosition, bool isStartCaret, bool isRTL)
{
WebCore::IntRect startCaretBounds(position.absoluteCaretBounds());
startCaretBounds.move(framePosition.x(), framePosition.y());
if (startCaretBounds.isEmpty() || !boundingRect.contains(startCaretBounds))
startCaretBounds = boundingRect;
return caretComparisonPointForRect(startCaretBounds, isStartCaret, isRTL);
}
static bool regionRectListContainsPoint(const IntRectRegion& region, const WebCore::IntPoint& point)
{
if (!region.extents().contains(point))
return false;
std::vector<Platform::IntRect> rectList = region.rects();
for (unsigned i = 0; i < rectList.size(); i++) {
if (rectList[i].contains(point))
return true;
}
return false;
}
bool SelectionHandler::inputNodeOverridesTouch() const
{
if (!m_webPage->m_inputHandler->isInputMode())
return false;
Node* focusedNode = m_webPage->focusedOrMainFrame()->document()->focusedElement();
if (!focusedNode || !focusedNode->isElementNode())
return false;
DEFINE_STATIC_LOCAL(QualifiedName, selectionTouchOverrideAttr, (nullAtom, "data-blackberry-end-selection-on-touch", nullAtom));
Element* element = toElement(focusedNode);
return DOMSupport::elementAttributeState(element, selectionTouchOverrideAttr) == DOMSupport::On;
}
RequestedHandlePosition SelectionHandler::requestedSelectionHandlePosition(const VisibleSelection& selection) const
{
Element* element = DOMSupport::selectionContainerElement(selection);
return DOMSupport::elementHandlePositionAttribute(element);
}
void SelectionHandler::selectionPositionChanged(bool forceUpdateWithoutChange)
{
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::selectionPositionChanged forceUpdateWithoutChange = %s",
forceUpdateWithoutChange ? "true" : "false");
if (!m_webPage->m_mainFrame)
return;
if (m_webPage->m_inputHandler->isInputMode() && m_webPage->m_inputHandler->processingChange()) {
if (m_webPage->m_selectionOverlay)
m_webPage->m_selectionOverlay->hide();
m_webPage->m_client->cancelSelectionVisuals();
m_didSuppressCaretPositionChangedNotification = true;
return;
}
notifyCaretPositionChangedIfNeeded(m_webPage->m_touchEventHandler->m_userTriggeredTouchPressOnTextInput);
Frame* frame = m_webPage->focusedOrMainFrame();
if (frame->view()->needsLayout())
return;
WebCore::IntPoint framePos = m_webPage->frameOffset(frame);
if (m_selectionActive && (m_caretActive || frame->selection()->isNone()))
m_selectionActive = false;
else if (frame->selection()->isRange())
m_selectionActive = true;
else if (!m_selectionActive)
return;
if (Node* focusedNode = frame->document()->focusedElement()) {
if (focusedNode->hasTagName(HTMLNames::selectTag) || (focusedNode->isElementNode() && DOMSupport::isPopupInputField(toElement(focusedNode)))) {
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::selectionPositionChanged selection is on a popup control, skipping rendering.");
return;
}
}
SelectionTimingLog(Platform::LogLevelInfo,
"SelectionHandler::selectionPositionChanged starting at %f",
m_timer.elapsed());
WebCore::IntRect startCaret(DOMSupport::InvalidPoint, WebCore::IntSize());
WebCore::IntRect endCaret(DOMSupport::InvalidPoint, WebCore::IntSize());
Vector<FloatQuad> quads;
DOMSupport::visibleTextQuads(frame->selection()->selection(), quads);
IntRectRegion unclippedRegion;
regionForTextQuads(quads, unclippedRegion, false );
if (!forceUpdateWithoutChange && m_lastSelectionRegion.isEqual(unclippedRegion))
return;
m_lastSelectionRegion = unclippedRegion;
bool isRTL = directionOfEnclosingBlock(frame->selection()) == RTL;
IntRectRegion visibleSelectionRegion;
if (!unclippedRegion.isEmpty()) {
WebCore::IntRect unclippedStartCaret;
WebCore::IntRect unclippedEndCaret;
WebCore::IntPoint startCaretReferencePoint = referencePoint(frame->selection()->selection().visibleStart(), unclippedRegion.extents(), framePos, true , isRTL);
WebCore::IntPoint endCaretReferencePoint = referencePoint(frame->selection()->selection().visibleEnd(), unclippedRegion.extents(), framePos, false , isRTL);
adjustCaretRects(unclippedStartCaret, false , unclippedEndCaret, false , unclippedRegion.rects(), startCaretReferencePoint, endCaretReferencePoint, isRTL);
regionForTextQuads(quads, visibleSelectionRegion);
#if SHOWDEBUG_SELECTIONHANDLER // Don't rely just on SelectionLog to avoid loop.
for (unsigned i = 0; i < unclippedRegion.numRects(); i++) {
SelectionLog(Platform::LogLevelInfo,
"Rect list - Unmodified #%d, %s",
i,
unclippedRegion.rects()[i].toString().c_str());
}
for (unsigned i = 0; i < visibleSelectionRegion.numRects(); i++) {
SelectionLog(Platform::LogLevelInfo,
"Rect list - Clipped to Visible #%d, %s",
i,
visibleSelectionRegion.rects()[i].toString().c_str());
}
#endif
bool shouldCareAboutPossibleClippedOutSelection = frame != m_webPage->mainFrame() || m_webPage->m_inputHandler->isInputMode();
if (!visibleSelectionRegion.isEmpty() || shouldCareAboutPossibleClippedOutSelection) {
bool shouldClipStartCaret = !regionRectListContainsPoint(visibleSelectionRegion, unclippedStartCaret.location());
bool shouldClipEndCaret = !regionRectListContainsPoint(visibleSelectionRegion, unclippedEndCaret.location());
adjustCaretRects(startCaret, shouldClipStartCaret, endCaret, shouldClipEndCaret, visibleSelectionRegion.rects(), startCaretReferencePoint, endCaretReferencePoint, isRTL);
}
}
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::selectionPositionChanged Start Rect=%s End Rect=%s",
Platform::IntRect(startCaret).toString().c_str(),
Platform::IntRect(endCaret).toString().c_str());
if (m_webPage->m_selectionOverlay)
m_webPage->m_selectionOverlay->draw(visibleSelectionRegion);
VisibleSelection currentSelection = frame->selection()->selection();
SelectionDetails details(startCaret, endCaret, visibleSelectionRegion, inputNodeOverridesTouch(),
m_lastSelection != currentSelection, requestedSelectionHandlePosition(frame->selection()->selection()), isRTL);
m_webPage->m_client->notifySelectionDetailsChanged(details);
m_lastSelection = currentSelection;
SelectionTimingLog(Platform::LogLevelInfo,
"SelectionHandler::selectionPositionChanged completed at %f",
m_timer.elapsed());
}
void SelectionHandler::notifyCaretPositionChangedIfNeeded(bool userTouchTriggeredOnTextField)
{
m_didSuppressCaretPositionChangedNotification = false;
if (m_caretActive || (m_webPage->m_inputHandler->isInputMode() && m_webPage->focusedOrMainFrame()->selection()->isCaret())) {
caretPositionChanged(userTouchTriggeredOnTextField);
}
}
void SelectionHandler::caretPositionChanged(bool userTouchTriggeredOnTextField)
{
SelectionLog(Platform::LogLevelInfo, "SelectionHandler::caretPositionChanged");
WebCore::IntRect caretLocation;
if (!m_webPage->m_inputHandler->isInputMode() && m_caretActive) {
m_caretActive = false;
m_webPage->m_client->notifyCaretChanged(caretLocation, userTouchTriggeredOnTextField);
return;
}
ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
ASSERT(m_webPage->m_inputHandler->isInputMode());
WebCore::IntRect clippingRectForContent(clippingRectForVisibleContent());
WebCore::IntPoint frameOffset(m_webPage->frameOffset(m_webPage->focusedOrMainFrame()));
if (m_webPage->focusedOrMainFrame()->selection()->selectionType() == VisibleSelection::CaretSelection) {
caretLocation = startCaretViewportRect(frameOffset);
if (!caretLocation.isEmpty())
caretLocation.intersect(clippingRectForContent); }
m_caretActive = !caretLocation.isEmpty();
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::caretPositionChanged caret Rect %s",
Platform::IntRect(caretLocation).toString().c_str());
bool isSingleLineInput = m_caretActive && !m_webPage->m_inputHandler->isMultilineInputMode();
WebCore::IntRect nodeBoundingBox = isSingleLineInput ? m_webPage->m_inputHandler->boundingBoxForInputField() : WebCore::IntRect();
if (!nodeBoundingBox.isEmpty()) {
nodeBoundingBox.moveBy(frameOffset);
nodeBoundingBox.intersect(clippingRectForContent);
}
SelectionLog(Platform::LogLevelInfo,
"SelectionHandler::caretPositionChanged: %s line input, single line bounding box %s%s",
isSingleLineInput ? "single" : "multi",
Platform::IntRect(nodeBoundingBox).toString().c_str(),
m_webPage->m_inputHandler->elementText().isEmpty() ? ", empty text field" : "");
m_webPage->m_client->notifyCaretChanged(caretLocation, userTouchTriggeredOnTextField, isSingleLineInput, nodeBoundingBox, m_webPage->m_inputHandler->elementText().isEmpty());
}
bool SelectionHandler::selectionContains(const WebCore::IntPoint& point)
{
ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
return m_webPage->focusedOrMainFrame()->selection()->contains(point);
}
}
}