Shrink various loading-related enums to shrink CachedResource
[WebKit-https.git] / Source / WebKit / Shared / mac / WebCoreArgumentCodersMac.mm
1 /*
2  * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
3  * Copyright (C) 2013 Company 100 Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #import "config.h"
28 #import "WebCoreArgumentCoders.h"
29
30 #import "ArgumentCodersCF.h"
31 #import "DataReference.h"
32 #import <WebCore/CertificateInfo.h>
33 #import <WebCore/ContentFilterUnblockHandler.h>
34 #import <WebCore/Credential.h>
35 #import <WebCore/KeyboardEvent.h>
36 #import <WebCore/ProtectionSpace.h>
37 #import <WebCore/ResourceError.h>
38 #import <WebCore/ResourceRequest.h>
39 #import <pal/spi/cf/CFNetworkSPI.h>
40 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
41 #import <wtf/MachSendRight.h>
42
43 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
44 #import <WebCore/MediaPlaybackTargetContext.h>
45 #import <objc/runtime.h>
46 #import <pal/spi/mac/AVFoundationSPI.h>
47 #import <wtf/SoftLinking.h>
48
49 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
50 SOFT_LINK_CLASS(AVFoundation, AVOutputContext)
51 #endif
52
53 using namespace WebCore;
54
55 namespace IPC {
56
57 static RetainPtr<CFMutableDictionaryRef> createSerializableRepresentation(CFIndex version, CFTypeRef* objects, CFIndex objectCount, CFDictionaryRef protocolProperties, CFNumberRef expectedContentLength, CFStringRef mimeType, CFTypeRef tokenNull)
58 {
59     auto archiveListArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
60
61     for (CFIndex i = 0; i < objectCount; ++i) {
62         CFTypeRef object = objects[i];
63         if (object) {
64             CFArrayAppendValue(archiveListArray.get(), object);
65             CFRelease(object);
66         } else {
67             // Append our token null representation.
68             CFArrayAppendValue(archiveListArray.get(), tokenNull);
69         }
70     }
71
72     auto dictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
73
74     auto versionNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &version));
75     CFDictionarySetValue(dictionary.get(), CFSTR("version"), versionNumber.get());
76     CFDictionarySetValue(dictionary.get(), CFSTR("archiveList"), archiveListArray.get());
77
78     if (protocolProperties) {
79         CFDictionarySetValue(dictionary.get(), CFSTR("protocolProperties"), protocolProperties);
80         CFRelease(protocolProperties);
81     }
82
83     if (expectedContentLength) {
84         CFDictionarySetValue(dictionary.get(), CFSTR("expectedContentLength"), expectedContentLength);
85         CFRelease(expectedContentLength);
86     }
87
88     if (mimeType) {
89         CFDictionarySetValue(dictionary.get(), CFSTR("mimeType"), mimeType);
90         CFRelease(mimeType);
91     }
92
93     CFAllocatorDeallocate(kCFAllocatorDefault, objects);
94
95     return dictionary;
96 }
97
98 static CFTypeRef dictionaryValueOfType(CFDictionaryRef dictionary, CFStringRef key, CFTypeID type)
99 {
100     CFTypeRef value = CFDictionaryGetValue(dictionary, key);
101     if (value && CFGetTypeID(value) == type)
102         return value;
103     return nullptr;
104 }
105
106 static bool createArchiveList(CFDictionaryRef representation, CFTypeRef tokenNull, CFIndex* version, CFTypeRef** objects, CFIndex* objectCount, CFDictionaryRef* protocolProperties, CFNumberRef* expectedContentLength, CFStringRef* mimeType)
107 {
108     CFNumberRef versionNumber = (CFNumberRef)dictionaryValueOfType(representation, CFSTR("version"), CFNumberGetTypeID());
109     if (!versionNumber)
110         return false;
111
112     if (!CFNumberGetValue(versionNumber, kCFNumberCFIndexType, version))
113         return false;
114
115     CFArrayRef archiveListArray = (CFArrayRef)dictionaryValueOfType(representation, CFSTR("archiveList"), CFArrayGetTypeID());
116     if (!archiveListArray)
117         return false;
118
119     *objectCount = CFArrayGetCount(archiveListArray);
120     *objects = (CFTypeRef*)malloc(sizeof(CFTypeRef) * *objectCount);
121     for (CFIndex i = 0; i < *objectCount; ++i) {
122         CFTypeRef object = CFArrayGetValueAtIndex(archiveListArray, i);
123         if (object == tokenNull)
124             (*objects)[i] = nullptr;
125         else
126             (*objects)[i] = object;
127     }
128
129     if (protocolProperties)
130         *protocolProperties = (CFDictionaryRef)dictionaryValueOfType(representation, CFSTR("protocolProperties"), CFDictionaryGetTypeID());
131
132     if (expectedContentLength)
133         *expectedContentLength = (CFNumberRef)dictionaryValueOfType(representation, CFSTR("expectedContentLength"), CFNumberGetTypeID());
134
135     if (mimeType)
136         *mimeType = (CFStringRef)dictionaryValueOfType(representation, CFSTR("mimeType"), CFStringGetTypeID());
137
138     return true;
139 }
140
141 static RetainPtr<CFDictionaryRef> createSerializableRepresentation(CFURLRequestRef cfRequest, CFTypeRef tokenNull)
142 {
143     CFIndex version;
144     CFTypeRef* objects;
145     CFIndex objectCount;
146     CFDictionaryRef protocolProperties;
147
148     // FIXME (12889518): Do not serialize HTTP message body.
149     // 1. It can be large and thus costly to send across.
150     // 2. It is misleading to provide a body with some requests, while others use body streams, which cannot be serialized at all.
151
152     _CFURLRequestCreateArchiveList(kCFAllocatorDefault, cfRequest, &version, &objects, &objectCount, &protocolProperties);
153
154     // This will deallocate the passed in arguments.
155     return createSerializableRepresentation(version, objects, objectCount, protocolProperties, nullptr, nullptr, tokenNull);
156 }
157
158 static RetainPtr<CFURLRequestRef> createCFURLRequestFromSerializableRepresentation(CFDictionaryRef representation, CFTypeRef tokenNull)
159 {
160     CFIndex version;
161     CFTypeRef* objects;
162     CFIndex objectCount;
163     CFDictionaryRef protocolProperties;
164
165     if (!createArchiveList(representation, tokenNull, &version, &objects, &objectCount, &protocolProperties, nullptr, nullptr))
166         return nullptr;
167
168     auto cfRequest = adoptCF(_CFURLRequestCreateFromArchiveList(kCFAllocatorDefault, version, objects, objectCount, protocolProperties));
169     free(objects);
170     return WTFMove(cfRequest);
171 }
172
173 static RetainPtr<CFDictionaryRef> createSerializableRepresentation(NSURLRequest *request, CFTypeRef tokenNull)
174 {
175     return createSerializableRepresentation([request _CFURLRequest], tokenNull);
176 }
177
178 static RetainPtr<NSURLRequest> createNSURLRequestFromSerializableRepresentation(CFDictionaryRef representation, CFTypeRef tokenNull)
179 {
180     auto cfRequest = createCFURLRequestFromSerializableRepresentation(representation, tokenNull);
181     if (!cfRequest)
182         return nullptr;
183     
184     return adoptNS([[NSURLRequest alloc] _initWithCFURLRequest:cfRequest.get()]);
185 }
186     
187 void ArgumentCoder<ResourceRequest>::encodePlatformData(Encoder& encoder, const ResourceRequest& resourceRequest)
188 {
189     RetainPtr<NSURLRequest> requestToSerialize = resourceRequest.nsURLRequest(HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody);
190
191     bool requestIsPresent = requestToSerialize;
192     encoder << requestIsPresent;
193
194     if (!requestIsPresent)
195         return;
196
197     // We don't send HTTP body over IPC for better performance.
198     // Also, it's not always possible to do, as streams can only be created in process that does networking.
199     if ([requestToSerialize HTTPBody] || [requestToSerialize HTTPBodyStream]) {
200         requestToSerialize = adoptNS([requestToSerialize mutableCopy]);
201         [(NSMutableURLRequest *)requestToSerialize setHTTPBody:nil];
202         [(NSMutableURLRequest *)requestToSerialize setHTTPBodyStream:nil];
203     }
204
205     RetainPtr<CFDictionaryRef> dictionary = createSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef());
206     IPC::encode(encoder, dictionary.get());
207
208     // The fallback array is part of NSURLRequest, but it is not encoded by WKNSURLRequestCreateSerializableRepresentation.
209     encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
210     encoder.encodeEnum(resourceRequest.requester());
211     encoder.encodeEnum(resourceRequest.cachePolicy());
212 }
213
214 bool ArgumentCoder<ResourceRequest>::decodePlatformData(Decoder& decoder, ResourceRequest& resourceRequest)
215 {
216     bool requestIsPresent;
217     if (!decoder.decode(requestIsPresent))
218         return false;
219
220     if (!requestIsPresent) {
221         resourceRequest = ResourceRequest();
222         return true;
223     }
224
225     RetainPtr<CFDictionaryRef> dictionary;
226     if (!IPC::decode(decoder, dictionary))
227         return false;
228
229     RetainPtr<NSURLRequest> nsURLRequest = createNSURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef());
230     if (!nsURLRequest)
231         return false;
232
233     resourceRequest = ResourceRequest(nsURLRequest.get());
234     
235     Vector<String> responseContentDispositionEncodingFallbackArray;
236     if (!decoder.decode(responseContentDispositionEncodingFallbackArray))
237         return false;
238
239     resourceRequest.setResponseContentDispositionEncodingFallbackArray(
240         responseContentDispositionEncodingFallbackArray.size() > 0 ? responseContentDispositionEncodingFallbackArray[0] : String(),
241         responseContentDispositionEncodingFallbackArray.size() > 1 ? responseContentDispositionEncodingFallbackArray[1] : String(),
242         responseContentDispositionEncodingFallbackArray.size() > 2 ? responseContentDispositionEncodingFallbackArray[2] : String()
243     );
244
245     ResourceRequest::Requester requester;
246     if (!decoder.decodeEnum(requester))
247         return false;
248     resourceRequest.setRequester(requester);
249
250     ResourceRequestCachePolicy cachePolicy;
251     if (!decoder.decodeEnum(cachePolicy))
252         return false;
253     resourceRequest.setCachePolicy(cachePolicy);
254
255     return true;
256 }
257
258 void ArgumentCoder<CertificateInfo>::encode(Encoder& encoder, const CertificateInfo& certificateInfo)
259 {
260     encoder.encodeEnum(certificateInfo.type());
261
262     switch (certificateInfo.type()) {
263 #if HAVE(SEC_TRUST_SERIALIZATION)
264     case CertificateInfo::Type::Trust:
265         IPC::encode(encoder, certificateInfo.trust());
266         break;
267 #endif
268     case CertificateInfo::Type::CertificateChain:
269         IPC::encode(encoder, certificateInfo.certificateChain());
270         break;
271     case CertificateInfo::Type::None:
272         // Do nothing.
273         break;
274     }
275 }
276
277 bool ArgumentCoder<CertificateInfo>::decode(Decoder& decoder, CertificateInfo& certificateInfo)
278 {
279     CertificateInfo::Type certificateInfoType;
280     if (!decoder.decodeEnum(certificateInfoType))
281         return false;
282
283     switch (certificateInfoType) {
284 #if HAVE(SEC_TRUST_SERIALIZATION)
285     case CertificateInfo::Type::Trust: {
286         RetainPtr<SecTrustRef> trust;
287         if (!IPC::decode(decoder, trust))
288             return false;
289
290         certificateInfo = CertificateInfo(WTFMove(trust));
291         return true;
292     }
293 #endif
294     case CertificateInfo::Type::CertificateChain: {
295         RetainPtr<CFArrayRef> certificateChain;
296         if (!IPC::decode(decoder, certificateChain))
297             return false;
298
299         certificateInfo = CertificateInfo(WTFMove(certificateChain));
300         return true;
301     }    
302     case CertificateInfo::Type::None:
303         // Do nothing.
304         break;
305     }
306
307     return true;
308 }
309
310 static void encodeNSError(Encoder& encoder, NSError *nsError)
311 {
312     String domain = [nsError domain];
313     encoder << domain;
314
315     int64_t code = [nsError code];
316     encoder << code;
317
318     NSDictionary *userInfo = [nsError userInfo];
319
320     RetainPtr<CFMutableDictionaryRef> filteredUserInfo = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, userInfo.count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
321
322     [userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL*) {
323         if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSURL class]] || [value isKindOfClass:[NSNumber class]])
324             CFDictionarySetValue(filteredUserInfo.get(), (__bridge CFTypeRef)key, (__bridge CFTypeRef)value);
325     }];
326
327     if (NSArray *clientIdentityAndCertificates = [userInfo objectForKey:@"NSErrorClientCertificateChainKey"]) {
328         ASSERT([clientIdentityAndCertificates isKindOfClass:[NSArray class]]);
329         ASSERT(^{
330             for (id object in clientIdentityAndCertificates) {
331                 if (CFGetTypeID((__bridge CFTypeRef)object) != SecIdentityGetTypeID() && CFGetTypeID((__bridge CFTypeRef)object) != SecCertificateGetTypeID())
332                     return false;
333             }
334             return true;
335         }());
336
337         // Turn SecIdentity members into SecCertificate to strip out private key information.
338         id clientCertificates = [NSMutableArray arrayWithCapacity:clientIdentityAndCertificates.count];
339         for (id object in clientIdentityAndCertificates) {
340             if (CFGetTypeID((__bridge CFTypeRef)object) != SecIdentityGetTypeID()) {
341                 [clientCertificates addObject:object];
342                 continue;
343             }
344             SecCertificateRef certificate = nil;
345             OSStatus status = SecIdentityCopyCertificate((SecIdentityRef)object, &certificate);
346             RetainPtr<SecCertificateRef> retainCertificate = adoptCF(certificate);
347             // The SecIdentity member is the key information of this attribute. Without it, we should nil
348             // the attribute.
349             if (status != errSecSuccess) {
350                 LOG_ERROR("Failed to encode nsError.userInfo[NSErrorClientCertificateChainKey]: %d", status);
351                 clientCertificates = nil;
352                 break;
353             }
354             [clientCertificates addObject:(__bridge id)certificate];
355         }
356         CFDictionarySetValue(filteredUserInfo.get(), CFSTR("NSErrorClientCertificateChainKey"), (__bridge CFTypeRef)clientCertificates);
357     }
358
359     id peerCertificateChain = [userInfo objectForKey:@"NSErrorPeerCertificateChainKey"];
360     if (!peerCertificateChain) {
361         if (SecTrustRef peerTrust = (__bridge SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
362             CFIndex count = SecTrustGetCertificateCount(peerTrust);
363             peerCertificateChain = [NSMutableArray arrayWithCapacity:count];
364             for (CFIndex i = 0; i < count; ++i)
365                 [peerCertificateChain addObject:(__bridge id)SecTrustGetCertificateAtIndex(peerTrust, i)];
366         }
367     }
368     ASSERT(!peerCertificateChain || [peerCertificateChain isKindOfClass:[NSArray class]]);
369     if (peerCertificateChain)
370         CFDictionarySetValue(filteredUserInfo.get(), CFSTR("NSErrorPeerCertificateChainKey"), (__bridge CFTypeRef)peerCertificateChain);
371
372 #if HAVE(SEC_TRUST_SERIALIZATION)
373     if (SecTrustRef peerTrust = (__bridge SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey])
374         CFDictionarySetValue(filteredUserInfo.get(), (__bridge CFStringRef)NSURLErrorFailingURLPeerTrustErrorKey, peerTrust);
375 #endif
376
377     IPC::encode(encoder, filteredUserInfo.get());
378
379     if (id underlyingError = [userInfo objectForKey:NSUnderlyingErrorKey]) {
380         ASSERT([underlyingError isKindOfClass:[NSError class]]);
381         encoder << true;
382         encodeNSError(encoder, underlyingError);
383     } else
384         encoder << false;
385 }
386
387 void ArgumentCoder<ResourceError>::encodePlatformData(Encoder& encoder, const ResourceError& resourceError)
388 {
389     encodeNSError(encoder, resourceError.nsError());
390 }
391
392 static bool decodeNSError(Decoder& decoder, RetainPtr<NSError>& nsError)
393 {
394     String domain;
395     if (!decoder.decode(domain))
396         return false;
397
398     int64_t code;
399     if (!decoder.decode(code))
400         return false;
401
402     RetainPtr<CFDictionaryRef> userInfo;
403     if (!IPC::decode(decoder, userInfo))
404         return false;
405
406     bool hasUnderlyingError = false;
407     if (!decoder.decode(hasUnderlyingError))
408         return false;
409
410     if (hasUnderlyingError) {
411         RetainPtr<NSError> underlyingNSError;
412         if (!decodeNSError(decoder, underlyingNSError))
413             return false;
414
415         auto mutableUserInfo = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(userInfo.get()) + 1, userInfo.get()));
416         CFDictionarySetValue(mutableUserInfo.get(), (__bridge CFStringRef)NSUnderlyingErrorKey, (__bridge CFTypeRef)underlyingNSError.get());
417         userInfo = WTFMove(mutableUserInfo);
418     }
419
420     nsError = adoptNS([[NSError alloc] initWithDomain:domain code:code userInfo:(__bridge NSDictionary *)userInfo.get()]);
421     return true;
422 }
423
424 bool ArgumentCoder<ResourceError>::decodePlatformData(Decoder& decoder, ResourceError& resourceError)
425 {
426     RetainPtr<NSError> nsError;
427     if (!decodeNSError(decoder, nsError))
428         return false;
429
430     resourceError = ResourceError(nsError.get());
431     return true;
432 }
433
434 void ArgumentCoder<ProtectionSpace>::encodePlatformData(Encoder& encoder, const ProtectionSpace& space)
435 {
436     auto archiver = secureArchiver();
437     [archiver encodeObject:space.nsSpace() forKey:@"protectionSpace"];
438     IPC::encode(encoder, (__bridge CFDataRef)archiver.get().encodedData);
439 }
440
441 bool ArgumentCoder<ProtectionSpace>::decodePlatformData(Decoder& decoder, ProtectionSpace& space)
442 {
443     RetainPtr<CFDataRef> data;
444     if (!IPC::decode(decoder, data))
445         return false;
446
447     auto unarchiver = secureUnarchiverFromData((__bridge NSData *)data.get());
448     @try {
449         if (RetainPtr<NSURLProtectionSpace> nsSpace = [unarchiver decodeObjectOfClass:[NSURLProtectionSpace class] forKey:@"protectionSpace"])
450             space = ProtectionSpace(nsSpace.get());
451     } @catch (NSException *exception) {
452         LOG_ERROR("Failed to decode NSURLProtectionSpace: %@", exception);
453     }
454
455     [unarchiver finishDecoding];
456     return true;
457 }
458
459 void ArgumentCoder<Credential>::encodePlatformData(Encoder& encoder, const Credential& credential)
460 {
461     NSURLCredential *nsCredential = credential.nsCredential();
462     // NSURLCredential doesn't serialize identities correctly, so we encode the pieces individually
463     // in the identity case. See <rdar://problem/18802434>.
464     if (SecIdentityRef identity = nsCredential.identity) {
465         encoder << true;
466         IPC::encode(encoder, identity);
467
468         if (NSArray *certificates = nsCredential.certificates) {
469             encoder << true;
470             IPC::encode(encoder, (__bridge CFArrayRef)certificates);
471         } else
472             encoder << false;
473
474         encoder << static_cast<uint64_t>(nsCredential.persistence);
475         return;
476     }
477
478     encoder << false;
479
480     auto archiver = secureArchiver();
481     [archiver encodeObject:nsCredential forKey:@"credential"];
482     IPC::encode(encoder, (__bridge CFDataRef)archiver.get().encodedData);
483 }
484
485 bool ArgumentCoder<Credential>::decodePlatformData(Decoder& decoder, Credential& credential)
486 {
487     bool hasIdentity;
488     if (!decoder.decode(hasIdentity))
489         return false;
490
491     if (hasIdentity) {
492         RetainPtr<SecIdentityRef> identity;
493         if (!IPC::decode(decoder, identity))
494             return false;
495
496         RetainPtr<CFArrayRef> certificates;
497         bool hasCertificates;
498         if (!decoder.decode(hasCertificates))
499             return false;
500
501         if (hasCertificates) {
502             if (!IPC::decode(decoder, certificates))
503                 return false;
504         }
505
506         uint64_t persistence;
507         if (!decoder.decode(persistence))
508             return false;
509
510         credential = Credential(adoptNS([[NSURLCredential alloc] initWithIdentity:identity.get() certificates:(__bridge NSArray *)certificates.get() persistence:(NSURLCredentialPersistence)persistence]).get());
511         return true;
512     }
513
514     RetainPtr<CFDataRef> data;
515     if (!IPC::decode(decoder, data))
516         return false;
517
518     auto unarchiver = secureUnarchiverFromData((__bridge NSData *)data.get());
519     @try {
520         if (RetainPtr<NSURLCredential> nsCredential = [unarchiver decodeObjectOfClass:[NSURLCredential class] forKey:@"credential"])
521             credential = Credential(nsCredential.get());
522     } @catch (NSException *exception) {
523         LOG_ERROR("Failed to decode NSURLCredential: %@", exception);
524     }
525
526     [unarchiver finishDecoding];
527     return true;
528 }
529
530 void ArgumentCoder<MachSendRight>::encode(Encoder& encoder, const MachSendRight& sendRight)
531 {
532     encoder << Attachment(sendRight.copySendRight().leakSendRight(), MACH_MSG_TYPE_MOVE_SEND);
533 }
534
535 void ArgumentCoder<MachSendRight>::encode(Encoder& encoder, MachSendRight&& sendRight)
536 {
537     encoder << Attachment(sendRight.leakSendRight(), MACH_MSG_TYPE_MOVE_SEND);
538 }
539
540 bool ArgumentCoder<MachSendRight>::decode(Decoder& decoder, MachSendRight& sendRight)
541 {
542     Attachment attachment;
543     if (!decoder.decode(attachment))
544         return false;
545
546     if (attachment.disposition() != MACH_MSG_TYPE_MOVE_SEND)
547         return false;
548
549     sendRight = MachSendRight::adopt(attachment.port());
550     return true;
551 }
552
553 void ArgumentCoder<KeypressCommand>::encode(Encoder& encoder, const KeypressCommand& keypressCommand)
554 {
555     encoder << keypressCommand.commandName << keypressCommand.text;
556 }
557     
558 std::optional<KeypressCommand> ArgumentCoder<KeypressCommand>::decode(Decoder& decoder)
559 {
560     std::optional<String> commandName;
561     decoder >> commandName;
562     if (!commandName)
563         return std::nullopt;
564     
565     std::optional<String> text;
566     decoder >> text;
567     if (!text)
568         return std::nullopt;
569     
570     KeypressCommand command;
571     command.commandName = WTFMove(*commandName);
572     command.text = WTFMove(*text);
573     return WTFMove(command);
574 }
575
576 #if ENABLE(CONTENT_FILTERING)
577
578 void ArgumentCoder<ContentFilterUnblockHandler>::encode(Encoder& encoder, const ContentFilterUnblockHandler& contentFilterUnblockHandler)
579 {
580     auto archiver = secureArchiver();
581     contentFilterUnblockHandler.encode(archiver.get());
582     IPC::encode(encoder, (__bridge CFDataRef)archiver.get().encodedData);
583 }
584
585 bool ArgumentCoder<ContentFilterUnblockHandler>::decode(Decoder& decoder, ContentFilterUnblockHandler& contentFilterUnblockHandler)
586 {
587     RetainPtr<CFDataRef> data;
588     if (!IPC::decode(decoder, data))
589         return false;
590
591     auto unarchiver = secureUnarchiverFromData((__bridge NSData *)data.get());
592     if (!ContentFilterUnblockHandler::decode(unarchiver.get(), contentFilterUnblockHandler))
593         return false;
594
595     [unarchiver finishDecoding];
596     return true;
597 }
598
599 #endif
600
601 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
602
603 static NSString *deviceContextKey()
604 {
605     static NSString * const key = @"deviceContext";
606     return key;
607 }
608
609 void ArgumentCoder<MediaPlaybackTargetContext>::encodePlatformData(Encoder& encoder, const MediaPlaybackTargetContext& target)
610 {
611     auto archiver = secureArchiver();
612
613     if ([getAVOutputContextClass() conformsToProtocol:@protocol(NSSecureCoding)])
614         [archiver encodeObject:target.avOutputContext() forKey:deviceContextKey()];
615
616     IPC::encode(encoder, (__bridge CFDataRef)archiver.get().encodedData);
617 }
618
619 bool ArgumentCoder<MediaPlaybackTargetContext>::decodePlatformData(Decoder& decoder, MediaPlaybackTargetContext& target)
620 {
621     if (![getAVOutputContextClass() conformsToProtocol:@protocol(NSSecureCoding)])
622         return false;
623
624     RetainPtr<CFDataRef> data;
625     if (!IPC::decode(decoder, data))
626         return false;
627
628     auto unarchiver = secureUnarchiverFromData((__bridge NSData *)data.get());
629
630     AVOutputContext *context = nil;
631     @try {
632         context = [unarchiver decodeObjectOfClass:getAVOutputContextClass() forKey:deviceContextKey()];
633     } @catch (NSException *exception) {
634         LOG_ERROR("The target picker being decoded is not an AVOutputContext.");
635         return false;
636     }
637
638     target = MediaPlaybackTargetContext(context);
639     
640     [unarchiver finishDecoding];
641     return true;
642 }
643
644 #endif
645
646 } // namespace IPC