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