[CF] Upstream CFNetwork-related WebKitSystemInterface functions
[WebKit-https.git] / Source / WebKit / Shared / mac / WebCoreArgumentCodersMac.mm
1 /*
2  * Copyright (C) 2010 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 "WebKitSystemInterface.h"
33 #import <WebCore/CertificateInfo.h>
34 #import <WebCore/ContentFilterUnblockHandler.h>
35 #import <WebCore/Credential.h>
36 #import <WebCore/KeyboardEvent.h>
37 #import <WebCore/MachSendRight.h>
38 #import <WebCore/ProtectionSpace.h>
39 #import <WebCore/ResourceError.h>
40 #import <WebCore/ResourceRequest.h>
41 #import <pal/spi/cf/CFNetworkSPI.h>
42
43 #if USE(CFURLCONNECTION)
44 #import <CFNetwork/CFURLRequest.h>
45 #endif
46
47 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
48 #import <WebCore/MediaPlaybackTargetContext.h>
49 #import <objc/runtime.h>
50 #import <pal/spi/mac/AVFoundationSPI.h>
51 #import <wtf/SoftLinking.h>
52
53 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
54 SOFT_LINK_CLASS(AVFoundation, AVOutputContext)
55 #endif
56
57 using namespace WebCore;
58
59 namespace IPC {
60
61 static RetainPtr<CFMutableDictionaryRef> createSerializableRepresentation(CFIndex version, CFTypeRef* objects, CFIndex objectCount, CFDictionaryRef protocolProperties, CFNumberRef expectedContentLength, CFStringRef mimeType, CFTypeRef tokenNull)
62 {
63     auto archiveListArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
64
65     for (CFIndex i = 0; i < objectCount; ++i) {
66         CFTypeRef object = objects[i];
67         if (object) {
68             CFArrayAppendValue(archiveListArray.get(), object);
69             CFRelease(object);
70         } else {
71             // Append our token null representation.
72             CFArrayAppendValue(archiveListArray.get(), tokenNull);
73         }
74     }
75
76     auto dictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
77
78     auto versionNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &version));
79     CFDictionarySetValue(dictionary.get(), CFSTR("version"), versionNumber.get());
80     CFDictionarySetValue(dictionary.get(), CFSTR("archiveList"), archiveListArray.get());
81
82     if (protocolProperties) {
83         CFDictionarySetValue(dictionary.get(), CFSTR("protocolProperties"), protocolProperties);
84         CFRelease(protocolProperties);
85     }
86
87     if (expectedContentLength) {
88         CFDictionarySetValue(dictionary.get(), CFSTR("expectedContentLength"), expectedContentLength);
89         CFRelease(expectedContentLength);
90     }
91
92     if (mimeType) {
93         CFDictionarySetValue(dictionary.get(), CFSTR("mimeType"), mimeType);
94         CFRelease(mimeType);
95     }
96
97     CFAllocatorDeallocate(kCFAllocatorDefault, objects);
98
99     return dictionary;
100 }
101
102 static CFTypeRef dictionaryValueOfType(CFDictionaryRef dictionary, CFStringRef key, CFTypeID type)
103 {
104     CFTypeRef value = CFDictionaryGetValue(dictionary, key);
105     if (value && CFGetTypeID(value) == type)
106         return value;
107     return nullptr;
108 }
109
110 static bool createArchiveList(CFDictionaryRef representation, CFTypeRef tokenNull, CFIndex* version, CFTypeRef** objects, CFIndex* objectCount, CFDictionaryRef* protocolProperties, CFNumberRef* expectedContentLength, CFStringRef* mimeType)
111 {
112     CFNumberRef versionNumber = (CFNumberRef)dictionaryValueOfType(representation, CFSTR("version"), CFNumberGetTypeID());
113     if (!versionNumber)
114         return false;
115
116     if (!CFNumberGetValue(versionNumber, kCFNumberCFIndexType, version))
117         return false;
118
119     CFArrayRef archiveListArray = (CFArrayRef)dictionaryValueOfType(representation, CFSTR("archiveList"), CFArrayGetTypeID());
120     if (!archiveListArray)
121         return false;
122
123     *objectCount = CFArrayGetCount(archiveListArray);
124     *objects = (CFTypeRef*)malloc(sizeof(CFTypeRef) * *objectCount);
125     for (CFIndex i = 0; i < *objectCount; ++i) {
126         CFTypeRef object = CFArrayGetValueAtIndex(archiveListArray, i);
127         if (object == tokenNull)
128             (*objects)[i] = nullptr;
129         else
130             (*objects)[i] = object;
131     }
132
133     if (protocolProperties)
134         *protocolProperties = (CFDictionaryRef)dictionaryValueOfType(representation, CFSTR("protocolProperties"), CFDictionaryGetTypeID());
135
136     if (expectedContentLength)
137         *expectedContentLength = (CFNumberRef)dictionaryValueOfType(representation, CFSTR("expectedContentLength"), CFNumberGetTypeID());
138
139     if (mimeType)
140         *mimeType = (CFStringRef)dictionaryValueOfType(representation, CFSTR("mimeType"), CFStringGetTypeID());
141
142     return true;
143 }
144
145 static RetainPtr<CFDictionaryRef> createSerializableRepresentation(CFURLRequestRef cfRequest, CFTypeRef tokenNull)
146 {
147     CFIndex version;
148     CFTypeRef* objects;
149     CFIndex objectCount;
150     CFDictionaryRef protocolProperties;
151
152     // FIXME (12889518): Do not serialize HTTP message body.
153     // 1. It can be large and thus costly to send across.
154     // 2. It is misleading to provide a body with some requests, while others use body streams, which cannot be serialized at all.
155
156     _CFURLRequestCreateArchiveList(kCFAllocatorDefault, cfRequest, &version, &objects, &objectCount, &protocolProperties);
157
158     // This will deallocate the passed in arguments.
159     return createSerializableRepresentation(version, objects, objectCount, protocolProperties, nullptr, nullptr, tokenNull);
160 }
161
162 static RetainPtr<CFURLRequestRef> createCFURLRequestFromSerializableRepresentation(CFDictionaryRef representation, CFTypeRef tokenNull)
163 {
164     CFIndex version;
165     CFTypeRef* objects;
166     CFIndex objectCount;
167     CFDictionaryRef protocolProperties;
168
169     if (!createArchiveList(representation, tokenNull, &version, &objects, &objectCount, &protocolProperties, nullptr, nullptr))
170         return nullptr;
171
172     auto cfRequest = adoptCF(_CFURLRequestCreateFromArchiveList(kCFAllocatorDefault, version, objects, objectCount, protocolProperties));
173     free(objects);
174     return WTFMove(cfRequest);
175 }
176
177 static RetainPtr<CFDictionaryRef> createSerializableRepresentation(NSURLRequest *request, CFTypeRef tokenNull)
178 {
179     return createSerializableRepresentation([request _CFURLRequest], tokenNull);
180 }
181
182 static RetainPtr<NSURLRequest> createNSURLRequestFromSerializableRepresentation(CFDictionaryRef representation, CFTypeRef tokenNull)
183 {
184     auto cfRequest = createCFURLRequestFromSerializableRepresentation(representation, tokenNull);
185     if (!cfRequest)
186         return nullptr;
187
188     return adoptNS([[NSURLRequest alloc] _initWithCFURLRequest:cfRequest.get()]);
189 }
190
191 #if USE(CFURLCONNECTION)
192 void ArgumentCoder<ResourceRequest>::encodePlatformData(Encoder& encoder, const ResourceRequest& resourceRequest)
193 {
194     RetainPtr<CFURLRequestRef> requestToSerialize = resourceRequest.cfURLRequest(DoNotUpdateHTTPBody);
195
196     bool requestIsPresent = requestToSerialize;
197     encoder << requestIsPresent;
198
199     if (!requestIsPresent)
200         return;
201
202     // We don't send HTTP body over IPC for better performance.
203     // Also, it's not always possible to do, as streams can only be created in process that does networking.
204     RetainPtr<CFDataRef> requestHTTPBody = adoptCF(CFURLRequestCopyHTTPRequestBody(requestToSerialize.get()));
205     RetainPtr<CFReadStreamRef> requestHTTPBodyStream = adoptCF(CFURLRequestCopyHTTPRequestBodyStream(requestToSerialize.get()));
206     if (requestHTTPBody || requestHTTPBodyStream) {
207         CFMutableURLRequestRef mutableRequest = CFURLRequestCreateMutableCopy(0, requestToSerialize.get());
208         requestToSerialize = adoptCF(mutableRequest);
209         CFURLRequestSetHTTPRequestBody(mutableRequest, nil);
210         CFURLRequestSetHTTPRequestBodyStream(mutableRequest, nil);
211     }
212
213     RetainPtr<CFDictionaryRef> dictionary = createSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef());
214     IPC::encode(encoder, dictionary.get());
215
216     // The fallback array is part of CFURLRequest, but it is not encoded by WKCFURLRequestCreateSerializableRepresentation.
217     encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
218     encoder.encodeEnum(resourceRequest.requester());
219     encoder.encodeEnum(resourceRequest.cachePolicy());
220 }
221 #else
222 void ArgumentCoder<ResourceRequest>::encodePlatformData(Encoder& encoder, const ResourceRequest& resourceRequest)
223 {
224     RetainPtr<NSURLRequest> requestToSerialize = resourceRequest.nsURLRequest(DoNotUpdateHTTPBody);
225
226     bool requestIsPresent = requestToSerialize;
227     encoder << requestIsPresent;
228
229     if (!requestIsPresent)
230         return;
231
232     // We don't send HTTP body over IPC for better performance.
233     // Also, it's not always possible to do, as streams can only be created in process that does networking.
234     if ([requestToSerialize HTTPBody] || [requestToSerialize HTTPBodyStream]) {
235         requestToSerialize = adoptNS([requestToSerialize mutableCopy]);
236         [(NSMutableURLRequest *)requestToSerialize setHTTPBody:nil];
237         [(NSMutableURLRequest *)requestToSerialize setHTTPBodyStream:nil];
238     }
239
240     RetainPtr<CFDictionaryRef> dictionary = createSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef());
241     IPC::encode(encoder, dictionary.get());
242
243     // The fallback array is part of NSURLRequest, but it is not encoded by WKNSURLRequestCreateSerializableRepresentation.
244     encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
245     encoder.encodeEnum(resourceRequest.requester());
246     encoder.encodeEnum(resourceRequest.cachePolicy());
247 }
248 #endif
249
250 bool ArgumentCoder<ResourceRequest>::decodePlatformData(Decoder& decoder, ResourceRequest& resourceRequest)
251 {
252     bool requestIsPresent;
253     if (!decoder.decode(requestIsPresent))
254         return false;
255
256     if (!requestIsPresent) {
257         resourceRequest = ResourceRequest();
258         return true;
259     }
260
261     RetainPtr<CFDictionaryRef> dictionary;
262     if (!IPC::decode(decoder, dictionary))
263         return false;
264
265 #if USE(CFURLCONNECTION)
266     RetainPtr<CFURLRequestRef> cfURLRequest = createCFURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef());
267     if (!cfURLRequest)
268         return false;
269
270     resourceRequest = ResourceRequest(cfURLRequest.get());
271 #else
272     RetainPtr<NSURLRequest> nsURLRequest = createNSURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef());
273     if (!nsURLRequest)
274         return false;
275
276     resourceRequest = ResourceRequest(nsURLRequest.get());
277 #endif
278     
279     Vector<String> responseContentDispositionEncodingFallbackArray;
280     if (!decoder.decode(responseContentDispositionEncodingFallbackArray))
281         return false;
282
283     resourceRequest.setResponseContentDispositionEncodingFallbackArray(
284         responseContentDispositionEncodingFallbackArray.size() > 0 ? responseContentDispositionEncodingFallbackArray[0] : String(),
285         responseContentDispositionEncodingFallbackArray.size() > 1 ? responseContentDispositionEncodingFallbackArray[1] : String(),
286         responseContentDispositionEncodingFallbackArray.size() > 2 ? responseContentDispositionEncodingFallbackArray[2] : String()
287     );
288
289     ResourceRequest::Requester requester;
290     if (!decoder.decodeEnum(requester))
291         return false;
292     resourceRequest.setRequester(requester);
293
294     ResourceRequestCachePolicy cachePolicy;
295     if (!decoder.decodeEnum(cachePolicy))
296         return false;
297     resourceRequest.setCachePolicy(cachePolicy);
298
299     return true;
300 }
301
302 void ArgumentCoder<CertificateInfo>::encode(Encoder& encoder, const CertificateInfo& certificateInfo)
303 {
304     encoder.encodeEnum(certificateInfo.type());
305
306     switch (certificateInfo.type()) {
307 #if HAVE(SEC_TRUST_SERIALIZATION)
308     case CertificateInfo::Type::Trust:
309         IPC::encode(encoder, certificateInfo.trust());
310         break;
311 #endif
312     case CertificateInfo::Type::CertificateChain:
313         IPC::encode(encoder, certificateInfo.certificateChain());
314         break;
315     case CertificateInfo::Type::None:
316         // Do nothing.
317         break;
318     }
319 }
320
321 bool ArgumentCoder<CertificateInfo>::decode(Decoder& decoder, CertificateInfo& certificateInfo)
322 {
323     CertificateInfo::Type certificateInfoType;
324     if (!decoder.decodeEnum(certificateInfoType))
325         return false;
326
327     switch (certificateInfoType) {
328 #if HAVE(SEC_TRUST_SERIALIZATION)
329     case CertificateInfo::Type::Trust: {
330         RetainPtr<SecTrustRef> trust;
331         if (!IPC::decode(decoder, trust))
332             return false;
333
334         certificateInfo = CertificateInfo(WTFMove(trust));
335         return true;
336     }
337 #endif
338     case CertificateInfo::Type::CertificateChain: {
339         RetainPtr<CFArrayRef> certificateChain;
340         if (!IPC::decode(decoder, certificateChain))
341             return false;
342
343         certificateInfo = CertificateInfo(WTFMove(certificateChain));
344         return true;
345     }    
346     case CertificateInfo::Type::None:
347         // Do nothing.
348         break;
349     }
350
351     return true;
352 }
353
354 static void encodeNSError(Encoder& encoder, NSError *nsError)
355 {
356     String domain = [nsError domain];
357     encoder << domain;
358
359     int64_t code = [nsError code];
360     encoder << code;
361
362     NSDictionary *userInfo = [nsError userInfo];
363
364     RetainPtr<CFMutableDictionaryRef> filteredUserInfo = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, userInfo.count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
365
366     [userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL*) {
367         if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSURL class]] || [value isKindOfClass:[NSNumber class]])
368             CFDictionarySetValue(filteredUserInfo.get(), key, value);
369     }];
370
371     if (NSArray *clientIdentityAndCertificates = [userInfo objectForKey:@"NSErrorClientCertificateChainKey"]) {
372         ASSERT([clientIdentityAndCertificates isKindOfClass:[NSArray class]]);
373         ASSERT(^{
374             for (id object in clientIdentityAndCertificates) {
375                 if (CFGetTypeID(object) != SecIdentityGetTypeID() && CFGetTypeID(object) != SecCertificateGetTypeID())
376                     return false;
377             }
378             return true;
379         }());
380
381         CFDictionarySetValue(filteredUserInfo.get(), @"NSErrorClientCertificateChainKey", clientIdentityAndCertificates);
382     }
383
384     id peerCertificateChain = [userInfo objectForKey:@"NSErrorPeerCertificateChainKey"];
385     if (!peerCertificateChain) {
386         if (SecTrustRef peerTrust = (SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
387             CFIndex count = SecTrustGetCertificateCount(peerTrust);
388             peerCertificateChain = [NSMutableArray arrayWithCapacity:count];
389             for (CFIndex i = 0; i < count; ++i)
390                 [peerCertificateChain addObject:(id)SecTrustGetCertificateAtIndex(peerTrust, i)];
391         }
392     }
393     ASSERT(!peerCertificateChain || [peerCertificateChain isKindOfClass:[NSArray class]]);
394     if (peerCertificateChain)
395         CFDictionarySetValue(filteredUserInfo.get(), @"NSErrorPeerCertificateChainKey", peerCertificateChain);
396
397 #if HAVE(SEC_TRUST_SERIALIZATION)
398     if (SecTrustRef peerTrust = (SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey])
399         CFDictionarySetValue(filteredUserInfo.get(), NSURLErrorFailingURLPeerTrustErrorKey, peerTrust);
400 #endif
401
402     IPC::encode(encoder, filteredUserInfo.get());
403
404     if (id underlyingError = [userInfo objectForKey:NSUnderlyingErrorKey]) {
405         ASSERT([underlyingError isKindOfClass:[NSError class]]);
406         encoder << true;
407         encodeNSError(encoder, underlyingError);
408     } else
409         encoder << false;
410 }
411
412 void ArgumentCoder<ResourceError>::encodePlatformData(Encoder& encoder, const ResourceError& resourceError)
413 {
414     bool errorIsNull = resourceError.isNull();
415     encoder << errorIsNull;
416
417     if (errorIsNull)
418         return;
419
420     NSError *nsError = resourceError.nsError();
421     encodeNSError(encoder, nsError);
422 }
423
424 static bool decodeNSError(Decoder& decoder, RetainPtr<NSError>& nsError)
425 {
426     String domain;
427     if (!decoder.decode(domain))
428         return false;
429
430     int64_t code;
431     if (!decoder.decode(code))
432         return false;
433
434     RetainPtr<CFDictionaryRef> userInfo;
435     if (!IPC::decode(decoder, userInfo))
436         return false;
437
438     bool hasUnderlyingError = false;
439     if (!decoder.decode(hasUnderlyingError))
440         return false;
441
442     if (hasUnderlyingError) {
443         RetainPtr<NSError> underlyingNSError;
444         if (!decodeNSError(decoder, underlyingNSError))
445             return false;
446
447         userInfo = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(userInfo.get()) + 1, userInfo.get()));
448         CFDictionarySetValue((CFMutableDictionaryRef)userInfo.get(), NSUnderlyingErrorKey, underlyingNSError.get());
449     }
450
451     nsError = adoptNS([[NSError alloc] initWithDomain:domain code:code userInfo:(NSDictionary *)userInfo.get()]);
452     return true;
453 }
454
455 bool ArgumentCoder<ResourceError>::decodePlatformData(Decoder& decoder, ResourceError& resourceError)
456 {
457     bool errorIsNull;
458     if (!decoder.decode(errorIsNull))
459         return false;
460     
461     if (errorIsNull) {
462         resourceError = ResourceError();
463         return true;
464     }
465     
466     RetainPtr<NSError> nsError;
467     if (!decodeNSError(decoder, nsError))
468         return false;
469
470     resourceError = ResourceError(nsError.get());
471     return true;
472 }
473
474 void ArgumentCoder<ProtectionSpace>::encodePlatformData(Encoder& encoder, const ProtectionSpace& space)
475 {
476     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
477     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
478     [archiver setRequiresSecureCoding:YES];
479     [archiver encodeObject:space.nsSpace() forKey:@"protectionSpace"];
480     [archiver finishEncoding];
481     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
482 }
483
484 bool ArgumentCoder<ProtectionSpace>::decodePlatformData(Decoder& decoder, ProtectionSpace& space)
485 {
486     RetainPtr<CFDataRef> data;
487     if (!IPC::decode(decoder, data))
488         return false;
489
490     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
491     [unarchiver setRequiresSecureCoding:YES];
492     @try {
493         if (RetainPtr<NSURLProtectionSpace> nsSpace = [unarchiver decodeObjectOfClass:[NSURLProtectionSpace class] forKey:@"protectionSpace"])
494             space = ProtectionSpace(nsSpace.get());
495     } @catch (NSException *exception) {
496         LOG_ERROR("Failed to decode NSURLProtectionSpace: %@", exception);
497     }
498
499     [unarchiver finishDecoding];
500     return true;
501 }
502
503 void ArgumentCoder<Credential>::encodePlatformData(Encoder& encoder, const Credential& credential)
504 {
505     NSURLCredential *nsCredential = credential.nsCredential();
506     // NSURLCredential doesn't serialize identities correctly, so we encode the pieces individually
507     // in the identity case. See <rdar://problem/18802434>.
508     if (SecIdentityRef identity = nsCredential.identity) {
509         encoder << true;
510         IPC::encode(encoder, identity);
511
512         if (NSArray *certificates = nsCredential.certificates) {
513             encoder << true;
514             IPC::encode(encoder, reinterpret_cast<CFArrayRef>(certificates));
515         } else
516             encoder << false;
517
518         encoder << static_cast<uint64_t>(nsCredential.persistence);
519         return;
520     }
521
522     encoder << false;
523     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
524     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
525     [archiver setRequiresSecureCoding:YES];
526     [archiver encodeObject:nsCredential forKey:@"credential"];
527     [archiver finishEncoding];
528     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
529 }
530
531 bool ArgumentCoder<Credential>::decodePlatformData(Decoder& decoder, Credential& credential)
532 {
533     bool hasIdentity;
534     if (!decoder.decode(hasIdentity))
535         return false;
536
537     if (hasIdentity) {
538         RetainPtr<SecIdentityRef> identity;
539         if (!IPC::decode(decoder, identity))
540             return false;
541
542         RetainPtr<CFArrayRef> certificates;
543         bool hasCertificates;
544         if (!decoder.decode(hasCertificates))
545             return false;
546
547         if (hasCertificates) {
548             if (!IPC::decode(decoder, certificates))
549                 return false;
550         }
551
552         uint64_t persistence;
553         if (!decoder.decode(persistence))
554             return false;
555
556         credential = Credential(adoptNS([[NSURLCredential alloc] initWithIdentity:identity.get() certificates:(NSArray *)certificates.get() persistence:(NSURLCredentialPersistence)persistence]).get());
557         return true;
558     }
559
560     RetainPtr<CFDataRef> data;
561     if (!IPC::decode(decoder, data))
562         return false;
563
564     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
565     [unarchiver setRequiresSecureCoding:YES];
566     @try {
567         if (RetainPtr<NSURLCredential> nsCredential = [unarchiver decodeObjectOfClass:[NSURLCredential class] forKey:@"credential"])
568             credential = Credential(nsCredential.get());
569     } @catch (NSException *exception) {
570         LOG_ERROR("Failed to decode NSURLCredential: %@", exception);
571     }
572
573     [unarchiver finishDecoding];
574     return true;
575 }
576
577 void ArgumentCoder<MachSendRight>::encode(Encoder& encoder, const MachSendRight& sendRight)
578 {
579     encoder << Attachment(sendRight.copySendRight().leakSendRight(), MACH_MSG_TYPE_MOVE_SEND);
580 }
581
582 void ArgumentCoder<MachSendRight>::encode(Encoder& encoder, MachSendRight&& sendRight)
583 {
584     encoder << Attachment(sendRight.leakSendRight(), MACH_MSG_TYPE_MOVE_SEND);
585 }
586
587 bool ArgumentCoder<MachSendRight>::decode(Decoder& decoder, MachSendRight& sendRight)
588 {
589     Attachment attachment;
590     if (!decoder.decode(attachment))
591         return false;
592
593     if (attachment.disposition() != MACH_MSG_TYPE_MOVE_SEND)
594         return false;
595
596     sendRight = MachSendRight::adopt(attachment.port());
597     return true;
598 }
599
600 void ArgumentCoder<KeypressCommand>::encode(Encoder& encoder, const KeypressCommand& keypressCommand)
601 {
602     encoder << keypressCommand.commandName << keypressCommand.text;
603 }
604     
605 bool ArgumentCoder<KeypressCommand>::decode(Decoder& decoder, KeypressCommand& keypressCommand)
606 {
607     if (!decoder.decode(keypressCommand.commandName))
608         return false;
609
610     if (!decoder.decode(keypressCommand.text))
611         return false;
612
613     return true;
614 }
615
616 #if ENABLE(CONTENT_FILTERING)
617 void ArgumentCoder<ContentFilterUnblockHandler>::encode(Encoder& encoder, const ContentFilterUnblockHandler& contentFilterUnblockHandler)
618 {
619     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
620     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
621     [archiver setRequiresSecureCoding:YES];
622     contentFilterUnblockHandler.encode(archiver.get());
623     [archiver finishEncoding];
624     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
625 }
626
627 bool ArgumentCoder<ContentFilterUnblockHandler>::decode(Decoder& decoder, ContentFilterUnblockHandler& contentFilterUnblockHandler)
628 {
629     RetainPtr<CFDataRef> data;
630     if (!IPC::decode(decoder, data))
631         return false;
632
633     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
634     [unarchiver setRequiresSecureCoding:YES];
635     if (!ContentFilterUnblockHandler::decode(unarchiver.get(), contentFilterUnblockHandler))
636         return false;
637
638     [unarchiver finishDecoding];
639     return true;
640 }
641 #endif
642
643 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
644
645 static NSString *deviceContextKey()
646 {
647     static NSString * const key = @"deviceContext";
648     return key;
649 }
650
651 void ArgumentCoder<MediaPlaybackTargetContext>::encodePlatformData(Encoder& encoder, const MediaPlaybackTargetContext& target)
652 {
653     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
654     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
655     [archiver setRequiresSecureCoding:YES];
656
657     if ([getAVOutputContextClass() conformsToProtocol:@protocol(NSSecureCoding)])
658         [archiver encodeObject:target.avOutputContext() forKey:deviceContextKey()];
659
660     [archiver finishEncoding];
661     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
662
663 }
664
665 bool ArgumentCoder<MediaPlaybackTargetContext>::decodePlatformData(Decoder& decoder, MediaPlaybackTargetContext& target)
666 {
667     if (![getAVOutputContextClass() conformsToProtocol:@protocol(NSSecureCoding)])
668         return false;
669
670     RetainPtr<CFDataRef> data;
671     if (!IPC::decode(decoder, data))
672         return false;
673
674     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
675     [unarchiver setRequiresSecureCoding:YES];
676
677     AVOutputContext *context = nil;
678     @try {
679         context = [unarchiver decodeObjectOfClass:getAVOutputContextClass() forKey:deviceContextKey()];
680     } @catch (NSException *exception) {
681         LOG_ERROR("The target picker being decoded is not an AVOutputContext.");
682         return false;
683     }
684
685     target = MediaPlaybackTargetContext(context);
686     
687     [unarchiver finishDecoding];
688     return true;
689 }
690 #endif
691
692 } // namespace IPC