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