summaryrefslogtreecommitdiffstats
path: root/content/common
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-11 21:56:11 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-11 21:56:11 +0000
commit415c2cdea5dfa8bc87bd36a91bdb8473026f943f (patch)
tree22aefe975f1be77a1bb18cdd96aeb86fbd96e096 /content/common
parent5471bc3e9d08d1d955c96a8193ee2d9638c1591a (diff)
downloadchromium_src-415c2cdea5dfa8bc87bd36a91bdb8473026f943f.zip
chromium_src-415c2cdea5dfa8bc87bd36a91bdb8473026f943f.tar.gz
chromium_src-415c2cdea5dfa8bc87bd36a91bdb8473026f943f.tar.bz2
Move some chrome\common code to content in preparation for moving chrome\gpu.
TBR=avi Review URL: http://codereview.chromium.org/6686002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77868 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/common')
-rw-r--r--content/common/chrome_application_mac.h71
-rw-r--r--content/common/chrome_application_mac.mm76
-rw-r--r--content/common/content_switches.cc34
-rw-r--r--content/common/content_switches.h14
-rw-r--r--content/common/debug_flags.cc49
-rw-r--r--content/common/debug_flags.h29
-rw-r--r--content/common/font_descriptor_mac.h33
-rw-r--r--content/common/font_descriptor_mac.mm20
-rw-r--r--content/common/font_descriptor_mac_unittest.mm91
-rw-r--r--content/common/font_loader_mac.h55
-rw-r--r--content/common/font_loader_mac.mm110
-rw-r--r--content/common/main_function_params.h37
-rw-r--r--content/common/sandbox_init_wrapper.h70
-rw-r--r--content/common/sandbox_init_wrapper_linux.cc13
-rw-r--r--content/common/sandbox_init_wrapper_mac.cc75
-rw-r--r--content/common/sandbox_init_wrapper_win.cc52
-rw-r--r--content/common/sandbox_mac.h180
-rw-r--r--content/common/sandbox_mac.mm579
-rw-r--r--content/common/sandbox_mac_diraccess_unittest.mm304
-rw-r--r--content/common/sandbox_mac_fontloading_unittest.mm179
-rw-r--r--content/common/sandbox_mac_system_access_unittest.mm96
-rw-r--r--content/common/sandbox_mac_unittest_helper.h117
-rw-r--r--content/common/sandbox_mac_unittest_helper.mm170
23 files changed, 2454 insertions, 0 deletions
diff --git a/content/common/chrome_application_mac.h b/content/common/chrome_application_mac.h
new file mode 100644
index 0000000..23178ff
--- /dev/null
+++ b/content/common/chrome_application_mac.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_CHROME_APPLICATION_MAC_H_
+#define CONTENT_COMMON_CHROME_APPLICATION_MAC_H_
+#pragma once
+
+#if defined(__OBJC__)
+
+#import <AppKit/AppKit.h>
+
+#include "base/basictypes.h"
+#include "base/message_pump_mac.h"
+#include "base/scoped_nsobject.h"
+
+// Event hooks must implement this protocol.
+@protocol CrApplicationEventHookProtocol
+- (void)hookForEvent:(NSEvent*)theEvent;
+@end
+
+
+@interface CrApplication : NSApplication<CrAppProtocol> {
+ @private
+ BOOL handlingSendEvent_;
+ // Array of objects implementing the CrApplicationEventHookProtocol
+ scoped_nsobject<NSMutableArray> eventHooks_;
+}
+- (BOOL)isHandlingSendEvent;
+
+// Add or remove an event hook to be called for every sendEvent:
+// that the application receives. These handlers are called before
+// the normal [NSApplication sendEvent:] call is made.
+
+// This is not a good alternative to a nested event loop. It should
+// be used only when normal event logic and notification breaks down
+// (e.g. when clicking outside a canBecomeKey:NO window to "switch
+// context" out of it).
+- (void)addEventHook:(id<CrApplicationEventHookProtocol>)hook;
+- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)hook;
+
++ (NSApplication*)sharedApplication;
+@end
+
+namespace chrome_application_mac {
+
+// Controls the state of |handlingSendEvent_| in the event loop so that it is
+// reset properly.
+class ScopedSendingEvent {
+ public:
+ ScopedSendingEvent();
+ ~ScopedSendingEvent();
+
+ private:
+ CrApplication* app_;
+ BOOL handling_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedSendingEvent);
+};
+
+} // namespace chrome_application_mac
+
+#endif // defined(__OBJC__)
+
+namespace chrome_application_mac {
+
+// To be used to instantiate CrApplication from C++ code.
+void RegisterCrApp();
+
+} // namespace chrome_application_mac
+
+#endif // CONTENT_COMMON_CHROME_APPLICATION_MAC_H_
diff --git a/content/common/chrome_application_mac.mm b/content/common/chrome_application_mac.mm
new file mode 100644
index 0000000..e4adb3a
--- /dev/null
+++ b/content/common/chrome_application_mac.mm
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 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.
+
+#import "content/common/chrome_application_mac.h"
+
+#include "base/logging.h"
+
+@interface CrApplication ()
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent;
+@end
+
+@implementation CrApplication
+// Initialize NSApplication using the custom subclass. Check whether NSApp
+// was already initialized using another class, because that would break
+// some things.
++ (NSApplication*)sharedApplication {
+ NSApplication* app = [super sharedApplication];
+ if (![NSApp isKindOfClass:self]) {
+ LOG(ERROR) << "NSApp should be of type " << [[self className] UTF8String]
+ << ", not " << [[NSApp className] UTF8String];
+ DCHECK(false) << "NSApp is of wrong type";
+ }
+ return app;
+}
+
+- (id)init {
+ if ((self = [super init])) {
+ eventHooks_.reset([[NSMutableArray alloc] init]);
+ }
+ return self;
+}
+
+- (BOOL)isHandlingSendEvent {
+ return handlingSendEvent_;
+}
+
+- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
+ handlingSendEvent_ = handlingSendEvent;
+}
+
+- (void)sendEvent:(NSEvent*)event {
+ chrome_application_mac::ScopedSendingEvent sendingEventScoper;
+ for (id<CrApplicationEventHookProtocol> handler in eventHooks_.get()) {
+ [handler hookForEvent:event];
+ }
+ [super sendEvent:event];
+}
+
+- (void)addEventHook:(id<CrApplicationEventHookProtocol>)handler {
+ [eventHooks_ addObject:handler];
+}
+
+- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)handler {
+ [eventHooks_ removeObject:handler];
+}
+
+@end
+
+namespace chrome_application_mac {
+
+ScopedSendingEvent::ScopedSendingEvent()
+ : app_(static_cast<CrApplication*>([CrApplication sharedApplication])),
+ handling_([app_ isHandlingSendEvent]) {
+ [app_ setHandlingSendEvent:YES];
+}
+
+ScopedSendingEvent::~ScopedSendingEvent() {
+ [app_ setHandlingSendEvent:handling_];
+}
+
+void RegisterCrApp() {
+ [CrApplication sharedApplication];
+}
+
+} // namespace chrome_application_mac
diff --git a/content/common/content_switches.cc b/content/common/content_switches.cc
index 89efd67..62529a1 100644
--- a/content/common/content_switches.cc
+++ b/content/common/content_switches.cc
@@ -16,10 +16,19 @@ const char kAllowSandboxDebugging[] = "allow-sandbox-debugging";
// Path to the exe to run for the renderer and plugin subprocesses.
const char kBrowserSubprocessPath[] = "browser-subprocess-path";
+// Disables client-visible 3D APIs, in particular WebGL and Pepper 3D.
+// This is controlled by policy and is kept separate from the other
+// enable/disable switches to avoid accidentally regressing the policy
+// support for controlling access to these APIs.
+const char kDisable3DAPIs[] = "disable-3d-apis";
+
// Disable limits on the number of backing stores. Can prevent blinking for
// users with many windows/tabs and lots of memory.
const char kDisableBackingStoreLimit[] = "disable-backing-store-limit";
+// Disable experimental WebGL support.
+const char kDisableExperimentalWebGL[] = "disable-webgl";
+
// Disable FileSystem API.
const char kDisableFileSystem[] = "disable-file-system";
@@ -89,6 +98,9 @@ const char kExperimentalLocationFeatures[] = "experimental-location-features";
// Load NPAPI plugins from the specified directory.
const char kExtraPluginDir[] = "extra-plugin-dir";
+// Causes the process to run as an extension subprocess.
+const char kExtensionProcess[] = "extension";
+
// Extra command line options for launching the GPU process (normally used
// for debugging). Use like renderer-cmd-prefix.
const char kGpuLauncher[] = "gpu-launcher";
@@ -99,6 +111,9 @@ const char kGpuProcess[] = "gpu-process";
// Causes the GPU process to display a dialog on launch.
const char kGpuStartupDialog[] = "gpu-startup-dialog";
+// Runs WebGL inside the renderer process.
+const char kInProcessWebGL[] = "in-process-webgl";
+
// Use LevelDB as back-end for Indexed Database API.
const char kLevelDBIndexedDatabase[] = "indexeddb-use-leveldb";
@@ -112,12 +127,18 @@ const char kLoggingLevel[] = "log-level";
// Make plugin processes log their sent and received messages to VLOG(1).
const char kLogPluginMessages[] = "log-plugin-messages";
+// Causes the process to run as a NativeClient loader.
+const char kNaClLoaderProcess[] = "nacl-loader";
+
// Don't Sandbox the GPU process, does not affect other sandboxed processes.
const char kNoGpuSandbox[] = "no-gpu-sandbox";
// Don't send HTTP-Referer headers.
const char kNoReferrers[] = "no-referrers";
+// Disables the sandbox for all process types that are normally sandboxed.
+const char kNoSandbox[] = "no-sandbox";
+
// Specifies a command that should be used to launch the plugin process. Useful
// for running the plugin process through purify or quantify. Ex:
// --plugin-launcher="path\to\purify /Run=yes"
@@ -168,6 +189,9 @@ const char kProcessPerTab[] = "process-per-tab";
// renderer or plugin host. If it's empty, it's the browser.
const char kProcessType[] = "type";
+// Causes the process to run as a profile import subprocess.
+const char kProfileImportProcess[] = "profile-import";
+
// Register Pepper plugins (see pepper_plugin_registry.cc for its format).
const char kRegisterPepperPlugins[] = "register-pepper-plugins";
@@ -187,6 +211,9 @@ const char kRendererStartupDialog[] = "renderer-startup-dialog";
// Runs the plugin processes inside the sandbox.
const char kSafePlugins[] = "safe-plugins";
+// Causes the process to run as a service process.
+const char kServiceProcess[] = "service";
+
// Runs the renderer and plugins in the same process as the browser
const char kSingleProcess[] = "single-process";
@@ -207,6 +234,13 @@ const char kUnlimitedQuotaForIndexedDB[] = "unlimited-quota-for-indexeddb";
// A string used to override the default user agent with a custom one.
const char kUserAgent[] = "user-agent";
+// Causes the process to run as a utility subprocess.
+const char kUtilityProcess[] = "utility";
+
+// The utility process is sandboxed, with access to one directory. This flag
+// specifies the directory that can be accessed.
+const char kUtilityProcessAllowedDir[] = "utility-allowed-dir";
+
// Will add kWaitForDebugger to every child processes. If a value is passed, it
// will be used as a filter to determine if the child process should have the
// kWaitForDebugger flag passed on or not.
diff --git a/content/common/content_switches.h b/content/common/content_switches.h
index 1d29799..022f164 100644
--- a/content/common/content_switches.h
+++ b/content/common/content_switches.h
@@ -13,7 +13,9 @@ namespace switches {
extern const char kAllowFileAccessFromFiles[];
extern const char kAllowSandboxDebugging[];
extern const char kBrowserSubprocessPath[];
+extern const char kDisable3DAPIs[];
extern const char kDisableBackingStoreLimit[];
+extern const char kDisableExperimentalWebGL[];
extern const char kDisableFileSystem[];
extern const char kDisableGpuSandbox[];
extern const char kDisableGpuWatchdog[];
@@ -32,16 +34,22 @@ extern const char kEnableSandboxLogging[];
extern const char kEnableSeccompSandbox[];
extern const char kEnableStatsTable[];
extern const char kExperimentalLocationFeatures[];
+// TODO(jam): this doesn't belong in content.
+extern const char kExtensionProcess[];
extern const char kExtraPluginDir[];
extern const char kGpuLauncher[];
extern const char kGpuProcess[];
extern const char kGpuStartupDialog[];
+extern const char kInProcessWebGL[];
extern const char kLevelDBIndexedDatabase[];
extern const char kLoadPlugin[];
extern const char kLoggingLevel[];
extern const char kLogPluginMessages[];
+// TODO(jam): this doesn't belong in content.
+extern const char kNaClLoaderProcess[];
extern const char kNoGpuSandbox[];
extern const char kNoReferrers[];
+extern const char kNoSandbox[];
extern const char kPluginLauncher[];
extern const char kPluginPath[];
extern const char kPluginProcess[];
@@ -55,17 +63,23 @@ extern const char kPpapiStartupDialog[];
extern const char kProcessPerSite[];
extern const char kProcessPerTab[];
extern const char kProcessType[];
+// TODO(jam): this doesn't belong in content.
+extern const char kProfileImportProcess[];
extern const char kRegisterPepperPlugins[];
extern const char kRendererCmdPrefix[];
extern const char kRendererCrashTest[];
extern const char kRendererProcess[];
extern const char kRendererStartupDialog[];
extern const char kSafePlugins[];
+// TODO(jam): this doesn't belong in content.
+extern const char kServiceProcess[];
extern const char kSingleProcess[];
extern const char kTestSandbox[];
extern const char kUnlimitedQuotaForFiles[];
extern const char kUnlimitedQuotaForIndexedDB[];
extern const char kUserAgent[];
+extern const char kUtilityProcess[];
+extern const char kUtilityProcessAllowedDir[];
extern const char kWaitForDebuggerChildren[];
extern const char kWebCoreLogChannels[];
extern const char kWebWorkerProcessPerCore[];
diff --git a/content/common/debug_flags.cc b/content/common/debug_flags.cc
new file mode 100644
index 0000000..9fcf2f0
--- /dev/null
+++ b/content/common/debug_flags.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 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/common/debug_flags.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/chrome_switches.h"
+#include "ipc/ipc_switches.h"
+
+bool DebugFlags::ProcessDebugFlags(CommandLine* command_line,
+ ChildProcessInfo::ProcessType type,
+ bool is_in_sandbox) {
+ bool should_help_child = false;
+ const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
+ if (current_cmd_line.HasSwitch(switches::kDebugChildren)) {
+ // Look to pass-on the kDebugOnStart flag.
+ std::string value = current_cmd_line.GetSwitchValueASCII(
+ switches::kDebugChildren);
+ if (value.empty() ||
+ (type == ChildProcessInfo::WORKER_PROCESS &&
+ value == switches::kWorkerProcess) ||
+ (type == ChildProcessInfo::RENDER_PROCESS &&
+ value == switches::kRendererProcess) ||
+ (type == ChildProcessInfo::PLUGIN_PROCESS &&
+ value == switches::kPluginProcess)) {
+ command_line->AppendSwitch(switches::kDebugOnStart);
+ should_help_child = true;
+ }
+ command_line->AppendSwitchASCII(switches::kDebugChildren, value);
+ } else if (current_cmd_line.HasSwitch(switches::kWaitForDebuggerChildren)) {
+ // Look to pass-on the kWaitForDebugger flag.
+ std::string value = current_cmd_line.GetSwitchValueASCII(
+ switches::kWaitForDebuggerChildren);
+ if (value.empty() ||
+ (type == ChildProcessInfo::WORKER_PROCESS &&
+ value == switches::kWorkerProcess) ||
+ (type == ChildProcessInfo::RENDER_PROCESS &&
+ value == switches::kRendererProcess) ||
+ (type == ChildProcessInfo::PLUGIN_PROCESS &&
+ value == switches::kPluginProcess)) {
+ command_line->AppendSwitch(switches::kWaitForDebugger);
+ }
+ command_line->AppendSwitchASCII(switches::kWaitForDebuggerChildren, value);
+ }
+ return should_help_child;
+}
diff --git a/content/common/debug_flags.h b/content/common/debug_flags.h
new file mode 100644
index 0000000..0699e4d
--- /dev/null
+++ b/content/common/debug_flags.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_DEBUG_FLAGS_H_
+#define CONTENT_COMMON_DEBUG_FLAGS_H_
+#pragma once
+
+#include "content/common/child_process_info.h"
+
+class CommandLine;
+
+class DebugFlags {
+ public:
+
+ // Updates the command line arguments with debug-related flags. If
+ // debug flags have been used with this process, they will be
+ // filtered and added to command_line as needed. is_in_sandbox must
+ // be true if the child process will be in a sandbox.
+ //
+ // Returns true if the caller should "help" the child process by
+ // calling the JIT debugger on it. It may only happen if
+ // is_in_sandbox is true.
+ static bool ProcessDebugFlags(CommandLine* command_line,
+ ChildProcessInfo::ProcessType type,
+ bool is_in_sandbox);
+};
+
+#endif // CONTENT_COMMON_DEBUG_FLAGS_H_
diff --git a/content/common/font_descriptor_mac.h b/content/common/font_descriptor_mac.h
new file mode 100644
index 0000000..91bb338
--- /dev/null
+++ b/content/common/font_descriptor_mac.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_FONT_DESCRIPTOR_MAC_H_
+#define CONTENT_COMMON_FONT_DESCRIPTOR_MAC_H_
+#pragma once
+
+#include "base/string16.h"
+
+#ifdef __OBJC__
+@class NSFont;
+#else
+class NSFont;
+#endif
+
+// Container to allow serializing an NSFont over IPC.
+struct FontDescriptor {
+ explicit FontDescriptor(NSFont* font);
+
+ FontDescriptor() : font_point_size(0) {}
+
+ // Return an autoreleased NSFont corresponding to the font description.
+ NSFont* nsFont() const;
+
+ // Name of the font.
+ string16 font_name;
+
+ // Size in points.
+ float font_point_size;
+};
+
+#endif // CONTENT_COMMON_FONT_DESCRIPTOR_MAC_H_
diff --git a/content/common/font_descriptor_mac.mm b/content/common/font_descriptor_mac.mm
new file mode 100644
index 0000000..90c7986
--- /dev/null
+++ b/content/common/font_descriptor_mac.mm
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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/common/font_descriptor_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "base/sys_string_conversions.h"
+
+FontDescriptor::FontDescriptor(NSFont* font) {
+ font_name = base::SysNSStringToUTF16([font fontName]);
+ font_point_size = [font pointSize];
+}
+
+NSFont* FontDescriptor::nsFont() const {
+ NSString* font_name_ns = base::SysUTF16ToNSString(font_name);
+ NSFont* font = [NSFont fontWithName:font_name_ns size:font_point_size];
+ return font;
+}
diff --git a/content/common/font_descriptor_mac_unittest.mm b/content/common/font_descriptor_mac_unittest.mm
new file mode 100644
index 0000000..afa905b
--- /dev/null
+++ b/content/common/font_descriptor_mac_unittest.mm
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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/common/font_descriptor_mac.h"
+
+#include <Cocoa/Cocoa.h>
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+class FontSerializationTest : public PlatformTest {};
+
+
+// Compare 2 fonts, make sure they point at the same font definition and have
+// the same style. Only Bold & Italic style attributes are tested since those
+// are the only ones we care about at the moment.
+bool CompareFonts(NSFont* font1, NSFont* font2) {
+ ATSFontRef id1 = CTFontGetPlatformFont(reinterpret_cast<CTFontRef>(font1), 0);
+ ATSFontRef id2 = CTFontGetPlatformFont(reinterpret_cast<CTFontRef>(font2), 0);
+
+ if (id1 != id2) {
+ LOG(ERROR) << "ATSFontRefs for "
+ << [[font1 fontName] UTF8String]
+ << " and "
+ << [[font2 fontName] UTF8String]
+ << " are different";
+ return false;
+ }
+
+ CGFloat size1 = [font1 pointSize];
+ CGFloat size2 = [font2 pointSize];
+ if (size1 != size2) {
+ LOG(ERROR) << "font sizes for "
+ << [[font1 fontName] UTF8String] << " (" << size1 << ")"
+ << "and"
+ << [[font2 fontName] UTF8String] << " (" << size2 << ")"
+ << " are different";
+ return false;
+ }
+
+ NSFontTraitMask traits1 = [[NSFontManager sharedFontManager]
+ traitsOfFont:font1];
+ NSFontTraitMask traits2 = [[NSFontManager sharedFontManager]
+ traitsOfFont:font2];
+
+ bool is_bold1 = traits1 & NSBoldFontMask;
+ bool is_bold2 = traits2 & NSBoldFontMask;
+ bool is_italic1 = traits1 & NSItalicFontMask;
+ bool is_italic2 = traits2 & NSItalicFontMask;
+
+ if (is_bold1 != is_bold2 || is_italic1 != is_italic2) {
+ LOG(ERROR) << "Style information for "
+ << [[font1 fontName] UTF8String]
+ << " and "
+ << [[font2 fontName] UTF8String]
+ << " are different";
+ return false;
+ }
+
+ return true;
+}
+
+// Verify that serialization and deserialization of fonts with various styles
+// is performed correctly by FontDescriptor.
+TEST_F(FontSerializationTest, StyledFonts) {
+ NSFont* plain_font = [NSFont systemFontOfSize:12.0];
+ ASSERT_TRUE(plain_font != nil);
+ FontDescriptor desc_plain(plain_font);
+ EXPECT_TRUE(CompareFonts(plain_font, desc_plain.nsFont()));
+
+ NSFont* bold_font = [NSFont boldSystemFontOfSize:30.0];
+ ASSERT_TRUE(bold_font != nil);
+ FontDescriptor desc_bold(bold_font);
+ EXPECT_TRUE(CompareFonts(bold_font, desc_bold.nsFont()));
+
+ NSFont* italic_bold_font =
+ [[NSFontManager sharedFontManager]
+ fontWithFamily:@"Courier"
+ traits:(NSBoldFontMask | NSItalicFontMask)
+ weight:5
+ size:18.0];
+ ASSERT_TRUE(italic_bold_font != nil);
+ FontDescriptor desc_italic_bold(italic_bold_font);
+ EXPECT_TRUE(CompareFonts(italic_bold_font, desc_italic_bold.nsFont()));
+}
+
+} // namsepace
diff --git a/content/common/font_loader_mac.h b/content/common/font_loader_mac.h
new file mode 100644
index 0000000..532a09c
--- /dev/null
+++ b/content/common/font_loader_mac.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_FONT_LOADER_MAC_H_
+#define CONTENT_COMMON_FONT_LOADER_MAC_H_
+#pragma once
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include "base/shared_memory.h"
+
+#ifdef __OBJC__
+@class NSFont;
+#else
+class NSFont;
+#endif
+
+// Provides functionality to transmit fonts over IPC.
+//
+// Note about font formats: .dfont (datafork suitcase) fonts are currently not
+// supported by this code since ATSFontActivateFromMemory() can't handle them
+// directly.
+
+class FontLoader {
+ public:
+ // Load a font specified by |font_to_encode| into a shared memory buffer
+ // suitable for sending over IPC.
+ //
+ // On return:
+ // returns true on success, false on failure.
+ // |font_data| - shared memory buffer containing the raw data for the font
+ // file.
+ // |font_data_size| - size of data contained in |font_data|.
+ static bool LoadFontIntoBuffer(NSFont* font_to_encode,
+ base::SharedMemory* font_data,
+ uint32* font_data_size);
+
+ // Given a shared memory buffer containing the raw data for a font file, load
+ // the font and return a container ref.
+ //
+ // |data| - A shared memory handle pointing to the raw data from a font file.
+ // |data_size| - Size of |data|.
+ //
+ // On return:
+ // returns true on success, false on failure.
+ // |font_container| - A font container corresponding to the designated font.
+ // The caller is responsible for releasing this value via ATSFontDeactivate()
+ // when done
+ static bool ATSFontContainerFromBuffer(base::SharedMemoryHandle font_data,
+ uint32 font_data_size,
+ ATSFontContainerRef* font_container);
+};
+
+#endif // CONTENT_COMMON_FONT_LOADER_MAC_H_
diff --git a/content/common/font_loader_mac.mm b/content/common/font_loader_mac.mm
new file mode 100644
index 0000000..124708b
--- /dev/null
+++ b/content/common/font_loader_mac.mm
@@ -0,0 +1,110 @@
+// Copyright (c) 2011 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/common/font_loader_mac.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/sys_string_conversions.h"
+
+// static
+bool FontLoader::LoadFontIntoBuffer(NSFont* font_to_encode,
+ base::SharedMemory* font_data,
+ uint32* font_data_size) {
+ CHECK(font_data && font_data_size);
+ *font_data_size = 0;
+
+ // Used only for logging.
+ std::string font_name([[font_to_encode fontName] UTF8String]);
+
+ // Load appropriate NSFont.
+ if (!font_to_encode) {
+ LOG(ERROR) << "Failed to load font " << font_name;
+ return false;
+ }
+
+ // NSFont -> ATSFontRef.
+ ATSFontRef ats_font =
+ CTFontGetPlatformFont(reinterpret_cast<CTFontRef>(font_to_encode), NULL);
+ if (!ats_font) {
+ LOG(ERROR) << "Conversion to ATSFontRef failed for " << font_name;
+ return false;
+ }
+
+ // ATSFontRef -> File path.
+ // Warning: Calling this function on a font activated from memory will result
+ // in failure with a -50 - paramErr. This may occur if
+ // CreateCGFontFromBuffer() is called in the same process as this function
+ // e.g. when writing a unit test that exercises these two functions together.
+ // If said unit test were to load a system font and activate it from memory
+ // it becomes impossible for the system to the find the original file ref
+ // since the font now lives in memory as far as it's concerned.
+ FSRef font_fsref;
+ if (ATSFontGetFileReference(ats_font, &font_fsref) != noErr) {
+ LOG(ERROR) << "Failed to find font file for " << font_name;
+ return false;
+ }
+ FilePath font_path = FilePath(base::mac::PathFromFSRef(font_fsref));
+
+ // Load file into shared memory buffer.
+ int64 font_file_size_64 = -1;
+ if (!file_util::GetFileSize(font_path, &font_file_size_64)) {
+ LOG(ERROR) << "Couldn't get font file size for " << font_path.value();
+ return false;
+ }
+
+ if (font_file_size_64 <= 0 || font_file_size_64 >= kint32max) {
+ LOG(ERROR) << "Bad size for font file " << font_path.value();
+ return false;
+ }
+
+ int32 font_file_size_32 = static_cast<int32>(font_file_size_64);
+ if (!font_data->CreateAndMapAnonymous(font_file_size_32)) {
+ LOG(ERROR) << "Failed to create shmem area for " << font_name;
+ return false;
+ }
+
+ int32 amt_read = file_util::ReadFile(font_path,
+ reinterpret_cast<char*>(font_data->memory()),
+ font_file_size_32);
+ if (amt_read != font_file_size_32) {
+ LOG(ERROR) << "Failed to read font data for " << font_path.value();
+ return false;
+ }
+
+ *font_data_size = font_file_size_32;
+ return true;
+}
+
+// static
+bool FontLoader::ATSFontContainerFromBuffer(base::SharedMemoryHandle font_data,
+ uint32 font_data_size,
+ ATSFontContainerRef* font_container)
+{
+ CHECK(font_container);
+
+ using base::SharedMemory;
+ DCHECK(SharedMemory::IsHandleValid(font_data));
+ DCHECK_GT(font_data_size, 0U);
+
+ SharedMemory shm(font_data, true);
+ if (!shm.Map(font_data_size))
+ return false;
+
+ // A value of 3 means the font is private and can't be seen by anyone else.
+ // This is the value used by WebKit when activating remote fonts.
+ const ATSFontContext kFontContextPrivate = 3;
+ OSStatus err = ATSFontActivateFromMemory(shm.memory(), font_data_size,
+ kFontContextPrivate, kATSFontFormatUnspecified, NULL,
+ kATSOptionFlagsDefault, font_container);
+ if (err != noErr || !font_container)
+ return false;
+
+ return true;
+}
diff --git a/content/common/main_function_params.h b/content/common/main_function_params.h
new file mode 100644
index 0000000..1c53c5f
--- /dev/null
+++ b/content/common/main_function_params.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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.
+
+// Wrapper to the parameter list for the "main" entry points (browser, renderer,
+// plugin) to shield the call sites from the differences between platforms
+// (e.g., POSIX doesn't need to pass any sandbox information).
+
+#ifndef CONTENT_COMMON_MAIN_FUNCTION_PARAMS_H_
+#define CONTENT_COMMON_MAIN_FUNCTION_PARAMS_H_
+#pragma once
+
+#include "base/command_line.h"
+#include "content/common/sandbox_init_wrapper.h"
+
+namespace base {
+namespace mac {
+class ScopedNSAutoreleasePool;
+}
+}
+
+class Task;
+
+struct MainFunctionParams {
+ MainFunctionParams(const CommandLine& cl, const SandboxInitWrapper& sb,
+ base::mac::ScopedNSAutoreleasePool* pool)
+ : command_line_(cl), sandbox_info_(sb), autorelease_pool_(pool),
+ ui_task(NULL) { }
+ const CommandLine& command_line_;
+ const SandboxInitWrapper& sandbox_info_;
+ base::mac::ScopedNSAutoreleasePool* autorelease_pool_;
+ // Used by InProcessBrowserTest. If non-null BrowserMain schedules this
+ // task to run on the MessageLoop and BrowserInit is not invoked.
+ Task* ui_task;
+};
+
+#endif // CONTENT_COMMON_MAIN_FUNCTION_PARAMS_H_
diff --git a/content/common/sandbox_init_wrapper.h b/content/common/sandbox_init_wrapper.h
new file mode 100644
index 0000000..c144992
--- /dev/null
+++ b/content/common/sandbox_init_wrapper.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_SANDBOX_INIT_WRAPPER_H_
+#define CONTENT_COMMON_SANDBOX_INIT_WRAPPER_H_
+#pragma once
+
+// Wraps the sandbox initialization and platform variables to consolodate
+// the code and reduce the number of platform ifdefs elsewhere. The POSIX
+// version of this wrapper is basically empty.
+
+#include "build/build_config.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#if defined(OS_WIN)
+#include "sandbox/src/sandbox.h"
+#endif
+
+class CommandLine;
+
+#if defined(OS_WIN)
+
+class SandboxInitWrapper {
+ public:
+ SandboxInitWrapper() : broker_services_(), target_services_() { }
+ // SetServices() needs to be called before InitializeSandbox() on Win32 with
+ // the info received from the chrome exe main.
+ void SetServices(sandbox::SandboxInterfaceInfo* sandbox_info);
+ sandbox::BrokerServices* BrokerServices() const { return broker_services_; }
+ sandbox::TargetServices* TargetServices() const { return target_services_; }
+
+ // Initialize the sandbox for renderer, gpu, utility, worker, nacl, and
+ // plug-in processes, depending on the command line flags. The browser
+ // process is not sandboxed.
+ // Returns true if the sandbox was initialized succesfully, false if an error
+ // occurred. If process_type isn't one that needs sandboxing true is always
+ // returned.
+ bool InitializeSandbox(const CommandLine& parsed_command_line,
+ const std::string& process_type);
+ private:
+ sandbox::BrokerServices* broker_services_;
+ sandbox::TargetServices* target_services_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxInitWrapper);
+};
+
+#elif defined(OS_POSIX)
+
+class SandboxInitWrapper {
+ public:
+ SandboxInitWrapper() { }
+
+ // Initialize the sandbox for renderer and plug-in processes, depending on
+ // the command line flags. The browser process is not sandboxed.
+ // Returns true if the sandbox was initialized succesfully, false if an error
+ // occurred. If process_type isn't one that needs sandboxing true is always
+ // returned.
+ bool InitializeSandbox(const CommandLine& parsed_command_line,
+ const std::string& process_type);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxInitWrapper);
+};
+
+#endif
+
+#endif // CONTENT_COMMON_SANDBOX_INIT_WRAPPER_H_
diff --git a/content/common/sandbox_init_wrapper_linux.cc b/content/common/sandbox_init_wrapper_linux.cc
new file mode 100644
index 0000000..450bd2d
--- /dev/null
+++ b/content/common/sandbox_init_wrapper_linux.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2011 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/common/sandbox_init_wrapper.h"
+
+#include "base/command_line.h"
+
+bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line,
+ const std::string& process_type) {
+ // TODO(port): Does Linux need to do anything here?
+ return true;
+}
diff --git a/content/common/sandbox_init_wrapper_mac.cc b/content/common/sandbox_init_wrapper_mac.cc
new file mode 100644
index 0000000..997ba43
--- /dev/null
+++ b/content/common/sandbox_init_wrapper_mac.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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/common/sandbox_init_wrapper.h"
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "content/common/content_switches.h"
+#include "content/common/sandbox_mac.h"
+
+bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line,
+ const std::string& process_type) {
+ using sandbox::Sandbox;
+
+ if (command_line.HasSwitch(switches::kNoSandbox))
+ return true;
+
+ Sandbox::SandboxProcessType sandbox_process_type;
+ FilePath allowed_dir; // Empty by default.
+
+ if (process_type.empty()) {
+ // Browser process isn't sandboxed.
+ return true;
+ } else if (process_type == switches::kRendererProcess) {
+ if (!command_line.HasSwitch(switches::kDisable3DAPIs) &&
+ !command_line.HasSwitch(switches::kDisableExperimentalWebGL) &&
+ command_line.HasSwitch(switches::kInProcessWebGL)) {
+ // TODO(kbr): this check seems to be necessary only on this
+ // platform because the sandbox is initialized later. Remove
+ // this once this flag is removed.
+ return true;
+ } else {
+ sandbox_process_type = Sandbox::SANDBOX_TYPE_RENDERER;
+ }
+ } else if (process_type == switches::kExtensionProcess) {
+ // Extension processes are just renderers [they use RenderMain()] with a
+ // different set of command line flags.
+ // If we ever get here it means something has changed in regards
+ // to the extension process mechanics and we should probably reexamine
+ // how we sandbox extension processes since they are no longer identical
+ // to renderers.
+ NOTREACHED();
+ return true;
+ } else if (process_type == switches::kUtilityProcess) {
+ // Utility process sandbox.
+ sandbox_process_type = Sandbox::SANDBOX_TYPE_UTILITY;
+ allowed_dir =
+ command_line.GetSwitchValuePath(switches::kUtilityProcessAllowedDir);
+ } else if (process_type == switches::kWorkerProcess) {
+ // Worker process sandbox.
+ sandbox_process_type = Sandbox::SANDBOX_TYPE_WORKER;
+ } else if (process_type == switches::kNaClLoaderProcess) {
+ // Native Client sel_ldr (user untrusted code) sandbox.
+ sandbox_process_type = Sandbox::SANDBOX_TYPE_NACL_LOADER;
+ } else if (process_type == switches::kGpuProcess) {
+ sandbox_process_type = Sandbox::SANDBOX_TYPE_GPU;
+ } else if ((process_type == switches::kPluginProcess) ||
+ (process_type == switches::kProfileImportProcess) ||
+ (process_type == switches::kServiceProcess)) {
+ return true;
+ } else {
+ // Failsafe: If you hit an unreached here, is your new process type in need
+ // of sandboxing?
+ NOTREACHED();
+ return true;
+ }
+
+ // Warm up APIs before turning on the sandbox.
+ Sandbox::SandboxWarmup(sandbox_process_type);
+
+ // Actually sandbox the process.
+ return Sandbox::EnableSandbox(sandbox_process_type, allowed_dir);
+}
diff --git a/content/common/sandbox_init_wrapper_win.cc b/content/common/sandbox_init_wrapper_win.cc
new file mode 100644
index 0000000..d322130
--- /dev/null
+++ b/content/common/sandbox_init_wrapper_win.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 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/common/sandbox_init_wrapper.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+
+#include "chrome/common/chrome_switches.h"
+
+void SandboxInitWrapper::SetServices(sandbox::SandboxInterfaceInfo* info) {
+ if (!info)
+ return;
+ if (info->legacy) {
+ // Looks like we are in the case when the new chrome.dll is being launched
+ // by the old chrome.exe, the old chrome exe has SandboxInterfaceInfo as a
+ // union, while now we have a struct.
+ // TODO(cpu): Remove this nasty hack after M10 release.
+ broker_services_ = reinterpret_cast<sandbox::BrokerServices*>(info->legacy);
+ target_services_ = reinterpret_cast<sandbox::TargetServices*>(info->legacy);
+ } else {
+ // Normal case, both the exe and the dll are the same version. Both
+ // interface pointers cannot be non-zero. A process can either be a target
+ // or a broker but not both.
+ broker_services_ = info->broker_services;
+ target_services_ = info->target_services;
+ DCHECK(!(target_services_ && broker_services_));
+ }
+}
+
+bool SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line,
+ const std::string& process_type) {
+ if (command_line.HasSwitch(switches::kNoSandbox))
+ return true;
+ if ((process_type == switches::kRendererProcess) ||
+ (process_type == switches::kExtensionProcess) ||
+ (process_type == switches::kWorkerProcess) ||
+ (process_type == switches::kNaClLoaderProcess) ||
+ (process_type == switches::kUtilityProcess)) {
+ // The above five process types must be sandboxed unless --no-sandbox
+ // is present in the command line.
+ if (!target_services_)
+ return false;
+ } else {
+ // Other process types might or might not be sandboxed.
+ // TODO(cpu): clean this mess.
+ if (!target_services_)
+ return true;
+ }
+ return (sandbox::SBOX_ALL_OK == target_services_->Init());
+}
diff --git a/content/common/sandbox_mac.h b/content/common/sandbox_mac.h
new file mode 100644
index 0000000..50c989b
--- /dev/null
+++ b/content/common/sandbox_mac.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_SANDBOX_MAC_H_
+#define CONTENT_COMMON_SANDBOX_MAC_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/gtest_prod_util.h"
+
+class FilePath;
+
+#if __OBJC__
+@class NSArray;
+@class NSString;
+#else
+class NSArray;
+class NSString;
+#endif
+
+namespace sandbox {
+
+// Class representing a substring of the sandbox profile tagged with its type.
+class SandboxSubstring {
+ public:
+ enum SandboxSubstringType {
+ PLAIN, // Just a plain string, no escaping necessary.
+ LITERAL, // Escape for use in (literal ...) expression.
+ REGEX, // Escape for use in (regex ...) expression.
+ };
+
+ SandboxSubstring() {}
+
+ explicit SandboxSubstring(const std::string& value)
+ : value_(value),
+ type_(PLAIN) {}
+
+ SandboxSubstring(const std::string& value, SandboxSubstringType type)
+ : value_(value),
+ type_(type) {}
+
+ const std::string& value() { return value_; }
+ SandboxSubstringType type() { return type_; }
+
+ private:
+ std::string value_;
+ SandboxSubstringType type_;
+};
+
+class Sandbox {
+ public:
+ // A map of variable name -> string to substitute in its place.
+ typedef base::hash_map<std::string, SandboxSubstring>
+ SandboxVariableSubstitions;
+
+ enum SandboxProcessType {
+ SANDBOX_TYPE_FIRST_TYPE, // Placeholder to ease iteration.
+
+ SANDBOX_TYPE_RENDERER = SANDBOX_TYPE_FIRST_TYPE,
+
+ // The worker process uses the most restrictive sandbox which has almost
+ // *everything* locked down. Only a couple of /System/Library/ paths and
+ // some other very basic operations (e.g., reading metadata to allow
+ // following symlinks) are permitted.
+ SANDBOX_TYPE_WORKER,
+
+ // Utility process is as restrictive as the worker process except full
+ // access is allowed to one configurable directory.
+ SANDBOX_TYPE_UTILITY,
+
+ // Native Client sandbox for the user's untrusted code.
+ SANDBOX_TYPE_NACL_LOADER,
+
+ // GPU process.
+ SANDBOX_TYPE_GPU,
+
+ SANDBOX_AFTER_TYPE_LAST_TYPE, // Placeholder to ease iteration.
+ };
+
+ // Warm up System APIs that empirically need to be accessed before the Sandbox
+ // is turned on. |sandbox_type| is the type of sandbox to warm up.
+ static void SandboxWarmup(SandboxProcessType sandbox_type);
+
+ // Turns on the OS X sandbox for this process.
+ // |sandbox_type| - type of Sandbox to use.
+ // |allowed_dir| - directory to allow access to, currently the only sandbox
+ // profile that supports this is SANDBOX_TYPE_UTILITY .
+ //
+ // Returns true on success, false if an error occurred enabling the sandbox.
+ static bool EnableSandbox(SandboxProcessType sandbox_type,
+ const FilePath& allowed_dir);
+
+
+ // Exposed for testing purposes, used by an accessory function of our tests
+ // so we can't use FRIEND_TEST.
+
+ // Build the Sandbox command necessary to allow access to a named directory
+ // indicated by |allowed_dir|.
+ // Returns a string containing the sandbox profile commands necessary to allow
+ // access to that directory or nil if an error occured.
+
+ // The header comment for PostProcessSandboxProfile() explains how variable
+ // substition works in sandbox templates.
+ // The returned string contains embedded variables. The function fills in
+ // |substitutions| to contain the values for these variables.
+ static NSString* BuildAllowDirectoryAccessSandboxString(
+ const FilePath& allowed_dir,
+ SandboxVariableSubstitions* substitutions);
+
+ // Assemble the final sandbox profile from a template by removing comments
+ // and substituting variables.
+ //
+ // |sandbox_template| is a string which contains 2 entitites to operate on:
+ //
+ // - Comments - The sandbox comment syntax is used to make the OS sandbox
+ // optionally ignore commands it doesn't support. e.g.
+ // ;10.6_ONLY (foo)
+ // Where (foo) is some command that is only supported on OS X 10.6.
+ // The ;10.6_ONLY comment can then be removed from the template to enable
+ // (foo) as appropriate.
+ //
+ // - Variables - denoted by @variable_name@ . These are defined in the
+ // sandbox template in cases where another string needs to be substituted at
+ // runtime. e.g. @HOMEDIR_AS_LITERAL@ is substituted at runtime for the user's
+ // home directory escaped appropriately for a (literal ...) expression.
+ //
+ // |comments_to_remove| is a list of NSStrings containing the comments to
+ // remove.
+ // |substitutions| is a hash of "variable name" -> "string to substitute".
+ // Where the replacement string is tagged with information on how it is to be
+ // escaped e.g. used as part of a regex string or a literal.
+ //
+ // On output |final_sandbox_profile_str| contains the final sandbox profile.
+ // Returns true on success, false otherwise.
+ static bool PostProcessSandboxProfile(
+ NSString* in_sandbox_data,
+ NSArray* comments_to_remove,
+ SandboxVariableSubstitions& substitutions,
+ std::string *final_sandbox_profile_str);
+
+ private:
+ // Escape |src_utf8| for use in a plain string variable in a sandbox
+ // configuraton file. On return |dst| is set to the quoted output.
+ // Returns: true on success, false otherwise.
+ static bool QuotePlainString(const std::string& src_utf8, std::string* dst);
+
+ // Escape |str_utf8| for use in a regex literal in a sandbox
+ // configuraton file. On return |dst| is set to the utf-8 encoded quoted
+ // output.
+ //
+ // The implementation of this function is based on empirical testing of the
+ // OS X sandbox on 10.5.8 & 10.6.2 which is undocumented and subject to
+ // change.
+ //
+ // Note: If str_utf8 contains any characters < 32 || >125 then the function
+ // fails and false is returned.
+ //
+ // Returns: true on success, false otherwise.
+ static bool QuoteStringForRegex(const std::string& str_utf8,
+ std::string* dst);
+
+ // Convert provided path into a "canonical" path matching what the Sandbox
+ // expects i.e. one without symlinks.
+ // This path is not necessarily unique e.g. in the face of hardlinks.
+ static void GetCanonicalSandboxPath(FilePath* path);
+
+ FRIEND_TEST(MacDirAccessSandboxTest, StringEscape);
+ FRIEND_TEST(MacDirAccessSandboxTest, RegexEscape);
+ FRIEND_TEST(MacDirAccessSandboxTest, SandboxAccess);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Sandbox);
+};
+
+} // namespace sandbox
+
+#endif // CONTENT_COMMON_SANDBOX_MAC_H_
diff --git a/content/common/sandbox_mac.mm b/content/common/sandbox_mac.mm
new file mode 100644
index 0000000..1d1d36f
--- /dev/null
+++ b/content/common/sandbox_mac.mm
@@ -0,0 +1,579 @@
+// Copyright (c) 2011 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/common/sandbox_mac.h"
+
+#import <Cocoa/Cocoa.h>
+#import <OpenGL/OpenGL.h>
+
+extern "C" {
+#include <sandbox.h>
+}
+#include <signal.h>
+#include <sys/param.h>
+
+#include "app/gfx/gl/gl_context.h"
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/mac/mac_util.h"
+#include "base/rand_util_c.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/sys_info.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "content/common/chrome_application_mac.h"
+#include "content/common/content_switches.h"
+#include "unicode/uchar.h"
+
+namespace {
+
+// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful,
+// returns true and appends the escape sequence to |dst|.
+bool EscapeSingleChar(char c, std::string* dst) {
+ const char *append = NULL;
+ switch (c) {
+ case '\b':
+ append = "\\b";
+ break;
+ case '\f':
+ append = "\\f";
+ break;
+ case '\n':
+ append = "\\n";
+ break;
+ case '\r':
+ append = "\\r";
+ break;
+ case '\t':
+ append = "\\t";
+ break;
+ case '\\':
+ append = "\\\\";
+ break;
+ case '"':
+ append = "\\\"";
+ break;
+ }
+
+ if (!append) {
+ return false;
+ }
+
+ dst->append(append);
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+
+// static
+bool Sandbox::QuotePlainString(const std::string& src_utf8, std::string* dst) {
+ dst->clear();
+
+ const char* src = src_utf8.c_str();
+ int32_t length = src_utf8.length();
+ int32_t position = 0;
+ while (position < length) {
+ UChar32 c;
+ U8_NEXT(src, position, length, c); // Macro increments |position|.
+ DCHECK_GE(c, 0);
+ if (c < 0)
+ return false;
+
+ if (c < 128) { // EscapeSingleChar only handles ASCII.
+ char as_char = static_cast<char>(c);
+ if (EscapeSingleChar(as_char, dst)) {
+ continue;
+ }
+ }
+
+ if (c < 32 || c > 126) {
+ // Any characters that aren't printable ASCII get the \u treatment.
+ unsigned int as_uint = static_cast<unsigned int>(c);
+ base::StringAppendF(dst, "\\u%04X", as_uint);
+ continue;
+ }
+
+ // If we got here we know that the character in question is strictly
+ // in the ASCII range so there's no need to do any kind of encoding
+ // conversion.
+ dst->push_back(static_cast<char>(c));
+ }
+ return true;
+}
+
+// static
+bool Sandbox::QuoteStringForRegex(const std::string& str_utf8,
+ std::string* dst) {
+ // Characters with special meanings in sandbox profile syntax.
+ const char regex_special_chars[] = {
+ '\\',
+
+ // Metacharacters
+ '^',
+ '.',
+ '[',
+ ']',
+ '$',
+ '(',
+ ')',
+ '|',
+
+ // Quantifiers
+ '*',
+ '+',
+ '?',
+ '{',
+ '}',
+ };
+
+ // Anchor regex at start of path.
+ dst->assign("^");
+
+ const char* src = str_utf8.c_str();
+ int32_t length = str_utf8.length();
+ int32_t position = 0;
+ while (position < length) {
+ UChar32 c;
+ U8_NEXT(src, position, length, c); // Macro increments |position|.
+ DCHECK_GE(c, 0);
+ if (c < 0)
+ return false;
+
+ // The Mac sandbox regex parser only handles printable ASCII characters.
+ // 33 >= c <= 126
+ if (c < 32 || c > 125) {
+ return false;
+ }
+
+ for (size_t i = 0; i < arraysize(regex_special_chars); ++i) {
+ if (c == regex_special_chars[i]) {
+ dst->push_back('\\');
+ break;
+ }
+ }
+
+ dst->push_back(static_cast<char>(c));
+ }
+
+ // Make sure last element of path is interpreted as a directory. Leaving this
+ // off would allow access to files if they start with the same name as the
+ // directory.
+ dst->append("(/|$)");
+
+ return true;
+}
+
+// Warm up System APIs that empirically need to be accessed before the Sandbox
+// is turned on.
+// This method is layed out in blocks, each one containing a separate function
+// that needs to be warmed up. The OS version on which we found the need to
+// enable the function is also noted.
+// This function is tested on the following OS versions:
+// 10.5.6, 10.6.0
+
+// static
+void Sandbox::SandboxWarmup(SandboxProcessType sandbox_type) {
+ base::mac::ScopedNSAutoreleasePool scoped_pool;
+
+ { // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6
+ base::mac::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+
+ // Allocate a 1x1 image.
+ char data[4];
+ base::mac::ScopedCFTypeRef<CGContextRef> context(
+ CGBitmapContextCreate(data, 1, 1, 8, 1 * 4,
+ rgb_colorspace,
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host));
+
+ // Load in the color profiles we'll need (as a side effect).
+ (void) base::mac::GetSRGBColorSpace();
+ (void) base::mac::GetSystemColorSpace();
+
+ // CGColorSpaceCreateSystemDefaultCMYK - 10.6
+ base::mac::ScopedCFTypeRef<CGColorSpaceRef> cmyk_colorspace(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericCMYK));
+ }
+
+ { // [-NSColor colorUsingColorSpaceName] - 10.5.6
+ NSColor* color = [NSColor controlTextColor];
+ [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ }
+
+ { // localtime() - 10.5.6
+ time_t tv = {0};
+ localtime(&tv);
+ }
+
+ { // Gestalt() tries to read /System/Library/CoreServices/SystemVersion.plist
+ // on 10.5.6
+ int32 tmp;
+ base::SysInfo::OperatingSystemVersionNumbers(&tmp, &tmp, &tmp);
+ }
+
+ { // CGImageSourceGetStatus() - 10.6
+ // Create a png with just enough data to get everything warmed up...
+ char png_header[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
+ NSData* data = [NSData dataWithBytes:png_header
+ length:arraysize(png_header)];
+ base::mac::ScopedCFTypeRef<CGImageSourceRef> img(
+ CGImageSourceCreateWithData((CFDataRef)data,
+ NULL));
+ CGImageSourceGetStatus(img);
+ }
+
+ {
+ // Allow access to /dev/urandom.
+ GetUrandomFD();
+ }
+
+ // Process-type dependent warm-up.
+ switch (sandbox_type) {
+ case SANDBOX_TYPE_GPU:
+ { // GPU-related stuff is very slow without this, probably because
+ // the sandbox prevents loading graphics drivers or some such.
+ CGLPixelFormatAttribute attribs[] = { (CGLPixelFormatAttribute)0 };
+ CGLPixelFormatObj format;
+ GLint n;
+ CGLChoosePixelFormat(attribs, &format, &n);
+ if (format)
+ CGLReleasePixelFormat(format);
+ }
+
+ {
+ // Preload either the desktop GL or the osmesa so, depending on the
+ // --use-gl flag.
+ gfx::GLContext::InitializeOneOff();
+ }
+ break;
+
+ default:
+ // To shut up a gcc warning.
+ break;
+ }
+}
+
+// static
+NSString* Sandbox::BuildAllowDirectoryAccessSandboxString(
+ const FilePath& allowed_dir,
+ SandboxVariableSubstitions* substitutions) {
+ // A whitelist is used to determine which directories can be statted
+ // This means that in the case of an /a/b/c/d/ directory, we may be able to
+ // stat the leaf directory, but not it's parent.
+ // The extension code in Chrome calls realpath() which fails if it can't call
+ // stat() on one of the parent directories in the path.
+ // The solution to this is to allow statting the parent directories themselves
+ // but not their contents. We need to add a separate rule for each parent
+ // directory.
+
+ // The sandbox only understands "real" paths. This resolving step is
+ // needed so the caller doesn't need to worry about things like /var
+ // being a link to /private/var (like in the paths CreateNewTempDirectory()
+ // returns).
+ FilePath allowed_dir_canonical(allowed_dir);
+ GetCanonicalSandboxPath(&allowed_dir_canonical);
+
+ // Collect a list of all parent directories.
+ FilePath last_path = allowed_dir_canonical;
+ std::vector<FilePath> subpaths;
+ for (FilePath path = allowed_dir_canonical.DirName();
+ path.value() != last_path.value();
+ path = path.DirName()) {
+ subpaths.push_back(path);
+ last_path = path;
+ }
+
+ // Iterate through all parents and allow stat() on them explicitly.
+ NSString* sandbox_command = @"(allow file-read-metadata ";
+ for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
+ i != subpaths.rend();
+ ++i) {
+ std::string subdir_escaped;
+ if (!QuotePlainString(i->value(), &subdir_escaped)) {
+ LOG(FATAL) << "String quoting failed " << i->value();
+ return nil;
+ }
+
+ NSString* subdir_escaped_ns =
+ base::SysUTF8ToNSString(subdir_escaped.c_str());
+ sandbox_command =
+ [sandbox_command stringByAppendingFormat:@"(literal \"%@\")",
+ subdir_escaped_ns];
+ }
+
+ // Finally append the leaf directory. Unlike it's parents (for which only
+ // stat() should be allowed), the leaf directory needs full access.
+ (*substitutions)["ALLOWED_DIR"] =
+ SandboxSubstring(allowed_dir_canonical.value(),
+ SandboxSubstring::REGEX);
+ sandbox_command =
+ [sandbox_command
+ stringByAppendingString:@") (allow file-read* file-write*"
+ " (regex #\"@ALLOWED_DIR@\") )"];
+ return sandbox_command;
+}
+
+// Load the appropriate template for the given sandbox type.
+// Returns the template as an NSString or nil on error.
+NSString* LoadSandboxTemplate(Sandbox::SandboxProcessType sandbox_type) {
+ // We use a custom sandbox definition file to lock things down as
+ // tightly as possible.
+ NSString* sandbox_config_filename = nil;
+ switch (sandbox_type) {
+ case Sandbox::SANDBOX_TYPE_RENDERER:
+ sandbox_config_filename = @"renderer";
+ break;
+ case Sandbox::SANDBOX_TYPE_WORKER:
+ sandbox_config_filename = @"worker";
+ break;
+ case Sandbox::SANDBOX_TYPE_UTILITY:
+ sandbox_config_filename = @"utility";
+ break;
+ case Sandbox::SANDBOX_TYPE_NACL_LOADER:
+ // The Native Client loader is used for safeguarding the user's
+ // untrusted code within Native Client.
+ sandbox_config_filename = @"nacl_loader";
+ break;
+ case Sandbox::SANDBOX_TYPE_GPU:
+ sandbox_config_filename = @"gpu";
+ break;
+ default:
+ NOTREACHED();
+ return nil;
+ }
+
+ // Read in the sandbox profile and the common prefix file.
+ NSString* common_sandbox_prefix_path =
+ [base::mac::MainAppBundle() pathForResource:@"common"
+ ofType:@"sb"];
+ NSString* common_sandbox_prefix_data =
+ [NSString stringWithContentsOfFile:common_sandbox_prefix_path
+ encoding:NSUTF8StringEncoding
+ error:NULL];
+
+ if (!common_sandbox_prefix_data) {
+ LOG(FATAL) << "Failed to find the sandbox profile on disk "
+ << [common_sandbox_prefix_path fileSystemRepresentation];
+ return nil;
+ }
+
+ NSString* sandbox_profile_path =
+ [base::mac::MainAppBundle() pathForResource:sandbox_config_filename
+ ofType:@"sb"];
+ NSString* sandbox_data =
+ [NSString stringWithContentsOfFile:sandbox_profile_path
+ encoding:NSUTF8StringEncoding
+ error:NULL];
+
+ if (!sandbox_data) {
+ LOG(FATAL) << "Failed to find the sandbox profile on disk "
+ << [sandbox_profile_path fileSystemRepresentation];
+ return nil;
+ }
+
+ // Prefix sandbox_data with common_sandbox_prefix_data.
+ return [common_sandbox_prefix_data stringByAppendingString:sandbox_data];
+}
+
+// Retrieve OS X version, output parameters are self explanatory.
+void GetOSVersion(bool* snow_leopard_or_higher) {
+ int32 major_version, minor_version, bugfix_version;
+ base::SysInfo::OperatingSystemVersionNumbers(&major_version,
+ &minor_version,
+ &bugfix_version);
+ *snow_leopard_or_higher =
+ (major_version > 10 || (major_version == 10 && minor_version >= 6));
+}
+
+// static
+bool Sandbox::PostProcessSandboxProfile(
+ NSString* sandbox_template,
+ NSArray* comments_to_remove,
+ SandboxVariableSubstitions& substitutions,
+ std::string *final_sandbox_profile_str) {
+ NSString* sandbox_data = [[sandbox_template copy] autorelease];
+
+ // Remove comments, e.g. ;10.6_ONLY .
+ for (NSString* to_remove in comments_to_remove) {
+ sandbox_data = [sandbox_data stringByReplacingOccurrencesOfString:to_remove
+ withString:@""];
+ }
+
+ // Split string on "@" characters.
+ std::vector<std::string> raw_sandbox_pieces;
+ if (Tokenize([sandbox_data UTF8String], "@", &raw_sandbox_pieces) == 0) {
+ LOG(FATAL) << "Bad Sandbox profile, should contain at least one token ("
+ << [sandbox_data UTF8String]
+ << ")";
+ return false;
+ }
+
+ // Iterate over string pieces and substitute variables, escaping as necessary.
+ size_t output_string_length = 0;
+ std::vector<std::string> processed_sandbox_pieces(raw_sandbox_pieces.size());
+ for (std::vector<std::string>::iterator it = raw_sandbox_pieces.begin();
+ it != raw_sandbox_pieces.end();
+ ++it) {
+ std::string new_piece;
+ SandboxVariableSubstitions::iterator replacement_it =
+ substitutions.find(*it);
+ if (replacement_it == substitutions.end()) {
+ new_piece = *it;
+ } else {
+ // Found something to substitute.
+ SandboxSubstring& replacement = replacement_it->second;
+ switch (replacement.type()) {
+ case SandboxSubstring::PLAIN:
+ new_piece = replacement.value();
+ break;
+
+ case SandboxSubstring::LITERAL:
+ QuotePlainString(replacement.value(), &new_piece);
+ break;
+
+ case SandboxSubstring::REGEX:
+ QuoteStringForRegex(replacement.value(), &new_piece);
+ break;
+ }
+ }
+ output_string_length += new_piece.size();
+ processed_sandbox_pieces.push_back(new_piece);
+ }
+
+ // Build final output string.
+ final_sandbox_profile_str->reserve(output_string_length);
+
+ for (std::vector<std::string>::iterator it = processed_sandbox_pieces.begin();
+ it != processed_sandbox_pieces.end();
+ ++it) {
+ final_sandbox_profile_str->append(*it);
+ }
+ return true;
+}
+
+
+// Turns on the OS X sandbox for this process.
+
+// static
+bool Sandbox::EnableSandbox(SandboxProcessType sandbox_type,
+ const FilePath& allowed_dir) {
+ // Sanity - currently only SANDBOX_TYPE_UTILITY supports a directory being
+ // passed in.
+ if (sandbox_type != SANDBOX_TYPE_UTILITY) {
+ DCHECK(allowed_dir.empty())
+ << "Only SANDBOX_TYPE_UTILITY allows a custom directory parameter.";
+ }
+
+ NSString* sandbox_data = LoadSandboxTemplate(sandbox_type);
+ if (!sandbox_data) {
+ return false;
+ }
+
+ SandboxVariableSubstitions substitutions;
+ if (!allowed_dir.empty()) {
+ // Add the sandbox commands necessary to access the given directory.
+ // Note: this function must be called before PostProcessSandboxProfile()
+ // since the string it inserts contains variables that need substitution.
+ NSString* allowed_dir_sandbox_command =
+ BuildAllowDirectoryAccessSandboxString(allowed_dir, &substitutions);
+
+ if (allowed_dir_sandbox_command) { // May be nil if function fails.
+ sandbox_data = [sandbox_data
+ stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS"
+ withString:allowed_dir_sandbox_command];
+ }
+ }
+
+ NSMutableArray* tokens_to_remove = [NSMutableArray array];
+
+ // Enable verbose logging if enabled on the command line. (See common.sb
+ // for details).
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ bool enable_logging =
+ command_line->HasSwitch(switches::kEnableSandboxLogging);;
+ if (enable_logging) {
+ [tokens_to_remove addObject:@";ENABLE_LOGGING"];
+ }
+
+ bool snow_leopard_or_higher;
+ GetOSVersion(&snow_leopard_or_higher);
+
+ // Without this, the sandbox will print a message to the system log every
+ // time it denies a request. This floods the console with useless spew. The
+ // (with no-log) syntax is only supported on 10.6+
+ if (snow_leopard_or_higher && !enable_logging) {
+ substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] =
+ SandboxSubstring("(with no-log)");
+ } else {
+ substitutions["DISABLE_SANDBOX_DENIAL_LOGGING"] = SandboxSubstring("");
+ }
+
+ // Splice the path of the user's home directory into the sandbox profile
+ // (see renderer.sb for details).
+ std::string home_dir = base::SysNSStringToUTF8(NSHomeDirectory());
+
+ FilePath home_dir_canonical(home_dir);
+ GetCanonicalSandboxPath(&home_dir_canonical);
+
+ substitutions["USER_HOMEDIR_AS_LITERAL"] =
+ SandboxSubstring(home_dir_canonical.value(),
+ SandboxSubstring::LITERAL);
+
+ if (snow_leopard_or_higher) {
+ // 10.6-only Sandbox rules.
+ [tokens_to_remove addObject:@";10.6_ONLY"];
+ } else {
+ // Sandbox rules only for versions before 10.6.
+ [tokens_to_remove addObject:@";BEFORE_10.6"];
+ }
+
+ // All information needed to assemble the final profile has been collected.
+ // Merge it all together.
+ std::string final_sandbox_profile_str;
+ if (!PostProcessSandboxProfile(sandbox_data, tokens_to_remove, substitutions,
+ &final_sandbox_profile_str)) {
+ return false;
+ }
+
+ // Initialize sandbox.
+ char* error_buff = NULL;
+ int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff);
+ bool success = (error == 0 && error_buff == NULL);
+ LOG_IF(FATAL, !success) << "Failed to initialize sandbox: "
+ << error
+ << " "
+ << error_buff;
+ sandbox_free_error(error_buff);
+ return success;
+}
+
+// static
+void Sandbox::GetCanonicalSandboxPath(FilePath* path) {
+ int fd = HANDLE_EINTR(open(path->value().c_str(), O_RDONLY));
+ if (fd < 0) {
+ PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: "
+ << path->value();
+ return;
+ }
+ file_util::ScopedFD file_closer(&fd);
+
+ FilePath::CharType canonical_path[MAXPATHLEN];
+ if (HANDLE_EINTR(fcntl(fd, F_GETPATH, canonical_path)) != 0) {
+ PLOG(FATAL) << "GetCanonicalSandboxPath() failed for: "
+ << path->value();
+ return;
+ }
+
+ *path = FilePath(canonical_path);
+}
+
+} // namespace sandbox
diff --git a/content/common/sandbox_mac_diraccess_unittest.mm b/content/common/sandbox_mac_diraccess_unittest.mm
new file mode 100644
index 0000000..8aad63b
--- /dev/null
+++ b/content/common/sandbox_mac_diraccess_unittest.mm
@@ -0,0 +1,304 @@
+// Copyright (c) 2011 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.
+
+#import <Cocoa/Cocoa.h>
+#include <dirent.h>
+
+extern "C" {
+#include <sandbox.h>
+}
+
+#include "base/file_util.h"
+#include "base/file_path.h"
+#include "base/test/multiprocess_test.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "content/common/sandbox_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace {
+
+static const char* kSandboxAccessPathKey = "sandbox_dir";
+static const char* kDeniedSuffix = "_denied";
+
+} // namespace
+
+// Tests need to be in the same namespace as the sandbox::Sandbox class to be
+// useable with FRIEND_TEST() declaration.
+namespace sandbox {
+
+class MacDirAccessSandboxTest : public base::MultiProcessTest {
+ public:
+ bool CheckSandbox(const std::string& directory_to_try) {
+ setenv(kSandboxAccessPathKey, directory_to_try.c_str(), 1);
+ base::ProcessHandle child_process = SpawnChild("mac_sandbox_path_access",
+ false);
+ int code = -1;
+ if (!base::WaitForExitCode(child_process, &code)) {
+ LOG(WARNING) << "base::WaitForExitCode failed";
+ return false;
+ }
+ return code == 0;
+ }
+};
+
+TEST_F(MacDirAccessSandboxTest, StringEscape) {
+ const struct string_escape_test_data {
+ const char* to_escape;
+ const char* escaped;
+ } string_escape_cases[] = {
+ {"", ""},
+ {"\b\f\n\r\t\\\"", "\\b\\f\\n\\r\\t\\\\\\\""},
+ {"/'", "/'"},
+ {"sandwich", "sandwich"},
+ {"(sandwich)", "(sandwich)"},
+ {"^\u2135.\u2136$", "^\\u2135.\\u2136$"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(string_escape_cases); ++i) {
+ std::string out;
+ std::string in(string_escape_cases[i].to_escape);
+ EXPECT_TRUE(Sandbox::QuotePlainString(in, &out));
+ EXPECT_EQ(string_escape_cases[i].escaped, out);
+ }
+}
+
+TEST_F(MacDirAccessSandboxTest, RegexEscape) {
+ const std::string kSandboxEscapeSuffix("(/|$)");
+ const struct regex_test_data {
+ const wchar_t *to_escape;
+ const char* escaped;
+ } regex_cases[] = {
+ {L"", ""},
+ {L"/'", "/'"}, // / & ' characters don't need escaping.
+ {L"sandwich", "sandwich"},
+ {L"(sandwich)", "\\(sandwich\\)"},
+ };
+
+ // Check that all characters whose values are smaller than 32 [1F] are
+ // rejected by the regex escaping code.
+ {
+ std::string out;
+ char fail_string[] = {31, 0};
+ char ok_string[] = {32, 0};
+ EXPECT_FALSE(Sandbox::QuoteStringForRegex(fail_string, &out));
+ EXPECT_TRUE(Sandbox::QuoteStringForRegex(ok_string, &out));
+ }
+
+ // Check that all characters whose values are larger than 126 [7E] are
+ // rejected by the regex escaping code.
+ {
+ std::string out;
+ EXPECT_TRUE(Sandbox::QuoteStringForRegex("}", &out)); // } == 0x7D == 125
+ EXPECT_FALSE(Sandbox::QuoteStringForRegex("~", &out)); // ~ == 0x7E == 126
+ EXPECT_FALSE(
+ Sandbox::QuoteStringForRegex(WideToUTF8(L"^\u2135.\u2136$"), &out));
+ }
+
+ {
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(regex_cases); ++i) {
+ std::string out;
+ std::string in = WideToUTF8(regex_cases[i].to_escape);
+ EXPECT_TRUE(Sandbox::QuoteStringForRegex(in, &out));
+ std::string expected("^");
+ expected.append(regex_cases[i].escaped);
+ expected.append(kSandboxEscapeSuffix);
+ EXPECT_EQ(expected, out);
+ }
+ }
+
+ {
+ std::string in_utf8("\\^.$|()[]*+?{}");
+ std::string expected;
+ expected.push_back('^');
+ for (size_t i = 0; i < in_utf8.length(); ++i) {
+ expected.push_back('\\');
+ expected.push_back(in_utf8[i]);
+ }
+ expected.append(kSandboxEscapeSuffix);
+
+ std::string out;
+ EXPECT_TRUE(Sandbox::QuoteStringForRegex(in_utf8, &out));
+ EXPECT_EQ(expected, out);
+
+ }
+}
+
+// A class to handle auto-deleting a directory.
+class ScopedDirectoryDelete {
+ public:
+ inline void operator()(FilePath* x) const {
+ if (x) {
+ file_util::Delete(*x, true);
+ }
+ }
+};
+
+typedef scoped_ptr_malloc<FilePath, ScopedDirectoryDelete> ScopedDirectory;
+
+TEST_F(MacDirAccessSandboxTest, SandboxAccess) {
+ using file_util::CreateDirectory;
+
+ FilePath tmp_dir;
+ ASSERT_TRUE(file_util::CreateNewTempDirectory("", &tmp_dir));
+ // This step is important on OS X since the sandbox only understands "real"
+ // paths and the paths CreateNewTempDirectory() returns are empirically in
+ // /var which is a symlink to /private/var .
+ Sandbox::GetCanonicalSandboxPath(&tmp_dir);
+ ScopedDirectory cleanup(&tmp_dir);
+
+ const char* sandbox_dir_cases[] = {
+ "simple_dir_name",
+ "^hello++ $", // Regex.
+ "\\^.$|()[]*+?{}", // All regex characters.
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(sandbox_dir_cases); ++i) {
+ const char* sandbox_dir_name = sandbox_dir_cases[i];
+ FilePath sandbox_dir = tmp_dir.Append(sandbox_dir_name);
+ ASSERT_TRUE(CreateDirectory(sandbox_dir));
+ ScopedDirectory cleanup_sandbox(&sandbox_dir);
+
+ // Create a sibling directory of the sandbox dir, whose name has sandbox dir
+ // as a substring but to which access is denied.
+ std::string sibling_sandbox_dir_name_denied =
+ std::string(sandbox_dir_cases[i]) + kDeniedSuffix;
+ FilePath sibling_sandbox_dir = tmp_dir.Append(
+ sibling_sandbox_dir_name_denied.c_str());
+ ASSERT_TRUE(CreateDirectory(sibling_sandbox_dir));
+ ScopedDirectory cleanup_sandbox_sibling(&sibling_sandbox_dir);
+
+ EXPECT_TRUE(CheckSandbox(sandbox_dir.value()));
+ }
+}
+
+MULTIPROCESS_TEST_MAIN(mac_sandbox_path_access) {
+ char *sandbox_allowed_dir = getenv(kSandboxAccessPathKey);
+ if (!sandbox_allowed_dir)
+ return -1;
+
+ // Build up a sandbox profile that only allows access to a single directory.
+ NSString *sandbox_profile =
+ @"(version 1)" \
+ "(deny default)" \
+ "(allow signal (target self))" \
+ "(allow sysctl-read)" \
+ ";ENABLE_DIRECTORY_ACCESS";
+
+ std::string allowed_dir(sandbox_allowed_dir);
+ Sandbox::SandboxVariableSubstitions substitutions;
+ NSString* allow_dir_sandbox_code =
+ Sandbox::BuildAllowDirectoryAccessSandboxString(
+ FilePath(sandbox_allowed_dir),
+ &substitutions);
+ sandbox_profile = [sandbox_profile
+ stringByReplacingOccurrencesOfString:@";ENABLE_DIRECTORY_ACCESS"
+ withString:allow_dir_sandbox_code];
+
+ std::string final_sandbox_profile_str;
+ if (!Sandbox::PostProcessSandboxProfile(sandbox_profile,
+ [NSArray array],
+ substitutions,
+ &final_sandbox_profile_str)) {
+ LOG(ERROR) << "Call to PostProcessSandboxProfile() failed";
+ return -1;
+ }
+
+ // Enable Sandbox.
+ char* error_buff = NULL;
+ int error = sandbox_init(final_sandbox_profile_str.c_str(), 0, &error_buff);
+ if (error == -1) {
+ LOG(ERROR) << "Failed to Initialize Sandbox: " << error_buff;
+ return -1;
+ }
+ sandbox_free_error(error_buff);
+
+ // Test Sandbox.
+
+ // We should be able to list the contents of the sandboxed directory.
+ DIR *file_list = NULL;
+ file_list = opendir(sandbox_allowed_dir);
+ if (!file_list) {
+ PLOG(ERROR) << "Sandbox overly restrictive: call to opendir("
+ << sandbox_allowed_dir
+ << ") failed";
+ return -1;
+ }
+ closedir(file_list);
+
+ // Test restrictions on accessing files.
+ FilePath allowed_dir_path(sandbox_allowed_dir);
+ FilePath allowed_file = allowed_dir_path.Append("ok_to_write");
+ FilePath denied_file1 = allowed_dir_path.DirName().Append("cant_access");
+
+ // Try to write a file who's name has the same prefix as the directory we
+ // allow access to.
+ FilePath basename = allowed_dir_path.BaseName();
+ FilePath allowed_parent_dir = allowed_dir_path.DirName();
+ std::string tricky_filename = basename.value() + "123";
+ FilePath denied_file2 = allowed_parent_dir.Append(tricky_filename);
+
+ if (open(allowed_file.value().c_str(), O_WRONLY | O_CREAT) <= 0) {
+ PLOG(ERROR) << "Sandbox overly restrictive: failed to write ("
+ << allowed_file.value()
+ << ")";
+ return -1;
+ }
+
+ // Test that we deny access to a sibling of the sandboxed directory whose
+ // name has the sandboxed directory name as a substring. e.g. if the sandbox
+ // directory is /foo/baz then test /foo/baz_denied.
+ {
+ struct stat tmp_stat_info;
+ std::string denied_sibling =
+ std::string(sandbox_allowed_dir) + kDeniedSuffix;
+ if (stat(denied_sibling.c_str(), &tmp_stat_info) > 0) {
+ PLOG(ERROR) << "Sandbox breach: was able to stat ("
+ << denied_sibling.c_str()
+ << ")";
+ return -1;
+ }
+ }
+
+ // Test that we can stat parent directories of the "allowed" directory.
+ {
+ struct stat tmp_stat_info;
+ if (stat(allowed_parent_dir.value().c_str(), &tmp_stat_info) != 0) {
+ PLOG(ERROR) << "Sandbox overly restrictive: unable to stat ("
+ << allowed_parent_dir.value()
+ << ")";
+ return -1;
+ }
+ }
+
+ // Test that we can't stat files outside the "allowed" directory.
+ {
+ struct stat tmp_stat_info;
+ if (stat(denied_file1.value().c_str(), &tmp_stat_info) > 0) {
+ PLOG(ERROR) << "Sandbox breach: was able to stat ("
+ << denied_file1.value()
+ << ")";
+ return -1;
+ }
+ }
+
+ if (open(denied_file1.value().c_str(), O_WRONLY | O_CREAT) > 0) {
+ PLOG(ERROR) << "Sandbox breach: was able to write ("
+ << denied_file1.value()
+ << ")";
+ return -1;
+ }
+
+ if (open(denied_file2.value().c_str(), O_WRONLY | O_CREAT) > 0) {
+ PLOG(ERROR) << "Sandbox breach: was able to write ("
+ << denied_file2.value()
+ << ")";
+ return -1;
+ }
+
+ return 0;
+}
+
+} // namespace sandbox
diff --git a/content/common/sandbox_mac_fontloading_unittest.mm b/content/common/sandbox_mac_fontloading_unittest.mm
new file mode 100644
index 0000000..7df1bfe
--- /dev/null
+++ b/content/common/sandbox_mac_fontloading_unittest.mm
@@ -0,0 +1,179 @@
+// Copyright (c) 2011 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "content/common/font_loader_mac.h"
+#include "content/common/sandbox_mac_unittest_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using sandboxtest::MacSandboxTest;
+using sandbox::Sandbox;
+
+bool CGFontFromFontContainer(ATSFontContainerRef container, CGFontRef* out) {
+ // Count the number of fonts that were loaded.
+ ItemCount fontCount = 0;
+ OSStatus err = ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 0,
+ NULL, &fontCount);
+
+ if (err != noErr || fontCount < 1) {
+ return false;
+ }
+
+ // Load font from container.
+ ATSFontRef font_ref_ats = 0;
+ ATSFontFindFromContainer(container, kATSOptionFlagsDefault, 1,
+ &font_ref_ats, NULL);
+
+ if (!font_ref_ats) {
+ return false;
+ }
+
+ // Convert to cgFont.
+ CGFontRef font_ref_cg = CGFontCreateWithPlatformFont(&font_ref_ats);
+
+ if (!font_ref_cg) {
+ return false;
+ }
+
+ *out = font_ref_cg;
+ return true;
+}
+
+class ScopedFontContainer {
+ public:
+ explicit ScopedFontContainer(ATSFontContainerRef ref)
+ : container_ref(ref) {}
+
+ ~ScopedFontContainer() {
+ ATSFontDeactivate(container_ref, NULL, kATSOptionFlagsDefault);
+ }
+
+ ATSFontContainerRef container_ref;
+};
+
+class FontLoadingTestCase : public sandboxtest::MacSandboxTestCase {
+ public:
+ FontLoadingTestCase() : font_data_length_(-1) {}
+ virtual bool BeforeSandboxInit();
+ virtual bool SandboxedTest();
+ private:
+ scoped_ptr<base::SharedMemory> font_shmem_;
+ size_t font_data_length_;
+};
+REGISTER_SANDBOX_TEST_CASE(FontLoadingTestCase);
+
+
+// Load raw font data into shared memory object.
+bool FontLoadingTestCase::BeforeSandboxInit() {
+ std::string font_data;
+ if (!file_util::ReadFileToString(FilePath(test_data_.c_str()), &font_data)) {
+ LOG(ERROR) << "Failed to read font data from file (" << test_data_ << ")";
+ return false;
+ }
+
+ font_data_length_ = font_data.length();
+ if (font_data_length_ <= 0) {
+ LOG(ERROR) << "No font data: " << font_data_length_;
+ return false;
+ }
+
+ font_shmem_.reset(new base::SharedMemory);
+ if (!font_shmem_.get()) {
+ LOG(ERROR) << "Failed to create shared memory object.";
+ return false;
+ }
+
+ if (!font_shmem_->CreateAndMapAnonymous(font_data_length_)) {
+ LOG(ERROR) << "SharedMemory::Create failed";
+ return false;
+ }
+
+ memcpy(font_shmem_->memory(), font_data.c_str(), font_data_length_);
+ if (!font_shmem_->Unmap()) {
+ LOG(ERROR) << "SharedMemory::Unmap failed";
+ return false;
+ }
+ return true;
+}
+
+bool FontLoadingTestCase::SandboxedTest() {
+ base::SharedMemoryHandle shmem_handle;
+ if (!font_shmem_->ShareToProcess(NULL, &shmem_handle)) {
+ LOG(ERROR) << "SharedMemory::ShareToProcess failed";
+ return false;
+ }
+
+ ATSFontContainerRef font_container;
+ if (!FontLoader::ATSFontContainerFromBuffer(shmem_handle, font_data_length_,
+ &font_container)) {
+ LOG(ERROR) << "Call to CreateCGFontFromBuffer() failed";
+ return false;
+ }
+
+ // Unload the font container when done.
+ ScopedFontContainer scoped_unloader(font_container);
+
+ CGFontRef cg_font_ref;
+ if (!CGFontFromFontContainer(font_container, &cg_font_ref)) {
+ LOG(ERROR) << "CGFontFromFontContainer failed";
+ return false;
+ }
+
+ if (!cg_font_ref) {
+ LOG(ERROR) << "Got NULL CGFontRef";
+ return false;
+ }
+ base::mac::ScopedCFTypeRef<CGFontRef> cgfont(cg_font_ref);
+
+ CTFontRef ct_font_ref =
+ CTFontCreateWithGraphicsFont(cgfont.get(), 16.0, NULL, NULL);
+ base::mac::ScopedCFTypeRef<CTFontRef> ctfont(ct_font_ref);
+
+ if (!ct_font_ref) {
+ LOG(ERROR) << "CTFontCreateWithGraphicsFont() failed";
+ return false;
+ }
+
+ // Do something with the font to make sure it's loaded.
+ CGFloat cap_height = CTFontGetCapHeight(ct_font_ref);
+
+ if (cap_height <= 0.0) {
+ LOG(ERROR) << "Got bad value for CTFontGetCapHeight " << cap_height;
+ return false;
+ }
+
+ return true;
+}
+
+TEST_F(MacSandboxTest, FontLoadingTest) {
+ FilePath temp_file_path;
+ FILE* temp_file = file_util::CreateAndOpenTemporaryFile(&temp_file_path);
+ ASSERT_TRUE(temp_file);
+ file_util::ScopedFILE temp_file_closer(temp_file);
+
+ base::SharedMemory font_data;
+ uint32 font_data_size;
+ NSFont* srcFont = [NSFont fontWithName:@"Geeza Pro" size:16.0];
+ EXPECT_TRUE(FontLoader::LoadFontIntoBuffer(srcFont,
+ &font_data, &font_data_size));
+ EXPECT_GT(font_data_size, 0U);
+
+ file_util::WriteFileDescriptor(fileno(temp_file),
+ static_cast<const char *>(font_data.memory()), font_data_size);
+
+ ASSERT_TRUE(RunTestInSandbox(Sandbox::SANDBOX_TYPE_RENDERER,
+ "FontLoadingTestCase", temp_file_path.value().c_str()));
+ temp_file_closer.reset();
+ ASSERT_TRUE(file_util::Delete(temp_file_path, false));
+}
+
+} // namespace
diff --git a/content/common/sandbox_mac_system_access_unittest.mm b/content/common/sandbox_mac_system_access_unittest.mm
new file mode 100644
index 0000000..46d7596
--- /dev/null
+++ b/content/common/sandbox_mac_system_access_unittest.mm
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 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.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/logging.h"
+#include "base/sys_string_conversions.h"
+#include "content/common/sandbox_mac.h"
+#include "content/common/sandbox_mac_unittest_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using sandboxtest::MacSandboxTest;
+
+//--------------------- Clipboard Sandboxing ----------------------
+// Test case for checking sandboxing of clipboard access.
+class MacSandboxedClipboardTestCase : public sandboxtest::MacSandboxTestCase {
+ public:
+ MacSandboxedClipboardTestCase();
+ virtual ~MacSandboxedClipboardTestCase();
+
+ virtual bool SandboxedTest();
+
+ virtual void SetTestData(const char* test_data);
+ private:
+ NSString* clipboard_name_;
+};
+
+REGISTER_SANDBOX_TEST_CASE(MacSandboxedClipboardTestCase);
+
+MacSandboxedClipboardTestCase::MacSandboxedClipboardTestCase() :
+ clipboard_name_(nil) {}
+
+MacSandboxedClipboardTestCase::~MacSandboxedClipboardTestCase() {
+ [clipboard_name_ release];
+}
+
+bool MacSandboxedClipboardTestCase::SandboxedTest() {
+ // Shouldn't be able to open the pasteboard in the sandbox.
+
+ if ([clipboard_name_ length] == 0) {
+ LOG(ERROR) << "Clipboard name is empty";
+ return false;
+ }
+
+ NSPasteboard* pb = [NSPasteboard pasteboardWithName:clipboard_name_];
+ if (pb != nil) {
+ LOG(ERROR) << "Was able to access named clipboard";
+ return false;
+ }
+
+ pb = [NSPasteboard generalPasteboard];
+ if (pb != nil) {
+ LOG(ERROR) << "Was able to access system clipboard";
+ return false;
+ }
+
+ return true;
+}
+
+void MacSandboxedClipboardTestCase::SetTestData(const char* test_data) {
+ clipboard_name_ = [base::SysUTF8ToNSString(test_data) retain];
+}
+
+TEST_F(MacSandboxTest, ClipboardAccess) {
+ NSPasteboard* pb = [NSPasteboard pasteboardWithUniqueName];
+ EXPECT_EQ([[pb types] count], 0U);
+
+ std::string pasteboard_name = base::SysNSStringToUTF8([pb name]);
+ EXPECT_TRUE(RunTestInAllSandboxTypes("MacSandboxedClipboardTestCase",
+ pasteboard_name.c_str()));
+
+ // After executing the test, the clipboard should still be empty.
+ EXPECT_EQ([[pb types] count], 0U);
+}
+
+//--------------------- File Access Sandboxing ----------------------
+// Test case for checking sandboxing of filesystem apis.
+class MacSandboxedFileAccessTestCase : public sandboxtest::MacSandboxTestCase {
+ public:
+ virtual bool SandboxedTest();
+};
+
+REGISTER_SANDBOX_TEST_CASE(MacSandboxedFileAccessTestCase);
+
+bool MacSandboxedFileAccessTestCase::SandboxedTest() {
+ return open("/etc/passwd", O_RDONLY) == -1;
+}
+
+TEST_F(MacSandboxTest, FileAccess) {
+ EXPECT_TRUE(RunTestInAllSandboxTypes("MacSandboxedFileAccessTestCase", NULL));
+}
+
+} // namespace
diff --git a/content/common/sandbox_mac_unittest_helper.h b/content/common/sandbox_mac_unittest_helper.h
new file mode 100644
index 0000000..f228678
--- /dev/null
+++ b/content/common/sandbox_mac_unittest_helper.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_COMMON_SANDBOX_MAC_UNITTEST_HELPER_H_
+#define CONTENT_COMMON_SANDBOX_MAC_UNITTEST_HELPER_H_
+#pragma once
+
+#include "base/test/multiprocess_test.h"
+#include "content/common/sandbox_mac.h"
+
+namespace sandboxtest {
+
+// Helpers for writing unit tests that runs in the context of the Mac sandbox.
+//
+// How to write a sandboxed test:
+// 1. Create a class that inherits from MacSandboxTestCase and overrides
+// its functions to run code before or after the sandbox is initialised in a
+// subprocess.
+// 2. Register the class you just created with the REGISTER_SANDBOX_TEST_CASE()
+// macro.
+// 3. Write a test [using TEST_F()] that inherits from MacSandboxTest and call
+// one of its helper functions to launch the test.
+//
+// Example:
+// class TestCaseThatRunsInSandboxedSubprocess :
+// public sandboxtest::MacSandboxTestCase {
+// public:
+// virtual bool SandboxedTest() {
+// .. test code that runs in sandbox goes here ..
+// return true; // always succeed.
+// }
+// };
+//
+// // Register the test case you just created.
+// REGISTER_SANDBOX_TEST_CASE(TestCaseThatRunsInSandboxedSubprocess);
+//
+// TEST_F(MacSandboxTest, ATest) {
+// EXPECT_TRUE(RunTestInAllSandboxTypes(
+// "TestCaseThatRunsInSandboxedSubprocess",
+// NULL));
+// }
+
+// Base test type with helper functions to spawn a subprocess that exercises
+// a given test in the sandbox.
+class MacSandboxTest : public base::MultiProcessTest {
+ public:
+ // Runs a test specified by |test_name| in a sandbox of the type specified
+ // by |sandbox_type|. |test_data| is a custom string that a test can pass
+ // to the child process runing in the sandbox.
+ // Returns true if the test passes, false if either of the functions in
+ // the corresponding MacSandboxTestCase return false.
+ bool RunTestInSandbox(sandbox::Sandbox::SandboxProcessType sandbox_type,
+ const char* test_name,
+ const char* test_data);
+
+ // Runs the test specified by |test_name| in all the different sandbox types,
+ // one by one.
+ // Returns true if the test passes, false if either of the functions in
+ // the corresponding MacSandboxTestCase return false in any of the spawned
+ // processes.
+ bool RunTestInAllSandboxTypes(const char* test_name,
+ const char* test_data);
+};
+
+// Class to ease writing test cases that run inside the OS X sandbox.
+// This class is instantiated in a subprocess, and allows you to run test code
+// at various stages of execution.
+// Note that you must register the subclass you create with the
+// REGISTER_SANDBOX_TEST_CASE so it's visible to the test driver.
+class MacSandboxTestCase {
+ public:
+ virtual ~MacSandboxTestCase() {}
+
+ // Code that runs in the sandboxed subprocess before the sandbox is
+ // initialized.
+ // Returning false from this function will cause the entire test case to fail.
+ virtual bool BeforeSandboxInit();
+
+ // Code that runs in the sandboxed subprocess when the sandbox has been
+ // enabled.
+ // Returning false from this function will cause the entire test case to fail.
+ virtual bool SandboxedTest() = 0;
+
+ // The data that's passed in the |user_data| parameter of
+ // RunTest[s]InSandbox() is passed to this function.
+ virtual void SetTestData(const char* test_data);
+
+ protected:
+ std::string test_data_;
+};
+
+// Plumbing to support the REGISTER_SANDBOX_TEST_CASE macro.
+namespace internal {
+
+// Register a test case with a given name.
+void AddSandboxTestCase(const char* test_name, MacSandboxTestCase* test_class);
+
+// Construction of this class causes a new entry to be placed in a global
+// map.
+template <class T> struct RegisterSandboxTest {
+ RegisterSandboxTest(const char* test_name) {
+ AddSandboxTestCase(test_name, new T);
+ }
+};
+
+#define REGISTER_SANDBOX_TEST_CASE(class_name) \
+ namespace { \
+ sandboxtest::internal::RegisterSandboxTest<class_name> \
+ register_test##class_name(#class_name); \
+ } // namespace
+
+} // namespace internal
+
+} // namespace sandboxtest
+
+#endif // CONTENT_COMMON_SANDBOX_MAC_UNITTEST_HELPER_H_
diff --git a/content/common/sandbox_mac_unittest_helper.mm b/content/common/sandbox_mac_unittest_helper.mm
new file mode 100644
index 0000000..6b485de
--- /dev/null
+++ b/content/common/sandbox_mac_unittest_helper.mm
@@ -0,0 +1,170 @@
+// Copyright (c) 2011 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/common/sandbox_mac_unittest_helper.h"
+
+extern "C" {
+#include <sandbox.h>
+}
+
+#include <map>
+
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "content/common/sandbox_mac.h"
+#include "testing/multiprocess_func_list.h"
+
+using sandbox::Sandbox;
+
+namespace {
+
+const char* kSandboxTypeKey = "CHROMIUM_SANDBOX_SANDBOX_TYPE";
+const char* kSandboxTestNameKey = "CHROMIUM_SANDBOX_TEST_NAME";
+const char* kTestDataKey = "CHROMIUM_SANDBOX_USER_DATA";
+
+} // namespace
+
+namespace sandboxtest {
+
+// Support infrastructure for REGISTER_SANDBOX_TEST_CASE macro.
+namespace internal {
+
+typedef std::map<std::string,MacSandboxTestCase*> SandboxTestMap;
+
+// A function that returns a common map from string -> test case class.
+SandboxTestMap& GetSandboxTestMap() {
+ static SandboxTestMap test_map;
+ return test_map;
+}
+
+void AddSandboxTestCase(const char* test_name, MacSandboxTestCase* test_class) {
+ SandboxTestMap& test_map = GetSandboxTestMap();
+ if (test_map.find(test_name) != test_map.end()) {
+ LOG(ERROR) << "Trying to register duplicate test" << test_name;
+ NOTREACHED();
+ }
+ test_map[test_name] = test_class;
+}
+
+} // namespace internal
+
+bool MacSandboxTest::RunTestInAllSandboxTypes(const char* test_name,
+ const char* test_data) {
+ // Go through all the sandbox types, and run the test case in each of them
+ // if one fails, abort.
+ for(int i = static_cast<int>(Sandbox::SANDBOX_TYPE_FIRST_TYPE);
+ i < Sandbox::SANDBOX_AFTER_TYPE_LAST_TYPE;
+ ++i) {
+
+ if (!RunTestInSandbox(static_cast<Sandbox::SandboxProcessType>(i),
+ test_name, test_data)) {
+ LOG(ERROR) << "Sandboxed test (" << test_name << ")" <<
+ "Failed in sandbox type " << i <<
+ "user data: (" << test_data << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool MacSandboxTest::RunTestInSandbox(Sandbox::SandboxProcessType sandbox_type,
+ const char* test_name,
+ const char* test_data) {
+ std::stringstream s;
+ s << static_cast<int>(static_cast<int>(sandbox_type));
+ setenv(kSandboxTypeKey, s.str().c_str(), 1);
+ setenv(kSandboxTestNameKey, test_name, 1);
+ if (test_data)
+ setenv(kTestDataKey, test_data, 1);
+
+ base::ProcessHandle child_process = SpawnChild("mac_sandbox_test_runner",
+ false);
+ int code = -1;
+ if (!base::WaitForExitCode(child_process, &code)) {
+ LOG(WARNING) << "base::WaitForExitCode failed";
+ return false;
+ }
+ return code == 0;
+}
+
+bool MacSandboxTestCase::BeforeSandboxInit() {
+ return true;
+}
+
+void MacSandboxTestCase::SetTestData(const char* test_data) {
+ test_data_ = test_data;
+}
+
+// Given a test name specified by |name| return that test case.
+// If no test case is found for the given name, return NULL.
+MacSandboxTestCase *SandboxTestForName(const char* name) {
+ using internal::SandboxTestMap;
+ using internal::GetSandboxTestMap;
+
+ SandboxTestMap all_tests = GetSandboxTestMap();
+
+ SandboxTestMap::iterator it = all_tests.find(name);
+ if (it == all_tests.end()) {
+ LOG(ERROR) << "Couldn't find sandbox test case(" << name << ")";
+ return NULL;
+ }
+
+ return it->second;
+}
+
+} // namespace sandboxtest
+
+namespace {
+
+// Main function for driver process that enables the sandbox and runs test
+// code.
+MULTIPROCESS_TEST_MAIN(mac_sandbox_test_runner) {
+ // Extract parameters.
+ char* sandbox_type_str = getenv(kSandboxTypeKey);
+ if (!sandbox_type_str) {
+ LOG(ERROR) << "Sandbox type not specified";
+ return -1;
+ }
+ Sandbox::SandboxProcessType sandbox_type =
+ static_cast<Sandbox::SandboxProcessType>(atoi(sandbox_type_str));
+ char* sandbox_test_name = getenv(kSandboxTestNameKey);
+ if (!sandbox_test_name) {
+ LOG(ERROR) << "Sandbox test name not specified";
+ return -1;
+ }
+
+ const char* test_data = getenv(kTestDataKey);
+
+ // Find Test Function to run;
+ scoped_ptr<sandboxtest::MacSandboxTestCase>
+ test_case(sandboxtest::SandboxTestForName(sandbox_test_name));
+ if (!test_case.get()) {
+ LOG(ERROR) << "Invalid sandbox test name (" << sandbox_test_name << ")";
+ return -1;
+ }
+ test_case->SetTestData(test_data);
+
+ // Run Test.
+ if (!test_case->BeforeSandboxInit()) {
+ LOG(ERROR) << sandbox_test_name << "Failed test before sandbox init";
+ return -1;
+ }
+
+ Sandbox::SandboxWarmup(sandbox_type);
+
+ if (!Sandbox::EnableSandbox(sandbox_type, FilePath())) {
+ LOG(ERROR) << "Failed to initialize sandbox " << sandbox_type;
+ return -1;
+ }
+
+ if (!test_case->SandboxedTest()) {
+ LOG(ERROR) << sandbox_test_name << "Failed sandboxed test";
+ return -1;
+ }
+
+ return 0;
+}
+
+} // namespace