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