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