Reduce PassRefPtr in WebKit2 - 3
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / WebPageProxyMac.mm
1 /*
2  * Copyright (C) 2010, 2011, 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 "WebPageProxy.h"
28
29 #if PLATFORM(MAC)
30
31 #import "APIUIClient.h"
32 #import "AttributedString.h"
33 #import "ColorSpaceData.h"
34 #import "DataReference.h"
35 #import "DictionaryPopupInfo.h"
36 #import "EditingRange.h"
37 #import "EditorState.h"
38 #import "MenuUtilities.h"
39 #import "NativeWebKeyboardEvent.h"
40 #import "PageClient.h"
41 #import "PageClientImpl.h"
42 #import "PluginComplexTextInputState.h"
43 #import "StringUtilities.h"
44 #import "TextChecker.h"
45 #import "WKBrowsingContextControllerInternal.h"
46 #import "WKSharingServicePickerDelegate.h"
47 #import "WebContextMenuProxyMac.h"
48 #import "WebPageMessages.h"
49 #import "WebProcessProxy.h"
50 #import <WebCore/DictationAlternative.h>
51 #import <WebCore/GraphicsLayer.h>
52 #import <WebCore/RuntimeApplicationChecks.h>
53 #import <WebCore/SharedBuffer.h>
54 #import <WebCore/TextAlternativeWithRange.h>
55 #import <WebCore/UserAgent.h>
56 #import <mach-o/dyld.h>
57 #import <wtf/NeverDestroyed.h>
58 #import <wtf/text/StringConcatenate.h>
59
60 @interface NSApplication (Details)
61 - (void)speakString:(NSString *)string;
62 @end
63
64 #define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, process().connection())
65 #define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(m_process->checkURLReceivedFromWebProcess(url), m_process->connection())
66
67 using namespace WebCore;
68
69 namespace WebKit {
70
71 static inline bool expectsLegacyImplicitRubberBandControl()
72 {
73     if (applicationIsSafari()) {
74         const int32_t firstVersionOfSafariNotExpectingImplicitRubberBandControl = 0x021A0F00; // 538.15.0
75         bool linkedAgainstSafariExpectingImplicitRubberBandControl = NSVersionOfLinkTimeLibrary("Safari") < firstVersionOfSafariNotExpectingImplicitRubberBandControl;
76         return linkedAgainstSafariExpectingImplicitRubberBandControl;
77     }
78
79     const int32_t firstVersionOfWebKit2WithNoImplicitRubberBandControl = 0x021A0200; // 538.2.0
80     int32_t linkedWebKit2Version = NSVersionOfLinkTimeLibrary("WebKit2");
81     return linkedWebKit2Version != -1 && linkedWebKit2Version < firstVersionOfWebKit2WithNoImplicitRubberBandControl;
82 }
83
84 void WebPageProxy::platformInitialize()
85 {
86     static bool clientExpectsLegacyImplicitRubberBandControl = expectsLegacyImplicitRubberBandControl();
87     setShouldUseImplicitRubberBandControl(clientExpectsLegacyImplicitRubberBandControl);
88 }
89
90 static String webKitBundleVersionString()
91 {
92     return [[NSBundle bundleForClass:NSClassFromString(@"WKView")] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
93 }
94
95 String WebPageProxy::standardUserAgent(const String& applicationNameForUserAgent)
96 {
97     return standardUserAgentWithApplicationName(applicationNameForUserAgent, webKitBundleVersionString());
98 }
99
100 void WebPageProxy::getIsSpeaking(bool& isSpeaking)
101 {
102     isSpeaking = [NSApp isSpeaking];
103 }
104
105 void WebPageProxy::speak(const String& string)
106 {
107     [NSApp speakString:nsStringFromWebCoreString(string)];
108 }
109
110 void WebPageProxy::stopSpeaking()
111 {
112     [NSApp stopSpeaking:nil];
113 }
114
115 void WebPageProxy::searchWithSpotlight(const String& string)
116 {
117     [[NSWorkspace sharedWorkspace] showSearchResultsForQueryString:nsStringFromWebCoreString(string)];
118 }
119     
120 void WebPageProxy::searchTheWeb(const String& string)
121 {
122     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
123     [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
124     [pasteboard setString:string forType:NSStringPboardType];
125     
126     NSPerformService(@"Search With %WebSearchProvider@", pasteboard);
127 }
128
129 void WebPageProxy::windowAndViewFramesChanged(const FloatRect& viewFrameInWindowCoordinates, const FloatPoint& accessibilityViewCoordinates)
130 {
131     if (!isValid())
132         return;
133
134     // In case the UI client overrides getWindowFrame(), we call it here to make sure we send the appropriate window frame.
135     FloatRect windowFrameInScreenCoordinates = m_uiClient->windowFrame(this);
136     FloatRect windowFrameInUnflippedScreenCoordinates = m_pageClient.convertToUserSpace(windowFrameInScreenCoordinates);
137
138     process().send(Messages::WebPage::WindowAndViewFramesChanged(windowFrameInScreenCoordinates, windowFrameInUnflippedScreenCoordinates, viewFrameInWindowCoordinates, accessibilityViewCoordinates), m_pageID);
139 }
140
141 void WebPageProxy::setMainFrameIsScrollable(bool isScrollable)
142 {
143     if (!isValid())
144         return;
145
146     process().send(Messages::WebPage::SetMainFrameIsScrollable(isScrollable), m_pageID);
147 }
148
149 #if !USE(ASYNC_NSTEXTINPUTCLIENT)
150
151 void WebPageProxy::setComposition(const String& text, Vector<CompositionUnderline> underlines, const EditingRange& selectionRange, const EditingRange& replacementRange)
152 {
153     if (!isValid()) {
154         // If this fails, we should call -discardMarkedText on input context to notify the input method.
155         // This will happen naturally later, as part of reloading the page.
156         return;
157     }
158
159     process().sendSync(Messages::WebPage::SetComposition(text, underlines, selectionRange, replacementRange), Messages::WebPage::SetComposition::Reply(m_editorState), m_pageID);
160 }
161
162 void WebPageProxy::confirmComposition()
163 {
164     if (!isValid())
165         return;
166
167     process().sendSync(Messages::WebPage::ConfirmComposition(), Messages::WebPage::ConfirmComposition::Reply(m_editorState), m_pageID);
168 }
169
170 bool WebPageProxy::insertText(const String& text, const EditingRange& replacementRange)
171 {
172     if (!isValid())
173         return true;
174
175     bool handled = true;
176     process().sendSync(Messages::WebPage::InsertText(text, replacementRange), Messages::WebPage::InsertText::Reply(handled, m_editorState), m_pageID);
177 #if PLATFORM(MAC) && !USE(ASYNC_NSTEXTINPUTCLIENT)
178     m_temporarilyClosedComposition = false;
179 #endif
180
181     return handled;
182 }
183
184 bool WebPageProxy::insertDictatedText(const String& text, const EditingRange& replacementRange, const Vector<TextAlternativeWithRange>& dictationAlternativesWithRange)
185 {
186 #if USE(DICTATION_ALTERNATIVES)
187     if (dictationAlternativesWithRange.isEmpty())
188         return insertText(text, replacementRange);
189
190     if (!isValid())
191         return true;
192
193     Vector<DictationAlternative> dictationAlternatives;
194
195     for (size_t i = 0; i < dictationAlternativesWithRange.size(); ++i) {
196         const TextAlternativeWithRange& alternativeWithRange = dictationAlternativesWithRange[i];
197         uint64_t dictationContext = m_pageClient.addDictationAlternatives(alternativeWithRange.alternatives);
198         if (dictationContext)
199             dictationAlternatives.append(DictationAlternative(alternativeWithRange.range.location, alternativeWithRange.range.length, dictationContext));
200     }
201
202     if (dictationAlternatives.isEmpty())
203         return insertText(text, replacementRange);
204
205     bool handled = true;
206     process().sendSync(Messages::WebPage::InsertDictatedText(text, replacementRange, dictationAlternatives), Messages::WebPage::InsertDictatedText::Reply(handled, m_editorState), m_pageID);
207     return handled;
208 #else
209     return insertText(text, replacementRange);
210 #endif
211 }
212
213 void WebPageProxy::getMarkedRange(EditingRange& result)
214 {
215     result = EditingRange();
216
217     if (!isValid())
218         return;
219
220     process().sendSync(Messages::WebPage::GetMarkedRange(), Messages::WebPage::GetMarkedRange::Reply(result), m_pageID);
221     MESSAGE_CHECK(result.isValid());
222 }
223
224 void WebPageProxy::getSelectedRange(EditingRange& result)
225 {
226     result = EditingRange();
227
228     if (!isValid())
229         return;
230
231     process().sendSync(Messages::WebPage::GetSelectedRange(), Messages::WebPage::GetSelectedRange::Reply(result), m_pageID);
232     MESSAGE_CHECK(result.isValid());
233 }
234
235 void WebPageProxy::getAttributedSubstringFromRange(const EditingRange& range, AttributedString& result)
236 {
237     if (!isValid())
238         return;
239     process().sendSync(Messages::WebPage::GetAttributedSubstringFromRange(range), Messages::WebPage::GetAttributedSubstringFromRange::Reply(result), m_pageID);
240 }
241
242 uint64_t WebPageProxy::characterIndexForPoint(const IntPoint point)
243 {
244     if (!isValid())
245         return 0;
246
247     uint64_t result = 0;
248     process().sendSync(Messages::WebPage::CharacterIndexForPoint(point), Messages::WebPage::CharacterIndexForPoint::Reply(result), m_pageID);
249     return result;
250 }
251
252 IntRect WebPageProxy::firstRectForCharacterRange(const EditingRange& range)
253 {
254     if (!isValid())
255         return IntRect();
256
257     IntRect resultRect;
258     process().sendSync(Messages::WebPage::FirstRectForCharacterRange(range), Messages::WebPage::FirstRectForCharacterRange::Reply(resultRect), m_pageID);
259     return resultRect;
260 }
261
262 bool WebPageProxy::executeKeypressCommands(const Vector<WebCore::KeypressCommand>& commands)
263 {
264     if (!isValid())
265         return false;
266
267     bool result = false;
268     process().sendSync(Messages::WebPage::ExecuteKeypressCommands(commands), Messages::WebPage::ExecuteKeypressCommands::Reply(result, m_editorState), m_pageID);
269 #if PLATFORM(MAC) && !USE(ASYNC_NSTEXTINPUTCLIENT)
270     m_temporarilyClosedComposition = false;
271 #endif
272     return result;
273 }
274
275 void WebPageProxy::cancelComposition()
276 {
277     if (!isValid())
278         return;
279
280     process().sendSync(Messages::WebPage::CancelComposition(), Messages::WebPage::CancelComposition::Reply(m_editorState), m_pageID);
281 }
282
283 #endif // !USE(ASYNC_NSTEXTINPUTCLIENT)
284
285 void WebPageProxy::insertDictatedTextAsync(const String& text, const EditingRange& replacementRange, const Vector<TextAlternativeWithRange>& dictationAlternativesWithRange, bool registerUndoGroup)
286 {
287 #if USE(DICTATION_ALTERNATIVES)
288     if (!isValid())
289         return;
290
291     Vector<DictationAlternative> dictationAlternatives;
292
293     for (const TextAlternativeWithRange& alternativeWithRange : dictationAlternativesWithRange) {
294         uint64_t dictationContext = m_pageClient.addDictationAlternatives(alternativeWithRange.alternatives);
295         if (dictationContext)
296             dictationAlternatives.append(DictationAlternative(alternativeWithRange.range.location, alternativeWithRange.range.length, dictationContext));
297     }
298
299     if (dictationAlternatives.isEmpty()) {
300         insertTextAsync(text, replacementRange, registerUndoGroup);
301         return;
302     }
303
304     process().send(Messages::WebPage::InsertDictatedTextAsync(text, replacementRange, dictationAlternatives, registerUndoGroup), m_pageID);
305 #else
306     insertTextAsync(text, replacementRange, registerUndoGroup);
307 #endif
308 }
309
310 void WebPageProxy::attributedSubstringForCharacterRangeAsync(const EditingRange& range, std::function<void (const AttributedString&, const EditingRange&, CallbackBase::Error)> callbackFunction)
311 {
312     if (!isValid()) {
313         callbackFunction(AttributedString(), EditingRange(), CallbackBase::Error::Unknown);
314         return;
315     }
316
317     uint64_t callbackID = m_callbacks.put(WTF::move(callbackFunction), m_process->throttler().backgroundActivityToken());
318
319     process().send(Messages::WebPage::AttributedSubstringForCharacterRangeAsync(range, callbackID), m_pageID);
320 }
321
322 void WebPageProxy::attributedStringForCharacterRangeCallback(const AttributedString& string, const EditingRange& actualRange, uint64_t callbackID)
323 {
324     MESSAGE_CHECK(actualRange.isValid());
325
326     auto callback = m_callbacks.take<AttributedStringForCharacterRangeCallback>(callbackID);
327     if (!callback) {
328         // FIXME: Log error or assert.
329         // this can validly happen if a load invalidated the callback, though
330         return;
331     }
332
333     callback->performCallbackWithReturnValue(string, actualRange);
334 }
335
336 void WebPageProxy::fontAtSelection(std::function<void (const String&, double, bool, CallbackBase::Error)>callbackFunction)
337 {
338     if (!isValid()) {
339         callbackFunction(String(), 0, false, CallbackBase::Error::Unknown);
340         return;
341     }
342     
343     uint64_t callbackID = m_callbacks.put(WTF::move(callbackFunction), m_process->throttler().backgroundActivityToken());
344     
345     process().send(Messages::WebPage::FontAtSelection(callbackID), m_pageID);
346 }
347
348 void WebPageProxy::fontAtSelectionCallback(const String& fontName, double fontSize, bool selectionHasMultipleFonts, uint64_t callbackID)
349 {
350     auto callback = m_callbacks.take<FontAtSelectionCallback>(callbackID);
351     if (!callback) {
352         // FIXME: Log error or assert.
353         // this can validly happen if a load invalidated the callback, though
354         return;
355     }
356     
357     callback->performCallbackWithReturnValue(fontName, fontSize, selectionHasMultipleFonts);
358 }
359
360 String WebPageProxy::stringSelectionForPasteboard()
361 {
362     String value;
363     if (!isValid())
364         return value;
365     
366     const auto messageTimeout = std::chrono::seconds(20);
367     process().sendSync(Messages::WebPage::GetStringSelectionForPasteboard(), Messages::WebPage::GetStringSelectionForPasteboard::Reply(value), m_pageID, messageTimeout);
368     return value;
369 }
370
371 RefPtr<WebCore::SharedBuffer> WebPageProxy::dataSelectionForPasteboard(const String& pasteboardType)
372 {
373     if (!isValid())
374         return nullptr;
375     SharedMemory::Handle handle;
376     uint64_t size = 0;
377     const auto messageTimeout = std::chrono::seconds(20);
378     process().sendSync(Messages::WebPage::GetDataSelectionForPasteboard(pasteboardType),
379                                                 Messages::WebPage::GetDataSelectionForPasteboard::Reply(handle, size), m_pageID, messageTimeout);
380     if (handle.isNull())
381         return nullptr;
382     RefPtr<SharedMemory> sharedMemoryBuffer = SharedMemory::map(handle, SharedMemory::Protection::ReadOnly);
383     return SharedBuffer::create(static_cast<unsigned char *>(sharedMemoryBuffer->data()), size);
384 }
385
386 bool WebPageProxy::readSelectionFromPasteboard(const String& pasteboardName)
387 {
388     if (!isValid())
389         return false;
390
391     bool result = false;
392     const auto messageTimeout = std::chrono::seconds(20);
393     process().sendSync(Messages::WebPage::ReadSelectionFromPasteboard(pasteboardName), Messages::WebPage::ReadSelectionFromPasteboard::Reply(result), m_pageID, messageTimeout);
394     return result;
395 }
396
397 #if ENABLE(SERVICE_CONTROLS)
398 void WebPageProxy::replaceSelectionWithPasteboardData(const Vector<String>& types, const IPC::DataReference& data)
399 {
400     process().send(Messages::WebPage::ReplaceSelectionWithPasteboardData(types, data), m_pageID);
401 }
402 #endif
403
404 #if ENABLE(DRAG_SUPPORT)
405 void WebPageProxy::setDragImage(const WebCore::IntPoint& clientPosition, const ShareableBitmap::Handle& dragImageHandle, bool isLinkDrag)
406 {
407     if (RefPtr<ShareableBitmap> dragImage = ShareableBitmap::create(dragImageHandle))
408         m_pageClient.setDragImage(clientPosition, dragImage.release(), isLinkDrag);
409
410     process().send(Messages::WebPage::DidStartDrag(), m_pageID);
411 }
412
413 void WebPageProxy::setPromisedDataForImage(const String& pasteboardName, const SharedMemory::Handle& imageHandle, uint64_t imageSize, const String& filename, const String& extension,
414                                    const String& title, const String& url, const String& visibleURL, const SharedMemory::Handle& archiveHandle, uint64_t archiveSize)
415 {
416     MESSAGE_CHECK_URL(url);
417     MESSAGE_CHECK_URL(visibleURL);
418     RefPtr<SharedMemory> sharedMemoryImage = SharedMemory::map(imageHandle, SharedMemory::Protection::ReadOnly);
419     RefPtr<SharedBuffer> imageBuffer = SharedBuffer::create(static_cast<unsigned char*>(sharedMemoryImage->data()), imageSize);
420     RefPtr<SharedBuffer> archiveBuffer;
421     
422     if (!archiveHandle.isNull()) {
423         RefPtr<SharedMemory> sharedMemoryArchive = SharedMemory::map(archiveHandle, SharedMemory::Protection::ReadOnly);
424         archiveBuffer = SharedBuffer::create(static_cast<unsigned char*>(sharedMemoryArchive->data()), archiveSize);
425     }
426     m_pageClient.setPromisedDataForImage(pasteboardName, imageBuffer, filename, extension, title, url, visibleURL, archiveBuffer);
427 }
428
429 #if ENABLE(ATTACHMENT_ELEMENT)
430 void WebPageProxy::setPromisedDataForAttachment(const String& pasteboardName, const String& filename, const String& extension, const String& title, const String& url, const String& visibleURL)
431 {
432     MESSAGE_CHECK_URL(url);
433     MESSAGE_CHECK_URL(visibleURL);
434     m_pageClient.setPromisedDataForAttachment(pasteboardName, filename, extension, title, url, visibleURL);
435 }
436 #endif
437 #endif
438
439 void WebPageProxy::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
440 {
441     if (!isValid())
442         return;
443
444     process().send(Messages::WebPage::PerformDictionaryLookupAtLocation(point), m_pageID);
445 }
446
447 void WebPageProxy::performDictionaryLookupOfCurrentSelection()
448 {
449     if (!isValid())
450         return;
451
452     process().send(Messages::WebPage::PerformDictionaryLookupOfCurrentSelection(), m_pageID);
453 }
454
455 // Complex text input support for plug-ins.
456 void WebPageProxy::sendComplexTextInputToPlugin(uint64_t pluginComplexTextInputIdentifier, const String& textInput)
457 {
458     if (!isValid())
459         return;
460     
461     process().send(Messages::WebPage::SendComplexTextInputToPlugin(pluginComplexTextInputIdentifier, textInput), m_pageID);
462 }
463
464 void WebPageProxy::uppercaseWord()
465 {
466     process().send(Messages::WebPage::UppercaseWord(), m_pageID);
467 }
468
469 void WebPageProxy::lowercaseWord()
470 {
471     process().send(Messages::WebPage::LowercaseWord(), m_pageID);
472 }
473
474 void WebPageProxy::capitalizeWord()
475 {
476     process().send(Messages::WebPage::CapitalizeWord(), m_pageID);
477 }
478
479 void WebPageProxy::setSmartInsertDeleteEnabled(bool isSmartInsertDeleteEnabled)
480 {
481     if (m_isSmartInsertDeleteEnabled == isSmartInsertDeleteEnabled)
482         return;
483
484     TextChecker::setSmartInsertDeleteEnabled(isSmartInsertDeleteEnabled);
485     m_isSmartInsertDeleteEnabled = isSmartInsertDeleteEnabled;
486     process().send(Messages::WebPage::SetSmartInsertDeleteEnabled(isSmartInsertDeleteEnabled), m_pageID);
487 }
488
489 void WebPageProxy::didPerformDictionaryLookup(const DictionaryPopupInfo& dictionaryPopupInfo)
490 {
491     m_pageClient.didPerformDictionaryLookup(dictionaryPopupInfo);
492 }
493     
494 void WebPageProxy::registerWebProcessAccessibilityToken(const IPC::DataReference& data)
495 {
496     if (!isValid())
497         return;
498     
499     m_pageClient.accessibilityWebProcessTokenReceived(data);
500 }    
501     
502 void WebPageProxy::makeFirstResponder()
503 {
504     m_pageClient.makeFirstResponder();
505 }
506
507 ColorSpaceData WebPageProxy::colorSpace()
508 {
509     return m_pageClient.colorSpace();
510 }
511
512 void WebPageProxy::registerUIProcessAccessibilityTokens(const IPC::DataReference& elementToken, const IPC::DataReference& windowToken)
513 {
514     if (!isValid())
515         return;
516
517     process().send(Messages::WebPage::RegisterUIProcessAccessibilityTokens(elementToken, windowToken), m_pageID);
518 }
519
520 void WebPageProxy::pluginFocusOrWindowFocusChanged(uint64_t pluginComplexTextInputIdentifier, bool pluginHasFocusAndWindowHasFocus)
521 {
522     m_pageClient.pluginFocusOrWindowFocusChanged(pluginComplexTextInputIdentifier, pluginHasFocusAndWindowHasFocus);
523 }
524
525 void WebPageProxy::setPluginComplexTextInputState(uint64_t pluginComplexTextInputIdentifier, uint64_t pluginComplexTextInputState)
526 {
527     MESSAGE_CHECK(isValidPluginComplexTextInputState(pluginComplexTextInputState));
528
529     m_pageClient.setPluginComplexTextInputState(pluginComplexTextInputIdentifier, static_cast<PluginComplexTextInputState>(pluginComplexTextInputState));
530 }
531
532 void WebPageProxy::executeSavedCommandBySelector(const String& selector, bool& handled)
533 {
534     MESSAGE_CHECK(isValidKeypressCommandName(selector));
535
536     handled = m_pageClient.executeSavedCommandBySelector(selector);
537 }
538
539 bool WebPageProxy::shouldDelayWindowOrderingForEvent(const WebKit::WebMouseEvent& event)
540 {
541     if (process().state() != WebProcessProxy::State::Running)
542         return false;
543
544     bool result = false;
545     const auto messageTimeout = std::chrono::seconds(3);
546     process().sendSync(Messages::WebPage::ShouldDelayWindowOrderingEvent(event), Messages::WebPage::ShouldDelayWindowOrderingEvent::Reply(result), m_pageID, messageTimeout);
547     return result;
548 }
549
550 bool WebPageProxy::acceptsFirstMouse(int eventNumber, const WebKit::WebMouseEvent& event)
551 {
552     if (!isValid())
553         return false;
554
555     bool result = false;
556     const auto messageTimeout = std::chrono::seconds(3);
557     process().sendSync(Messages::WebPage::AcceptsFirstMouse(eventNumber, event), Messages::WebPage::AcceptsFirstMouse::Reply(result), m_pageID, messageTimeout);
558     return result;
559 }
560
561 WKView* WebPageProxy::wkView() const
562 {
563     return m_pageClient.wkView();
564 }
565
566 void WebPageProxy::intrinsicContentSizeDidChange(const IntSize& intrinsicContentSize)
567 {
568     m_pageClient.intrinsicContentSizeDidChange(intrinsicContentSize);
569 }
570
571 void WebPageProxy::setAcceleratedCompositingRootLayer(LayerOrView* rootLayer)
572 {
573     m_pageClient.setAcceleratedCompositingRootLayer(rootLayer);
574 }
575
576 LayerOrView* WebPageProxy::acceleratedCompositingRootLayer() const
577 {
578     return m_pageClient.acceleratedCompositingRootLayer();
579 }
580
581 static NSString *temporaryPDFDirectoryPath()
582 {
583     static NSString *temporaryPDFDirectoryPath;
584
585     if (!temporaryPDFDirectoryPath) {
586         NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPDFs-XXXXXX"];
587         CString templateRepresentation = [temporaryDirectoryTemplate fileSystemRepresentation];
588
589         if (mkdtemp(templateRepresentation.mutableData()))
590             temporaryPDFDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:templateRepresentation.data() length:templateRepresentation.length()] copy];
591     }
592
593     return temporaryPDFDirectoryPath;
594 }
595
596 static NSString *pathToPDFOnDisk(const String& suggestedFilename)
597 {
598     NSString *pdfDirectoryPath = temporaryPDFDirectoryPath();
599     if (!pdfDirectoryPath) {
600         WTFLogAlways("Cannot create temporary PDF download directory.");
601         return nil;
602     }
603
604     NSString *path = [pdfDirectoryPath stringByAppendingPathComponent:suggestedFilename];
605
606     NSFileManager *fileManager = [NSFileManager defaultManager];
607     if ([fileManager fileExistsAtPath:path]) {
608         NSString *pathTemplatePrefix = [pdfDirectoryPath stringByAppendingPathComponent:@"XXXXXX-"];
609         NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:suggestedFilename];
610         CString pathTemplateRepresentation = [pathTemplate fileSystemRepresentation];
611
612         int fd = mkstemps(pathTemplateRepresentation.mutableData(), pathTemplateRepresentation.length() - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1);
613         if (fd < 0) {
614             WTFLogAlways("Cannot create PDF file in the temporary directory (%s).", suggestedFilename.utf8().data());
615             return nil;
616         }
617
618         close(fd);
619         path = [fileManager stringWithFileSystemRepresentation:pathTemplateRepresentation.data() length:pathTemplateRepresentation.length()];
620     }
621
622     return path;
623 }
624
625 void WebPageProxy::savePDFToTemporaryFolderAndOpenWithNativeApplicationRaw(const String& suggestedFilename, const String& originatingURLString, const uint8_t* data, unsigned long size, const String& pdfUUID)
626 {
627     // FIXME: Write originatingURLString to the file's originating URL metadata (perhaps WKSetMetadataURL?).
628     UNUSED_PARAM(originatingURLString);
629
630     if (!suggestedFilename.endsWith(".pdf", false)) {
631         WTFLogAlways("Cannot save file without .pdf extension to the temporary directory.");
632         return;
633     }
634
635     if (!size) {
636         WTFLogAlways("Cannot save empty PDF file to the temporary directory.");
637         return;
638     }
639
640     NSString *nsPath = pathToPDFOnDisk(suggestedFilename);
641
642     if (!nsPath)
643         return;
644
645     RetainPtr<NSNumber> permissions = adoptNS([[NSNumber alloc] initWithInt:S_IRUSR]);
646     RetainPtr<NSDictionary> fileAttributes = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys:permissions.get(), NSFilePosixPermissions, nil]);
647     RetainPtr<NSData> nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:(void*)data length:size freeWhenDone:NO]);
648
649     if (![[NSFileManager defaultManager] createFileAtPath:nsPath contents:nsData.get() attributes:fileAttributes.get()]) {
650         WTFLogAlways("Cannot create PDF file in the temporary directory (%s).", suggestedFilename.utf8().data());
651         return;
652     }
653
654     m_temporaryPDFFiles.add(pdfUUID, nsPath);
655
656     [[NSWorkspace sharedWorkspace] openFile:nsPath];
657 }
658
659 void WebPageProxy::savePDFToTemporaryFolderAndOpenWithNativeApplication(const String& suggestedFilename, const String& originatingURLString, const IPC::DataReference& data, const String& pdfUUID)
660 {
661     if (data.isEmpty()) {
662         WTFLogAlways("Cannot save empty PDF file to the temporary directory.");
663         return;
664     }
665
666     savePDFToTemporaryFolderAndOpenWithNativeApplicationRaw(suggestedFilename, originatingURLString, data.data(), data.size(), pdfUUID);
667 }
668
669 void WebPageProxy::openPDFFromTemporaryFolderWithNativeApplication(const String& pdfUUID)
670 {
671     String pdfFilename = m_temporaryPDFFiles.get(pdfUUID);
672
673     if (!pdfFilename.endsWith(".pdf", false))
674         return;
675
676     [[NSWorkspace sharedWorkspace] openFile:pdfFilename];
677 }
678
679 #if ENABLE(TELEPHONE_NUMBER_DETECTION)
680 void WebPageProxy::showTelephoneNumberMenu(const String& telephoneNumber, const WebCore::IntPoint& point)
681 {
682     RetainPtr<NSMenu> menu = menuForTelephoneNumber(telephoneNumber);
683     m_pageClient.showPlatformContextMenu(menu.get(), point);
684 }
685 #endif
686
687 #if ENABLE(SERVICE_CONTROLS)
688 void WebPageProxy::showSelectionServiceMenu(const IPC::DataReference& selectionAsRTFD, const Vector<String>& telephoneNumbers, bool isEditable, const IntPoint& point)
689 {
690     Vector<WebContextMenuItemData> items;
691     ContextMenuContextData contextData(selectionAsRTFD.vector(), telephoneNumbers, isEditable);
692
693     internalShowContextMenu(point, contextData, items, ContextMenuClientEligibility::NotEligibleForClient, UserData());
694 }
695 #endif
696
697 CGRect WebPageProxy::boundsOfLayerInLayerBackedWindowCoordinates(CALayer *layer) const
698 {
699     return m_pageClient.boundsOfLayerInLayerBackedWindowCoordinates(layer);
700 }
701
702 bool WebPageProxy::appleMailPaginationQuirkEnabled()
703 {
704     return applicationIsAppleMail();
705 }
706
707 void WebPageProxy::setFont(const String& fontFamily, double fontSize, uint64_t fontTraits)
708 {
709     if (!isValid())
710         return;
711
712     process().send(Messages::WebPage::SetFont(fontFamily, fontSize, fontTraits), m_pageID);
713 }
714
715 void WebPageProxy::editorStateChanged(const EditorState& editorState)
716 {
717     bool couldChangeSecureInputState = m_editorState.isInPasswordField != editorState.isInPasswordField || m_editorState.selectionIsNone;
718 #if !USE(ASYNC_NSTEXTINPUTCLIENT)
719     bool closedComposition = !editorState.shouldIgnoreCompositionSelectionChange && !editorState.hasComposition && (m_editorState.hasComposition || m_temporarilyClosedComposition);
720     m_temporarilyClosedComposition = editorState.shouldIgnoreCompositionSelectionChange && (m_temporarilyClosedComposition || m_editorState.hasComposition) && !editorState.hasComposition;
721     bool editabilityChanged = m_editorState.isContentEditable != editorState.isContentEditable;
722 #endif
723     
724     m_editorState = editorState;
725     
726     // Selection being none is a temporary state when editing. Flipping secure input state too quickly was causing trouble (not fully understood).
727     if (couldChangeSecureInputState && !editorState.selectionIsNone)
728         m_pageClient.updateSecureInputState();
729     
730     if (editorState.shouldIgnoreCompositionSelectionChange)
731         return;
732     
733     m_pageClient.selectionDidChange();
734
735 #if !USE(ASYNC_NSTEXTINPUTCLIENT)
736     if (closedComposition)
737         m_pageClient.notifyInputContextAboutDiscardedComposition();
738     if (editabilityChanged) {
739         // This is only needed in sync code path, because AppKit automatically refreshes input context for async clients (<rdar://problem/18604360>).
740         m_pageClient.notifyApplicationAboutInputContextChange();
741     }
742     if (editorState.hasComposition) {
743         // Abandon the current inline input session if selection changed for any other reason but an input method changing the composition.
744         // FIXME: This logic should be in WebCore, no need to round-trip to UI process to cancel the composition.
745         cancelComposition();
746         m_pageClient.notifyInputContextAboutDiscardedComposition();
747     }
748 #endif
749 }
750
751 void WebPageProxy::platformInitializeShareMenuItem(ContextMenuItem& item)
752 {
753 #if ENABLE(SERVICE_CONTROLS)
754     NSMenuItem *nsItem = item.platformDescription();
755
756     NSSharingServicePicker *sharingServicePicker = [nsItem representedObject];
757     sharingServicePicker.delegate = [WKSharingServicePickerDelegate sharedSharingServicePickerDelegate];
758     
759     [[WKSharingServicePickerDelegate sharedSharingServicePickerDelegate] setFiltersEditingServices:NO];
760     [[WKSharingServicePickerDelegate sharedSharingServicePickerDelegate] setHandlesEditingReplacement:NO];
761     [[WKSharingServicePickerDelegate sharedSharingServicePickerDelegate] setMenuProxy:static_cast<WebContextMenuProxyMac*>(m_activeContextMenu.get())];
762
763     // Setting the picker lets the delegate retain it to keep it alive, but this picker is kept alive by the menu item.
764     [[WKSharingServicePickerDelegate sharedSharingServicePickerDelegate] setPicker:nil];
765 #endif
766 }
767     
768 } // namespace WebKit
769
770 #endif // PLATFORM(MAC)