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