1/*
2 * Copyright (C) 2010 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 "InjectedBundle.h"
28
29#import "APIData.h"
30#import "ObjCObjectGraph.h"
31#import "WKBundleAPICast.h"
32#import "WKBundleInitialize.h"
33#import "WKWebProcessBundleParameters.h"
34#import "WKWebProcessPlugInInternal.h"
35#import "WebProcessCreationParameters.h"
36#import <Foundation/NSBundle.h>
37#import <stdio.h>
38#import <wtf/RetainPtr.h>
39#import <wtf/text/CString.h>
40#import <wtf/text/WTFString.h>
41
42using namespace WebCore;
43
44@interface NSBundle (WKAppDetails)
45- (CFBundleRef)_cfBundle;
46@end
47
48namespace WebKit {
49
50bool InjectedBundle::initialize(const WebProcessCreationParameters& parameters, API::Object* initializationUserData)
51{
52    if (m_sandboxExtension) {
53        if (!m_sandboxExtension->consumePermanently()) {
54            WTFLogAlways("InjectedBundle::load failed - Could not consume bundle sandbox extension for [%s].\n", m_path.utf8().data());
55            return false;
56        }
57
58        m_sandboxExtension = 0;
59    }
60
61    RetainPtr<CFStringRef> injectedBundlePathStr = m_path.createCFString();
62    if (!injectedBundlePathStr) {
63        WTFLogAlways("InjectedBundle::load failed - Could not create the path string.\n");
64        return false;
65    }
66
67    RetainPtr<CFURLRef> bundleURL = adoptCF(CFURLCreateWithFileSystemPath(0, injectedBundlePathStr.get(), kCFURLPOSIXPathStyle, false));
68    if (!bundleURL) {
69        WTFLogAlways("InjectedBundle::load failed - Could not create the url from the path string.\n");
70        return false;
71    }
72
73    m_platformBundle = [[NSBundle alloc] initWithURL:(NSURL *)bundleURL.get()];
74    if (!m_platformBundle) {
75        WTFLogAlways("InjectedBundle::load failed - Could not create the bundle.\n");
76        return false;
77    }
78
79    if (![m_platformBundle load]) {
80        WTFLogAlways("InjectedBundle::load failed - Could not load the executable from the bundle.\n");
81        return false;
82    }
83
84    // First check to see if the bundle has a WKBundleInitialize function.
85    WKBundleInitializeFunctionPtr initializeFunction = reinterpret_cast<WKBundleInitializeFunctionPtr>(CFBundleGetFunctionPointerForName([m_platformBundle _cfBundle], CFSTR("WKBundleInitialize")));
86    if (initializeFunction) {
87        initializeFunction(toAPI(this), toAPI(initializationUserData));
88        return true;
89    }
90
91#if WK_API_ENABLED
92    if (parameters.bundleParameterData) {
93        auto bundleParameterData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(parameters.bundleParameterData->bytes())) length:parameters.bundleParameterData->size() freeWhenDone:NO]);
94
95        auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:bundleParameterData.get()]);
96        [unarchiver setRequiresSecureCoding:YES];
97
98        NSDictionary *dictionary = nil;
99        @try {
100            dictionary = [unarchiver.get() decodeObjectOfClass:[NSObject class] forKey:@"parameters"];
101            ASSERT([dictionary isKindOfClass:[NSDictionary class]]);
102        } @catch (NSException *exception) {
103            LOG_ERROR("Failed to decode bundle parameters: %@", exception);
104        }
105
106        m_bundleParameters = adoptNS([[WKWebProcessBundleParameters alloc] initWithDictionary:dictionary]);
107    }
108
109    // Otherwise, look to see if the bundle has a principal class
110    Class principalClass = [m_platformBundle principalClass];
111    if (!principalClass) {
112        WTFLogAlways("InjectedBundle::load failed - No initialize function or principal class found in the bundle executable.\n");
113        return false;
114    }
115
116    if (![principalClass conformsToProtocol:@protocol(WKWebProcessPlugIn)]) {
117        WTFLogAlways("InjectedBundle::load failed - Principal class does not conform to the WKWebProcessPlugIn protocol.\n");
118        return false;
119    }
120
121    id <WKWebProcessPlugIn> instance = (id <WKWebProcessPlugIn>)[[principalClass alloc] init];
122    if (!instance) {
123        WTFLogAlways("InjectedBundle::load failed - Could not initialize an instance of the principal class.\n");
124        return false;
125    }
126
127    WKWebProcessPlugInController* plugInController = WebKit::wrapper(*this);
128    [plugInController _setPrincipalClassInstance:instance];
129
130    if ([instance respondsToSelector:@selector(webProcessPlugIn:initializeWithObject:)]) {
131        RetainPtr<id> objCInitializationUserData;
132        if (initializationUserData && initializationUserData->type() == API::Object::Type::ObjCObjectGraph)
133            objCInitializationUserData = static_cast<ObjCObjectGraph*>(initializationUserData)->rootObject();
134        [instance webProcessPlugIn:plugInController initializeWithObject:objCInitializationUserData.get()];
135    }
136
137    return true;
138#else
139    return false;
140#endif
141}
142
143#if WK_API_ENABLED
144WKWebProcessBundleParameters *InjectedBundle::bundleParameters()
145{
146    // We must not return nil even if no parameters are currently set, in order to allow the client
147    // to use KVO.
148    if (!m_bundleParameters)
149        m_bundleParameters = adoptNS([[WKWebProcessBundleParameters alloc] initWithDictionary:@{ }]);
150
151    return m_bundleParameters.get();
152}
153#endif
154
155void InjectedBundle::setBundleParameter(const String& key, const IPC::DataReference& value)
156{
157#if WK_API_ENABLED
158    auto bundleParameterData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<void*>(static_cast<const void*>(value.data())) length:value.size() freeWhenDone:NO]);
159
160    auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingWithData:bundleParameterData.get()]);
161    [unarchiver setRequiresSecureCoding:YES];
162
163    id parameter = nil;
164    @try {
165        parameter = [unarchiver decodeObjectOfClass:[NSObject class] forKey:@"parameter"];
166    } @catch (NSException *exception) {
167        LOG_ERROR("Failed to decode bundle parameter: %@", exception);
168    }
169
170    if (!m_bundleParameters && parameter)
171        m_bundleParameters = adoptNS([[WKWebProcessBundleParameters alloc] initWithDictionary:[NSDictionary dictionary]]);
172
173    [m_bundleParameters setParameter:parameter forKey:key];
174#endif
175}
176
177} // namespace WebKit
178