• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2008 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#if USE(PLUGIN_HOST_PROCESS)
27
28#import "NetscapePluginHostManager.h"
29
30#import "NetscapePluginHostProxy.h"
31#import "NetscapePluginInstanceProxy.h"
32#import "WebLocalizableStrings.h"
33#import "WebKitSystemInterface.h"
34#import "WebNetscapePluginPackage.h"
35#import <mach/mach_port.h>
36#import <servers/bootstrap.h>
37#import <spawn.h>
38#import <wtf/Assertions.h>
39#import <wtf/RetainPtr.h>
40#import <wtf/StdLibExtras.h>
41
42extern "C" {
43#import "WebKitPluginAgent.h"
44#import "WebKitPluginHost.h"
45}
46
47using namespace std;
48
49namespace WebKit {
50
51NetscapePluginHostManager& NetscapePluginHostManager::shared()
52{
53    DEFINE_STATIC_LOCAL(NetscapePluginHostManager, pluginHostManager, ());
54
55    return pluginHostManager;
56}
57
58static const NSString *pluginHostAppName = @"WebKitPluginHost.app";
59
60NetscapePluginHostManager::NetscapePluginHostManager()
61    : m_pluginVendorPort(MACH_PORT_NULL)
62{
63}
64
65NetscapePluginHostManager::~NetscapePluginHostManager()
66{
67}
68
69NetscapePluginHostProxy* NetscapePluginHostManager::hostForPackage(WebNetscapePluginPackage *package)
70{
71    pair<PluginHostMap::iterator, bool> result = m_pluginHosts.add(package, 0);
72
73    // The package was already in the map, just return it.
74    if (!result.second)
75        return result.first->second;
76
77    mach_port_t clientPort;
78    if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort) != KERN_SUCCESS) {
79        m_pluginHosts.remove(result.first);
80        return 0;
81    }
82
83    mach_port_t pluginHostPort;
84    ProcessSerialNumber pluginHostPSN;
85    if (!spawnPluginHost(package, clientPort, pluginHostPort, pluginHostPSN)) {
86        mach_port_destroy(mach_task_self(), clientPort);
87        m_pluginHosts.remove(result.first);
88        return 0;
89    }
90
91    // Since Flash NPObjects add methods dynamically, we don't want to cache when a property/method doesn't exist
92    // on an object because it could be added later.
93    bool shouldCacheMissingPropertiesAndMethods = ![[[package bundle] bundleIdentifier] isEqualToString:@"com.macromedia.Flash Player.plugin"];
94
95    NetscapePluginHostProxy* hostProxy = new NetscapePluginHostProxy(clientPort, pluginHostPort, pluginHostPSN, shouldCacheMissingPropertiesAndMethods);
96
97    CFRetain(package);
98    result.first->second = hostProxy;
99
100    return hostProxy;
101}
102
103bool NetscapePluginHostManager::spawnPluginHost(WebNetscapePluginPackage *package, mach_port_t clientPort, mach_port_t& pluginHostPort, ProcessSerialNumber& pluginHostPSN)
104{
105    if (m_pluginVendorPort == MACH_PORT_NULL) {
106        if (!initializeVendorPort())
107            return false;
108    }
109
110    mach_port_t renderServerPort = WKInitializeRenderServer();
111    if (renderServerPort == MACH_PORT_NULL)
112        return false;
113
114    NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName];
115    NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath];
116
117    RetainPtr<CFStringRef> localization(AdoptCF, WKCopyCFLocalizationPreferredName(NULL));
118
119    NSDictionary *launchProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
120                                      pluginHostAppExecutablePath, @"pluginHostPath",
121                                      [NSNumber numberWithInt:[package pluginHostArchitecture]], @"cpuType",
122                                      localization.get(), @"localization",
123                                      nil];
124
125    NSData *data = [NSPropertyListSerialization dataFromPropertyList:launchProperties format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
126    ASSERT(data);
127
128    [launchProperties release];
129
130    kern_return_t kr = _WKPASpawnPluginHost(m_pluginVendorPort, reinterpret_cast<uint8_t*>(const_cast<void*>([data bytes])), [data length], &pluginHostPort);
131
132    if (kr == MACH_SEND_INVALID_DEST) {
133        // The plug-in vendor port has gone away for some reason. Try to reinitialize it.
134        m_pluginVendorPort = MACH_PORT_NULL;
135        if (!initializeVendorPort())
136            return false;
137
138        // And spawn the plug-in host again.
139        kr = _WKPASpawnPluginHost(m_pluginVendorPort, reinterpret_cast<uint8_t*>(const_cast<void*>([data bytes])), [data length], &pluginHostPort);
140    }
141
142    if (kr != KERN_SUCCESS) {
143        // FIXME: Check for invalid dest and try to re-spawn the plug-in agent.
144        LOG_ERROR("Failed to spawn plug-in host, error %x", kr);
145        return false;
146    }
147
148    NSString *visibleName = [NSString stringWithFormat:UI_STRING("%@ (%@ Internet plug-in)",
149                                                                 "visible name of the plug-in host process. The first argument is the plug-in name "
150                                                                 "and the second argument is the application name."),
151                             [[package filename] stringByDeletingPathExtension], [[NSProcessInfo processInfo] processName]];
152
153    NSDictionary *hostProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
154                                    visibleName, @"visibleName",
155                                    [package path], @"bundlePath",
156                                    nil];
157
158    data = [NSPropertyListSerialization dataFromPropertyList:hostProperties format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil];
159    ASSERT(data);
160
161    [hostProperties release];
162
163    ProcessSerialNumber psn;
164    GetCurrentProcess(&psn);
165
166    kr = _WKPHCheckInWithPluginHost(pluginHostPort, (uint8_t*)[data bytes], [data length], clientPort, psn.highLongOfPSN, psn.lowLongOfPSN, renderServerPort,
167                                    &pluginHostPSN.highLongOfPSN, &pluginHostPSN.lowLongOfPSN);
168
169    if (kr != KERN_SUCCESS) {
170        mach_port_deallocate(mach_task_self(), pluginHostPort);
171        LOG_ERROR("Failed to check in with plug-in host, error %x", kr);
172
173        return false;
174    }
175
176    return true;
177}
178
179bool NetscapePluginHostManager::initializeVendorPort()
180{
181    ASSERT(m_pluginVendorPort == MACH_PORT_NULL);
182
183    // Get the plug-in agent port.
184    mach_port_t pluginAgentPort;
185    if (bootstrap_look_up(bootstrap_port, "com.apple.WebKit.PluginAgent", &pluginAgentPort) != KERN_SUCCESS) {
186        LOG_ERROR("Failed to look up the plug-in agent port");
187        return false;
188    }
189
190    NSData *appNameData = [[[NSProcessInfo processInfo] processName] dataUsingEncoding:NSUTF8StringEncoding];
191
192    // Tell the plug-in agent that we exist.
193    if (_WKPACheckInApplication(pluginAgentPort, (uint8_t*)[appNameData bytes], [appNameData length], &m_pluginVendorPort) != KERN_SUCCESS)
194        return false;
195
196    // FIXME: Should we add a notification for when the vendor port dies?
197
198    return true;
199}
200
201void NetscapePluginHostManager::pluginHostDied(NetscapePluginHostProxy* pluginHost)
202{
203    PluginHostMap::iterator end = m_pluginHosts.end();
204
205    // This has O(n) complexity but the number of active plug-in hosts is very small so it shouldn't matter.
206    for (PluginHostMap::iterator it = m_pluginHosts.begin(); it != end; ++it) {
207        if (it->second == pluginHost) {
208            m_pluginHosts.remove(it);
209            return;
210        }
211    }
212}
213
214PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginHostManager::instantiatePlugin(WebNetscapePluginPackage *pluginPackage, WebHostedNetscapePluginView *pluginView, NSString *mimeType, NSArray *attributeKeys, NSArray *attributeValues, NSString *userAgent, NSURL *sourceURL, bool fullFrame)
215{
216    NetscapePluginHostProxy* hostProxy = hostForPackage(pluginPackage);
217    if (!hostProxy)
218        return 0;
219
220    RetainPtr<NSMutableDictionary> properties(AdoptNS, [[NSMutableDictionary alloc] init]);
221
222    if (mimeType)
223        [properties.get() setObject:mimeType forKey:@"mimeType"];
224
225    ASSERT_ARG(userAgent, userAgent);
226    [properties.get() setObject:userAgent forKey:@"userAgent"];
227
228    ASSERT_ARG(attributeKeys, attributeKeys);
229    [properties.get() setObject:attributeKeys forKey:@"attributeKeys"];
230
231    ASSERT_ARG(attributeValues, attributeValues);
232    [properties.get() setObject:attributeValues forKey:@"attributeValues"];
233
234    if (sourceURL)
235        [properties.get() setObject:[sourceURL absoluteString] forKey:@"sourceURL"];
236
237    [properties.get() setObject:[NSNumber numberWithBool:fullFrame] forKey:@"fullFrame"];
238
239    NSData *data = [NSPropertyListSerialization dataFromPropertyList:properties.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil];
240    ASSERT(data);
241
242    RefPtr<NetscapePluginInstanceProxy> instance = NetscapePluginInstanceProxy::create(hostProxy, pluginView, fullFrame);
243    uint32_t requestID = instance->nextRequestID();
244    kern_return_t kr = _WKPHInstantiatePlugin(hostProxy->port(), requestID, (uint8_t*)[data bytes], [data length], instance->pluginID());
245    if (kr == MACH_SEND_INVALID_DEST) {
246        // Invalidate the instance.
247        instance->invalidate();
248
249        // The plug-in host must have died, but we haven't received the death notification yet.
250        pluginHostDied(hostProxy);
251
252        // Try to spawn it again.
253        hostProxy = hostForPackage(pluginPackage);
254
255        // Create a new instance.
256        instance = NetscapePluginInstanceProxy::create(hostProxy, pluginView, fullFrame);
257        requestID = instance->nextRequestID();
258        kr = _WKPHInstantiatePlugin(hostProxy->port(), requestID, (uint8_t*)[data bytes], [data length], instance->pluginID());
259    }
260
261    auto_ptr<NetscapePluginInstanceProxy::InstantiatePluginReply> reply = instance->waitForReply<NetscapePluginInstanceProxy::InstantiatePluginReply>(requestID);
262    if (!reply.get() || reply->m_resultCode != KERN_SUCCESS) {
263        instance->cleanup();
264        return 0;
265    }
266
267    instance->setRenderContextID(reply->m_renderContextID);
268    instance->setUseSoftwareRenderer(reply->m_useSoftwareRenderer);
269
270    return instance.release();
271}
272
273void NetscapePluginHostManager::createPropertyListFile(WebNetscapePluginPackage *package)
274{
275    NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName];
276    NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath];
277    NSString *bundlePath = [package path];
278
279    pid_t pid;
280    posix_spawnattr_t attr;
281    posix_spawnattr_init(&attr);
282
283    // Set the architecture.
284    size_t ocount = 0;
285    int cpuTypes[1] = { [package pluginHostArchitecture] };
286    posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &ocount);
287
288    // Spawn the plug-in host and tell it to call the registration function.
289    const char* args[] = { [pluginHostAppExecutablePath fileSystemRepresentation], "-createPluginMIMETypesPreferences", [bundlePath fileSystemRepresentation], 0 };
290
291    int result = posix_spawn(&pid, args[0], 0, &attr, const_cast<char* const*>(args), 0);
292    posix_spawnattr_destroy(&attr);
293
294    if (!result && pid > 0) {
295        // Wait for the process to finish.
296        while (waitpid(pid, 0,  0) == -1) { }
297    }
298}
299
300void NetscapePluginHostManager::didCreateWindow()
301{
302    // See if any of our hosts are in full-screen mode.
303    PluginHostMap::iterator end = m_pluginHosts.end();
304    for (PluginHostMap::iterator it = m_pluginHosts.begin(); it != end; ++it) {
305        NetscapePluginHostProxy* hostProxy = it->second;
306
307        if (!hostProxy->isMenuBarVisible()) {
308            // Make ourselves the front process.
309            ProcessSerialNumber psn;
310            GetCurrentProcess(&psn);
311            SetFrontProcess(&psn);
312            return;
313        }
314    }
315}
316
317} // namespace WebKit
318
319#endif // USE(PLUGIN_HOST_PROCESS)
320