1/* 2 * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "PluginProcessProxy.h" 28 29#if ENABLE(PLUGIN_PROCESS) 30 31#import "DynamicLinkerEnvironmentExtractor.h" 32#import "EnvironmentVariables.h" 33#import "PluginProcessCreationParameters.h" 34#import "PluginProcessMessages.h" 35#import "SandboxUtilities.h" 36#import "WebKitSystemInterface.h" 37#import <WebCore/FileSystem.h> 38#import <WebCore/KURL.h> 39#import <WebCore/RuntimeApplicationChecks.h> 40#import <crt_externs.h> 41#import <mach-o/dyld.h> 42#import <spawn.h> 43#import <wtf/text/CString.h> 44 45#import <QuartzCore/CARemoteLayerServer.h> 46 47@interface WKPlaceholderModalWindow : NSWindow 48@end 49 50@implementation WKPlaceholderModalWindow 51 52// Prevent NSApp from calling requestUserAttention: when the window is shown 53// modally, even if the app is inactive. See 6823049. 54- (BOOL)_wantsUserAttention 55{ 56 return NO; 57} 58 59@end 60 61using namespace WebCore; 62 63namespace WebKit { 64 65bool PluginProcessProxy::pluginNeedsExecutableHeap(const PluginModuleInfo& pluginInfo) 66{ 67 static bool forceNonexecutableHeapForPlugins = [[NSUserDefaults standardUserDefaults] boolForKey:@"ForceNonexecutableHeapForPlugins"]; 68 if (forceNonexecutableHeapForPlugins) 69 return false; 70 71 if (pluginInfo.bundleIdentifier == "com.apple.QuickTime Plugin.plugin") 72 return false; 73 74 return true; 75} 76 77bool PluginProcessProxy::createPropertyListFile(const PluginModuleInfo& plugin) 78{ 79 NSBundle *webKit2Bundle = [NSBundle bundleWithIdentifier:@"com.apple.WebKit2"]; 80 NSString *frameworksPath = [[webKit2Bundle bundlePath] stringByDeletingLastPathComponent]; 81 const char* frameworkExecutablePath = [[webKit2Bundle executablePath] fileSystemRepresentation]; 82 83 NSString *processPath = [webKit2Bundle pathForAuxiliaryExecutable:@"PluginProcess.app"]; 84 NSString *processAppExecutablePath = [[NSBundle bundleWithPath:processPath] executablePath]; 85 86 CString pluginPathString = fileSystemRepresentation(plugin.path); 87 88 posix_spawnattr_t attr; 89 posix_spawnattr_init(&attr); 90 91 cpu_type_t cpuTypes[] = { plugin.pluginArchitecture }; 92 size_t outCount = 0; 93 posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &outCount); 94 95 EnvironmentVariables environmentVariables; 96 97 DynamicLinkerEnvironmentExtractor environmentExtractor([[NSBundle mainBundle] executablePath], _NSGetMachExecuteHeader()->cputype); 98 environmentExtractor.getExtractedEnvironmentVariables(environmentVariables); 99 100 // To make engineering builds work, if the path is outside of /System set up 101 // DYLD_FRAMEWORK_PATH to pick up other frameworks, but don't do it for the 102 // production configuration because it involves extra file system access. 103 if (![frameworksPath hasPrefix:@"/System/"]) 104 environmentVariables.appendValue("DYLD_FRAMEWORK_PATH", [frameworksPath fileSystemRepresentation], ':'); 105 106 const char* args[] = { [processAppExecutablePath fileSystemRepresentation], frameworkExecutablePath, "-type", "pluginprocess", "-createPluginMIMETypesPreferences", pluginPathString.data(), 0 }; 107 108 pid_t pid; 109 int result = posix_spawn(&pid, args[0], 0, &attr, const_cast<char* const*>(args), environmentVariables.environmentPointer()); 110 posix_spawnattr_destroy(&attr); 111 112 if (result) 113 return false; 114 int status; 115 if (waitpid(pid, &status, 0) < 0) 116 return false; 117 118 if (!WIFEXITED(status)) 119 return false; 120 121 if (WEXITSTATUS(status) != EXIT_SUCCESS) 122 return false; 123 124 return true; 125} 126 127#if HAVE(XPC) 128static bool shouldUseXPC() 129{ 130 if (id value = [[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseXPCServiceForWebProcess"]) 131 return [value boolValue]; 132 133#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 134 // FIXME: Temporary workaround for <rdar://problem/13236883> 135 if (applicationIsSafari()) 136 return false; 137 138 return true; 139#else 140 return false; 141#endif 142} 143#endif 144 145void PluginProcessProxy::platformGetLaunchOptions(ProcessLauncher::LaunchOptions& launchOptions, const PluginProcessAttributes& pluginProcessAttributes) 146{ 147 launchOptions.architecture = pluginProcessAttributes.moduleInfo.pluginArchitecture; 148 launchOptions.executableHeap = PluginProcessProxy::pluginNeedsExecutableHeap(pluginProcessAttributes.moduleInfo); 149 launchOptions.extraInitializationData.add("plugin-path", pluginProcessAttributes.moduleInfo.path); 150 151 if (pluginProcessAttributes.sandboxPolicy == PluginProcessSandboxPolicyUnsandboxed) { 152 if (!processIsSandboxed(getpid())) 153 launchOptions.extraInitializationData.add("disable-sandbox", "1"); 154 else 155 WTFLogAlways("Main process is sandboxed, ignoring plug-in sandbox policy"); 156 } 157 158#if HAVE(XPC) 159 launchOptions.useXPC = shouldUseXPC(); 160#endif 161} 162 163void PluginProcessProxy::platformInitializePluginProcess(PluginProcessCreationParameters& parameters) 164{ 165 // For now only Flash is known to behave with asynchronous plug-in initialization. 166 parameters.supportsAsynchronousPluginInitialization = m_pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin"; 167 168#if USE(ACCELERATED_COMPOSITING) && HAVE(HOSTED_CORE_ANIMATION) 169 mach_port_t renderServerPort = [[CARemoteLayerServer sharedServer] serverPort]; 170 if (renderServerPort != MACH_PORT_NULL) 171 parameters.acceleratedCompositingPort = CoreIPC::MachPort(renderServerPort, MACH_MSG_TYPE_COPY_SEND); 172#endif 173} 174 175bool PluginProcessProxy::getPluginProcessSerialNumber(ProcessSerialNumber& pluginProcessSerialNumber) 176{ 177 pid_t pluginProcessPID = processIdentifier(); 178#if COMPILER(CLANG) 179#pragma clang diagnostic push 180#pragma clang diagnostic ignored "-Wdeprecated-declarations" 181#endif 182 return GetProcessForPID(pluginProcessPID, &pluginProcessSerialNumber) == noErr; 183#if COMPILER(CLANG) 184#pragma clang diagnostic pop 185#endif 186} 187 188void PluginProcessProxy::makePluginProcessTheFrontProcess() 189{ 190 ProcessSerialNumber pluginProcessSerialNumber; 191 if (!getPluginProcessSerialNumber(pluginProcessSerialNumber)) 192 return; 193 194#if COMPILER(CLANG) 195#pragma clang diagnostic push 196#pragma clang diagnostic ignored "-Wdeprecated-declarations" 197#endif 198 SetFrontProcess(&pluginProcessSerialNumber); 199#if COMPILER(CLANG) 200#pragma clang diagnostic pop 201#endif 202} 203 204void PluginProcessProxy::makeUIProcessTheFrontProcess() 205{ 206 ProcessSerialNumber processSerialNumber; 207#if COMPILER(CLANG) 208#pragma clang diagnostic push 209#pragma clang diagnostic ignored "-Wdeprecated-declarations" 210#endif 211 GetCurrentProcess(&processSerialNumber); 212 SetFrontProcess(&processSerialNumber); 213#if COMPILER(CLANG) 214#pragma clang diagnostic pop 215#endif 216} 217 218void PluginProcessProxy::setFullscreenWindowIsShowing(bool fullscreenWindowIsShowing) 219{ 220 if (m_fullscreenWindowIsShowing == fullscreenWindowIsShowing) 221 return; 222 223 m_fullscreenWindowIsShowing = fullscreenWindowIsShowing; 224 if (m_fullscreenWindowIsShowing) 225 enterFullscreen(); 226 else 227 exitFullscreen(); 228} 229 230void PluginProcessProxy::enterFullscreen() 231{ 232 // Get the current presentation options. 233 m_preFullscreenAppPresentationOptions = [NSApp presentationOptions]; 234 235 // Figure out which presentation options to use. 236 unsigned presentationOptions = m_preFullscreenAppPresentationOptions & ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar); 237 presentationOptions |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar; 238 239 [NSApp setPresentationOptions:presentationOptions]; 240 makePluginProcessTheFrontProcess(); 241} 242 243void PluginProcessProxy::exitFullscreen() 244{ 245 // If the plug-in host is the current application then we should bring ourselves to the front when it exits full-screen mode. 246 ProcessSerialNumber frontProcessSerialNumber; 247#if COMPILER(CLANG) 248#pragma clang diagnostic push 249#pragma clang diagnostic ignored "-Wdeprecated-declarations" 250#endif 251 GetFrontProcess(&frontProcessSerialNumber); 252#if COMPILER(CLANG) 253#pragma clang diagnostic pop 254#endif 255 256 // The UI process must be the front process in order to change the presentation mode. 257 makeUIProcessTheFrontProcess(); 258 [NSApp setPresentationOptions:m_preFullscreenAppPresentationOptions]; 259 260 ProcessSerialNumber pluginProcessSerialNumber; 261 if (!getPluginProcessSerialNumber(pluginProcessSerialNumber)) 262 return; 263 264 // If the plug-in process was not the front process, switch back to the previous front process. 265 // (Otherwise we'll keep the UI process as the front process). 266 Boolean isPluginProcessFrontProcess; 267#if COMPILER(CLANG) 268#pragma clang diagnostic push 269#pragma clang diagnostic ignored "-Wdeprecated-declarations" 270#endif 271 SameProcess(&frontProcessSerialNumber, &pluginProcessSerialNumber, &isPluginProcessFrontProcess); 272#if COMPILER(CLANG) 273#pragma clang diagnostic pop 274#endif 275 if (!isPluginProcessFrontProcess) { 276#if COMPILER(CLANG) 277#pragma clang diagnostic push 278#pragma clang diagnostic ignored "-Wdeprecated-declarations" 279#endif 280 SetFrontProcess(&frontProcessSerialNumber); 281#if COMPILER(CLANG) 282#pragma clang diagnostic pop 283#endif 284 } 285} 286 287void PluginProcessProxy::setModalWindowIsShowing(bool modalWindowIsShowing) 288{ 289 if (modalWindowIsShowing == m_modalWindowIsShowing) 290 return; 291 292 m_modalWindowIsShowing = modalWindowIsShowing; 293 294 if (m_modalWindowIsShowing) 295 beginModal(); 296 else 297 endModal(); 298} 299 300void PluginProcessProxy::beginModal() 301{ 302 ASSERT(!m_placeholderWindow); 303 ASSERT(!m_activationObserver); 304 305 m_placeholderWindow = adoptNS([[WKPlaceholderModalWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]); 306 [m_placeholderWindow.get() setReleasedWhenClosed:NO]; 307 308 m_activationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillBecomeActiveNotification object:NSApp queue:nil 309 usingBlock:^(NSNotification *){ applicationDidBecomeActive(); }]; 310 311 // The call to -[NSApp runModalForWindow:] below will run a nested run loop, and if the plug-in process 312 // crashes the PluginProcessProxy object can be destroyed. Protect against this here. 313 RefPtr<PluginProcessProxy> protect(this); 314 315 [NSApp runModalForWindow:m_placeholderWindow.get()]; 316 317 [m_placeholderWindow.get() orderOut:nil]; 318 m_placeholderWindow = nullptr; 319} 320 321void PluginProcessProxy::endModal() 322{ 323 ASSERT(m_placeholderWindow); 324 ASSERT(m_activationObserver); 325 326 [[NSNotificationCenter defaultCenter] removeObserver:m_activationObserver.get()]; 327 m_activationObserver = nullptr; 328 329 [NSApp stopModal]; 330 331 makeUIProcessTheFrontProcess(); 332} 333 334void PluginProcessProxy::applicationDidBecomeActive() 335{ 336 makePluginProcessTheFrontProcess(); 337} 338 339void PluginProcessProxy::setProcessSuppressionEnabled(bool processSuppressionEnabled) 340{ 341 if (!isValid()) 342 return; 343 344 m_connection->send(Messages::PluginProcess::SetProcessSuppressionEnabled(processSuppressionEnabled), 0); 345} 346 347void PluginProcessProxy::openPluginPreferencePane() 348{ 349 if (!m_pluginProcessAttributes.moduleInfo.preferencePanePath) 350 return; 351 352 NSURL *preferenceURL = [NSURL fileURLWithPath:m_pluginProcessAttributes.moduleInfo.preferencePanePath]; 353 if (!preferenceURL) { 354 LOG_ERROR("Creating URL for preference pane path \"%@\" failed.", (NSString *)m_pluginProcessAttributes.moduleInfo.preferencePanePath); 355 return; 356 } 357 358 NSArray *preferenceURLs = [NSArray arrayWithObject:preferenceURL]; 359 360 LSLaunchURLSpec prefSpec; 361 prefSpec.appURL = 0; 362 prefSpec.itemURLs = reinterpret_cast<CFArrayRef>(preferenceURLs); 363 prefSpec.passThruParams = 0; 364 prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents; 365 prefSpec.asyncRefCon = 0; 366 367 OSStatus error = LSOpenFromURLSpec(&prefSpec, 0); 368 if (error != noErr) 369 LOG_ERROR("LSOpenFromURLSpec to open \"%@\" failed with error %d.", (NSString *)m_pluginProcessAttributes.moduleInfo.preferencePanePath, error); 370} 371 372static bool isFlashUpdater(const String& launchPath, const Vector<String>& arguments) 373{ 374 if (launchPath != "/Applications/Utilities/Adobe Flash Player Install Manager.app/Contents/MacOS/Adobe Flash Player Install Manager") 375 return false; 376 377 if (arguments.size() != 1) 378 return false; 379 380 if (arguments[0] != "-update") 381 return false; 382 383 return true; 384} 385 386static bool shouldLaunchProcess(const PluginProcessAttributes& pluginProcessAttributes, const String& launchPath, const Vector<String>& arguments) 387{ 388 if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin") 389 return isFlashUpdater(launchPath, arguments); 390 391 return false; 392} 393 394void PluginProcessProxy::launchProcess(const String& launchPath, const Vector<String>& arguments, bool& result) 395{ 396 if (!shouldLaunchProcess(m_pluginProcessAttributes, launchPath, arguments)) { 397 result = false; 398 return; 399 } 400 401 result = true; 402 403 RetainPtr<NSMutableArray> argumentsArray = adoptNS([[NSMutableArray alloc] initWithCapacity:arguments.size()]); 404 for (size_t i = 0; i < arguments.size(); ++i) 405 [argumentsArray addObject:(NSString *)arguments[i]]; 406 407 [NSTask launchedTaskWithLaunchPath:launchPath arguments:argumentsArray.get()]; 408} 409 410static bool isJavaUpdaterURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString) 411{ 412 NSURL *url = [NSURL URLWithString:urlString]; 413 if (![url isFileURL]) 414 return false; 415 416 NSString *javaUpdaterPath = [NSString pathWithComponents:[NSArray arrayWithObjects:(NSString *)pluginProcessAttributes.moduleInfo.path, @"Contents/Resources/Java Updater.app", nil]]; 417 return [url.path isEqualToString:javaUpdaterPath]; 418} 419 420static bool shouldLaunchApplicationAtURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString) 421{ 422 if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.oracle.java.JavaAppletPlugin") 423 return isJavaUpdaterURL(pluginProcessAttributes, urlString); 424 425 return false; 426} 427 428void PluginProcessProxy::launchApplicationAtURL(const String& urlString, const Vector<String>& arguments, bool& result) 429{ 430 if (!shouldLaunchApplicationAtURL(m_pluginProcessAttributes, urlString)) { 431 result = false; 432 return; 433 } 434 435 result = true; 436 437 RetainPtr<NSMutableArray> argumentsArray = adoptNS([[NSMutableArray alloc] initWithCapacity:arguments.size()]); 438 for (size_t i = 0; i < arguments.size(); ++i) 439 [argumentsArray addObject:(NSString *)arguments[i]]; 440 441 NSDictionary *configuration = [NSDictionary dictionaryWithObject:argumentsArray.get() forKey:NSWorkspaceLaunchConfigurationArguments]; 442 [[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL URLWithString:urlString] options:NSWorkspaceLaunchAsync configuration:configuration error:nullptr]; 443} 444 445static bool isSilverlightPreferencesURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString) 446{ 447 NSURL *silverlightPreferencesURL = [NSURL fileURLWithPathComponents:[NSArray arrayWithObjects:(NSString *)pluginProcessAttributes.moduleInfo.path, @"Contents/Resources/Silverlight Preferences.app", nil]]; 448 449 return [[NSURL URLWithString:urlString] isEqual:silverlightPreferencesURL]; 450} 451 452static bool shouldOpenURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString) 453{ 454 if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.microsoft.SilverlightPlugin") 455 return isSilverlightPreferencesURL(pluginProcessAttributes, urlString); 456 457 return false; 458} 459 460void PluginProcessProxy::openURL(const String& urlString, bool& result, int32_t& status, String& launchedURLString) 461{ 462 if (!shouldOpenURL(m_pluginProcessAttributes, urlString)) { 463 result = false; 464 return; 465 } 466 467 result = true; 468 CFURLRef launchedURL; 469 status = LSOpenCFURLRef(KURL(ParsedURLString, urlString).createCFURL().get(), &launchedURL); 470 471 if (launchedURL) { 472 launchedURLString = KURL(launchedURL).string(); 473 CFRelease(launchedURL); 474 } 475} 476 477static bool shouldOpenFile(const PluginProcessAttributes& pluginProcessAttributes, const String& fullPath) 478{ 479 if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin") { 480 if (fullPath == "/Library/PreferencePanes/Flash Player.prefPane") 481 return true; 482 } 483 484 return false; 485} 486 487void PluginProcessProxy::openFile(const String& fullPath, bool& result) 488{ 489 if (!shouldOpenFile(m_pluginProcessAttributes, fullPath)) { 490 result = false; 491 return; 492 } 493 494 result = true; 495 [[NSWorkspace sharedWorkspace] openFile:fullPath]; 496} 497 498} // namespace WebKit 499 500#endif // ENABLE(PLUGIN_PROCESS) 501