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