diff options
30 files changed, 864 insertions, 27 deletions
@@ -40,7 +40,7 @@ deps = { "http://v8.googlecode.com/svn/trunk@2966", "src/native_client": - "http://nativeclient.googlecode.com/svn/trunk/src/native_client@730", + "http://nativeclient.googlecode.com/svn/trunk/src/native_client@788", "src/third_party/skia": "http://skia.googlecode.com/svn/trunk@341", diff --git a/base/format_macros.h b/base/format_macros.h index 4b713ef..383579f 100644 --- a/base/format_macros.h +++ b/base/format_macros.h @@ -37,9 +37,17 @@ #else // OS_WIN +#if !defined(PRId64) #define PRId64 "I64d" +#endif + +#if !defined(PRIu64) #define PRIu64 "I64u" +#endif + +#if !defined(PRIx64) #define PRIx64 "I64x" +#endif #define WidePRId64 L"I64d" #define WidePRIu64 L"I64u" diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index 9f135b5..b5b0b7b 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -83,6 +83,7 @@ extern int BrowserMain(const MainFunctionParams&); extern int RendererMain(const MainFunctionParams&); extern int PluginMain(const MainFunctionParams&); extern int WorkerMain(const MainFunctionParams&); +extern int NaClMain(const MainFunctionParams&); extern int UtilityMain(const MainFunctionParams&); extern int ProfileImportMain(const MainFunctionParams&); extern int ZygoteMain(const MainFunctionParams&); @@ -581,6 +582,8 @@ int ChromeMain(int argc, char** argv) { #endif } else if (process_type == switches::kWorkerProcess) { rv = WorkerMain(main_params); + } else if (process_type == switches::kNaClProcess) { + rv = NaClMain(main_params); } else if (process_type == switches::kZygoteProcess) { #if defined(OS_LINUX) if (ZygoteMain(main_params)) { diff --git a/chrome/browser/nacl_process_host.cc b/chrome/browser/nacl_process_host.cc new file mode 100644 index 0000000..72db561 --- /dev/null +++ b/chrome/browser/nacl_process_host.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2009 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 "build/build_config.h" + +#include "chrome/browser/nacl_process_host.h" + +#if defined(OS_POSIX) +#include <fcntl.h> +#endif + +#if defined(OS_POSIX) +#include "base/global_descriptors_posix.h" +#endif +#include "base/path_service.h" +#include "base/process_util.h" +#include "chrome/browser/renderer_host/resource_message_filter.h" +#include "chrome/common/chrome_descriptors.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/nacl_messages.h" +#include "ipc/ipc_switches.h" + +#if defined(OS_WIN) +#include "chrome/browser/sandbox_policy.h" +#endif + +#if defined(OS_POSIX) +#include "ipc/ipc_channel_posix.h" +#endif + +NaClProcessHost::NaClProcessHost( + ResourceDispatcherHost *resource_dispatcher_host) + : ChildProcessHost(NACL_PROCESS, resource_dispatcher_host), + resource_dispatcher_host_(resource_dispatcher_host) { +} + +bool NaClProcessHost::Launch(ResourceMessageFilter* renderer_msg_filter, + const int descriptor, + nacl::FileDescriptor* handle) { + nacl::Handle pair[2]; + bool success = false; + // Create a connected socket + if (nacl::SocketPair(pair) == -1) { + NATIVE_HANDLE(*handle) = nacl::kInvalidHandle; + return false; + } + + // Launch the process + success = LaunchSelLdr(renderer_msg_filter, descriptor, pair[1]); + + if (!success) { + nacl::Close(pair[0]); + NATIVE_HANDLE(*handle) = nacl::kInvalidHandle; + return false; + } + + nacl::Handle duplicate_handle = nacl::kInvalidHandle; +#if NACL_WINDOWS + DuplicateHandle(base::GetCurrentProcessHandle(), + reinterpret_cast<HANDLE>(pair[0]), + renderer_msg_filter->handle(), + reinterpret_cast<HANDLE*>(&duplicate_handle), + GENERIC_READ | GENERIC_WRITE, + FALSE, + DUPLICATE_CLOSE_SOURCE); + *handle = duplicate_handle; +#else + duplicate_handle = pair[0]; + int flags = fcntl(duplicate_handle, F_GETFD); + if (flags != -1) { + flags |= FD_CLOEXEC; + fcntl(duplicate_handle, F_SETFD, flags); + } + // No need to dup the handle - we don't pass it anywhere else so + // it cannot be closed. + handle->fd = duplicate_handle; + handle->auto_close = true; +#endif + + return true; +} + +bool NaClProcessHost::LaunchSelLdr(ResourceMessageFilter* renderer_msg_filter, + const int descriptor, + const nacl::Handle handle) { + if (!CreateChannel()) + return false; + + // Build command line for nacl. + const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + std::wstring exe_path = + browser_command_line.GetSwitchValue(switches::kBrowserSubprocessPath); + if (exe_path.empty() && !PathService::Get(base::FILE_EXE, &exe_path)) + return false; + + CommandLine cmd_line(exe_path); + if (logging::DialogsAreSuppressed()) + cmd_line.AppendSwitch(switches::kNoErrorDialogs); + + // propagate the following switches to the plugin command line (along with + // any associated values) if present in the browser command line + // TODO(gregoryd): check which flags of those below can be supported. + static const wchar_t* const switch_names[] = { + switches::kNoSandbox, + switches::kTestSandbox, + switches::kDisableBreakpad, + switches::kFullMemoryCrashReport, + switches::kEnableLogging, + switches::kDisableLogging, + switches::kLoggingLevel, + switches::kEnableDCHECK, + switches::kSilentDumpOnDCHECK, + switches::kMemoryProfiling, + }; + + for (size_t i = 0; i < arraysize(switch_names); ++i) { + if (browser_command_line.HasSwitch(switch_names[i])) { + cmd_line.AppendSwitchWithValue( + switch_names[i], + browser_command_line.GetSwitchValue(switch_names[i])); + } + } + + cmd_line.AppendSwitchWithValue(switches::kProcessType, + switches::kNaClProcess); + + cmd_line.AppendSwitchWithValue(switches::kProcessChannelID, + ASCIIToWide(channel_id())); + + base::ProcessHandle process = 0; +#if defined(OS_WIN) + process = sandbox::StartProcess(&cmd_line); +#else + base::file_handle_mapping_vector fds_to_map; + const int ipcfd = channel().GetClientFileDescriptor(); + if (ipcfd > -1) + fds_to_map.push_back(std::pair<int, int>( + ipcfd, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); + base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process); +#endif + + if (!process) + return false; + SetHandle(process); + + // send a message with duplicated handle to sel_ldr + return SendStartMessage(process, descriptor, handle); +} + +bool NaClProcessHost::SendStartMessage(base::ProcessHandle process, + int descriptor, + nacl::Handle handle) { + nacl::FileDescriptor channel; +#if defined(OS_WIN) + if (!DuplicateHandle(GetCurrentProcess(), + reinterpret_cast<HANDLE>(handle), + process, + reinterpret_cast<HANDLE*>(&channel), + GENERIC_READ | GENERIC_WRITE, + FALSE, DUPLICATE_CLOSE_SOURCE)) { + return false; + } +#else + channel.fd = dup(handle); + channel.auto_close = true; +#endif + NaClProcessMsg_Start* msg = new NaClProcessMsg_Start(descriptor, + channel); + + if (!Send(msg)) { + return false; + } + return true; +} + +void NaClProcessHost::OnMessageReceived(const IPC::Message& msg) { + NOTREACHED() << "Invalid message with type = " << msg.type(); +} + +URLRequestContext* NaClProcessHost::GetRequestContext( + uint32 request_id, + const ViewHostMsg_Resource_Request& request_data) { + return NULL; +} + diff --git a/chrome/browser/nacl_process_host.h b/chrome/browser/nacl_process_host.h new file mode 100644 index 0000000..59de526 --- /dev/null +++ b/chrome/browser/nacl_process_host.h @@ -0,0 +1,55 @@ +// Copyright (c) 2009 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 CHROME_BROWSER_NACL_PROCESS_HOST_H_ +#define CHROME_BROWSER_NACL_PROCESS_HOST_H_ + +#include "build/build_config.h" +#include "chrome/common/child_process_host.h" +#include "chrome/common/nacl_types.h" +#include "native_client/src/shared/imc/nacl_imc.h" + +class ResourceMessageFilter; + +// Represents the browser side of the browser <--> NaCl communication +// channel. There will be one NaClProcessHost per NaCl process +// The browser is responsible for starting the NaCl process +// when requested by the renderer. +// After that, most of the communication is directly between NaCl plugin +// running in the renderer and NaCl processes. +class NaClProcessHost : public ChildProcessHost { + public: + explicit NaClProcessHost(ResourceDispatcherHost *resource_dispatcher_host); + ~NaClProcessHost() {} + + // Initialize the new NaCl process, returning true on success. + bool Launch(ResourceMessageFilter* renderer_msg_filter, + const int descriptor, + nacl::FileDescriptor *handle); + + virtual void OnMessageReceived(const IPC::Message& msg); + + private: + bool LaunchSelLdr(ResourceMessageFilter* renderer_msg_filter, + const int descriptor, + const nacl::Handle handle); + + bool SendStartMessage(base::ProcessHandle process, + int descriptor, + nacl::Handle handle); + + // ResourceDispatcherHost::Receiver implementation: + virtual URLRequestContext* GetRequestContext( + uint32 request_id, + const ViewHostMsg_Resource_Request& request_data); + + virtual bool CanShutdown() { return true; } + + private: + ResourceDispatcherHost* resource_dispatcher_host_; + + DISALLOW_COPY_AND_ASSIGN(NaClProcessHost); +}; + +#endif // CHROME_BROWSER_NACL_PROCESS_HOST_H_ diff --git a/chrome/browser/plugin_service.cc b/chrome/browser/plugin_service.cc index 42a67e9..5c5a8bb 100644 --- a/chrome/browser/plugin_service.cc +++ b/chrome/browser/plugin_service.cc @@ -23,6 +23,7 @@ #include "chrome/common/notification_type.h" #include "chrome/common/notification_service.h" #include "chrome/common/render_messages.h" +#include "native_client/src/trusted/plugin/nacl_entry_points.h" #include "webkit/glue/plugins/plugin_constants_win.h" #include "webkit/glue/plugins/plugin_list.h" @@ -44,6 +45,8 @@ PluginService::PluginService() NPAPI::PluginList::Singleton()->AddExtraPluginPath( FilePath::FromWStringHack(path)); } + if (command_line->HasSwitch(switches::kInternalNaCl)) + RegisterInternalNaClPlugin(); #if defined(OS_WIN) hkcu_key_.Create( diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index fb91a80..43482f1 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -326,6 +326,7 @@ bool BrowserRenderProcessHost::Init() { switches::kDisableAudio, switches::kSimpleDataSource, switches::kEnableBenchmarking, + switches::kInternalNaCl, switches::kEnableDatabases, }; diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 0b8a923..3091033 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -15,6 +15,7 @@ #include "chrome/browser/chrome_thread.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/in_process_webkit/dom_storage_dispatcher_host.h" +#include "chrome/browser/nacl_process_host.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/browser/net/dns_global.h" #include "chrome/browser/plugin_service.h" @@ -295,6 +296,7 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { OnReceiveContextMenuMsg(msg)) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_OpenChannelToPlugin, OnOpenChannelToPlugin) + IPC_MESSAGE_HANDLER(ViewHostMsg_LaunchNaCl, OnLaunchNaCl) IPC_MESSAGE_HANDLER(ViewHostMsg_CreateDedicatedWorker, OnCreateDedicatedWorker) IPC_MESSAGE_HANDLER(ViewHostMsg_CancelCreateDedicatedWorker, @@ -585,6 +587,12 @@ void ResourceMessageFilter::OnOpenChannelToPlugin(const GURL& url, this, url, mime_type, locale, reply_msg); } +void ResourceMessageFilter::OnLaunchNaCl(int channel_descriptor, + nacl::FileDescriptor* handle) { + NaClProcessHost* nacl_host = new NaClProcessHost(resource_dispatcher_host_); + nacl_host->Launch(this, channel_descriptor, handle); +} + void ResourceMessageFilter::OnCreateDedicatedWorker(const GURL& url, int render_view_route_id, int* route_id) { diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 6b93eee..ca37294 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -25,6 +25,7 @@ #include "chrome/browser/renderer_host/render_widget_helper.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/common/modal_dialog_event.h" +#include "chrome/common/nacl_types.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/transport_dib.h" #include "ipc/ipc_channel_proxy.h" @@ -154,6 +155,8 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, const std::string& mime_type, const std::wstring& locale, IPC::Message* reply_msg); + void OnLaunchNaCl(int channel_descriptor, + nacl::FileDescriptor* handle); void OnCreateDedicatedWorker(const GURL& url, int render_view_route_id, int* route_id); diff --git a/chrome/browser/sandbox_policy.cc b/chrome/browser/sandbox_policy.cc index 13f4ee2..784e5a7 100644 --- a/chrome/browser/sandbox_policy.cc +++ b/chrome/browser/sandbox_policy.cc @@ -359,6 +359,8 @@ base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, type = ChildProcessInfo::PLUGIN_PROCESS; } else if (type_str == switches::kWorkerProcess) { type = ChildProcessInfo::WORKER_PROCESS; + } else if (type_str == switches::kNaClProcess) { + type = ChildProcessInfo::NACL_PROCESS; } else if (type_str == switches::kUtilityProcess) { type = ChildProcessInfo::UTILITY_PROCESS; } else { @@ -371,8 +373,10 @@ base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, (type != ChildProcessInfo::PLUGIN_PROCESS || browser_command_line.HasSwitch(switches::kSafePlugins)); #if !defined (GOOGLE_CHROME_BUILD) - if (browser_command_line.HasSwitch(switches::kInProcessPlugins)) { + if (browser_command_line.HasSwitch(switches::kInProcessPlugins) || + browser_command_line.HasSwitch(switches::kInternalNaCl)) { // In process plugins won't work if the sandbox is enabled. + // The internal NaCl plugin doesn't work in the sandbox for now. in_sandbox = false; } #endif @@ -394,6 +398,8 @@ base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, sandbox::TargetPolicy* policy = broker_service->CreatePolicy(); bool on_sandbox_desktop = false; + // TODO(gregoryd): try locked-down policy for sel_ldr after we fix IMC. + // TODO(gregoryd): do we need a new desktop for sel_ldr? if (type == ChildProcessInfo::PLUGIN_PROCESS) { if (!AddPolicyForPlugin(cmd_line, policy)) return 0; diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index d5fe44f..d45b623 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -108,7 +108,26 @@ '../views/grid_layout_unittest.cc', ], 'conditions': [ + ['OS=="win"', { + 'nacl_defines': [ + 'NACL_WINDOWS=1', + 'NACL_LINUX=0', + 'NACL_OSX=0', + ], + },], + ['OS=="linux"', { + 'nacl_defines': [ + 'NACL_WINDOWS=0', + 'NACL_LINUX=1', + 'NACL_OSX=0', + ], + },], ['OS=="mac"', { + 'nacl_defines': [ + 'NACL_WINDOWS=0', + 'NACL_LINUX=0', + 'NACL_OSX=1', + ], 'conditions': [ ['branding=="Chrome"', { 'mac_bundle_id': 'com.google.Chrome', @@ -128,6 +147,20 @@ 'chrome_personalization%': 0, 'use_syncapi_stub%': 1, }], # OS=="win" + ['target_arch=="ia32"', { + 'nacl_defines': [ + # TODO(gregoryd): consider getting this from NaCl's common.gypi + 'NACL_TARGET_SUBARCH=32', + 'NACL_BUILD_SUBARCH=32', + ], + }], + ['target_arch=="x64"', { + 'nacl_defines': [ + # TODO(gregoryd): consider getting this from NaCl's common.gypi + 'NACL_TARGET_SUBARCH=64', + 'NACL_BUILD_SUBARCH=64', + ], + }], ], # conditions }, # variables 'target_defaults': { @@ -590,6 +623,8 @@ 'common/sandbox_init_wrapper.h', 'common/security_filter_peer.cc', 'common/security_filter_peer.h', + 'common/nacl_messages.h', + 'common/nacl_messages_internal.h', 'common/sqlite_compiled_statement.cc', 'common/sqlite_compiled_statement.h', 'common/sqlite_utils.cc', @@ -704,6 +739,9 @@ '..', '<(INTERMEDIATE_DIR)', ], + 'defines': [ + '<@(nacl_defines)', + ], 'sources': [ # All .cc, .h, .m, and .mm files under browser except for tests and # mocks. @@ -1830,6 +1868,8 @@ 'browser/search_engines/template_url_prepopulate_data.h', 'browser/search_engines/template_url_table_model.cc', 'browser/search_engines/template_url_table_model.h', + 'browser/nacl_process_host.cc', + 'browser/nacl_process_host.h', 'browser/session_startup_pref.cc', 'browser/session_startup_pref.h', 'browser/sessions/base_session_service.cc', @@ -2868,11 +2908,57 @@ ], }, { + 'target_name': 'nacl', + 'type': '<(library)', + 'msvs_guid': '83E86DAF-5763-4711-AD34-5FDAE395560C', + 'dependencies': [ + 'common', + 'chrome_resources', + 'chrome_strings', + '../third_party/npapi/npapi.gyp:npapi', + '../webkit/webkit.gyp:glue', + '../native_client/src/trusted/plugin/plugin_chrome.gyp:npGoogleNaClPluginChrome', + '../native_client/src/trusted/service_runtime/service_runtime.gyp:sel', + '../native_client/src/trusted/validator_x86/validator_x86.gyp:ncvalidate', + '../native_client/src/trusted/platform_qualify/platform_qualify.gyp:platform_qual_lib', + ], + 'include_dirs': [ + '<(INTERMEDIATE_DIR)', + ], + 'defines': [ + 'NACL_BLOCK_SHIFT=5', + 'NACL_BLOCK_SIZE=32', + '<@(nacl_defines)', + ], + 'sources': [ + # All .cc, .h, .m, and .mm files under nacl except for tests and + # mocks. + 'nacl/sel_main.cc', + 'nacl/nacl_main.cc', + 'nacl/nacl_thread.cc', + 'nacl/nacl_thread.h', + ], + # TODO(gregoryd): consider switching NaCl to use Chrome OS defines + 'conditions': [ + ['OS=="win"', { + 'defines': [ + '__STD_C', + '_CRT_SECURE_NO_DEPRECATE', + '_SCL_SECURE_NO_DEPRECATE', + ], + 'include_dirs': [ + 'third_party/wtl/include', + ], + },], + ], + }, + { 'target_name': 'renderer', 'type': '<(library)', 'msvs_guid': '9301A569-5D2B-4D11-9332-B1E30AEACB8D', 'dependencies': [ 'common', + 'nacl', 'plugin', 'chrome_resources', 'chrome_strings', @@ -2887,6 +2973,9 @@ 'include_dirs': [ '..', ], + 'defines': [ + '<@(nacl_defines)', + ], 'sources': [ # TODO(jrg): to link ipc_tests, these files need to be in renderer.a. # But app/ is the wrong directory for them. @@ -3364,6 +3453,8 @@ 'infoplist_strings_tool', # Bring in pdfsqueeze and run it on all pdfs '../build/temp_gyp/pdfsqueeze.gyp:pdfsqueeze', + # This library provides the real implementation for NaClSyscallSeg + '../native_client/src/trusted/service_runtime/arch/x86_32/service_runtime_x86_32.gyp:service_runtime_x86_32_chrome' ], 'rules': [ { diff --git a/chrome/common/child_process_info.h b/chrome/common/child_process_info.h index 250ff57..c99b2c0 100644 --- a/chrome/common/child_process_info.h +++ b/chrome/common/child_process_info.h @@ -17,6 +17,7 @@ class ChildProcessInfo { RENDER_PROCESS, PLUGIN_PROCESS, WORKER_PROCESS, + NACL_PROCESS, UTILITY_PROCESS, PROFILE_IMPORT_PROCESS, ZYGOTE_PROCESS, diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 14aea58..7fa5db0 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -71,6 +71,9 @@ const wchar_t kPluginProcess[] = L"plugin"; // Causes the process to run as a worker subprocess. const wchar_t kWorkerProcess[] = L"worker"; +// Causes the process to run as a NativeClient's sel_ldr subprocess. +const wchar_t kNaClProcess[] = L"nacl"; + // Causes the process to run as a utility subprocess. const wchar_t kUtilityProcess[] = L"utility"; @@ -367,6 +370,9 @@ const wchar_t kEnableRemoteFonts[] = L"enable-remote-fonts"; // Use the low fragmentation heap for the CRT. const wchar_t kUseLowFragHeapCrt[] = L"use-lf-heap"; +// Runs the Native Client inside the renderer process. +const wchar_t kInternalNaCl[] = L"internal-nacl"; + #ifndef NDEBUG // Debug only switch to specify which gears plugin dll to load. const wchar_t kGearsPluginPathOverride[] = L"gears-plugin-path"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 9ed753c..d5f7b9a 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -30,6 +30,7 @@ extern const wchar_t kZygoteProcess[]; extern const wchar_t kBrowserSubprocessPath[]; extern const wchar_t kPluginProcess[]; extern const wchar_t kWorkerProcess[]; +extern const wchar_t kNaClProcess[]; extern const wchar_t kUtilityProcess[]; extern const wchar_t kProfileImportProcess[]; extern const wchar_t kSingleProcess[]; @@ -133,6 +134,8 @@ extern const wchar_t kEnableRemoteFonts[]; extern const wchar_t kUseLowFragHeapCrt[]; +extern const wchar_t kInternalNaCl[]; + #ifndef NDEBUG extern const wchar_t kGearsPluginPathOverride[]; #endif diff --git a/chrome/common/nacl_messages.h b/chrome/common/nacl_messages.h new file mode 100644 index 0000000..8d360dd --- /dev/null +++ b/chrome/common/nacl_messages.h @@ -0,0 +1,19 @@ +// Copyright (c) 2009 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. + +// Defines messages between the browser and NaCl process. + +#ifndef CHROME_COMMON_NACL_MESSAGES_H_ +#define CHROME_COMMON_NACL_MESSAGES_H_ + +#include <string> + +#include "base/basictypes.h" +#include "ipc/ipc_message_utils.h" + +#define MESSAGES_INTERNAL_FILE "chrome/common/nacl_messages_internal.h" +#include "ipc/ipc_message_macros.h" + +#endif // CHROME_COMMON_NACL_MESSAGES_H_ + diff --git a/chrome/common/nacl_messages_internal.h b/chrome/common/nacl_messages_internal.h new file mode 100644 index 0000000..3794255 --- /dev/null +++ b/chrome/common/nacl_messages_internal.h @@ -0,0 +1,18 @@ +// Copyright (c) 2009 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 "chrome/common/nacl_types.h" +#include "ipc/ipc_message_macros.h" + +//----------------------------------------------------------------------------- +// NaClProcess messages +// These are messages sent from the browser to the NaCl process. +IPC_BEGIN_MESSAGES(NaClProcess) + // Tells the NaCl process to start. + IPC_MESSAGE_CONTROL2(NaClProcessMsg_Start, + int /* descriptor id */, + nacl::FileDescriptor /* handle value */) + +IPC_END_MESSAGES(NaClProcess) + diff --git a/chrome/common/nacl_types.h b/chrome/common/nacl_types.h new file mode 100644 index 0000000..394edd3 --- /dev/null +++ b/chrome/common/nacl_types.h @@ -0,0 +1,21 @@ +// Copyright (c) 2009 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. + +// Handle passing definitions for NaCl +#ifndef CHROME_COMMON_NACL_TYPES_H_ +#define CHROME_COMMON_NACL_TYPES_H_ + +// TODO(gregoryd): add a Windows definition for base::FileDescriptor, +// replace the macros with inline functions. +namespace nacl { +#if defined(OS_WIN) +typedef HANDLE FileDescriptor; +#define NATIVE_HANDLE(desc) (desc) +#elif defined(OS_POSIX) +typedef base::FileDescriptor FileDescriptor; +#define NATIVE_HANDLE(desc) ((desc).fd) +#endif +} + +#endif // CHROME_COMMON_NACL_TYPES_H_ diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 4eec034..11d14d5 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -21,6 +21,7 @@ #include "base/values.h" #include "chrome/common/css_colors.h" #include "chrome/common/extensions/update_manifest.h" +#include "chrome/common/nacl_types.h" #include "chrome/common/notification_type.h" #include "chrome/common/transport_dib.h" #include "chrome/common/view_types.h" @@ -1089,6 +1090,14 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC::ChannelHandle /* handle to channel */, WebPluginInfo /* info */) + // A renderer sends this to the browser process when it wants to start + // a new instance of the Native Client process. The browser will launch + // the process and return a handle to an IMC channel. + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_LaunchNaCl, + int /* channel number */, + nacl::FileDescriptor /* handle - one side + of a socket pair */) + #if defined(OS_LINUX) // A renderer sends this when it needs a browser-side widget for // hosting a windowed plugin. id is the XID of the plugin window, for which diff --git a/chrome/common/sandbox_init_wrapper.cc b/chrome/common/sandbox_init_wrapper.cc index c296ff8..14006ed 100644 --- a/chrome/common/sandbox_init_wrapper.cc +++ b/chrome/common/sandbox_init_wrapper.cc @@ -27,6 +27,7 @@ void SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line, if (!command_line.HasSwitch(switches::kNoSandbox)) { if ((process_type == switches::kRendererProcess) || (process_type == switches::kWorkerProcess) || + (process_type == switches::kNaClProcess) || (process_type == switches::kUtilityProcess) || (process_type == switches::kPluginProcess && command_line.HasSwitch(switches::kSafePlugins))) { diff --git a/chrome/nacl/DEPS b/chrome/nacl/DEPS new file mode 100644 index 0000000..8bdeefd --- /dev/null +++ b/chrome/nacl/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+app", + "+chrome/test", + "+sandbox/src", + "+native_client/src", +] diff --git a/chrome/nacl/nacl_main.cc b/chrome/nacl/nacl_main.cc new file mode 100644 index 0000000..f519748 --- /dev/null +++ b/chrome/nacl/nacl_main.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2006-2009 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 "build/build_config.h" + +#if defined(OS_WIN) +#include "app/win_util.h" +#include "chrome/test/injection_test_dll.h" +#include "sandbox/src/sandbox.h" +#endif + +#include "base/command_line.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/system_monitor.h" +#include "chrome/common/child_process.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/main_function_params.h" +#include "chrome/nacl/nacl_thread.h" + +// main() routine for running as the sel_ldr process. +int NaClMain(const MainFunctionParams& parameters) { + // The main thread of the plugin services IO. + MessageLoopForIO main_message_loop; + std::wstring app_name = chrome::kBrowserAppName; + PlatformThread::SetName(WideToASCII(app_name + L"_NaClMain").c_str()); + + // Initialize the SystemMonitor + base::SystemMonitor::Start(); + +#if defined(OS_WIN) + const CommandLine& parsed_command_line = parameters.command_line_; + + sandbox::TargetServices* target_services = + parameters.sandbox_info_.TargetServices(); + + DLOG(INFO) << "Started plugin with " << + parsed_command_line.command_line_string(); + + HMODULE sandbox_test_module = NULL; + bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox); + if (target_services && !no_sandbox) { + // The command line might specify a test plugin to load. + if (parsed_command_line.HasSwitch(switches::kTestSandbox)) { + std::wstring test_plugin_name = + parsed_command_line.GetSwitchValue(switches::kTestSandbox); + sandbox_test_module = LoadLibrary(test_plugin_name.c_str()); + DCHECK(sandbox_test_module); + } + } + +#else + NOTIMPLEMENTED() << " non-windows startup, plugin startup dialog etc."; +#endif + + { + ChildProcess nacl_process; + nacl_process.set_main_thread(new NaClThread()); +#if defined(OS_WIN) + if (!no_sandbox && target_services) + target_services->LowerToken(); +#endif + + MessageLoop::current()->Run(); + } + + return 0; +} + + diff --git a/chrome/nacl/nacl_thread.cc b/chrome/nacl/nacl_thread.cc new file mode 100644 index 0000000..ff4f508 --- /dev/null +++ b/chrome/nacl/nacl_thread.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2006-2009 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 "chrome/nacl/nacl_thread.h" + +#include "build/build_config.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/nacl_messages.h" +#include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" + +int SelMain(const int desc, const NaClHandle handle); + +NaClThread::NaClThread() { +} + +NaClThread::~NaClThread() { +} + +NaClThread* NaClThread::current() { + return static_cast<NaClThread*>(ChildThread::current()); +} + +void NaClThread::OnControlMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(NaClThread, msg) + IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStartSelLdr) + IPC_END_MESSAGE_MAP() +} + +void NaClThread::OnStartSelLdr(const int channel_descriptor, + const nacl::FileDescriptor handle) { + SelMain(channel_descriptor, NATIVE_HANDLE(handle)); +} diff --git a/chrome/nacl/nacl_thread.h b/chrome/nacl/nacl_thread.h new file mode 100644 index 0000000..a69ce3a --- /dev/null +++ b/chrome/nacl/nacl_thread.h @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2009 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 CHROME_NACL_NACL_THREAD_H_ +#define CHROME_NACL_NACL_THREAD_H_ + +#include "base/file_path.h" +#include "base/native_library.h" +#include "chrome/common/child_thread.h" +#include "chrome/common/nacl_types.h" + +class NotificationService; + +// The NaClThread class represents a background thread where NaCl app gets +// started. +class NaClThread : public ChildThread { + public: + NaClThread(); + ~NaClThread(); + // Returns the one NaCl thread. + static NaClThread* current(); + + private: + virtual void OnControlMessageReceived(const IPC::Message& msg); + void OnStartSelLdr(const int channel_descriptor, + const nacl::FileDescriptor handle); + // TODO(gregoryd): do we need to override Cleanup as in PluginThread? + + scoped_ptr<NotificationService> notification_service_; + + DISALLOW_EVIL_CONSTRUCTORS(NaClThread); +}; + +#endif // CHROME_NACL_NACL_THREAD_H_ diff --git a/chrome/nacl/sel_main.cc b/chrome/nacl/sel_main.cc new file mode 100644 index 0000000..7174706 --- /dev/null +++ b/chrome/nacl/sel_main.cc @@ -0,0 +1,210 @@ +// Copyright (c) 2006-2009 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 "build/build_config.h" + +#include "native_client/src/include/portability.h" + +#if NACL_OSX +#include <crt_externs.h> +#endif + +EXTERN_C_BEGIN +#include "native_client/src/shared/platform/nacl_sync.h" +#include "native_client/src/shared/platform/nacl_sync_checked.h" +#include "native_client/src/trusted/service_runtime/nacl_globals.h" +#include "native_client/src/trusted/service_runtime/expiration.h" +#include "native_client/src/trusted/service_runtime/nacl_app.h" +#include "native_client/src/trusted/service_runtime/nacl_all_modules.h" +#include "native_client/src/trusted/service_runtime/sel_ldr.h" +#include "native_client/src/trusted/platform_qualify/nacl_os_qualify.h" +EXTERN_C_END + +int verbosity = 0; + +#ifdef __GNUC__ + +/* + * GDB's canonical overlay managment routine. + * We need its symbol in the symbol table so don't inline it. + * TODO(dje): add some explanation for the non-GDB person. + */ + +static void __attribute__ ((noinline)) _ovly_debug_event (void) { + /* + * The asm volatile is here as instructed by the GCC docs. + * It's not enough to declare a function noinline. + * GCC will still look inside the function to see if it's worth calling. + */ + asm volatile (""); +} + +#endif + +static void StopForDebuggerInit (const struct NaClApp *state) { + /* Put xlate_base in a place where gdb can find it. */ + nacl_global_xlate_base = state->xlate_base; + +#ifdef __GNUC__ + _ovly_debug_event (); +#endif +} + +int SelMain(const int desc, const NaClHandle handle) { + char *av[1]; + int ac = 1; + + char **envp; + struct NaClApp state; + char *nacl_file = 0; + int main_thread_only = 1; + int export_addr_to = -2; + + struct NaClApp *nap; + + NaClErrorCode errcode; + + int ret_code = 1; +#if NACL_OSX + // Mac dynamic libraries cannot access the environ variable directly. + envp = *_NSGetEnviron(); +#else + extern char **environ; + envp = environ; +#endif + + + if (NaClHasExpired()) { + // TODO(gregoryd): report error to browser? + fprintf(stderr, "This version of Native Client has expired.\n"); + fprintf(stderr, "Please visit: http://code.google.com/p/nativeclient/\n"); + exit(-1); + } + + NaClAllModulesInit(); + + /* used to be -P */ + NaClSrpcFileDescriptor = desc; + /* used to be -X */ + export_addr_to = desc; + + /* to be passed to NaClMain, eventually... */ + av[0] = const_cast<char*>("NaClMain"); + + if (!NaClAppCtor(&state)) { + fprintf(stderr, "Error while constructing app state\n"); + goto done_file_dtor; + } + + state.restrict_to_main_thread = main_thread_only; + + nap = &state; + errcode = LOAD_OK; + + /* import IMC handle - used to be "-i" */ + NaClAddImcHandle(nap, handle, desc); + + /* + * in order to report load error to the browser plugin through the + * secure command channel, we do not immediate jump to cleanup code + * on error. rather, we continue processing (assuming earlier + * errors do not make it inappropriate) until the secure command + * channel is set up, and then bail out. + */ + + /* + * Ensure this operating system platform is supported. + */ + if (!NaClOsIsSupported()) { + errcode = LOAD_UNSUPPORTED_OS_PLATFORM; + nap->module_load_status = errcode; + fprintf(stderr, "Error while loading \"%s\": %s\n", + nacl_file, + NaClErrorString(errcode)); + } + + /* Give debuggers a well known point at which xlate_base is known. */ + StopForDebuggerInit(&state); + + /* + * If export_addr_to is set to a non-negative integer, we create a + * bound socket and socket address pair and bind the former to + * descriptor 3 and the latter to descriptor 4. The socket address + * is written out to the export_addr_to descriptor. + * + * The service runtime also accepts a connection on the bound socket + * and spawns a secure command channel thread to service it. + * + * If export_addr_to is -1, we only create the bound socket and + * socket address pair, and we do not export to an IMC socket. This + * use case is typically only used in testing, where we only "dump" + * the socket address to stdout or similar channel. + */ + if (-2 < export_addr_to) { + NaClCreateServiceSocket(nap); + if (0 <= export_addr_to) { + NaClSendServiceAddressTo(nap, export_addr_to); + /* + * NB: spawns a thread that uses the command channel. we do + * this after NaClAppLoadFile so that NaClApp object is more + * fully populated. Hereafter any changes to nap should be done + * while holding locks. + */ + NaClSecureCommandChannel(nap); + } + } + + NaClXMutexLock(&nap->mu); + nap->module_load_status = LOAD_OK; + NaClXCondVarBroadcast(&nap->cv); + NaClXMutexUnlock(&nap->mu); + + if (NULL != nap->secure_channel) { + /* + * wait for start_module RPC call on secure channel thread. + */ + NaClWaitForModuleStartStatusCall(nap); + } + + /* + * error reporting done; can quit now if there was an error earlier. + */ + if (LOAD_OK != errcode) { + goto done; + } + + /* + * only nap->ehdrs.e_entry is usable, no symbol table is + * available. + */ + if (!NaClCreateMainThread(nap, + ac, + av, + envp)) { + fprintf(stderr, "creating main thread failed\n"); + goto done; + } + + ret_code = NaClWaitForMainThreadToExit(nap); + + /* + * exit_group or equiv kills any still running threads while module + * addr space is still valid. otherwise we'd have to kill threads + * before we clean up the address space. + */ + return ret_code; + + done: + fflush(stdout); + + NaClAppDtor(&state); + + done_file_dtor: + fflush(stdout); + + NaClAllModulesFini(); + + return ret_code; +} + diff --git a/chrome/renderer/render_process.cc b/chrome/renderer/render_process.cc index 610e5d3..0c579ea 100644 --- a/chrome/renderer/render_process.cc +++ b/chrome/renderer/render_process.cc @@ -25,11 +25,13 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/render_messages.h" +#include "chrome/common/nacl_types.h" #include "chrome/common/transport_dib.h" #include "chrome/renderer/render_view.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_message_utils.h" #include "media/base/media.h" +#include "native_client/src/trusted/plugin/nacl_entry_points.h" #include "webkit/glue/webkit_glue.h" //----------------------------------------------------------------------------- @@ -80,6 +82,9 @@ RenderProcess::RenderProcess() StatisticsRecorder::set_dump_on_exit(true); } + if (command_line.HasSwitch(switches::kInternalNaCl)) + RegisterInternalNaClPlugin(RenderProcess::LaunchNaClProcess); + FilePath module_path; initialized_media_library_ = PathService::Get(base::DIR_MODULE, &module_path) && @@ -112,6 +117,19 @@ bool RenderProcess::InProcessPlugins() { #endif } +bool RenderProcess::LaunchNaClProcess(int imc_fd, + nacl::Handle* handle) { + // TODO(gregoryd): nacl::FileDescriptor will be soon merged with + // base::FileDescriptor + nacl::FileDescriptor descriptor; + if (!RenderThread::current()->Send( + new ViewHostMsg_LaunchNaCl(imc_fd, &descriptor))) { + return false; + } + *handle = NATIVE_HANDLE(descriptor); + return true; +} + // ----------------------------------------------------------------------------- // Platform specific code for dealing with bitmap transport... diff --git a/chrome/renderer/render_process.h b/chrome/renderer/render_process.h index d445fbe..0ab9996 100644 --- a/chrome/renderer/render_process.h +++ b/chrome/renderer/render_process.h @@ -8,6 +8,7 @@ #include "base/timer.h" #include "chrome/common/child_process.h" #include "chrome/renderer/render_thread.h" +#include "native_client/src/shared/imc/nacl_imc.h" #include "skia/ext/platform_canvas.h" namespace gfx { @@ -51,6 +52,10 @@ class RenderProcess : public ChildProcess { // Just like in_process_plugins(), but called before RenderProcess is created. static bool InProcessPlugins(); + // Sends a message to the browser process asking to launch a new NaCl process. + // Called from NaCl plugin code. + static bool LaunchNaClProcess(int imc_fd, nacl::Handle *handle); + private: // Look in the shared memory cache for a suitable object to reuse. // result: (output) the memory found diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 842dfc0..2d2407d 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -2357,7 +2357,13 @@ webkit_glue::WebPluginDelegate* RenderView::CreatePluginDelegate( else mime_type_to_use = &mime_type; - if (RenderProcess::current()->in_process_plugins()) { + bool in_process_plugin = RenderProcess::current()->in_process_plugins(); + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kInternalNaCl)) { + if (mime_type == "application/x-nacl-srpc") { + in_process_plugin = true; + } + } + if (in_process_plugin) { #if defined(OS_WIN) // In-proc plugins aren't supported on Linux or Mac. return WebPluginDelegateImpl::Create( path, *mime_type_to_use, gfx::NativeViewFromId(host_window_)); diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 15d5297..fd0c064 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -44,6 +44,7 @@ enum IPCMessageStart { WorkerProcessHostMsgStart, WorkerMsgStart, WorkerHostMsgStart, + NaClProcessMsgStart, // NOTE: When you add a new message class, also update // IPCStatusView::IPCStatusView to ensure logging works. // NOTE: this enum is used by IPC_MESSAGE_MACRO to generate a unique message diff --git a/webkit/glue/plugins/plugin_lib.h b/webkit/glue/plugins/plugin_lib.h index ed523c12..16513af 100644 --- a/webkit/glue/plugins/plugin_lib.h +++ b/webkit/glue/plugins/plugin_lib.h @@ -61,6 +61,8 @@ class PluginLib : public base::RefCounted<PluginLib> { // supports. const WebPluginInfo& plugin_info() { return web_plugin_info_; } + bool internal() { return internal_; } + // // NPAPI functions // @@ -91,7 +93,7 @@ class PluginLib : public base::RefCounted<PluginLib> { void Shutdown(); private: - bool internal_; // Whether this an internal plugin. + bool internal_; // True for plugins that are built-in into chrome.dll WebPluginInfo web_plugin_info_; // supported mime types, description base::NativeLibrary library_; // the opened library reference NPPluginFuncs plugin_funcs_; // the struct of plugin side functions diff --git a/webkit/glue/plugins/webplugin_delegate_impl_win.cc b/webkit/glue/plugins/webplugin_delegate_impl_win.cc index 1f13cf8..9d8ed45 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl_win.cc +++ b/webkit/glue/plugins/webplugin_delegate_impl_win.cc @@ -326,35 +326,40 @@ void WebPluginDelegateImpl::PluginDestroyed() { void WebPluginDelegateImpl::PlatformInitialize() { plugin_->SetWindow(windowed_handle_); - if (windowless_) { + + if (windowless_ && !instance_->plugin_lib()->internal()) { CreateDummyWindowForActivation(); handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); plugin_->SetWindowlessPumpEvent(handle_event_pump_messages_event_); } - // Windowless plugins call the WindowFromPoint API and passes the result of - // that to the TrackPopupMenu API call as the owner window. This causes the - // API to fail as the API expects the window handle to live on the same thread - // as the caller. It works in the other browsers as the plugin lives on the - // browser thread. Our workaround is to intercept the TrackPopupMenu API and - // replace the window handle with the dummy activation window. - if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) { - g_iat_patch_track_popup_menu.Pointer()->Patch( - GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu", - WebPluginDelegateImpl::TrackPopupMenuPatch); - } + // We cannot patch internal plugins as they are not shared libraries. + if (!instance_->plugin_lib()->internal()) { + // Windowless plugins call the WindowFromPoint API and passes the result of + // that to the TrackPopupMenu API call as the owner window. This causes the + // API to fail as the API expects the window handle to live on the same + // thread as the caller. It works in the other browsers as the plugin lives + // on the browser thread. Our workaround is to intercept the TrackPopupMenu + // API and replace the window handle with the dummy activation window. + if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) { + g_iat_patch_track_popup_menu.Pointer()->Patch( + GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu", + WebPluginDelegateImpl::TrackPopupMenuPatch); + } - // Windowless plugins can set cursors by calling the SetCursor API. This - // works because the thread inputs of the browser UI thread and the plugin - // thread are attached. We intercept the SetCursor API for windowless plugins - // and remember the cursor being set. This is shipped over to the browser - // in the HandleEvent call, which ensures that the cursor does not change - // when a windowless plugin instance changes the cursor in a background tab. - if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() && - (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { - g_iat_patch_set_cursor.Pointer()->Patch( - GetPluginPath().value().c_str(), "user32.dll", "SetCursor", - WebPluginDelegateImpl::SetCursorPatch); + // Windowless plugins can set cursors by calling the SetCursor API. This + // works because the thread inputs of the browser UI thread and the plugin + // thread are attached. We intercept the SetCursor API for windowless + // plugins and remember the cursor being set. This is shipped over to the + // browser in the HandleEvent call, which ensures that the cursor does not + // change when a windowless plugin instance changes the cursor + // in a background tab. + if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() && + (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { + g_iat_patch_set_cursor.Pointer()->Patch( + GetPluginPath().value().c_str(), "user32.dll", "SetCursor", + WebPluginDelegateImpl::SetCursorPatch); + } } // On XP, WMP will use its old UI unless a registry key under HKLM has the |