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