Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      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.
     12  *
     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.
     24  */
     25 
     26 #import "config.h"
     27 #import "WebPage.h"
     28 
     29 #import "AccessibilityWebPageObject.h"
     30 #import "AttributedString.h"
     31 #import "DataReference.h"
     32 #import "EditorState.h"
     33 #import "PluginView.h"
     34 #import "WebCoreArgumentCoders.h"
     35 #import "WebEvent.h"
     36 #import "WebEventConversion.h"
     37 #import "WebFrame.h"
     38 #import "WebPageProxyMessages.h"
     39 #import "WebProcess.h"
     40 #import <WebCore/AXObjectCache.h>
     41 #import <WebCore/FocusController.h>
     42 #import <WebCore/Frame.h>
     43 #import <WebCore/FrameView.h>
     44 #import <WebCore/HitTestResult.h>
     45 #import <WebCore/HTMLConverter.h>
     46 #import <WebCore/KeyboardEvent.h>
     47 #import <WebCore/Page.h>
     48 #import <WebCore/PlatformKeyboardEvent.h>
     49 #import <WebCore/ResourceHandle.h>
     50 #import <WebCore/ScrollView.h>
     51 #import <WebCore/TextIterator.h>
     52 #import <WebCore/WindowsKeyboardCodes.h>
     53 #import <WebCore/visible_units.h>
     54 #import <WebKitSystemInterface.h>
     55 
     56 using namespace WebCore;
     57 
     58 namespace WebKit {
     59 
     60 static PassRefPtr<Range> convertToRange(Frame*, NSRange);
     61 
     62 void WebPage::platformInitialize()
     63 {
     64     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
     65 
     66 #if !defined(BUILDING_ON_SNOW_LEOPARD)
     67     AccessibilityWebPageObject* mockAccessibilityElement = [[[AccessibilityWebPageObject alloc] init] autorelease];
     68 
     69     // Get the pid for the starting process.
     70     pid_t pid = WebProcess::shared().presenterApplicationPid();
     71     WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
     72     [mockAccessibilityElement setWebPage:this];
     73 
     74     // send data back over
     75     NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement);
     76     CoreIPC::DataReference dataToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
     77     send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
     78     m_mockAccessibilityElement = mockAccessibilityElement;
     79 #endif
     80 }
     81 
     82 void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
     83 {
     84 }
     85 
     86 typedef HashMap<String, String> SelectorNameMap;
     87 
     88 // Map selectors into Editor command names.
     89 // This is not needed for any selectors that have the same name as the Editor command.
     90 static const SelectorNameMap* createSelectorExceptionMap()
     91 {
     92     SelectorNameMap* map = new HashMap<String, String>;
     93 
     94     map->add("insertNewlineIgnoringFieldEditor:", "InsertNewline");
     95     map->add("insertParagraphSeparator:", "InsertNewline");
     96     map->add("insertTabIgnoringFieldEditor:", "InsertTab");
     97     map->add("pageDown:", "MovePageDown");
     98     map->add("pageDownAndModifySelection:", "MovePageDownAndModifySelection");
     99     map->add("pageUp:", "MovePageUp");
    100     map->add("pageUpAndModifySelection:", "MovePageUpAndModifySelection");
    101 
    102     return map;
    103 }
    104 
    105 static String commandNameForSelectorName(const String& selectorName)
    106 {
    107     // Check the exception map first.
    108     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
    109     SelectorNameMap::const_iterator it = exceptionMap->find(selectorName);
    110     if (it != exceptionMap->end())
    111         return it->second;
    112 
    113     // Remove the trailing colon.
    114     // No need to capitalize the command name since Editor command names are not case sensitive.
    115     size_t selectorNameLength = selectorName.length();
    116     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
    117         return String();
    118     return selectorName.left(selectorNameLength - 1);
    119 }
    120 
    121 static Frame* frameForEvent(KeyboardEvent* event)
    122 {
    123     Node* node = event->target()->toNode();
    124     ASSERT(node);
    125     Frame* frame = node->document()->frame();
    126     ASSERT(frame);
    127     return frame;
    128 }
    129 
    130 bool WebPage::executeKeypressCommandsInternal(const Vector<WebCore::KeypressCommand>& commands, KeyboardEvent* event)
    131 {
    132     Frame* frame = frameForEvent(event);
    133     ASSERT(frame->page() == corePage());
    134 
    135     bool eventWasHandled = false;
    136     for (size_t i = 0; i < commands.size(); ++i) {
    137         if (commands[i].commandName == "insertText:") {
    138             ASSERT(!frame->editor()->hasComposition());
    139 
    140             if (!frame->editor()->canEdit())
    141                 continue;
    142 
    143             // An insertText: might be handled by other responders in the chain if we don't handle it.
    144             // One example is space bar that results in scrolling down the page.
    145             eventWasHandled |= frame->editor()->insertText(commands[i].text, event);
    146         } else {
    147             Editor::Command command = frame->editor()->command(commandNameForSelectorName(commands[i].commandName));
    148             if (command.isSupported()) {
    149                 bool commandExecutedByEditor = command.execute(event);
    150                 eventWasHandled |= commandExecutedByEditor;
    151                 if (!commandExecutedByEditor) {
    152                     bool performedNonEditingBehavior = event->keyEvent()->type() == PlatformKeyboardEvent::RawKeyDown && performNonEditingBehaviorForSelector(commands[i].commandName);
    153                     eventWasHandled |= performedNonEditingBehavior;
    154                 }
    155             } else {
    156                 bool commandWasHandledByUIProcess = false;
    157                 WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::ExecuteSavedCommandBySelector(commands[i].commandName),
    158                     Messages::WebPageProxy::ExecuteSavedCommandBySelector::Reply(commandWasHandledByUIProcess), m_pageID);
    159                 eventWasHandled |= commandWasHandledByUIProcess;
    160             }
    161         }
    162     }
    163     return eventWasHandled;
    164 }
    165 
    166 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event, bool saveCommands)
    167 {
    168     ASSERT(!saveCommands || event->keypressCommands().isEmpty()); // Save commands once for each event.
    169 
    170     Frame* frame = frameForEvent(event);
    171 
    172     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
    173     if (!platformEvent)
    174         return false;
    175     Vector<KeypressCommand>& commands = event->keypressCommands();
    176 
    177     if ([platformEvent->macEvent() type] == NSFlagsChanged)
    178         return false;
    179 
    180     bool eventWasHandled = false;
    181 
    182     if (saveCommands) {
    183         KeyboardEvent* oldEvent = m_keyboardEventBeingInterpreted;
    184         m_keyboardEventBeingInterpreted = event;
    185         bool sendResult = WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::InterpretQueuedKeyEvent(editorState()),
    186             Messages::WebPageProxy::InterpretQueuedKeyEvent::Reply(eventWasHandled, commands), m_pageID);
    187         m_keyboardEventBeingInterpreted = oldEvent;
    188         if (!sendResult)
    189             return false;
    190 
    191         // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline.
    192         // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that
    193         // should be handled like normal text input after DOM event dispatch.
    194         if (!event->keypressCommands().isEmpty())
    195             return false;
    196     } else {
    197         // Are there commands that could just cause text insertion if executed via Editor?
    198         // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
    199         // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
    200         // (e.g. Tab that inserts a Tab character, or Enter).
    201         bool haveTextInsertionCommands = false;
    202         for (size_t i = 0; i < commands.size(); ++i) {
    203             if (frame->editor()->command(commandNameForSelectorName(commands[i].commandName)).isTextInsertion())
    204                 haveTextInsertionCommands = true;
    205         }
    206         // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
    207         // Keypress (Char event) handler is the latest opportunity to execute.
    208         if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char) {
    209             eventWasHandled = executeKeypressCommandsInternal(event->keypressCommands(), event);
    210             event->keypressCommands().clear();
    211         }
    212     }
    213     return eventWasHandled;
    214 }
    215 
    216 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
    217 {
    218     for (HashSet<PluginView*>::const_iterator it = m_pluginViews.begin(), end = m_pluginViews.end(); it != end; ++it) {
    219         if ((*it)->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
    220             break;
    221     }
    222 }
    223 
    224 void WebPage::setComposition(const String& text, Vector<CompositionUnderline> underlines, uint64_t selectionStart, uint64_t selectionEnd, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, EditorState& newState)
    225 {
    226     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    227 
    228     if (frame->selection()->isContentEditable()) {
    229         RefPtr<Range> replacementRange;
    230         if (replacementRangeStart != NSNotFound) {
    231             replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
    232             frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
    233         }
    234 
    235         frame->editor()->setComposition(text, underlines, selectionStart, selectionEnd);
    236     }
    237 
    238     newState = editorState();
    239 }
    240 
    241 void WebPage::confirmComposition(EditorState& newState)
    242 {
    243     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    244 
    245     frame->editor()->confirmComposition();
    246 
    247     newState = editorState();
    248 }
    249 
    250 void WebPage::insertText(const String& text, uint64_t replacementRangeStart, uint64_t replacementRangeEnd, bool& handled, EditorState& newState)
    251 {
    252     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    253 
    254     RefPtr<Range> replacementRange;
    255     if (replacementRangeStart != NSNotFound) {
    256         replacementRange = convertToRange(frame, NSMakeRange(replacementRangeStart, replacementRangeEnd - replacementRangeStart));
    257         frame->selection()->setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
    258     }
    259 
    260     if (!frame->editor()->hasComposition()) {
    261         // An insertText: might be handled by other responders in the chain if we don't handle it.
    262         // One example is space bar that results in scrolling down the page.
    263         handled = frame->editor()->insertText(text, m_keyboardEventBeingInterpreted);
    264     } else {
    265         handled = true;
    266         frame->editor()->confirmComposition(text);
    267     }
    268 
    269     newState = editorState();
    270 }
    271 
    272 void WebPage::getMarkedRange(uint64_t& location, uint64_t& length)
    273 {
    274     location = NSNotFound;
    275     length = 0;
    276     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    277     if (!frame)
    278         return;
    279 
    280     RefPtr<Range> range = frame->editor()->compositionRange();
    281     size_t locationSize;
    282     size_t lengthSize;
    283     if (range && TextIterator::locationAndLengthFromRange(range.get(), locationSize, lengthSize)) {
    284         location = static_cast<uint64_t>(locationSize);
    285         length = static_cast<uint64_t>(lengthSize);
    286     }
    287 }
    288 
    289 void WebPage::getSelectedRange(uint64_t& location, uint64_t& length)
    290 {
    291     location = NSNotFound;
    292     length = 0;
    293     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    294     if (!frame)
    295         return;
    296 
    297     size_t locationSize;
    298     size_t lengthSize;
    299     RefPtr<Range> range = frame->selection()->toNormalizedRange();
    300     if (range && TextIterator::locationAndLengthFromRange(range.get(), locationSize, lengthSize)) {
    301         location = static_cast<uint64_t>(locationSize);
    302         length = static_cast<uint64_t>(lengthSize);
    303     }
    304 }
    305 
    306 void WebPage::getAttributedSubstringFromRange(uint64_t location, uint64_t length, AttributedString& result)
    307 {
    308     NSRange nsRange = NSMakeRange(location, length - location);
    309 
    310     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    311     if (!frame)
    312         return;
    313 
    314     if (frame->selection()->isNone() || !frame->selection()->isContentEditable() || frame->selection()->isInPasswordField())
    315         return;
    316 
    317     RefPtr<Range> range = convertToRange(frame, nsRange);
    318     if (!range)
    319         return;
    320 
    321     result.string = [WebHTMLConverter editingAttributedStringFromRange:range.get()];
    322     NSAttributedString* attributedString = result.string.get();
    323 
    324     // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing
    325     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
    326     // To work around this we truncate the resultant string to the correct length.
    327     if ([attributedString length] > nsRange.length) {
    328         ASSERT([attributedString length] == nsRange.length + 1);
    329         ASSERT([[attributedString string] characterAtIndex:nsRange.length] == '\n' || [[attributedString string] characterAtIndex:nsRange.length] == ' ');
    330         result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, nsRange.length)];
    331     }
    332 }
    333 
    334 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
    335 {
    336     index = NSNotFound;
    337     Frame* frame = m_page->mainFrame();
    338     if (!frame)
    339         return;
    340 
    341     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
    342     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
    343 
    344     RefPtr<Range> range = frame->rangeForPoint(result.point());
    345     if (!range)
    346         return;
    347 
    348     size_t location;
    349     size_t length;
    350     if (TextIterator::locationAndLengthFromRange(range.get(), location, length))
    351         index = static_cast<uint64_t>(location);
    352 }
    353 
    354 PassRefPtr<Range> convertToRange(Frame* frame, NSRange nsrange)
    355 {
    356     if (nsrange.location > INT_MAX)
    357         return 0;
    358     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
    359         nsrange.length = INT_MAX - nsrange.location;
    360 
    361     // our critical assumption is that we are only called by input methods that
    362     // concentrate on a given area containing the selection
    363     // We have to do this because of text fields and textareas. The DOM for those is not
    364     // directly in the document DOM, so serialization is problematic. Our solution is
    365     // to use the root editable element of the selection start as the positional base.
    366     // That fits with AppKit's idea of an input context.
    367     Element* selectionRoot = frame->selection()->rootEditableElement();
    368     Element* scope = selectionRoot ? selectionRoot : frame->document()->documentElement();
    369     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
    370 }
    371 
    372 void WebPage::firstRectForCharacterRange(uint64_t location, uint64_t length, WebCore::IntRect& resultRect)
    373 {
    374     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    375     resultRect.setLocation(IntPoint(0, 0));
    376     resultRect.setSize(IntSize(0, 0));
    377 
    378     RefPtr<Range> range = convertToRange(frame, NSMakeRange(location, length));
    379     if (!range)
    380         return;
    381 
    382     ASSERT(range->startContainer());
    383     ASSERT(range->endContainer());
    384 
    385     IntRect rect = frame->editor()->firstRectForRange(range.get());
    386     resultRect = frame->view()->contentsToWindow(rect);
    387 }
    388 
    389 void WebPage::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands, bool& handled, EditorState& newState)
    390 {
    391     handled = executeKeypressCommandsInternal(commands, m_keyboardEventBeingInterpreted);
    392     newState = editorState();
    393 }
    394 
    395 static bool isPositionInRange(const VisiblePosition& position, Range* range)
    396 {
    397     RefPtr<Range> positionRange = makeRange(position, position);
    398 
    399     ExceptionCode ec = 0;
    400     range->compareBoundaryPoints(Range::START_TO_START, positionRange.get(), ec);
    401     if (ec)
    402         return false;
    403 
    404     if (!range->isPointInRange(positionRange->startContainer(), positionRange->startOffset(), ec))
    405         return false;
    406     if (ec)
    407         return false;
    408 
    409     return true;
    410 }
    411 
    412 static bool shouldUseSelection(const VisiblePosition& position, const VisibleSelection& selection)
    413 {
    414     RefPtr<Range> selectedRange = selection.toNormalizedRange();
    415     if (!selectedRange)
    416         return false;
    417 
    418     return isPositionInRange(position, selectedRange.get());
    419 }
    420 
    421 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
    422 {
    423     Frame* frame = m_page->mainFrame();
    424     if (!frame)
    425         return;
    426 
    427     // Find the frame the point is over.
    428     IntPoint point = roundedIntPoint(floatPoint);
    429     HitTestResult result = frame->eventHandler()->hitTestResultAtPoint(point, false);
    430     frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : m_page->focusController()->focusedOrMainFrame();
    431 
    432     IntPoint translatedPoint = frame->view()->windowToContents(point);
    433 
    434     // Don't do anything if there is no character at the point.
    435     if (!frame->rangeForPoint(translatedPoint))
    436         return;
    437 
    438     VisiblePosition position = frame->visiblePositionForPoint(translatedPoint);
    439     VisibleSelection selection = m_page->focusController()->focusedOrMainFrame()->selection()->selection();
    440     if (shouldUseSelection(position, selection)) {
    441         performDictionaryLookupForSelection(DictionaryPopupInfo::HotKey, frame, selection);
    442         return;
    443     }
    444 
    445     NSDictionary *options = nil;
    446 
    447 #if !defined(BUILDING_ON_SNOW_LEOPARD)
    448     // As context, we are going to use the surrounding paragraph of text.
    449     VisiblePosition paragraphStart = startOfParagraph(position);
    450     VisiblePosition paragraphEnd = endOfParagraph(position);
    451 
    452     NSRange rangeToPass = NSMakeRange(TextIterator::rangeLength(makeRange(paragraphStart, position).get()), 0);
    453 
    454     RefPtr<Range> fullCharacterRange = makeRange(paragraphStart, paragraphEnd);
    455     String fullPlainTextString = plainText(fullCharacterRange.get());
    456 
    457     NSRange extractedRange = WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
    458 
    459     RefPtr<Range> finalRange = TextIterator::subrange(fullCharacterRange.get(), extractedRange.location, extractedRange.length);
    460     if (!finalRange)
    461         return;
    462 #else
    463     RefPtr<Range> finalRange = makeRange(startOfWord(position), endOfWord(position));
    464     if (!finalRange)
    465         return;
    466 #endif
    467 
    468     performDictionaryLookupForRange(DictionaryPopupInfo::HotKey, frame, finalRange.get(), options);
    469 }
    470 
    471 void WebPage::performDictionaryLookupForSelection(DictionaryPopupInfo::Type type, Frame* frame, const VisibleSelection& selection)
    472 {
    473     RefPtr<Range> selectedRange = selection.toNormalizedRange();
    474     if (!selectedRange)
    475         return;
    476 
    477     NSDictionary *options = nil;
    478 
    479 #if !defined(BUILDING_ON_SNOW_LEOPARD)
    480     VisiblePosition selectionStart = selection.visibleStart();
    481     VisiblePosition selectionEnd = selection.visibleEnd();
    482 
    483     // As context, we are going to use the surrounding paragraphs of text.
    484     VisiblePosition paragraphStart = startOfParagraph(selectionStart);
    485     VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
    486 
    487     int lengthToSelectionStart = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
    488     int lengthToSelectionEnd = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get());
    489     NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, lengthToSelectionEnd - lengthToSelectionStart);
    490 
    491     String fullPlainTextString = plainText(makeRange(paragraphStart, paragraphEnd).get());
    492 
    493     // Since we already have the range we want, we just need to grab the returned options.
    494     WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
    495 #endif
    496 
    497     performDictionaryLookupForRange(type, frame, selectedRange.get(), options);
    498 }
    499 
    500 void WebPage::performDictionaryLookupForRange(DictionaryPopupInfo::Type type, Frame* frame, Range* range, NSDictionary *options)
    501 {
    502     String rangeText = range->text();
    503     if (rangeText.stripWhiteSpace().isEmpty())
    504         return;
    505 
    506     RenderObject* renderer = range->startContainer()->renderer();
    507     RenderStyle* style = renderer->style();
    508     NSFont *font = style->font().primaryFont()->getNSFont();
    509     if (!font)
    510         return;
    511 
    512     CFDictionaryRef fontDescriptorAttributes = (CFDictionaryRef)[[font fontDescriptor] fontAttributes];
    513     if (!fontDescriptorAttributes)
    514         return;
    515 
    516     Vector<FloatQuad> quads;
    517     range->textQuads(quads);
    518     if (quads.isEmpty())
    519         return;
    520 
    521     IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
    522 
    523     DictionaryPopupInfo dictionaryPopupInfo;
    524     dictionaryPopupInfo.type = type;
    525     dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y());
    526     dictionaryPopupInfo.fontInfo.fontAttributeDictionary = fontDescriptorAttributes;
    527 #if !defined(BUILDING_ON_SNOW_LEOPARD)
    528     dictionaryPopupInfo.options = (CFDictionaryRef)options;
    529 #endif
    530 
    531     send(Messages::WebPageProxy::DidPerformDictionaryLookup(rangeText, dictionaryPopupInfo));
    532 }
    533 
    534 bool WebPage::performNonEditingBehaviorForSelector(const String& selector)
    535 {
    536     // FIXME: All these selectors have corresponding Editor commands, but the commands only work in editable content.
    537     // Should such non-editing behaviors be implemented in Editor or EventHandler::defaultArrowEventHandler() perhaps?
    538     if (selector == "moveUp:")
    539         scroll(m_page.get(), ScrollUp, ScrollByLine);
    540     else if (selector == "moveToBeginningOfParagraph:")
    541         scroll(m_page.get(), ScrollUp, ScrollByPage);
    542     else if (selector == "moveToBeginningOfDocument:") {
    543         scroll(m_page.get(), ScrollUp, ScrollByDocument);
    544         scroll(m_page.get(), ScrollLeft, ScrollByDocument);
    545     } else if (selector == "moveDown:")
    546         scroll(m_page.get(), ScrollDown, ScrollByLine);
    547     else if (selector == "moveToEndOfParagraph:")
    548         scroll(m_page.get(), ScrollDown, ScrollByPage);
    549     else if (selector == "moveToEndOfDocument:") {
    550         scroll(m_page.get(), ScrollDown, ScrollByDocument);
    551         scroll(m_page.get(), ScrollLeft, ScrollByDocument);
    552     } else if (selector == "moveLeft:")
    553         scroll(m_page.get(), ScrollLeft, ScrollByLine);
    554     else if (selector == "moveWordLeft:")
    555         scroll(m_page.get(), ScrollLeft, ScrollByPage);
    556     else if (selector == "moveToLeftEndOfLine:")
    557         m_page->goBack();
    558     else if (selector == "moveRight:")
    559         scroll(m_page.get(), ScrollRight, ScrollByLine);
    560     else if (selector == "moveWordRight:")
    561         scroll(m_page.get(), ScrollRight, ScrollByPage);
    562     else if (selector == "moveToRightEndOfLine:")
    563         m_page->goForward();
    564     else
    565         return false;
    566 
    567     return true;
    568 }
    569 
    570 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
    571 {
    572     return false;
    573 }
    574 
    575 void WebPage::registerUIProcessAccessibilityTokens(const CoreIPC::DataReference& elementToken, const CoreIPC::DataReference& windowToken)
    576 {
    577 #if !defined(BUILDING_ON_SNOW_LEOPARD)
    578     NSData* elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
    579     NSData* windowTokenData = [NSData dataWithBytes:windowToken.data() length:windowToken.size()];
    580     id remoteElement = WKAXRemoteElementForToken(elementTokenData);
    581     id remoteWindow = WKAXRemoteElementForToken(windowTokenData);
    582     WKAXSetWindowForRemoteElement(remoteWindow, remoteElement);
    583 
    584     [accessibilityRemoteObject() setRemoteParent:remoteElement];
    585 #endif
    586 }
    587 
    588 void WebPage::writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes, bool& result)
    589 {
    590     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    591     if (!frame || frame->selection()->isNone()) {
    592         result = false;
    593         return;
    594     }
    595     frame->editor()->writeSelectionToPasteboard(pasteboardName, pasteboardTypes);
    596     result = true;
    597 }
    598 
    599 void WebPage::readSelectionFromPasteboard(const String& pasteboardName, bool& result)
    600 {
    601     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    602     if (!frame || frame->selection()->isNone()) {
    603         result = false;
    604         return;
    605     }
    606     frame->editor()->readSelectionFromPasteboard(pasteboardName);
    607     result = true;
    608 }
    609 
    610 AccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
    611 {
    612     return m_mockAccessibilityElement.get();
    613 }
    614 
    615 bool WebPage::platformHasLocalDataForURL(const WebCore::KURL& url)
    616 {
    617     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
    618     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
    619     NSCachedURLResponse *cachedResponse;
    620 #if USE(CFURLSTORAGESESSIONS)
    621     if (CFURLStorageSessionRef storageSession = ResourceHandle::privateBrowsingStorageSession())
    622         cachedResponse = WKCachedResponseForRequest(storageSession, request);
    623     else
    624 #endif
    625         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
    626     [request release];
    627 
    628     return cachedResponse;
    629 }
    630 
    631 String WebPage::cachedResponseMIMETypeForURL(const WebCore::KURL& url)
    632 {
    633     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
    634     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
    635     NSCachedURLResponse *cachedResponse;
    636 #if USE(CFURLSTORAGESESSIONS)
    637     if (CFURLStorageSessionRef storageSession = ResourceHandle::privateBrowsingStorageSession())
    638         cachedResponse = WKCachedResponseForRequest(storageSession, request);
    639     else
    640 #endif
    641         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
    642     [request release];
    643 
    644     return [[cachedResponse response] MIMEType];
    645 }
    646 
    647 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest& request)
    648 {
    649     if ([NSURLConnection canHandleRequest:request.nsURLRequest()])
    650         return true;
    651 
    652     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
    653     return request.url().protocolIs("applewebdata");
    654 }
    655 
    656 void WebPage::setDragSource(NSObject *dragSource)
    657 {
    658     m_dragSource = dragSource;
    659 }
    660 
    661 void WebPage::platformDragEnded()
    662 {
    663     // The draggedImage method releases its responder; we retain here to balance that.
    664     [m_dragSource.get() retain];
    665     // The drag source we care about here is NSFilePromiseDragSource, which doesn't look at
    666     // the arguments. It's OK to just pass arbitrary constant values, so we just pass all zeroes.
    667     [m_dragSource.get() draggedImage:nil endedAt:NSZeroPoint operation:NSDragOperationNone];
    668     m_dragSource = nullptr;
    669 }
    670 
    671 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent& event, bool& result)
    672 {
    673     result = false;
    674     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    675     if (!frame)
    676         return;
    677 
    678     HitTestResult hitResult = frame->eventHandler()->hitTestResultAtPoint(event.position(), true);
    679     if (hitResult.isSelected())
    680         result = frame->eventHandler()->eventMayStartDrag(platform(event));
    681 }
    682 
    683 void WebPage::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event, bool& result)
    684 {
    685     result = false;
    686     Frame* frame = m_page->focusController()->focusedOrMainFrame();
    687     if (!frame)
    688         return;
    689 
    690     HitTestResult hitResult = frame->eventHandler()->hitTestResultAtPoint(event.position(), true);
    691     frame->eventHandler()->setActivationEventNumber(eventNumber);
    692     if (hitResult.isSelected())
    693         result = frame->eventHandler()->eventMayStartDrag(platform(event));
    694     else
    695         result = !!hitResult.scrollbar();
    696 }
    697 
    698 } // namespace WebKit
    699