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#include "base/mac/mac_util.h"
14#include "base/mac/scoped_cftyperef.h"
15#include "base/strings/string_number_conversions.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#include "content/common/sandbox_init_mac.h"
20
21namespace content {
22
23namespace {
24
25// You are about to read a pretty disgusting hack. In a static initializer,
26// CoreFoundation decides to connect with cfprefsd(8) using Mach IPC. There is
27// no public way to close this Mach port after-the-fact, nor a way to stop it
28// from happening since it is done pre-main in dyld. But the address of the
29// CFMachPort can be found in the run loop's string description. Below, that
30// address is parsed, cast, and then used to invalidate the Mach port to
31// disable communication with cfprefsd.
32void DisconnectCFNotificationCenter() {
33  base::ScopedCFTypeRef<CFStringRef> run_loop_description(
34      CFCopyDescription(CFRunLoopGetCurrent()));
35  const CFIndex length = CFStringGetLength(run_loop_description);
36  for (CFIndex i = 0; i < length; ) {
37    // Find the start of a CFMachPort run loop source, which looks like this,
38    // without new lines:
39    // 1 : <CFRunLoopSource 0x7d16ea90 [0xa160af80]>{signalled = No,
40    // valid = Yes, order = 0, context =
41    // <CFMachPort 0x7d16fe00 [0xa160af80]>{valid = Yes, port = 3a0f,
42    // source = 0x7d16ea90, callout =
43    // _ZL14MessageHandlerP12__CFMachPortPvlS1_ (0x96df59c2), context =
44    // <CFMachPort context 0x1475b>}}
45    CFRange run_loop_source_context_range;
46    if (!CFStringFindWithOptions(run_loop_description,
47            CFSTR(", context = <CFMachPort "), CFRangeMake(i, length - i),
48            0, &run_loop_source_context_range)) {
49      break;
50    }
51    i = run_loop_source_context_range.location +
52        run_loop_source_context_range.length;
53
54    // The address of the CFMachPort is the first hexadecimal address after the
55    // CF type name.
56    CFRange port_address_range = CFRangeMake(i, 0);
57    for (CFIndex j = port_address_range.location; j < length; ++j) {
58      UniChar c = CFStringGetCharacterAtIndex(run_loop_description, j);
59      if (c == ' ')
60        break;
61      ++port_address_range.length;
62    }
63
64    base::ScopedCFTypeRef<CFStringRef> port_address_string(
65        CFStringCreateWithSubstring(NULL, run_loop_description,
66            port_address_range));
67    if (!port_address_string)
68      continue;
69
70    // Convert the string to an address.
71    std::string port_address_std_string =
72        base::SysCFStringRefToUTF8(port_address_string);
73#if __LP64__
74    uint64 port_address = 0;
75    if (!base::HexStringToUInt64(port_address_std_string, &port_address))
76      continue;
77#else
78    uint32 port_address = 0;
79    if (!base::HexStringToUInt(port_address_std_string, &port_address))
80      continue;
81#endif
82
83    // Cast the address to an object.
84    CFMachPortRef mach_port = reinterpret_cast<CFMachPortRef>(port_address);
85    if (CFGetTypeID(mach_port) != CFMachPortGetTypeID())
86      continue;
87
88    // Verify that this is the Mach port that needs to be disconnected by the
89    // name of its callout function. Example description (no new lines):
90    // <CFMachPort 0x7d16fe00 [0xa160af80]>{valid = Yes, port = 3a0f, source =
91    // 0x7d16ea90, callout = __CFXNotificationReceiveFromServer (0x96df59c2),
92    // context = <CFMachPort context 0x1475b>}
93    base::ScopedCFTypeRef<CFStringRef> port_description(
94        CFCopyDescription(mach_port));
95    if (CFStringFindWithOptions(port_description,
96            CFSTR(", callout = __CFXNotificationReceiveFromServer ("),
97            CFRangeMake(0, CFStringGetLength(port_description)),
98            0,
99            NULL)) {
100      CFMachPortInvalidate(mach_port);
101      return;
102    }
103  }
104}
105
106}  // namespace
107
108RendererMainPlatformDelegate::RendererMainPlatformDelegate(
109    const MainFunctionParams& parameters)
110        : parameters_(parameters) {
111}
112
113RendererMainPlatformDelegate::~RendererMainPlatformDelegate() {
114}
115
116// TODO(mac-port): Any code needed to initialize a process for purposes of
117// running a renderer needs to also be reflected in chrome_main.cc for
118// --single-process support.
119void RendererMainPlatformDelegate::PlatformInitialize() {
120  if (base::mac::IsOSYosemiteOrLater()) {
121    // This is needed by the NSAnimations run for the scrollbars. If we switch
122    // from native scrollers to drawing them in some other way, this warmup can
123    // be removed <http://crbug.com/306348>.
124    [NSScreen screens];
125  }
126
127  if (![NSThread isMultiThreaded]) {
128    NSString* string = @"";
129    [NSThread detachNewThreadSelector:@selector(length)
130                             toTarget:string
131                           withObject:nil];
132  }
133}
134
135void RendererMainPlatformDelegate::PlatformUninitialize() {
136}
137
138bool RendererMainPlatformDelegate::EnableSandbox() {
139  // Enable the sandbox.
140  bool sandbox_initialized = InitializeSandbox();
141
142  // The sandbox is now engaged. Make sure that the renderer has not connected
143  // itself to Cocoa.
144  CHECK(NSApp == nil);
145
146  DisconnectCFNotificationCenter();
147
148  return sandbox_initialized;
149}
150
151}  // namespace content
152