| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/renderer_main_platform_delegate.h" |
| |
| #include <Carbon/Carbon.h> |
| #import <Cocoa/Cocoa.h> |
| #include <objc/runtime.h> |
| |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #import "base/mac/foundation_util.h" |
| #import "base/mac/mac_util.h" |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "content/common/sandbox_mac.h" |
| #include "content/public/common/content_switches.h" |
| #import "content/public/common/injection_test_mac.h" |
| #include "content/common/sandbox_init_mac.h" |
| #include "third_party/mach_override/mach_override.h" |
| |
| extern "C" { |
| // SPI logging functions for CF that are exported externally. |
| void CFLog(int32_t level, CFStringRef format, ...); |
| void _CFLogvEx(void* log_func, void* copy_desc_func, |
| CFDictionaryRef format_options, int32_t level, |
| CFStringRef format, va_list args); |
| } // extern "C" |
| |
| namespace content { |
| |
| namespace { |
| |
| // This leaked array stores the text input services input and layout sources, |
| // which is returned in CrTISCreateInputSourceList(). This list is computed |
| // right after the sandbox is initialized. |
| CFArrayRef g_text_input_services_source_list_ = NULL; |
| |
| CFArrayRef CrTISCreateInputSourceList( |
| CFDictionaryRef properties, |
| Boolean includeAllInstalled) { |
| DCHECK(g_text_input_services_source_list_); |
| // Callers assume ownership of the result, so increase the retain count. |
| CFRetain(g_text_input_services_source_list_); |
| return g_text_input_services_source_list_; |
| } |
| |
| // Text Input Services expects to be able to XPC to HIServices, but the |
| // renderer sandbox blocks that. TIS then becomes very vocal about this on |
| // every new renderer startup, so filter out those log messages. |
| void CrRendererCFLog(int32_t level, CFStringRef format, ...) { |
| const CFStringRef kAnnoyingLogMessages[] = { |
| CFSTR("Error received in message reply handler: %s\n"), |
| CFSTR("Connection Invalid error for service %s.\n"), |
| }; |
| |
| for (size_t i = 0; i < arraysize(kAnnoyingLogMessages); ++i) { |
| if (CFStringCompare(format, kAnnoyingLogMessages[i], 0) == |
| kCFCompareEqualTo) { |
| return; |
| } |
| } |
| |
| va_list args; |
| va_start(args, format); |
| _CFLogvEx(NULL, NULL, NULL, level, format, args); |
| va_end(args); |
| } |
| |
| } // namespace |
| |
| RendererMainPlatformDelegate::RendererMainPlatformDelegate( |
| const MainFunctionParams& parameters) |
| : parameters_(parameters) { |
| } |
| |
| RendererMainPlatformDelegate::~RendererMainPlatformDelegate() { |
| } |
| |
| // TODO(mac-port): Any code needed to initialize a process for purposes of |
| // running a renderer needs to also be reflected in chrome_main.cc for |
| // --single-process support. |
| void RendererMainPlatformDelegate::PlatformInitialize() { |
| // Initialize NSApplication up front. Without this call, drawing of |
| // native UI elements (e.g. buttons) in WebKit will explode. |
| [NSApplication sharedApplication]; |
| |
| if (![NSThread isMultiThreaded]) { |
| NSString* string = @""; |
| [NSThread detachNewThreadSelector:@selector(length) |
| toTarget:string |
| withObject:nil]; |
| } |
| } |
| |
| void RendererMainPlatformDelegate::PlatformUninitialize() { |
| } |
| |
| static void LogTestMessage(std::string message, bool is_error) { |
| if (is_error) |
| LOG(ERROR) << message; |
| else |
| LOG(INFO) << message; |
| } |
| |
| bool RendererMainPlatformDelegate::InitSandboxTests(bool no_sandbox) { |
| const CommandLine& command_line = parameters_.command_line; |
| |
| if (command_line.HasSwitch(switches::kTestSandbox)) { |
| std::string bundle_path = |
| command_line.GetSwitchValueNative(switches::kTestSandbox); |
| if (bundle_path.empty()) { |
| NOTREACHED() << "Bad bundle path"; |
| return false; |
| } |
| NSBundle* tests_bundle = |
| [NSBundle bundleWithPath:base::SysUTF8ToNSString(bundle_path)]; |
| if (![tests_bundle load]) { |
| NOTREACHED() << "Failed to load bundle"; |
| return false; |
| } |
| sandbox_tests_bundle_ = [tests_bundle retain]; |
| [objc_getClass("RendererSandboxTestsRunner") setLogFunction:LogTestMessage]; |
| } |
| return true; |
| } |
| |
| bool RendererMainPlatformDelegate::EnableSandbox() { |
| // http://openradar.appspot.com/radar?id=1156410 is fixed on OS X 10.9+. |
| // See http://crbug.com/31225 and http://crbug.com/152566 |
| // To check if this is broken: |
| // 1. Enable Multi language input (simplified chinese) |
| // 2. Ensure "Show/Hide Trackpad Handwriting" shortcut works. |
| // (ctrl+shift+space). |
| // 3. Now open a new tab in Google Chrome or start Google Chrome |
| // 4. Try ctrl+shift+space shortcut again. Shortcut will not work, IME will |
| // either not appear or (worse) not disappear on ctrl-shift-space. |
| // (Run `ps aux | grep Chinese` (10.6/10.7) or `ps aux | grep Trackpad` |
| // and then kill that pid to make it go away.) |
| // |
| // Chinese Handwriting was introduced in 10.6 and is confirmed broken on |
| // 10.6, 10.7, and 10.8. It's reportedly fixed on 10.9. |
| bool needs_ime_hack = !base::mac::IsOSLaterThanMountainLion_DontCallThis(); |
| |
| if (needs_ime_hack) { |
| mach_error_t err = mach_override_ptr( |
| (void*)&TISCreateInputSourceList, |
| (void*)&CrTISCreateInputSourceList, |
| NULL); |
| CHECK_EQ(err_none, err); |
| |
| // Override the private CFLog function so that the console is not spammed |
| // by TIS failing to connect to HIServices over XPC. |
| err = mach_override_ptr((void*)&CFLog, (void*)&CrRendererCFLog, NULL); |
| CHECK_EQ(err_none, err); |
| } |
| |
| // Enable the sandbox. |
| bool sandbox_initialized = InitializeSandbox(); |
| |
| if (needs_ime_hack) { |
| // After the sandbox is initialized, call into TIS. Doing this before |
| // the sandbox is in place will open up renderer access to the |
| // pasteboard and an XPC connection to "com.apple.hiservices-xpcservice". |
| base::ScopedCFTypeRef<TISInputSourceRef> layout_source( |
| TISCopyCurrentKeyboardLayoutInputSource()); |
| base::ScopedCFTypeRef<TISInputSourceRef> input_source( |
| TISCopyCurrentKeyboardInputSource()); |
| |
| CFTypeRef source_list[] = { layout_source.get(), input_source.get() }; |
| g_text_input_services_source_list_ = CFArrayCreate(kCFAllocatorDefault, |
| source_list, arraysize(source_list), &kCFTypeArrayCallBacks); |
| } |
| |
| return sandbox_initialized; |
| } |
| |
| void RendererMainPlatformDelegate::RunSandboxTests(bool no_sandbox) { |
| Class tests_runner = objc_getClass("RendererSandboxTestsRunner"); |
| if (tests_runner) { |
| if (![tests_runner runTests]) |
| LOG(ERROR) << "Running renderer with failing sandbox tests!"; |
| [sandbox_tests_bundle_ unload]; |
| [sandbox_tests_bundle_ release]; |
| sandbox_tests_bundle_ = nil; |
| } |
| } |
| |
| } // namespace content |