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 45 extern "C" { 46 #import "WebKitPluginAgent.h" 47 #import "WebKitPluginHost.h" 48 } 49 50 using namespace std; 51 52 namespace WebKit { 53 54 NetscapePluginHostManager& NetscapePluginHostManager::shared() 55 { 56 DEFINE_STATIC_LOCAL(NetscapePluginHostManager, pluginHostManager, ()); 57 58 return pluginHostManager; 59 } 60 61 static const NSString *pluginHostAppName = @"WebKitPluginHost.app"; 62 63 NetscapePluginHostManager::NetscapePluginHostManager() 64 : m_pluginVendorPort(MACH_PORT_NULL) 65 { 66 } 67 68 NetscapePluginHostManager::~NetscapePluginHostManager() 69 { 70 } 71 72 NetscapePluginHostProxy* 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 106 bool 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 183 bool 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 205 void 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 218 PassRefPtr<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 280 void 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 307 void 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