Simplify hitTestResultAtPoint and nodesFromRect APIs
[WebKit-https.git] / Source / WebKit / blackberry / WebKitSupport / SelectionHandler.cpp
index b65baba..08c0f88 100644 (file)
 
 #include "DOMSupport.h"
 #include "Document.h"
-#include "Editor.h"
-#include "EditorClient.h"
 #include "FatFingers.h"
 #include "FloatQuad.h"
 #include "Frame.h"
 #include "FrameSelection.h"
 #include "FrameView.h"
-#include "HTMLAnchorElement.h"
-#include "HTMLAreaElement.h"
 #include "HitTestResult.h"
 #include "InputHandler.h"
 #include "IntRect.h"
-#include "Page.h"
-#include "RenderPart.h"
-#include "TextGranularity.h"
 #include "TouchEventHandler.h"
-#include "WebPage.h"
 #include "WebPageClient.h"
 #include "WebPage_p.h"
+#include "WebSelectionOverlay.h"
 
 #include "htmlediting.h"
 #include "visible_units.h"
 
 #include <BlackBerryPlatformKeyboardEvent.h>
+#include <BlackBerryPlatformLog.h>
 
 #include <sys/keycodes.h>
 
 // Note: This generates a lot of logs when dumping rects lists. It will seriously
 // impact performance. Do not enable this during performance tests.
 #define SHOWDEBUG_SELECTIONHANDLER 0
+#define SHOWDEBUG_SELECTIONHANDLER_TIMING 0
 
 using namespace BlackBerry::Platform;
 using namespace WebCore;
 
 #if SHOWDEBUG_SELECTIONHANDLER
-#define DEBUG_SELECTION(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
+#define SelectionLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
 #else
-#define DEBUG_SELECTION(severity, format, ...)
+#define SelectionLog(severity, format, ...)
 #endif // SHOWDEBUG_SELECTIONHANDLER
 
+#if SHOWDEBUG_SELECTIONHANDLER_TIMING
+#define SelectionTimingLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
+#else
+#define SelectionTimingLog(severity, format, ...)
+#endif // SHOWDEBUG_SELECTIONHANDLER_TIMING
+
 namespace BlackBerry {
 namespace WebKit {
 
@@ -81,7 +82,7 @@ void SelectionHandler::cancelSelection()
     m_selectionActive = false;
     m_lastSelectionRegion = IntRectRegion();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::cancelSelection");
+    SelectionLog(LogLevelInfo, "SelectionHandler::cancelSelection");
 
     if (m_webPage->m_inputHandler->isInputMode())
         m_webPage->m_inputHandler->cancelSelection();
@@ -158,7 +159,7 @@ static VisiblePosition visiblePositionForPointIgnoringClipping(const Frame& fram
     // outside the visible rect. To work around the bug, this is a copy of
     // visiblePositionAtPoint which which passes ignoreClipping=true.
     // See RIM Bug #4315.
-    HitTestResult result = frame.eventHandler()->hitTestResultAtPoint(framePoint, true /* allowShadowContent */, true /* ignoreClipping */);
+    HitTestResult result = frame.eventHandler()->hitTestResultAtPoint(framePoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::AllowShadowContent | HitTestRequest::IgnoreClipping);
 
     Node* node = result.innerNode();
     if (!node || node->document() != frame.document())
@@ -184,7 +185,7 @@ static unsigned short directionOfPointRelativeToRect(const WebCore::IntPoint& po
 
     // Do height movement check first but add padding. We may be off on both x & y axis and only
     // want to move in one direction at a time.
-    if (point.y() + (useTopPadding ? verticalPadding : 0) < rect.y())
+    if (point.y() - (useTopPadding ? verticalPadding : 0) < rect.y())
         return KEYCODE_UP;
     if (point.y() > rect.maxY() + (useBottomPadding ? verticalPadding : 0))
         return KEYCODE_DOWN;
@@ -211,7 +212,7 @@ bool SelectionHandler::shouldUpdateSelectionOrCaretForPoint(const WebCore::IntPo
     bool aboveCaret = point.y() < caretRect.y();
     bool belowCaret = point.y() >= caretRect.maxY();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::shouldUpdateSelectionOrCaretForPoint multiline = %s above = %s below = %s first line = %s last line = %s start = %s \n"
+    SelectionLog(LogLevelInfo, "SelectionHandler::shouldUpdateSelectionOrCaretForPoint multiline = %s above = %s below = %s first line = %s last line = %s start = %s"
             , m_webPage->m_inputHandler->isMultilineInputMode() ? "true" : "false", aboveCaret ? "true" : "false", belowCaret ? "true" : "false"
             , inSameLine(currentSelection.visibleStart(), startOfEditableContent(currentSelection.visibleStart())) ? "true" : "false"
             , inSameLine(currentSelection.visibleEnd(), endOfEditableContent(currentSelection.visibleEnd())) ? "true" : "false"
@@ -234,7 +235,7 @@ void SelectionHandler::setCaretPosition(const WebCore::IntPoint &position)
 
     m_caretActive = true;
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setCaretPosition requested point %d, %d", position.x(), position.y());
+    SelectionLog(LogLevelInfo, "SelectionHandler::setCaretPosition requested point %d, %d", position.x(), position.y());
 
     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
     FrameSelection* controller = focusedFrame->selection();
@@ -242,7 +243,7 @@ void SelectionHandler::setCaretPosition(const WebCore::IntPoint &position)
     WebCore::IntRect currentCaretRect = controller->selection().visibleStart().absoluteCaretBounds();
 
     if (relativePoint == DOMSupport::InvalidPoint || !shouldUpdateSelectionOrCaretForPoint(relativePoint, currentCaretRect)) {
-        selectionPositionChanged();
+        selectionPositionChanged(true /* forceUpdateWithoutChange */);
         return;
     }
 
@@ -252,19 +253,21 @@ void SelectionHandler::setCaretPosition(const WebCore::IntPoint &position)
         if (unsigned short character = directionOfPointRelativeToRect(relativePoint, currentCaretRect))
             m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character));
 
-        selectionPositionChanged();
+        // Send the selection changed in case this does not trigger a selection change to
+        // ensure the caret position is accurate. This may be a duplicate event.
+        selectionPositionChanged(true /* forceUpdateWithoutChange */);
         return;
     }
 
     VisibleSelection newSelection(visibleCaretPosition);
     if (controller->selection() == newSelection) {
-        selectionPositionChanged();
+        selectionPositionChanged(true /* forceUpdateWithoutChange */);
         return;
     }
 
     controller->setSelection(newSelection);
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setCaretPosition point valid, cursor updated");
+    SelectionLog(LogLevelInfo, "SelectionHandler::setCaretPosition point valid, cursor updated");
 }
 
 // This function makes sure we are not reducing the selection to a caret selection.
@@ -402,7 +405,7 @@ bool SelectionHandler::updateOrHandleInputSelection(VisibleSelection& newSelecti
     // Check if the handle movement is valid.
     if (!shouldUpdateSelectionOrCaretForPoint(relativeStart, currentStartCaretRect, true /* startCaret */)
         || !shouldUpdateSelectionOrCaretForPoint(relativeEnd, currentEndCaretRect, false /* startCaret */)) {
-        selectionPositionChanged();
+        selectionPositionChanged(true /* forceUpdateWithoutChange */);
         return true;
     }
 
@@ -432,14 +435,14 @@ bool SelectionHandler::updateOrHandleInputSelection(VisibleSelection& newSelecti
     if (!character)
         return false;
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setSelection making selection change attempt using key event %d", character);
+    SelectionLog(LogLevelInfo, "SelectionHandler::updateOrHandleInputSelection making selection change attempt using key event %d", character);
 
     if (shouldExtendSelectionInDirection(controller->selection(), character))
         m_webPage->m_inputHandler->handleKeyboardInput(Platform::KeyboardEvent(character, Platform::KeyboardEvent::KeyDown, KEYMOD_SHIFT));
 
-    // Must send the selectionPositionChanged every time, sometimes this will duplicate but an accepted
-    // handleNavigationMove may not make an actual selection change.
-    selectionPositionChanged();
+    // Send the selection changed in case this does not trigger a selection change to
+    // ensure the caret position is accurate. This may be a duplicate event.
+    selectionPositionChanged(true /* forceUpdateWithoutChange */);
     return true;
 }
 
@@ -454,7 +457,11 @@ void SelectionHandler::setSelection(const WebCore::IntPoint& start, const WebCor
     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
     FrameSelection* controller = focusedFrame->selection();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setSelection adjusted points %d, %d, %d, %d", start.x(), start.y(), end.x(), end.y());
+#if SHOWDEBUG_SELECTIONHANDLER_TIMING
+    m_timer.start();
+#endif
+
+    SelectionLog(LogLevelInfo, "SelectionHandler::setSelection adjusted points %d, %d, %d, %d", start.x(), start.y(), end.x(), end.y());
 
     // Note that IntPoint(-1, -1) is being our sentinel so far for
     // clipped out selection starting or ending location.
@@ -508,7 +515,7 @@ void SelectionHandler::setSelection(const WebCore::IntPoint& start, const WebCor
     }
 
     if (controller->selection() == newSelection) {
-        selectionPositionChanged();
+        selectionPositionChanged(true /* forceUpdateWithoutChange */);
         return;
     }
 
@@ -520,20 +527,21 @@ void SelectionHandler::setSelection(const WebCore::IntPoint& start, const WebCor
 
     IntRectRegion unclippedRegion;
     regionForTextQuads(quads, unclippedRegion, false /* shouldClipToVisibleContent */);
-    if (!unclippedRegion.isEmpty()) {
-        // Check if the handles reversed position.
-        if (m_selectionActive && !newSelection.isBaseFirst())
-            m_webPage->m_client->notifySelectionHandlesReversed();
 
-        controller->setSelection(newSelection);
-
-        DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::setSelection selection points valid, selection updated\n");
-    } else {
+    if (unclippedRegion.isEmpty()) {
         // Requested selection results in an empty selection, skip this change.
-        selectionPositionChanged();
+        selectionPositionChanged(true /* forceUpdateWithoutChange */);
 
-        DEBUG_SELECTION(LogLevelWarn, "SelectionHandler::setSelection selection points invalid, selection not updated\n");
+        SelectionLog(LogLevelWarn, "SelectionHandler::setSelection selection points invalid, selection not updated.");
+        return;
     }
+
+    // Check if the handles reversed position.
+    if (m_selectionActive && !newSelection.isBaseFirst())
+        m_webPage->m_client->notifySelectionHandlesReversed();
+
+    controller->setSelection(newSelection);
+    SelectionLog(LogLevelInfo, "SelectionHandler::setSelection selection points valid, selection updated.");
 }
 
 // FIXME re-use this in context. Must be updated to include an option to return the href.
@@ -561,7 +569,7 @@ void SelectionHandler::selectAtPoint(const WebCore::IntPoint& location)
     WebCore::IntPoint targetPosition;
     // FIXME: Factory this get right fat finger code into a helper.
     const FatFingersResult lastFatFingersResult = m_webPage->m_touchEventHandler->lastFatFingersResult();
-    if (lastFatFingersResult.positionWasAdjusted() && lastFatFingersResult.nodeAsElementIfApplicable()) {
+    if (lastFatFingersResult.resultMatches(location, FatFingers::Text) && lastFatFingersResult.positionWasAdjusted() && lastFatFingersResult.nodeAsElementIfApplicable()) {
         targetNode = lastFatFingersResult.node(FatFingersResult::ShadowContentNotAllowed);
         targetPosition = lastFatFingersResult.adjustedPosition();
     } else {
@@ -597,14 +605,13 @@ static bool expandSelectionToGranularity(Frame* frame, VisibleSelection selectio
         selection = DOMSupport::visibleSelectionForClosestActualWordStart(selection);
 
     selection.expandUsingGranularity(granularity);
-    RefPtr<Range> newRange = selection.toNormalizedRange();
-    RefPtr<Range> oldRange = frame->selection()->selection().toNormalizedRange();
-    EAffinity affinity = frame->selection()->affinity();
+    selection.setAffinity(frame->selection()->affinity());
 
-    if (isInputMode && !frame->editor()->client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false))
+    if (isInputMode && !frame->selection()->shouldChangeSelection(selection))
         return false;
 
-    return frame->selection()->setSelectedRange(newRange.get(), affinity, true);
+    frame->selection()->setSelection(selection);
+    return true;
 }
 
 void SelectionHandler::selectObject(const WebCore::IntPoint& location, TextGranularity granularity)
@@ -613,7 +620,7 @@ void SelectionHandler::selectObject(const WebCore::IntPoint& location, TextGranu
     ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectObject adjusted points %d, %d", location.x(), location.y());
+    SelectionLog(LogLevelInfo, "SelectionHandler::selectObject adjusted points %d, %d", location.x(), location.y());
 
     WebCore::IntPoint relativePoint = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), focusedFrame, location);
     VisiblePosition pointLocation(focusedFrame->visiblePositionForPoint(relativePoint));
@@ -632,7 +639,7 @@ void SelectionHandler::selectObject(TextGranularity granularity)
     ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectObject using current selection");
+    SelectionLog(LogLevelInfo, "SelectionHandler::selectObject using current selection");
 
     // Use the current selection as the selection point.
     ASSERT(focusedFrame->selection()->selectionType() != VisibleSelection::NoSelection);
@@ -649,7 +656,7 @@ void SelectionHandler::selectObject(Node* node)
     ASSERT(m_webPage && m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->selection());
     Frame* focusedFrame = m_webPage->focusedOrMainFrame();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectNode");
+    SelectionLog(LogLevelInfo, "SelectionHandler::selectNode");
 
     VisibleSelection selection = VisibleSelection::selectionFromContentsOfNode(node);
     focusedFrame->selection()->setSelection(selection);
@@ -834,9 +841,9 @@ bool SelectionHandler::inputNodeOverridesTouch() const
 
 // Note: This is the only function in SelectionHandler in which the coordinate
 // system is not entirely WebKit.
-void SelectionHandler::selectionPositionChanged(bool visualChangeOnly)
+void SelectionHandler::selectionPositionChanged(bool forceUpdateWithoutChange)
 {
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::selectionPositionChanged visibleChangeOnly = %s", visualChangeOnly ? "true" : "false");
+    SelectionLog(LogLevelInfo, "SelectionHandler::selectionPositionChanged forceUpdateWithoutChange = %s", forceUpdateWithoutChange ? "true" : "false");
 
     // This method can get called during WebPage shutdown process.
     // If that is the case, just bail out since the client is not
@@ -865,6 +872,8 @@ void SelectionHandler::selectionPositionChanged(bool visualChangeOnly)
     else if (!m_selectionActive)
         return;
 
+    SelectionTimingLog(LogLevelInfo, "SelectionHandler::selectionPositionChanged starting at %f", m_timer.elapsed());
+
     WebCore::IntRect startCaret;
     WebCore::IntRect endCaret;
 
@@ -877,7 +886,7 @@ void SelectionHandler::selectionPositionChanged(bool visualChangeOnly)
 
     // If there is no change in selected text and the visual rects
     // have not changed then don't bother notifying anything.
-    if (visualChangeOnly && m_lastSelectionRegion.isEqual(unclippedRegion))
+    if (!forceUpdateWithoutChange && m_lastSelectionRegion.isEqual(unclippedRegion))
         return;
 
     m_lastSelectionRegion = unclippedRegion;
@@ -896,11 +905,11 @@ void SelectionHandler::selectionPositionChanged(bool visualChangeOnly)
 
         regionForTextQuads(quads, visibleSelectionRegion);
 
-#if SHOWDEBUG_SELECTIONHANDLER // Don't rely just on DEBUG_SELECTION to avoid loop.
+#if SHOWDEBUG_SELECTIONHANDLER // Don't rely just on SelectionLog to avoid loop.
         for (unsigned int i = 0; i < unclippedRegion.numRects(); i++)
-            DEBUG_SELECTION(LogLevelCritical, "Rect list - Unmodified #%d, (%d, %d) (%d x %d)", i, unclippedRegion.rects()[i].x(), unclippedRegion.rects()[i].y(), unclippedRegion.rects()[i].width(), unclippedRegion.rects()[i].height());
+            SelectionLog(LogLevelInfo, "Rect list - Unmodified #%d, (%d, %d) (%d x %d)", i, unclippedRegion.rects()[i].x(), unclippedRegion.rects()[i].y(), unclippedRegion.rects()[i].width(), unclippedRegion.rects()[i].height());
         for (unsigned int i = 0; i < visibleSelectionRegion.numRects(); i++)
-            DEBUG_SELECTION(LogLevelCritical, "Rect list  - Clipped to Visible #%d, (%d, %d) (%d x %d)", i, visibleSelectionRegion.rects()[i].x(), visibleSelectionRegion.rects()[i].y(), visibleSelectionRegion.rects()[i].width(), visibleSelectionRegion.rects()[i].height());
+            SelectionLog(LogLevelInfo, "Rect list  - Clipped to Visible #%d, (%d, %d) (%d x %d)", i, visibleSelectionRegion.rects()[i].x(), visibleSelectionRegion.rects()[i].y(), visibleSelectionRegion.rects()[i].width(), visibleSelectionRegion.rects()[i].height());
 #endif
 
         bool shouldCareAboutPossibleClippedOutSelection = frame != m_webPage->mainFrame() || m_webPage->m_inputHandler->isInputMode();
@@ -929,22 +938,28 @@ void SelectionHandler::selectionPositionChanged(bool visualChangeOnly)
         }
     }
 
-    DEBUG_SELECTION(BlackBerry::Platform::LogLevelInfo, "SelectionHandler::selectionPositionChanged Start Rect=(%d, %d) (%d x %d) End Rect=(%d, %d) (%d x %d)",
+    SelectionLog(BlackBerry::Platform::LogLevelInfo, "SelectionHandler::selectionPositionChanged Start Rect=(%d, %d) (%d x %d) End Rect=(%d, %d) (%d x %d)",
                     startCaret.x(), startCaret.y(), startCaret.width(), startCaret.height(), endCaret.x(), endCaret.y(), endCaret.width(), endCaret.height());
 
+    if (m_webPage->m_selectionOverlay)
+        m_webPage->m_selectionOverlay->draw(visibleSelectionRegion);
 
     m_webPage->m_client->notifySelectionDetailsChanged(startCaret, endCaret, visibleSelectionRegion, inputNodeOverridesTouch());
+    SelectionTimingLog(LogLevelInfo, "SelectionHandler::selectionPositionChanged completed at %f", m_timer.elapsed());
 }
 
 // NOTE: This function is not in WebKit coordinates.
 void SelectionHandler::caretPositionChanged()
 {
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::caretPositionChanged");
+    SelectionLog(LogLevelInfo, "SelectionHandler::caretPositionChanged");
 
     WebCore::IntRect caretLocation;
+    // If the input field is empty, we always turn off the caret.
     // If the input field is not active, we must be turning off the caret.
-    if (!m_webPage->m_inputHandler->isInputMode() && m_caretActive) {
-        m_caretActive = false;
+    bool emptyInputField = m_webPage->m_inputHandler->elementText().isEmpty();
+    if (emptyInputField || (!m_webPage->m_inputHandler->isInputMode() && m_caretActive)) {
+        if (!emptyInputField)
+            m_caretActive = false;
         // Send an empty caret change to turn off the caret.
         m_webPage->m_client->notifyCaretChanged(caretLocation, m_webPage->m_touchEventHandler->lastFatFingersResult().isTextInput() /* userTouchTriggered */);
         return;
@@ -955,19 +970,19 @@ void SelectionHandler::caretPositionChanged()
     // This function should only reach this point if input mode is active.
     ASSERT(m_webPage->m_inputHandler->isInputMode());
 
+    WebCore::IntPoint frameOffset(m_webPage->frameOffset(m_webPage->focusedOrMainFrame()));
+    WebCore::IntRect clippingRectForContent(clippingRectForVisibleContent());
     if (m_webPage->focusedOrMainFrame()->selection()->selectionType() == VisibleSelection::CaretSelection) {
-        WebCore::IntPoint frameOffset = m_webPage->frameOffset(m_webPage->focusedOrMainFrame());
-
         caretLocation = m_webPage->focusedOrMainFrame()->selection()->selection().visibleStart().absoluteCaretBounds();
         caretLocation.move(frameOffset.x(), frameOffset.y());
 
         // Clip against the containing frame and node boundaries.
-        caretLocation.intersect(clippingRectForVisibleContent());
+        caretLocation.intersect(clippingRectForContent);
     }
 
     m_caretActive = !caretLocation.isEmpty();
 
-    DEBUG_SELECTION(LogLevelInfo, "SelectionHandler::caretPositionChanged caret Rect %d, %d, %dx%d",
+    SelectionLog(LogLevelInfo, "SelectionHandler::caretPositionChanged caret Rect %d, %d, %dx%d",
                         caretLocation.x(), caretLocation.y(), caretLocation.width(), caretLocation.height());
 
     caretLocation = m_webPage->mapToTransformed(caretLocation);
@@ -976,6 +991,19 @@ void SelectionHandler::caretPositionChanged()
     bool singleLineInput = !m_webPage->m_inputHandler->isMultilineInputMode();
     WebCore::IntRect nodeBoundingBox = singleLineInput ? m_webPage->m_inputHandler->boundingBoxForInputField() : WebCore::IntRect();
 
+    if (!nodeBoundingBox.isEmpty()) {
+        nodeBoundingBox.move(frameOffset.x(), frameOffset.y());
+
+        // Clip against the containing frame and node boundaries.
+        nodeBoundingBox.intersect(clippingRectForContent);
+
+        nodeBoundingBox = m_webPage->mapToTransformed(nodeBoundingBox);
+        m_webPage->clipToTransformedContentsRect(nodeBoundingBox);
+    }
+
+    SelectionLog(LogLevelInfo, "SelectionHandler::single line %s single line bounding box %d, %d, %dx%d",
+                    singleLineInput ? "true" : "false", nodeBoundingBox.x(), nodeBoundingBox.y(), nodeBoundingBox.width(), nodeBoundingBox.height());
+
     m_webPage->m_client->notifyCaretChanged(caretLocation, m_webPage->m_touchEventHandler->lastFatFingersResult().isTextInput() /* userTouchTriggered */, singleLineInput, nodeBoundingBox);
 }