Another build fix after r166879.
[WebKit-https.git] / Source / WebKit2 / WebProcess / WebPage / mac / WebPageMac.mm
1 /*
2  * Copyright (C) 2010, 2011, 2012 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 #if PLATFORM(MAC)
30
31 #import "AttributedString.h"
32 #import "DataReference.h"
33 #import "EditingRange.h"
34 #import "EditorState.h"
35 #import "PDFKitImports.h"
36 #import "PageBanner.h"
37 #import "PluginView.h"
38 #import "PrintInfo.h"
39 #import "WKAccessibilityWebPageObjectMac.h"
40 #import "WebCoreArgumentCoders.h"
41 #import "WebEvent.h"
42 #import "WebEventConversion.h"
43 #import "WebFrame.h"
44 #import "WebImage.h"
45 #import "WebInspector.h"
46 #import "WebPageProxyMessages.h"
47 #import "WebPreferencesStore.h"
48 #import "WebProcess.h"
49 #import <PDFKit/PDFKit.h>
50 #import <QuartzCore/QuartzCore.h>
51 #import <WebCore/AXObjectCache.h>
52 #import <WebCore/BackForwardController.h>
53 #import <WebCore/EventHandler.h>
54 #import <WebCore/FocusController.h>
55 #import <WebCore/FrameLoader.h>
56 #import <WebCore/FrameView.h>
57 #import <WebCore/HTMLConverter.h>
58 #import <WebCore/HitTestResult.h>
59 #import <WebCore/KeyboardEvent.h>
60 #import <WebCore/MIMETypeRegistry.h>
61 #import <WebCore/MainFrame.h>
62 #import <WebCore/NetworkingContext.h>
63 #import <WebCore/Page.h>
64 #import <WebCore/PlatformKeyboardEvent.h>
65 #import <WebCore/PluginDocument.h>
66 #import <WebCore/RenderElement.h>
67 #import <WebCore/RenderObject.h>
68 #import <WebCore/RenderStyle.h>
69 #import <WebCore/ResourceHandle.h>
70 #import <WebCore/ScrollView.h>
71 #import <WebCore/StyleInheritedData.h>
72 #import <WebCore/TextIterator.h>
73 #import <WebCore/VisibleUnits.h>
74 #import <WebCore/WindowsKeyboardCodes.h>
75 #import <WebKitSystemInterface.h>
76
77 using namespace WebCore;
78
79 namespace WebKit {
80
81 void WebPage::platformInitialize()
82 {
83 #if USE(CFNETWORK)
84     m_page->addSchedulePair(SchedulePair::create([[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes));
85 #else
86     m_page->addSchedulePair(SchedulePair::create([NSRunLoop currentRunLoop], kCFRunLoopCommonModes));
87 #endif
88
89     WKAccessibilityWebPageObject* mockAccessibilityElement = [[[WKAccessibilityWebPageObject alloc] init] autorelease];
90
91     // Get the pid for the starting process.
92     pid_t pid = WebProcess::shared().presenterApplicationPid();    
93     WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
94     [mockAccessibilityElement setWebPage:this];
95     
96     // send data back over
97     NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement); 
98     IPC::DataReference dataToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
99     send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
100     m_mockAccessibilityElement = mockAccessibilityElement;
101 }
102
103 NSObject *WebPage::accessibilityObjectForMainFramePlugin()
104 {
105     if (!m_page)
106         return 0;
107     
108     if (PluginView* pluginView = pluginViewForFrame(&m_page->mainFrame()))
109         return pluginView->accessibilityObject();
110
111     return 0;
112 }
113
114 void WebPage::platformPreferencesDidChange(const WebPreferencesStore& store)
115 {
116 }
117
118 bool WebPage::shouldUsePDFPlugin() const
119 {
120     return pdfPluginEnabled() && classFromPDFKit(@"PDFLayerController");
121 }
122
123 typedef HashMap<String, String> SelectorNameMap;
124
125 // Map selectors into Editor command names.
126 // This is not needed for any selectors that have the same name as the Editor command.
127 static const SelectorNameMap* createSelectorExceptionMap()
128 {
129     SelectorNameMap* map = new HashMap<String, String>;
130
131     map->add("insertNewlineIgnoringFieldEditor:", "InsertNewline");
132     map->add("insertParagraphSeparator:", "InsertNewline");
133     map->add("insertTabIgnoringFieldEditor:", "InsertTab");
134     map->add("pageDown:", "MovePageDown");
135     map->add("pageDownAndModifySelection:", "MovePageDownAndModifySelection");
136     map->add("pageUp:", "MovePageUp");
137     map->add("pageUpAndModifySelection:", "MovePageUpAndModifySelection");
138
139     return map;
140 }
141
142 static String commandNameForSelectorName(const String& selectorName)
143 {
144     // Check the exception map first.
145     static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
146     SelectorNameMap::const_iterator it = exceptionMap->find(selectorName);
147     if (it != exceptionMap->end())
148         return it->value;
149
150     // Remove the trailing colon.
151     // No need to capitalize the command name since Editor command names are not case sensitive.
152     size_t selectorNameLength = selectorName.length();
153     if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
154         return String();
155     return selectorName.left(selectorNameLength - 1);
156 }
157
158 static Frame* frameForEvent(KeyboardEvent* event)
159 {
160     Node* node = event->target()->toNode();
161     ASSERT(node);
162     Frame* frame = node->document().frame();
163     ASSERT(frame);
164     return frame;
165 }
166
167 bool WebPage::executeKeypressCommandsInternal(const Vector<WebCore::KeypressCommand>& commands, KeyboardEvent* event)
168 {
169     Frame& frame = event ? *frameForEvent(event) : m_page->focusController().focusedOrMainFrame();
170     ASSERT(frame.page() == corePage());
171
172     bool eventWasHandled = false;
173     for (size_t i = 0; i < commands.size(); ++i) {
174         if (commands[i].commandName == "insertText:") {
175             if (frame.editor().hasComposition()) {
176                 eventWasHandled = true;
177                 frame.editor().confirmComposition(commands[i].text);
178             } else {
179                 if (!frame.editor().canEdit())
180                     continue;
181
182                 // An insertText: might be handled by other responders in the chain if we don't handle it.
183                 // One example is space bar that results in scrolling down the page.
184                 eventWasHandled |= frame.editor().insertText(commands[i].text, event);
185             }
186         } else {
187             Editor::Command command = frame.editor().command(commandNameForSelectorName(commands[i].commandName));
188             if (command.isSupported()) {
189                 bool commandExecutedByEditor = command.execute(event);
190                 eventWasHandled |= commandExecutedByEditor;
191                 if (!commandExecutedByEditor) {
192                     bool performedNonEditingBehavior = event->keyEvent()->type() == PlatformEvent::RawKeyDown && performNonEditingBehaviorForSelector(commands[i].commandName, event);
193                     eventWasHandled |= performedNonEditingBehavior;
194                 }
195             } else {
196                 bool commandWasHandledByUIProcess = false;
197                 WebProcess::shared().parentProcessConnection()->sendSync(Messages::WebPageProxy::ExecuteSavedCommandBySelector(commands[i].commandName), 
198                     Messages::WebPageProxy::ExecuteSavedCommandBySelector::Reply(commandWasHandledByUIProcess), m_pageID);
199                 eventWasHandled |= commandWasHandledByUIProcess;
200             }
201         }
202     }
203     return eventWasHandled;
204 }
205
206 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event)
207 {
208     Frame* frame = frameForEvent(event);
209     
210     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
211     if (!platformEvent)
212         return false;
213     const Vector<KeypressCommand>& commands = event->keypressCommands();
214
215     ASSERT(!platformEvent->macEvent()); // Cannot have a native event in WebProcess.
216
217     // Don't handle Esc while handling keydown event, we need to dispatch a keypress first.
218     if (platformEvent->type() != PlatformEvent::Char && platformEvent->windowsVirtualKeyCode() == VK_ESCAPE && commands.size() == 1 && commandNameForSelectorName(commands[0].commandName) == "cancelOperation")
219         return false;
220
221     bool eventWasHandled = false;
222
223     // Are there commands that could just cause text insertion if executed via Editor?
224     // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
225     // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
226     // (e.g. Tab that inserts a Tab character, or Enter).
227     bool haveTextInsertionCommands = false;
228     for (auto& command : commands) {
229         if (frame->editor().command(commandNameForSelectorName(command.commandName)).isTextInsertion())
230             haveTextInsertionCommands = true;
231     }
232     // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
233     // Keypress (Char event) handler is the latest opportunity to execute.
234     if (!haveTextInsertionCommands || platformEvent->type() == PlatformEvent::Char) {
235         eventWasHandled = executeKeypressCommandsInternal(event->keypressCommands(), event);
236         event->keypressCommands().clear();
237     }
238
239     return eventWasHandled;
240 }
241
242 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
243 {
244     for (auto* pluginView : m_pluginViews) {
245         if (pluginView->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
246             break;
247     }
248 }
249
250 #if !USE(ASYNC_NSTEXTINPUTCLIENT)
251
252 void WebPage::setComposition(const String& text, Vector<CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementEditingRange, EditorState& newState)
253 {
254     Frame& frame = m_page->focusController().focusedOrMainFrame();
255
256     if (frame.selection().selection().isContentEditable()) {
257         RefPtr<Range> replacementRange;
258         if (replacementEditingRange.location != notFound) {
259             replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
260             frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
261         }
262
263         frame.editor().setComposition(text, underlines, selectionRange.location, selectionRange.location + selectionRange.length);
264     }
265
266     newState = editorState();
267 }
268
269 void WebPage::confirmComposition(EditorState& newState)
270 {
271     Frame& frame = m_page->focusController().focusedOrMainFrame();
272     frame.editor().confirmComposition();
273     newState = editorState();
274 }
275
276 void WebPage::insertText(const String& text, const EditingRange& replacementEditingRange, bool& handled, EditorState& newState)
277 {
278     Frame& frame = m_page->focusController().focusedOrMainFrame();
279
280     if (replacementEditingRange.location != notFound) {
281         RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
282         if (replacementRange)
283             frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
284     }
285
286     if (!frame.editor().hasComposition()) {
287         // An insertText: might be handled by other responders in the chain if we don't handle it.
288         // One example is space bar that results in scrolling down the page.
289         handled = frame.editor().insertText(text, nullptr);
290     } else {
291         handled = true;
292         frame.editor().confirmComposition(text);
293     }
294
295     newState = editorState();
296 }
297
298 void WebPage::insertDictatedText(const String& text, const EditingRange& replacementEditingRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool& handled, EditorState& newState)
299 {
300     Frame& frame = m_page->focusController().focusedOrMainFrame();
301
302     if (replacementEditingRange.location != notFound) {
303         RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
304         if (replacementRange)
305             frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
306     }
307
308     ASSERT(!frame.editor().hasComposition());
309     handled = frame.editor().insertDictatedText(text, dictationAlternativeLocations, nullptr);
310     newState = editorState();
311 }
312
313 void WebPage::getMarkedRange(EditingRange& result)
314 {
315     Frame& frame = m_page->focusController().focusedOrMainFrame();
316
317     RefPtr<Range> range = frame.editor().compositionRange();
318     size_t locationSize;
319     size_t lengthSize;
320     if (range && TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize))
321         result = EditingRange(static_cast<uint64_t>(locationSize), static_cast<uint64_t>(lengthSize));
322     else
323         result = EditingRange();
324 }
325
326 void WebPage::getSelectedRange(EditingRange& result)
327 {
328     Frame& frame = m_page->focusController().focusedOrMainFrame();
329
330     size_t locationSize;
331     size_t lengthSize;
332     RefPtr<Range> range = frame.selection().toNormalizedRange();
333     if (range && TextIterator::getLocationAndLengthFromRange(frame.selection().rootEditableElementOrDocumentElement(), range.get(), locationSize, lengthSize))
334         result = EditingRange(static_cast<uint64_t>(locationSize), static_cast<uint64_t>(lengthSize));
335     else
336         result = EditingRange();
337 }
338
339 void WebPage::getAttributedSubstringFromRange(const EditingRange& editingRange, AttributedString& result)
340 {
341     Frame& frame = m_page->focusController().focusedOrMainFrame();
342
343     const VisibleSelection& selection = frame.selection().selection();
344     if (selection.isNone() || !selection.isContentEditable() || selection.isInPasswordField())
345         return;
346
347     RefPtr<Range> range = rangeFromEditingRange(frame, editingRange);
348     if (!range)
349         return;
350
351     result.string = editingAttributedStringFromRange(*range);
352     NSAttributedString* attributedString = result.string.get();
353     
354     // WebCore::editingAttributedStringFromRange() insists on inserting a trailing
355     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
356     // To work around this we truncate the resultant string to the correct length.
357     if ([attributedString length] > editingRange.length) {
358         ASSERT([attributedString length] == editingRange.length + 1);
359         ASSERT([[attributedString string] characterAtIndex:editingRange.length] == '\n' || [[attributedString string] characterAtIndex:editingRange.length] == ' ');
360         result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)];
361     }
362 }
363
364 void WebPage::characterIndexForPoint(IntPoint point, uint64_t& index)
365 {
366     index = notFound;
367
368     HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(point);
369     Frame* frame = result.innerNonSharedNode() ? result.innerNodeFrame() : &m_page->focusController().focusedOrMainFrame();
370     
371     RefPtr<Range> range = frame->rangeForPoint(result.roundedPointInInnerNodeFrame());
372     if (!range)
373         return;
374
375     size_t location;
376     size_t length;
377     if (TextIterator::getLocationAndLengthFromRange(frame->selection().rootEditableElementOrDocumentElement(), range.get(), location, length))
378         index = static_cast<uint64_t>(location);
379 }
380     
381 void WebPage::firstRectForCharacterRange(const EditingRange& editingRange, WebCore::IntRect& resultRect)
382 {
383     Frame& frame = m_page->focusController().focusedOrMainFrame();
384     resultRect.setLocation(IntPoint(0, 0));
385     resultRect.setSize(IntSize(0, 0));
386     
387     RefPtr<Range> range = rangeFromEditingRange(frame, editingRange);
388     if (!range)
389         return;
390     
391     ASSERT(range->startContainer());
392     ASSERT(range->endContainer());
393      
394     IntRect rect = frame.editor().firstRectForRange(range.get());
395     resultRect = frame.view()->contentsToWindow(rect);
396 }
397
398 void WebPage::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands, bool& handled, EditorState& newState)
399 {
400     handled = executeKeypressCommandsInternal(commands, nullptr);
401     newState = editorState();
402 }
403
404 void WebPage::cancelComposition(EditorState& newState)
405 {
406     Frame& frame = m_page->focusController().focusedOrMainFrame();
407     frame.editor().cancelComposition();
408     newState = editorState();
409 }
410
411 #endif // !USE(ASYNC_NSTEXTINPUTCLIENT)
412
413 void WebPage::insertDictatedTextAsync(const String& text, const EditingRange& replacementEditingRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations)
414 {
415     Frame& frame = m_page->focusController().focusedOrMainFrame();
416
417     if (replacementEditingRange.location != notFound) {
418         RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
419         if (replacementRange)
420             frame.selection().setSelection(VisibleSelection(replacementRange.get(), SEL_DEFAULT_AFFINITY));
421     }
422
423     ASSERT(!frame.editor().hasComposition());
424     frame.editor().insertDictatedText(text, dictationAlternativeLocations, nullptr);
425 }
426
427 void WebPage::attributedSubstringForCharacterRangeAsync(const EditingRange& editingRange, uint64_t callbackID)
428 {
429     AttributedString result;
430
431     Frame& frame = m_page->focusController().focusedOrMainFrame();
432
433     const VisibleSelection& selection = frame.selection().selection();
434     if (selection.isNone() || !selection.isContentEditable() || selection.isInPasswordField()) {
435         send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
436         return;
437     }
438
439     RefPtr<Range> range = rangeFromEditingRange(frame, editingRange);
440     if (!range) {
441         send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
442         return;
443     }
444
445     result.string = editingAttributedStringFromRange(*range);
446     NSAttributedString* attributedString = result.string.get();
447     
448     // WebCore::editingAttributedStringFromRange() insists on inserting a trailing
449     // whitespace at the end of the string which breaks the ATOK input method.  <rdar://problem/5400551>
450     // To work around this we truncate the resultant string to the correct length.
451     if ([attributedString length] > editingRange.length) {
452         ASSERT([attributedString length] == editingRange.length + 1);
453         ASSERT([[attributedString string] characterAtIndex:editingRange.length] == '\n' || [[attributedString string] characterAtIndex:editingRange.length] == ' ');
454         result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)];
455     }
456
457     send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(editingRange.location, [result.string length]), callbackID));
458 }
459
460 static bool isPositionInRange(const VisiblePosition& position, Range* range)
461 {
462     RefPtr<Range> positionRange = makeRange(position, position);
463
464     ExceptionCode ec = 0;
465     range->compareBoundaryPoints(Range::START_TO_START, positionRange.get(), ec);
466     if (ec)
467         return false;
468
469     if (!range->isPointInRange(positionRange->startContainer(), positionRange->startOffset(), ec))
470         return false;
471     if (ec)
472         return false;
473
474     return true;
475 }
476
477 static bool shouldUseSelection(const VisiblePosition& position, const VisibleSelection& selection)
478 {
479     if (!selection.isRange())
480         return false;
481
482     RefPtr<Range> selectedRange = selection.toNormalizedRange();
483     if (!selectedRange)
484         return false;
485
486     return isPositionInRange(position, selectedRange.get());
487 }
488
489 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
490 {
491     if (PluginView* pluginView = pluginViewForFrame(&m_page->mainFrame())) {
492         if (pluginView->performDictionaryLookupAtLocation(floatPoint))
493             return;
494     }
495
496     // Find the frame the point is over.
497     IntPoint point = roundedIntPoint(floatPoint);
498     HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(m_page->mainFrame().view()->windowToContents(point));
499     Frame* frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document().frame() : &m_page->focusController().focusedOrMainFrame();
500
501     IntPoint translatedPoint = frame->view()->windowToContents(point);
502
503     // Don't do anything if there is no character at the point.
504     if (!frame->rangeForPoint(translatedPoint))
505         return;
506
507     VisiblePosition position = frame->visiblePositionForPoint(translatedPoint);
508     VisibleSelection selection = m_page->focusController().focusedOrMainFrame().selection().selection();
509     if (shouldUseSelection(position, selection)) {
510         performDictionaryLookupForSelection(frame, selection);
511         return;
512     }
513
514     NSDictionary *options = nil;
515
516     // As context, we are going to use four lines of text before and after the point. (Dictionary can sometimes look up things that are four lines long)
517     const int numberOfLinesOfContext = 4;
518     VisiblePosition contextStart = position;
519     VisiblePosition contextEnd = position;
520     for (int i = 0; i < numberOfLinesOfContext; i++) {
521         VisiblePosition n = previousLinePosition(contextStart, contextStart.lineDirectionPointForBlockDirectionNavigation());
522         if (n.isNull() || n == contextStart)
523             break;
524         contextStart = n;
525     }
526     for (int i = 0; i < numberOfLinesOfContext; i++) {
527         VisiblePosition n = nextLinePosition(contextEnd, contextEnd.lineDirectionPointForBlockDirectionNavigation());
528         if (n.isNull() || n == contextEnd)
529             break;
530         contextEnd = n;
531     }
532
533     VisiblePosition lineStart = startOfLine(contextStart);
534     if (!lineStart.isNull())
535         contextStart = lineStart;
536
537     VisiblePosition lineEnd = endOfLine(contextEnd);
538     if (!lineEnd.isNull())
539         contextEnd = lineEnd;
540
541     NSRange rangeToPass = NSMakeRange(TextIterator::rangeLength(makeRange(contextStart, position).get()), 0);
542
543     RefPtr<Range> fullCharacterRange = makeRange(contextStart, contextEnd);
544     String fullPlainTextString = plainText(fullCharacterRange.get());
545
546     NSRange extractedRange = WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
547
548     // This function sometimes returns {NSNotFound, 0} if it was unable to determine a good string.
549     if (extractedRange.location == NSNotFound)
550         return;
551
552     RefPtr<Range> finalRange = TextIterator::subrange(fullCharacterRange.get(), extractedRange.location, extractedRange.length);
553     if (!finalRange)
554         return;
555
556     performDictionaryLookupForRange(frame, *finalRange, options);
557 }
558
559 void WebPage::performDictionaryLookupForSelection(Frame* frame, const VisibleSelection& selection)
560 {
561     RefPtr<Range> selectedRange = selection.toNormalizedRange();
562     if (!selectedRange)
563         return;
564
565     NSDictionary *options = nil;
566
567     VisiblePosition selectionStart = selection.visibleStart();
568     VisiblePosition selectionEnd = selection.visibleEnd();
569
570     // As context, we are going to use the surrounding paragraphs of text.
571     VisiblePosition paragraphStart = startOfParagraph(selectionStart);
572     VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
573
574     int lengthToSelectionStart = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
575     int lengthToSelectionEnd = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get());
576     NSRange rangeToPass = NSMakeRange(lengthToSelectionStart, lengthToSelectionEnd - lengthToSelectionStart);
577
578     String fullPlainTextString = plainText(makeRange(paragraphStart, paragraphEnd).get());
579
580     // Since we already have the range we want, we just need to grab the returned options.
581     WKExtractWordDefinitionTokenRangeFromContextualString(fullPlainTextString, rangeToPass, &options);
582
583     performDictionaryLookupForRange(frame, *selectedRange, options);
584 }
585
586 void WebPage::performDictionaryLookupForRange(Frame* frame, Range& range, NSDictionary *options)
587 {
588     if (range.text().stripWhiteSpace().isEmpty())
589         return;
590     
591     RenderObject* renderer = range.startContainer()->renderer();
592     const RenderStyle& style = renderer->style();
593
594     Vector<FloatQuad> quads;
595     range.textQuads(quads);
596     if (quads.isEmpty())
597         return;
598
599     IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
600     
601     DictionaryPopupInfo dictionaryPopupInfo;
602     dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y() + (style.fontMetrics().ascent() * pageScaleFactor()));
603     dictionaryPopupInfo.options = (CFDictionaryRef)options;
604
605     NSAttributedString *nsAttributedString = editingAttributedStringFromRange(range);
606
607     RetainPtr<NSMutableAttributedString> scaledNSAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[nsAttributedString string]]);
608
609     NSFontManager *fontManager = [NSFontManager sharedFontManager];
610
611     [nsAttributedString enumerateAttributesInRange:NSMakeRange(0, [nsAttributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
612         RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
613
614         NSFont *font = [scaledAttributes objectForKey:NSFontAttributeName];
615         if (font) {
616             font = [fontManager convertFont:font toSize:[font pointSize] * pageScaleFactor()];
617             [scaledAttributes setObject:font forKey:NSFontAttributeName];
618         }
619
620         [scaledNSAttributedString addAttributes:scaledAttributes.get() range:range];
621     }];
622
623     AttributedString attributedString;
624     attributedString.string = scaledNSAttributedString;
625
626     send(Messages::WebPageProxy::DidPerformDictionaryLookup(attributedString, dictionaryPopupInfo));
627 }
628
629 bool WebPage::performNonEditingBehaviorForSelector(const String& selector, KeyboardEvent* event)
630 {
631     // First give accessibility a chance to handle the event.
632     Frame* frame = frameForEvent(event);
633     frame->eventHandler().handleKeyboardSelectionMovementForAccessibility(event);
634     if (event->defaultHandled())
635         return true;
636
637     // FIXME: All these selectors have corresponding Editor commands, but the commands only work in editable content.
638     // Should such non-editing behaviors be implemented in Editor or EventHandler::defaultArrowEventHandler() perhaps?
639     
640     bool didPerformAction = false;
641
642     if (selector == "moveUp:")
643         didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByLine);
644     else if (selector == "moveToBeginningOfParagraph:")
645         didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByPage);
646     else if (selector == "moveToBeginningOfDocument:") {
647         didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByDocument);
648         didPerformAction |= scroll(m_page.get(), ScrollLeft, ScrollByDocument);
649     } else if (selector == "moveDown:")
650         didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByLine);
651     else if (selector == "moveToEndOfParagraph:")
652         didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByPage);
653     else if (selector == "moveToEndOfDocument:") {
654         didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByDocument);
655         didPerformAction |= scroll(m_page.get(), ScrollLeft, ScrollByDocument);
656     } else if (selector == "moveLeft:")
657         didPerformAction = scroll(m_page.get(), ScrollLeft, ScrollByLine);
658     else if (selector == "moveWordLeft:")
659         didPerformAction = scroll(m_page.get(), ScrollLeft, ScrollByPage);
660     else if (selector == "moveToLeftEndOfLine:")
661         didPerformAction = m_page->backForward().goBack();
662     else if (selector == "moveRight:")
663         didPerformAction = scroll(m_page.get(), ScrollRight, ScrollByLine);
664     else if (selector == "moveWordRight:")
665         didPerformAction = scroll(m_page.get(), ScrollRight, ScrollByPage);
666     else if (selector == "moveToRightEndOfLine:")
667         didPerformAction = m_page->backForward().goForward();
668
669     return didPerformAction;
670 }
671
672 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
673 {
674     return false;
675 }
676
677 void WebPage::registerUIProcessAccessibilityTokens(const IPC::DataReference& elementToken, const IPC::DataReference& windowToken)
678 {
679     NSData* elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
680     NSData* windowTokenData = [NSData dataWithBytes:windowToken.data() length:windowToken.size()];
681     id remoteElement = WKAXRemoteElementForToken(elementTokenData);
682     id remoteWindow = WKAXRemoteElementForToken(windowTokenData);
683     WKAXSetWindowForRemoteElement(remoteWindow, remoteElement);
684     
685     [accessibilityRemoteObject() setRemoteParent:remoteElement];
686 }
687
688 void WebPage::readSelectionFromPasteboard(const String& pasteboardName, bool& result)
689 {
690     Frame& frame = m_page->focusController().focusedOrMainFrame();
691     if (frame.selection().isNone()) {
692         result = false;
693         return;
694     }
695     frame.editor().readSelectionFromPasteboard(pasteboardName);
696     result = true;
697 }
698
699 void WebPage::getStringSelectionForPasteboard(String& stringValue)
700 {
701     Frame& frame = m_page->focusController().focusedOrMainFrame();
702
703     if (PluginView* pluginView = focusedPluginViewForFrame(frame)) {
704         String selection = pluginView->getSelectionString();
705         if (!selection.isNull()) {
706             stringValue = selection;
707             return;
708         }
709     }
710
711     if (frame.selection().isNone())
712         return;
713
714     stringValue = frame.editor().stringSelectionForPasteboard();
715 }
716
717 void WebPage::getDataSelectionForPasteboard(const String pasteboardType, SharedMemory::Handle& handle, uint64_t& size)
718 {
719     Frame& frame = m_page->focusController().focusedOrMainFrame();
720     if (frame.selection().isNone())
721         return;
722
723     RefPtr<SharedBuffer> buffer = frame.editor().dataSelectionForPasteboard(pasteboardType);
724     if (!buffer) {
725         size = 0;
726         return;
727     }
728     size = buffer->size();
729     RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::create(size);
730     memcpy(sharedMemoryBuffer->data(), buffer->data(), size);
731     sharedMemoryBuffer->createHandle(handle, SharedMemory::ReadOnly);
732 }
733
734 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
735 {
736     return m_mockAccessibilityElement.get();
737 }
738          
739 bool WebPage::platformHasLocalDataForURL(const WebCore::URL& url)
740 {
741     NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
742     [request setValue:(NSString*)userAgent() forHTTPHeaderField:@"User-Agent"];
743     NSCachedURLResponse *cachedResponse;
744     if (CFURLStorageSessionRef storageSession = corePage()->mainFrame().loader().networkingContext()->storageSession().platformSession())
745         cachedResponse = WKCachedResponseForRequest(storageSession, request);
746     else
747         cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
748     [request release];
749     
750     return cachedResponse;
751 }
752
753 static NSCachedURLResponse *cachedResponseForURL(WebPage* webPage, const URL& url)
754 {
755     RetainPtr<NSMutableURLRequest> request = adoptNS([[NSMutableURLRequest alloc] initWithURL:url]);
756     [request setValue:(NSString *)webPage->userAgent() forHTTPHeaderField:@"User-Agent"];
757
758     if (CFURLStorageSessionRef storageSession = webPage->corePage()->mainFrame().loader().networkingContext()->storageSession().platformSession())
759         return WKCachedResponseForRequest(storageSession, request.get());
760
761     return [[NSURLCache sharedURLCache] cachedResponseForRequest:request.get()];
762 }
763
764 String WebPage::cachedSuggestedFilenameForURL(const URL& url)
765 {
766     return [[cachedResponseForURL(this, url) response] suggestedFilename];
767 }
768
769 String WebPage::cachedResponseMIMETypeForURL(const URL& url)
770 {
771     return [[cachedResponseForURL(this, url) response] MIMEType];
772 }
773
774 PassRefPtr<SharedBuffer> WebPage::cachedResponseDataForURL(const URL& url)
775 {
776     return SharedBuffer::wrapNSData([cachedResponseForURL(this, url) data]);
777 }
778
779 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest& request)
780 {
781     if ([NSURLConnection canHandleRequest:request.nsURLRequest(DoNotUpdateHTTPBody)])
782         return true;
783
784     // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
785     return request.url().protocolIs("applewebdata");
786 }
787
788 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent& event, bool& result)
789 {
790     Frame& frame = m_page->focusController().focusedOrMainFrame();
791
792 #if ENABLE(DRAG_SUPPORT)
793     HitTestResult hitResult = frame.eventHandler().hitTestResultAtPoint(frame.view()->windowToContents(event.position()), HitTestRequest::ReadOnly | HitTestRequest::Active);
794     if (hitResult.isSelected())
795         result = frame.eventHandler().eventMayStartDrag(platform(event));
796     else
797 #endif
798         result = false;
799 }
800
801 void WebPage::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event, bool& result)
802 {
803     result = false;
804     Frame& frame = m_page->focusController().focusedOrMainFrame();
805
806     HitTestResult hitResult = frame.eventHandler().hitTestResultAtPoint(frame.view()->windowToContents(event.position()), HitTestRequest::ReadOnly | HitTestRequest::Active);
807     frame.eventHandler().setActivationEventNumber(eventNumber);
808 #if ENABLE(DRAG_SUPPORT)
809     if (hitResult.isSelected())
810         result = frame.eventHandler().eventMayStartDrag(platform(event));
811     else
812 #endif
813         result = !!hitResult.scrollbar();
814 }
815
816 void WebPage::setTopOverhangImage(PassRefPtr<WebImage> image)
817 {
818     FrameView* frameView = m_mainFrame->coreFrame()->view();
819     if (!frameView)
820         return;
821
822     GraphicsLayer* layer = frameView->setWantsLayerForTopOverHangArea(image.get());
823     if (!layer)
824         return;
825
826     layer->setSize(image->size());
827     layer->setPosition(FloatPoint(0, -image->size().height()));
828
829     RetainPtr<CGImageRef> cgImage = image->bitmap()->makeCGImageCopy();
830     layer->platformLayer().contents = (id)cgImage.get();
831 }
832
833 void WebPage::setBottomOverhangImage(PassRefPtr<WebImage> image)
834 {
835     FrameView* frameView = m_mainFrame->coreFrame()->view();
836     if (!frameView)
837         return;
838
839     GraphicsLayer* layer = frameView->setWantsLayerForBottomOverHangArea(image.get());
840     if (!layer)
841         return;
842
843     layer->setSize(image->size());
844     
845     RetainPtr<CGImageRef> cgImage = image->bitmap()->makeCGImageCopy();
846     layer->platformLayer().contents = (id)cgImage.get();
847 }
848
849 void WebPage::updateHeaderAndFooterLayersForDeviceScaleChange(float scaleFactor)
850 {    
851     if (m_headerBanner)
852         m_headerBanner->didChangeDeviceScaleFactor(scaleFactor);
853     if (m_footerBanner)
854         m_footerBanner->didChangeDeviceScaleFactor(scaleFactor);
855 }
856
857 void WebPage::computePagesForPrintingPDFDocument(uint64_t frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects)
858 {
859     ASSERT(resultPageRects.isEmpty());
860     WebFrame* frame = WebProcess::shared().webFrame(frameID);
861     Frame* coreFrame = frame ? frame->coreFrame() : 0;
862     RetainPtr<PDFDocument> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame) : 0;
863     if ([pdfDocument allowsPrinting]) {
864         NSUInteger pageCount = [pdfDocument pageCount];
865         IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
866         for (NSUInteger i = 1; i <= pageCount; ++i) {
867             resultPageRects.append(pageRect);
868             pageRect.move(0, pageRect.height());
869         }
870     }
871 }
872
873 static inline CGFloat roundCGFloat(CGFloat f)
874 {
875     if (sizeof(CGFloat) == sizeof(float))
876         return roundf(static_cast<float>(f));
877     return static_cast<CGFloat>(round(f));
878 }
879
880 static void drawPDFPage(PDFDocument *pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize)
881 {
882     CGContextSaveGState(context);
883
884     CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor);
885
886     PDFPage *pdfPage = [pdfDocument pageAtIndex:pageIndex];
887     NSRect cropBox = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
888     if (NSIsEmptyRect(cropBox))
889         cropBox = [pdfPage boundsForBox:kPDFDisplayBoxMediaBox];
890     else
891         cropBox = NSIntersectionRect(cropBox, [pdfPage boundsForBox:kPDFDisplayBoxMediaBox]);
892
893     // Always auto-rotate PDF content regardless of the paper orientation.
894     NSInteger rotation = [pdfPage rotation];
895     if (rotation == 90 || rotation == 270)
896         std::swap(cropBox.size.width, cropBox.size.height);
897
898     bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height);
899     if (shouldRotate)
900         std::swap(cropBox.size.width, cropBox.size.height);
901
902     // Center.
903     CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width;
904     CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height;
905     if (widthDifference || heightDifference)
906         CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2));
907
908     if (shouldRotate) {
909         CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble));
910         CGContextTranslateCTM(context, 0, -cropBox.size.width);
911     }
912
913     [NSGraphicsContext saveGraphicsState];
914     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]];
915     [pdfPage drawWithBox:kPDFDisplayBoxCropBox];
916     [NSGraphicsContext restoreGraphicsState];
917
918     CGAffineTransform transform = CGContextGetCTM(context);
919
920     for (PDFAnnotation *annotation in [pdfPage annotations]) {
921         if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
922             continue;
923
924         PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
925         NSURL *url = [linkAnnotation URL];
926         if (!url)
927             continue;
928
929         CGRect urlRect = NSRectToCGRect([linkAnnotation bounds]);
930         CGRect transformedRect = CGRectApplyAffineTransform(urlRect, transform);
931         CGPDFContextSetURLForRect(context, (CFURLRef)url, transformedRect);
932     }
933
934     CGContextRestoreGState(context);
935 }
936
937 void WebPage::drawPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::IntRect& rect)
938 {
939     NSUInteger pageCount = [pdfDocument pageCount];
940     IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
941     IntRect pageRect(IntPoint(), paperSize);
942     for (NSUInteger i = 0; i < pageCount; ++i) {
943         if (pageRect.intersects(rect)) {
944             CGContextSaveGState(context);
945
946             CGContextTranslateCTM(context, pageRect.x() - rect.x(), pageRect.y() - rect.y());
947             drawPDFPage(pdfDocument, i, context, printInfo.pageSetupScaleFactor, paperSize);
948
949             CGContextRestoreGState(context);
950         }
951         pageRect.move(0, pageRect.height());
952     }
953 }
954
955 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, uint32_t first, uint32_t count)
956 {
957     NSUInteger pageCount = [pdfDocument pageCount];
958     for (uint32_t page = first; page < first + count; ++page) {
959         if (page >= pageCount)
960             break;
961
962         RetainPtr<CFDictionaryRef> pageInfo = adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
963
964         CGPDFContextBeginPage(context, pageInfo.get());
965         drawPDFPage(pdfDocument, page, context, printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight));
966         CGPDFContextEndPage(context);
967     }
968 }
969
970 void WebPage::didUpdateViewStateTimerFired()
971 {
972     [CATransaction flush];
973     send(Messages::WebPageProxy::DidUpdateViewState());
974 }
975
976 #if ENABLE(WEBGL)
977 WebCore::WebGLLoadPolicy WebPage::webGLPolicyForURL(WebFrame* frame, const String& url)
978 {
979     uint32_t policyResult = 0;
980
981     if (sendSync(Messages::WebPageProxy::WebGLPolicyForURL(url), Messages::WebPageProxy::WebGLPolicyForURL::Reply(policyResult)))
982         return static_cast<WebGLLoadPolicy>(policyResult);
983
984     return WebGLAllowCreation;
985 }
986
987 WebCore::WebGLLoadPolicy WebPage::resolveWebGLPolicyForURL(WebFrame* frame, const String& url)
988 {
989     uint32_t policyResult = 0;
990
991     if (sendSync(Messages::WebPageProxy::ResolveWebGLPolicyForURL(url), Messages::WebPageProxy::ResolveWebGLPolicyForURL::Reply(policyResult)))
992         return static_cast<WebGLLoadPolicy>(policyResult);
993
994     return WebGLAllowCreation;
995 }
996 #endif // ENABLE(WEBGL)
997
998 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
999 void WebPage::handleTelephoneNumberClick(const String& number, const IntPoint& point)
1000 {
1001     send(Messages::WebPageProxy::ShowTelephoneNumberMenu(number, point));
1002 }
1003 #endif
1004
1005 } // namespace WebKit
1006
1007 #endif // PLATFORM(MAC)