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