Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebKit / UIProcess / Plugins / mac / PluginProcessProxyMac.mm
1 /*
2  * Copyright (C) 2010-2018 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 "PluginProcessCreationParameters.h"
32 #import "PluginProcessManager.h"
33 #import "PluginProcessMessages.h"
34 #import "SandboxUtilities.h"
35 #import <QuartzCore/CARemoteLayerServer.h>
36 #import <WebCore/FileSystem.h>
37 #import <crt_externs.h>
38 #import <mach-o/dyld.h>
39 #import <pal/spi/cf/CFNetworkSPI.h>
40 #import <spawn.h>
41 #import <wtf/ProcessPrivilege.h>
42 #import <wtf/URL.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
59 namespace WebKit {
60 using namespace WebCore;
61
62     
63 void PluginProcessProxy::platformGetLaunchOptionsWithAttributes(ProcessLauncher::LaunchOptions& launchOptions, const PluginProcessAttributes& pluginProcessAttributes)
64 {
65     if (pluginProcessAttributes.moduleInfo.pluginArchitecture == CPU_TYPE_X86)
66         launchOptions.processType = ProcessLauncher::ProcessType::Plugin32;
67     else
68         launchOptions.processType = ProcessLauncher::ProcessType::Plugin64;
69
70     launchOptions.extraInitializationData.add("plugin-path", pluginProcessAttributes.moduleInfo.path);
71
72     if (PluginProcessManager::singleton().experimentalPlugInSandboxProfilesEnabled())
73         launchOptions.extraInitializationData.add("experimental-sandbox-plugin", "1");
74
75     if (pluginProcessAttributes.sandboxPolicy == PluginProcessSandboxPolicyUnsandboxed) {
76         if (!currentProcessIsSandboxed())
77             launchOptions.extraInitializationData.add("disable-sandbox", "1");
78         else
79             WTFLogAlways("Main process is sandboxed, ignoring plug-in sandbox policy");
80     }
81 }
82
83 void PluginProcessProxy::platformInitializePluginProcess(PluginProcessCreationParameters& parameters)
84 {
85     // For now only Flash is known to behave with asynchronous plug-in initialization.
86     parameters.supportsAsynchronousPluginInitialization = m_pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin";
87
88 #if HAVE(HOSTED_CORE_ANIMATION)
89     parameters.acceleratedCompositingPort = MachSendRight::create([CARemoteLayerServer sharedServer].serverPort);
90 #endif
91     parameters.networkATSContext = adoptCF(_CFNetworkCopyATSContext());
92 }
93
94 bool PluginProcessProxy::getPluginProcessSerialNumber(ProcessSerialNumber& pluginProcessSerialNumber)
95 {
96     pid_t pluginProcessPID = processIdentifier();
97     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
98     return GetProcessForPID(pluginProcessPID, &pluginProcessSerialNumber) == noErr;
99     ALLOW_DEPRECATED_DECLARATIONS_END
100 }
101
102 void PluginProcessProxy::makePluginProcessTheFrontProcess()
103 {
104     ProcessSerialNumber pluginProcessSerialNumber;
105     if (!getPluginProcessSerialNumber(pluginProcessSerialNumber))
106         return;
107
108     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
109     SetFrontProcess(&pluginProcessSerialNumber);
110     ALLOW_DEPRECATED_DECLARATIONS_END
111 }
112
113 void PluginProcessProxy::makeUIProcessTheFrontProcess()
114 {
115     ProcessSerialNumber processSerialNumber;
116     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
117     GetCurrentProcess(&processSerialNumber);
118     SetFrontProcess(&processSerialNumber);            
119     ALLOW_DEPRECATED_DECLARATIONS_END
120 }
121
122 void PluginProcessProxy::setFullscreenWindowIsShowing(bool fullscreenWindowIsShowing)
123 {
124     if (m_fullscreenWindowIsShowing == fullscreenWindowIsShowing)
125         return;
126
127     m_fullscreenWindowIsShowing = fullscreenWindowIsShowing;
128     if (m_fullscreenWindowIsShowing)
129         enterFullscreen();
130     else
131         exitFullscreen();
132 }
133
134 void PluginProcessProxy::enterFullscreen()
135 {
136     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
137     // Get the current presentation options.
138     m_preFullscreenAppPresentationOptions = [NSApp presentationOptions];
139
140     // Figure out which presentation options to use.
141     unsigned presentationOptions = m_preFullscreenAppPresentationOptions & ~(NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar);
142     presentationOptions |= NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
143
144     [NSApp setPresentationOptions:presentationOptions];
145     makePluginProcessTheFrontProcess();
146 }
147
148 void PluginProcessProxy::exitFullscreen()
149 {
150     // If the plug-in host is the current application then we should bring ourselves to the front when it exits full-screen mode.
151     ProcessSerialNumber frontProcessSerialNumber;
152     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
153     GetFrontProcess(&frontProcessSerialNumber);
154     ALLOW_DEPRECATED_DECLARATIONS_END
155
156     // The UI process must be the front process in order to change the presentation mode.
157     makeUIProcessTheFrontProcess();
158     [NSApp setPresentationOptions:m_preFullscreenAppPresentationOptions];
159
160     ProcessSerialNumber pluginProcessSerialNumber;
161     if (!getPluginProcessSerialNumber(pluginProcessSerialNumber))
162         return;
163
164     // If the plug-in process was not the front process, switch back to the previous front process.
165     // (Otherwise we'll keep the UI process as the front process).
166     Boolean isPluginProcessFrontProcess;
167     ALLOW_DEPRECATED_DECLARATIONS_BEGIN
168     SameProcess(&frontProcessSerialNumber, &pluginProcessSerialNumber, &isPluginProcessFrontProcess);
169     ALLOW_DEPRECATED_DECLARATIONS_END
170     if (!isPluginProcessFrontProcess) {
171         ALLOW_DEPRECATED_DECLARATIONS_BEGIN
172         SetFrontProcess(&frontProcessSerialNumber);
173         ALLOW_DEPRECATED_DECLARATIONS_END
174     }
175 }
176
177 void PluginProcessProxy::setModalWindowIsShowing(bool modalWindowIsShowing)
178 {
179     if (modalWindowIsShowing == m_modalWindowIsShowing)
180         return;
181     
182     m_modalWindowIsShowing = modalWindowIsShowing;
183     
184     if (m_modalWindowIsShowing)
185         beginModal();
186     else
187         endModal();
188 }
189
190 void PluginProcessProxy::beginModal()
191 {
192     ASSERT(!m_placeholderWindow);
193     ASSERT(!m_activationObserver);
194     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
195
196     m_placeholderWindow = adoptNS([[WKPlaceholderModalWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]);
197     [m_placeholderWindow setReleasedWhenClosed:NO];
198     
199     m_activationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillBecomeActiveNotification object:NSApp queue:nil
200                                                                          usingBlock:^(NSNotification *){ applicationDidBecomeActive(); }];
201
202     // The call to -[NSApp runModalForWindow:] below will run a nested run loop, and if the plug-in process
203     // crashes the PluginProcessProxy object can be destroyed. Protect against this here.
204     Ref<PluginProcessProxy> protect(*this);
205
206     [NSApp runModalForWindow:m_placeholderWindow.get()];
207     
208     [m_placeholderWindow orderOut:nil];
209     m_placeholderWindow = nullptr;
210 }
211
212 void PluginProcessProxy::endModal()
213 {
214     ASSERT(m_placeholderWindow);
215     ASSERT(m_activationObserver);
216     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
217
218     [[NSNotificationCenter defaultCenter] removeObserver:m_activationObserver.get()];
219     m_activationObserver = nullptr;
220     
221     [NSApp stopModal];
222
223     makeUIProcessTheFrontProcess();
224 }
225     
226 void PluginProcessProxy::applicationDidBecomeActive()
227 {
228     makePluginProcessTheFrontProcess();
229 }
230
231 static bool isFlashUpdater(const String& launchPath, const Vector<String>& arguments)
232 {
233     if (launchPath != "/Applications/Utilities/Adobe Flash Player Install Manager.app/Contents/MacOS/Adobe Flash Player Install Manager")
234         return false;
235
236     if (arguments.size() != 1)
237         return false;
238
239     if (arguments[0] != "-update")
240         return false;
241
242     return true;
243 }
244
245 static bool shouldLaunchProcess(const PluginProcessAttributes& pluginProcessAttributes, const String& launchPath, const Vector<String>& arguments)
246 {
247     if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin")
248         return isFlashUpdater(launchPath, arguments);
249
250     return false;
251 }
252
253 void PluginProcessProxy::launchProcess(const String& launchPath, const Vector<String>& arguments, bool& result)
254 {
255     if (!shouldLaunchProcess(m_pluginProcessAttributes, launchPath, arguments)) {
256         result = false;
257         return;
258     }
259
260     result = true;
261
262     RetainPtr<NSMutableArray> argumentsArray = adoptNS([[NSMutableArray alloc] initWithCapacity:arguments.size()]);
263     for (size_t i = 0; i < arguments.size(); ++i)
264         [argumentsArray addObject:(NSString *)arguments[i]];
265
266     [NSTask launchedTaskWithLaunchPath:launchPath arguments:argumentsArray.get()];
267 }
268
269 static bool isJavaUpdaterURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString)
270 {
271     NSURL *url = [NSURL URLWithString:urlString];
272     if (![url isFileURL])
273         return false;
274
275     NSArray *javaUpdaterAppNames = [NSArray arrayWithObjects:@"Java Updater.app", @"JavaUpdater.app", nil];
276
277     for (NSString *javaUpdaterAppName in javaUpdaterAppNames) {
278         NSString *javaUpdaterPath = [NSString pathWithComponents:[NSArray arrayWithObjects:(NSString *)pluginProcessAttributes.moduleInfo.path, @"Contents/Resources", javaUpdaterAppName, nil]];
279         if ([url.path isEqualToString:javaUpdaterPath])
280             return YES;
281     }
282
283     return NO;
284 }
285
286 static bool shouldLaunchApplicationAtURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString)
287 {
288     if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.oracle.java.JavaAppletPlugin")
289         return isJavaUpdaterURL(pluginProcessAttributes, urlString);
290
291     return false;
292 }
293
294 void PluginProcessProxy::launchApplicationAtURL(const String& urlString, const Vector<String>& arguments, bool& result)
295 {
296     if (!shouldLaunchApplicationAtURL(m_pluginProcessAttributes, urlString)) {
297         result = false;
298         return;
299     }
300
301     result = true;
302
303     RetainPtr<NSMutableArray> argumentsArray = adoptNS([[NSMutableArray alloc] initWithCapacity:arguments.size()]);
304     for (size_t i = 0; i < arguments.size(); ++i)
305         [argumentsArray addObject:(NSString *)arguments[i]];
306
307     NSDictionary *configuration = [NSDictionary dictionaryWithObject:argumentsArray.get() forKey:NSWorkspaceLaunchConfigurationArguments];
308     [[NSWorkspace sharedWorkspace] launchApplicationAtURL:[NSURL URLWithString:urlString] options:NSWorkspaceLaunchAsync configuration:configuration error:nullptr];
309 }
310
311 static bool isSilverlightPreferencesURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString)
312 {
313     NSURL *silverlightPreferencesURL = [NSURL fileURLWithPathComponents:[NSArray arrayWithObjects:(NSString *)pluginProcessAttributes.moduleInfo.path, @"Contents/Resources/Silverlight Preferences.app", nil]];
314
315     return [[NSURL URLWithString:urlString] isEqual:silverlightPreferencesURL];
316 }
317
318 static bool shouldOpenURL(const PluginProcessAttributes& pluginProcessAttributes, const String& urlString)
319 {
320     if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.microsoft.SilverlightPlugin")
321         return isSilverlightPreferencesURL(pluginProcessAttributes, urlString);
322
323     return false;
324 }
325
326 void PluginProcessProxy::openURL(const String& urlString, bool& result, int32_t& status, String& launchedURLString)
327 {
328     if (!shouldOpenURL(m_pluginProcessAttributes, urlString)) {
329         result = false;
330         return;
331     }
332
333     result = true;
334     CFURLRef launchedURL;
335     status = LSOpenCFURLRef(URL({ }, urlString).createCFURL().get(), &launchedURL);
336
337     if (launchedURL) {
338         launchedURLString = URL(launchedURL).string();
339         CFRelease(launchedURL);
340     }
341 }
342
343 static bool shouldOpenFile(const PluginProcessAttributes& pluginProcessAttributes, const String& fullPath)
344 {
345     if (pluginProcessAttributes.moduleInfo.bundleIdentifier == "com.macromedia.Flash Player.plugin") {
346         if (fullPath == "/Library/PreferencePanes/Flash Player.prefPane")
347             return true;
348     }
349
350     return false;
351 }
352
353 void PluginProcessProxy::openFile(const String& fullPath, bool& result)
354 {
355     if (!shouldOpenFile(m_pluginProcessAttributes, fullPath)) {
356         result = false;
357         return;
358     }
359
360     result = true;
361     [[NSWorkspace sharedWorkspace] openFile:fullPath];
362 }
363
364 int pluginProcessLatencyQOS()
365 {
366     static const int qos = [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitPluginProcessLatencyQOS"];
367     return qos;
368 }
369
370 int pluginProcessThroughputQOS()
371 {
372     static const int qos = [[NSUserDefaults standardUserDefaults] integerForKey:@"WebKitPluginProcessThroughputQOS"];
373     return qos;
374 }
375
376 } // namespace WebKit
377
378 #endif // ENABLE(NETSCAPE_PLUGIN_API)