72e48333b8e8010c853cb3605c8a8bdff8d75cca
[WebKit-https.git] / Source / WebKit / UIProcess / Cocoa / WebPageProxyCocoa.mm
1 /*
2  * Copyright (C) 2014-2020 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 #import "APIAttachment.h"
30 #import "APIUIClient.h"
31 #import "Connection.h"
32 #import "DataDetectionResult.h"
33 #import "InsertTextOptions.h"
34 #import "LoadParameters.h"
35 #import "PageClient.h"
36 #import "QuickLookThumbnailLoader.h"
37 #import "SafeBrowsingSPI.h"
38 #import "SafeBrowsingWarning.h"
39 #import "SharedBufferDataReference.h"
40 #import "WebPageMessages.h"
41 #import "WebPasteboardProxy.h"
42 #import "WebProcessProxy.h"
43 #import "WebsiteDataStore.h"
44 #import <WebCore/DragItem.h>
45 #import <WebCore/LocalCurrentGraphicsContext.h>
46 #import <WebCore/NotImplemented.h>
47 #import <WebCore/SearchPopupMenuCocoa.h>
48 #import <WebCore/TextAlternativeWithRange.h>
49 #import <WebCore/ValidationBubble.h>
50 #import <pal/spi/cocoa/NEFilterSourceSPI.h>
51 #import <wtf/BlockPtr.h>
52 #import <wtf/SoftLinking.h>
53 #import <wtf/cf/TypeCastsCF.h>
54
55 #if ENABLE(MEDIA_USAGE)
56 #import "MediaUsageManagerCocoa.h"
57 #endif
58
59 #if PLATFORM(IOS)
60 #import <pal/spi/cocoa/WebFilterEvaluatorSPI.h>
61
62 SOFT_LINK_PRIVATE_FRAMEWORK(WebContentAnalysis);
63 SOFT_LINK_CLASS(WebContentAnalysis, WebFilterEvaluator);
64 #endif
65
66 SOFT_LINK_FRAMEWORK_OPTIONAL(NetworkExtension);
67 SOFT_LINK_CLASS_OPTIONAL(NetworkExtension, NEFilterSource);
68
69 #define MESSAGE_CHECK(assertion) MESSAGE_CHECK_BASE(assertion, process().connection())
70 #define MESSAGE_CHECK_COMPLETION(assertion, completion) MESSAGE_CHECK_COMPLETION_BASE(assertion, process().connection(), completion)
71
72 namespace WebKit {
73 using namespace WebCore;
74
75 #if ENABLE(DATA_DETECTION)
76 void WebPageProxy::setDataDetectionResult(const DataDetectionResult& dataDetectionResult)
77 {
78     m_dataDetectionResults = dataDetectionResult.results;
79 }
80 #endif
81
82 void WebPageProxy::saveRecentSearches(const String& name, const Vector<WebCore::RecentSearch>& searchItems)
83 {
84     MESSAGE_CHECK(!name.isNull());
85
86     WebCore::saveRecentSearches(name, searchItems);
87 }
88
89 void WebPageProxy::loadRecentSearches(const String& name, CompletionHandler<void(Vector<WebCore::RecentSearch>&&)>&& completionHandler)
90 {
91     MESSAGE_CHECK_COMPLETION(!name.isNull(), completionHandler({ }));
92
93     completionHandler(WebCore::loadRecentSearches(name));
94 }
95
96 void WebPageProxy::grantAccessToCurrentPasteboardData(const String& pasteboardName)
97 {
98     if (!hasRunningProcess())
99         return;
100
101     WebPasteboardProxy::singleton().grantAccessToCurrentData(m_process.get(), pasteboardName);
102 }
103
104 void WebPageProxy::beginSafeBrowsingCheck(const URL& url, bool forMainFrameNavigation, WebFramePolicyListenerProxy& listener)
105 {
106 #if HAVE(SAFE_BROWSING)
107     SSBLookupContext *context = [SSBLookupContext sharedLookupContext];
108     if (!context)
109         return listener.didReceiveSafeBrowsingResults({ });
110     [context lookUpURL:url completionHandler:makeBlockPtr([listener = makeRef(listener), forMainFrameNavigation, url = url] (SSBLookupResult *result, NSError *error) mutable {
111         RunLoop::main().dispatch([listener = WTFMove(listener), result = retainPtr(result), error = retainPtr(error), forMainFrameNavigation, url = WTFMove(url)] {
112             if (error) {
113                 listener->didReceiveSafeBrowsingResults({ });
114                 return;
115             }
116
117             for (SSBServiceLookupResult *lookupResult in [result serviceLookupResults]) {
118                 if (lookupResult.isPhishing || lookupResult.isMalware || lookupResult.isUnwantedSoftware) {
119                     listener->didReceiveSafeBrowsingResults(SafeBrowsingWarning::create(url, forMainFrameNavigation, lookupResult));
120                     return;
121                 }
122             }
123             listener->didReceiveSafeBrowsingResults({ });
124         });
125     }).get()];
126 #else
127     listener.didReceiveSafeBrowsingResults({ });
128 #endif
129 }
130
131 #if ENABLE(CONTENT_FILTERING)
132 void WebPageProxy::contentFilterDidBlockLoadForFrame(const WebCore::ContentFilterUnblockHandler& unblockHandler, FrameIdentifier frameID)
133 {
134     contentFilterDidBlockLoadForFrameShared(m_process.copyRef(), unblockHandler, frameID);
135 }
136
137 void WebPageProxy::contentFilterDidBlockLoadForFrameShared(Ref<WebProcessProxy>&& process, const WebCore::ContentFilterUnblockHandler& unblockHandler, FrameIdentifier frameID)
138 {
139     if (WebFrameProxy* frame = process->webFrame(frameID))
140         frame->contentFilterDidBlockLoad(unblockHandler);
141 }
142 #endif
143
144 void WebPageProxy::addPlatformLoadParameters(WebProcessProxy& process, LoadParameters& loadParameters)
145 {
146     loadParameters.dataDetectionContext = m_uiClient->dataDetectionContext();
147
148     if (!process.hasNetworkExtensionSandboxAccess() && [getNEFilterSourceClass() filterRequired]) {
149         SandboxExtension::Handle helperHandle;
150         SandboxExtension::createHandleForMachLookup("com.apple.nehelper"_s, WTF::nullopt, helperHandle);
151         loadParameters.neHelperExtensionHandle = WTFMove(helperHandle);
152         SandboxExtension::Handle managerHandle;
153 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
154         SandboxExtension::createHandleForMachLookup("com.apple.nesessionmanager"_s, WTF::nullopt, managerHandle);
155 #else
156         SandboxExtension::createHandleForMachLookup("com.apple.nesessionmanager.content-filter"_s, WTF::nullopt, managerHandle);
157 #endif
158         loadParameters.neSessionManagerExtensionHandle = WTFMove(managerHandle);
159
160         process.markHasNetworkExtensionSandboxAccess();
161     }
162
163 #if PLATFORM(IOS)
164     if (!process.hasManagedSessionSandboxAccess() && [getWebFilterEvaluatorClass() isManagedSession]) {
165         SandboxExtension::Handle handle;
166         SandboxExtension::createHandleForMachLookup("com.apple.uikit.viewservice.com.apple.WebContentFilter.remoteUI"_s, WTF::nullopt, handle);
167         loadParameters.contentFilterExtensionHandle = WTFMove(handle);
168
169         SandboxExtension::Handle frontboardServiceExtensionHandle;
170         if (SandboxExtension::createHandleForMachLookup("com.apple.frontboard.systemappservices"_s, WTF::nullopt, frontboardServiceExtensionHandle))
171             loadParameters.frontboardServiceExtensionHandle = WTFMove(frontboardServiceExtensionHandle);
172
173         process.markHasManagedSessionSandboxAccess();
174     }
175 #endif
176 }
177
178 void WebPageProxy::createSandboxExtensionsIfNeeded(const Vector<String>& files, SandboxExtension::Handle& fileReadHandle, SandboxExtension::HandleArray& fileUploadHandles)
179 {
180     if (!files.size())
181         return;
182
183     if (files.size() == 1) {
184         BOOL isDirectory;
185         if ([[NSFileManager defaultManager] fileExistsAtPath:files[0] isDirectory:&isDirectory] && !isDirectory) {
186 #if HAVE(SANDBOX_ISSUE_READ_EXTENSION_TO_PROCESS_BY_AUDIT_TOKEN)
187             ASSERT(process().connection() && process().connection()->getAuditToken());
188             if (process().connection() && process().connection()->getAuditToken())
189                 SandboxExtension::createHandleForReadByAuditToken("/", *(process().connection()->getAuditToken()), fileReadHandle);
190             else
191                 SandboxExtension::createHandle("/", SandboxExtension::Type::ReadOnly, fileReadHandle);
192 #else
193             SandboxExtension::createHandle("/", SandboxExtension::Type::ReadOnly, fileReadHandle);
194 #endif
195             willAcquireUniversalFileReadSandboxExtension(m_process);
196         }
197     }
198
199     fileUploadHandles.allocate(files.size());
200     for (size_t i = 0; i< files.size(); i++) {
201         NSString *file = files[i];
202         if (![[NSFileManager defaultManager] fileExistsAtPath:file])
203             continue;
204         SandboxExtension::createHandle(file, SandboxExtension::Type::ReadOnly, fileUploadHandles[i]);
205     }
206 }
207
208 bool WebPageProxy::scrollingUpdatesDisabledForTesting()
209 {
210     return pageClient().scrollingUpdatesDisabledForTesting();
211 }
212
213 #if ENABLE(DRAG_SUPPORT)
214
215 void WebPageProxy::startDrag(const DragItem& dragItem, const ShareableBitmap::Handle& dragImageHandle)
216 {
217     pageClient().startDrag(dragItem, dragImageHandle);
218 }
219
220 // FIXME: Move these functions to WebPageProxyIOS.mm.
221 #if PLATFORM(IOS_FAMILY)
222
223 void WebPageProxy::setPromisedDataForImage(const String&, const SharedMemory::Handle&, uint64_t, const String&, const String&, const String&, const String&, const String&, const SharedMemory::Handle&, uint64_t)
224 {
225     notImplemented();
226 }
227
228 void WebPageProxy::setDragCaretRect(const IntRect& dragCaretRect)
229 {
230     if (m_currentDragCaretRect == dragCaretRect)
231         return;
232
233     auto previousRect = m_currentDragCaretRect;
234     m_currentDragCaretRect = dragCaretRect;
235     pageClient().didChangeDragCaretRect(previousRect, dragCaretRect);
236 }
237
238 #endif // PLATFORM(IOS_FAMILY)
239
240 #endif // ENABLE(DRAG_SUPPORT)
241
242 #if ENABLE(ATTACHMENT_ELEMENT)
243
244 void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&& attachment, const String& preferredFileName, const IPC::SharedBufferDataReference& dataReference)
245 {
246     if (dataReference.isEmpty())
247         return;
248
249     auto fileWrapper = adoptNS([pageClient().allocFileWrapperInstance() initRegularFileWithContents:dataReference.buffer()->createNSData().autorelease()]);
250     [fileWrapper setPreferredFilename:preferredFileName];
251     attachment->setFileWrapper(fileWrapper.get());
252 }
253
254 void WebPageProxy::platformRegisterAttachment(Ref<API::Attachment>&& attachment, const String& filePath)
255 {
256     if (!filePath)
257         return;
258
259     auto fileWrapper = adoptNS([pageClient().allocFileWrapperInstance() initWithURL:[NSURL fileURLWithPath:filePath] options:0 error:nil]);
260     attachment->setFileWrapper(fileWrapper.get());
261 }
262
263 void WebPageProxy::platformCloneAttachment(Ref<API::Attachment>&& fromAttachment, Ref<API::Attachment>&& toAttachment)
264 {
265     toAttachment->setFileWrapper(fromAttachment->fileWrapper());
266 }
267
268 #endif // ENABLE(ATTACHMENT_ELEMENT)
269     
270 void WebPageProxy::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point)
271 {
272     if (!hasRunningProcess())
273         return;
274     
275     send(Messages::WebPage::PerformDictionaryLookupAtLocation(point));
276 }
277
278 void WebPageProxy::performDictionaryLookupOfCurrentSelection()
279 {
280     if (!hasRunningProcess())
281         return;
282     
283     send(Messages::WebPage::PerformDictionaryLookupOfCurrentSelection());
284 }
285
286 void WebPageProxy::insertDictatedTextAsync(const String& text, const EditingRange& replacementRange, const Vector<TextAlternativeWithRange>& dictationAlternativesWithRange, InsertTextOptions&& options)
287 {
288     if (!hasRunningProcess())
289         return;
290
291     Vector<DictationAlternative> dictationAlternatives;
292     for (const auto& alternativeWithRange : dictationAlternativesWithRange) {
293         if (auto context = pageClient().addDictationAlternatives(alternativeWithRange.alternatives.get()))
294             dictationAlternatives.append({ alternativeWithRange.range, context });
295     }
296
297     if (dictationAlternatives.isEmpty()) {
298         insertTextAsync(text, replacementRange, WTFMove(options));
299         return;
300     }
301
302     send(Messages::WebPage::InsertDictatedTextAsync { text, replacementRange, dictationAlternatives, WTFMove(options) });
303 }
304     
305 #if ENABLE(APPLE_PAY)
306
307 IPC::Connection* WebPageProxy::paymentCoordinatorConnection(const WebPaymentCoordinatorProxy&)
308 {
309     return messageSenderConnection();
310 }
311
312 const String& WebPageProxy::paymentCoordinatorBoundInterfaceIdentifier(const WebPaymentCoordinatorProxy&)
313 {
314     return websiteDataStore().configuration().boundInterfaceIdentifier();
315 }
316
317 const String& WebPageProxy::paymentCoordinatorSourceApplicationBundleIdentifier(const WebPaymentCoordinatorProxy&)
318 {
319     return websiteDataStore().configuration().sourceApplicationBundleIdentifier();
320 }
321
322 const String& WebPageProxy::paymentCoordinatorSourceApplicationSecondaryIdentifier(const WebPaymentCoordinatorProxy&)
323 {
324     return websiteDataStore().configuration().sourceApplicationSecondaryIdentifier();
325 }
326
327 void WebPageProxy::paymentCoordinatorAddMessageReceiver(WebPaymentCoordinatorProxy&, IPC::ReceiverName receiverName, IPC::MessageReceiver& messageReceiver)
328 {
329     process().addMessageReceiver(receiverName, m_webPageID, messageReceiver);
330 }
331
332 void WebPageProxy::paymentCoordinatorRemoveMessageReceiver(WebPaymentCoordinatorProxy&, IPC::ReceiverName receiverName)
333 {
334     process().removeMessageReceiver(receiverName, m_webPageID);
335 }
336
337 #endif
338
339 #if ENABLE(SPEECH_SYNTHESIS)
340 void WebPageProxy::didStartSpeaking(WebCore::PlatformSpeechSynthesisUtterance&)
341 {
342     if (speechSynthesisData().speakingStartedCompletionHandler)
343         speechSynthesisData().speakingStartedCompletionHandler();
344 }
345
346 void WebPageProxy::didFinishSpeaking(WebCore::PlatformSpeechSynthesisUtterance&)
347 {
348     if (speechSynthesisData().speakingFinishedCompletionHandler)
349         speechSynthesisData().speakingFinishedCompletionHandler();
350 }
351
352 void WebPageProxy::didPauseSpeaking(WebCore::PlatformSpeechSynthesisUtterance&)
353 {
354     if (speechSynthesisData().speakingPausedCompletionHandler)
355         speechSynthesisData().speakingPausedCompletionHandler();
356 }
357
358 void WebPageProxy::didResumeSpeaking(WebCore::PlatformSpeechSynthesisUtterance&)
359 {
360     if (speechSynthesisData().speakingResumedCompletionHandler)
361         speechSynthesisData().speakingResumedCompletionHandler();
362 }
363
364 void WebPageProxy::speakingErrorOccurred(WebCore::PlatformSpeechSynthesisUtterance&)
365 {
366     send(Messages::WebPage::SpeakingErrorOccurred());
367 }
368
369 void WebPageProxy::boundaryEventOccurred(WebCore::PlatformSpeechSynthesisUtterance&, WebCore::SpeechBoundary speechBoundary, unsigned charIndex)
370 {
371     send(Messages::WebPage::BoundaryEventOccurred(speechBoundary == WebCore::SpeechBoundary::SpeechWordBoundary, charIndex));
372 }
373
374 void WebPageProxy::voicesDidChange()
375 {
376     send(Messages::WebPage::VoicesDidChange());
377 }
378 #endif // ENABLE(SPEECH_SYNTHESIS)
379
380 #if HAVE(VISIBILITY_PROPAGATION_VIEW)
381 void WebPageProxy::didCreateContextForVisibilityPropagation(LayerHostingContextID contextID)
382 {
383     m_contextIDForVisibilityPropagation = contextID;
384     pageClient().didCreateContextForVisibilityPropagation(contextID);
385 }
386
387 void WebPageProxy::didCreateContextInGPUProcessForVisibilityPropagation(LayerHostingContextID contextID)
388 {
389     pageClient().didCreateContextInGPUProcessForVisibilityPropagation(contextID);
390 }
391 #endif
392
393 void WebPageProxy::grantAccessToPreferenceService()
394 {
395 #if ENABLE(CFPREFS_DIRECT_MODE)
396     process().unblockPreferenceServiceIfNeeded();
397 #endif
398 }
399
400 #if ENABLE(MEDIA_USAGE)
401 MediaUsageManager& WebPageProxy::mediaUsageManager()
402 {
403     if (!m_mediaUsageManager)
404         m_mediaUsageManager = MediaUsageManager::create();
405
406     return *m_mediaUsageManager;
407 }
408
409 void WebPageProxy::addMediaUsageManagerSession(WebCore::MediaSessionIdentifier identifier, const String& bundleIdentifier, const URL& pageURL)
410 {
411     mediaUsageManager().addMediaSession(identifier, bundleIdentifier, pageURL);
412 }
413
414 void WebPageProxy::updateMediaUsageManagerSessionState(WebCore::MediaSessionIdentifier identifier, const WebCore::MediaUsageInfo& info)
415 {
416     mediaUsageManager().updateMediaUsage(identifier, info);
417 }
418
419 void WebPageProxy::removeMediaUsageManagerSession(WebCore::MediaSessionIdentifier identifier)
420 {
421     mediaUsageManager().removeMediaSession(identifier);
422 }
423 #endif
424
425 #if HAVE(QUICKLOOK_THUMBNAILING)
426
427 static RefPtr<WebKit::ShareableBitmap> convertPlatformImageToBitmap(CocoaImage *image, const WebCore::IntSize& size)
428 {
429     WebKit::ShareableBitmap::Configuration bitmapConfiguration;
430     auto bitmap = WebKit::ShareableBitmap::createShareable(size, bitmapConfiguration);
431     if (!bitmap)
432         return nullptr;
433
434     auto graphicsContext = bitmap->createGraphicsContext();
435     if (!graphicsContext)
436         return nullptr;
437
438     LocalCurrentGraphicsContext savedContext(*graphicsContext);
439 #if PLATFORM(IOS_FAMILY)
440     [image drawInRect:CGRectMake(0, 0, bitmap->size().width(), bitmap->size().height())];
441 #elif USE(APPKIT)
442     [image drawInRect:NSMakeRect(0, 0, bitmap->size().width(), bitmap->size().height()) fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1 respectFlipped:YES hints:nil];
443 #endif
444
445     return bitmap;
446 }
447
448 void WebPageProxy::requestThumbnailWithOperation(WKQLThumbnailLoadOperation *operation)
449 {
450     [operation setCompletionBlock:^{
451         dispatch_async(dispatch_get_main_queue(), ^{
452             auto identifier = [operation identifier];
453             auto convertedImage = convertPlatformImageToBitmap([operation thumbnail], WebCore::IntSize(400, 400));
454             if (!convertedImage)
455                 return;
456             this->updateAttachmentIcon(identifier, convertedImage);
457         });
458     }];
459         
460     [[WKQLThumbnailQueueManager sharedInstance].queue addOperation:operation];
461 }
462
463
464 void WebPageProxy::requestThumbnailWithFileWrapper(NSFileWrapper* fileWrapper, const String& identifier)
465 {
466     auto operation = adoptNS([[WKQLThumbnailLoadOperation alloc] initWithAttachment:fileWrapper identifier:identifier]);
467     requestThumbnailWithOperation(operation.get());
468 }
469
470 void WebPageProxy::requestThumbnailWithPath(const String& identifier, const String& filePath)
471 {
472     auto operation = adoptNS([[WKQLThumbnailLoadOperation alloc] initWithURL:filePath identifier:identifier]);
473     requestThumbnailWithOperation(operation.get());
474     
475 }
476
477 #endif // HAVE(QUICKLOOK_THUMBNAILING)
478
479 } // namespace WebKit
480
481 #undef MESSAGE_CHECK_COMPLETION
482 #undef MESSAGE_CHECK