2 * Copyright (C) 2010-2012, 2015-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
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"
39 #import "PageBanner.h"
40 #import "PluginView.h"
43 #import "WKAccessibilityWebPageObjectMac.h"
44 #import "WebCoreArgumentCoders.h"
46 #import "WebEventConversion.h"
48 #import "WebHitTestResultData.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/Editor.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/NetworkStorageSession.h>
75 #import <WebCore/NetworkingContext.h>
76 #import <WebCore/Page.h>
77 #import <WebCore/PageOverlayController.h>
78 #import <WebCore/PlatformKeyboardEvent.h>
79 #import <WebCore/PlatformMediaSessionManager.h>
80 #import <WebCore/PluginDocument.h>
81 #import <WebCore/RenderElement.h>
82 #import <WebCore/RenderObject.h>
83 #import <WebCore/RenderStyle.h>
84 #import <WebCore/RenderView.h>
85 #import <WebCore/ResourceHandle.h>
86 #import <WebCore/ScrollView.h>
87 #import <WebCore/StyleInheritedData.h>
88 #import <WebCore/TextIterator.h>
89 #import <WebCore/VisibleUnits.h>
90 #import <WebCore/WindowsKeyboardCodes.h>
91 #import <WebCore/htmlediting.h>
92 #import <WebKitSystemInterface.h>
93 #import <wtf/TemporaryChange.h>
95 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
96 #include <WebCore/MediaPlaybackTargetMac.h>
97 #include <WebCore/MediaPlaybackTargetMock.h>
100 using namespace WebCore;
104 void WebPage::platformInitialize()
106 WKAccessibilityWebPageObject* mockAccessibilityElement = [[[WKAccessibilityWebPageObject alloc] init] autorelease];
108 // Get the pid for the starting process.
109 pid_t pid = WebProcess::singleton().presenterApplicationPid();
110 WKAXInitializeElementWithPresenterPid(mockAccessibilityElement, pid);
111 [mockAccessibilityElement setWebPage:this];
113 // send data back over
114 NSData* remoteToken = (NSData *)WKAXRemoteTokenForElement(mockAccessibilityElement);
115 IPC::DataReference dataToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteToken bytes]), [remoteToken length]);
116 send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(dataToken));
117 m_mockAccessibilityElement = mockAccessibilityElement;
120 void WebPage::platformDetach()
122 [m_mockAccessibilityElement setWebPage:nullptr];
125 void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
127 if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::No || !result.isContentEditable) {
128 result.isMissingPostLayoutData = true;
132 const VisibleSelection& selection = frame.selection().selection();
133 RefPtr<Range> selectedRange = selection.toNormalizedRange();
137 auto& postLayoutData = result.postLayoutData();
138 VisiblePosition selectionStart = selection.visibleStart();
139 VisiblePosition selectionEnd = selection.visibleEnd();
140 VisiblePosition paragraphStart = startOfParagraph(selectionStart);
141 VisiblePosition paragraphEnd = endOfParagraph(selectionEnd);
143 postLayoutData.candidateRequestStartPosition = TextIterator::rangeLength(makeRange(paragraphStart, selectionStart).get());
144 postLayoutData.selectedTextLength = TextIterator::rangeLength(makeRange(paragraphStart, selectionEnd).get()) - postLayoutData.candidateRequestStartPosition;
145 postLayoutData.paragraphContextForCandidateRequest = plainText(frame.editor().contextRangeForCandidateRequest().get());
146 postLayoutData.stringForCandidateRequest = frame.editor().stringForCandidateRequest();
148 IntRect rectForSelectionCandidates;
149 Vector<FloatQuad> quads;
150 selectedRange->absoluteTextQuads(quads);
151 if (!quads.isEmpty())
152 postLayoutData.selectionClipRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
155 void WebPage::handleAcceptedCandidate(WebCore::TextCheckingResult acceptedCandidate)
157 Frame* frame = m_page->focusController().focusedFrame();
161 frame->editor().handleAcceptedCandidate(acceptedCandidate);
162 send(Messages::WebPageProxy::DidHandleAcceptedCandidate());
165 void WebPage::requestActiveNowPlayingSessionInfo()
167 bool hasActiveSession = false;
168 String title = emptyString();
169 double duration = NAN;
170 double elapsedTime = NAN;
171 if (auto* sharedManager = WebCore::PlatformMediaSessionManager::sharedManagerIfExists()) {
172 hasActiveSession = sharedManager->hasActiveNowPlayingSession();
173 title = sharedManager->lastUpdatedNowPlayingTitle();
174 duration = sharedManager->lastUpdatedNowPlayingDuration();
175 elapsedTime = sharedManager->lastUpdatedNowPlayingElapsedTime();
178 send(Messages::WebPageProxy::HandleActiveNowPlayingSessionInfoResponse(hasActiveSession, title, duration, elapsedTime));
181 NSObject *WebPage::accessibilityObjectForMainFramePlugin()
186 if (PluginView* pluginView = pluginViewForFrame(&m_page->mainFrame()))
187 return pluginView->accessibilityObject();
192 void WebPage::platformPreferencesDidChange(const WebPreferencesStore& store)
196 bool WebPage::shouldUsePDFPlugin() const
198 return pdfPluginEnabled() && classFromPDFKit(@"PDFLayerController");
201 typedef HashMap<String, String> SelectorNameMap;
203 // Map selectors into Editor command names.
204 // This is not needed for any selectors that have the same name as the Editor command.
205 static const SelectorNameMap* createSelectorExceptionMap()
207 SelectorNameMap* map = new HashMap<String, String>;
209 map->add("insertNewlineIgnoringFieldEditor:", "InsertNewline");
210 map->add("insertParagraphSeparator:", "InsertNewline");
211 map->add("insertTabIgnoringFieldEditor:", "InsertTab");
212 map->add("pageDown:", "MovePageDown");
213 map->add("pageDownAndModifySelection:", "MovePageDownAndModifySelection");
214 map->add("pageUp:", "MovePageUp");
215 map->add("pageUpAndModifySelection:", "MovePageUpAndModifySelection");
220 static String commandNameForSelectorName(const String& selectorName)
222 // Check the exception map first.
223 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap();
224 SelectorNameMap::const_iterator it = exceptionMap->find(selectorName);
225 if (it != exceptionMap->end())
228 // Remove the trailing colon.
229 // No need to capitalize the command name since Editor command names are not case sensitive.
230 size_t selectorNameLength = selectorName.length();
231 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':')
233 return selectorName.left(selectorNameLength - 1);
236 static Frame* frameForEvent(KeyboardEvent* event)
238 Node* node = event->target()->toNode();
240 Frame* frame = node->document().frame();
245 bool WebPage::executeKeypressCommandsInternal(const Vector<WebCore::KeypressCommand>& commands, KeyboardEvent* event)
247 Frame& frame = event ? *frameForEvent(event) : m_page->focusController().focusedOrMainFrame();
248 ASSERT(frame.page() == corePage());
250 bool eventWasHandled = false;
251 for (size_t i = 0; i < commands.size(); ++i) {
252 if (commands[i].commandName == "insertText:") {
253 if (frame.editor().hasComposition()) {
254 eventWasHandled = true;
255 frame.editor().confirmComposition(commands[i].text);
257 if (!frame.editor().canEdit())
260 // An insertText: might be handled by other responders in the chain if we don't handle it.
261 // One example is space bar that results in scrolling down the page.
262 eventWasHandled |= frame.editor().insertText(commands[i].text, event);
265 Editor::Command command = frame.editor().command(commandNameForSelectorName(commands[i].commandName));
266 if (command.isSupported()) {
267 bool commandExecutedByEditor = command.execute(event);
268 eventWasHandled |= commandExecutedByEditor;
269 if (!commandExecutedByEditor) {
270 bool performedNonEditingBehavior = event->keyEvent()->type() == PlatformEvent::RawKeyDown && performNonEditingBehaviorForSelector(commands[i].commandName, event);
271 eventWasHandled |= performedNonEditingBehavior;
274 bool commandWasHandledByUIProcess = false;
275 WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::ExecuteSavedCommandBySelector(commands[i].commandName),
276 Messages::WebPageProxy::ExecuteSavedCommandBySelector::Reply(commandWasHandledByUIProcess), m_pageID);
277 eventWasHandled |= commandWasHandledByUIProcess;
281 return eventWasHandled;
284 bool WebPage::handleEditingKeyboardEvent(KeyboardEvent* event)
286 Frame* frame = frameForEvent(event);
288 const PlatformKeyboardEvent* platformEvent = event->keyEvent();
291 const Vector<KeypressCommand>& commands = event->keypressCommands();
293 ASSERT(!platformEvent->macEvent()); // Cannot have a native event in WebProcess.
295 // Don't handle Esc while handling keydown event, we need to dispatch a keypress first.
296 if (platformEvent->type() != PlatformEvent::Char && platformEvent->windowsVirtualKeyCode() == VK_ESCAPE && commands.size() == 1 && commandNameForSelectorName(commands[0].commandName) == "cancelOperation")
299 bool eventWasHandled = false;
301 // Are there commands that could just cause text insertion if executed via Editor?
302 // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore
303 // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
304 // (e.g. Tab that inserts a Tab character, or Enter).
305 bool haveTextInsertionCommands = false;
306 for (auto& command : commands) {
307 if (frame->editor().command(commandNameForSelectorName(command.commandName)).isTextInsertion())
308 haveTextInsertionCommands = true;
310 // If there are no text insertion commands, default keydown handler is the right time to execute the commands.
311 // Keypress (Char event) handler is the latest opportunity to execute.
312 if (!haveTextInsertionCommands || platformEvent->type() == PlatformEvent::Char) {
313 eventWasHandled = executeKeypressCommandsInternal(event->keypressCommands(), event);
314 event->keypressCommands().clear();
317 return eventWasHandled;
320 void WebPage::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
322 for (auto* pluginView : m_pluginViews) {
323 if (pluginView->sendComplexTextInput(pluginComplexTextInputIdentifier, textInput))
328 void WebPage::insertDictatedTextAsync(const String& text, const EditingRange& replacementEditingRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, bool registerUndoGroup)
330 Frame& frame = m_page->focusController().focusedOrMainFrame();
332 Ref<Frame> protector(frame);
334 if (replacementEditingRange.location != notFound) {
335 RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange);
336 if (replacementRange)
337 frame.selection().setSelection(VisibleSelection(*replacementRange, SEL_DEFAULT_AFFINITY));
340 if (registerUndoGroup)
341 send(Messages::WebPageProxy::RegisterInsertionUndoGrouping());
343 ASSERT(!frame.editor().hasComposition());
344 frame.editor().insertDictatedText(text, dictationAlternativeLocations, nullptr);
347 void WebPage::attributedSubstringForCharacterRangeAsync(const EditingRange& editingRange, uint64_t callbackID)
349 AttributedString result;
351 Frame& frame = m_page->focusController().focusedOrMainFrame();
353 const VisibleSelection& selection = frame.selection().selection();
354 if (selection.isNone() || !selection.isContentEditable() || selection.isInPasswordField()) {
355 send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
359 RefPtr<Range> range = rangeFromEditingRange(frame, editingRange);
361 send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
365 result.string = editingAttributedStringFromRange(*range);
366 NSAttributedString* attributedString = result.string.get();
368 // WebCore::editingAttributedStringFromRange() insists on inserting a trailing
369 // whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551>
370 // To work around this we truncate the resultant string to the correct length.
371 if ([attributedString length] > editingRange.length) {
372 ASSERT([attributedString length] == editingRange.length + 1);
373 ASSERT([[attributedString string] characterAtIndex:editingRange.length] == '\n' || [[attributedString string] characterAtIndex:editingRange.length] == ' ');
374 result.string = [attributedString attributedSubstringFromRange:NSMakeRange(0, editingRange.length)];
377 EditingRange rangeToSend(editingRange.location, [result.string length]);
378 ASSERT(rangeToSend.isValid());
379 if (!rangeToSend.isValid()) {
380 // Send an empty EditingRange as a last resort for <rdar://problem/27078089>.
381 send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, EditingRange(), callbackID));
385 send(Messages::WebPageProxy::AttributedStringForCharacterRangeCallback(result, rangeToSend, callbackID));
388 void WebPage::fontAtSelection(uint64_t callbackID)
392 bool selectionHasMultipleFonts = false;
393 Frame& frame = m_page->focusController().focusedOrMainFrame();
395 if (!frame.selection().selection().isNone()) {
396 if (auto* font = frame.editor().fontForSelection(selectionHasMultipleFonts)) {
397 if (auto ctFont = font->getCTFont()) {
398 fontName = adoptCF(CTFontCopyPostScriptName(ctFont)).get();
399 fontSize = CTFontGetSize(ctFont);
403 send(Messages::WebPageProxy::FontAtSelectionCallback(fontName, fontSize, selectionHasMultipleFonts, callbackID));
406 void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
408 if (PluginView* pluginView = pluginViewForFrame(&m_page->mainFrame())) {
409 if (pluginView->performDictionaryLookupAtLocation(floatPoint))
413 // Find the frame the point is over.
414 IntPoint point = roundedIntPoint(floatPoint);
415 HitTestResult result = m_page->mainFrame().eventHandler().hitTestResultAtPoint(m_page->mainFrame().view()->windowToContents(point));
416 Frame* frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document().frame() : &m_page->focusController().focusedOrMainFrame();
417 NSDictionary *options = nil;
418 RefPtr<Range> range = DictionaryLookup::rangeAtHitTestResult(result, &options);
422 performDictionaryLookupForRange(frame, *range, options, TextIndicatorPresentationTransition::Bounce);
425 void WebPage::performDictionaryLookupForSelection(Frame* frame, const VisibleSelection& selection, TextIndicatorPresentationTransition presentationTransition)
427 NSDictionary *options = nil;
428 RefPtr<Range> selectedRange = DictionaryLookup::rangeForSelection(selection, &options);
430 performDictionaryLookupForRange(frame, *selectedRange, options, presentationTransition);
433 void WebPage::performDictionaryLookupOfCurrentSelection()
435 Frame* frame = &m_page->focusController().focusedOrMainFrame();
436 performDictionaryLookupForSelection(frame, frame->selection().selection(), TextIndicatorPresentationTransition::BounceAndCrossfade);
439 DictionaryPopupInfo WebPage::dictionaryPopupInfoForRange(Frame* frame, Range& range, NSDictionary **options, TextIndicatorPresentationTransition presentationTransition)
441 Editor& editor = frame->editor();
442 editor.setIsGettingDictionaryPopupInfo(true);
444 DictionaryPopupInfo dictionaryPopupInfo;
445 if (range.text().stripWhiteSpace().isEmpty()) {
446 editor.setIsGettingDictionaryPopupInfo(false);
447 return dictionaryPopupInfo;
450 RenderObject* renderer = range.startContainer().renderer();
451 const RenderStyle& style = renderer->style();
453 Vector<FloatQuad> quads;
454 range.absoluteTextQuads(quads);
455 if (quads.isEmpty()) {
456 editor.setIsGettingDictionaryPopupInfo(false);
457 return dictionaryPopupInfo;
460 IntRect rangeRect = frame->view()->contentsToWindow(quads[0].enclosingBoundingBox());
462 dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y() + (style.fontMetrics().ascent() * pageScaleFactor()));
463 dictionaryPopupInfo.options = *options;
465 NSAttributedString *nsAttributedString = editingAttributedStringFromRange(range, IncludeImagesInAttributedString::No);
467 RetainPtr<NSMutableAttributedString> scaledNSAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[nsAttributedString string]]);
469 NSFontManager *fontManager = [NSFontManager sharedFontManager];
471 [nsAttributedString enumerateAttributesInRange:NSMakeRange(0, [nsAttributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
472 RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
474 NSFont *font = [scaledAttributes objectForKey:NSFontAttributeName];
476 font = [fontManager convertFont:font toSize:[font pointSize] * pageScaleFactor()];
477 [scaledAttributes setObject:font forKey:NSFontAttributeName];
480 [scaledNSAttributedString addAttributes:scaledAttributes.get() range:range];
483 TextIndicatorOptions indicatorOptions = TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges;
484 if (presentationTransition == TextIndicatorPresentationTransition::BounceAndCrossfade)
485 indicatorOptions |= TextIndicatorOptionIncludeSnapshotWithSelectionHighlight;
487 RefPtr<TextIndicator> textIndicator = TextIndicator::createWithRange(range, indicatorOptions, presentationTransition);
488 if (!textIndicator) {
489 editor.setIsGettingDictionaryPopupInfo(false);
490 return dictionaryPopupInfo;
493 dictionaryPopupInfo.textIndicator = textIndicator->data();
494 dictionaryPopupInfo.attributedString = scaledNSAttributedString;
496 editor.setIsGettingDictionaryPopupInfo(false);
497 return dictionaryPopupInfo;
500 #if ENABLE(PDFKIT_PLUGIN)
501 DictionaryPopupInfo WebPage::dictionaryPopupInfoForSelectionInPDFPlugin(PDFSelection *selection, PDFPlugin& pdfPlugin, NSDictionary **options, WebCore::TextIndicatorPresentationTransition presentationTransition)
503 DictionaryPopupInfo dictionaryPopupInfo;
504 if (!selection.string.length)
505 return dictionaryPopupInfo;
507 NSRect rangeRect = pdfPlugin.rectForSelectionInRootView(selection);
509 NSAttributedString *nsAttributedString = selection.attributedString;
511 RetainPtr<NSMutableAttributedString> scaledNSAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[nsAttributedString string]]);
513 NSFontManager *fontManager = [NSFontManager sharedFontManager];
515 CGFloat scaleFactor = pdfPlugin.scaleFactor();
517 __block CGFloat maxAscender = 0;
518 __block CGFloat maxDescender = 0;
519 [nsAttributedString enumerateAttributesInRange:NSMakeRange(0, [nsAttributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
520 RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
522 NSFont *font = [scaledAttributes objectForKey:NSFontAttributeName];
524 maxAscender = std::max(maxAscender, font.ascender * scaleFactor);
525 maxDescender = std::min(maxDescender, font.descender * scaleFactor);
526 font = [fontManager convertFont:font toSize:[font pointSize] * scaleFactor];
527 [scaledAttributes setObject:font forKey:NSFontAttributeName];
530 [scaledNSAttributedString addAttributes:scaledAttributes.get() range:range];
533 // Based on TextIndicator implementation:
534 // TODO(144307): Come up with a better way to share this information than duplicating these values.
535 CGFloat verticalMargin = 2.5;
536 CGFloat horizontalMargin = 0.5;
538 rangeRect.origin.y -= CGCeiling(rangeRect.size.height - maxAscender - std::abs(maxDescender) + verticalMargin * scaleFactor);
539 rangeRect.origin.x += CGFloor(horizontalMargin * scaleFactor);
541 TextIndicatorData dataForSelection;
542 dataForSelection.selectionRectInRootViewCoordinates = rangeRect;
543 dataForSelection.textBoundingRectInRootViewCoordinates = rangeRect;
544 dataForSelection.contentImageScaleFactor = scaleFactor;
545 dataForSelection.presentationTransition = presentationTransition;
547 dictionaryPopupInfo.origin = rangeRect.origin;
548 dictionaryPopupInfo.options = *options;
549 dictionaryPopupInfo.textIndicator = dataForSelection;
550 dictionaryPopupInfo.attributedString = scaledNSAttributedString;
552 return dictionaryPopupInfo;
556 void WebPage::performDictionaryLookupForRange(Frame* frame, Range& range, NSDictionary *options, TextIndicatorPresentationTransition presentationTransition)
558 DictionaryPopupInfo dictionaryPopupInfo = dictionaryPopupInfoForRange(frame, range, &options, presentationTransition);
559 send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfo));
562 bool WebPage::performNonEditingBehaviorForSelector(const String& selector, KeyboardEvent* event)
564 // First give accessibility a chance to handle the event.
565 Frame* frame = frameForEvent(event);
566 frame->eventHandler().handleKeyboardSelectionMovementForAccessibility(*event);
567 if (event->defaultHandled())
570 // FIXME: All these selectors have corresponding Editor commands, but the commands only work in editable content.
571 // Should such non-editing behaviors be implemented in Editor or EventHandler::defaultArrowEventHandler() perhaps?
573 bool didPerformAction = false;
575 if (selector == "moveUp:")
576 didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByLine);
577 else if (selector == "moveToBeginningOfParagraph:")
578 didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByPage);
579 else if (selector == "moveToBeginningOfDocument:") {
580 didPerformAction = scroll(m_page.get(), ScrollUp, ScrollByDocument);
581 didPerformAction |= scroll(m_page.get(), ScrollLeft, ScrollByDocument);
582 } else if (selector == "moveDown:")
583 didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByLine);
584 else if (selector == "moveToEndOfParagraph:")
585 didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByPage);
586 else if (selector == "moveToEndOfDocument:") {
587 didPerformAction = scroll(m_page.get(), ScrollDown, ScrollByDocument);
588 didPerformAction |= scroll(m_page.get(), ScrollLeft, ScrollByDocument);
589 } else if (selector == "moveLeft:")
590 didPerformAction = scroll(m_page.get(), ScrollLeft, ScrollByLine);
591 else if (selector == "moveWordLeft:")
592 didPerformAction = scroll(m_page.get(), ScrollLeft, ScrollByPage);
593 else if (selector == "moveToLeftEndOfLine:")
594 didPerformAction = m_userInterfaceLayoutDirection == WebCore::UserInterfaceLayoutDirection::LTR ? m_page->backForward().goBack() : m_page->backForward().goForward();
595 else if (selector == "moveRight:")
596 didPerformAction = scroll(m_page.get(), ScrollRight, ScrollByLine);
597 else if (selector == "moveWordRight:")
598 didPerformAction = scroll(m_page.get(), ScrollRight, ScrollByPage);
599 else if (selector == "moveToRightEndOfLine:")
600 didPerformAction = m_userInterfaceLayoutDirection == WebCore::UserInterfaceLayoutDirection::LTR ? m_page->backForward().goForward() : m_page->backForward().goBack();
602 return didPerformAction;
605 #if ENABLE(SERVICE_CONTROLS)
606 static String& replaceSelectionPasteboardName()
608 static NeverDestroyed<String> string("ReplaceSelectionPasteboard");
612 void WebPage::replaceSelectionWithPasteboardData(const Vector<String>& types, const IPC::DataReference& data)
614 for (auto& type : types)
615 WebPasteboardOverrides::sharedPasteboardOverrides().addOverride(replaceSelectionPasteboardName(), type, data.vector());
618 readSelectionFromPasteboard(replaceSelectionPasteboardName(), result);
620 for (auto& type : types)
621 WebPasteboardOverrides::sharedPasteboardOverrides().removeOverride(replaceSelectionPasteboardName(), type);
625 bool WebPage::performDefaultBehaviorForKeyEvent(const WebKeyboardEvent&)
630 void WebPage::registerUIProcessAccessibilityTokens(const IPC::DataReference& elementToken, const IPC::DataReference& windowToken)
632 NSData* elementTokenData = [NSData dataWithBytes:elementToken.data() length:elementToken.size()];
633 NSData* windowTokenData = [NSData dataWithBytes:windowToken.data() length:windowToken.size()];
634 id remoteElement = WKAXRemoteElementForToken(elementTokenData);
635 id remoteWindow = WKAXRemoteElementForToken(windowTokenData);
636 WKAXSetWindowForRemoteElement(remoteWindow, remoteElement);
638 [accessibilityRemoteObject() setRemoteParent:remoteElement];
641 void WebPage::readSelectionFromPasteboard(const String& pasteboardName, bool& result)
643 Frame& frame = m_page->focusController().focusedOrMainFrame();
644 if (frame.selection().isNone()) {
648 frame.editor().readSelectionFromPasteboard(pasteboardName);
652 void WebPage::getStringSelectionForPasteboard(String& stringValue)
654 Frame& frame = m_page->focusController().focusedOrMainFrame();
656 if (PluginView* pluginView = focusedPluginViewForFrame(frame)) {
657 String selection = pluginView->getSelectionString();
658 if (!selection.isNull()) {
659 stringValue = selection;
664 if (frame.selection().isNone())
667 stringValue = frame.editor().stringSelectionForPasteboard();
670 void WebPage::getDataSelectionForPasteboard(const String pasteboardType, SharedMemory::Handle& handle, uint64_t& size)
672 Frame& frame = m_page->focusController().focusedOrMainFrame();
673 if (frame.selection().isNone())
676 RefPtr<SharedBuffer> buffer = frame.editor().dataSelectionForPasteboard(pasteboardType);
681 size = buffer->size();
682 RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::allocate(size);
683 memcpy(sharedMemoryBuffer->data(), buffer->data(), size);
684 sharedMemoryBuffer->createHandle(handle, SharedMemory::Protection::ReadOnly);
687 WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
689 return m_mockAccessibilityElement.get();
692 bool WebPage::platformHasLocalDataForURL(const WebCore::URL& url)
694 NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
695 [request setValue:(NSString*)userAgent(url) forHTTPHeaderField:@"User-Agent"];
696 NSCachedURLResponse *cachedResponse;
697 if (CFURLStorageSessionRef storageSession = corePage()->mainFrame().loader().networkingContext()->storageSession().platformSession())
698 cachedResponse = WKCachedResponseForRequest(storageSession, request);
700 cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
703 return cachedResponse;
706 static NSCachedURLResponse *cachedResponseForURL(WebPage* webPage, const URL& url)
708 RetainPtr<NSMutableURLRequest> request = adoptNS([[NSMutableURLRequest alloc] initWithURL:url]);
709 [request setValue:(NSString *)webPage->userAgent(url) forHTTPHeaderField:@"User-Agent"];
711 if (CFURLStorageSessionRef storageSession = webPage->corePage()->mainFrame().loader().networkingContext()->storageSession().platformSession())
712 return WKCachedResponseForRequest(storageSession, request.get());
714 return [[NSURLCache sharedURLCache] cachedResponseForRequest:request.get()];
717 String WebPage::cachedSuggestedFilenameForURL(const URL& url)
719 return [[cachedResponseForURL(this, url) response] suggestedFilename];
722 String WebPage::cachedResponseMIMETypeForURL(const URL& url)
724 return [[cachedResponseForURL(this, url) response] MIMEType];
727 PassRefPtr<SharedBuffer> WebPage::cachedResponseDataForURL(const URL& url)
729 return SharedBuffer::wrapNSData([cachedResponseForURL(this, url) data]);
732 bool WebPage::platformCanHandleRequest(const WebCore::ResourceRequest& request)
734 if ([NSURLConnection canHandleRequest:request.nsURLRequest(DoNotUpdateHTTPBody)])
737 // FIXME: Return true if this scheme is any one WebKit2 knows how to handle.
738 return request.url().protocolIs("applewebdata");
741 void WebPage::shouldDelayWindowOrderingEvent(const WebKit::WebMouseEvent& event, bool& result)
743 Frame& frame = m_page->focusController().focusedOrMainFrame();
745 #if ENABLE(DRAG_SUPPORT)
746 HitTestResult hitResult = frame.eventHandler().hitTestResultAtPoint(frame.view()->windowToContents(event.position()), HitTestRequest::ReadOnly | HitTestRequest::Active);
747 if (hitResult.isSelected())
748 result = frame.eventHandler().eventMayStartDrag(platform(event));
754 void WebPage::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event, bool& result)
758 if (WebProcess::singleton().parentProcessConnection()->inSendSync()) {
759 // In case we're already inside a sendSync message, it's possible that the page is in a
760 // transitionary state, so any hit-testing could cause crashes so we just return early in that case.
764 Frame& frame = m_page->focusController().focusedOrMainFrame();
766 HitTestResult hitResult = frame.eventHandler().hitTestResultAtPoint(frame.view()->windowToContents(event.position()), HitTestRequest::ReadOnly | HitTestRequest::Active);
767 frame.eventHandler().setActivationEventNumber(eventNumber);
768 #if ENABLE(DRAG_SUPPORT)
769 if (hitResult.isSelected())
770 result = frame.eventHandler().eventMayStartDrag(platform(event));
773 result = !!hitResult.scrollbar();
776 void WebPage::setTopOverhangImage(PassRefPtr<WebImage> image)
778 FrameView* frameView = m_mainFrame->coreFrame()->view();
782 GraphicsLayer* layer = frameView->setWantsLayerForTopOverHangArea(image.get());
786 layer->setSize(image->size());
787 layer->setPosition(FloatPoint(0, -image->size().height()));
789 RetainPtr<CGImageRef> cgImage = image->bitmap()->makeCGImageCopy();
790 layer->platformLayer().contents = (id)cgImage.get();
793 void WebPage::setBottomOverhangImage(PassRefPtr<WebImage> image)
795 FrameView* frameView = m_mainFrame->coreFrame()->view();
799 GraphicsLayer* layer = frameView->setWantsLayerForBottomOverHangArea(image.get());
803 layer->setSize(image->size());
805 RetainPtr<CGImageRef> cgImage = image->bitmap()->makeCGImageCopy();
806 layer->platformLayer().contents = (id)cgImage.get();
809 void WebPage::updateHeaderAndFooterLayersForDeviceScaleChange(float scaleFactor)
812 m_headerBanner->didChangeDeviceScaleFactor(scaleFactor);
814 m_footerBanner->didChangeDeviceScaleFactor(scaleFactor);
817 void WebPage::computePagesForPrintingPDFDocument(uint64_t frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects)
819 ASSERT(resultPageRects.isEmpty());
820 WebFrame* frame = WebProcess::singleton().webFrame(frameID);
821 Frame* coreFrame = frame ? frame->coreFrame() : 0;
822 RetainPtr<PDFDocument> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame) : 0;
823 if ([pdfDocument allowsPrinting]) {
824 NSUInteger pageCount = [pdfDocument pageCount];
825 IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
826 for (NSUInteger i = 1; i <= pageCount; ++i) {
827 resultPageRects.append(pageRect);
828 pageRect.move(0, pageRect.height());
833 static inline CGFloat roundCGFloat(CGFloat f)
835 if (sizeof(CGFloat) == sizeof(float))
836 return roundf(static_cast<float>(f));
837 return static_cast<CGFloat>(round(f));
840 static void drawPDFPage(PDFDocument *pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize)
842 CGContextSaveGState(context);
844 CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor);
846 PDFPage *pdfPage = [pdfDocument pageAtIndex:pageIndex];
847 NSRect cropBox = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
848 if (NSIsEmptyRect(cropBox))
849 cropBox = [pdfPage boundsForBox:kPDFDisplayBoxMediaBox];
851 cropBox = NSIntersectionRect(cropBox, [pdfPage boundsForBox:kPDFDisplayBoxMediaBox]);
853 // Always auto-rotate PDF content regardless of the paper orientation.
854 NSInteger rotation = [pdfPage rotation];
855 if (rotation == 90 || rotation == 270)
856 std::swap(cropBox.size.width, cropBox.size.height);
858 bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height);
860 std::swap(cropBox.size.width, cropBox.size.height);
863 CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width;
864 CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height;
865 if (widthDifference || heightDifference)
866 CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2));
869 CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble));
870 CGContextTranslateCTM(context, 0, -cropBox.size.width);
873 [NSGraphicsContext saveGraphicsState];
874 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO]];
875 #pragma clang diagnostic push
876 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
877 [pdfPage drawWithBox:kPDFDisplayBoxCropBox];
878 #pragma clang diagnostic pop
879 [NSGraphicsContext restoreGraphicsState];
881 CGAffineTransform transform = CGContextGetCTM(context);
883 for (PDFAnnotation *annotation in [pdfPage annotations]) {
884 if (![annotation isKindOfClass:pdfAnnotationLinkClass()])
887 #pragma clang diagnostic push
888 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
889 PDFAnnotationLink *linkAnnotation = (PDFAnnotationLink *)annotation;
890 #pragma clang diagnostic pop
891 NSURL *url = [linkAnnotation URL];
895 CGRect urlRect = NSRectToCGRect([linkAnnotation bounds]);
896 CGRect transformedRect = CGRectApplyAffineTransform(urlRect, transform);
897 CGPDFContextSetURLForRect(context, (CFURLRef)url, transformedRect);
900 CGContextRestoreGState(context);
903 void WebPage::drawPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::IntRect& rect)
905 NSUInteger pageCount = [pdfDocument pageCount];
906 IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
907 IntRect pageRect(IntPoint(), paperSize);
908 for (NSUInteger i = 0; i < pageCount; ++i) {
909 if (pageRect.intersects(rect)) {
910 CGContextSaveGState(context);
912 CGContextTranslateCTM(context, pageRect.x() - rect.x(), pageRect.y() - rect.y());
913 drawPDFPage(pdfDocument, i, context, printInfo.pageSetupScaleFactor, paperSize);
915 CGContextRestoreGState(context);
917 pageRect.move(0, pageRect.height());
921 void WebPage::drawPagesToPDFFromPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, uint32_t first, uint32_t count)
923 NSUInteger pageCount = [pdfDocument pageCount];
924 for (uint32_t page = first; page < first + count; ++page) {
925 if (page >= pageCount)
928 RetainPtr<CFDictionaryRef> pageInfo = adoptCF(CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
930 CGPDFContextBeginPage(context, pageInfo.get());
931 drawPDFPage(pdfDocument, page, context, printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight));
932 CGPDFContextEndPage(context);
937 WebCore::WebGLLoadPolicy WebPage::webGLPolicyForURL(WebFrame* frame, const String& url)
939 uint32_t policyResult = 0;
941 if (sendSync(Messages::WebPageProxy::WebGLPolicyForURL(url), Messages::WebPageProxy::WebGLPolicyForURL::Reply(policyResult)))
942 return static_cast<WebGLLoadPolicy>(policyResult);
944 return WebGLAllowCreation;
947 WebCore::WebGLLoadPolicy WebPage::resolveWebGLPolicyForURL(WebFrame* frame, const String& url)
949 uint32_t policyResult = 0;
951 if (sendSync(Messages::WebPageProxy::ResolveWebGLPolicyForURL(url), Messages::WebPageProxy::ResolveWebGLPolicyForURL::Reply(policyResult)))
952 return static_cast<WebGLLoadPolicy>(policyResult);
954 return WebGLAllowCreation;
956 #endif // ENABLE(WEBGL)
958 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
959 void WebPage::handleTelephoneNumberClick(const String& number, const IntPoint& point)
961 send(Messages::WebPageProxy::ShowTelephoneNumberMenu(number, point));
965 #if ENABLE(SERVICE_CONTROLS)
966 void WebPage::handleSelectionServiceClick(FrameSelection& selection, const Vector<String>& phoneNumbers, const IntPoint& point)
968 RefPtr<Range> range = selection.selection().firstRange();
972 NSAttributedString *attributedSelection = attributedStringFromRange(*range);
973 if (!attributedSelection)
976 NSData *selectionData = [attributedSelection RTFDFromRange:NSMakeRange(0, attributedSelection.length) documentAttributes:@{ }];
978 Vector<uint8_t> selectionDataVector;
979 selectionDataVector.append(reinterpret_cast<const uint8_t*>(selectionData.bytes), selectionData.length);
981 send(Messages::WebPageProxy::ShowContextMenu(ContextMenuContextData(point, selectionDataVector, phoneNumbers, selection.selection().isContentEditable()), UserData()));
985 String WebPage::platformUserAgent(const URL&) const
990 void WebPage::performImmediateActionHitTestAtLocation(WebCore::FloatPoint locationInViewCoordinates)
994 MainFrame& mainFrame = corePage()->mainFrame();
995 if (!mainFrame.view() || !mainFrame.view()->renderView()) {
996 send(Messages::WebPageProxy::DidPerformImmediateActionHitTest(WebHitTestResultData(), false, UserData()));
1000 IntPoint locationInContentCoordinates = mainFrame.view()->rootViewToContents(roundedIntPoint(locationInViewCoordinates));
1001 HitTestResult hitTestResult = mainFrame.eventHandler().hitTestResultAtPoint(locationInContentCoordinates);
1003 bool immediateActionHitTestPreventsDefault = false;
1004 Element* element = hitTestResult.targetElement();
1006 mainFrame.eventHandler().setImmediateActionStage(ImmediateActionStage::PerformedHitTest);
1008 immediateActionHitTestPreventsDefault = element->dispatchMouseForceWillBegin();
1010 WebHitTestResultData immediateActionResult(hitTestResult);
1012 RefPtr<Range> selectionRange = corePage()->focusController().focusedOrMainFrame().selection().selection().firstRange();
1014 URL absoluteLinkURL = hitTestResult.absoluteLinkURL();
1015 Element *URLElement = hitTestResult.URLElement();
1016 if (!absoluteLinkURL.isEmpty() && URLElement) {
1017 RefPtr<Range> linkRange = rangeOfContents(*URLElement);
1018 immediateActionResult.linkTextIndicator = TextIndicator::createWithRange(*linkRange, TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges, TextIndicatorPresentationTransition::FadeIn);
1021 NSDictionary *options = nil;
1022 RefPtr<Range> lookupRange = lookupTextAtLocation(locationInViewCoordinates, &options);
1023 immediateActionResult.lookupText = lookupRange ? lookupRange->text() : String();
1026 if (Node* node = hitTestResult.innerNode()) {
1027 if (Frame* hitTestResultFrame = node->document().frame())
1028 immediateActionResult.dictionaryPopupInfo = dictionaryPopupInfoForRange(hitTestResultFrame, *lookupRange.get(), &options, TextIndicatorPresentationTransition::FadeIn);
1032 bool pageOverlayDidOverrideDataDetectors = false;
1033 for (const auto& overlay : mainFrame.pageOverlayController().pageOverlays()) {
1034 WebPageOverlay* webOverlay = WebPageOverlay::fromCoreOverlay(*overlay);
1038 RefPtr<Range> mainResultRange;
1039 DDActionContext *actionContext = webOverlay->actionContextForResultAtPoint(locationInContentCoordinates, mainResultRange);
1040 if (!actionContext || !mainResultRange)
1043 pageOverlayDidOverrideDataDetectors = true;
1044 immediateActionResult.detectedDataActionContext = actionContext;
1046 Vector<FloatQuad> quads;
1047 mainResultRange->absoluteTextQuads(quads);
1048 FloatRect detectedDataBoundingBox;
1049 FrameView* frameView = mainResultRange->ownerDocument().view();
1050 for (const auto& quad : quads)
1051 detectedDataBoundingBox.unite(frameView->contentsToWindow(quad.enclosingBoundingBox()));
1053 immediateActionResult.detectedDataBoundingBox = detectedDataBoundingBox;
1054 immediateActionResult.detectedDataTextIndicator = TextIndicator::createWithRange(*mainResultRange, TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges, TextIndicatorPresentationTransition::FadeIn);
1055 immediateActionResult.detectedDataOriginatingPageOverlay = overlay->pageOverlayID();
1060 // FIXME: Avoid scanning if we will just throw away the result (e.g. we're over a link).
1061 if (!pageOverlayDidOverrideDataDetectors && hitTestResult.innerNode() && (hitTestResult.innerNode()->isTextNode() || hitTestResult.isOverTextInsideFormControlElement())) {
1062 FloatRect detectedDataBoundingBox;
1063 RefPtr<Range> detectedDataRange;
1064 immediateActionResult.detectedDataActionContext = DataDetection::detectItemAroundHitTestResult(hitTestResult, detectedDataBoundingBox, detectedDataRange);
1065 if (immediateActionResult.detectedDataActionContext && detectedDataRange) {
1066 immediateActionResult.detectedDataBoundingBox = detectedDataBoundingBox;
1067 immediateActionResult.detectedDataTextIndicator = TextIndicator::createWithRange(*detectedDataRange, TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges, TextIndicatorPresentationTransition::FadeIn);
1071 #if ENABLE(PDFKIT_PLUGIN)
1072 // See if we have a PDF
1073 if (element && is<HTMLPlugInImageElement>(*element)) {
1074 HTMLPlugInImageElement& pluginImageElement = downcast<HTMLPlugInImageElement>(*element);
1075 PluginView* pluginView = reinterpret_cast<PluginView*>(pluginImageElement.pluginWidget());
1076 Plugin* plugin = pluginView ? pluginView->plugin() : nullptr;
1077 if (is<PDFPlugin>(plugin)) {
1078 PDFPlugin* pdfPugin = downcast<PDFPlugin>(plugin);
1079 // FIXME: We don't have API to identify images inside PDFs based on position.
1080 NSDictionary *options = nil;
1081 PDFSelection *selection = nil;
1082 String selectedText = pdfPugin->lookupTextAtLocation(locationInViewCoordinates, immediateActionResult, &selection, &options);
1083 if (!selectedText.isEmpty()) {
1084 if (element->document().isPluginDocument()) {
1085 // FIXME(144030): Focus does not seem to get set to the PDF when invoking the menu.
1086 PluginDocument& pluginDocument = static_cast<PluginDocument&>(element->document());
1087 pluginDocument.setFocusedElement(element);
1090 immediateActionResult.lookupText = selectedText;
1091 immediateActionResult.isTextNode = true;
1092 immediateActionResult.isSelected = true;
1093 immediateActionResult.allowsCopy = true;
1095 immediateActionResult.dictionaryPopupInfo = dictionaryPopupInfoForSelectionInPDFPlugin(selection, *pdfPugin, &options, TextIndicatorPresentationTransition::FadeIn);
1101 RefPtr<API::Object> userData;
1102 injectedBundleContextMenuClient().prepareForImmediateAction(*this, hitTestResult, userData);
1104 send(Messages::WebPageProxy::DidPerformImmediateActionHitTest(immediateActionResult, immediateActionHitTestPreventsDefault, UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
1107 RefPtr<WebCore::Range> WebPage::lookupTextAtLocation(FloatPoint locationInViewCoordinates, NSDictionary **options)
1109 MainFrame& mainFrame = corePage()->mainFrame();
1110 if (!mainFrame.view() || !mainFrame.view()->renderView())
1113 IntPoint point = roundedIntPoint(locationInViewCoordinates);
1114 HitTestResult result = mainFrame.eventHandler().hitTestResultAtPoint(m_page->mainFrame().view()->windowToContents(point));
1115 return DictionaryLookup::rangeAtHitTestResult(result, options);
1118 void WebPage::immediateActionDidUpdate()
1120 m_page->mainFrame().eventHandler().setImmediateActionStage(ImmediateActionStage::ActionUpdated);
1123 void WebPage::immediateActionDidCancel()
1125 ImmediateActionStage lastStage = m_page->mainFrame().eventHandler().immediateActionStage();
1126 if (lastStage == ImmediateActionStage::ActionUpdated)
1127 m_page->mainFrame().eventHandler().setImmediateActionStage(ImmediateActionStage::ActionCancelledAfterUpdate);
1129 m_page->mainFrame().eventHandler().setImmediateActionStage(ImmediateActionStage::ActionCancelledWithoutUpdate);
1132 void WebPage::immediateActionDidComplete()
1134 m_page->mainFrame().eventHandler().setImmediateActionStage(ImmediateActionStage::ActionCompleted);
1137 void WebPage::dataDetectorsDidPresentUI(PageOverlay::PageOverlayID overlayID)
1139 MainFrame& mainFrame = corePage()->mainFrame();
1140 for (const auto& overlay : mainFrame.pageOverlayController().pageOverlays()) {
1141 if (overlay->pageOverlayID() == overlayID) {
1142 if (WebPageOverlay* webOverlay = WebPageOverlay::fromCoreOverlay(*overlay))
1143 webOverlay->dataDetectorsDidPresentUI();
1149 void WebPage::dataDetectorsDidChangeUI(PageOverlay::PageOverlayID overlayID)
1151 MainFrame& mainFrame = corePage()->mainFrame();
1152 for (const auto& overlay : mainFrame.pageOverlayController().pageOverlays()) {
1153 if (overlay->pageOverlayID() == overlayID) {
1154 if (WebPageOverlay* webOverlay = WebPageOverlay::fromCoreOverlay(*overlay))
1155 webOverlay->dataDetectorsDidChangeUI();
1161 void WebPage::dataDetectorsDidHideUI(PageOverlay::PageOverlayID overlayID)
1163 MainFrame& mainFrame = corePage()->mainFrame();
1165 // Dispatching a fake mouse event will allow clients to display any UI that is normally displayed on hover.
1166 mainFrame.eventHandler().dispatchFakeMouseMoveEventSoon();
1168 for (const auto& overlay : mainFrame.pageOverlayController().pageOverlays()) {
1169 if (overlay->pageOverlayID() == overlayID) {
1170 if (WebPageOverlay* webOverlay = WebPageOverlay::fromCoreOverlay(*overlay))
1171 webOverlay->dataDetectorsDidHideUI();
1177 void WebPage::setFont(const String& fontFamily, double fontSize, uint64_t fontTraits)
1179 Frame& frame = m_page->focusController().focusedOrMainFrame();
1180 frame.editor().applyFontStyles(fontFamily, fontSize, fontTraits);
1183 #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
1184 void WebPage::playbackTargetSelected(uint64_t contextId, const WebCore::MediaPlaybackTargetContext& targetContext) const
1186 switch (targetContext.type()) {
1187 case MediaPlaybackTargetContext::AVOutputContextType:
1188 m_page->setPlaybackTarget(contextId, WebCore::MediaPlaybackTargetMac::create(targetContext.avOutputContext()));
1190 case MediaPlaybackTargetContext::MockType:
1191 m_page->setPlaybackTarget(contextId, WebCore::MediaPlaybackTargetMock::create(targetContext.mockDeviceName(), targetContext.mockState()));
1193 case MediaPlaybackTargetContext::None:
1194 ASSERT_NOT_REACHED();
1199 void WebPage::playbackTargetAvailabilityDidChange(uint64_t contextId, bool changed)
1201 m_page->playbackTargetAvailabilityDidChange(contextId, changed);
1204 void WebPage::setShouldPlayToPlaybackTarget(uint64_t contextId, bool shouldPlay)
1206 m_page->setShouldPlayToPlaybackTarget(contextId, shouldPlay);
1211 } // namespace WebKit
1213 #endif // PLATFORM(MAC)