[WTF] Add environment variable helpers
[WebKit-https.git] / Source / WebKit / PluginProcess / mac / PluginProcessMac.mm
1 /*
2  * Copyright (C) 2010-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #import "config.h"
28 #import "PluginProcess.h"
29
30 #if ENABLE(NETSCAPE_PLUGIN_API)
31
32 #import "ArgumentCoders.h"
33 #import "NetscapePlugin.h"
34 #import "PluginInfoStore.h"
35 #import "PluginProcessCreationParameters.h"
36 #import "PluginProcessProxyMessages.h"
37 #import "PluginProcessShim.h"
38 #import "PluginSandboxProfile.h"
39 #import "SandboxInitializationParameters.h"
40 #import "SandboxUtilities.h"
41 #import <CoreAudio/AudioHardware.h>
42 #import <WebCore/LocalizedStrings.h>
43 #import <WebCore/RuntimeEnabledFeatures.h>
44 #import <dlfcn.h>
45 #import <mach-o/dyld.h>
46 #import <mach-o/getsect.h>
47 #import <mach/mach_vm.h>
48 #import <mach/vm_statistics.h>
49 #import <objc/runtime.h>
50 #import <pal/spi/cg/CoreGraphicsSPI.h>
51 #import <pal/spi/cocoa/LaunchServicesSPI.h>
52 #import <pal/spi/mac/HIToolboxSPI.h>
53 #import <pal/spi/mac/NSApplicationSPI.h>
54 #import <pal/spi/mac/NSWindowSPI.h>
55 #import <sysexits.h>
56 #import <wtf/Environment.h>
57 #import <wtf/HashSet.h>
58 #import <wtf/NeverDestroyed.h>
59
60 const CFStringRef kLSPlugInBundleIdentifierKey = CFSTR("LSPlugInBundleIdentifierKey");
61
62 // These values were chosen to match default NSURLCache sizes at the time of this writing.
63 const NSUInteger pluginMemoryCacheSize = 512000;
64 const NSUInteger pluginDiskCacheSize = 20000000;
65
66 namespace WebKit {
67
68 class FullscreenWindowTracker {
69     WTF_MAKE_NONCOPYABLE(FullscreenWindowTracker);
70
71 public:
72     FullscreenWindowTracker() { }
73     
74     template<typename T> void windowShown(T window);
75     template<typename T> void windowHidden(T window);
76
77 private:
78     HashSet<CGWindowID> m_windows;
79 };
80
81 static bool rectCoversAnyScreen(NSRect rect)
82 {
83     for (NSScreen *screen in [NSScreen screens]) {
84         if (NSContainsRect(rect, [screen frame]))
85             return YES;
86     }
87     return NO;
88 }
89
90 #ifndef NP_NO_CARBON
91 static bool windowCoversAnyScreen(WindowRef window)
92 {
93     HIRect bounds;
94     HIWindowGetBounds(window, kWindowStructureRgn, kHICoordSpaceScreenPixel, &bounds);
95
96     // Convert to Cocoa-style screen coordinates that use a Y offset relative to the zeroth screen's origin.
97     bounds.origin.y = NSHeight([(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame]) - CGRectGetMaxY(bounds);
98
99     return rectCoversAnyScreen(NSRectFromCGRect(bounds));
100 }
101
102 static CGWindowID cgWindowID(WindowRef window)
103 {
104     return reinterpret_cast<CGWindowID>(GetNativeWindowFromWindowRef(window));
105 }
106
107 #endif
108
109 static bool windowCoversAnyScreen(NSWindow *window)
110 {
111     return rectCoversAnyScreen(window.frame);
112 }
113
114 static CGWindowID cgWindowID(NSWindow *window)
115 {
116     return window.windowNumber;
117 }
118
119 template<typename T>
120 void FullscreenWindowTracker::windowShown(T window)
121 {
122     CGWindowID windowID = cgWindowID(window);
123     if (!windowID)
124         return;
125
126     // If this window is already visible then there is nothing to do.
127     if (m_windows.contains(windowID))
128         return;
129     
130     // If the window is not full-screen then we're not interested in it.
131     if (!windowCoversAnyScreen(window))
132         return;
133
134     bool windowSetWasEmpty = m_windows.isEmpty();
135
136     m_windows.add(windowID);
137     
138     // If this is the first full screen window to be shown, notify the UI process.
139     if (windowSetWasEmpty)
140         PluginProcess::singleton().setFullscreenWindowIsShowing(true);
141 }
142
143 template<typename T>
144 void FullscreenWindowTracker::windowHidden(T window)
145 {
146     CGWindowID windowID = cgWindowID(window);
147     if (!windowID)
148         return;
149
150     // If this is not a window that we're tracking then there is nothing to do.
151     if (!m_windows.remove(windowID))
152         return;
153
154     // If this was the last full screen window that was visible, notify the UI process.
155     if (m_windows.isEmpty())
156         PluginProcess::singleton().setFullscreenWindowIsShowing(false);
157 }
158
159 static FullscreenWindowTracker& fullscreenWindowTracker()
160 {
161     static NeverDestroyed<FullscreenWindowTracker> fullscreenWindowTracker;
162     return fullscreenWindowTracker;
163 }
164
165 #if defined(__i386__)
166
167 static bool shouldCallRealDebugger()
168 {
169     static bool isUserbreakSet = false;
170     static dispatch_once_t flag;
171     dispatch_once(&flag, ^{
172         if (auto var = Environment::getInt("USERBREAK"))
173             isUserbreakSet = *var;
174     });
175     
176     return isUserbreakSet;
177 }
178
179 static bool isWindowActive(WindowRef windowRef, bool& result)
180 {
181 #ifndef NP_NO_CARBON
182     if (NetscapePlugin* plugin = NetscapePlugin::netscapePluginFromWindow(windowRef)) {
183         result = plugin->isWindowActive();
184         return true;
185     }
186 #endif
187     return false;
188 }
189
190 static UInt32 getCurrentEventButtonState()
191 {
192 #ifndef NP_NO_CARBON
193     return NetscapePlugin::buttonState();
194 #else
195     ASSERT_NOT_REACHED();
196     return 0;
197 #endif
198 }
199
200 static void carbonWindowShown(WindowRef window)
201 {
202 #ifndef NP_NO_CARBON
203     fullscreenWindowTracker().windowShown(window);
204 #endif
205 }
206
207 static void carbonWindowHidden(WindowRef window)
208 {
209 #ifndef NP_NO_CARBON
210     fullscreenWindowTracker().windowHidden(window);
211 #endif
212 }
213
214 static bool openCFURLRef(CFURLRef url, int32_t& status, CFURLRef* launchedURL)
215 {
216     String launchedURLString;
217     if (!PluginProcess::singleton().openURL(URL(url).string(), status, launchedURLString))
218         return false;
219
220     if (!launchedURLString.isNull() && launchedURL)
221         *launchedURL = URL(URL(), launchedURLString).createCFURL().leakRef();
222     return true;
223 }
224
225 static bool isMallocTinyMemoryTag(int tag)
226 {
227     switch (tag) {
228     case VM_MEMORY_MALLOC_TINY:
229         return true;
230
231     default:
232         return false;
233     }
234 }
235
236 static bool shouldMapMallocMemoryExecutable;
237
238 static bool shouldMapMemoryExecutable(int flags)
239 {
240     if (!shouldMapMallocMemoryExecutable)
241         return false;
242
243     if (!isMallocTinyMemoryTag((flags >> 24) & 0xff))
244         return false;
245
246     return true;
247 }
248
249 #endif
250
251 static void setModal(bool modalWindowIsShowing)
252 {
253     PluginProcess::singleton().setModalWindowIsShowing(modalWindowIsShowing);
254 }
255
256 static unsigned modalCount;
257
258 static void beginModal()
259 {
260     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
261     // Make sure to make ourselves the front process
262     ProcessSerialNumber psn;
263     GetCurrentProcess(&psn);
264     SetFrontProcess(&psn);
265     ALLOW_DEPRECATED_DECLARATIONS_END
266
267     if (!modalCount++)
268         setModal(true);
269 }
270
271 static void endModal()
272 {
273     if (!--modalCount)
274         setModal(false);
275 }
276
277 static IMP NSApplication_RunModalForWindow;
278
279 static NSInteger replacedRunModalForWindow(id self, SEL _cmd, NSWindow* window)
280 {
281     beginModal();
282     NSInteger result = ((NSInteger (*)(id, SEL, NSWindow *))NSApplication_RunModalForWindow)(self, _cmd, window);
283     endModal();
284
285     return result;
286 }
287
288 static bool oldPluginProcessNameShouldEqualNewPluginProcessNameForAdobeReader;
289
290 static bool isAdobeAcrobatAddress(const void* address)
291 {
292     Dl_info imageInfo;
293     if (!dladdr(address, &imageInfo))
294         return false;
295
296     const char* pathSuffix = "/Contents/Frameworks/Acrobat.framework/Acrobat";
297
298     int pathSuffixLength = strlen(pathSuffix);
299     int imageFilePathLength = strlen(imageInfo.dli_fname);
300
301     if (imageFilePathLength < pathSuffixLength)
302         return false;
303
304     if (strcmp(imageInfo.dli_fname + (imageFilePathLength - pathSuffixLength), pathSuffix))
305         return false;
306
307     return true;
308 }
309
310 static bool stringCompare(CFStringRef a, CFStringRef b, CFStringCompareFlags options, void* returnAddress, CFComparisonResult& result)
311 {
312     if (pthread_main_np() != 1)
313         return false;
314
315     if (!oldPluginProcessNameShouldEqualNewPluginProcessNameForAdobeReader)
316         return false;
317
318     if (options != kCFCompareCaseInsensitive)
319         return false;
320
321     const char* aCString = CFStringGetCStringPtr(a, kCFStringEncodingASCII);
322     if (!aCString)
323         return false;
324
325     const char* bCString = CFStringGetCStringPtr(b, kCFStringEncodingASCII);
326     if (!bCString)
327         return false;
328
329     if (strcmp(aCString, "com.apple.WebKit.PluginProcess"))
330         return false;
331
332     if (strcmp(bCString, "com.apple.WebKit.Plugin.64"))
333         return false;
334
335     // Check if the LHS string comes from the Acrobat framework.
336     if (!isAdobeAcrobatAddress(a))
337         return false;
338
339     // Check if the return adress is part of the Acrobat framework as well.
340     if (!isAdobeAcrobatAddress(returnAddress))
341         return false;
342
343     result = kCFCompareEqualTo;
344     return true;
345 }
346
347 static void initializeShim()
348 {
349     // Initialize the shim for 32-bit only.
350     const PluginProcessShimCallbacks callbacks = {
351 #if defined(__i386__)
352         shouldCallRealDebugger,
353         isWindowActive,
354         getCurrentEventButtonState,
355         beginModal,
356         endModal,
357         carbonWindowShown,
358         carbonWindowHidden,
359         setModal,
360         openCFURLRef,
361         shouldMapMemoryExecutable,
362 #endif
363         stringCompare,
364     };
365
366     PluginProcessShimInitializeFunc initFunc = reinterpret_cast<PluginProcessShimInitializeFunc>(dlsym(RTLD_DEFAULT, "WebKitPluginProcessShimInitialize"));
367     initFunc(callbacks);
368 }
369
370 static void (*NSConcreteTask_launch)(NSTask *, SEL);
371
372 static void replacedNSConcreteTask_launch(NSTask *self, SEL _cmd)
373 {
374     String launchPath = self.launchPath;
375
376     Vector<String> arguments;
377     arguments.reserveInitialCapacity(self.arguments.count);
378     for (NSString *argument in self.arguments)
379         arguments.uncheckedAppend(argument);
380
381     if (PluginProcess::singleton().launchProcess(launchPath, arguments))
382         return;
383
384     NSConcreteTask_launch(self, _cmd);
385 }
386
387 static NSRunningApplication *(*NSWorkspace_launchApplicationAtURL_options_configuration_error)(NSWorkspace *, SEL, NSURL *, NSWorkspaceLaunchOptions, NSDictionary *, NSError **);
388
389 static NSRunningApplication *replacedNSWorkspace_launchApplicationAtURL_options_configuration_error(NSWorkspace *self, SEL _cmd, NSURL *url, NSWorkspaceLaunchOptions options, NSDictionary *configuration, NSError **error)
390 {
391     Vector<String> arguments;
392     if (NSArray *argumentsArray = [configuration objectForKey:NSWorkspaceLaunchConfigurationArguments]) {
393         if ([argumentsArray isKindOfClass:[NSArray array]]) {
394             for (NSString *argument in argumentsArray) {
395                 if ([argument isKindOfClass:[NSString class]])
396                     arguments.append(argument);
397             }
398         }
399     }
400
401     if (PluginProcess::singleton().launchApplicationAtURL(URL(url).string(), arguments)) {
402         if (error)
403             *error = nil;
404         return nil;
405     }
406
407     return NSWorkspace_launchApplicationAtURL_options_configuration_error(self, _cmd, url, options, configuration, error);
408 }
409
410 static BOOL (*NSWorkspace_openFile)(NSWorkspace *, SEL, NSString *);
411
412 static BOOL replacedNSWorkspace_openFile(NSWorkspace *self, SEL _cmd, NSString *fullPath)
413 {
414     if (PluginProcess::singleton().openFile(fullPath))
415         return true;
416
417     return NSWorkspace_openFile(self, _cmd, fullPath);
418 }
419
420 static void initializeCocoaOverrides()
421 {
422     // Override -[NSConcreteTask launch:]
423     Method launchMethod = class_getInstanceMethod(objc_getClass("NSConcreteTask"), @selector(launch));
424     NSConcreteTask_launch = reinterpret_cast<void (*)(NSTask *, SEL)>(method_setImplementation(launchMethod, reinterpret_cast<IMP>(replacedNSConcreteTask_launch)));
425
426     // Override -[NSWorkspace launchApplicationAtURL:options:configuration:error:]
427     Method launchApplicationAtURLOptionsConfigurationErrorMethod = class_getInstanceMethod(objc_getClass("NSWorkspace"), @selector(launchApplicationAtURL:options:configuration:error:));
428     NSWorkspace_launchApplicationAtURL_options_configuration_error = reinterpret_cast<NSRunningApplication *(*)(NSWorkspace *, SEL, NSURL *, NSWorkspaceLaunchOptions, NSDictionary *, NSError **)>(method_setImplementation(launchApplicationAtURLOptionsConfigurationErrorMethod, reinterpret_cast<IMP>(replacedNSWorkspace_launchApplicationAtURL_options_configuration_error)));
429
430     // Override -[NSWorkspace openFile:]
431     Method openFileMethod = class_getInstanceMethod(objc_getClass("NSWorkspace"), @selector(openFile:));
432     NSWorkspace_openFile = reinterpret_cast<BOOL (*)(NSWorkspace *, SEL, NSString *)>(method_setImplementation(openFileMethod, reinterpret_cast<IMP>(replacedNSWorkspace_openFile)));
433
434     // Override -[NSApplication runModalForWindow:]
435     Method runModalForWindowMethod = class_getInstanceMethod(objc_getClass("NSApplication"), @selector(runModalForWindow:));
436     NSApplication_RunModalForWindow = method_setImplementation(runModalForWindowMethod, reinterpret_cast<IMP>(replacedRunModalForWindow));
437
438     NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
439
440     // Track when any Cocoa window is about to be be shown.
441     id orderOnScreenObserver = [defaultCenter addObserverForName:NSWindowWillOrderOnScreenNotification
442                                                           object:nil
443                                                            queue:nil
444                                                            usingBlock:^(NSNotification *notification) { fullscreenWindowTracker().windowShown([notification object]); }];
445     // Track when any Cocoa window is about to be hidden.
446     id orderOffScreenObserver = [defaultCenter addObserverForName:NSWindowWillOrderOffScreenNotification
447                                                            object:nil
448                                                             queue:nil
449                                                        usingBlock:^(NSNotification *notification) { fullscreenWindowTracker().windowHidden([notification object]); }];
450
451     // Leak the two observers so that they observe notifications for the lifetime of the process.
452     CFRetain((__bridge CFTypeRef)orderOnScreenObserver);
453     CFRetain((__bridge CFTypeRef)orderOffScreenObserver);
454 }
455
456 void PluginProcess::setModalWindowIsShowing(bool modalWindowIsShowing)
457 {
458     parentProcessConnection()->send(Messages::PluginProcessProxy::SetModalWindowIsShowing(modalWindowIsShowing), 0);
459 }
460
461 void PluginProcess::setFullscreenWindowIsShowing(bool fullscreenWindowIsShowing)
462 {
463     parentProcessConnection()->send(Messages::PluginProcessProxy::SetFullscreenWindowIsShowing(fullscreenWindowIsShowing), 0);
464 }
465
466 bool PluginProcess::launchProcess(const String& launchPath, const Vector<String>& arguments)
467 {
468     bool result;
469     if (!parentProcessConnection()->sendSync(Messages::PluginProcessProxy::LaunchProcess(launchPath, arguments), Messages::PluginProcessProxy::LaunchProcess::Reply(result), 0))
470         return false;
471
472     return result;
473 }
474
475 bool PluginProcess::launchApplicationAtURL(const String& urlString, const Vector<String>& arguments)
476 {
477     bool result = false;
478     if (!parentProcessConnection()->sendSync(Messages::PluginProcessProxy::LaunchApplicationAtURL(urlString, arguments), Messages::PluginProcessProxy::LaunchProcess::Reply(result), 0))
479         return false;
480
481     return result;
482 }
483
484 bool PluginProcess::openURL(const String& urlString, int32_t& status, String& launchedURLString)
485 {
486     bool result;
487     if (!parentProcessConnection()->sendSync(Messages::PluginProcessProxy::OpenURL(urlString), Messages::PluginProcessProxy::OpenURL::Reply(result, status, launchedURLString), 0))
488         return false;
489
490     return result;
491 }
492
493 bool PluginProcess::openFile(const String& fullPath)
494 {
495     bool result;
496     if (!parentProcessConnection()->sendSync(Messages::PluginProcessProxy::OpenFile(fullPath), Messages::PluginProcessProxy::OpenFile::Reply(result), 0))
497         return false;
498
499     return result;
500 }
501
502 static void muteAudio(void)
503 {
504     AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyProcessIsAudible, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
505     UInt32 propertyData = 0;
506     OSStatus result = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, 0, sizeof(UInt32), &propertyData);
507     ASSERT_UNUSED(result, result == noErr);
508 }
509
510 void PluginProcess::platformInitializePluginProcess(PluginProcessCreationParameters&& parameters)
511 {
512     m_compositingRenderServerPort = WTFMove(parameters.acceleratedCompositingPort);
513     if (parameters.processType == PluginProcessTypeSnapshot)
514         muteAudio();
515
516     [NSURLCache setSharedURLCache:adoptNS([[NSURLCache alloc]
517         initWithMemoryCapacity:pluginMemoryCacheSize
518         diskCapacity:pluginDiskCacheSize
519         diskPath:m_nsurlCacheDirectory]).get()];
520
521 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
522     // Disable Dark Mode in the plugin process to avoid rendering issues.
523     [NSApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]];
524 #endif
525 }
526
527 void PluginProcess::platformInitializeProcess(const AuxiliaryProcessInitializationParameters& parameters)
528 {
529     initializeShim();
530
531     initializeCocoaOverrides();
532
533     bool experimentalPlugInSandboxProfilesEnabled = parameters.extraInitializationData.get("experimental-sandbox-plugin") == "1";
534     WebCore::RuntimeEnabledFeatures::sharedFeatures().setExperimentalPlugInSandboxProfilesEnabled(experimentalPlugInSandboxProfilesEnabled);
535
536     // FIXME: It would be better to proxy SetCursor calls over to the UI process instead of
537     // allowing plug-ins to change the mouse cursor at any time.
538     // FIXME: SetsCursorInBackground connection property is deprecated in favor of kCGSSetsCursorInBackgroundTagBit window tag bit.
539     // <rdar://problem/7752422> asks for an API to set cursor from background processes.
540     CGSConnectionID cid = CGSMainConnectionID();
541     CGSSetConnectionProperty(cid, cid, CFSTR("SetsCursorInBackground"), (CFTypeRef)kCFBooleanTrue);
542
543     RetainPtr<CFURLRef> pluginURL = adoptCF(CFURLCreateWithFileSystemPath(0, m_pluginPath.createCFString().get(), kCFURLPOSIXPathStyle, false));
544     if (!pluginURL)
545         return;
546
547     RetainPtr<CFBundleRef> pluginBundle = adoptCF(CFBundleCreate(kCFAllocatorDefault, pluginURL.get()));
548     if (!pluginBundle)
549         return;
550
551     m_pluginBundleIdentifier = CFBundleGetIdentifier(pluginBundle.get());
552
553     if (m_pluginBundleIdentifier == "com.adobe.acrobat.pdfviewerNPAPI")
554         oldPluginProcessNameShouldEqualNewPluginProcessNameForAdobeReader = true;
555
556 #if defined(__i386__)
557     if (m_pluginBundleIdentifier == "com.microsoft.SilverlightPlugin") {
558         // Set this so that any calls to mach_vm_map for pages reserved by malloc will be executable.
559         shouldMapMallocMemoryExecutable = true;
560
561         // Go through the address space looking for already existing malloc regions and change the
562         // protection to make them executable.
563         mach_vm_size_t size;
564         uint32_t depth = 0;
565         struct vm_region_submap_info_64 info = { };
566         mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
567         for (mach_vm_address_t addr = 0; ; addr += size) {
568             kern_return_t kr = mach_vm_region_recurse(mach_task_self(), &addr, &size, &depth, (vm_region_recurse_info_64_t)&info, &count);
569             if (kr != KERN_SUCCESS)
570                 break;
571
572             if (isMallocTinyMemoryTag(info.user_tag))
573                 mach_vm_protect(mach_task_self(), addr, size, false, info.protection | VM_PROT_EXECUTE);
574         }
575
576         // Silverlight expects the data segment of its coreclr library to be executable.
577         // Register with dyld to get notified when libraries are bound, then look for the
578         // coreclr image and make its __DATA segment executable.
579         _dyld_register_func_for_add_image([](const struct mach_header* mh, intptr_t vmaddr_slide) {
580             Dl_info imageInfo;
581             if (!dladdr(mh, &imageInfo))
582                 return;
583
584             const char* pathSuffix = "/Silverlight.plugin/Contents/MacOS/CoreCLR.bundle/Contents/MacOS/coreclr";
585
586             int pathSuffixLength = strlen(pathSuffix);
587             int imageFilePathLength = strlen(imageInfo.dli_fname);
588
589             if (imageFilePathLength < pathSuffixLength)
590                 return;
591
592             if (strcmp(imageInfo.dli_fname + (imageFilePathLength - pathSuffixLength), pathSuffix))
593                 return;
594
595             unsigned long segmentSize;
596             const uint8_t* segmentData = getsegmentdata(mh, "__DATA", &segmentSize);
597             if (!segmentData)
598                 return;
599
600             mach_vm_size_t size;
601             uint32_t depth = 0;
602             struct vm_region_submap_info_64 info = { };
603             mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
604             for (mach_vm_address_t addr = reinterpret_cast<mach_vm_address_t>(segmentData); addr < reinterpret_cast<mach_vm_address_t>(segmentData) + segmentSize ; addr += size) {
605                 kern_return_t kr = mach_vm_region_recurse(mach_task_self(), &addr, &size, &depth, (vm_region_recurse_info_64_t)&info, &count);
606                 if (kr != KERN_SUCCESS)
607                     break;
608
609                 mach_vm_protect(mach_task_self(), addr, size, false, info.protection | VM_PROT_EXECUTE);
610             }
611         });
612     }
613 #endif
614
615     // FIXME: Workaround for Java not liking its plugin process to be suppressed - <rdar://problem/14267843>
616     if (m_pluginBundleIdentifier == "com.oracle.java.JavaAppletPlugin")
617         (new UserActivity("com.oracle.java.JavaAppletPlugin"))->start();
618     
619     if (!pluginHasSandboxProfile(m_pluginBundleIdentifier)) {
620         // Allow Apple Events from Citrix plugin. This can be removed when <rdar://problem/14012823> is fixed.
621         setenv("__APPLEEVENTSSERVICENAME", "com.apple.coreservices.appleevents", 1);
622     }
623 }
624
625 void PluginProcess::initializeProcessName(const AuxiliaryProcessInitializationParameters& parameters)
626 {
627     NSString *applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ (%@ Internet plug-in)", "visible name of the plug-in host process. The first argument is the plug-in name and the second argument is the application name."), [[(NSString *)m_pluginPath lastPathComponent] stringByDeletingPathExtension], (NSString *)parameters.uiProcessName];
628     _LSSetApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), _kLSDisplayNameKey, (CFStringRef)applicationName, nullptr);
629     if (!m_pluginBundleIdentifier.isEmpty())
630         _LSSetApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), kLSPlugInBundleIdentifierKey, m_pluginBundleIdentifier.createCFString().get(), nullptr);
631 }
632
633 void PluginProcess::initializeSandbox(const AuxiliaryProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters)
634 {
635     // PluginProcess may already be sandboxed if its parent process was sandboxed, and launched a child process instead of an XPC service.
636     // This is generally not expected, however we currently always spawn a child process to create a MIME type preferences file.
637     if (currentProcessIsSandboxed()) {
638         RELEASE_ASSERT(!parameters.connectionIdentifier.xpcConnection);
639         return;
640     }
641
642     char cacheDirectory[PATH_MAX];
643     if (!confstr(_CS_DARWIN_USER_CACHE_DIR, cacheDirectory, sizeof(cacheDirectory))) {
644         WTFLogAlways("PluginProcess: couldn't retrieve system cache directory path: %d\n", errno);
645         exit(EX_OSERR);
646     }
647
648     m_nsurlCacheDirectory = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:cacheDirectory length:strlen(cacheDirectory)] stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]];
649     if (![[NSFileManager defaultManager] createDirectoryAtURL:[NSURL fileURLWithPath:m_nsurlCacheDirectory isDirectory:YES] withIntermediateDirectories:YES attributes:nil error:nil]) {
650         WTFLogAlways("PluginProcess: couldn't create NSURL cache directory '%s'\n", cacheDirectory);
651         exit(EX_OSERR);
652     }
653
654     if (PluginInfoStore::shouldAllowPluginToRunUnsandboxed(m_pluginBundleIdentifier))
655         return;
656
657     bool parentIsSandboxed = parameters.connectionIdentifier.xpcConnection && connectedProcessIsSandboxed(parameters.connectionIdentifier.xpcConnection.get());
658
659     if (parameters.extraInitializationData.get("disable-sandbox") == "1") {
660         if (parentIsSandboxed) {
661             WTFLogAlways("Sandboxed processes may not disable plug-in sandbox, terminating %s.", parameters.clientIdentifier.utf8().data());
662             exit(EX_OSERR);
663         }
664         return;
665     }
666
667     String sandboxProfile = pluginSandboxProfile(m_pluginBundleIdentifier);
668     if (sandboxProfile.isEmpty()) {
669         if (parentIsSandboxed) {
670             WTFLogAlways("Sandboxed processes may only use sandboxed plug-ins, terminating %s.", parameters.clientIdentifier.utf8().data());
671             exit(EX_OSERR);
672         }
673         return;
674     }
675
676     sandboxParameters.setSandboxProfile(sandboxProfile);
677
678     char temporaryDirectory[PATH_MAX];
679     if (!confstr(_CS_DARWIN_USER_TEMP_DIR, temporaryDirectory, sizeof(temporaryDirectory))) {
680         WTFLogAlways("PluginProcess: couldn't retrieve system temporary directory path: %d\n", errno);
681         exit(EX_OSERR);
682     }
683
684     if (strlcpy(temporaryDirectory, [[[[NSFileManager defaultManager] stringWithFileSystemRepresentation:temporaryDirectory length:strlen(temporaryDirectory)] stringByAppendingPathComponent:@"WebKitPlugin-XXXXXX"] fileSystemRepresentation], sizeof(temporaryDirectory)) >= sizeof(temporaryDirectory)
685         || !mkdtemp(temporaryDirectory)) {
686         WTFLogAlways("PluginProcess: couldn't create private temporary directory '%s'\n", temporaryDirectory);
687         exit(EX_OSERR);
688     }
689
690     sandboxParameters.setUserDirectorySuffix([[[[NSFileManager defaultManager] stringWithFileSystemRepresentation:temporaryDirectory length:strlen(temporaryDirectory)] lastPathComponent] fileSystemRepresentation]);
691
692     sandboxParameters.addPathParameter("PLUGIN_PATH", m_pluginPath);
693     sandboxParameters.addPathParameter("NSURL_CACHE_DIR", m_nsurlCacheDirectory);
694
695     [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"NSUseRemoteSavePanel" : @YES }];
696
697     AuxiliaryProcess::initializeSandbox(parameters, sandboxParameters);
698 }
699
700 bool PluginProcess::shouldOverrideQuarantine()
701 {
702     return m_pluginBundleIdentifier != "com.cisco.webex.plugin.gpc64";
703 }
704
705 void PluginProcess::stopRunLoop()
706 {
707     AuxiliaryProcess::stopNSAppRunLoop();
708 }
709
710 } // namespace WebKit
711
712 #endif // ENABLE(NETSCAPE_PLUGIN_API)