- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / renderer_main_platform_delegate_mac.mm
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.
4
5 #include "content/renderer/renderer_main_platform_delegate.h"
6
7 #include <Carbon/Carbon.h>
8 #import <Cocoa/Cocoa.h>
9 #include <objc/runtime.h>
10
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"
22
23 extern "C" {
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);
29 }  // extern "C"
30
31 namespace content {
32
33 namespace {
34
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;
39
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_;
47 }
48
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"),
56   };
57
58   for (size_t i = 0; i < arraysize(kAnnoyingLogMessages); ++i) {
59     if (CFStringCompare(format, kAnnoyingLogMessages[i], 0) ==
60             kCFCompareEqualTo) {
61       return;
62     }
63   }
64
65   va_list args;
66   va_start(args, format);
67   _CFLogvEx(NULL, NULL, NULL, level, format, args);
68   va_end(args);
69 }
70
71 }  // namespace
72
73 RendererMainPlatformDelegate::RendererMainPlatformDelegate(
74     const MainFunctionParams& parameters)
75         : parameters_(parameters) {
76 }
77
78 RendererMainPlatformDelegate::~RendererMainPlatformDelegate() {
79 }
80
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];
88
89   if (![NSThread isMultiThreaded]) {
90     NSString* string = @"";
91     [NSThread detachNewThreadSelector:@selector(length)
92                              toTarget:string
93                            withObject:nil];
94   }
95 }
96
97 void RendererMainPlatformDelegate::PlatformUninitialize() {
98 }
99
100 static void LogTestMessage(std::string message, bool is_error) {
101   if (is_error)
102     LOG(ERROR) << message;
103   else
104     LOG(INFO) << message;
105 }
106
107 bool RendererMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
108   const CommandLine& command_line = parameters_.command_line;
109
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";
115       return false;
116     }
117     NSBundle* tests_bundle =
118         [NSBundle bundleWithPath:base::SysUTF8ToNSString(bundle_path)];
119     if (![tests_bundle load]) {
120       NOTREACHED() << "Failed to load bundle";
121       return false;
122     }
123     sandbox_tests_bundle_ = [tests_bundle retain];
124     [objc_getClass("RendererSandboxTestsRunner") setLogFunction:LogTestMessage];
125   }
126   return true;
127 }
128
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.)
141   //
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();
145
146   if (needs_ime_hack) {
147     mach_error_t err = mach_override_ptr(
148         (void*)&TISCreateInputSourceList,
149         (void*)&CrTISCreateInputSourceList,
150         NULL);
151     CHECK_EQ(err_none, err);
152
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);
157   }
158
159   // Enable the sandbox.
160   bool sandbox_initialized = InitializeSandbox();
161
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());
170
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);
174   }
175
176   return sandbox_initialized;
177 }
178
179 void RendererMainPlatformDelegate::RunSandboxTests(bool no_sandbox) {
180   Class tests_runner = objc_getClass("RendererSandboxTestsRunner");
181   if (tests_runner) {
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;
187   }
188 }
189
190 }  // namespace content