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)
     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