2 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
29 #import "EditorState.h"
30 #import "InteractionInformationAtPosition.h"
31 #import "WebChromeClient.h"
32 #import "WebCoreArgumentCoders.h"
34 #import "WebPageProxyMessages.h"
35 #import "WebProcess.h"
36 #import "WKGestureTypes.h"
37 #import <CoreText/CTFont.h>
38 #import <WebCore/Chrome.h>
39 #import <WebCore/Element.h>
40 #import <WebCore/EventHandler.h>
41 #import <WebCore/FocusController.h>
42 #import <WebCore/FloatQuad.h>
43 #import <WebCore/Frame.h>
44 #import <WebCore/FrameView.h>
45 #import <WebCore/HitTestResult.h>
46 #import <WebCore/HTMLElementTypeHelpers.h>
47 #import <WebCore/MainFrame.h>
48 #import <WebCore/Node.h>
49 #import <WebCore/NotImplemented.h>
50 #import <WebCore/Page.h>
51 #import <WebCore/PlatformKeyboardEvent.h>
52 #import <WebCore/PlatformMouseEvent.h>
53 #import <WebCore/RenderImage.h>
54 #import <WebCore/SharedBuffer.h>
55 #import <WebCore/TextIterator.h>
56 #import <WebCore/VisibleUnits.h>
57 #import <WebCore/WebEvent.h>
59 using namespace WebCore;
63 void WebPage::platformInitialize()
68 void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
73 bool WebPage::executeKeypressCommandsInternal(const Vector<WebCore::KeypressCommand>&, KeyboardEvent*)
79 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event, bool)
81 bool eventWasHandled = false;
82 bool sendResult = WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(editorState(), event->keyEvent()->type() == PlatformKeyboardEvent::Char),
83 Messages::WebPageProxy::InterpretKeyEvent::Reply(eventWasHandled), m_pageID);
87 return eventWasHandled;
90 void WebPage::sendComplexTextInputToPlugin(uint64_t, const String&)
95 void WebPage::setComposition(const String& text, Vector<WebCore::CompositionUnderline> underlines, uint64_t selectionStart, uint64_t selectionEnd)
97 Frame& frame = m_page->focusController().focusedOrMainFrame();
99 if (frame.selection().isContentEditable())
100 frame.editor().setComposition(text, underlines, selectionStart, selectionEnd);
103 void WebPage::confirmComposition()
105 Frame& frame = m_page->focusController().focusedOrMainFrame();
106 frame.editor().confirmComposition();
109 void WebPage::cancelComposition(EditorState&)
114 static PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
116 if (nsrange.location > INT_MAX)
118 if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
119 nsrange.length = INT_MAX - nsrange.location;
121 // our critical assumption is that we are only called by input methods that
122 // concentrate on a given area containing the selection
123 // We have to do this because of text fields and textareas. The DOM for those is not
124 // directly in the document DOM, so serialization is problematic. Our solution is
125 // to use the root editable element of the selection start as the positional base.
126 // That fits with AppKit's idea of an input context.
127 return TextIterator::rangeFromLocationAndLength(frame->selection().rootEditableElementOrDocumentElement(), nsrange.location, nsrange.length);
130 void WebPage::insertText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd)
132 Frame& frame = m_page->focusController().focusedOrMainFrame();
134 if (replacementRangeStart != NSNotFound) {
135 RefPtr<Range> replacementRange = convertToRange(&frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
136 if (replacementRange)
137 frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
140 if (!frame.editor().hasComposition()) {
141 // An insertText: might be handled by other responders in the chain if we don't handle it.
142 // One example is space bar that results in scrolling down the page.
143 frame.editor().insertText(text, 0);
145 frame.editor().confirmComposition(text);
148 void WebPage::insertDictatedText(const String&, uint64_t, uint64_t, const Vector<WebCore::DictationAlternative>&, bool&, EditorState&)
153 void WebPage::getMarkedRange(uint64_t&, uint64_t&)
158 void WebPage::getSelectedRange(uint64_t&, uint64_t&)
163 void WebPage::getAttributedSubstringFromRange(uint64_t, uint64_t, AttributedString&)
168 void WebPage::characterIndexForPoint(IntPoint, uint64_t&)
173 void WebPage::firstRectForCharacterRange(uint64_t, uint64_t, WebCore::IntRect&)
178 void WebPage::executeKeypressCommands(const Vector<WebCore::KeypressCommand>&, bool&, EditorState&)
183 void WebPage::performDictionaryLookupAtLocation(const FloatPoint&)
188 void WebPage::performDictionaryLookupForSelection(Frame*, const VisibleSelection&)
193 void WebPage::performDictionaryLookupForRange(Frame*, Range*, NSDictionary *)
198 bool WebPage::performNonEditingBehaviorForSelector(const String&, WebCore::KeyboardEvent*)
204 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
210 void WebPage::registerUIProcessAccessibilityTokens(const IPC::DataReference&, const IPC::DataReference&)
215 void WebPage::readSelectionFromPasteboard(const String&, bool&)
220 void WebPage::getStringSelectionForPasteboard(String&)
225 void WebPage::getDataSelectionForPasteboard(const String, SharedMemory::Handle&, uint64_t&)
230 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
236 bool WebPage::platformHasLocalDataForURL(const WebCore::URL&)
242 String WebPage::cachedSuggestedFilenameForURL(const URL&)
248 String WebPage::cachedResponseMIMETypeForURL(const URL&)
254 PassRefPtr<SharedBuffer> WebPage::cachedResponseDataForURL(const URL&)
260 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest&)
266 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent&, bool&)
271 void WebPage::acceptsFirstMouse(int, const WebKit::WebMouseEvent&, bool&)
276 void WebPage::computePagesForPrintingPDFDocument(uint64_t, const PrintInfo&, Vector<IntRect>&)
281 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef, PDFDocument *, const PrintInfo&, uint32_t, uint32_t)
286 void WebPage::advanceToNextMisspelling(bool)
291 void WebPage::handleTap(const IntPoint& point)
293 Frame& mainframe = m_page->mainFrame();
294 FloatPoint adjustedPoint;
295 mainframe.nodeRespondingToClickEvents(point, adjustedPoint);
296 IntPoint roundedAdjustedPoint = roundedIntPoint(adjustedPoint);
298 mainframe.eventHandler().mouseMoved(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, NoButton, PlatformEvent::MouseMoved, 0, false, false, false, false, 0));
299 mainframe.eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, false, false, false, false, 0));
300 mainframe.eventHandler().handleMouseReleaseEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, false, false, false, false, 0));
303 void WebPage::tapHighlightAtPosition(uint64_t requestID, const FloatPoint& position)
305 Frame& mainframe = m_page->mainFrame();
306 FloatPoint adjustedPoint;
307 Node* node = mainframe.nodeRespondingToClickEvents(position, adjustedPoint);
312 RenderObject *renderer = node->renderer();
314 Vector<FloatQuad> quads;
316 renderer->absoluteQuads(quads);
317 Color highlightColor = node->computedStyle()->tapHighlightColor();
319 RoundedRect::Radii borderRadii;
320 if (renderer->isBox()) {
321 RenderBox* box = toRenderBox(renderer);
322 borderRadii = box->borderRadii();
325 send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, highlightColor, quads, borderRadii.topLeft(), borderRadii.topRight(), borderRadii.bottomLeft(), borderRadii.bottomRight()));
329 void WebPage::blurAssistedNode()
331 if (m_assistedNode && m_assistedNode->isElementNode())
332 toElement(m_assistedNode.get())->blur();
335 static FloatQuad innerFrameQuad(Frame* frame, Node* assistedNode)
337 frame->document()->updateLayoutIgnorePendingStylesheets();
338 RenderObject* renderer;
339 if (assistedNode->hasTagName(HTMLNames::textareaTag) || assistedNode->hasTagName(HTMLNames::inputTag))
340 renderer = assistedNode->renderer();
342 renderer = assistedNode->rootEditableElement()->renderer();
347 RenderStyle& style = renderer->style();
348 IntRect boundingBox = renderer->absoluteBoundingBoxRect(true /* use transforms*/);
350 boundingBox.move(style.borderLeftWidth(), style.borderTopWidth());
351 boundingBox.setWidth(boundingBox.width() - style.borderLeftWidth() - style.borderRightWidth());
352 boundingBox.setHeight(boundingBox.height() - style.borderBottomWidth() - style.borderTopWidth());
354 return FloatQuad(boundingBox);
357 static IntPoint constrainPoint(const IntPoint& point, Frame* frame, Node* assistedNode)
359 const int DEFAULT_CONSTRAIN_INSET = 2;
360 IntRect innerFrame = innerFrameQuad(frame, assistedNode).enclosingBoundingBox();
361 IntPoint constrainedPoint = point;
363 int minX = innerFrame.x() + DEFAULT_CONSTRAIN_INSET;
364 int maxX = innerFrame.maxX() - DEFAULT_CONSTRAIN_INSET;
365 int minY = innerFrame.y() + DEFAULT_CONSTRAIN_INSET;
366 int maxY = innerFrame.maxY() - DEFAULT_CONSTRAIN_INSET;
368 if (point.x() < minX)
369 constrainedPoint.setX(minX);
370 else if (point.x() > maxX)
371 constrainedPoint.setX(maxX);
373 if (point.y() < minY)
374 constrainedPoint.setY(minY);
375 else if (point.y() >= maxY)
376 constrainedPoint.setY(maxY);
378 return constrainedPoint;
381 void WebPage::selectWithGesture(const IntPoint& point, uint32_t granularity, uint32_t gestureType, uint32_t gestureState, uint64_t callbackID)
383 Frame& frame = m_page->focusController().focusedOrMainFrame();
384 FloatPoint adjustedPoint(point);
386 IntPoint constrainedPoint = m_assistedNode ? constrainPoint(point, &frame, m_assistedNode.get()) : point;
387 VisiblePosition position = frame.visiblePositionForPoint(constrainedPoint);
388 if (position.isNull()) {
389 send(Messages::WebPageProxy::GestureCallback(point, gestureType, gestureState, 0, callbackID));
393 switch (static_cast<WKGestureType>(gestureType)) {
394 case WKGestureOneFingerTap:
396 VisiblePosition result;
397 // move the the position at the end of the word
398 if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
399 // Don't cross line boundaries.
401 } else if (withinTextUnitOfGranularity(position, WordGranularity, DirectionForward)) {
402 // The position lies within a word.
403 RefPtr<Range> wordRange = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionForward);
405 result = wordRange->startPosition();
406 if (distanceBetweenPositions(position, result) > 1)
407 result = wordRange->endPosition();
408 } else if (atBoundaryOfGranularity(position, WordGranularity, DirectionBackward)) {
409 // The position is at the end of a word.
412 // The position is not within a word.
413 // Go to the next boundary.
414 result = positionOfNextBoundaryOfGranularity(position, WordGranularity, DirectionForward);
416 // If there is no such boundary we go to the end of the element.
418 result = endOfEditableContent(position);
420 if (result.isNotNull())
421 range = Range::create(*frame.document(), result, result);
423 m_shouldReturnWordAtSelection = true;
428 range = Range::create(*frame.document(), position, position);
431 case WKGestureTapAndAHalf:
432 switch (static_cast<WKGestureRecognizerState>(gestureState)) {
433 case WKGestureRecognizerStateBegan:
434 range = wordRangeFromPosition(position);
435 m_currentWordRange = Range::create(*frame.document(), range->startPosition(), range->endPosition());
437 case WKGestureRecognizerStateChanged:
439 range = Range::create(*frame.document(), m_currentWordRange->startPosition(), m_currentWordRange->endPosition());
441 if (position < range->startPosition())
442 range->setStart(position.deepEquivalent(), ec);
443 if (position > range->endPosition())
444 range->setEnd(position.deepEquivalent(), ec);
447 case WKGestureRecognizerStateEnded:
448 case WKGestureRecognizerStateCancelled:
449 m_currentWordRange = nullptr;
451 case WKGestureRecognizerStateFailed:
452 case WKGestureRecognizerStatePossible:
453 ASSERT_NOT_REACHED();
457 case WKGestureOneFingerDoubleTap:
458 if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
459 // Double-tap at end of line only places insertion point there.
460 // This helps to get the callout for pasting at ends of lines,
461 // paragraphs, and documents.
462 range = Range::create(*frame.document(), position, position);
464 range = wordRangeFromPosition(position);
467 case WKGestureTwoFingerSingleTap:
468 // Single tap with two fingers selects the entire paragraph.
469 range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
472 case WKGestureOneFingerTripleTap:
473 if (atBoundaryOfGranularity(position, LineGranularity, DirectionForward)) {
474 // Triple-tap at end of line only places insertion point there.
475 // This helps to get the callout for pasting at ends of lines,
476 // paragraphs, and documents.
477 range = Range::create(*frame.document(), position, position);
479 range = enclosingTextUnitOfGranularity(position, ParagraphGranularity, DirectionForward);
482 case WKGestureMakeWebSelection:
483 // FIXME: Here we should implement the logic for block selections.
484 range = wordRangeFromPosition(position);
491 frame.selection().setSelectedRange(range.get(), position.affinity(), true);
493 send(Messages::WebPageProxy::GestureCallback(point, gestureType, gestureState, 0, callbackID));
494 m_shouldReturnWordAtSelection = false;
497 static PassRefPtr<Range> rangeForPosition(Frame* frame, const VisiblePosition& position, bool baseIsStart)
500 VisiblePosition result = position;
503 VisiblePosition selectionStart = frame->selection().selection().visibleStart();
504 bool wouldFlip = position <= selectionStart;
507 result = selectionStart.next();
509 if (result.isNotNull())
510 range = Range::create(*frame->document(), selectionStart, result);
512 VisiblePosition selectionEnd = frame->selection().selection().visibleEnd();
513 bool wouldFlip = position >= selectionEnd;
516 result = selectionEnd.previous();
518 if (result.isNotNull())
519 range = Range::create(*frame->document(), result, selectionEnd);
522 return range.release();
525 static PassRefPtr<Range> rangeAtWordBoundaryForPosition(Frame* frame, const VisiblePosition& position, bool baseIsStart, SelectionDirection direction)
527 SelectionDirection sameDirection = baseIsStart ? DirectionForward : DirectionBackward;
528 SelectionDirection oppositeDirection = baseIsStart ? DirectionBackward : DirectionForward;
529 VisiblePosition base = baseIsStart ? frame->selection().selection().visibleStart() : frame->selection().selection().visibleEnd();
530 VisiblePosition extent = baseIsStart ? frame->selection().selection().visibleEnd() : frame->selection().selection().visibleStart();
531 VisiblePosition initialExtent = position;
533 if (atBoundaryOfGranularity(extent, WordGranularity, sameDirection)) {
534 // This is a word boundary. Leave selection where it is.
538 if (atBoundaryOfGranularity(extent, WordGranularity, oppositeDirection)) {
539 // This is a word boundary in the wrong direction. Nudge the selection to a character before proceeding.
540 extent = baseIsStart ? extent.previous() : extent.next();
543 // Extend to the boundary of the word.
545 VisiblePosition wordBoundary = positionOfNextBoundaryOfGranularity(extent, WordGranularity, sameDirection);
546 if (wordBoundary.isNotNull()
547 && atBoundaryOfGranularity(wordBoundary, WordGranularity, sameDirection)
548 && initialExtent != wordBoundary) {
549 extent = wordBoundary;
550 return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base);
552 // Conversely, if the initial extent equals the current word boundary, then
553 // run the rest of this function to see if the selection should extend
554 // the other direction to the other word.
556 // If this is where the extent was initially, then iterate in the other direction in the document until we hit the next word.
557 while (extent.isNotNull()
558 && !atBoundaryOfGranularity(extent, WordGranularity, sameDirection)
560 && !atBoundaryOfGranularity(extent, LineBoundary, sameDirection)
561 && !atBoundaryOfGranularity(extent, LineBoundary, oppositeDirection)) {
562 extent = baseIsStart ? extent.next() : extent.previous();
565 // Don't let the smart extension make the extent equal the base.
566 // Expand out to word boundary.
567 if (extent.isNull() || extent == base)
568 extent = wordBoundary;
572 return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base);
575 void WebPage::updateSelectionWithTouches(const IntPoint& point, uint32_t touches, bool baseIsStart, uint64_t callbackID)
577 Frame& frame = m_page->focusController().focusedOrMainFrame();
578 VisiblePosition position = frame.visiblePositionForPoint(point);
579 if (position.isNull()) {
580 send(Messages::WebPageProxy::TouchesCallback(point, touches, callbackID));
585 VisiblePosition result;
587 switch (static_cast<WKSelectionTouch>(touches)) {
588 case WKSelectionTouchStarted:
589 case WKSelectionTouchEndedNotMoving:
592 case WKSelectionTouchEnded:
593 if (frame.selection().isContentEditable()) {
594 result = closestWordBoundaryForPosition(position);
595 if (result.isNotNull())
596 range = Range::create(*frame.document(), result, result);
598 range = rangeForPosition(&frame, position, baseIsStart);
601 case WKSelectionTouchEndedMovingForward:
602 range = rangeAtWordBoundaryForPosition(&frame, position, baseIsStart, DirectionForward);
605 case WKSelectionTouchEndedMovingBackward:
606 range = rangeAtWordBoundaryForPosition(&frame, position, baseIsStart, DirectionBackward);
609 case WKSelectionTouchMoved:
610 range = rangeForPosition(&frame, position, baseIsStart);
614 frame.selection().setSelectedRange(range.get(), position.affinity(), true);
616 send(Messages::WebPageProxy::TouchesCallback(point, touches, callbackID));
619 void WebPage::selectWithTwoTouches(const WebCore::IntPoint& from, const WebCore::IntPoint& to, uint32_t gestureType, uint32_t gestureState, uint64_t callbackID)
621 Frame& frame = m_page->focusController().focusedOrMainFrame();
622 VisiblePosition fromPosition = frame.visiblePositionForPoint(from);
623 VisiblePosition toPosition = frame.visiblePositionForPoint(to);
625 if (fromPosition.isNotNull() && toPosition.isNotNull()) {
626 if (fromPosition < toPosition)
627 range = Range::create(*frame.document(), fromPosition, toPosition);
629 range = Range::create(*frame.document(), toPosition, fromPosition);
630 frame.selection().setSelectedRange(range.get(), fromPosition.affinity(), true);
633 // We can use the same callback for the gestures with one point.
634 send(Messages::WebPageProxy::GestureCallback(from, gestureType, gestureState, 0, callbackID));
637 void WebPage::extendSelection(uint32_t granularity)
639 // For the moment we handle only WordGranularity.
640 if (granularity != WordGranularity)
643 Frame& frame = m_page->focusController().focusedOrMainFrame();
644 ASSERT(frame.selection().isCaret());
645 VisiblePosition position = frame.selection().selection().start();
646 frame.selection().setSelectedRange(wordRangeFromPosition(position).get(), position.affinity(), true);
649 void WebPage::requestAutocorrectionData(const String& textForAutocorrection, uint64_t callbackID)
652 Frame& frame = m_page->focusController().focusedOrMainFrame();
653 ASSERT(frame.selection().isCaret());
654 VisiblePosition position = frame.selection().selection().start();
655 Vector<SelectionRect> selectionRects;
657 range = wordRangeFromPosition(position);
658 String textForRange = plainText(range.get());
659 const unsigned maxSearchAttempts = 5;
660 for (size_t i = 0; i < maxSearchAttempts && textForRange != textForAutocorrection; ++i)
662 position = range->startPosition().previous();
663 if (position.isNull() || position == range->startPosition())
665 range = Range::create(*frame.document(), wordRangeFromPosition(position)->startPosition(), range->endPosition());
666 textForRange = plainText(range.get());
668 if (textForRange == textForAutocorrection)
669 range->collectSelectionRects(selectionRects);
671 Vector<FloatRect> rectsForText;
672 rectsForText.resize(selectionRects.size());
674 for (size_t i = 0; i < selectionRects.size(); i++)
675 rectsForText[i] = selectionRects[i].rect();
677 bool multipleFonts = false;
678 CTFontRef font = nil;
679 if (const SimpleFontData* fontData = frame.editor().fontForSelection(multipleFonts))
680 font = fontData->getCTFont();
682 CGFloat fontSize = CTFontGetSize(font);
683 uint64_t fontTraits = CTFontGetSymbolicTraits(font);
684 RetainPtr<NSString> fontName = adoptNS((NSString *)CTFontCopyFamilyName(font));
685 send(Messages::WebPageProxy::AutocorrectionDataCallback(rectsForText, fontName.get(), fontSize, fontTraits, callbackID));
688 void WebPage::applyAutocorrection(const String& correction, const String& originalText, uint64_t callbackID)
691 Frame& frame = m_page->focusController().focusedOrMainFrame();
692 ASSERT(frame.selection().isCaret());
693 VisiblePosition position = frame.selection().selection().start();
695 range = wordRangeFromPosition(position);
696 String textForRange = plainText(range.get());
697 if (textForRange != originalText) {
698 for (size_t i = 0; i < originalText.length(); ++i)
699 position = position.previous();
700 if (position.isNull())
701 position = startOfDocument(static_cast<Node*>(frame.document()->documentElement()));
702 range = Range::create(*frame.document(), position, frame.selection().selection().start());
704 textForRange = (range) ? plainText(range.get()) : emptyString();
705 unsigned loopCount = 0;
706 const unsigned maxPositionsAttempts = 10;
707 while (textForRange.length() && textForRange.length() > originalText.length() && loopCount < maxPositionsAttempts) {
708 position = position.next();
709 if (position.isNotNull() && position >= frame.selection().selection().start())
712 range = Range::create(*frame.document(), position, frame.selection().selection().start());
713 textForRange = (range) ? plainText(range.get()) : emptyString();
717 if (textForRange != originalText) {
718 send(Messages::WebPageProxy::StringCallback(String(), callbackID));
722 frame.selection().setSelectedRange(range.get(), UPSTREAM, true);
723 if (correction.length())
724 frame.editor().insertText(correction, 0);
726 frame.editor().deleteWithDirection(DirectionBackward, CharacterGranularity, false, true);
728 send(Messages::WebPageProxy::StringCallback(correction, callbackID));
731 static void computeAutocorrectionContext(Frame& frame, String& contextBefore, String& markedText, String& selectedText, String& contextAfter, uint64_t& location, uint64_t& length)
734 VisiblePosition startPosition = frame.selection().selection().start();
735 VisiblePosition endPosition = frame.selection().selection().end();
736 location = NSNotFound;
738 const unsigned minContextWordCount = 3;
739 const unsigned minContextLenght = 12;
740 const unsigned maxContextLength = 30;
742 if (frame.selection().isRange())
743 selectedText = plainText(frame.selection().selection().toNormalizedRange().get());
745 if (frame.editor().hasComposition()) {
746 range = Range::create(*frame.document(), frame.editor().compositionRange()->startPosition(), startPosition);
747 String markedTextBefore;
749 markedTextBefore = plainText(range.get());
750 range = Range::create(*frame.document(), endPosition, frame.editor().compositionRange()->endPosition());
751 String markedTextAfter;
753 markedTextAfter = plainText(range.get());
754 markedText = markedTextBefore + selectedText + markedTextAfter;
755 if (!markedText.isEmpty()) {
756 location = markedTextBefore.length();
757 length = selectedText.length();
760 if (startPosition != startOfEditableContent(startPosition)) {
761 VisiblePosition currentPosition = startPosition;
762 VisiblePosition previousPosition;
763 unsigned totalContextLength = 0;
764 for (unsigned i = 0; i < minContextWordCount; ++i) {
765 if (contextBefore.length() >= minContextLenght)
767 previousPosition = startOfWord(positionOfNextBoundaryOfGranularity(currentPosition, WordGranularity, DirectionBackward));
768 if (previousPosition.isNull())
770 String currentWord = plainText(Range::create(*frame.document(), previousPosition, currentPosition).get());
771 totalContextLength += currentWord.length();
772 if (totalContextLength >= maxContextLength)
774 currentPosition = previousPosition;
776 if (currentPosition.isNotNull() && currentPosition != startPosition) {
777 contextBefore = plainText(Range::create(*frame.document(), currentPosition, startPosition).get());
778 if (atBoundaryOfGranularity(currentPosition, ParagraphGranularity, DirectionBackward))
779 contextBefore = ASCIILiteral("\n ") + contextBefore;
783 if (endPosition != endOfEditableContent(endPosition)) {
784 VisiblePosition nextPosition;
785 if (!atBoundaryOfGranularity(endPosition, WordGranularity, DirectionForward) && withinTextUnitOfGranularity(endPosition, WordGranularity, DirectionForward))
786 nextPosition = positionOfNextBoundaryOfGranularity(endPosition, WordGranularity, DirectionForward);
787 if (nextPosition.isNotNull())
788 contextAfter = plainText(Range::create(*frame.document(), endPosition, nextPosition).get());
793 void WebPage::requestAutocorrectionContext(uint64_t callbackID)
795 String contextBefore;
802 computeAutocorrectionContext(m_page->focusController().focusedOrMainFrame(), contextBefore, markedText, selectedText, contextAfter, location, length);
804 send(Messages::WebPageProxy::AutocorrectionContextCallback(contextBefore, markedText, selectedText, contextAfter, location, length, callbackID));
807 void WebPage::getAutocorrectionContext(String& contextBefore, String& markedText, String& selectedText, String& contextAfter, uint64_t& location, uint64_t& length)
809 computeAutocorrectionContext(m_page->focusController().focusedOrMainFrame(), contextBefore, markedText, selectedText, contextAfter, location, length);
812 void WebPage::getPositionInformation(const IntPoint& point, InteractionInformationAtPosition& info)
814 FloatPoint adjustedPoint;
815 Node* hitNode = m_page->mainFrame().nodeRespondingToClickEvents(point, adjustedPoint);
818 info.nodeAtPositionIsAssistedNode = (hitNode == m_assistedNode);
820 info.clickableElementName = hitNode->nodeName();
822 const HTMLElement* element = toHTMLElement(hitNode);
826 if (element->renderer() && element->renderer()->isRenderImage()) {
827 URL url = toRenderImage(element->renderer())->cachedImage()->url();
828 if (!url.string().isNull())
829 info.url = url.string();
830 } else if (element->isLink())
831 info.url = element->getAttribute(HTMLNames::hrefAttr).string();
833 Frame& frame = m_page->mainFrame();
834 hitNode = frame.eventHandler().hitTestResultAtPoint((point), HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent).innerNode();
835 if (hitNode->isTextNode()) {
836 VisiblePosition position = frame.visiblePositionForPoint(point);
837 RefPtr<Range> range = wordRangeFromPosition(position);
839 range->collectSelectionRects(info.selectionRects);
841 // FIXME: implement the logic for the block selection.
847 void WebPage::requestPositionInformation(const IntPoint& point)
849 InteractionInformationAtPosition info;
851 getPositionInformation(point, info);
852 send(Messages::WebPageProxy::DidReceivePositionInformation(info));
855 void WebPage::elementDidFocus(WebCore::Node* node)
857 m_assistedNode = node;
858 if (node->hasTagName(WebCore::HTMLNames::inputTag) || node->hasTagName(WebCore::HTMLNames::textareaTag) || node->hasEditableStyle())
859 send(Messages::WebPageProxy::StartAssistingNode(WebCore::IntRect(), true, true));
862 void WebPage::elementDidBlur(WebCore::Node* node)
864 if (m_assistedNode == node) {
865 send(Messages::WebPageProxy::StopAssistingNode());
870 void WebPage::didFinishScrolling(const WebCore::FloatPoint& contentOffset)
872 m_page->mainFrame().view()->setScrollOffset(WebCore::IntPoint(contentOffset));
875 void WebPage::didFinishZooming(float newScale)
877 m_page->setPageScaleFactor(newScale, m_page->mainFrame().view()->scrollPosition());
880 } // namespace WebKit