[BlackBerry] Text selection - selection gets broken in test.com/individuals.htm
[WebKit-https.git] / Source / WebKit / blackberry / WebKitSupport / SelectionHandler.cpp
1 /*
2  * Copyright (C) 2010, 2011, 2012 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 "Editor.h"
25 #include "EditorClient.h"
26 #include "FatFingers.h"
27 #include "Frame.h"
28 #include "FrameSelection.h"
29 #include "FrameView.h"
30 #include "HTMLAnchorElement.h"
31 #include "HTMLAreaElement.h"
32 #include "HitTestResult.h"
33 #include "InputHandler.h"
34 #include "IntRect.h"
35 #include "Page.h"
36 #include "RenderPart.h"
37 #include "TextGranularity.h"
38 #include "TouchEventHandler.h"
39 #include "WebPage.h"
40 #include "WebPageClient.h"
41 #include "WebPage_p.h"
42
43 #include "htmlediting.h"
44 #include "visible_units.h"
45
46 #include <BlackBerryPlatformKeyboardEvent.h>
47
48 #include <sys/keycodes.h>
49
50 #define SHOWDEBUG_SELECTIONHANDLER 0
51
52 using namespace BlackBerry::Platform;
53 using namespace WebCore;
54
55 #if SHOWDEBUG_SELECTIONHANDLER
56 #define DEBUG_SELECTION(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
57 #else
58 #define DEBUG_SELECTION(severity, format, ...)
59 #endif // SHOWDEBUG_SELECTIONHANDLER
60
61 namespace BlackBerry {
62 namespace WebKit {
63
64 SelectionHandler::SelectionHandler(WebPagePrivate* page)
65     : m_webPage(page)
66     , m_selectionActive(false)
67     , m_caretActive(false)
68     , m_lastUpdatedEndPointIsValid(false)
69 {
70 }
71
72 SelectionHandler::~SelectionHandler()
73 {
74 }
75
76 void SelectionHandler::cancelSelection()
77 {
78     m_selectionActive = false;
79     m_lastSelectionRegion = IntRectRegion();
80
81     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::cancelSelection");
82
83     if (m_webPage->m_inputHandler->isInputMode())
84         m_webPage->m_inputHandler->cancelSelection();
85     else
86         m_webPage->focusedOrMainFrame()->selection()->clear();
87 }
88
89 WebString SelectionHandler::selectedText() const
90 {
91     return m_webPage->focusedOrMainFrame()->editor()->selectedText();
92 }
93
94 void SelectionHandler::getConsolidatedRegionOfTextQuadsForSelection(const VisibleSelection& selection, IntRectRegion& region) const
95 {
96     ASSERT(region.isEmpty());
97
98     if (!selection.isRange())
99         return;
100
101     ASSERT(selection.firstRange());
102
103     Vector<FloatQuad> quadList;
104     DOMSupport::visibleTextQuads(*(selection.firstRange()), quadList, true /* use selection height */);
105
106     if (!quadList.isEmpty()) {
107         FrameView* frameView = m_webPage->focusedOrMainFrame()->view();
108
109         // frameRect is in frame coordinates.
110         WebCore::IntRect frameRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
111
112         // framePosition is in main frame coordinates.
113         WebCore::IntPoint framePosition = m_webPage->frameOffset(m_webPage->focusedOrMainFrame());
114
115         // Convert the text quads into a more platform friendy
116         // IntRectRegion and adjust for subframes.
117         std::vector<Platform::IntRect> adjustedIntRects;
118         Platform::IntRect selectionBoundingBox;
119         for (unsigned i = 0; i < quadList.size(); i++) {
120             WebCore::IntRect enclosingRect = quadList[i].enclosingBoundingBox();
121             enclosingRect.intersect(frameRect);
122             enclosingRect.move(framePosition.x(), framePosition.y());
123             adjustedIntRects.push_back(enclosingRect);
124             selectionBoundingBox = unionOfRects(enclosingRect, selectionBoundingBox);
125         }
126         region = IntRectRegion(selectionBoundingBox, adjustedIntRects.size(), adjustedIntRects);
127     }
128 }
129
130 static VisiblePosition visiblePositionForPointIgnoringClipping(const Frame& frame, const WebCore::IntPoint& framePoint)
131 {
132     // Frame::visiblePositionAtPoint hard-codes ignoreClipping=false in the
133     // call to hitTestResultAtPoint. This has a bug where some pages (such as
134     // metafilter) will return the wrong VisiblePosition for points that are
135     // outside the visible rect. To work around the bug, this is a copy of
136     // visiblePositionAtPoint which which passes ignoreClipping=true.
137     // See RIM Bug #4315.
138     HitTestResult result = frame.eventHandler()->hitTestResultAtPoint(framePoint, true /* allowShadowContent */, true /* ignoreClipping */);
139
140     Node* node = result.innerNode();
141     if (!node || node->document() != frame.document())
142         return VisiblePosition();
143
144     RenderObject* renderer = node->renderer();
145     if (!renderer)
146         return VisiblePosition();
147
148     VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint());
149     if (visiblePos.isNull())
150         visiblePos = VisiblePosition(Position(createLegacyEditingPosition(node, 0)));
151
152     return visiblePos;
153 }
154
155 static unsigned short directionOfPointRelativeToRect(const WebCore::IntPoint& point, const WebCore::IntRect& rect, const bool useTopPadding = true, const bool useBottomPadding = true)
156 {
157     ASSERT(!rect.contains(point));
158
159     // Padding to prevent accidental trigger of up/down when intending to do horizontal movement.
160     const int verticalPadding = 5;
161
162     // Do height movement check first but add padding. We may be off on both x & y axis and only
163     // want to move in one direction at a time.
164     if (point.y() + (useTopPadding ? verticalPadding : 0) < rect.y())
165         return KEYCODE_UP;
166     if (point.y() > rect.maxY() + (useBottomPadding ? verticalPadding : 0))
167         return KEYCODE_DOWN;
168     if (point.x() < rect.location().x())
169         return KEYCODE_LEFT;
170     if (point.x() > rect.maxX())
171         return KEYCODE_RIGHT;
172
173     return 0;
174 }
175
176 bool SelectionHandler::shouldUpdateSelectionOrCaretForPoint(const WebCore::IntPoint& point, const WebCore::IntRect& caretRect, bool startCaret) const
177 {
178     ASSERT(m_webPage->m_inputHandler->isInputMode());
179
180     // If the point isn't valid don't block change as it is not actually changing.
181     if (point == DOMSupport::InvalidPoint)
182         return true;
183
184     VisibleSelection currentSelection = m_webPage->focusedOrMainFrame()->selection()->selection();
185
186     // If the input field is single line or we are on the first or last
187     // line of a multiline input field only horizontal movement is supported.
188     bool aboveCaret = point.y() < caretRect.y();
189     bool belowCaret = point.y() >= caretRect.maxY();
190
191     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::shouldUpdateSelectionOrCaretForPoint multiline = %s above = %s below = %s first line = %s last line = %s start = %s \n"
192             , m_webPage->m_inputHandler->isMultilineInputMode() ? "true" : "false", aboveCaret ? "true" : "false", belowCaret ? "true" : "false"
193             , inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) ? "true" : "false"
194             , inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) ? "true" : "false"
195             , startCaret ? "true" : "false");
196
197     if (!m_webPage->m_inputHandler->isMultilineInputMode() && (aboveCaret || belowCaret))
198         return false;
199     if (startCaret && inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) && aboveCaret)
200         return false;
201     if (!startCaret && inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) && belowCaret)
202         return false;
203
204     return true;
205 }
206
207 void SelectionHandler::setCaretPosition(const WebCore::IntPoint &position)
208 {
209     if (!m_webPage->m_inputHandler->isInputMode())
210         return;
211
212     m_caretActive = true;
213
214     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setCaretPosition requested point %d, %d", position.x(), position.y());
215
216     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
217     FrameSelection* controller = focusedFrame->selection();
218     WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, position);
219     WebCore::IntRect currentCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
220
221     if (relativePoint == DOMSupport::InvalidPoint || !shouldUpdateSelectionOrCaretForPoint(relativePoint, currentCaretRect)) {
222         selectionPositionChanged();
223         return;
224     }
225
226     VisiblePosition visibleCaretPosition(focusedFrame->visiblePositionForPoint(relativePoint));
227
228     if (!DOMSupport::isPositionInNode(m_webPage->focusedOrMainFrame()->document()->focusedNode(), visibleCaretPosition.deepEquivalent())) {
229         if (unsigned short character = directionOfPointRelativeToRect(relativePoint, currentCaretRect))
230             m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character));
231
232         selectionPositionChanged();
233         return;
234     }
235
236     VisibleSelection newSelection(visibleCaretPosition);
237     if (controller->selection() == newSelection) {
238         selectionPositionChanged();
239         return;
240     }
241
242     controller->setSelection(newSelection);
243
244     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setCaretPosition point valid, cursor updated");
245 }
246
247 // This function makes sure we are not reducing the selection to a caret selection.
248 static bool shouldExtendSelectionInDirection(const VisibleSelection& selection, unsigned short character)
249 {
250     FrameSelection tempSelection;
251     tempSelection.setSelection(selection);
252     switch (character) {
253     case KEYCODE_LEFT:
254         tempSelection.modify(FrameSelection::AlterationExtend, DirectionLeft, CharacterGranularity);
255         break;
256     case KEYCODE_RIGHT:
257         tempSelection.modify(FrameSelection::AlterationExtend, DirectionRight, CharacterGranularity);
258         break;
259     case KEYCODE_UP:
260         tempSelection.modify(FrameSelection::AlterationExtend, DirectionBackward, LineGranularity);
261         break;
262     case KEYCODE_DOWN:
263         tempSelection.modify(FrameSelection::AlterationExtend, DirectionForward, LineGranularity);
264         break;
265     default:
266         break;
267     }
268
269     if ((character == KEYCODE_LEFT || character == KEYCODE_RIGHT)
270         && (!inSameLine(selection.visibleStart(), tempSelection.selection().visibleStart())
271            || !inSameLine(selection.visibleEnd(), tempSelection.selection().visibleEnd())))
272         return false;
273
274     return tempSelection.selection().selectionType() == VisibleSelection::RangeSelection;
275 }
276
277 static int clamp(const int min, const int value, const int max)
278 {
279     return value < min ? min : std::min(value, max);
280 }
281
282 static VisiblePosition directionalVisiblePositionAtExtentOfBox(Frame* frame, const WebCore::IntRect& boundingBox, unsigned short direction, const WebCore::IntPoint& basePoint)
283 {
284     ASSERT(frame);
285
286     if (!frame)
287         return VisiblePosition();
288
289     switch (direction) {
290     case KEYCODE_LEFT:
291         // Extend x to start and clamp y to the edge of bounding box.
292         return frame->visiblePositionForPoint(WebCore::IntPoint(boundingBox.x(), clamp(boundingBox.y(), basePoint.y(), boundingBox.maxY())));
293     case KEYCODE_RIGHT:
294         // Extend x to end and clamp y to the edge of bounding box.
295         return frame->visiblePositionForPoint(WebCore::IntPoint(boundingBox.maxX(), clamp(boundingBox.y(), basePoint.y(), boundingBox.maxY())));
296     case KEYCODE_UP:
297         // Extend y to top and clamp x to the edge of bounding box.
298         return frame->visiblePositionForPoint(WebCore::IntPoint(clamp(boundingBox.x(), basePoint.x(), boundingBox.maxX()), boundingBox.y()));
299     case KEYCODE_DOWN:
300         // Extend y to bottom and clamp x to the edge of bounding box.
301         return frame->visiblePositionForPoint(WebCore::IntPoint(clamp(boundingBox.x(), basePoint.x(), boundingBox.maxX()), boundingBox.maxY()));
302     default:
303         break;
304     }
305
306     return frame->visiblePositionForPoint(WebCore::IntPoint(basePoint.x(), basePoint.y()));
307 }
308
309 static bool pointIsOutsideOfBoundingBoxInDirection(unsigned direction, const WebCore::IntPoint& selectionPoint, const WebCore::IntRect& boundingBox)
310 {
311     if ((direction == KEYCODE_LEFT && selectionPoint.x() < boundingBox.x())
312         || (direction == KEYCODE_UP && selectionPoint.y() < boundingBox.y())
313         || (direction == KEYCODE_RIGHT && selectionPoint.x() > boundingBox.maxX())
314         || (direction == KEYCODE_DOWN && selectionPoint.y() > boundingBox.maxY()))
315         return true;
316
317     return false;
318 }
319
320 unsigned short SelectionHandler::extendSelectionToFieldBoundary(bool isStartHandle, const WebCore::IntPoint& selectionPoint, VisibleSelection& newSelection)
321 {
322     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
323     if (!focusedFrame->document()->focusedNode() || !focusedFrame->document()->focusedNode()->renderer())
324         return 0;
325
326     FrameSelection* controller = focusedFrame->selection();
327
328     WebCore::IntRect caretRect = isStartHandle ? controller->selection().visibleStart().absoluteCaretBounds()
329                                       : controller->selection().visibleEnd().absoluteCaretBounds();
330
331     WebCore::IntRect nodeBoundingBox = focusedFrame->document()->focusedNode()->renderer()->absoluteBoundingBoxRect();
332     nodeBoundingBox.inflate(-1);
333
334     // Start handle is outside of the field. Treat it as the changed handle and move
335     // relative to the start caret rect.
336     unsigned short character = directionOfPointRelativeToRect(selectionPoint, caretRect, isStartHandle /* useTopPadding */, !isStartHandle /* useBottomPadding */);
337
338     // Prevent incorrect movement, handles can only extend the selection this way
339     // to prevent inversion of the handles.
340     if (isStartHandle && (character == KEYCODE_RIGHT || character == KEYCODE_DOWN)
341         || !isStartHandle && (character == KEYCODE_LEFT || character == KEYCODE_UP))
342         character = 0;
343
344     VisiblePosition newVisiblePosition = isStartHandle ? controller->selection().extent() : controller->selection().base();
345     // Extend the selection to the bounds of the box before doing incremental scroll if the point is outside the node.
346     // Don't extend selection and handle the character at the same time.
347     if (pointIsOutsideOfBoundingBoxInDirection(character, selectionPoint, nodeBoundingBox))
348         newVisiblePosition = directionalVisiblePositionAtExtentOfBox(focusedFrame, nodeBoundingBox, character, selectionPoint);
349
350     if (isStartHandle)
351         newSelection = VisibleSelection(newVisiblePosition, newSelection.extent(), true /* isDirectional */);
352     else
353         newSelection = VisibleSelection(newSelection.base(), newVisiblePosition, true /* isDirectional */);
354
355     // If no selection will be changed, return the character to extend using navigation.
356     if (controller->selection() == newSelection)
357         return character;
358
359     // Selection has been updated.
360     return 0;
361 }
362
363 // Returns true if handled.
364 bool SelectionHandler::updateOrHandleInputSelection(VisibleSelection& newSelection, const WebCore::IntPoint& relativeStart
365                                                     , const WebCore::IntPoint& relativeEnd)
366 {
367     ASSERT(m_webPage->m_inputHandler->isInputMode());
368
369     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
370     Node* focusedNode = focusedFrame->document()->focusedNode();
371     if (!focusedNode || !focusedNode->renderer())
372         return false;
373
374     FrameSelection* controller = focusedFrame->selection();
375
376     WebCore::IntRect currentStartCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
377     WebCore::IntRect currentEndCaretRect = controller->selection().visibleEnd().absoluteCaretBounds();
378
379     // Check if the handle movement is valid.
380     if (!shouldUpdateSelectionOrCaretForPoint(relativeStart, currentStartCaretRect, true /* startCaret */)
381         || !shouldUpdateSelectionOrCaretForPoint(relativeEnd, currentEndCaretRect, false /* startCaret */)) {
382         selectionPositionChanged();
383         return true;
384     }
385
386     WebCore::IntRect nodeBoundingBox = focusedNode->renderer()->absoluteBoundingBoxRect();
387
388     // Only do special handling if one handle is outside of the node.
389     bool startIsOutsideOfField = relativeStart != DOMSupport::InvalidPoint && !nodeBoundingBox.contains(relativeStart);
390     bool endIsOutsideOfField = relativeEnd != DOMSupport::InvalidPoint && !nodeBoundingBox.contains(relativeEnd);
391     if (startIsOutsideOfField && endIsOutsideOfField)
392         return false;
393
394     unsigned short character = 0;
395     if (startIsOutsideOfField) {
396         character = extendSelectionToFieldBoundary(true /* isStartHandle */, relativeStart, newSelection);
397         if (character) {
398             // Invert the selection so that the cursor point is at the beginning.
399             controller->setSelection(VisibleSelection(controller->selection().end(), controller->selection().start(), true /* isDirectional */));
400         }
401     } else if (endIsOutsideOfField) {
402         character = extendSelectionToFieldBoundary(false /* isStartHandle */, relativeEnd, newSelection);
403         if (character) {
404             // Reset the selection so that the end is the edit point.
405             controller->setSelection(VisibleSelection(controller->selection().start(), controller->selection().end(), true /* isDirectional */));
406         }
407     }
408
409     if (!character)
410         return false;
411
412     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setSelection making selection change attempt using key event %d", character);
413
414     if (shouldExtendSelectionInDirection(controller->selection(), character))
415         m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character, Platform::KeyboardEvent::KeyDown, KEYMOD_SHIFT));
416
417     // Must send the selectionPositionChanged every time, sometimes this will duplicate but an accepted
418     // handleNavigationMove may not make an actual selection change.
419     selectionPositionChanged();
420     return true;
421 }
422
423 void SelectionHandler::setSelection(const WebCore::IntPoint& start, const WebCore::IntPoint& end)
424 {
425     m_selectionActive = true;
426
427     ASSERT(m_webPage);
428     ASSERT(m_webPage->focusedOrMainFrame());
429     ASSERT(m_webPage->focusedOrMainFrame()->selection());
430
431     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
432     FrameSelection* controller = focusedFrame->selection();
433
434     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setSelection adjusted points %d, %d, %d, %d", start.x(), start.y(), end.x(), end.y());
435
436     // Note that IntPoint(-1, -1) is being our sentinel so far for
437     // clipped out selection starting or ending location.
438     bool startIsValid = start != DOMSupport::InvalidPoint;
439     m_lastUpdatedEndPointIsValid = end != DOMSupport::InvalidPoint;
440
441     // At least one of the locations must be valid.
442     ASSERT(startIsValid || m_lastUpdatedEndPointIsValid);
443
444     WebCore::IntPoint relativeStart = start;
445     WebCore::IntPoint relativeEnd = end;
446
447     VisibleSelection newSelection(controller->selection());
448
449     // We need the selection to be ordered base then extent.
450     if (!controller->selection().isBaseFirst())
451         controller->setSelection(VisibleSelection(controller->selection().start(), controller->selection().end(), true /* isDirectional */));
452
453     // We don't return early in the following, so that we can do input field scrolling if the
454     // handle is outside the bounds of the field. This can be extended to handle sub-region
455     // scrolling as well
456     if (startIsValid) {
457         relativeStart = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, start);
458
459         VisiblePosition base = visiblePositionForPointIgnoringClipping(*focusedFrame, clipPointToVisibleContainer(start));
460         if (base.isNotNull()) {
461             // The function setBase validates the "base"
462             newSelection.setBase(base);
463             newSelection.setWithoutValidation(newSelection.base(), controller->selection().end());
464             // Don't return early.
465         }
466     }
467
468     if (m_lastUpdatedEndPointIsValid) {
469         relativeEnd = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, end);
470
471         VisiblePosition extent = visiblePositionForPointIgnoringClipping(*focusedFrame, clipPointToVisibleContainer(end));
472         if (extent.isNotNull()) {
473             // The function setExtent validates the "extent"
474             newSelection.setExtent(extent);
475             newSelection.setWithoutValidation(controller->selection().start(), newSelection.extent());
476             // Don't return early.
477         }
478     }
479
480     newSelection.setIsDirectional(true);
481
482     if (m_webPage->m_inputHandler->isInputMode()) {
483         if (updateOrHandleInputSelection(newSelection, relativeStart, relativeEnd))
484             return;
485     }
486
487     if (controller->selection() == newSelection) {
488         selectionPositionChanged();
489         return;
490     }
491
492     // If the selection size is reduce to less than a character, selection type becomes
493     // Caret. As long as it is still a range, it's a valid selection. Selection cannot
494     // be cancelled through this function.
495     IntRectRegion region;
496     getConsolidatedRegionOfTextQuadsForSelection(newSelection, region);
497     clipRegionToVisibleContainer(region);
498     if (!region.isEmpty()) {
499         // Check if the handles reversed position.
500         if (m_selectionActive && !newSelection.isBaseFirst())
501             m_webPage->m_client->notifySelectionHandlesReversed();
502
503         controller->setSelection(newSelection);
504
505         DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setSelection selection points valid, selection updated");
506     } else {
507         // Requested selection results in an empty selection, skip this change.
508         selectionPositionChanged();
509
510         DEBUG_SELECTION(LogLevelWarn, "SelectionHandler::setSelection selection points invalid, selection not updated");
511     }
512 }
513
514 // FIXME re-use this in context. Must be updated to include an option to return the href.
515 // This function should be moved to a new unit file. Names suggetions include DOMQueries
516 // and NodeTypes. Functions currently in InputHandler.cpp, SelectionHandler.cpp and WebPage.cpp
517 // can all be moved in.
518 static Node* enclosingLinkEventParentForNode(Node* node)
519 {
520     if (!node)
521         return 0;
522
523     Node* linkNode = node->enclosingLinkEventParentOrSelf();
524     return linkNode && linkNode->isLink() ? linkNode : 0;
525 }
526
527 void SelectionHandler::selectAtPoint(const WebCore::IntPoint& location)
528 {
529     // If point is invalid trigger selection based expansion.
530     if (location == DOMSupport::InvalidPoint) {
531         selectObject(WordGranularity);
532         return;
533     }
534
535     Node* targetNode;
536     WebCore::IntPoint targetPosition;
537     // FIXME: Factory this get right fat finger code into a helper.
538     const FatFingersResult lastFatFingersResult = m_webPage->m_touchEventHandler->lastFatFingersResult();
539     if (lastFatFingersResult.positionWasAdjusted() && lastFatFingersResult.nodeAsElementIfApplicable()) {
540         targetNode = lastFatFingersResult.node(FatFingersResult::ShadowContentNotAllowed);
541         targetPosition = lastFatFingersResult.adjustedPosition();
542     } else {
543         FatFingersResult newFatFingersResult = FatFingers(m_webPage, location, FatFingers::Text).findBestPoint();
544         if (!newFatFingersResult.positionWasAdjusted())
545             return;
546
547         targetPosition = newFatFingersResult.adjustedPosition();
548         targetNode = newFatFingersResult.node(FatFingersResult::ShadowContentNotAllowed);
549     }
550
551     ASSERT(targetNode);
552
553     // If the node at the point is a link, focus on the entire link, not a word.
554     if (Node* link = enclosingLinkEventParentForNode(targetNode)) {
555         selectObject(link);
556         return;
557     }
558
559     // selectAtPoint API currently only supports WordGranularity but may be extended in the future.
560     selectObject(targetPosition, WordGranularity);
561 }
562
563 static bool expandSelectionToGranularity(Frame* frame, VisibleSelection selection, TextGranularity granularity, bool isInputMode)
564 {
565     ASSERT(frame);
566     ASSERT(frame->selection());
567
568     if (!(selection.start().anchorNode() && selection.start().anchorNode()->isTextNode()))
569         return false;
570
571     if (granularity == WordGranularity)
572         selection = DOMSupport::visibleSelectionForClosestActualWordStart(selection);
573
574     selection.expandUsingGranularity(granularity);
575     RefPtr<Range> newRange = selection.toNormalizedRange();
576     RefPtr<Range> oldRange = frame->selection()->selection().toNormalizedRange();
577     EAffinity affinity = frame->selection()->affinity();
578
579     if (isInputMode && !frame->editor()->client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false))
580         return false;
581
582     return frame->selection()->setSelectedRange(newRange.get(), affinity, true);
583 }
584
585 void SelectionHandler::selectObject(const WebCore::IntPoint& location, TextGranularity granularity)
586 {
587     ASSERT(location.x() >= 0 && location.y() >= 0);
588     ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
589     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
590
591     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectObject adjusted points %d, %d", location.x(), location.y());
592
593     WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, location);
594     VisiblePosition pointLocation(focusedFrame->visiblePositionForPoint(relativePoint));
595     VisibleSelection selection = VisibleSelection(pointLocation, pointLocation);
596
597     m_selectionActive = expandSelectionToGranularity(focusedFrame, selection, granularity, m_webPage->m_inputHandler->isInputMode());
598 }
599
600 void SelectionHandler::selectObject(TextGranularity granularity)
601 {
602     ASSERT(m_webPage && m_webPage->m_inputHandler);
603     // Using caret location, must be inside an input field.
604     if (!m_webPage->m_inputHandler->isInputMode())
605         return;
606
607     ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
608     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
609
610     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectObject using current selection");
611
612     // Use the current selection as the selection point.
613     ASSERT(focusedFrame->selection()->selectionType() != VisibleSelection::NoSelection);
614     m_selectionActive = expandSelectionToGranularity(focusedFrame, focusedFrame->selection()->selection(), granularity, true /* isInputMode */);
615 }
616
617 void SelectionHandler::selectObject(Node* node)
618 {
619     if (!node)
620         return;
621
622     m_selectionActive = true;
623
624     ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
625     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
626
627     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectNode");
628
629     VisibleSelection selection = VisibleSelection::selectionFromContentsOfNode(node);
630     focusedFrame->selection()->setSelection(selection);
631 }
632
633 static TextDirection directionOfEnclosingBlock(FrameSelection* selection)
634 {
635     Node* enclosingBlockNode = enclosingBlock(selection->selection().extent().deprecatedNode());
636     if (!enclosingBlockNode)
637         return LTR;
638
639     if (RenderObject* renderer = enclosingBlockNode->renderer())
640         return renderer->style()->direction();
641
642     return LTR;
643 }
644
645 // Returns > 0 if p1 is "closer" to referencePoint, < 0 if p2 is "closer", 0 if they are equidistant.
646 // Because text is usually arranged in horizontal rows, distance is measured along the y-axis, with x-axis used only to break ties.
647 // If rightGravity is true, the right-most x-coordinate is chosen, otherwise teh left-most coordinate is chosen.
648 static inline int comparePointsToReferencePoint(const WebCore::IntPoint& p1, const WebCore::IntPoint& p2, const WebCore::IntPoint& referencePoint, bool rightGravity)
649 {
650     int dy1 = abs(referencePoint.y() - p1.y());
651     int dy2 = abs(referencePoint.y() - p2.y());
652     if (dy1 != dy2)
653         return dy2 - dy1;
654
655     // Same y-coordinate, choose the farthest right (or left) point.
656     if (p1.x() == p2.x())
657         return 0;
658
659     if (p1.x() > p2.x())
660         return rightGravity ? 1 : -1;
661
662     return rightGravity ? -1 : 1;
663 }
664
665 // NOTE/FIXME: Due to r77286, we are getting off-by-one results in the IntRect class counterpart implementation of the
666 //             methods below. As done in r89803, r77928 and a few others, lets use local method to fix it.
667 //             We should keep our eyes very open on it, since it can affect BackingStore very badly.
668 static WebCore::IntPoint minXMinYCorner(const WebCore::IntRect& rect) { return rect.location(); } // typically topLeft
669 static WebCore::IntPoint maxXMinYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x() + rect.width() - 1, rect.y()); } // typically topRight
670 static WebCore::IntPoint minXMaxYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x(), rect.y() + rect.height() - 1); } // typically bottomLeft
671 static WebCore::IntPoint maxXMaxYCorner(const WebCore::IntRect& rect) { return WebCore::IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height() - 1); } // typically bottomRight
672
673 // The caret is a one-pixel wide line down either the right or left edge of a
674 // rect, depending on the text direction.
675 static inline bool caretIsOnLeft(bool isStartCaret, bool isRTL)
676 {
677     if (isStartCaret)
678         return !isRTL;
679
680     return isRTL;
681 }
682
683 static inline WebCore::IntPoint caretLocationForRect(const WebCore::IntRect& rect, bool isStartCaret, bool isRTL)
684 {
685     return caretIsOnLeft(isStartCaret, isRTL) ? minXMinYCorner(rect) : maxXMinYCorner(rect);
686 }
687
688 static inline WebCore::IntPoint caretComparisonPointForRect(const WebCore::IntRect& rect, bool isStartCaret, bool isRTL)
689 {
690     if (isStartCaret)
691         return caretIsOnLeft(isStartCaret, isRTL) ? minXMinYCorner(rect) : maxXMinYCorner(rect);
692
693     return caretIsOnLeft(isStartCaret, isRTL) ? minXMaxYCorner(rect) : maxXMaxYCorner(rect);
694 }
695
696 static void adjustCaretRects(WebCore::IntRect& startCaret, bool isStartCaretClippedOut,
697                              WebCore::IntRect& endCaret, bool isEndCaretClippedOut,
698                              const std::vector<Platform::IntRect> rectList,
699                              const WebCore::IntPoint& startReferencePoint,
700                              const WebCore::IntPoint& endReferencePoint,
701                              bool isRTL)
702 {
703     // startReferencePoint is the best guess at the top left of the selection; endReferencePoint is the best guess at the bottom right.
704     if (isStartCaretClippedOut)
705         startCaret.setLocation(DOMSupport::InvalidPoint);
706     else {
707         startCaret = rectList[0];
708         startCaret.setLocation(caretLocationForRect(startCaret, true, isRTL));
709     }
710
711     if (isEndCaretClippedOut)
712         endCaret.setLocation(DOMSupport::InvalidPoint);
713     else {
714         endCaret = rectList[0];
715         endCaret.setLocation(caretLocationForRect(endCaret, false, isRTL));
716     }
717
718     if (isStartCaretClippedOut && isEndCaretClippedOut)
719         return;
720
721     // Reset width to 1 as we are strictly interested in caret location.
722     startCaret.setWidth(1);
723     endCaret.setWidth(1);
724
725     for (unsigned i = 1; i < rectList.size(); i++) {
726         WebCore::IntRect currentRect(rectList[i]);
727
728         // Compare and update the start and end carets with their respective reference points.
729         if (!isStartCaretClippedOut && comparePointsToReferencePoint(
730                     caretComparisonPointForRect(currentRect, true, isRTL),
731                     caretComparisonPointForRect(startCaret, true, isRTL),
732                     startReferencePoint, isRTL) > 0) {
733             startCaret.setLocation(caretLocationForRect(currentRect, true, isRTL));
734             startCaret.setHeight(currentRect.height());
735         }
736
737         if (!isEndCaretClippedOut && comparePointsToReferencePoint(
738                     caretComparisonPointForRect(currentRect, false, isRTL),
739                     caretComparisonPointForRect(endCaret, false, isRTL),
740                     endReferencePoint, !isRTL) > 0) {
741             endCaret.setLocation(caretLocationForRect(currentRect, false, isRTL));
742             endCaret.setHeight(currentRect.height());
743         }
744     }
745 }
746
747 void SelectionHandler::clipRegionToVisibleContainer(IntRectRegion& region)
748 {
749     ASSERT(m_webPage->m_mainFrame && m_webPage->m_mainFrame->view());
750
751     Frame* frame = m_webPage->focusedOrMainFrame();
752
753     // Don't allow the region to extend outside of the all its ancestor frames' visible area.
754     if (frame != m_webPage->mainFrame()) {
755         WebCore::IntRect containingContentRect;
756         containingContentRect = m_webPage->getRecursiveVisibleWindowRect(frame->view(), true /* no clip to main frame window */);
757         containingContentRect = m_webPage->m_mainFrame->view()->windowToContents(containingContentRect);
758         region = intersectRegions(IntRectRegion(containingContentRect), region);
759     }
760
761     // Don't allow the region to extend outside of the input field.
762     if (m_webPage->m_inputHandler->isInputMode()
763         && frame->document()->focusedNode()
764         && frame->document()->focusedNode()->renderer()) {
765
766         // Adjust the bounding box to the frame offset.
767         WebCore::IntRect boundingBox(frame->document()->focusedNode()->renderer()->absoluteBoundingBoxRect());
768         boundingBox = m_webPage->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(boundingBox));
769
770         region = intersectRegions(IntRectRegion(boundingBox), region);
771     }
772 }
773
774 WebCore::IntPoint SelectionHandler::clipPointToVisibleContainer(const WebCore::IntPoint& point) const
775 {
776     ASSERT(m_webPage->m_mainFrame && m_webPage->m_mainFrame->view());
777
778     Frame* frame = m_webPage->focusedOrMainFrame();
779     WebCore::IntPoint clippedPoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), frame, point, true /* clampToTargetFrame */);
780
781     if (m_webPage->m_inputHandler->isInputMode()
782             && frame->document()->focusedNode()
783             && frame->document()->focusedNode()->renderer()) {
784         WebCore::IntRect boundingBox(frame->document()->focusedNode()->renderer()->absoluteBoundingBoxRect());
785         boundingBox.inflate(-1);
786         clippedPoint = WebCore::IntPoint(clamp(boundingBox.x(), clippedPoint.x(), boundingBox.maxX()), clamp(boundingBox.y(), clippedPoint.y(), boundingBox.maxY()));
787     }
788
789     return clippedPoint;
790 }
791
792 static WebCore::IntPoint referencePoint(const VisiblePosition& position, const WebCore::IntRect& boundingRect, const WebCore::IntPoint& framePosition, bool isStartCaret, bool isRTL)
793 {
794     // If one of the carets is invalid (this happens, for instance, if the
795     // selection ends in an empty div) fall back to using the corner of the
796     // entire region (which is already in frame coordinates so doesn't need
797     // adjusting).
798     WebCore::IntRect startCaretBounds(position.absoluteCaretBounds());
799     if (startCaretBounds.isEmpty())
800         startCaretBounds = boundingRect;
801     else
802         startCaretBounds.move(framePosition.x(), framePosition.y());
803
804     return caretComparisonPointForRect(startCaretBounds, isStartCaret, isRTL);
805 }
806
807 // Note: This is the only function in SelectionHandler in which the coordinate
808 // system is not entirely WebKit.
809 void SelectionHandler::selectionPositionChanged(bool visualChangeOnly)
810 {
811     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectionPositionChanged visibleChangeOnly = %s", visualChangeOnly ? "true" : "false");
812
813     // This method can get called during WebPage shutdown process.
814     // If that is the case, just bail out since the client is not
815     // in a safe state of trust to request anything else from it.
816     if (!m_webPage->m_mainFrame)
817         return;
818
819     if (m_webPage->m_inputHandler->isInputMode() && m_webPage->m_inputHandler->processingChange()) {
820         m_webPage->m_client->cancelSelectionVisuals();
821         return;
822     }
823
824     if (m_caretActive || (m_webPage->m_inputHandler->isInputMode() && m_webPage->focusedOrMainFrame()->selection()->isCaret())) {
825         // This may update the caret to no longer be active.
826         caretPositionChanged();
827     }
828
829     // Enter selection mode if selection type is RangeSelection, and disable selection if
830     // selection is active and becomes caret selection.
831     Frame* frame = m_webPage->focusedOrMainFrame();
832     WebCore::IntPoint framePos = m_webPage->frameOffset(frame);
833     if (m_selectionActive && (m_caretActive || frame->selection()->isNone()))
834         m_selectionActive = false;
835     else if (frame->selection()->isRange())
836         m_selectionActive = true;
837     else if (!m_selectionActive)
838         return;
839
840     WebCore::IntRect startCaret;
841     WebCore::IntRect endCaret;
842
843     // Get the text rects from the selections range.
844     IntRectRegion region;
845     getConsolidatedRegionOfTextQuadsForSelection(frame->selection()->selection(), region);
846
847     // If there is no change in selected text and the visual rects
848     // have not changed then don't bother notifying anything.
849     if (visualChangeOnly && m_lastSelectionRegion.isEqual(region))
850         return;
851
852     m_lastSelectionRegion = region;
853
854     if (!region.isEmpty()) {
855         WebCore::IntRect unclippedStartCaret;
856         WebCore::IntRect unclippedEndCaret;
857
858         bool isRTL = directionOfEnclosingBlock(frame->selection()) == RTL;
859
860         std::vector<Platform::IntRect> rectList = region.rects();
861
862         WebCore::IntPoint startCaretReferencePoint = referencePoint(frame->selection()->selection().visibleStart(), region.extents(), framePos, true /* isStartCaret */, isRTL);
863         WebCore::IntPoint endCaretReferencePoint = referencePoint(frame->selection()->selection().visibleEnd(), region.extents(), framePos, false /* isStartCaret */, isRTL);
864
865         adjustCaretRects(unclippedStartCaret, false /* unclipped */, unclippedEndCaret, false /* unclipped */, rectList, startCaretReferencePoint, endCaretReferencePoint, isRTL);
866
867         clipRegionToVisibleContainer(region);
868
869 #if SHOWDEBUG_SELECTIONHANDLER // Don't rely just on DEBUG_SELECTION to avoid loop.
870         for (unsigned int i = 0; i < rectList.size(); i++)
871             DEBUG_SELECTION(LogLevelCritical, "Rect list - Unmodified #%d, (%d, %d) (%d x %d)", i, rectList[i].x(), rectList[i].y(), rectList[i].width(), rectList[i].height());
872         for (unsigned int i = 0; i < region.numRects(); i++)
873             DEBUG_SELECTION(LogLevelCritical, "Rect list  - Consolidated #%d, (%d, %d) (%d x %d)", i, region.rects()[i].x(), region.rects()[i].y(), region.rects()[i].width(), region.rects()[i].height());
874 #endif
875
876         bool shouldCareAboutPossibleClippedOutSelection = frame != m_webPage->mainFrame() || m_webPage->m_inputHandler->isInputMode();
877
878         if (!region.isEmpty() || shouldCareAboutPossibleClippedOutSelection) {
879             // Adjust the handle markers to be at the end of the painted rect. When selecting links
880             // and other elements that may have a larger visible area than needs to be rendered a gap
881             // can exist between the handle and overlay region.
882
883             bool shouldClipStartCaret = !region.isRectInRegion(unclippedStartCaret);
884             bool shouldClipEndCaret = !region.isRectInRegion(unclippedEndCaret);
885
886             // Find the top corner and bottom corner.
887             std::vector<Platform::IntRect> clippedRectList = region.rects();
888             adjustCaretRects(startCaret, shouldClipStartCaret, endCaret, shouldClipEndCaret, clippedRectList, startCaretReferencePoint, endCaretReferencePoint, isRTL);
889
890             // Translate the caret values as they must be in transformed coordinates.
891             if (!shouldClipStartCaret) {
892                 startCaret = m_webPage->mapToTransformed(startCaret);
893                 m_webPage->clipToTransformedContentsRect(startCaret);
894             }
895
896             if (!shouldClipEndCaret) {
897                 endCaret = m_webPage->mapToTransformed(endCaret);
898                 m_webPage->clipToTransformedContentsRect(endCaret);
899             }
900         }
901     }
902
903     DEBUG_SELECTION(BlackBerry::Platform::LogLevelInfo, "SelectionHandler::selectionPositionChanged Start Rect=(%d, %d) (%d x %d) End Rect=(%d, %d) (%d x %d)",
904                     startCaret.x(), startCaret.y(), startCaret.width(), startCaret.height(), endCaret.x(), endCaret.y(), endCaret.width(), endCaret.height());
905
906
907     m_webPage->m_client->notifySelectionDetailsChanged(startCaret, endCaret, region);
908 }
909
910 // NOTE: This function is not in WebKit coordinates.
911 void SelectionHandler::caretPositionChanged()
912 {
913     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::caretPositionChanged");
914
915     WebCore::IntRect caretLocation;
916     // If the input field is not active, we must be turning off the caret.
917     if (!m_webPage->m_inputHandler->isInputMode() && m_caretActive) {
918         m_caretActive = false;
919         // Send an empty caret change to turn off the caret.
920         m_webPage->m_client->notifyCaretChanged(caretLocation, m_webPage->m_touchEventHandler->lastFatFingersResult().isTextInput() /* userTouchTriggered */);
921         return;
922     }
923
924     ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
925
926     // This function should only reach this point if input mode is active.
927     ASSERT(m_webPage->m_inputHandler->isInputMode());
928
929     if (m_webPage->focusedOrMainFrame()->selection()->selectionType() == VisibleSelection::CaretSelection) {
930         WebCore::IntPoint frameOffset = m_webPage->frameOffset(m_webPage->focusedOrMainFrame());
931
932         caretLocation = m_webPage->focusedOrMainFrame()->selection()->selection().visibleStart().absoluteCaretBounds();
933         caretLocation.move(frameOffset.x(), frameOffset.y());
934
935         // Clip against the containing frame and node boundaries.
936         IntRectRegion region(caretLocation);
937         clipRegionToVisibleContainer(region);
938         caretLocation = region.extents();
939     }
940
941     m_caretActive = !caretLocation.isEmpty();
942
943     DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::caretPositionChanged caret Rect %d, %d, %dx%d",
944                         caretLocation.x(), caretLocation.y(), caretLocation.width(), caretLocation.height());
945
946     caretLocation = m_webPage->mapToTransformed(caretLocation);
947     m_webPage->clipToTransformedContentsRect(caretLocation);
948
949     m_webPage->m_client->notifyCaretChanged(caretLocation, m_webPage->m_touchEventHandler->lastFatFingersResult().isTextInput() /* userTouchTriggered */);
950 }
951
952 bool SelectionHandler::selectionContains(const WebCore::IntPoint& point)
953 {
954     ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
955     return m_webPage->focusedOrMainFrame()->selection()->contains(point);
956 }
957
958 }
959 }