OSDN Git Service

Merge Webkit at r70949: Initial merge by git.
[android-x86/external-webkit.git] / WebKit / mac / Plugins / Hosted / NetscapePluginHostManager.mm
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) && ENABLE(NETSCAPE_PLUGIN_API)
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
42 extern "C" {
43 #import "WebKitPluginAgent.h"
44 #import "WebKitPluginHost.h"
45 }
46
47 using namespace std;
48 using namespace WebCore;
49
50 namespace WebKit {
51
52 NetscapePluginHostManager& NetscapePluginHostManager::shared()
53 {
54     DEFINE_STATIC_LOCAL(NetscapePluginHostManager, pluginHostManager, ());
55     
56     return pluginHostManager;
57 }
58
59 static NSString * const pluginHostAppName = @"WebKitPluginHost.app";
60
61 NetscapePluginHostManager::NetscapePluginHostManager()
62     : m_pluginVendorPort(MACH_PORT_NULL)
63 {
64 }
65  
66 NetscapePluginHostManager::~NetscapePluginHostManager()
67 {
68 }
69
70 NetscapePluginHostProxy* NetscapePluginHostManager::hostForPlugin(const WTF::String& pluginPath, cpu_type_t pluginArchitecture, const String& bundleIdentifier)
71 {
72     pair<PluginHostMap::iterator, bool> result = m_pluginHosts.add(pluginPath, 0);
73     
74     // The package was already in the map, just return it.
75     if (!result.second)
76         return result.first->second;
77         
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);
81         return 0;
82     }
83     
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);
89         return 0;
90     }
91     
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";
95     
96     NetscapePluginHostProxy* hostProxy = new NetscapePluginHostProxy(clientPort, pluginHostPort, pluginHostPSN, shouldCacheMissingPropertiesAndMethods);
97     
98     result.first->second = hostProxy;
99     
100     return hostProxy;
101 }
102
103 bool NetscapePluginHostManager::spawnPluginHost(const String& pluginPath, cpu_type_t pluginArchitecture, 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:pluginArchitecture], @"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                              [[(NSString*)pluginPath lastPathComponent] stringByDeletingPathExtension], [[NSProcessInfo processInfo] processName]];
152     
153     NSDictionary *hostProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
154                                     visibleName, @"visibleName",
155                                     (NSString *)pluginPath, @"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
179 bool 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
201 void 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
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)
215 {
216     NetscapePluginHostProxy* hostProxy = hostForPlugin(pluginPath, pluginArchitecture, bundleIdentifier);
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     [properties.get() setObject:[NSNumber numberWithBool:isPrivateBrowsingEnabled] forKey:@"privateBrowsingEnabled"];
239     [properties.get() setObject:[NSNumber numberWithBool:isAcceleratedCompositingEnabled] forKey:@"acceleratedCompositingEnabled"];
240
241     NSData *data = [NSPropertyListSerialization dataFromPropertyList:properties.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil];
242     ASSERT(data);
243     
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();
250         
251         // The plug-in host must have died, but we haven't received the death notification yet.
252         pluginHostDied(hostProxy);
253
254         // Try to spawn it again.
255         hostProxy = hostForPlugin(pluginPath, pluginArchitecture, bundleIdentifier);
256         
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());
261     }
262
263     auto_ptr<NetscapePluginInstanceProxy::InstantiatePluginReply> reply = instance->waitForReply<NetscapePluginInstanceProxy::InstantiatePluginReply>(requestID);
264     if (!reply.get() || reply->m_resultCode != KERN_SUCCESS) {
265         instance->cleanup();
266         return 0;
267     }
268     
269     instance->setRenderContextID(reply->m_renderContextID);
270     instance->setRendererType(reply->m_rendererType);
271
272     return instance.release();
273 }
274
275 void NetscapePluginHostManager::createPropertyListFile(const String& pluginPath, cpu_type_t pluginArchitecture)
276 {   
277     NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName];
278     NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath];
279     NSString *bundlePath = pluginPath;
280
281     pid_t pid;
282     posix_spawnattr_t attr;
283     posix_spawnattr_init(&attr);
284     
285     // Set the architecture.
286     size_t ocount = 0;
287     int cpuTypes[] = { pluginArchitecture };
288     posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &ocount);
289     
290     // Spawn the plug-in host and tell it to call the registration function.
291     const char* args[] = { [pluginHostAppExecutablePath fileSystemRepresentation], "-createPluginMIMETypesPreferences", [bundlePath fileSystemRepresentation], 0 };
292     
293     int result = posix_spawn(&pid, args[0], 0, &attr, const_cast<char* const*>(args), 0);
294     posix_spawnattr_destroy(&attr);
295     
296     if (!result && pid > 0) {
297         // Wait for the process to finish.
298         while (waitpid(pid, 0,  0) == -1) { }
299     }
300 }
301     
302 void NetscapePluginHostManager::didCreateWindow()
303 {
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;
308         
309         if (!hostProxy->isMenuBarVisible()) {
310             // Make ourselves the front process.
311             ProcessSerialNumber psn;
312             GetCurrentProcess(&psn);
313             SetFrontProcess(&psn);
314             return;
315         }
316     }
317 }
318
319 } // namespace WebKit
320
321 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)