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