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