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