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