1/*
2 * Copyright (C) 2012 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 "ObjCObjectGraphCoders.h"
28
29#import "ArgumentCodersMac.h"
30#import "WKTypeRefWrapper.h"
31
32// For UIProcess side encoding/decoding
33#import "WKAPICast.h"
34#import "WKBrowsingContextControllerInternal.h"
35#import "WebContextUserMessageCoders.h"
36#import "WebPageProxy.h"
37#import "WebProcessProxy.h"
38
39// For WebProcess side encoding/decoding
40#import "InjectedBundleUserMessageCoders.h"
41#import "WKBundleAPICast.h"
42#import "WKWebProcessPlugInBrowserContextControllerInternal.h"
43#import "WKWebProcessPlugInInternal.h"
44#import "WebPage.h"
45#import "WebProcess.h"
46
47namespace WebKit {
48
49enum WebKitNSType {
50    NullType,
51    NSDictionaryType,
52    NSArrayType,
53    NSStringType,
54    NSNumberType,
55    NSDateType,
56    NSDataType,
57#if WK_API_ENABLED
58    WKBrowsingContextControllerType,
59    WKTypeRefWrapperType,
60#endif
61    UnknownType,
62};
63
64static WebKitNSType typeFromObject(id object)
65{
66    ASSERT(object);
67
68    if ([object isKindOfClass:[NSDictionary class]])
69        return NSDictionaryType;
70    if ([object isKindOfClass:[NSString class]])
71        return NSStringType;
72    if ([object isKindOfClass:[NSArray class]])
73        return NSArrayType;
74    if ([object isKindOfClass:[NSNumber class]])
75        return NSNumberType;
76    if ([object isKindOfClass:[NSDate class]])
77        return NSDateType;
78    if ([object isKindOfClass:[NSData class]])
79        return NSDataType;
80#if WK_API_ENABLED
81    if ([object isKindOfClass:[WKBrowsingContextController class]] || [object isKindOfClass:[WKWebProcessPlugInBrowserContextController class]])
82        return WKBrowsingContextControllerType;
83    if ([object isKindOfClass:[WKTypeRefWrapper class]])
84        return WKTypeRefWrapperType;
85#endif
86
87    return UnknownType;
88}
89
90template<typename Owner>
91class ObjCObjectGraphEncoder {
92public:
93    bool baseEncode(IPC::ArgumentEncoder& encoder, const Owner& coder, WebKitNSType& type) const
94    {
95        if (!m_root) {
96            encoder << static_cast<uint32_t>(NullType);
97            return true;
98        }
99
100        type = typeFromObject(m_root);
101        if (type == UnknownType) {
102            [NSException raise:NSInvalidArgumentException format:@"Can not encode objects of class type '%@'", static_cast<NSString *>(NSStringFromClass([m_root class]))];
103        }
104
105        encoder << static_cast<uint32_t>(type);
106
107        switch (type) {
108        case NSStringType: {
109            IPC::encode(encoder, static_cast<NSString *>(m_root));
110            return true;
111        }
112        case NSArrayType: {
113            NSArray *array = static_cast<NSArray *>(m_root);
114
115            NSUInteger size = [array count];
116            encoder << static_cast<uint64_t>(size);
117
118            for (NSUInteger i = 0; i < size; ++i)
119                encoder << Owner(coder, [array objectAtIndex:i]);
120            return true;
121        }
122        case NSDictionaryType: {
123            NSDictionary* dictionary = static_cast<NSDictionary *>(m_root);
124
125            NSUInteger size = [dictionary count];
126            encoder << static_cast<uint64_t>(size);
127
128            NSArray *keys = [dictionary allKeys];
129            NSArray *values = [dictionary allValues];
130            for (NSUInteger i = 0; i < size; ++i) {
131                encoder << Owner(coder, [keys objectAtIndex:i]);
132                encoder << Owner(coder, [values objectAtIndex:i]);
133            }
134
135            return true;
136        }
137        case NSNumberType: {
138            IPC::encode(encoder, static_cast<NSNumber *>(m_root));
139            return true;
140        }
141        case NSDateType: {
142            IPC::encode(encoder, static_cast<NSDate *>(m_root));
143            return true;
144        }
145        case NSDataType: {
146            IPC::encode(encoder, static_cast<NSData *>(m_root));
147            return true;
148        }
149        default:
150            break;
151        }
152
153        return false;
154    }
155
156protected:
157    ObjCObjectGraphEncoder(id root)
158        : m_root(root)
159    {
160    }
161
162    id m_root;
163};
164
165template<typename Owner>
166class ObjCObjectGraphDecoder {
167public:
168    static bool baseDecode(IPC::ArgumentDecoder& decoder, Owner& coder, WebKitNSType& type)
169    {
170        uint32_t typeAsUInt32;
171        if (!decoder.decode(typeAsUInt32))
172            return false;
173
174        type = static_cast<WebKitNSType>(typeAsUInt32);
175
176        switch (type) {
177        case NSStringType: {
178            RetainPtr<NSString> string;
179            if (!IPC::decode(decoder, string))
180                return false;
181            coder.m_root = string;
182            break;
183        }
184        case NSArrayType: {
185            uint64_t size;
186            if (!decoder.decode(size))
187                return false;
188
189            RetainPtr<NSMutableArray> array = adoptNS([[NSMutableArray alloc] initWithCapacity:size]);
190            for (uint64_t i = 0; i < size; ++i) {
191                RetainPtr<id> value;
192                Owner messageCoder(coder, value);
193                if (!decoder.decode(messageCoder))
194                    return false;
195
196                [array addObject:value.get()];
197            }
198
199            coder.m_root = array;
200            break;
201        }
202        case NSDictionaryType: {
203            uint64_t size;
204            if (!decoder.decode(size))
205                return false;
206
207            RetainPtr<NSMutableDictionary> dictionary = adoptNS([[NSMutableDictionary alloc] initWithCapacity:size]);
208            for (uint64_t i = 0; i < size; ++i) {
209                // Try to decode the key name.
210                RetainPtr<id> key;
211                Owner keyMessageCoder(coder, key);
212                if (!decoder.decode(keyMessageCoder))
213                    return false;
214
215                RetainPtr<id> value;
216                Owner valueMessageCoder(coder, value);
217                if (!decoder.decode(valueMessageCoder))
218                    return false;
219
220                [dictionary setObject:value.get() forKey:key.get()];
221            }
222
223            coder.m_root = dictionary;
224            break;
225        }
226        case NSNumberType: {
227            RetainPtr<NSNumber> number;
228            if (!IPC::decode(decoder, number))
229                return false;
230            coder.m_root = number;
231            break;
232        }
233        case NSDateType: {
234            RetainPtr<NSDate> date;
235            if (!IPC::decode(decoder, date))
236                return false;
237            coder.m_root = date;
238            break;
239        }
240        case NSDataType: {
241            RetainPtr<NSData> data;
242            if (!IPC::decode(decoder, data))
243                return false;
244            coder.m_root = data;
245            break;
246        }
247        default:
248            break;
249        }
250
251        return true;
252    }
253
254protected:
255    ObjCObjectGraphDecoder(RetainPtr<id>& root)
256        : m_root(root)
257    {
258    }
259
260    RetainPtr<id>& m_root;
261};
262
263
264// WebContext Additions
265
266class WebContextObjCObjectGraphEncoderImpl : public ObjCObjectGraphEncoder<WebContextObjCObjectGraphEncoderImpl> {
267public:
268    typedef ObjCObjectGraphEncoder<WebContextObjCObjectGraphEncoderImpl> Base;
269
270    explicit WebContextObjCObjectGraphEncoderImpl(id root, WebProcessProxy& process)
271        : Base(root)
272        , m_process(process)
273    {
274    }
275
276    WebContextObjCObjectGraphEncoderImpl(const WebContextObjCObjectGraphEncoderImpl& userMessageEncoder, id root)
277        : Base(root)
278        , m_process(userMessageEncoder.m_process)
279    {
280    }
281
282    void encode(IPC::ArgumentEncoder& encoder) const
283    {
284        WebKitNSType type = NullType;
285        if (baseEncode(encoder, *this, type))
286            return;
287
288        switch (type) {
289#if WK_API_ENABLED
290        case WKBrowsingContextControllerType: {
291            WKBrowsingContextController *browsingContextController = static_cast<WKBrowsingContextController *>(m_root);
292
293            encoder << toImpl(browsingContextController._pageRef)->pageID();
294            break;
295        }
296        case WKTypeRefWrapperType: {
297            WKTypeRefWrapper *wrapper = static_cast<WKTypeRefWrapper *>(m_root);
298            encoder << WebContextUserMessageEncoder(toImpl(wrapper.object), m_process);
299            break;
300        }
301#endif
302        default:
303            ASSERT_NOT_REACHED();
304            break;
305        }
306    }
307
308private:
309    WebProcessProxy& m_process;
310};
311
312
313class WebContextObjCObjectGraphDecoderImpl : public ObjCObjectGraphDecoder<WebContextObjCObjectGraphDecoderImpl> {
314public:
315    typedef ObjCObjectGraphDecoder<WebContextObjCObjectGraphDecoderImpl> Base;
316
317    WebContextObjCObjectGraphDecoderImpl(RetainPtr<id>& root, WebProcessProxy& process)
318        : Base(root)
319        , m_process(process)
320    {
321    }
322
323    WebContextObjCObjectGraphDecoderImpl(WebContextObjCObjectGraphDecoderImpl& userMessageDecoder, RetainPtr<id>& root)
324        : Base(root)
325        , m_process(userMessageDecoder.m_process)
326    {
327    }
328
329    static bool decode(IPC::ArgumentDecoder& decoder, WebContextObjCObjectGraphDecoderImpl& coder)
330    {
331        WebKitNSType type = NullType;
332        if (!Base::baseDecode(decoder, coder, type))
333            return false;
334
335        if (coder.m_root)
336            return true;
337
338        if (type == NullType || type == UnknownType) {
339            coder.m_root = [NSNull null];
340            return true;
341        }
342
343        switch (type) {
344#if WK_API_ENABLED
345        case WKBrowsingContextControllerType: {
346            uint64_t pageID;
347            if (!decoder.decode(pageID))
348                return false;
349
350            WebPageProxy* webPage = coder.m_process.webPage(pageID);
351            if (!webPage)
352                coder.m_root = [NSNull null];
353            else
354                coder.m_root = [WKBrowsingContextController _browsingContextControllerForPageRef:toAPI(webPage)];
355            break;
356        }
357        case WKTypeRefWrapperType: {
358            RefPtr<API::Object> object;
359            WebContextUserMessageDecoder objectDecoder(object, coder.m_process);
360            if (!decoder.decode(objectDecoder))
361                return false;
362            coder.m_root = adoptNS([[WKTypeRefWrapper alloc] initWithObject:toAPI(object.get())]);
363            break;
364        }
365#endif
366        default:
367            return false;
368        }
369
370        return true;
371    }
372
373private:
374    WebProcessProxy& m_process;
375};
376
377
378// InjectedBundle Additions
379
380class InjectedBundleObjCObjectGraphEncoderImpl : public ObjCObjectGraphEncoder<InjectedBundleObjCObjectGraphEncoderImpl> {
381public:
382    typedef ObjCObjectGraphEncoder<InjectedBundleObjCObjectGraphEncoderImpl> Base;
383
384    explicit InjectedBundleObjCObjectGraphEncoderImpl(id root, WebProcess& process)
385        : Base(root)
386        , m_process(process)
387    {
388    }
389
390    explicit InjectedBundleObjCObjectGraphEncoderImpl(const InjectedBundleObjCObjectGraphEncoderImpl& encoder, id root)
391        : Base(root)
392        , m_process(encoder.m_process)
393    {
394    }
395
396    void encode(IPC::ArgumentEncoder& encoder) const
397    {
398        WebKitNSType type = NullType;
399        if (baseEncode(encoder, *this, type))
400            return;
401
402        switch (type) {
403#if WK_API_ENABLED
404        case WKBrowsingContextControllerType: {
405            WKWebProcessPlugInBrowserContextController *browserContextController = static_cast<WKWebProcessPlugInBrowserContextController *>(m_root);
406
407            encoder << toImpl(browserContextController._bundlePageRef)->pageID();
408            break;
409        }
410        case WKTypeRefWrapperType: {
411            WKTypeRefWrapper *wrapper = static_cast<WKTypeRefWrapper *>(m_root);
412            encoder << InjectedBundleUserMessageEncoder(toImpl(wrapper.object));
413            break;
414        }
415#endif
416        default:
417            ASSERT_NOT_REACHED();
418            break;
419        }
420    }
421
422private:
423    WebProcess& m_process;
424};
425
426class InjectedBundleObjCObjectGraphDecoderImpl : public ObjCObjectGraphDecoder<InjectedBundleObjCObjectGraphDecoderImpl> {
427public:
428    typedef ObjCObjectGraphDecoder<InjectedBundleObjCObjectGraphDecoderImpl> Base;
429
430    InjectedBundleObjCObjectGraphDecoderImpl(RetainPtr<id>& root, WebProcess& process)
431        : Base(root)
432        , m_process(process)
433    {
434    }
435
436    InjectedBundleObjCObjectGraphDecoderImpl(InjectedBundleObjCObjectGraphDecoderImpl& userMessageDecoder, RetainPtr<id>& root)
437        : Base(root)
438        , m_process(userMessageDecoder.m_process)
439    {
440    }
441
442    static bool decode(IPC::ArgumentDecoder& decoder, InjectedBundleObjCObjectGraphDecoderImpl& coder)
443    {
444        WebKitNSType type = NullType;
445        if (!Base::baseDecode(decoder, coder, type))
446            return false;
447
448        if (coder.m_root)
449            return true;
450
451        if (type == NullType || type == UnknownType) {
452            coder.m_root = [NSNull null];
453            return true;
454        }
455
456        switch (type) {
457#if WK_API_ENABLED
458        case WKBrowsingContextControllerType: {
459            uint64_t pageID;
460            if (!decoder.decode(pageID))
461                return false;
462
463            WebPage* webPage = coder.m_process.webPage(pageID);
464            if (!webPage)
465                coder.m_root = [NSNull null];
466            else
467                coder.m_root = wrapper(*webPage);
468            break;
469        }
470        case WKTypeRefWrapperType: {
471            RefPtr<API::Object> object;
472            InjectedBundleUserMessageDecoder objectDecoder(object);
473            if (!decoder.decode(objectDecoder))
474                return false;
475            coder.m_root = adoptNS([[WKTypeRefWrapper alloc] initWithObject:toAPI(object.get())]);
476            break;
477        }
478#endif
479        default:
480            return false;
481        }
482
483        return true;
484    }
485
486private:
487    WebProcess& m_process;
488};
489
490
491// Adaptors
492
493WebContextObjCObjectGraphEncoder::WebContextObjCObjectGraphEncoder(ObjCObjectGraph* objectGraph, WebProcessProxy& process)
494    : m_objectGraph(objectGraph)
495    , m_process(process)
496{
497}
498
499void WebContextObjCObjectGraphEncoder::encode(IPC::ArgumentEncoder& encoder) const
500{
501    encoder << WebContextObjCObjectGraphEncoderImpl(m_objectGraph->rootObject(), m_process);
502}
503
504WebContextObjCObjectGraphDecoder::WebContextObjCObjectGraphDecoder(RefPtr<ObjCObjectGraph>& objectGraph, WebProcessProxy& process)
505    : m_objectGraph(objectGraph)
506    , m_process(process)
507{
508}
509
510bool WebContextObjCObjectGraphDecoder::decode(IPC::ArgumentDecoder& decoder, WebContextObjCObjectGraphDecoder& coder)
511{
512    RetainPtr<id> root;
513    WebContextObjCObjectGraphDecoderImpl coderImpl(root, coder.m_process);
514    if (!decoder.decode(coderImpl))
515        return false;
516
517    coder.m_objectGraph = ObjCObjectGraph::create(root.get());
518    return true;
519}
520
521InjectedBundleObjCObjectGraphEncoder::InjectedBundleObjCObjectGraphEncoder(ObjCObjectGraph* objectGraph, WebProcess& process)
522    : m_objectGraph(objectGraph)
523    , m_process(process)
524{
525}
526
527void InjectedBundleObjCObjectGraphEncoder::encode(IPC::ArgumentEncoder& encoder) const
528{
529    encoder << InjectedBundleObjCObjectGraphEncoderImpl(m_objectGraph->rootObject(), m_process);
530}
531
532InjectedBundleObjCObjectGraphDecoder::InjectedBundleObjCObjectGraphDecoder(RefPtr<ObjCObjectGraph>& objectGraph, WebProcess& process)
533    : m_objectGraph(objectGraph)
534    , m_process(process)
535{
536}
537
538bool InjectedBundleObjCObjectGraphDecoder::decode(IPC::ArgumentDecoder& decoder, InjectedBundleObjCObjectGraphDecoder& coder)
539{
540    RetainPtr<id> root;
541    InjectedBundleObjCObjectGraphDecoderImpl coderImpl(root, coder.m_process);
542    if (!decoder.decode(coderImpl))
543        return false;
544
545    coder.m_objectGraph = ObjCObjectGraph::create(root.get());
546    return true;
547}
548
549} // namespace WebKit
550