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