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