1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/renderer_main_platform_delegate.h"
7 #include <Carbon/Carbon.h>
8 #import <Cocoa/Cocoa.h>
9 #include <objc/runtime.h>
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #import "base/mac/foundation_util.h"
14 #import "base/mac/mac_util.h"
15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "content/common/sandbox_mac.h"
18 #include "content/public/common/content_switches.h"
19 #import "content/public/common/injection_test_mac.h"
20 #include "content/common/sandbox_init_mac.h"
21 #include "third_party/mach_override/mach_override.h"
24 // SPI logging functions for CF that are exported externally.
25 void CFLog(int32_t level, CFStringRef format, ...);
26 void _CFLogvEx(void* log_func, void* copy_desc_func,
27 CFDictionaryRef format_options, int32_t level,
28 CFStringRef format, va_list args);
35 // This leaked array stores the text input services input and layout sources,
36 // which is returned in CrTISCreateInputSourceList(). This list is computed
37 // right after the sandbox is initialized.
38 CFArrayRef g_text_input_services_source_list_ = NULL;
40 CFArrayRef CrTISCreateInputSourceList(
41 CFDictionaryRef properties,
42 Boolean includeAllInstalled) {
43 DCHECK(g_text_input_services_source_list_);
44 // Callers assume ownership of the result, so increase the retain count.
45 CFRetain(g_text_input_services_source_list_);
46 return g_text_input_services_source_list_;
49 // Text Input Services expects to be able to XPC to HIServices, but the
50 // renderer sandbox blocks that. TIS then becomes very vocal about this on
51 // every new renderer startup, so filter out those log messages.
52 void CrRendererCFLog(int32_t level, CFStringRef format, ...) {
53 const CFStringRef kAnnoyingLogMessages[] = {
54 CFSTR("Error received in message reply handler: %s\n"),
55 CFSTR("Connection Invalid error for service %s.\n"),
58 for (size_t i = 0; i < arraysize(kAnnoyingLogMessages); ++i) {
59 if (CFStringCompare(format, kAnnoyingLogMessages[i], 0) ==
66 va_start(args, format);
67 _CFLogvEx(NULL, NULL, NULL, level, format, args);
73 RendererMainPlatformDelegate::RendererMainPlatformDelegate(
74 const MainFunctionParams& parameters)
75 : parameters_(parameters) {
78 RendererMainPlatformDelegate::~RendererMainPlatformDelegate() {
81 // TODO(mac-port): Any code needed to initialize a process for purposes of
82 // running a renderer needs to also be reflected in chrome_main.cc for
83 // --single-process support.
84 void RendererMainPlatformDelegate::PlatformInitialize() {
85 // Initialize NSApplication up front. Without this call, drawing of
86 // native UI elements (e.g. buttons) in WebKit will explode.
87 [NSApplication sharedApplication];
89 if (![NSThread isMultiThreaded]) {
90 NSString* string = @"";
91 [NSThread detachNewThreadSelector:@selector(length)
97 void RendererMainPlatformDelegate::PlatformUninitialize() {
100 static void LogTestMessage(std::string message, bool is_error) {
102 LOG(ERROR) << message;
104 LOG(INFO) << message;
107 bool RendererMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
108 const CommandLine& command_line = parameters_.command_line;
110 if (command_line.HasSwitch(switches::kTestSandbox)) {
111 std::string bundle_path =
112 command_line.GetSwitchValueNative(switches::kTestSandbox);
113 if (bundle_path.empty()) {
114 NOTREACHED() << "Bad bundle path";
117 NSBundle* tests_bundle =
118 [NSBundle bundleWithPath:base::SysUTF8ToNSString(bundle_path)];
119 if (![tests_bundle load]) {
120 NOTREACHED() << "Failed to load bundle";
123 sandbox_tests_bundle_ = [tests_bundle retain];
124 [objc_getClass("RendererSandboxTestsRunner") setLogFunction:LogTestMessage];
129 bool RendererMainPlatformDelegate::EnableSandbox() {
130 // rdar://9251340 http://openradar.me/9251340
131 // See http://crbug.com/31225 and http://crbug.com/152566
132 // To check if this is broken:
133 // 1. Enable Multi language input (simplified chinese)
134 // 2. Ensure "Show/Hide Trackpad Handwriting" shortcut works.
135 // (ctrl+shift+space).
136 // 3. Now open a new tab in Google Chrome or start Google Chrome
137 // 4. Try ctrl+shift+space shortcut again. Shortcut will not work, IME will
138 // either not appear or (worse) not disappear on ctrl-shift-space.
139 // (Run `ps aux | grep Chinese` (10.6/10.7) or `ps aux | grep Trackpad`
140 // and then kill that pid to make it go away.)
142 // Chinese Handwriting was introduced in 10.6 and is confirmed broken on
143 // 10.6, 10.7, and 10.8. It's fixed on 10.9.
144 bool needs_ime_hack = base::mac::IsOSMountainLionOrEarlier();
146 if (needs_ime_hack) {
147 mach_error_t err = mach_override_ptr(
148 (void*)&TISCreateInputSourceList,
149 (void*)&CrTISCreateInputSourceList,
151 CHECK_EQ(err_none, err);
153 // Override the private CFLog function so that the console is not spammed
154 // by TIS failing to connect to HIServices over XPC.
155 err = mach_override_ptr((void*)&CFLog, (void*)&CrRendererCFLog, NULL);
156 CHECK_EQ(err_none, err);
159 // Enable the sandbox.
160 bool sandbox_initialized = InitializeSandbox();
162 if (needs_ime_hack) {
163 // After the sandbox is initialized, call into TIS. Doing this before
164 // the sandbox is in place will open up renderer access to the
165 // pasteboard and an XPC connection to "com.apple.hiservices-xpcservice".
166 base::ScopedCFTypeRef<TISInputSourceRef> layout_source(
167 TISCopyCurrentKeyboardLayoutInputSource());
168 base::ScopedCFTypeRef<TISInputSourceRef> input_source(
169 TISCopyCurrentKeyboardInputSource());
171 CFTypeRef source_list[] = { layout_source.get(), input_source.get() };
172 g_text_input_services_source_list_ = CFArrayCreate(kCFAllocatorDefault,
173 source_list, arraysize(source_list), &kCFTypeArrayCallBacks);
176 return sandbox_initialized;
179 void RendererMainPlatformDelegate::RunSandboxTests(bool no_sandbox) {
180 Class tests_runner = objc_getClass("RendererSandboxTestsRunner");
182 if (![tests_runner runTests])
183 LOG(ERROR) << "Running renderer with failing sandbox tests!";
184 [sandbox_tests_bundle_ unload];
185 [sandbox_tests_bundle_ release];
186 sandbox_tests_bundle_ = nil;
190 } // namespace content