• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
56using namespace WebCore;
57
58namespace WebKit {
59
60static PassRefPtr<Range> convertToRange(Frame*, NSRange);
61
62void 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
82void WebPage::platformPreferencesDidChange(const WebPreferencesStore&)
83{
84}
85
86typedef 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.
90static 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
105static 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
121static 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
130bool 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
166bool 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
216void 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
224void 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
241void WebPage::confirmComposition(EditorState& newState)
242{
243    Frame* frame = m_page->focusController()->focusedOrMainFrame();
244
245    frame->editor()->confirmComposition();
246
247    newState = editorState();
248}
249
250void 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
272void 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
289void 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
306void 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
334void 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
354PassRefPtr<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
372void 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
389void WebPage::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands, bool& handled, EditorState& newState)
390{
391    handled = executeKeypressCommandsInternal(commands, m_keyboardEventBeingInterpreted);
392    newState = editorState();
393}
394
395static 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
412static 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
421void 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
471void 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
500void 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
534bool 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
570bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
571{
572    return false;
573}
574
575void 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
588void 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
599void 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
610AccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
611{
612    return m_mockAccessibilityElement.get();
613}
614
615bool 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
631String 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
647bool 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
656void WebPage::setDragSource(NSObject *dragSource)
657{
658    m_dragSource = dragSource;
659}
660
661void 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
671void 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
683void 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