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