Home | History | Annotate | Download | only in Hosted
      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 
     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_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 
    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)
    322