6094fc05a73c0557734c6147ebf0fa5c894e44b8
[WebKit-https.git] / Source / WebKit2 / Shared / API / Cocoa / WKRemoteObjectCoder.mm
1 /*
2  * Copyright (C) 2013 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 "WKRemoteObjectCoder.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIArray.h"
32 #import "APIData.h"
33 #import "APIDictionary.h"
34 #import "APINumber.h"
35 #import "APIString.h"
36 #import "_WKRemoteObjectInterfaceInternal.h"
37 #import <objc/runtime.h>
38 #import <wtf/RetainPtr.h>
39 #import <wtf/TemporaryChange.h>
40 #import <wtf/text/CString.h>
41
42 static const char* const classNameKey = "$class";
43 static const char* const objectStreamKey = "$objectStream";
44 static const char* const stringKey = "$string";
45
46 static NSString * const selectorKey = @"selector";
47 static NSString * const typeStringKey = @"typeString";
48
49 static RefPtr<API::Dictionary> createEncodedObject(WKRemoteObjectEncoder *, id);
50
51 @interface NSMethodSignature (Details)
52 - (NSString *)_typeString;
53 @end
54
55 @interface NSCoder (Details)
56 - (void)validateClassSupportsSecureCoding:(Class)objectClass;
57 @end
58
59 @implementation WKRemoteObjectEncoder {
60     RefPtr<API::Dictionary> _rootDictionary;
61     API::Array* _objectStream;
62
63     API::Dictionary* _currentDictionary;
64 }
65
66 - (id)init
67 {
68     if (!(self = [super init]))
69         return nil;
70
71     _rootDictionary = API::Dictionary::create();
72     _currentDictionary = _rootDictionary.get();
73
74     return self;
75 }
76
77 #if !ASSERT_DISABLED
78 - (void)dealloc
79 {
80     ASSERT(_currentDictionary == _rootDictionary);
81
82     [super dealloc];
83 }
84 #endif
85
86 - (API::Dictionary*)rootObjectDictionary
87 {
88     return _rootDictionary.get();
89 }
90
91 static void ensureObjectStream(WKRemoteObjectEncoder *encoder)
92 {
93     if (encoder->_objectStream)
94         return;
95
96     Ref<API::Array> objectStream = API::Array::create();
97     encoder->_objectStream = objectStream.ptr();
98
99     encoder->_rootDictionary->set(objectStreamKey, WTF::move(objectStream));
100 }
101
102 static void encodeToObjectStream(WKRemoteObjectEncoder *encoder, id value)
103 {
104     ensureObjectStream(encoder);
105
106     size_t position = encoder->_objectStream->size();
107     encoder->_objectStream->elements().append(nullptr);
108
109     RefPtr<API::Dictionary> encodedObject = createEncodedObject(encoder, value);
110     ASSERT(!encoder->_objectStream->elements()[position]);
111     encoder->_objectStream->elements()[position] = encodedObject.release();
112 }
113
114 static void encodeInvocation(WKRemoteObjectEncoder *encoder, NSInvocation *invocation)
115 {
116     NSMethodSignature *methodSignature = invocation.methodSignature;
117     [encoder encodeObject:methodSignature._typeString forKey:typeStringKey];
118     [encoder encodeObject:NSStringFromSelector(invocation.selector) forKey:selectorKey];
119
120     NSUInteger argumentCount = methodSignature.numberOfArguments;
121
122     // The invocation should always have have self and _cmd arguments.
123     ASSERT(argumentCount >= 2);
124
125     // We ignore self and _cmd.
126     for (NSUInteger i = 2; i < argumentCount; ++i) {
127         const char* type = [methodSignature getArgumentTypeAtIndex:i];
128
129         switch (*type) {
130         // double
131         case 'd': {
132             double value;
133             [invocation getArgument:&value atIndex:i];
134
135             encodeToObjectStream(encoder, @(value));
136             break;
137         }
138
139         // float
140         case 'f': {
141             float value;
142             [invocation getArgument:&value atIndex:i];
143
144             encodeToObjectStream(encoder, @(value));
145             break;
146         }
147
148         // int
149         case 'i': {
150             int value;
151             [invocation getArgument:&value atIndex:i];
152
153             encodeToObjectStream(encoder, @(value));
154             break;
155         }
156
157         // unsigned
158         case 'I': {
159             unsigned value;
160             [invocation getArgument:&value atIndex:i];
161
162             encodeToObjectStream(encoder, @(value));
163             break;
164         }
165
166         // char
167         case 'c': {
168             char value;
169             [invocation getArgument:&value atIndex:i];
170
171             encodeToObjectStream(encoder, @(value));
172             break;
173         }
174
175         // bool
176         case 'B': {
177             BOOL value;
178             [invocation getArgument:&value atIndex:i];
179
180             encodeToObjectStream(encoder, @(value));
181             break;
182         }
183
184         // long
185         case 'q': {
186             long value;
187             [invocation getArgument:&value atIndex:i];
188
189             encodeToObjectStream(encoder, @(value));
190             break;
191         }
192
193         // unsigned long
194         case 'Q': {
195             unsigned long value;
196             [invocation getArgument:&value atIndex:i];
197
198             encodeToObjectStream(encoder, @(value));
199             break;
200         }
201
202         // Objective-C object
203         case '@': {
204             id value;
205             [invocation getArgument:&value atIndex:i];
206
207             encodeToObjectStream(encoder, value);
208             break;
209         }
210
211         default:
212             [NSException raise:NSInvalidArgumentException format:@"Unsupported invocation argument type '%s'", type];
213         }
214     }
215 }
216
217 static void encodeString(WKRemoteObjectEncoder *encoder, NSString *string)
218 {
219     encoder->_currentDictionary->set(stringKey, API::String::create(string));
220 }
221
222 static void encodeObject(WKRemoteObjectEncoder *encoder, id object)
223 {
224     ASSERT(object);
225
226     if (![object conformsToProtocol:@protocol(NSSecureCoding)] && ![object isKindOfClass:[NSInvocation class]])
227         [NSException raise:NSInvalidArgumentException format:@"%@ does not conform to NSSecureCoding", object];
228
229     if (class_isMetaClass(object_getClass(object)))
230         [NSException raise:NSInvalidArgumentException format:@"Class objects may not be encoded"];
231
232     Class objectClass = [object classForCoder];
233     if (!objectClass)
234         [NSException raise:NSInvalidArgumentException format:@"-classForCoder returned nil for %@", object];
235
236     encoder->_currentDictionary->set(classNameKey, API::String::create(class_getName(objectClass)));
237
238     if ([object isKindOfClass:[NSInvocation class]]) {
239         // We have to special case NSInvocation since we don't want to encode the target.
240         encodeInvocation(encoder, object);
241         return;
242     }
243
244     if (objectClass == [NSString class] || objectClass == [NSMutableString class]) {
245         encodeString(encoder, object);
246         return;
247     }
248
249     [object encodeWithCoder:encoder];
250 }
251
252 static RefPtr<API::Dictionary> createEncodedObject(WKRemoteObjectEncoder *encoder, id object)
253 {
254     if (!object)
255         return nil;
256
257     Ref<API::Dictionary> dictionary = API::Dictionary::create();
258     TemporaryChange<API::Dictionary*> dictionaryChange(encoder->_currentDictionary, dictionary.ptr());
259
260     encodeObject(encoder, object);
261
262     return WTF::move(dictionary);
263 }
264
265 - (void)encodeValueOfObjCType:(const char *)type at:(const void *)address
266 {
267     switch (*type) {
268     // int
269     case 'i':
270         encodeToObjectStream(self, @(*static_cast<const int*>(address)));
271         break;
272
273     // Objective-C object.
274     case '@':
275         encodeToObjectStream(self, *static_cast<const id*>(address));
276         break;
277
278     default:
279         [NSException raise:NSInvalidArgumentException format:@"Unsupported type '%s'", type];
280     }
281 }
282
283 - (BOOL)allowsKeyedCoding
284 {
285     return YES;
286 }
287
288 static NSString *escapeKey(NSString *key)
289 {
290     if (key.length && [key characterAtIndex:0] == '$')
291         return [@"$" stringByAppendingString:key];
292
293     return key;
294 }
295
296 - (void)encodeObject:(id)object forKey:(NSString *)key
297 {
298     _currentDictionary->set(escapeKey(key), createEncodedObject(self, object));
299 }
300
301 - (void)encodeBytes:(const uint8_t *)bytes length:(NSUInteger)length forKey:(NSString *)key
302 {
303     _currentDictionary->set(escapeKey(key), API::Data::create(bytes, length));
304 }
305
306 - (void)encodeBool:(BOOL)value forKey:(NSString *)key
307 {
308     _currentDictionary->set(escapeKey(key), API::Boolean::create(value));
309 }
310
311 - (void)encodeInt:(int)value forKey:(NSString *)key
312 {
313     _currentDictionary->set(escapeKey(key), API::UInt64::create(value));
314 }
315
316 - (void)encodeInt32:(int32_t)value forKey:(NSString *)key
317 {
318     _currentDictionary->set(escapeKey(key), API::UInt64::create(value));
319 }
320
321 - (void)encodeInt64:(int64_t)value forKey:(NSString *)key
322 {
323     _currentDictionary->set(escapeKey(key), API::UInt64::create(value));
324 }
325
326 - (void)encodeInteger:(NSInteger)intv forKey:(NSString *)key
327 {
328     return [self encodeInt64:intv forKey:key];
329 }
330
331 - (void)encodeFloat:(float)value forKey:(NSString *)key
332 {
333     _currentDictionary->set(escapeKey(key), API::Double::create(value));
334 }
335
336 - (void)encodeDouble:(double)value forKey:(NSString *)key
337 {
338     _currentDictionary->set(escapeKey(key), API::Double::create(value));
339 }
340
341 - (BOOL)requiresSecureCoding
342 {
343     return YES;
344 }
345
346 @end
347
348 @implementation WKRemoteObjectDecoder {
349     RetainPtr<_WKRemoteObjectInterface> _interface;
350
351     const API::Dictionary* _rootDictionary;
352     const API::Dictionary* _currentDictionary;
353
354     const API::Array* _objectStream;
355     size_t _objectStreamPosition;
356
357     NSSet *_allowedClasses;
358 }
359
360 - (id)initWithInterface:(_WKRemoteObjectInterface *)interface rootObjectDictionary:(const API::Dictionary*)rootObjectDictionary
361 {
362     if (!(self = [super init]))
363         return nil;
364
365     _interface = interface;
366
367     _rootDictionary = rootObjectDictionary;
368     _currentDictionary = _rootDictionary;
369
370     _objectStream = _rootDictionary->get<API::Array>(objectStreamKey);
371
372     return self;
373 }
374
375 - (void)decodeValueOfObjCType:(const char *)type at:(void *)data
376 {
377     switch (*type) {
378     // int
379     case 'i':
380         *static_cast<int*>(data) = [decodeObjectFromObjectStream(self, [NSSet setWithObject:[NSNumber class]]) intValue];
381         break;
382
383     default:
384         [NSException raise:NSInvalidUnarchiveOperationException format:@"Unsupported type '%s'", type];
385     }
386 }
387
388 - (BOOL)allowsKeyedCoding
389 {
390     return YES;
391 }
392
393 - (BOOL)containsValueForKey:(NSString *)key
394 {
395     return _currentDictionary->map().contains(escapeKey(key));
396 }
397
398 - (id)decodeObjectForKey:(NSString *)key
399 {
400     return [self decodeObjectOfClasses:nil forKey:key];
401 }
402
403 static id decodeObject(WKRemoteObjectDecoder *, const API::Dictionary*, NSSet *allowedClasses);
404
405 static id decodeObjectFromObjectStream(WKRemoteObjectDecoder *decoder, NSSet *allowedClasses)
406 {
407     if (!decoder->_objectStream)
408         return nil;
409
410     if (decoder->_objectStreamPosition == decoder->_objectStream->size())
411         return nil;
412
413     const API::Dictionary* dictionary = decoder->_objectStream->at<API::Dictionary>(decoder->_objectStreamPosition++);
414
415     return decodeObject(decoder, dictionary, allowedClasses);
416 }
417
418 static void checkIfClassIsAllowed(WKRemoteObjectDecoder *decoder, Class objectClass)
419 {
420     NSSet *allowedClasses = decoder->_allowedClasses;
421
422     // Check if the class or any of its superclasses are in the allowed classes set.
423     for (Class cls = objectClass; cls; cls = class_getSuperclass(cls)) {
424         if ([allowedClasses containsObject:cls])
425             return;
426     }
427
428     [NSException raise:NSInvalidUnarchiveOperationException format:@"Object of class \"%@\" is not allowed. Allowed classes are \"%@\"", objectClass, allowedClasses];
429 }
430
431 static void validateClass(WKRemoteObjectDecoder *decoder, Class objectClass)
432 {
433     ASSERT(objectClass);
434
435     checkIfClassIsAllowed(decoder, objectClass);
436
437     // NSInvocation doesn't support NSSecureCoding, but we allow it anyway.
438     if (objectClass == [NSInvocation class])
439         return;
440
441     [decoder validateClassSupportsSecureCoding:objectClass];
442 }
443
444 static void decodeInvocationArguments(WKRemoteObjectDecoder *decoder, NSInvocation *invocation, const Vector<RetainPtr<NSSet>>& allowedArgumentClasses)
445 {
446     NSMethodSignature *methodSignature = invocation.methodSignature;
447     NSUInteger argumentCount = methodSignature.numberOfArguments;
448
449     // The invocation should always have have self and _cmd arguments.
450     ASSERT(argumentCount >= 2);
451
452     // We ignore self and _cmd.
453     for (NSUInteger i = 2; i < argumentCount; ++i) {
454         const char* type = [methodSignature getArgumentTypeAtIndex:i];
455
456         switch (*type) {
457         // double
458         case 'd': {
459             double value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) doubleValue];
460             [invocation setArgument:&value atIndex:i];
461             break;
462         }
463
464         // float
465         case 'f': {
466             float value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) floatValue];
467             [invocation setArgument:&value atIndex:i];
468             break;
469         }
470
471         // int
472         case 'i': {
473             int value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) intValue];
474             [invocation setArgument:&value atIndex:i];
475             break;
476         }
477
478         // unsigned
479         case 'I': {
480             unsigned value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) unsignedIntValue];
481             [invocation setArgument:&value atIndex:i];
482             break;
483         }
484
485         // char
486         case 'c': {
487             char value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) charValue];
488             [invocation setArgument:&value atIndex:i];
489             break;
490         }
491
492         // bool
493         case 'B': {
494             bool value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) boolValue];
495             [invocation setArgument:&value atIndex:i];
496             break;
497         }
498
499         // long
500         case 'q': {
501             long value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) longValue];
502             [invocation setArgument:&value atIndex:i];
503             break;
504         }
505
506         // unsigned long
507         case 'Q': {
508             unsigned long value = [decodeObjectFromObjectStream(decoder, [NSSet setWithObject:[NSNumber class]]) unsignedLongValue];
509             [invocation setArgument:&value atIndex:i];
510             break;
511         }
512             
513         // Objective-C object
514         case '@': {
515             NSSet *allowedClasses = allowedArgumentClasses[i - 2].get();
516
517             id value = decodeObjectFromObjectStream(decoder, allowedClasses);
518             [invocation setArgument:&value atIndex:i];
519
520             // FIXME: Make sure the invocation doesn't outlive the value.
521             break;
522         }
523
524         default:
525             [NSException raise:NSInvalidArgumentException format:@"Unsupported invocation argument type '%s' for argument %zu", type, (unsigned long)i];
526         }
527     }
528 }
529
530 static NSInvocation *decodeInvocation(WKRemoteObjectDecoder *decoder)
531 {
532     NSString *selectorString = [decoder decodeObjectOfClass:[NSString class] forKey:selectorKey];
533     if (!selectorString)
534         [NSException raise:NSInvalidUnarchiveOperationException format:@"Invocation had no selector"];
535
536     SEL selector = NSSelectorFromString(selectorString);
537     ASSERT(selector);
538
539     NSMethodSignature *localMethodSignature = [decoder->_interface _methodSignatureForSelector:selector];
540     if (!localMethodSignature)
541         [NSException raise:NSInvalidUnarchiveOperationException format:@"Selector \"%@\" is not defined in the local interface", selectorString];
542
543     NSString *typeSignature = [decoder decodeObjectOfClass:[NSString class] forKey:typeStringKey];
544     if (!typeSignature)
545         [NSException raise:NSInvalidUnarchiveOperationException format:@"Invocation had no type signature"];
546
547     NSMethodSignature *remoteMethodSignature = [NSMethodSignature signatureWithObjCTypes:typeSignature.UTF8String];
548     if (![localMethodSignature isEqual:remoteMethodSignature])
549         [NSException raise:NSInvalidUnarchiveOperationException format:@"Local and remote method signatures are not equal for method \"%@\"", selectorString];
550
551     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:localMethodSignature];
552
553     const auto& allowedClasses = [decoder->_interface _allowedArgumentClassesForSelector:selector];
554     decodeInvocationArguments(decoder, invocation, allowedClasses);
555
556     [invocation setArgument:&selector atIndex:1];
557     return invocation;
558 }
559
560 static NSString *decodeString(WKRemoteObjectDecoder *decoder)
561 {
562     API::String* string = decoder->_currentDictionary->get<API::String>(stringKey);
563     if (!string)
564         [NSException raise:NSInvalidUnarchiveOperationException format:@"String missing"];
565
566     return string->string();
567 }
568
569 static id decodeObject(WKRemoteObjectDecoder *decoder)
570 {
571     API::String* classNameString = decoder->_currentDictionary->get<API::String>(classNameKey);
572     if (!classNameString)
573         [NSException raise:NSInvalidUnarchiveOperationException format:@"Class name missing"];
574
575     CString className = classNameString->string().utf8();
576
577     Class objectClass = objc_lookUpClass(className.data());
578     if (!objectClass)
579         [NSException raise:NSInvalidUnarchiveOperationException format:@"Class \"%s\" does not exist", className.data()];
580
581     validateClass(decoder, objectClass);
582
583     if (objectClass == [NSInvocation class])
584         return decodeInvocation(decoder);
585
586     if (objectClass == [NSString class])
587         return decodeString(decoder);
588
589     if (objectClass == [NSMutableString class])
590         return [NSMutableString stringWithString:decodeString(decoder)];
591
592     id result = [objectClass allocWithZone:decoder.zone];
593     if (!result)
594         [NSException raise:NSInvalidUnarchiveOperationException format:@"Class \"%s\" returned nil from +alloc while being decoded", className.data()];
595
596     result = [result initWithCoder:decoder];
597     if (!result)
598         [NSException raise:NSInvalidUnarchiveOperationException format:@"Object of class \"%s\" returned nil from -initWithCoder: while being decoded", className.data()];
599
600     result = [result awakeAfterUsingCoder:decoder];
601     if (!result)
602         [NSException raise:NSInvalidUnarchiveOperationException format:@"Object of class \"%s\" returned nil from -awakeAfterUsingCoder: while being decoded", className.data()];
603
604     return [result autorelease];
605 }
606
607 static id decodeObject(WKRemoteObjectDecoder *decoder, const API::Dictionary* dictionary, NSSet *allowedClasses)
608 {
609     if (!dictionary)
610         return nil;
611
612     TemporaryChange<const API::Dictionary*> dictionaryChange(decoder->_currentDictionary, dictionary);
613
614     // If no allowed classes were listed, just use the currently allowed classes.
615     if (!allowedClasses)
616         return decodeObject(decoder);
617
618     TemporaryChange<NSSet *> allowedClassesChange(decoder->_allowedClasses, allowedClasses);
619     return decodeObject(decoder);
620 }
621
622 - (BOOL)decodeBoolForKey:(NSString *)key
623 {
624     const API::Boolean* value = _currentDictionary->get<API::Boolean>(escapeKey(key));
625     if (!value)
626         return false;
627     return value->value();
628 }
629
630 - (int)decodeIntForKey:(NSString *)key
631 {
632     const API::UInt64* value = _currentDictionary->get<API::UInt64>(escapeKey(key));
633     if (!value)
634         return 0;
635     return static_cast<int>(value->value());
636 }
637
638 - (int32_t)decodeInt32ForKey:(NSString *)key
639 {
640     const API::UInt64* value = _currentDictionary->get<API::UInt64>(escapeKey(key));
641     if (!value)
642         return 0;
643     return static_cast<int32_t>(value->value());
644 }
645
646 - (int64_t)decodeInt64ForKey:(NSString *)key
647 {
648     const API::UInt64* value = _currentDictionary->get<API::UInt64>(escapeKey(key));
649     if (!value)
650         return 0;
651     return value->value();
652 }
653
654 - (NSInteger)decodeIntegerForKey:(NSString *)key
655 {
656     return [self decodeInt64ForKey:key];
657 }
658
659 - (float)decodeFloatForKey:(NSString *)key
660 {
661     const API::Double* value = _currentDictionary->get<API::Double>(escapeKey(key));
662     if (!value)
663         return 0;
664     return value->value();
665 }
666
667 - (double)decodeDoubleForKey:(NSString *)key
668 {
669     const API::Double* value = _currentDictionary->get<API::Double>(escapeKey(key));
670     if (!value)
671         return 0;
672     return value->value();
673 }
674
675 - (const uint8_t *)decodeBytesForKey:(NSString *)key returnedLength:(NSUInteger *)length
676 {
677     auto* data = _currentDictionary->get<API::Data>(escapeKey(key));
678     if (!data || !data->size()) {
679         *length = 0;
680         return nullptr;
681     }
682
683     *length = data->size();
684     return data->bytes();
685 }
686
687 - (BOOL)requiresSecureCoding
688 {
689     return YES;
690 }
691
692 - (id)decodeObjectOfClasses:(NSSet *)classes forKey:(NSString *)key
693 {
694     return decodeObject(self, _currentDictionary->get<API::Dictionary>(escapeKey(key)), classes);
695 }
696
697 - (NSSet *)allowedClasses
698 {
699     return _allowedClasses;
700 }
701
702 @end
703
704 #endif // WK_API_ENABLED