1/*
2 * Copyright (C) 2010, 2011, 2012, 2013 Research In Motion Limited. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "config.h"
20#include "SelectionHandler.h"
21
22#include "DOMSupport.h"
23#include "Document.h"
24#include "FatFingers.h"
25#include "FloatQuad.h"
26#include "FocusController.h"
27#include "Frame.h"
28#include "FrameSelection.h"
29#include "FrameView.h"
30#include "HitTestResult.h"
31#include "InputHandler.h"
32#include "IntRect.h"
33#include "Page.h"
34#include "RenderLayer.h"
35#include "SelectionOverlay.h"
36#include "TouchEventHandler.h"
37#include "VisibleUnits.h"
38#include "WebPageClient.h"
39#include "WebPage_p.h"
40
41#include "htmlediting.h"
42
43#include <BlackBerryPlatformKeyboardEvent.h>
44#include <BlackBerryPlatformLog.h>
45#include <BlackBerryPlatformViewportAccessor.h>
46
47#include <sys/keycodes.h>
48
49// Note: This generates a lot of logs when dumping rects lists. It will seriously
50// impact performance. Do not enable this during performance tests.
51#define SHOWDEBUG_SELECTIONHANDLER 0
52#define SHOWDEBUG_SELECTIONHANDLER_TIMING 0
53
54using namespace BlackBerry::Platform;
55using namespace WebCore;
56
57#if SHOWDEBUG_SELECTIONHANDLER
58#define SelectionLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
59#else
60#define SelectionLog(severity, format, ...)
61#endif // SHOWDEBUG_SELECTIONHANDLER
62
63#if SHOWDEBUG_SELECTIONHANDLER_TIMING
64#define SelectionTimingLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
65#else
66#define SelectionTimingLog(severity, format, ...)
67#endif // SHOWDEBUG_SELECTIONHANDLER_TIMING
68
69namespace BlackBerry {
70namespace WebKit {
71
72SelectionHandler::SelectionHandler(WebPagePrivate* page)
73    : m_webPage(page)
74    , m_selectionActive(false)
75    , m_caretActive(false)
76    , m_lastUpdatedEndPointIsValid(false)
77    , m_didSuppressCaretPositionChangedNotification(false)
78{
79}
80
81SelectionHandler::~SelectionHandler()
82{
83}
84
85void SelectionHandler::cancelSelection()
86{
87    m_selectionActive = false;
88    m_lastSelectionRegion = IntRectRegion();
89
90    if (m_webPage->m_selectionOverlay)
91        m_webPage->m_selectionOverlay->hide();
92    // Notify client with empty selection to ensure the handles are removed if
93    // rendering happened prior to processing on webkit thread
94    m_webPage->m_client->notifySelectionDetailsChanged(SelectionDetails());
95
96    m_webPage->updateSelectionScrollView(0);
97
98    SelectionLog(Platform::LogLevelInfo, "SelectionHandler::cancelSelection");
99
100    if (m_webPage->m_inputHandler->isInputMode())
101        m_webPage->m_inputHandler->cancelSelection();
102    else
103        m_webPage->focusedOrMainFrame()->selection()->clear();
104}
105
106BlackBerry::Platform::String SelectionHandler::selectedText() const
107{
108    return m_webPage->focusedOrMainFrame()->editor().selectedText();
109}
110
111WebCore::IntRect SelectionHandler::clippingRectForVisibleContent() const
112{
113    // Get the containing content rect for the frame.
114    Frame* frame = m_webPage->focusedOrMainFrame();
115    WebCore::IntRect clipRect = WebCore::IntRect(WebCore::IntPoint(0, 0), frame->view()->contentsSize());
116    if (frame != m_webPage->mainFrame()) {
117        clipRect = m_webPage->getRecursiveVisibleWindowRect(frame->view(), true /* no clip to main frame window */);
118        clipRect = m_webPage->m_mainFrame->view()->windowToContents(clipRect);
119    }
120
121    // Get the input field containing box.
122    WebCore::IntRect inputBoundingBox = m_webPage->m_inputHandler->boundingBoxForInputField();
123    if (!inputBoundingBox.isEmpty()) {
124        // Adjust the bounding box to the frame offset.
125        inputBoundingBox = m_webPage->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(inputBoundingBox));
126        clipRect.intersect(inputBoundingBox);
127    }
128    return clipRect;
129}
130
131void SelectionHandler::regionForTextQuads(Vector<FloatQuad> &quadList, IntRectRegion& region, bool shouldClipToVisibleContent) const
132{
133    ASSERT(region.isEmpty());
134
135    if (!quadList.isEmpty()) {
136        FrameView* frameView = m_webPage->focusedOrMainFrame()->view();
137
138        // frameRect is in frame coordinates.
139        WebCore::IntRect frameRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
140
141        // framePosition is in main frame coordinates.
142        WebCore::IntPoint framePosition = m_webPage->frameOffset(m_webPage->focusedOrMainFrame());
143
144        // Get the visibile content rect.
145        WebCore::IntRect clippingRect = shouldClipToVisibleContent ? clippingRectForVisibleContent() : WebCore::IntRect(-1, -1, 0, 0);
146
147        // Convert the text quads into a more platform friendy
148        // IntRectRegion and adjust for subframes.
149        Platform::IntRect selectionBoundingBox;
150        std::vector<Platform::IntRect> adjustedIntRects;
151        for (unsigned i = 0; i < quadList.size(); i++) {
152            WebCore::IntRect enclosingRect = quadList[i].enclosingBoundingBox();
153            enclosingRect.intersect(frameRect);
154            enclosingRect.move(framePosition.x(), framePosition.y());
155
156            // Clip to the visible content.
157            if (clippingRect.location() != DOMSupport::InvalidPoint)
158                enclosingRect.intersect(clippingRect);
159
160            adjustedIntRects.push_back(enclosingRect);
161            selectionBoundingBox.unite(enclosingRect);
162        }
163        region = IntRectRegion(selectionBoundingBox, adjustedIntRects.size(), adjustedIntRects);
164    }
165}
166
167static VisiblePosition visiblePositionForPointIgnoringClipping(const Frame& frame, const WebCore::IntPoint& framePoint)
168{
169    // Frame::visiblePositionAtPoint hard-codes ignoreClipping=false in the
170    // call to hitTestResultAtPoint. This has a bug where some pages (such as
171    // metafilter) will return the wrong VisiblePosition for points that are
172    // outside the visible rect. To work around the bug, this is a copy of
173    // visiblePositionAtPoint which which passes ignoreClipping=true.
174    // See RIM Bug #4315.
175    HitTestResult result = frame.eventHandler()->hitTestResultAtPoint(framePoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping);
176
177    Node* node = result.innerNode();
178    if (!node || node->document() != frame.document())
179        return VisiblePosition();
180
181    RenderObject* renderer = node->renderer();
182    if (!renderer)
183        return VisiblePosition();
184
185    VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint());
186    if (visiblePos.isNull())
187        visiblePos = VisiblePosition(Position(createLegacyEditingPosition(node, 0)));
188
189    return visiblePos;
190}
191
192static unsigned directionOfPointRelativeToRect(const WebCore::IntPoint& point, const WebCore::IntRect& rect, const bool useTopPadding = true, const bool useBottomPadding = true)
193{
194    ASSERT(!rect.contains(point));
195
196    // Padding to prevent accidental trigger of up/down when intending to do horizontal movement.
197    const int verticalPadding = 5;
198
199    // Do height movement check first but add padding. We may be off on both x & y axis and only
200    // want to move in one direction at a time.
201    if (point.y() - (useTopPadding ? verticalPadding : 0) < rect.y())
202        return KEYCODE_UP;
203    if (point.y() > rect.maxY() + (useBottomPadding ? verticalPadding : 0))
204        return KEYCODE_DOWN;
205    if (point.x() < rect.location().x())
206        return KEYCODE_LEFT;
207    if (point.x() > rect.maxX())
208        return KEYCODE_RIGHT;
209
210    return 0;
211}
212
213bool SelectionHandler::shouldUpdateSelectionOrCaretForPoint(const WebCore::IntPoint& point, const WebCore::IntRect& caretRect, bool startCaret) const
214{
215    ASSERT(m_webPage->m_inputHandler->isInputMode());
216
217    // If the point isn't valid don't block change as it is not actually changing.
218    if (point == DOMSupport::InvalidPoint)
219        return true;
220
221    VisibleSelection currentSelection = m_webPage->focusedOrMainFrame()->selection()->selection();
222
223    // If the input field is single line or we are on the first or last
224    // line of a multiline input field only horizontal movement is supported.
225    bool aboveCaret = point.y() < caretRect.y();
226    bool belowCaret = point.y() >= caretRect.maxY();
227
228    SelectionLog(Platform::LogLevelInfo,
229        "SelectionHandler::shouldUpdateSelectionOrCaretForPoint multiline = %s above = %s below = %s first line = %s last line = %s start = %s",
230        m_webPage->m_inputHandler->isMultilineInputMode() ? "true" : "false",
231        aboveCaret ? "true" : "false",
232        belowCaret ? "true" : "false",
233        inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) ? "true" : "false",
234        inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) ? "true" : "false",
235        startCaret ? "true" : "false");
236
237    if (!m_webPage->m_inputHandler->isMultilineInputMode() && (aboveCaret || belowCaret))
238        return false;
239    if (startCaret && inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) && aboveCaret)
240        return false;
241    if (!startCaret && inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) && belowCaret)
242        return false;
243
244    return true;
245}
246
247void SelectionHandler::setCaretPosition(const WebCore::IntPoint& position)
248{
249    if (!m_webPage->m_inputHandler->isInputMode() || !m_webPage->focusedOrMainFrame()->document()->focusedElement())
250        return;
251
252    m_caretActive = true;
253
254    SelectionLog(Platform::LogLevelInfo,
255        "SelectionHandler::setCaretPosition requested point %s",
256        Platform::IntPoint(position).toString().c_str());
257
258    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
259    FrameSelection* controller = focusedFrame->selection();
260    WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, position);
261    WebCore::IntRect currentCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
262
263    if (relativePoint == DOMSupport::InvalidPoint || !shouldUpdateSelectionOrCaretForPoint(relativePoint, currentCaretRect)) {
264        selectionPositionChanged(true /* forceUpdateWithoutChange */);
265        return;
266    }
267
268    WebCore::IntRect nodeOutlineBounds(m_webPage->m_inputHandler->boundingBoxForInputField());
269    if (!nodeOutlineBounds.isEmpty() && !nodeOutlineBounds.contains(relativePoint)) {
270        if (unsigned character = directionOfPointRelativeToRect(relativePoint, currentCaretRect))
271            m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character));
272
273        // Send the selection changed in case this does not trigger a selection change to
274        // ensure the caret position is accurate. This may be a duplicate event.
275        selectionPositionChanged(true /* forceUpdateWithoutChange */);
276        return;
277    }
278
279    VisibleSelection newSelection(focusedFrame->visiblePositionForPoint(relativePoint));
280    if (controller->selection() == newSelection) {
281        selectionPositionChanged(true /* forceUpdateWithoutChange */);
282        return;
283    }
284
285    controller->setSelection(newSelection);
286
287    SelectionLog(Platform::LogLevelInfo, "SelectionHandler::setCaretPosition point valid, cursor updated");
288}
289
290void SelectionHandler::inputHandlerDidFinishProcessingChange()
291{
292    if (m_didSuppressCaretPositionChangedNotification)
293        notifyCaretPositionChangedIfNeeded(false);
294}
295
296// This function makes sure we are not reducing the selection to a caret selection.
297static bool shouldExtendSelectionInDirection(const VisibleSelection& selection, unsigned character)
298{
299    FrameSelection tempSelection;
300    tempSelection.setSelection(selection);
301    switch (character) {
302    case KEYCODE_LEFT:
303        tempSelection.modify(FrameSelection::AlterationExtend, DirectionLeft, CharacterGranularity);
304        break;
305    case KEYCODE_RIGHT:
306        tempSelection.modify(FrameSelection::AlterationExtend, DirectionRight, CharacterGranularity);
307        break;
308    case KEYCODE_UP:
309        tempSelection.modify(FrameSelection::AlterationExtend, DirectionBackward, LineGranularity);
310        break;
311    case KEYCODE_DOWN:
312        tempSelection.modify(FrameSelection::AlterationExtend, DirectionForward, LineGranularity);
313        break;
314    default:
315        break;
316    }
317
318    if ((character == KEYCODE_LEFT || character == KEYCODE_RIGHT)
319        && (!inSameLine(selection.visibleStart(), tempSelection.selection().visibleStart())
320            || !inSameLine(selection.visibleEnd(), tempSelection.selection().visibleEnd())))
321        return false;
322
323    return tempSelection.selection().selectionType() == VisibleSelection::RangeSelection;
324}
325
326static int clamp(const int min, const int value, const int max)
327{
328    return value < min ? min : std::min(value, max);
329}
330
331static VisiblePosition directionalVisiblePositionAtExtentOfBox(Frame* frame, const WebCore::IntRect& boundingBox, unsigned direction, const WebCore::IntPoint& basePoint)
332{
333    ASSERT(frame);
334
335    if (!frame)
336        return VisiblePosition();
337
338    switch (direction) {
339    case KEYCODE_LEFT:
340        // Extend x to start and clamp y to the edge of bounding box.
341        return frame->visiblePositionForPoint(WebCore::IntPoint(boundingBox.x(), clamp(boundingBox.y(), basePoint.y(), boundingBox.maxY())));
342    case KEYCODE_RIGHT:
343        // Extend x to end and clamp y to the edge of bounding box.
344        return frame->visiblePositionForPoint(WebCore::IntPoint(boundingBox.maxX(), clamp(boundingBox.y(), basePoint.y(), boundingBox.maxY())));
345    case KEYCODE_UP:
346        // Extend y to top and clamp x to the edge of bounding box.
347        return frame->visiblePositionForPoint(WebCore::IntPoint(clamp(boundingBox.x(), basePoint.x(), boundingBox.maxX()), boundingBox.y()));
348    case KEYCODE_DOWN:
349        // Extend y to bottom and clamp x to the edge of bounding box.
350        return frame->visiblePositionForPoint(WebCore::IntPoint(clamp(boundingBox.x(), basePoint.x(), boundingBox.maxX()), boundingBox.maxY()));
351    default:
352        break;
353    }
354
355    return frame->visiblePositionForPoint(WebCore::IntPoint(basePoint.x(), basePoint.y()));
356}
357
358static bool pointIsOutsideOfBoundingBoxInDirection(unsigned direction, const WebCore::IntPoint& selectionPoint, const WebCore::IntRect& boundingBox)
359{
360    if ((direction == KEYCODE_LEFT && selectionPoint.x() < boundingBox.x())
361        || (direction == KEYCODE_UP && selectionPoint.y() < boundingBox.y())
362        || (direction == KEYCODE_RIGHT && selectionPoint.x() > boundingBox.maxX())
363        || (direction == KEYCODE_DOWN && selectionPoint.y() > boundingBox.maxY()))
364        return true;
365
366    return false;
367}
368
369unsigned SelectionHandler::extendSelectionToFieldBoundary(bool isStartHandle, const WebCore::IntPoint& selectionPoint, VisibleSelection& newSelection)
370{
371    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
372    if (!focusedFrame->document()->focusedElement() || !focusedFrame->document()->focusedElement()->renderer())
373        return 0;
374
375    VisibleSelection activeSelection = focusedFrame->selection()->selection();
376
377    WebCore::IntRect caretRect = isStartHandle ? activeSelection.visibleStart().absoluteCaretBounds() : activeSelection.visibleEnd().absoluteCaretBounds();
378
379    WebCore::IntRect nodeBoundingBox = focusedFrame->document()->focusedElement()->renderer()->absoluteBoundingBoxRect();
380    nodeBoundingBox.inflate(-1);
381
382    // Start handle is outside of the field. Treat it as the changed handle and move
383    // relative to the start caret rect.
384    unsigned character = directionOfPointRelativeToRect(selectionPoint, caretRect, isStartHandle /* useTopPadding */, !isStartHandle /* useBottomPadding */);
385
386    // Prevent incorrect movement, handles can only extend the selection this way
387    // to prevent inversion of the handles.
388    if ((isStartHandle && (character == KEYCODE_RIGHT || character == KEYCODE_DOWN))
389        || (!isStartHandle && (character == KEYCODE_LEFT || character == KEYCODE_UP)))
390        character = 0;
391
392    VisiblePosition newVisiblePosition = isStartHandle ? activeSelection.extent() : activeSelection.base();
393    // Extend the selection to the bounds of the box before doing incremental scroll if the point is outside the node.
394    // Don't extend selection and handle the character at the same time.
395    if (pointIsOutsideOfBoundingBoxInDirection(character, selectionPoint, nodeBoundingBox))
396        newVisiblePosition = directionalVisiblePositionAtExtentOfBox(focusedFrame, nodeBoundingBox, character, selectionPoint);
397
398    if (isStartHandle)
399        newSelection = VisibleSelection(newVisiblePosition, newSelection.extent(), true /* isDirectional */);
400    else
401        newSelection = VisibleSelection(newSelection.base(), newVisiblePosition, true /* isDirectional */);
402
403    // If no selection will be changed, return the character to extend using navigation.
404    if (activeSelection == newSelection)
405        return character;
406
407    // Selection has been updated.
408    return 0;
409}
410
411// Returns true if handled.
412bool SelectionHandler::updateOrHandleInputSelection(VisibleSelection& newSelection, const WebCore::IntPoint& relativeStart, const WebCore::IntPoint& relativeEnd)
413{
414    ASSERT(m_webPage->m_inputHandler->isInputMode());
415
416    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
417    Node* focusedNode = focusedFrame->document()->focusedElement();
418    if (!focusedNode || !focusedNode->renderer())
419        return false;
420
421    FrameSelection* controller = focusedFrame->selection();
422
423    WebCore::IntRect currentStartCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
424    WebCore::IntRect currentEndCaretRect = controller->selection().visibleEnd().absoluteCaretBounds();
425
426    // Check if the handle movement is valid.
427    if (!shouldUpdateSelectionOrCaretForPoint(relativeStart, currentStartCaretRect, true /* startCaret */)
428        || !shouldUpdateSelectionOrCaretForPoint(relativeEnd, currentEndCaretRect, false /* startCaret */)) {
429        selectionPositionChanged(true /* forceUpdateWithoutChange */);
430        return true;
431    }
432
433    WebCore::IntRect nodeBoundingBox = focusedNode->renderer()->absoluteBoundingBoxRect();
434
435    // Only do special handling if one handle is outside of the node.
436    bool startIsOutsideOfField = relativeStart != DOMSupport::InvalidPoint && !nodeBoundingBox.contains(relativeStart);
437    bool endIsOutsideOfField = relativeEnd != DOMSupport::InvalidPoint && !nodeBoundingBox.contains(relativeEnd);
438    if (startIsOutsideOfField && endIsOutsideOfField)
439        return false;
440
441    unsigned character = 0;
442    bool needToInvertDirection = false;
443    if (startIsOutsideOfField) {
444        character = extendSelectionToFieldBoundary(true /* isStartHandle */, relativeStart, newSelection);
445        if (character && controller->selection().isBaseFirst()) {
446            // Invert the selection so that the cursor point is at the beginning.
447            controller->setSelection(VisibleSelection(controller->selection().end(), controller->selection().start(), true /* isDirectional */));
448            needToInvertDirection = true;
449        }
450    } else if (endIsOutsideOfField) {
451        character = extendSelectionToFieldBoundary(false /* isStartHandle */, relativeEnd, newSelection);
452        if (character && !controller->selection().isBaseFirst()) {
453            // Reset the selection so that the end is the edit point.
454            controller->setSelection(VisibleSelection(controller->selection().start(), controller->selection().end(), true /* isDirectional */));
455        }
456    }
457
458    if (!character)
459        return false;
460
461    SelectionLog(Platform::LogLevelInfo,
462        "SelectionHandler::updateOrHandleInputSelection making selection change attempt using key event %d",
463        character);
464
465    if (shouldExtendSelectionInDirection(controller->selection(), character))
466        m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character, Platform::KeyboardEvent::KeyDown, KEYMOD_SHIFT));
467
468    if (needToInvertDirection)
469        controller->setSelection(VisibleSelection(controller->selection().extent(), controller->selection().base(), true /* isDirectional */));
470
471    // Send the selection changed in case this does not trigger a selection change to
472    // ensure the caret position is accurate. This may be a duplicate event.
473    selectionPositionChanged(true /* forceUpdateWithoutChange */);
474    return true;
475}
476
477void SelectionHandler::setSelection(WebCore::IntPoint start, WebCore::IntPoint end)
478{
479    m_selectionActive = true;
480
481    ASSERT(m_webPage);
482    ASSERT(m_webPage->focusedOrMainFrame());
483    ASSERT(m_webPage->focusedOrMainFrame()->selection());
484
485    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
486    FrameSelection* controller = focusedFrame->selection();
487
488#if SHOWDEBUG_SELECTIONHANDLER_TIMING
489    m_timer.start();
490#endif
491
492    SelectionLog(Platform::LogLevelInfo,
493        "SelectionHandler::setSelection adjusted points %s, %s",
494        Platform::IntPoint(start).toString().c_str(),
495        Platform::IntPoint(end).toString().c_str());
496
497    // Note that IntPoint(-1, -1) is being our sentinel so far for
498    // clipped out selection starting or ending location.
499    bool startIsValid = start != DOMSupport::InvalidPoint;
500    m_lastUpdatedEndPointIsValid = end != DOMSupport::InvalidPoint;
501
502    // At least one of the locations must be valid.
503    ASSERT(startIsValid || m_lastUpdatedEndPointIsValid);
504
505    if (m_webPage->m_inputHandler->isInputMode() && !m_webPage->m_inputHandler->isMultilineInputMode()) {
506        WebCore::IntRect caret(startCaretViewportRect(m_webPage->frameOffset(focusedFrame)));
507        if (!caret.isEmpty()) {
508            int centerOfCaretY = caret.center().y();
509            if (startIsValid)
510                start.setY(centerOfCaretY);
511            if (m_lastUpdatedEndPointIsValid)
512                end.setY(centerOfCaretY);
513        }
514    }
515
516    WebCore::IntPoint relativeStart = start;
517    WebCore::IntPoint relativeEnd = end;
518
519    // Initialize the new start and end of our selection at the current positions.
520    VisiblePosition newStart = controller->selection().visibleStart();
521    VisiblePosition newEnd = controller->selection().visibleEnd();
522
523    // We don't return early in the following, so that we can do input field scrolling if the
524    // handle is outside the bounds of the field. This can be extended to handle sub-region
525    // scrolling as well
526    if (startIsValid) {
527        relativeStart = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, start);
528        VisiblePosition base = visiblePositionForPointIgnoringClipping(*focusedFrame, clipPointToVisibleContainer(start));
529        if (base.isNotNull())
530            newStart = base;
531    }
532
533    if (m_lastUpdatedEndPointIsValid) {
534        relativeEnd = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, end);
535        VisiblePosition extent = visiblePositionForPointIgnoringClipping(*focusedFrame, clipPointToVisibleContainer(end));
536        if (extent.isNotNull())
537            newEnd = extent;
538    }
539
540    VisibleSelection newSelection(newStart, newEnd, true /* isDirectional */);
541
542    if (!controller->selection().isRange())
543        m_webPage->updateSelectionScrollView(newSelection.visibleEnd().deepEquivalent().anchorNode());
544
545    if (m_webPage->m_inputHandler->isInputMode()) {
546        if (updateOrHandleInputSelection(newSelection, relativeStart, relativeEnd))
547            return;
548    }
549
550    if (controller->selection() == newSelection) {
551        selectionPositionChanged(true /* forceUpdateWithoutChange */);
552        return;
553    }
554
555    // If the selection size is reduce to less than a character, selection type becomes
556    // Caret. As long as it is still a range, it's a valid selection. Selection cannot
557    // be cancelled through this function.
558    Vector<FloatQuad> quads;
559    DOMSupport::visibleTextQuads(newSelection, quads);
560
561    IntRectRegion unclippedRegion;
562    regionForTextQuads(quads, unclippedRegion, false /* shouldClipToVisibleContent */);
563
564    if (unclippedRegion.isEmpty()) {
565        // Requested selection results in an empty selection, skip this change.
566        selectionPositionChanged(true /* forceUpdateWithoutChange */);
567
568        SelectionLog(Platform::LogLevelWarn, "SelectionHandler::setSelection selection points invalid, selection not updated.");
569        return;
570    }
571
572    // Check if the handles reversed position.
573    if (m_selectionActive && !newSelection.isBaseFirst()) {
574        m_webPage->m_client->notifySelectionHandlesReversed();
575        newSelection = VisibleSelection(newSelection.extent(), newSelection.base());
576    }
577
578    controller->setSelection(newSelection);
579    SelectionLog(Platform::LogLevelInfo, "SelectionHandler::setSelection selection points valid, selection updated.");
580}
581
582// FIXME re-use this in context. Must be updated to include an option to return the href.
583// This function should be moved to a new unit file. Names suggetions include DOMQueries
584// and NodeTypes. Functions currently in InputHandler.cpp, SelectionHandler.cpp and WebPage.cpp
585// can all be moved in.
586static Node* enclosingLinkEventParentForNode(Node* node)
587{
588    if (!node)
589        return 0;
590
591    Node* linkNode = node->enclosingLinkEventParentOrSelf();
592    return linkNode && linkNode->isLink() ? linkNode : 0;
593}
594
595TextGranularity textGranularityFromSelectionExpansionType(SelectionExpansionType selectionExpansionType)
596{
597    TextGranularity granularity;
598    switch (selectionExpansionType) {
599    case Word:
600    default:
601        granularity = WordGranularity;
602        break;
603    case Sentence:
604        granularity = SentenceGranularity;
605        break;
606    case Paragraph:
607        granularity = ParagraphGranularity;
608        break;
609    }
610    return granularity;
611}
612
613
614bool SelectionHandler::selectNodeIfFatFingersResultIsLink(FatFingersResult fatFingersResult)
615{
616    if (!fatFingersResult.isValid())
617        return false;
618    Node* targetNode = fatFingersResult.node(FatFingersResult::ShadowContentNotAllowed);
619    ASSERT(targetNode);
620    // If the node at the point is a link, focus on the entire link, not a word.
621    if (Node* link = enclosingLinkEventParentForNode(targetNode)) {
622        Element* element = fatFingersResult.nodeAsElementIfApplicable();
623        if (!element)
624            return false;
625        m_animationHighlightColor = element->renderStyle()->initialTapHighlightColor();
626
627        selectObject(link);
628        // If selected object is a link, no need to wait for further expansion.
629        m_webPage->m_client->stopExpandingSelection();
630        return true;
631    }
632    return false;
633}
634
635WebCore::IntRect SelectionHandler::startCaretViewportRect(const WebCore::IntPoint& frameOffset) const
636{
637    WebCore::IntRect caretRect;
638    Frame* frame = m_webPage->focusedOrMainFrame();
639    if (!frame)
640        return caretRect;
641
642    if (frame->selection()->selectionType() != VisibleSelection::NoSelection) {
643        caretRect = frame->selection()->selection().visibleStart().absoluteCaretBounds();
644        caretRect.moveBy(frameOffset);
645    }
646
647    return caretRect;
648}
649
650void SelectionHandler::selectAtPoint(const WebCore::IntPoint& location, SelectionExpansionType selectionExpansionType)
651{
652    if (selectionExpansionType == Word) {
653        m_animationOverlayStartPos = VisiblePosition();
654        m_animationOverlayEndPos = VisiblePosition();
655        m_currentAnimationOverlayRegion = IntRectRegion();
656        m_nextAnimationOverlayRegion = IntRectRegion();
657        m_selectionSubframeViewportRect = WebCore::IntRect();
658    }
659
660    // If point is invalid trigger selection based expansion.
661    if (location == DOMSupport::InvalidPoint) {
662        selectObject(WordGranularity);
663        return;
664    }
665
666    WebCore::IntPoint targetPosition;
667
668    FatFingersResult fatFingersResult = m_webPage->m_touchEventHandler->lastFatFingersResult();
669    if (selectNodeIfFatFingersResultIsLink(fatFingersResult))
670        return;
671    if (!fatFingersResult.resultMatches(location, FatFingers::Text) || !fatFingersResult.positionWasAdjusted() || !fatFingersResult.nodeAsElementIfApplicable()) {
672        // Cache text result for later use.
673        fatFingersResult = FatFingers(m_webPage, location, FatFingers::Text).findBestPoint();
674        m_webPage->m_touchEventHandler->cacheTextResult(fatFingersResult);
675    }
676
677    if (!fatFingersResult.positionWasAdjusted()) {
678        if (isSelectionActive())
679            cancelSelection();
680        m_webPage->m_client->notifySelectionDetailsChanged(SelectionDetails());
681        m_webPage->m_touchEventHandler->sendClickAtFatFingersPoint();
682        return;
683    }
684
685    targetPosition = fatFingersResult.adjustedPosition();
686    if (selectNodeIfFatFingersResultIsLink(fatFingersResult))
687        return;
688
689    selectObject(targetPosition, textGranularityFromSelectionExpansionType(selectionExpansionType));
690}
691
692static bool isInvalidParagraph(const VisiblePosition& pos)
693{
694    return endOfParagraph(pos).isNull() || pos == endOfParagraph(pos);
695}
696
697void SelectionHandler::selectNextParagraph()
698{
699    FrameSelection* controller = m_webPage->focusedOrMainFrame()->selection();
700
701    VisiblePosition startPos = VisiblePosition(controller->start(), controller->affinity());
702    if (isStartOfLine(startPos) && isEndOfDocument(startPos))
703        startPos = startPos.previous(CannotCrossEditingBoundary);
704
705    // Find next paragraph end position.
706    VisiblePosition endPos(controller->end(), controller->affinity()); // endPos here indicates the end of current paragraph
707    endPos = endPos.next(CannotCrossEditingBoundary); // find the start of next paragraph
708    while (!isEndOfDocument(endPos) && endPos.isNotNull() && isInvalidParagraph(endPos))
709        endPos = endPos.next(CannotCrossEditingBoundary); // go to next position
710    endPos = endOfParagraph(endPos); // find the end of paragraph
711
712    // Set selection if the paragraph is covered by overlay and endPos is not null.
713    if (m_currentAnimationOverlayRegion.extents().bottom() >= endPos.absoluteCaretBounds().maxY() && endPos.isNotNull()) {
714        VisibleSelection selection = VisibleSelection(startPos, endPos);
715        selection.setAffinity(controller->affinity());
716        controller->setSelection(selection);
717
718        // Stop expansion if reaching the end of page.
719        if (isEndOfDocument(endPos))
720            m_webPage->m_client->stopExpandingSelection();
721    }
722}
723
724void SelectionHandler::drawAnimationOverlay(IntRectRegion overlayRegion, bool isExpandingOverlayAtConstantRate, bool isStartOfSelection)
725{
726    if (isExpandingOverlayAtConstantRate) {
727        // When overlay expands at a constant rate, the current overlay height increases
728        // m_overlayExpansionHeight each time and the width is always same as next overlay region.
729        WebCore::IntRect currentOverlayRect = m_currentAnimationOverlayRegion.extents();
730        WebCore::IntRect nextOverlayRect = m_nextAnimationOverlayRegion.extents();
731        WebCore::IntRect overlayRect(WebCore::IntRect(nextOverlayRect.location(), WebCore::IntSize(nextOverlayRect.width(), currentOverlayRect.height() + m_overlayExpansionHeight)));
732        overlayRegion = IntRectRegion(overlayRect);
733    }
734
735    m_webPage->m_selectionHighlight->draw(overlayRegion,
736        m_animationHighlightColor.red(), m_animationHighlightColor.green(), m_animationHighlightColor.blue(), m_animationHighlightColor.alpha(),
737        false /* do not hide after scroll */,
738        isStartOfSelection);
739    m_currentAnimationOverlayRegion = overlayRegion;
740}
741
742IntRectRegion SelectionHandler::regionForSelectionQuads(VisibleSelection selection)
743{
744    Vector<FloatQuad> quads;
745    DOMSupport::visibleTextQuads(selection, quads);
746    IntRectRegion region;
747    regionForTextQuads(quads, region);
748    return region;
749}
750
751bool SelectionHandler::findNextAnimationOverlayRegion()
752{
753    // If overlay is at the end of document, stop overlay expansion.
754    if (isEndOfDocument(m_animationOverlayEndPos) || m_animationOverlayEndPos.isNull())
755        return false;
756
757    m_animationOverlayEndPos = m_animationOverlayEndPos.next(CannotCrossEditingBoundary);
758    while (!isEndOfDocument(m_animationOverlayEndPos) && m_animationOverlayEndPos.isNotNull() && isInvalidParagraph(m_animationOverlayEndPos))
759        m_animationOverlayEndPos = m_animationOverlayEndPos.next(CannotCrossEditingBoundary); // go to next position
760    m_animationOverlayEndPos = endOfParagraph(m_animationOverlayEndPos); // find end of paragraph
761
762    VisibleSelection selection(m_animationOverlayStartPos, m_animationOverlayEndPos);
763    m_nextAnimationOverlayRegion = regionForSelectionQuads(selection);
764    return true;
765}
766
767void SelectionHandler::expandSelection(bool isScrollStarted)
768{
769    if (m_currentAnimationOverlayRegion.isEmpty() || m_nextAnimationOverlayRegion.isEmpty())
770        return;
771    WebCore::IntPoint nextOverlayBottomRightPoint = WebCore::IntPoint(m_currentAnimationOverlayRegion.extents().bottomRight()) + WebCore::IntPoint(0, m_overlayExpansionHeight);
772    if (nextOverlayBottomRightPoint.y() > m_nextAnimationOverlayRegion.extents().bottom())
773        // Find next overlay region so that we can update overlay region's width while expanding.
774        if (!findNextAnimationOverlayRegion()) {
775            drawAnimationOverlay(m_nextAnimationOverlayRegion, false);
776            selectNextParagraph();
777            return;
778        }
779
780    // Draw overlay if the position is in the viewport and is not null.
781    // Otherwise, start scrolling if it hasn't started.
782    if (ensureSelectedTextVisible(nextOverlayBottomRightPoint, false /* do not scroll */) && m_animationOverlayEndPos.isNotNull())
783        drawAnimationOverlay(IntRectRegion(), true /* isExpandingOverlayAtConstantRate */);
784    else if (!isScrollStarted) {
785        m_webPage->m_client->startSelectionScroll();
786        return;
787    }
788
789    selectNextParagraph();
790}
791
792bool SelectionHandler::ensureSelectedTextVisible(const WebCore::IntPoint& point, bool scrollIfNeeded)
793{
794    WebCore::IntRect viewportRect = selectionViewportRect();
795    if (!scrollIfNeeded)
796        // If reaching the bottom of content, ignore scroll margin so the text on the bottom can be selected.
797        return viewportRect.maxY() >= m_webPage->contentsSize().height() ? viewportRect.maxY() >= point.y() : viewportRect.maxY() >= point.y() + m_scrollMargin.height();
798
799    // Scroll position adjustment here is based on main frame. If selecting in a subframe, don't do animation.
800    if (!m_selectionSubframeViewportRect.isEmpty())
801        return false;
802
803    WebCore::IntRect endLocation = m_animationOverlayEndPos.absoluteCaretBounds();
804
805    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
806    Frame* mainFrame = m_webPage->mainFrame();
807    // If we are selecting within an iframe, translate coordinates to main frame.
808    if (focusedFrame && focusedFrame->view() && mainFrame && mainFrame->view() && focusedFrame != mainFrame)
809        endLocation = mainFrame->view()->windowToContents(focusedFrame->view()->contentsToWindow(endLocation));
810
811    Node* anchorNode = m_animationOverlayEndPos.deepEquivalent().anchorNode();
812    if (!anchorNode || !anchorNode->renderer())
813        return false;
814
815    RenderLayer* layer = anchorNode->renderer()->enclosingLayer();
816    if (!layer)
817        return false;
818
819    endLocation.inflateX(m_scrollMargin.width());
820    endLocation.inflateY(m_scrollMargin.height());
821
822    WebCore::IntRect revealRect(layer->getRectToExpose(viewportRect, endLocation, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded));
823    revealRect.setX(std::min(std::max(revealRect.x(), 0), m_webPage->maximumScrollPosition().x()));
824    revealRect.setY(std::min(std::max(revealRect.y(), 0), m_webPage->maximumScrollPosition().y()));
825
826    // Animate scroll position to revealRect.
827    m_webPage->animateToScaleAndDocumentScrollPosition(m_webPage->currentScale() /* Don't zoom */, WebCore::FloatPoint(revealRect.x(), revealRect.y()));
828    return true;
829}
830
831WebCore::IntRect SelectionHandler::selectionViewportRect() const
832{
833    if (m_selectionSubframeViewportRect.isEmpty())
834        return WebCore::IntRect(m_webPage->scrollPosition(), m_selectionViewportSize);
835    return m_selectionSubframeViewportRect;
836}
837
838void SelectionHandler::setParagraphExpansionScrollMargin(const WebCore::IntSize& scrollMargin)
839{
840    m_scrollMargin.setWidth(scrollMargin.width());
841    m_scrollMargin.setHeight(scrollMargin.height());
842}
843
844bool SelectionHandler::expandSelectionToGranularity(Frame* frame, VisibleSelection selection, TextGranularity granularity, bool isInputMode)
845{
846    ASSERT(frame);
847    ASSERT(frame->selection());
848
849    if (!(selection.start().anchorNode() && selection.start().anchorNode()->isTextNode()))
850        return false;
851
852    if (granularity == WordGranularity)
853        selection = DOMSupport::visibleSelectionForClosestActualWordStart(selection);
854
855    selection.expandUsingGranularity(granularity);
856    selection.setAffinity(frame->selection()->affinity());
857
858    if (isInputMode && !frame->selection()->shouldChangeSelection(selection))
859        return false;
860
861    m_animationOverlayStartPos = selection.visibleStart();
862    m_animationOverlayEndPos = selection.visibleEnd();
863
864    if (granularity == WordGranularity) {
865        m_webPage->updateSelectionScrollView(selection.visibleEnd().deepEquivalent().anchorNode());
866
867        Element* element = m_animationOverlayStartPos.deepEquivalent().element();
868        if (!element)
869            return false;
870        m_animationHighlightColor = element->renderStyle()->initialTapHighlightColor();
871    }
872
873    ensureSelectedTextVisible(WebCore::IntPoint(), true /* scroll if needed */);
874    drawAnimationOverlay(regionForSelectionQuads(selection), false /* isExpandingOverlayAtConstantRate */, granularity == WordGranularity /* isStartOfSelection */);
875    frame->selection()->setSelection(selection);
876    if (granularity == ParagraphGranularity)
877        findNextAnimationOverlayRegion();
878    return true;
879}
880
881void SelectionHandler::selectObject(const WebCore::IntPoint& location, TextGranularity granularity)
882{
883    ASSERT(location.x() >= 0 && location.y() >= 0);
884    ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
885    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
886
887    SelectionLog(Platform::LogLevelInfo,
888        "SelectionHandler::selectObject adjusted points %s",
889        Platform::IntPoint(location).toString().c_str());
890
891    WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, location);
892
893    VisiblePosition pointLocation(focusedFrame->visiblePositionForPoint(relativePoint));
894    VisibleSelection selection = VisibleSelection(pointLocation, pointLocation);
895
896    // Move focus to the new node if we're not selecting in old input field.
897    if (!m_webPage->m_inputHandler->boundingBoxForInputField().contains(relativePoint)) {
898        Node* anchorNode = selection.start().anchorNode();
899        if (!anchorNode || anchorNode->isElementNode())
900            m_webPage->m_page->focusController()->setFocusedElement(toElement(anchorNode), focusedFrame);
901    }
902
903    m_selectionActive = expandSelectionToGranularity(focusedFrame, selection, granularity, m_webPage->m_inputHandler->isInputMode());
904}
905
906void SelectionHandler::selectObject(TextGranularity granularity)
907{
908    ASSERT(m_webPage && m_webPage->m_inputHandler);
909    // Using caret location, must be inside an input field.
910    if (!m_webPage->m_inputHandler->isInputMode())
911        return;
912
913    ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
914    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
915
916    SelectionLog(Platform::LogLevelInfo, "SelectionHandler::selectObject using current selection");
917
918    ASSERT(focusedFrame->selection()->selectionType() != VisibleSelection::NoSelection);
919
920    // Use the current selection as the selection point.
921    VisibleSelection selectionOrigin = focusedFrame->selection()->selection();
922
923    // If this is the end of the input field, make sure we select the last word.
924    if (m_webPage->m_inputHandler->isCaretAtEndOfText())
925        selectionOrigin = previousWordPosition(selectionOrigin.start());
926
927    m_selectionActive = expandSelectionToGranularity(focusedFrame, selectionOrigin, granularity, true /* isInputMode */);
928}
929
930void SelectionHandler::selectObject(Node* node)
931{
932    if (!node)
933        return;
934
935    // Clear input focus if we're not selecting text there.
936    if (node != m_webPage->m_inputHandler->currentFocusElement().get())
937        m_webPage->clearFocusNode();
938
939    m_selectionActive = true;
940
941    ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
942    Frame* focusedFrame = m_webPage->focusedOrMainFrame();
943
944    SelectionLog(Platform::LogLevelInfo, "SelectionHandler::selectNode");
945
946    VisibleSelection selection = VisibleSelection::selectionFromContentsOfNode(node);
947    drawAnimationOverlay(regionForSelectionQuads(selection), false /* isExpandingOverlayAtConstantRate */, true /* isStartOfSelection */);
948    focusedFrame->selection()->setSelection(selection);
949    m_webPage->updateSelectionScrollView(node);
950}
951
952static TextDirection directionOfEnclosingBlock(FrameSelection* selection)
953{
954    Node* enclosingBlockNode = enclosingBlock(selection->selection().extent().deprecatedNode());
955    if (!enclosingBlockNode)
956        return LTR;
957
958    if (RenderObject* renderer = enclosingBlockNode->renderer())
959        return renderer->style()->direction();
960
961    return LTR;
962}
963
964// Returns > 0 if p1 is "closer" to referencePoint, < 0 if p2 is "closer", 0 if they are equidistant.
965// Because text is usually arranged in horizontal rows, distance is measured along the y-axis, with x-axis used only to break ties.
966// If rightGravity is true, the right-most x-coordinate is chosen, otherwise teh left-most coordinate is chosen.
967static inline int comparePointsToReferencePoint(const WebCore::IntPoint& p1, const WebCore::IntPoint& p2, const WebCore::IntPoint& referencePoint, bool rightGravity)
968{
969    int dy1 = abs(referencePoint.y() - p1.y());
970    int dy2 = abs(referencePoint.y() - p2.y());
971    if (dy1 != dy2)
972        return dy2 - dy1;
973
974    // Same y-coordinate, choose the farthest right (or left) point.
975    if (p1.x() == p2.x())
976        return 0;
977
978    if (p1.x() > p2.x())
979        return rightGravity ? 1 : -1;
980
981    return rightGravity ? -1 : 1;
982}
983
984// NOTE/FIXME: Due to r77286, we are getting off-by-one results in the IntRect class counterpart implementation of the
985//             methods below. As done in r89803, r77928 and a few others, lets use local method to fix it.
986//             We should keep our eyes very open on it, since it can affect BackingStore very badly.
987static WebCore::IntPoint minXMinYCorner(const WebCore::IntRect& rect) { return rect.location(); } // typically topLeft
988static WebCore::IntPoint maxXMinYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x() + rect.width() - 1, rect.y()); } // typically topRight
989static WebCore::IntPoint minXMaxYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x(), rect.y() + rect.height() - 1); } // typically bottomLeft
990static WebCore::IntPoint maxXMaxYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1); } // typically bottomRight
991
992// The caret is a one-pixel wide line down either the right or left edge of a
993// rect, depending on the text direction.
994static inline bool caretIsOnLeft(bool isStartCaret, bool isRTL)
995{
996    if (isStartCaret)
997        return !isRTL;
998
999    return isRTL;
1000}
1001
1002static inline WebCore::IntPoint caretLocationForRect(const WebCore::IntRect& rect, bool isStartCaret, bool isRTL)
1003{
1004    return caretIsOnLeft(isStartCaret, isRTL) ? minXMinYCorner(rect) : maxXMinYCorner(rect);
1005}
1006
1007static inline WebCore::IntPoint caretComparisonPointForRect(const WebCore::IntRect& rect, bool isStartCaret, bool isRTL)
1008{
1009    if (isStartCaret)
1010        return caretIsOnLeft(isStartCaret, isRTL) ? minXMinYCorner(rect) : maxXMinYCorner(rect);
1011
1012    return caretIsOnLeft(isStartCaret, isRTL) ? minXMaxYCorner(rect) : maxXMaxYCorner(rect);
1013}
1014
1015static void adjustCaretRects(WebCore::IntRect& startCaret, bool isStartCaretClippedOut, WebCore::IntRect& endCaret, bool isEndCaretClippedOut,
1016    const std::vector<Platform::IntRect> rectList, const WebCore::IntPoint& startReferencePoint, const WebCore::IntPoint& endReferencePoint, bool isRTL)
1017{
1018    // startReferencePoint is the best guess at the top left of the selection; endReferencePoint is the best guess at the bottom right.
1019    if (isStartCaretClippedOut)
1020        startCaret.setLocation(DOMSupport::InvalidPoint);
1021    else {
1022        startCaret = rectList[0];
1023        startCaret.setLocation(caretLocationForRect(startCaret, true, isRTL));
1024        // Reset width to 1 as we are strictly interested in caret location.
1025        startCaret.setWidth(1);
1026    }
1027
1028    if (isEndCaretClippedOut)
1029        endCaret.setLocation(DOMSupport::InvalidPoint);
1030    else {
1031        endCaret = rectList[0];
1032        endCaret.setLocation(caretLocationForRect(endCaret, false, isRTL));
1033        // Reset width to 1 as we are strictly interested in caret location.
1034        endCaret.setWidth(1);
1035    }
1036
1037    if (isStartCaretClippedOut && isEndCaretClippedOut)
1038        return;
1039
1040    for (unsigned i = 1; i < rectList.size(); i++) {
1041        WebCore::IntRect currentRect(rectList[i]);
1042
1043        // Compare and update the start and end carets with their respective reference points.
1044        if (!isStartCaretClippedOut && comparePointsToReferencePoint(
1045            caretComparisonPointForRect(currentRect, true, isRTL),
1046            caretComparisonPointForRect(startCaret, true, isRTL),
1047            startReferencePoint, isRTL) > 0) {
1048                startCaret.setLocation(caretLocationForRect(currentRect, true, isRTL));
1049                startCaret.setHeight(currentRect.height());
1050        }
1051
1052        if (!isEndCaretClippedOut && comparePointsToReferencePoint(
1053            caretComparisonPointForRect(currentRect, false, isRTL),
1054            caretComparisonPointForRect(endCaret, false, isRTL),
1055            endReferencePoint, !isRTL) > 0) {
1056                endCaret.setLocation(caretLocationForRect(currentRect, false, isRTL));
1057                endCaret.setHeight(currentRect.height());
1058        }
1059    }
1060}
1061
1062WebCore::IntPoint SelectionHandler::clipPointToVisibleContainer(const WebCore::IntPoint& point) const
1063{
1064    ASSERT(m_webPage->m_mainFrame && m_webPage->m_mainFrame->view());
1065
1066    Frame* frame = m_webPage->focusedOrMainFrame();
1067    WebCore::IntPoint clippedPoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), frame, point, true /* clampToTargetFrame */);
1068
1069    if (m_webPage->m_inputHandler->isInputMode()
1070        && frame->document()->focusedElement()
1071        && frame->document()->focusedElement()->renderer()) {
1072            WebCore::IntRect boundingBox(frame->document()->focusedElement()->renderer()->absoluteBoundingBoxRect());
1073            boundingBox.inflate(-1);
1074            clippedPoint = WebCore::IntPoint(clamp(boundingBox.x(), clippedPoint.x(), boundingBox.maxX()), clamp(boundingBox.y(), clippedPoint.y(), boundingBox.maxY()));
1075    }
1076
1077    return clippedPoint;
1078}
1079
1080static WebCore::IntPoint referencePoint(const VisiblePosition& position, const WebCore::IntRect& boundingRect, const WebCore::IntPoint& framePosition, bool isStartCaret, bool isRTL)
1081{
1082    // If one of the carets is invalid (this happens, for instance, if the
1083    // selection ends in an empty div) fall back to using the corner of the
1084    // entire region (which is already in frame coordinates so doesn't need
1085    // adjusting).
1086    WebCore::IntRect startCaretBounds(position.absoluteCaretBounds());
1087    startCaretBounds.move(framePosition.x(), framePosition.y());
1088    if (startCaretBounds.isEmpty() || !boundingRect.contains(startCaretBounds))
1089        startCaretBounds = boundingRect;
1090
1091    return caretComparisonPointForRect(startCaretBounds, isStartCaret, isRTL);
1092}
1093
1094// Check all rects in the region for a point match. The region is non-banded
1095// and non-sorted so all must be checked.
1096static bool regionRectListContainsPoint(const IntRectRegion& region, const WebCore::IntPoint& point)
1097{
1098    if (!region.extents().contains(point))
1099        return false;
1100
1101    std::vector<Platform::IntRect> rectList = region.rects();
1102    for (unsigned i = 0; i < rectList.size(); i++) {
1103        if (rectList[i].contains(point))
1104            return true;
1105    }
1106    return false;
1107}
1108
1109bool SelectionHandler::inputNodeOverridesTouch() const
1110{
1111    if (!m_webPage->m_inputHandler->isInputMode())
1112        return false;
1113
1114    Node* focusedNode = m_webPage->focusedOrMainFrame()->document()->focusedElement();
1115    if (!focusedNode || !focusedNode->isElementNode())
1116        return false;
1117
1118    // TODO consider caching this in InputHandler so it is only calculated once per focus.
1119    DEFINE_STATIC_LOCAL(QualifiedName, selectionTouchOverrideAttr, (nullAtom, "data-blackberry-end-selection-on-touch", nullAtom));
1120    Element* element = toElement(focusedNode);
1121    return DOMSupport::elementAttributeState(element, selectionTouchOverrideAttr) == DOMSupport::On;
1122}
1123
1124RequestedHandlePosition SelectionHandler::requestedSelectionHandlePosition(const VisibleSelection& selection) const
1125{
1126    Element* element = DOMSupport::selectionContainerElement(selection);
1127    return DOMSupport::elementHandlePositionAttribute(element);
1128}
1129
1130// Note: This is the only function in SelectionHandler in which the coordinate
1131// system is not entirely WebKit.
1132void SelectionHandler::selectionPositionChanged(bool forceUpdateWithoutChange)
1133{
1134    SelectionLog(Platform::LogLevelInfo,
1135        "SelectionHandler::selectionPositionChanged forceUpdateWithoutChange = %s",
1136        forceUpdateWithoutChange ? "true" : "false");
1137
1138    // This method can get called during WebPage shutdown process.
1139    // If that is the case, just bail out since the client is not
1140    // in a safe state of trust to request anything else from it.
1141    if (!m_webPage->m_mainFrame)
1142        return;
1143
1144    if (m_webPage->m_inputHandler->isInputMode() && m_webPage->m_inputHandler->processingChange()) {
1145        if (m_webPage->m_selectionOverlay)
1146            m_webPage->m_selectionOverlay->hide();
1147        m_webPage->m_client->cancelSelectionVisuals();
1148
1149        // Since we're not calling notifyCaretPositionChangedIfNeeded now, we have to do so at the end of processing
1150        // to avoid dropping a notification.
1151        m_didSuppressCaretPositionChangedNotification = true;
1152        return;
1153    }
1154
1155    notifyCaretPositionChangedIfNeeded(m_webPage->m_touchEventHandler->m_userTriggeredTouchPressOnTextInput);
1156
1157    // Enter selection mode if selection type is RangeSelection, and disable selection if
1158    // selection is active and becomes caret selection.
1159    Frame* frame = m_webPage->focusedOrMainFrame();
1160
1161    if (frame->view()->needsLayout())
1162        return;
1163
1164    WebCore::IntPoint framePos = m_webPage->frameOffset(frame);
1165    if (m_selectionActive && (m_caretActive || frame->selection()->isNone()))
1166        m_selectionActive = false;
1167    else if (frame->selection()->isRange())
1168        m_selectionActive = true;
1169    else if (!m_selectionActive)
1170        return;
1171
1172    if (Node* focusedNode = frame->document()->focusedElement()) {
1173        if (focusedNode->hasTagName(HTMLNames::selectTag) || (focusedNode->isElementNode() && DOMSupport::isPopupInputField(toElement(focusedNode)))) {
1174            SelectionLog(Platform::LogLevelInfo, "SelectionHandler::selectionPositionChanged selection is on a popup control, skipping rendering.");
1175            return;
1176        }
1177    }
1178
1179    SelectionTimingLog(Platform::LogLevelInfo,
1180        "SelectionHandler::selectionPositionChanged starting at %f",
1181        m_timer.elapsed());
1182
1183    WebCore::IntRect startCaret(DOMSupport::InvalidPoint, WebCore::IntSize());
1184    WebCore::IntRect endCaret(DOMSupport::InvalidPoint, WebCore::IntSize());
1185
1186    // Get the text rects from the selections range.
1187    Vector<FloatQuad> quads;
1188    DOMSupport::visibleTextQuads(frame->selection()->selection(), quads);
1189
1190    IntRectRegion unclippedRegion;
1191    regionForTextQuads(quads, unclippedRegion, false /* shouldClipToVisibleContent */);
1192
1193    // If there is no change in selected text and the visual rects
1194    // have not changed then don't bother notifying anything.
1195    if (!forceUpdateWithoutChange && m_lastSelectionRegion.isEqual(unclippedRegion))
1196        return;
1197
1198    m_lastSelectionRegion = unclippedRegion;
1199    bool isRTL = directionOfEnclosingBlock(frame->selection()) == RTL;
1200
1201    IntRectRegion visibleSelectionRegion;
1202    if (!unclippedRegion.isEmpty()) {
1203        WebCore::IntRect unclippedStartCaret;
1204        WebCore::IntRect unclippedEndCaret;
1205
1206        WebCore::IntPoint startCaretReferencePoint = referencePoint(frame->selection()->selection().visibleStart(), unclippedRegion.extents(), framePos, true /* isStartCaret */, isRTL);
1207        WebCore::IntPoint endCaretReferencePoint = referencePoint(frame->selection()->selection().visibleEnd(), unclippedRegion.extents(), framePos, false /* isStartCaret */, isRTL);
1208
1209        adjustCaretRects(unclippedStartCaret, false /* unclipped */, unclippedEndCaret, false /* unclipped */, unclippedRegion.rects(), startCaretReferencePoint, endCaretReferencePoint, isRTL);
1210
1211        regionForTextQuads(quads, visibleSelectionRegion);
1212
1213#if SHOWDEBUG_SELECTIONHANDLER // Don't rely just on SelectionLog to avoid loop.
1214        for (unsigned i = 0; i < unclippedRegion.numRects(); i++) {
1215            SelectionLog(Platform::LogLevelInfo,
1216                "Rect list - Unmodified #%d, %s",
1217                i,
1218                unclippedRegion.rects()[i].toString().c_str());
1219        }
1220        for (unsigned i = 0; i < visibleSelectionRegion.numRects(); i++) {
1221            SelectionLog(Platform::LogLevelInfo,
1222                "Rect list  - Clipped to Visible #%d, %s",
1223                i,
1224                visibleSelectionRegion.rects()[i].toString().c_str());
1225        }
1226#endif
1227
1228        bool shouldCareAboutPossibleClippedOutSelection = frame != m_webPage->mainFrame() || m_webPage->m_inputHandler->isInputMode();
1229
1230        if (!visibleSelectionRegion.isEmpty() || shouldCareAboutPossibleClippedOutSelection) {
1231            // Adjust the handle markers to be at the end of the painted rect. When selecting links
1232            // and other elements that may have a larger visible area than needs to be rendered a gap
1233            // can exist between the handle and overlay region.
1234
1235            bool shouldClipStartCaret = !regionRectListContainsPoint(visibleSelectionRegion, unclippedStartCaret.location());
1236            bool shouldClipEndCaret = !regionRectListContainsPoint(visibleSelectionRegion, unclippedEndCaret.location());
1237
1238            // Find the top corner and bottom corner.
1239            adjustCaretRects(startCaret, shouldClipStartCaret, endCaret, shouldClipEndCaret, visibleSelectionRegion.rects(), startCaretReferencePoint, endCaretReferencePoint, isRTL);
1240        }
1241    }
1242
1243    SelectionLog(Platform::LogLevelInfo,
1244        "SelectionHandler::selectionPositionChanged Start Rect=%s End Rect=%s",
1245        Platform::IntRect(startCaret).toString().c_str(),
1246        Platform::IntRect(endCaret).toString().c_str());
1247
1248    if (m_webPage->m_selectionOverlay)
1249        m_webPage->m_selectionOverlay->draw(visibleSelectionRegion);
1250
1251    VisibleSelection currentSelection = frame->selection()->selection();
1252    SelectionDetails details(startCaret, endCaret, visibleSelectionRegion, inputNodeOverridesTouch(),
1253        m_lastSelection != currentSelection, requestedSelectionHandlePosition(frame->selection()->selection()), isRTL);
1254
1255    m_webPage->m_client->notifySelectionDetailsChanged(details);
1256    m_lastSelection = currentSelection;
1257    SelectionTimingLog(Platform::LogLevelInfo,
1258        "SelectionHandler::selectionPositionChanged completed at %f",
1259        m_timer.elapsed());
1260}
1261
1262
1263void SelectionHandler::notifyCaretPositionChangedIfNeeded(bool userTouchTriggeredOnTextField)
1264{
1265    m_didSuppressCaretPositionChangedNotification = false;
1266
1267    if (m_caretActive || (m_webPage->m_inputHandler->isInputMode() && m_webPage->focusedOrMainFrame()->selection()->isCaret())) {
1268        // This may update the caret to no longer be active.
1269        caretPositionChanged(userTouchTriggeredOnTextField);
1270    }
1271}
1272
1273void SelectionHandler::caretPositionChanged(bool userTouchTriggeredOnTextField)
1274{
1275    SelectionLog(Platform::LogLevelInfo, "SelectionHandler::caretPositionChanged");
1276
1277    WebCore::IntRect caretLocation;
1278    // If the input field is not active, we must be turning off the caret.
1279    if (!m_webPage->m_inputHandler->isInputMode() && m_caretActive) {
1280        m_caretActive = false;
1281        // Send an empty caret change to turn off the caret.
1282        m_webPage->m_client->notifyCaretChanged(caretLocation, userTouchTriggeredOnTextField);
1283        return;
1284    }
1285
1286    ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
1287
1288    // This function should only reach this point if input mode is active.
1289    ASSERT(m_webPage->m_inputHandler->isInputMode());
1290
1291    WebCore::IntRect clippingRectForContent(clippingRectForVisibleContent());
1292    WebCore::IntPoint frameOffset(m_webPage->frameOffset(m_webPage->focusedOrMainFrame()));
1293    if (m_webPage->focusedOrMainFrame()->selection()->selectionType() == VisibleSelection::CaretSelection) {
1294        caretLocation = startCaretViewportRect(frameOffset);
1295        if (!caretLocation.isEmpty())
1296            caretLocation.intersect(clippingRectForContent); // Clip against the containing frame and node boundaries.
1297    }
1298
1299    m_caretActive = !caretLocation.isEmpty();
1300
1301    SelectionLog(Platform::LogLevelInfo,
1302        "SelectionHandler::caretPositionChanged caret Rect %s",
1303        Platform::IntRect(caretLocation).toString().c_str());
1304
1305    bool isSingleLineInput = m_caretActive && !m_webPage->m_inputHandler->isMultilineInputMode();
1306    WebCore::IntRect nodeBoundingBox = isSingleLineInput ? m_webPage->m_inputHandler->boundingBoxForInputField() : WebCore::IntRect();
1307
1308    if (!nodeBoundingBox.isEmpty()) {
1309        nodeBoundingBox.moveBy(frameOffset);
1310
1311        // Clip against the containing frame and node boundaries.
1312        nodeBoundingBox.intersect(clippingRectForContent);
1313    }
1314
1315    SelectionLog(Platform::LogLevelInfo,
1316        "SelectionHandler::caretPositionChanged: %s line input, single line bounding box %s%s",
1317        isSingleLineInput ? "single" : "multi",
1318        Platform::IntRect(nodeBoundingBox).toString().c_str(),
1319        m_webPage->m_inputHandler->elementText().isEmpty() ? ", empty text field" : "");
1320
1321    m_webPage->m_client->notifyCaretChanged(caretLocation, userTouchTriggeredOnTextField, isSingleLineInput, nodeBoundingBox, m_webPage->m_inputHandler->elementText().isEmpty());
1322}
1323
1324bool SelectionHandler::selectionContains(const WebCore::IntPoint& point)
1325{
1326    ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
1327    return m_webPage->focusedOrMainFrame()->selection()->contains(point);
1328}
1329
1330}
1331}
1332