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