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