2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
28 #import "NetscapePluginHostManager.h"
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>
38 #import <wtf/Assertions.h>
39 #import <wtf/RetainPtr.h>
40 #import <wtf/StdLibExtras.h>
43 #import "WebKitPluginAgent.h"
44 #import "WebKitPluginHost.h"
48 using namespace WebCore;
52 NetscapePluginHostManager& NetscapePluginHostManager::shared()
54 DEFINE_STATIC_LOCAL(NetscapePluginHostManager, pluginHostManager, ());
56 return pluginHostManager;
59 static NSString * const pluginHostAppName = @"WebKitPluginHost.app";
61 NetscapePluginHostManager::NetscapePluginHostManager()
62 : m_pluginVendorPort(MACH_PORT_NULL)
66 NetscapePluginHostManager::~NetscapePluginHostManager()
70 NetscapePluginHostProxy* NetscapePluginHostManager::hostForPlugin(const WTF::String& pluginPath, cpu_type_t pluginArchitecture, const String& bundleIdentifier)
72 pair<PluginHostMap::iterator, bool> result = m_pluginHosts.add(pluginPath, 0);
74 // The package was already in the map, just return it.
76 return result.first->second;
78 mach_port_t clientPort;
79 if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort) != KERN_SUCCESS) {
80 m_pluginHosts.remove(result.first);
84 mach_port_t pluginHostPort;
85 ProcessSerialNumber pluginHostPSN;
86 if (!spawnPluginHost(pluginPath, pluginArchitecture, clientPort, pluginHostPort, pluginHostPSN)) {
87 mach_port_destroy(mach_task_self(), clientPort);
88 m_pluginHosts.remove(result.first);
92 // Since Flash NPObjects add methods dynamically, we don't want to cache when a property/method doesn't exist
93 // on an object because it could be added later.
94 bool shouldCacheMissingPropertiesAndMethods = bundleIdentifier != "com.macromedia.Flash Player.plugin";
96 NetscapePluginHostProxy* hostProxy = new NetscapePluginHostProxy(clientPort, pluginHostPort, pluginHostPSN, shouldCacheMissingPropertiesAndMethods);
98 result.first->second = hostProxy;
103 bool NetscapePluginHostManager::spawnPluginHost(const String& pluginPath, cpu_type_t pluginArchitecture, mach_port_t clientPort, mach_port_t& pluginHostPort, ProcessSerialNumber& pluginHostPSN)
105 if (m_pluginVendorPort == MACH_PORT_NULL) {
106 if (!initializeVendorPort())
110 mach_port_t renderServerPort = WKInitializeRenderServer();
111 if (renderServerPort == MACH_PORT_NULL)
114 NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName];
115 NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath];
117 RetainPtr<CFStringRef> localization(AdoptCF, WKCopyCFLocalizationPreferredName(NULL));
119 NSDictionary *launchProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
120 pluginHostAppExecutablePath, @"pluginHostPath",
121 [NSNumber numberWithInt:pluginArchitecture], @"cpuType",
122 localization.get(), @"localization",
125 NSData *data = [NSPropertyListSerialization dataFromPropertyList:launchProperties format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
128 [launchProperties release];
130 kern_return_t kr = _WKPASpawnPluginHost(m_pluginVendorPort, reinterpret_cast<uint8_t*>(const_cast<void*>([data bytes])), [data length], &pluginHostPort);
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())
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);
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);
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 [[(NSString*)pluginPath lastPathComponent] stringByDeletingPathExtension], [[NSProcessInfo processInfo] processName]];
153 NSDictionary *hostProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
154 visibleName, @"visibleName",
155 (NSString *)pluginPath, @"bundlePath",
158 data = [NSPropertyListSerialization dataFromPropertyList:hostProperties format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil];
161 [hostProperties release];
163 ProcessSerialNumber psn;
164 GetCurrentProcess(&psn);
166 kr = _WKPHCheckInWithPluginHost(pluginHostPort, (uint8_t*)[data bytes], [data length], clientPort, psn.highLongOfPSN, psn.lowLongOfPSN, renderServerPort,
167 &pluginHostPSN.highLongOfPSN, &pluginHostPSN.lowLongOfPSN);
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);
179 bool NetscapePluginHostManager::initializeVendorPort()
181 ASSERT(m_pluginVendorPort == MACH_PORT_NULL);
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");
190 NSData *appNameData = [[[NSProcessInfo processInfo] processName] dataUsingEncoding:NSUTF8StringEncoding];
192 // Tell the plug-in agent that we exist.
193 if (_WKPACheckInApplication(pluginAgentPort, (uint8_t*)[appNameData bytes], [appNameData length], &m_pluginVendorPort) != KERN_SUCCESS)
196 // FIXME: Should we add a notification for when the vendor port dies?
201 void NetscapePluginHostManager::pluginHostDied(NetscapePluginHostProxy* pluginHost)
203 PluginHostMap::iterator end = m_pluginHosts.end();
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);
214 PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginHostManager::instantiatePlugin(const String& pluginPath, cpu_type_t pluginArchitecture, const String& bundleIdentifier, WebHostedNetscapePluginView *pluginView, NSString *mimeType, NSArray *attributeKeys, NSArray *attributeValues, NSString *userAgent, NSURL *sourceURL, bool fullFrame, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled)
216 NetscapePluginHostProxy* hostProxy = hostForPlugin(pluginPath, pluginArchitecture, bundleIdentifier);
220 RetainPtr<NSMutableDictionary> properties(AdoptNS, [[NSMutableDictionary alloc] init]);
223 [properties.get() setObject:mimeType forKey:@"mimeType"];
225 ASSERT_ARG(userAgent, userAgent);
226 [properties.get() setObject:userAgent forKey:@"userAgent"];
228 ASSERT_ARG(attributeKeys, attributeKeys);
229 [properties.get() setObject:attributeKeys forKey:@"attributeKeys"];
231 ASSERT_ARG(attributeValues, attributeValues);
232 [properties.get() setObject:attributeValues forKey:@"attributeValues"];
235 [properties.get() setObject:[sourceURL absoluteString] forKey:@"sourceURL"];
237 [properties.get() setObject:[NSNumber numberWithBool:fullFrame] forKey:@"fullFrame"];
238 [properties.get() setObject:[NSNumber numberWithBool:isPrivateBrowsingEnabled] forKey:@"privateBrowsingEnabled"];
239 [properties.get() setObject:[NSNumber numberWithBool:isAcceleratedCompositingEnabled] forKey:@"acceleratedCompositingEnabled"];
241 NSData *data = [NSPropertyListSerialization dataFromPropertyList:properties.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil];
244 RefPtr<NetscapePluginInstanceProxy> instance = NetscapePluginInstanceProxy::create(hostProxy, pluginView, fullFrame);
245 uint32_t requestID = instance->nextRequestID();
246 kern_return_t kr = _WKPHInstantiatePlugin(hostProxy->port(), requestID, (uint8_t*)[data bytes], [data length], instance->pluginID());
247 if (kr == MACH_SEND_INVALID_DEST) {
248 // Invalidate the instance.
249 instance->invalidate();
251 // The plug-in host must have died, but we haven't received the death notification yet.
252 pluginHostDied(hostProxy);
254 // Try to spawn it again.
255 hostProxy = hostForPlugin(pluginPath, pluginArchitecture, bundleIdentifier);
257 // Create a new instance.
258 instance = NetscapePluginInstanceProxy::create(hostProxy, pluginView, fullFrame);
259 requestID = instance->nextRequestID();
260 kr = _WKPHInstantiatePlugin(hostProxy->port(), requestID, (uint8_t*)[data bytes], [data length], instance->pluginID());
263 auto_ptr<NetscapePluginInstanceProxy::InstantiatePluginReply> reply = instance->waitForReply<NetscapePluginInstanceProxy::InstantiatePluginReply>(requestID);
264 if (!reply.get() || reply->m_resultCode != KERN_SUCCESS) {
269 instance->setRenderContextID(reply->m_renderContextID);
270 instance->setRendererType(reply->m_rendererType);
272 return instance.release();
275 void NetscapePluginHostManager::createPropertyListFile(const String& pluginPath, cpu_type_t pluginArchitecture)
277 NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName];
278 NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath];
279 NSString *bundlePath = pluginPath;
282 posix_spawnattr_t attr;
283 posix_spawnattr_init(&attr);
285 // Set the architecture.
287 int cpuTypes[] = { pluginArchitecture };
288 posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &ocount);
290 // Spawn the plug-in host and tell it to call the registration function.
291 const char* args[] = { [pluginHostAppExecutablePath fileSystemRepresentation], "-createPluginMIMETypesPreferences", [bundlePath fileSystemRepresentation], 0 };
293 int result = posix_spawn(&pid, args[0], 0, &attr, const_cast<char* const*>(args), 0);
294 posix_spawnattr_destroy(&attr);
296 if (!result && pid > 0) {
297 // Wait for the process to finish.
298 while (waitpid(pid, 0, 0) == -1) { }
302 void NetscapePluginHostManager::didCreateWindow()
304 // See if any of our hosts are in full-screen mode.
305 PluginHostMap::iterator end = m_pluginHosts.end();
306 for (PluginHostMap::iterator it = m_pluginHosts.begin(); it != end; ++it) {
307 NetscapePluginHostProxy* hostProxy = it->second;
309 if (!hostProxy->isMenuBarVisible()) {
310 // Make ourselves the front process.
311 ProcessSerialNumber psn;
312 GetCurrentProcess(&psn);
313 SetFrontProcess(&psn);
319 } // namespace WebKit
321 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)