blob: 9f6172e71e1ff7800de39c96c4a5e36cbc57287c [file] [log] [blame]
// 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
VLOG(0) << 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() {
// rdar://9251340 http://openradar.me/9251340
// 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 fixed on 10.9.
bool needs_ime_hack = base::mac::IsOSMountainLionOrEarlier();
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