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