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