Rename Source/WebKit2 to Source/WebKit.
[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
42 #if USE(CFURLCONNECTION)
43 #import <CFNetwork/CFURLRequest.h>
44 #endif
45
46 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
47 #import <WebCore/AVFoundationSPI.h>
48 #import <WebCore/MediaPlaybackTargetContext.h>
49 #import <objc/runtime.h>
50 #import <wtf/SoftLinking.h>
51
52 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
53 SOFT_LINK_CLASS(AVFoundation, AVOutputContext)
54 #endif
55
56 using namespace WebCore;
57
58 namespace IPC {
59
60 #if USE(CFURLCONNECTION)
61 void ArgumentCoder<ResourceRequest>::encodePlatformData(Encoder& encoder, const ResourceRequest& resourceRequest)
62 {
63     RetainPtr<CFURLRequestRef> requestToSerialize = resourceRequest.cfURLRequest(DoNotUpdateHTTPBody);
64
65     bool requestIsPresent = requestToSerialize;
66     encoder << requestIsPresent;
67
68     if (!requestIsPresent)
69         return;
70
71     // We don't send HTTP body over IPC for better performance.
72     // Also, it's not always possible to do, as streams can only be created in process that does networking.
73     RetainPtr<CFDataRef> requestHTTPBody = adoptCF(CFURLRequestCopyHTTPRequestBody(requestToSerialize.get()));
74     RetainPtr<CFReadStreamRef> requestHTTPBodyStream = adoptCF(CFURLRequestCopyHTTPRequestBodyStream(requestToSerialize.get()));
75     if (requestHTTPBody || requestHTTPBodyStream) {
76         CFMutableURLRequestRef mutableRequest = CFURLRequestCreateMutableCopy(0, requestToSerialize.get());
77         requestToSerialize = adoptCF(mutableRequest);
78         CFURLRequestSetHTTPRequestBody(mutableRequest, nil);
79         CFURLRequestSetHTTPRequestBodyStream(mutableRequest, nil);
80     }
81
82     RetainPtr<CFDictionaryRef> dictionary = adoptCF(WKCFURLRequestCreateSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef()));
83     IPC::encode(encoder, dictionary.get());
84
85     // The fallback array is part of CFURLRequest, but it is not encoded by WKCFURLRequestCreateSerializableRepresentation.
86     encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
87     encoder.encodeEnum(resourceRequest.requester());
88     encoder.encodeEnum(resourceRequest.cachePolicy());
89 }
90 #else
91 void ArgumentCoder<ResourceRequest>::encodePlatformData(Encoder& encoder, const ResourceRequest& resourceRequest)
92 {
93     RetainPtr<NSURLRequest> requestToSerialize = resourceRequest.nsURLRequest(DoNotUpdateHTTPBody);
94
95     bool requestIsPresent = requestToSerialize;
96     encoder << requestIsPresent;
97
98     if (!requestIsPresent)
99         return;
100
101     // We don't send HTTP body over IPC for better performance.
102     // Also, it's not always possible to do, as streams can only be created in process that does networking.
103     if ([requestToSerialize HTTPBody] || [requestToSerialize HTTPBodyStream]) {
104         requestToSerialize = adoptNS([requestToSerialize mutableCopy]);
105         [(NSMutableURLRequest *)requestToSerialize setHTTPBody:nil];
106         [(NSMutableURLRequest *)requestToSerialize setHTTPBodyStream:nil];
107     }
108
109     RetainPtr<CFDictionaryRef> dictionary = adoptCF(WKNSURLRequestCreateSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef()));
110     IPC::encode(encoder, dictionary.get());
111
112     // The fallback array is part of NSURLRequest, but it is not encoded by WKNSURLRequestCreateSerializableRepresentation.
113     encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
114     encoder.encodeEnum(resourceRequest.requester());
115     encoder.encodeEnum(resourceRequest.cachePolicy());
116 }
117 #endif
118
119 bool ArgumentCoder<ResourceRequest>::decodePlatformData(Decoder& decoder, ResourceRequest& resourceRequest)
120 {
121     bool requestIsPresent;
122     if (!decoder.decode(requestIsPresent))
123         return false;
124
125     if (!requestIsPresent) {
126         resourceRequest = ResourceRequest();
127         return true;
128     }
129
130     RetainPtr<CFDictionaryRef> dictionary;
131     if (!IPC::decode(decoder, dictionary))
132         return false;
133
134 #if USE(CFURLCONNECTION)
135     RetainPtr<CFURLRequestRef> cfURLRequest = adoptCF(WKCreateCFURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef()));
136     if (!cfURLRequest)
137         return false;
138
139     resourceRequest = ResourceRequest(cfURLRequest.get());
140 #else
141     RetainPtr<NSURLRequest> nsURLRequest = WKNSURLRequestFromSerializableRepresentation(dictionary.get(), IPC::tokenNullTypeRef());
142     if (!nsURLRequest)
143         return false;
144
145     resourceRequest = ResourceRequest(nsURLRequest.get());
146 #endif
147     
148     Vector<String> responseContentDispositionEncodingFallbackArray;
149     if (!decoder.decode(responseContentDispositionEncodingFallbackArray))
150         return false;
151
152     resourceRequest.setResponseContentDispositionEncodingFallbackArray(
153         responseContentDispositionEncodingFallbackArray.size() > 0 ? responseContentDispositionEncodingFallbackArray[0] : String(),
154         responseContentDispositionEncodingFallbackArray.size() > 1 ? responseContentDispositionEncodingFallbackArray[1] : String(),
155         responseContentDispositionEncodingFallbackArray.size() > 2 ? responseContentDispositionEncodingFallbackArray[2] : String()
156     );
157
158     ResourceRequest::Requester requester;
159     if (!decoder.decodeEnum(requester))
160         return false;
161     resourceRequest.setRequester(requester);
162
163     ResourceRequestCachePolicy cachePolicy;
164     if (!decoder.decodeEnum(cachePolicy))
165         return false;
166     resourceRequest.setCachePolicy(cachePolicy);
167
168     return true;
169 }
170
171 void ArgumentCoder<CertificateInfo>::encode(Encoder& encoder, const CertificateInfo& certificateInfo)
172 {
173     encoder.encodeEnum(certificateInfo.type());
174
175     switch (certificateInfo.type()) {
176 #if HAVE(SEC_TRUST_SERIALIZATION)
177     case CertificateInfo::Type::Trust:
178         IPC::encode(encoder, certificateInfo.trust());
179         break;
180 #endif
181     case CertificateInfo::Type::CertificateChain:
182         IPC::encode(encoder, certificateInfo.certificateChain());
183         break;
184     case CertificateInfo::Type::None:
185         // Do nothing.
186         break;
187     }
188 }
189
190 bool ArgumentCoder<CertificateInfo>::decode(Decoder& decoder, CertificateInfo& certificateInfo)
191 {
192     CertificateInfo::Type certificateInfoType;
193     if (!decoder.decodeEnum(certificateInfoType))
194         return false;
195
196     switch (certificateInfoType) {
197 #if HAVE(SEC_TRUST_SERIALIZATION)
198     case CertificateInfo::Type::Trust: {
199         RetainPtr<SecTrustRef> trust;
200         if (!IPC::decode(decoder, trust))
201             return false;
202
203         certificateInfo = CertificateInfo(WTFMove(trust));
204         return true;
205     }
206 #endif
207     case CertificateInfo::Type::CertificateChain: {
208         RetainPtr<CFArrayRef> certificateChain;
209         if (!IPC::decode(decoder, certificateChain))
210             return false;
211
212         certificateInfo = CertificateInfo(WTFMove(certificateChain));
213         return true;
214     }    
215     case CertificateInfo::Type::None:
216         // Do nothing.
217         break;
218     }
219
220     return true;
221 }
222
223 static void encodeNSError(Encoder& encoder, NSError *nsError)
224 {
225     String domain = [nsError domain];
226     encoder << domain;
227
228     int64_t code = [nsError code];
229     encoder << code;
230
231     NSDictionary *userInfo = [nsError userInfo];
232
233     RetainPtr<CFMutableDictionaryRef> filteredUserInfo = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, userInfo.count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
234
235     [userInfo enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL*) {
236         if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSURL class]] || [value isKindOfClass:[NSNumber class]])
237             CFDictionarySetValue(filteredUserInfo.get(), key, value);
238     }];
239
240     if (NSArray *clientIdentityAndCertificates = [userInfo objectForKey:@"NSErrorClientCertificateChainKey"]) {
241         ASSERT([clientIdentityAndCertificates isKindOfClass:[NSArray class]]);
242         ASSERT(^{
243             for (id object in clientIdentityAndCertificates) {
244                 if (CFGetTypeID(object) != SecIdentityGetTypeID() && CFGetTypeID(object) != SecCertificateGetTypeID())
245                     return false;
246             }
247             return true;
248         }());
249
250         CFDictionarySetValue(filteredUserInfo.get(), @"NSErrorClientCertificateChainKey", clientIdentityAndCertificates);
251     }
252
253     id peerCertificateChain = [userInfo objectForKey:@"NSErrorPeerCertificateChainKey"];
254     if (!peerCertificateChain) {
255         if (SecTrustRef peerTrust = (SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey]) {
256             CFIndex count = SecTrustGetCertificateCount(peerTrust);
257             peerCertificateChain = [NSMutableArray arrayWithCapacity:count];
258             for (CFIndex i = 0; i < count; ++i)
259                 [peerCertificateChain addObject:(id)SecTrustGetCertificateAtIndex(peerTrust, i)];
260         }
261     }
262     ASSERT(!peerCertificateChain || [peerCertificateChain isKindOfClass:[NSArray class]]);
263     if (peerCertificateChain)
264         CFDictionarySetValue(filteredUserInfo.get(), @"NSErrorPeerCertificateChainKey", peerCertificateChain);
265
266 #if HAVE(SEC_TRUST_SERIALIZATION)
267     if (SecTrustRef peerTrust = (SecTrustRef)[userInfo objectForKey:NSURLErrorFailingURLPeerTrustErrorKey])
268         CFDictionarySetValue(filteredUserInfo.get(), NSURLErrorFailingURLPeerTrustErrorKey, peerTrust);
269 #endif
270
271     IPC::encode(encoder, filteredUserInfo.get());
272
273     if (id underlyingError = [userInfo objectForKey:NSUnderlyingErrorKey]) {
274         ASSERT([underlyingError isKindOfClass:[NSError class]]);
275         encoder << true;
276         encodeNSError(encoder, underlyingError);
277     } else
278         encoder << false;
279 }
280
281 void ArgumentCoder<ResourceError>::encodePlatformData(Encoder& encoder, const ResourceError& resourceError)
282 {
283     bool errorIsNull = resourceError.isNull();
284     encoder << errorIsNull;
285
286     if (errorIsNull)
287         return;
288
289     NSError *nsError = resourceError.nsError();
290     encodeNSError(encoder, nsError);
291 }
292
293 static bool decodeNSError(Decoder& decoder, RetainPtr<NSError>& nsError)
294 {
295     String domain;
296     if (!decoder.decode(domain))
297         return false;
298
299     int64_t code;
300     if (!decoder.decode(code))
301         return false;
302
303     RetainPtr<CFDictionaryRef> userInfo;
304     if (!IPC::decode(decoder, userInfo))
305         return false;
306
307     bool hasUnderlyingError = false;
308     if (!decoder.decode(hasUnderlyingError))
309         return false;
310
311     if (hasUnderlyingError) {
312         RetainPtr<NSError> underlyingNSError;
313         if (!decodeNSError(decoder, underlyingNSError))
314             return false;
315
316         userInfo = adoptCF(CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(userInfo.get()) + 1, userInfo.get()));
317         CFDictionarySetValue((CFMutableDictionaryRef)userInfo.get(), NSUnderlyingErrorKey, underlyingNSError.get());
318     }
319
320     nsError = adoptNS([[NSError alloc] initWithDomain:domain code:code userInfo:(NSDictionary *)userInfo.get()]);
321     return true;
322 }
323
324 bool ArgumentCoder<ResourceError>::decodePlatformData(Decoder& decoder, ResourceError& resourceError)
325 {
326     bool errorIsNull;
327     if (!decoder.decode(errorIsNull))
328         return false;
329     
330     if (errorIsNull) {
331         resourceError = ResourceError();
332         return true;
333     }
334     
335     RetainPtr<NSError> nsError;
336     if (!decodeNSError(decoder, nsError))
337         return false;
338
339     resourceError = ResourceError(nsError.get());
340     return true;
341 }
342
343 void ArgumentCoder<ProtectionSpace>::encodePlatformData(Encoder& encoder, const ProtectionSpace& space)
344 {
345     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
346     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
347     [archiver setRequiresSecureCoding:YES];
348     [archiver encodeObject:space.nsSpace() forKey:@"protectionSpace"];
349     [archiver finishEncoding];
350     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
351 }
352
353 bool ArgumentCoder<ProtectionSpace>::decodePlatformData(Decoder& decoder, ProtectionSpace& space)
354 {
355     RetainPtr<CFDataRef> data;
356     if (!IPC::decode(decoder, data))
357         return false;
358
359     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
360     [unarchiver setRequiresSecureCoding:YES];
361     @try {
362         if (RetainPtr<NSURLProtectionSpace> nsSpace = [unarchiver decodeObjectOfClass:[NSURLProtectionSpace class] forKey:@"protectionSpace"])
363             space = ProtectionSpace(nsSpace.get());
364     } @catch (NSException *exception) {
365         LOG_ERROR("Failed to decode NSURLProtectionSpace: %@", exception);
366     }
367
368     [unarchiver finishDecoding];
369     return true;
370 }
371
372 void ArgumentCoder<Credential>::encodePlatformData(Encoder& encoder, const Credential& credential)
373 {
374     NSURLCredential *nsCredential = credential.nsCredential();
375     // NSURLCredential doesn't serialize identities correctly, so we encode the pieces individually
376     // in the identity case. See <rdar://problem/18802434>.
377     if (SecIdentityRef identity = nsCredential.identity) {
378         encoder << true;
379         IPC::encode(encoder, identity);
380
381         if (NSArray *certificates = nsCredential.certificates) {
382             encoder << true;
383             IPC::encode(encoder, reinterpret_cast<CFArrayRef>(certificates));
384         } else
385             encoder << false;
386
387         encoder << static_cast<uint64_t>(nsCredential.persistence);
388         return;
389     }
390
391     encoder << false;
392     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
393     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
394     [archiver setRequiresSecureCoding:YES];
395     [archiver encodeObject:nsCredential forKey:@"credential"];
396     [archiver finishEncoding];
397     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
398 }
399
400 bool ArgumentCoder<Credential>::decodePlatformData(Decoder& decoder, Credential& credential)
401 {
402     bool hasIdentity;
403     if (!decoder.decode(hasIdentity))
404         return false;
405
406     if (hasIdentity) {
407         RetainPtr<SecIdentityRef> identity;
408         if (!IPC::decode(decoder, identity))
409             return false;
410
411         RetainPtr<CFArrayRef> certificates;
412         bool hasCertificates;
413         if (!decoder.decode(hasCertificates))
414             return false;
415
416         if (hasCertificates) {
417             if (!IPC::decode(decoder, certificates))
418                 return false;
419         }
420
421         uint64_t persistence;
422         if (!decoder.decode(persistence))
423             return false;
424
425         credential = Credential(adoptNS([[NSURLCredential alloc] initWithIdentity:identity.get() certificates:(NSArray *)certificates.get() persistence:(NSURLCredentialPersistence)persistence]).get());
426         return true;
427     }
428
429     RetainPtr<CFDataRef> data;
430     if (!IPC::decode(decoder, data))
431         return false;
432
433     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
434     [unarchiver setRequiresSecureCoding:YES];
435     @try {
436         if (RetainPtr<NSURLCredential> nsCredential = [unarchiver decodeObjectOfClass:[NSURLCredential class] forKey:@"credential"])
437             credential = Credential(nsCredential.get());
438     } @catch (NSException *exception) {
439         LOG_ERROR("Failed to decode NSURLCredential: %@", exception);
440     }
441
442     [unarchiver finishDecoding];
443     return true;
444 }
445
446 void ArgumentCoder<MachSendRight>::encode(Encoder& encoder, const MachSendRight& sendRight)
447 {
448     encoder << Attachment(sendRight.copySendRight().leakSendRight(), MACH_MSG_TYPE_MOVE_SEND);
449 }
450
451 void ArgumentCoder<MachSendRight>::encode(Encoder& encoder, MachSendRight&& sendRight)
452 {
453     encoder << Attachment(sendRight.leakSendRight(), MACH_MSG_TYPE_MOVE_SEND);
454 }
455
456 bool ArgumentCoder<MachSendRight>::decode(Decoder& decoder, MachSendRight& sendRight)
457 {
458     Attachment attachment;
459     if (!decoder.decode(attachment))
460         return false;
461
462     if (attachment.disposition() != MACH_MSG_TYPE_MOVE_SEND)
463         return false;
464
465     sendRight = MachSendRight::adopt(attachment.port());
466     return true;
467 }
468
469 void ArgumentCoder<KeypressCommand>::encode(Encoder& encoder, const KeypressCommand& keypressCommand)
470 {
471     encoder << keypressCommand.commandName << keypressCommand.text;
472 }
473     
474 bool ArgumentCoder<KeypressCommand>::decode(Decoder& decoder, KeypressCommand& keypressCommand)
475 {
476     if (!decoder.decode(keypressCommand.commandName))
477         return false;
478
479     if (!decoder.decode(keypressCommand.text))
480         return false;
481
482     return true;
483 }
484
485 #if ENABLE(CONTENT_FILTERING)
486 void ArgumentCoder<ContentFilterUnblockHandler>::encode(Encoder& encoder, const ContentFilterUnblockHandler& contentFilterUnblockHandler)
487 {
488     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
489     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
490     [archiver setRequiresSecureCoding:YES];
491     contentFilterUnblockHandler.encode(archiver.get());
492     [archiver finishEncoding];
493     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
494 }
495
496 bool ArgumentCoder<ContentFilterUnblockHandler>::decode(Decoder& decoder, ContentFilterUnblockHandler& contentFilterUnblockHandler)
497 {
498     RetainPtr<CFDataRef> data;
499     if (!IPC::decode(decoder, data))
500         return false;
501
502     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
503     [unarchiver setRequiresSecureCoding:YES];
504     if (!ContentFilterUnblockHandler::decode(unarchiver.get(), contentFilterUnblockHandler))
505         return false;
506
507     [unarchiver finishDecoding];
508     return true;
509 }
510 #endif
511
512 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
513
514 static NSString *deviceContextKey()
515 {
516     static NSString * const key = @"deviceContext";
517     return key;
518 }
519
520 void ArgumentCoder<MediaPlaybackTargetContext>::encodePlatformData(Encoder& encoder, const MediaPlaybackTargetContext& target)
521 {
522     RetainPtr<NSMutableData> data = adoptNS([[NSMutableData alloc] init]);
523     RetainPtr<NSKeyedArchiver> archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
524     [archiver setRequiresSecureCoding:YES];
525
526     if ([getAVOutputContextClass() conformsToProtocol:@protocol(NSSecureCoding)])
527         [archiver encodeObject:target.avOutputContext() forKey:deviceContextKey()];
528
529     [archiver finishEncoding];
530     IPC::encode(encoder, reinterpret_cast<CFDataRef>(data.get()));
531
532 }
533
534 bool ArgumentCoder<MediaPlaybackTargetContext>::decodePlatformData(Decoder& decoder, MediaPlaybackTargetContext& target)
535 {
536     if (![getAVOutputContextClass() conformsToProtocol:@protocol(NSSecureCoding)])
537         return false;
538
539     RetainPtr<CFDataRef> data;
540     if (!IPC::decode(decoder, data))
541         return false;
542
543     RetainPtr<NSKeyedUnarchiver> unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)data.get()]);
544     [unarchiver setRequiresSecureCoding:YES];
545
546     AVOutputContext *context = nil;
547     @try {
548         context = [unarchiver decodeObjectOfClass:getAVOutputContextClass() forKey:deviceContextKey()];
549     } @catch (NSException *exception) {
550         LOG_ERROR("The target picker being decoded is not an AVOutputContext.");
551         return false;
552     }
553
554     target = MediaPlaybackTargetContext(context);
555     
556     [unarchiver finishDecoding];
557     return true;
558 }
559 #endif
560
561 } // namespace IPC