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 "WebLocalizableStringsInternal.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; 48using namespace WebCore; 49 50namespace WebKit { 51 52NetscapePluginHostManager& NetscapePluginHostManager::shared() 53{ 54 DEFINE_STATIC_LOCAL(NetscapePluginHostManager, pluginHostManager, ()); 55 56 return pluginHostManager; 57} 58 59static NSString * const pluginHostAppName = @"WebKitPluginHost.app"; 60 61NetscapePluginHostManager::NetscapePluginHostManager() 62 : m_pluginVendorPort(MACH_PORT_NULL) 63{ 64} 65 66NetscapePluginHostManager::~NetscapePluginHostManager() 67{ 68} 69 70NetscapePluginHostProxy* 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 103bool 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_INTERNAL("%@ (%@ 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 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(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 275void 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 302void 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) 322