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