1/*
2 * Copyright (C) 2010, 2011, 2012 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
28#import "ChildProcessEntryPoint.h"
29#import "EnvironmentUtilities.h"
30#import "EnvironmentVariables.h"
31#import "StringUtilities.h"
32#import "WKBase.h"
33#import "WebProcess.h"
34#import <mach/mach_error.h>
35#import <servers/bootstrap.h>
36#import <spawn.h>
37#import <stdio.h>
38#import <wtf/RetainPtr.h>
39#import <wtf/RunLoop.h>
40#import <wtf/text/CString.h>
41#import <wtf/text/WTFString.h>
42
43#if PLATFORM(IOS)
44#import <GraphicsServices/GraphicsServices.h>
45#import <WebCore/WebCoreThreadSystemInterface.h>
46#endif // PLATFORM(IOS)
47
48#if USE(APPKIT)
49@interface NSApplication (WebNSApplicationDetails)
50-(void)_installAutoreleasePoolsOnCurrentThreadIfNecessary;
51@end
52#endif
53
54extern "C" kern_return_t bootstrap_register2(mach_port_t, name_t, mach_port_t, uint64_t);
55
56using namespace WebCore;
57
58namespace WebKit {
59
60class WebContentProcessMainDelegate : public ChildProcessMainDelegate {
61public:
62    WebContentProcessMainDelegate(const CommandLine& commandLine)
63        : ChildProcessMainDelegate(commandLine)
64    {
65    }
66
67    virtual void doPreInitializationWork()
68    {
69        // Remove the WebProcess and SecItem shims from the DYLD_INSERT_LIBRARIES environment variable so any processes
70        // spawned by the WebProcess don't try to insert the shims and crash.
71        EnvironmentUtilities::stripValuesEndingWithString("DYLD_INSERT_LIBRARIES", "/WebProcessShim.dylib");
72        EnvironmentUtilities::stripValuesEndingWithString("DYLD_INSERT_LIBRARIES", "/SecItemShim.dylib");
73
74#if USE(APPKIT)
75        // Initialize AppKit.
76        [NSApplication sharedApplication];
77
78        // Installs autorelease pools on the current runloop which prevents memory from accumulating between user events.
79        // FIXME: Remove when <rdar://problem/8929426> is fixed.
80        [NSApp _installAutoreleasePoolsOnCurrentThreadIfNecessary];
81#endif
82
83#if PLATFORM(IOS)
84        GSInitialize();
85        InitWebCoreThreadSystemInterface();
86#endif // PLATFORM(IOS)
87    }
88
89    virtual bool getConnectionIdentifier(IPC::Connection::Identifier& identifier)
90    {
91        String clientExecutable = m_commandLine["client-executable"];
92        if (clientExecutable.isEmpty())
93            return ChildProcessMainDelegate::getConnectionIdentifier(identifier);
94
95        mach_port_name_t publishedService;
96        mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &publishedService);
97        mach_port_insert_right(mach_task_self(), publishedService, publishedService, MACH_MSG_TYPE_MAKE_SEND);
98        // Make it possible to look up.
99        String serviceName = String::format("com.apple.WebKit.WebProcess-%d", getpid());
100        if (kern_return_t kr = bootstrap_register2(bootstrap_port, const_cast<char*>(serviceName.utf8().data()), publishedService, 0)) {
101            WTFLogAlways("Failed to register service name \"%s\". %s (%x)\n", serviceName.utf8().data(), mach_error_string(kr), kr);
102            return false;
103        }
104
105        CString command = clientExecutable.utf8();
106        const char* args[] = { command.data(), 0 };
107
108        EnvironmentVariables environmentVariables;
109        environmentVariables.set(EnvironmentVariables::preexistingProcessServiceNameKey(), serviceName.utf8().data());
110        environmentVariables.set(EnvironmentVariables::preexistingProcessTypeKey(), m_commandLine["type"].utf8().data());
111
112        posix_spawn_file_actions_t fileActions;
113        posix_spawn_file_actions_init(&fileActions);
114        posix_spawn_file_actions_addinherit_np(&fileActions, STDIN_FILENO);
115        posix_spawn_file_actions_addinherit_np(&fileActions, STDOUT_FILENO);
116        posix_spawn_file_actions_addinherit_np(&fileActions, STDERR_FILENO);
117
118        posix_spawnattr_t attributes;
119        posix_spawnattr_init(&attributes);
120        posix_spawnattr_setflags(&attributes, POSIX_SPAWN_CLOEXEC_DEFAULT | POSIX_SPAWN_SETPGROUP);
121
122        int spawnResult = posix_spawn(0, command.data(), &fileActions, &attributes, const_cast<char**>(args), environmentVariables.environmentPointer());
123
124        posix_spawnattr_destroy(&attributes);
125        posix_spawn_file_actions_destroy(&fileActions);
126
127        if (spawnResult)
128            return false;
129
130        mach_msg_empty_rcv_t message;
131        if (kern_return_t kr = mach_msg(&message.header, MACH_RCV_MSG, 0, sizeof(message), publishedService, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)) {
132            WTFLogAlways("Failed to receive port from the UI process. %s (%x)\n", mach_error_string(kr), kr);
133            return false;
134        }
135
136        mach_port_mod_refs(mach_task_self(), publishedService, MACH_PORT_RIGHT_RECEIVE, -1);
137        mach_port_t serverPort = message.header.msgh_remote_port;
138        mach_port_type_t portType;
139        kern_return_t kr = mach_port_type(mach_task_self(), serverPort, &portType);
140        if (kr || !(portType & MACH_PORT_TYPE_SEND)) {
141            WTFLogAlways("Failed to obtain send right for port received from the UI process.\n");
142            return false;
143        }
144
145        identifier = serverPort;
146        return true;
147    }
148
149    virtual bool getClientIdentifier(String& clientIdentifier)
150    {
151        String clientExecutable = m_commandLine["client-executable"];
152        if (clientExecutable.isEmpty())
153            return ChildProcessMainDelegate::getClientIdentifier(clientIdentifier);
154
155        RetainPtr<NSURL> clientExecutableURL = adoptNS([[NSURL alloc] initFileURLWithPath:nsStringFromWebCoreString(clientExecutable)]);
156        RetainPtr<CFURLRef> clientBundleURL = adoptCF(WKCopyBundleURLForExecutableURL((CFURLRef)clientExecutableURL.get()));
157        RetainPtr<NSBundle> clientBundle = adoptNS([[NSBundle alloc] initWithURL:(NSURL *)clientBundleURL.get()]);
158        clientIdentifier = [clientBundle bundleIdentifier];
159        if (clientIdentifier.isEmpty())
160            return false;
161        return true;
162    }
163
164    virtual bool getClientProcessName(String& clientProcessName)
165    {
166        String clientExecutable = m_commandLine["client-executable"];
167        if (clientExecutable.isEmpty())
168            return ChildProcessMainDelegate::getClientProcessName(clientProcessName);
169
170        // Conjure up a process name by using everything after the last slash from the client-executable,
171        // e.g. /Applications/Safari.app/Contents/MacOS/Safari becomes Safari.
172        size_t lastSlash = clientExecutable.reverseFind('/');
173        clientProcessName = clientExecutable.substring(lastSlash + 1);
174        if (clientProcessName.isEmpty())
175            return false;
176        return true;
177    }
178
179    virtual void startRunLoop() override
180    {
181#if USE(APPKIT)
182        ASSERT(NSApp);
183        [NSApp run];
184#else
185        RunLoop::run();
186#endif
187    }
188};
189
190} // namespace WebKit
191
192using namespace WebKit;
193
194extern "C" WK_EXPORT int WebContentProcessMain(int argc, char** argv);
195
196int WebContentProcessMain(int argc, char** argv)
197{
198    return ChildProcessMain<WebProcess, WebContentProcessMainDelegate>(argc, argv);
199}
200