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