diff options
64 files changed, 1323 insertions, 320 deletions
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index faed5fc..80d34b3 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc @@ -505,8 +505,6 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { #endif BrowserGpuChannelHostFactory::Terminate(); - GamepadService::GetInstance()->Terminate(); - // The device monitors are using |system_monitor_| as dependency, so delete // them before |system_monitor_| goes away. // On Mac and windows, the monitor needs to be destroyed on the same thread @@ -606,6 +604,10 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { // more head start for those operations to finish. BrowserThreadImpl::ShutdownThreadPool(); + // Must happen after the I/O thread is shutdown since this class lives on the + // I/O thread and isn't threadsafe. + GamepadService::GetInstance()->Terminate(); + if (parts_.get()) parts_->PostDestroyThreads(); } diff --git a/content/browser/gamepad/data_fetcher.h b/content/browser/gamepad/gamepad_data_fetcher.h index 94bf4ae..e5e009d 100644 --- a/content/browser/gamepad/data_fetcher.h +++ b/content/browser/gamepad/gamepad_data_fetcher.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_GAMEPAD_DATA_FETCHER_H_ -#define CONTENT_BROWSER_GAMEPAD_DATA_FETCHER_H_ +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_DATA_FETCHER_H_ +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_DATA_FETCHER_H_ namespace WebKit { class WebGamepads; @@ -11,6 +11,8 @@ class WebGamepads; namespace content { +// Abstract interface for imlementing platform- (and test-) specific behaviro +// for getting the gamepad data. class GamepadDataFetcher { public: virtual ~GamepadDataFetcher() {} @@ -19,6 +21,6 @@ class GamepadDataFetcher { virtual void PauseHint(bool paused) {} }; -} // namespace content +} // namespace content -#endif // CONTENT_BROWSER_GAMEPAD_DATA_FETCHER_H_ +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_DATA_FETCHER_H_ diff --git a/content/browser/gamepad/platform_data_fetcher.cc b/content/browser/gamepad/gamepad_platform_data_fetcher.cc index 1e80174..d14a3ca 100644 --- a/content/browser/gamepad/platform_data_fetcher.cc +++ b/content/browser/gamepad/gamepad_platform_data_fetcher.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/gamepad/platform_data_fetcher.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGamepads.h" @@ -16,4 +16,4 @@ void GamepadDataFetcherEmpty::GetGamepadData(WebKit::WebGamepads* pads, pads->length = 0; } -} // content +} // namespace content diff --git a/content/browser/gamepad/platform_data_fetcher.h b/content/browser/gamepad/gamepad_platform_data_fetcher.h index 178e478..f073a53 100644 --- a/content/browser/gamepad/platform_data_fetcher.h +++ b/content/browser/gamepad/gamepad_platform_data_fetcher.h @@ -5,19 +5,19 @@ // Define the default data fetcher that GamepadProvider will use if none is // supplied. (GamepadPlatformDataFetcher). -#ifndef CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_H_ -#define CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_H_ +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_H_ +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_H_ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" #if defined(OS_WIN) -#include "content/browser/gamepad/platform_data_fetcher_win.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" #elif defined(OS_MACOSX) -#include "content/browser/gamepad/platform_data_fetcher_mac.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher_mac.h" #elif defined(OS_LINUX) -#include "content/browser/gamepad/platform_data_fetcher_linux.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher_linux.h" #endif namespace content { @@ -52,4 +52,4 @@ typedef GamepadDataFetcherEmpty GamepadPlatformDataFetcher; } // namespace content -#endif // CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_H_ +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_H_ diff --git a/content/browser/gamepad/platform_data_fetcher_linux.cc b/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc index e364eac..0342917 100644 --- a/content/browser/gamepad/platform_data_fetcher_linux.cc +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/gamepad/platform_data_fetcher_linux.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher_linux.h" #include <fcntl.h> #include <libudev.h> diff --git a/content/browser/gamepad/platform_data_fetcher_linux.h b/content/browser/gamepad/gamepad_platform_data_fetcher_linux.h index 55cb74e..681946f 100644 --- a/content/browser/gamepad/platform_data_fetcher_linux.h +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_linux.h @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_ -#define CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_ +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_ +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_ #include <string> #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_standard_mappings.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGamepads.h" @@ -53,4 +53,4 @@ class GamepadPlatformDataFetcherLinux : public GamepadDataFetcher { } // namespace content -#endif // CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_ +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_ diff --git a/content/browser/gamepad/platform_data_fetcher_mac.h b/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h index c92307d..8bcc9c2 100644 --- a/content/browser/gamepad/platform_data_fetcher_mac.h +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_ -#define CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_ +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_ +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/mac/scoped_cftyperef.h" #include "build/build_config.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_standard_mappings.h" #include "content/common/gamepad_hardware_buffer.h" @@ -79,4 +79,4 @@ class GamepadPlatformDataFetcherMac : public GamepadDataFetcher { } // namespace content -#endif // CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_ +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_ diff --git a/content/browser/gamepad/platform_data_fetcher_mac.mm b/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm index 1d1bd72..8748730 100644 --- a/content/browser/gamepad/platform_data_fetcher_mac.mm +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/gamepad/platform_data_fetcher_mac.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher_mac.h" #include "base/mac/foundation_util.h" #include "base/memory/scoped_nsobject.h" diff --git a/content/browser/gamepad/platform_data_fetcher_win.cc b/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc index 92587a4..838a626 100644 --- a/content/browser/gamepad/platform_data_fetcher_win.cc +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/gamepad/platform_data_fetcher_win.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" #include "base/debug/trace_event.h" #include "content/common/gamepad_messages.h" diff --git a/content/browser/gamepad/platform_data_fetcher_win.h b/content/browser/gamepad/gamepad_platform_data_fetcher_win.h index 9cae9cc..92c44ff 100644 --- a/content/browser/gamepad/platform_data_fetcher_win.h +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_win.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_ -#define CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_ +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_ +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_ #include "build/build_config.h" @@ -16,7 +16,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/scoped_native_library.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" namespace content { @@ -54,4 +54,4 @@ class GamepadPlatformDataFetcherWin : public GamepadDataFetcher { } // namespace content -#endif // CONTENT_BROWSER_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_ +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_ diff --git a/content/browser/gamepad/gamepad_provider.cc b/content/browser/gamepad/gamepad_provider.cc index 507c704..63a352f 100644 --- a/content/browser/gamepad/gamepad_provider.cc +++ b/content/browser/gamepad/gamepad_provider.cc @@ -9,42 +9,41 @@ #include "base/bind.h" #include "base/logging.h" #include "base/message_loop.h" +#include "base/message_loop_proxy.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_provider.h" -#include "content/browser/gamepad/platform_data_fetcher.h" +#include "content/browser/gamepad/gamepad_platform_data_fetcher.h" #include "content/common/gamepad_hardware_buffer.h" #include "content/common/gamepad_messages.h" +#include "content/common/gamepad_user_gesture.h" #include "content/public/browser/browser_thread.h" namespace content { +GamepadProvider::ClosureAndThread::ClosureAndThread( + const base::Closure& c, + const scoped_refptr<base::MessageLoopProxy>& m) + : closure(c), + message_loop(m) { +} + +GamepadProvider::ClosureAndThread::~ClosureAndThread() { +} + GamepadProvider::GamepadProvider() : is_paused_(true), have_scheduled_do_poll_(false), devices_changed_(true) { - size_t data_size = sizeof(GamepadHardwareBuffer); - base::SystemMonitor* monitor = base::SystemMonitor::Get(); - if (monitor) - monitor->AddDevicesChangedObserver(this); - bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); - CHECK(res); - GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); - memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); - - polling_thread_.reset(new base::Thread("Gamepad polling thread")); - polling_thread_->StartWithOptions( - base::Thread::Options(MessageLoop::TYPE_IO, 0)); + Initialize(scoped_ptr<GamepadDataFetcher>()); } -void GamepadProvider::SetDataFetcher(GamepadDataFetcher* fetcher) { - MessageLoop* polling_loop = polling_thread_->message_loop(); - polling_loop->PostTask( - FROM_HERE, - base::Bind(&GamepadProvider::DoInitializePollingThread, - Unretained(this), - fetcher)); +GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) + : is_paused_(true), + have_scheduled_do_poll_(false), + devices_changed_(true) { + Initialize(fetcher.Pass()); } GamepadProvider::~GamepadProvider() { @@ -56,7 +55,7 @@ GamepadProvider::~GamepadProvider() { data_fetcher_.reset(); } -base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle( +base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( base::ProcessHandle process) { base::SharedMemoryHandle renderer_handle; gamepad_shared_memory_.ShareToProcess(process, &renderer_handle); @@ -91,24 +90,46 @@ void GamepadProvider::Resume() { base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this))); } +void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) { + base::AutoLock lock(user_gesture_lock_); + user_gesture_observers_.push_back( + ClosureAndThread(closure, MessageLoop::current()->message_loop_proxy())); +} + void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) { base::AutoLock lock(devices_changed_lock_); devices_changed_ = true; } -void GamepadProvider::DoInitializePollingThread(GamepadDataFetcher* fetcher) { - DCHECK(MessageLoop::current() == polling_thread_->message_loop()); +void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) { + size_t data_size = sizeof(GamepadHardwareBuffer); + base::SystemMonitor* monitor = base::SystemMonitor::Get(); + if (monitor) + monitor->AddDevicesChangedObserver(this); + bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); + CHECK(res); + GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); + memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); - if (data_fetcher_ != NULL) { - // Already initialized. - return; - } + polling_thread_.reset(new base::Thread("Gamepad polling thread")); + polling_thread_->StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0)); + + polling_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&GamepadProvider::DoInitializePollingThread, + base::Unretained(this), + base::Passed(&fetcher))); +} - if (fetcher == NULL) - fetcher = new GamepadPlatformDataFetcher; +void GamepadProvider::DoInitializePollingThread( + scoped_ptr<GamepadDataFetcher> fetcher) { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + DCHECK(!data_fetcher_.get()); // Should only initialize once. - // Pass ownership of fetcher to provider_. - data_fetcher_.reset(fetcher); + if (!fetcher.get()) + fetcher.reset(new GamepadPlatformDataFetcher); + data_fetcher_ = fetcher.Pass(); } void GamepadProvider::SendPauseHint(bool paused) { @@ -142,6 +163,8 @@ void GamepadProvider::DoPoll() { data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); hwbuf->sequence.WriteEnd(); + CheckForUserGesture(); + // Schedule our next interval of polling. ScheduleDoPoll(); } @@ -170,4 +193,18 @@ GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { return static_cast<GamepadHardwareBuffer*>(mem); } +void GamepadProvider::CheckForUserGesture() { + base::AutoLock lock(user_gesture_lock_); + if (user_gesture_observers_.empty()) + return; // Don't need to check if nobody is listening. + + if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) { + for (size_t i = 0; i < user_gesture_observers_.size(); i++) { + user_gesture_observers_[i].message_loop->PostTask(FROM_HERE, + user_gesture_observers_[i].closure); + } + user_gesture_observers_.clear(); + } +} + } // namespace content diff --git a/content/browser/gamepad/gamepad_provider.h b/content/browser/gamepad/gamepad_provider.h index 22c39c5..c4a7806 100644 --- a/content/browser/gamepad/gamepad_provider.h +++ b/content/browser/gamepad/gamepad_provider.h @@ -5,6 +5,11 @@ #ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PROVIDER_H_ #define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PROVIDER_H_ +#include <utility> +#include <vector> + +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop_proxy.h" @@ -14,6 +19,7 @@ #include "content/common/content_export.h" namespace base { +class MessageLoopProxy; class Thread; } @@ -25,13 +31,16 @@ struct GamepadHardwareBuffer; class CONTENT_EXPORT GamepadProvider : public base::SystemMonitor::DevicesChangedObserver { public: - explicit GamepadProvider(); - virtual ~GamepadProvider(); + GamepadProvider(); - // Set the platform-specific data fetcher. Mostly used for testing. - void SetDataFetcher(GamepadDataFetcher* fetcher); + // Manually specifies the data fetcher. Used for testing. + explicit GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher); + + virtual ~GamepadProvider(); - base::SharedMemoryHandle GetRendererSharedMemoryHandle( + // Returns the shared memory handle of the gamepad data duplicated into the + // given process. + base::SharedMemoryHandle GetSharedMemoryHandleForProcess( base::ProcessHandle renderer_process); // Pause and resume the background polling thread. Can be called from any @@ -39,14 +48,19 @@ class CONTENT_EXPORT GamepadProvider : void Pause(); void Resume(); + // Registers the given closure for calling when the user has interacted with + // the device. This callback will only be issued once. + void RegisterForUserGesture(const base::Closure& closure); + // base::SystemMonitor::DevicesChangedObserver implementation. virtual void OnDevicesChanged(base::SystemMonitor::DeviceType type) OVERRIDE; private: + void Initialize(scoped_ptr<GamepadDataFetcher> fetcher); // Method for setting up the platform-specific data fetcher. Takes ownership // of |fetcher|. - void DoInitializePollingThread(GamepadDataFetcher* fetcher); + void DoInitializePollingThread(scoped_ptr<GamepadDataFetcher> fetcher); // Method for sending pause hints to the low-level data fetcher. Runs on // polling_thread_. @@ -58,6 +72,9 @@ class CONTENT_EXPORT GamepadProvider : GamepadHardwareBuffer* SharedMemoryAsHardwareBuffer(); + // Checks the gamepad state to see if the user has interacted with it. + void CheckForUserGesture(); + enum { kDesiredSamplingIntervalMs = 16 }; // Keeps track of when the background thread is paused. Access to is_paused_ @@ -70,6 +87,23 @@ class CONTENT_EXPORT GamepadProvider : // |is_paused_|. bool have_scheduled_do_poll_; + // Lists all observers registered for user gestures, and the thread which + // to issue the callbacks on. Since we always issue the callback on the + // thread which the registration happened, and this class lives on the I/O + // thread, the message loop proxies will normally just be the I/O thread. + // However, this will be the main thread for unit testing. + base::Lock user_gesture_lock_; + struct ClosureAndThread { + ClosureAndThread(const base::Closure& c, + const scoped_refptr<base::MessageLoopProxy>& m); + ~ClosureAndThread(); + + base::Closure closure; + scoped_refptr<base::MessageLoopProxy> message_loop; + }; + typedef std::vector<ClosureAndThread> UserGestureObserverVector; + UserGestureObserverVector user_gesture_observers_; + // Updated based on notification from SystemMonitor when the system devices // have been updated, and this notification is passed on to the data fetcher // to enable it to avoid redundant (and possibly expensive) is-connected diff --git a/content/browser/gamepad/gamepad_provider_unittest.cc b/content/browser/gamepad/gamepad_provider_unittest.cc index d81b3f5..30f5e43 100644 --- a/content/browser/gamepad/gamepad_provider_unittest.cc +++ b/content/browser/gamepad/gamepad_provider_unittest.cc @@ -3,15 +3,14 @@ // found in the LICENSE file. #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/process_util.h" -#include "base/synchronization/waitable_event.h" -#include "base/system_monitor/system_monitor.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_provider.h" +#include "content/browser/gamepad/gamepad_test_helpers.h" #include "content/common/gamepad_hardware_buffer.h" #include "content/common/gamepad_messages.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGamepads.h" namespace content { @@ -19,35 +18,37 @@ namespace { using WebKit::WebGamepads; -class MockDataFetcher : public GamepadDataFetcher { +// Helper class to generate and record user gesture callbacks. +class UserGestureListener { public: - explicit MockDataFetcher(const WebGamepads& test_data) - : test_data_(test_data), - read_data_(false, false) { + UserGestureListener() + : weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + has_user_gesture_(false) { } - virtual void GetGamepadData(WebGamepads* pads, - bool devices_changed_hint) OVERRIDE { - *pads = test_data_; - read_data_.Signal(); + + base::Closure GetClosure() { + return base::Bind(&UserGestureListener::GotUserGesture, + weak_factory_.GetWeakPtr()); } - void WaitForDataRead() { return read_data_.Wait(); } + bool has_user_gesture() const { return has_user_gesture_; } + + private: + void GotUserGesture() { + has_user_gesture_ = true; + } - WebGamepads test_data_; - base::WaitableEvent read_data_; + base::WeakPtrFactory<UserGestureListener> weak_factory_; + bool has_user_gesture_; }; // Main test fixture -class GamepadProviderTest : public testing::Test { +class GamepadProviderTest : public testing::Test, public GamepadTestHelper { public: GamepadProvider* CreateProvider(const WebGamepads& test_data) { -#if defined(OS_MACOSX) - base::SystemMonitor::AllocateSystemIOPorts(); -#endif - system_monitor_.reset(new base::SystemMonitor); - mock_data_fetcher_ = new MockDataFetcher(test_data); - provider_.reset(new GamepadProvider); - provider_->SetDataFetcher(mock_data_fetcher_); + mock_data_fetcher_ = new MockGamepadDataFetcher(test_data); + provider_.reset(new GamepadProvider( + scoped_ptr<GamepadDataFetcher>(mock_data_fetcher_))); return provider_.get(); } @@ -55,14 +56,16 @@ class GamepadProviderTest : public testing::Test { GamepadProviderTest() { } - MessageLoop main_message_loop_; - scoped_ptr<base::SystemMonitor> system_monitor_; - MockDataFetcher* mock_data_fetcher_; scoped_ptr<GamepadProvider> provider_; + + // Pointer owned by the provider. + MockGamepadDataFetcher* mock_data_fetcher_; + + DISALLOW_COPY_AND_ASSIGN(GamepadProviderTest); }; // Crashes. http://crbug.com/106163 -TEST_F(GamepadProviderTest, DISABLED_PollingAccess) { +TEST_F(GamepadProviderTest, PollingAccess) { WebGamepads test_data; test_data.length = 1; test_data.items[0].connected = true; @@ -74,14 +77,15 @@ TEST_F(GamepadProviderTest, DISABLED_PollingAccess) { test_data.items[0].axes[1] = .5f; GamepadProvider* provider = CreateProvider(test_data); + provider->Resume(); - main_message_loop_.RunAllPending(); + message_loop().RunAllPending(); mock_data_fetcher_->WaitForDataRead(); // Renderer-side, pull data out of poll buffer. - base::SharedMemoryHandle handle = - provider->GetRendererSharedMemoryHandle(base::GetCurrentProcessHandle()); + base::SharedMemoryHandle handle = provider->GetSharedMemoryHandleForProcess( + base::GetCurrentProcessHandle()); scoped_ptr<base::SharedMemory> shared_memory( new base::SharedMemory(handle, true)); EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer))); @@ -105,6 +109,51 @@ TEST_F(GamepadProviderTest, DISABLED_PollingAccess) { EXPECT_EQ(0.5f, output.items[0].axes[1]); } +// Tests that waiting for a user gesture works properly. +TEST_F(GamepadProviderTest, UserGesture) { + WebGamepads no_button_data; + no_button_data.length = 1; + no_button_data.items[0].connected = true; + no_button_data.items[0].timestamp = 0; + no_button_data.items[0].buttonsLength = 1; + no_button_data.items[0].axesLength = 2; + no_button_data.items[0].buttons[0] = 0.f; + no_button_data.items[0].axes[0] = -1.f; + no_button_data.items[0].axes[1] = .5f; + + WebGamepads button_down_data = no_button_data; + button_down_data.items[0].buttons[0] = 1.f; + + UserGestureListener listener; + GamepadProvider* provider = CreateProvider(no_button_data); + provider->Resume(); + + // Register for a user gesture and make sure the provider reads it twice + // see below for why). + provider->RegisterForUserGesture(listener.GetClosure()); + mock_data_fetcher_->WaitForDataRead(); + mock_data_fetcher_->WaitForDataRead(); + + // It should not have issued our callback. + message_loop().RunAllPending(); + EXPECT_FALSE(listener.has_user_gesture()); + + // Set a button down and wait for it to be read twice. + // + // We wait for two reads before calling RunAllPending because the provider + // will read the data on the background thread (setting the event) and *then* + // will issue the callback on our thread. Waiting for it to read twice + // ensures that it was able to issue callbacks for the first read (if it + // issued one) before we try to check for it. + mock_data_fetcher_->SetTestData(button_down_data); + mock_data_fetcher_->WaitForDataRead(); + mock_data_fetcher_->WaitForDataRead(); + + // It should have issued our callback. + message_loop().RunAllPending(); + EXPECT_TRUE(listener.has_user_gesture()); +} + } // namespace } // namespace content diff --git a/content/browser/gamepad/gamepad_service.cc b/content/browser/gamepad/gamepad_service.cc index cb9206b..3b631ce 100644 --- a/content/browser/gamepad/gamepad_service.cc +++ b/content/browser/gamepad/gamepad_service.cc @@ -5,19 +5,22 @@ #include "content/browser/gamepad/gamepad_service.h" #include "base/bind.h" +#include "base/logging.h" #include "base/memory/singleton.h" -#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_provider.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_source.h" -#include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" namespace content { -GamepadService::GamepadService() : - num_readers_(0) { +GamepadService::GamepadService() : num_readers_(0) { +} + +GamepadService::GamepadService(scoped_ptr<GamepadDataFetcher> fetcher) + : num_readers_(0), + provider_(new GamepadProvider(fetcher.Pass())) { + thread_checker_.DetachFromThread(); } GamepadService::~GamepadService() { @@ -28,69 +31,40 @@ GamepadService* GamepadService::GetInstance() { LeakySingletonTraits<GamepadService> >::get(); } -void GamepadService::Start( - GamepadDataFetcher* data_fetcher, - RenderProcessHost* associated_rph) { +void GamepadService::AddConsumer() { + DCHECK(thread_checker_.CalledOnValidThread()); + num_readers_++; DCHECK(num_readers_ > 0); - if (provider_ == NULL) { + if (!provider_.get()) provider_.reset(new GamepadProvider); - provider_->SetDataFetcher(data_fetcher); - } provider_->Resume(); - - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&GamepadService::RegisterForTerminationNotification, - base::Unretained(this), - associated_rph)); -} - -void GamepadService::Terminate() { - provider_.reset(); } -void GamepadService::RegisterForTerminationNotification( - RenderProcessHost* rph) { - registrar_.Add(this, - NOTIFICATION_RENDERER_PROCESS_TERMINATED, - Source<RenderProcessHost>(rph)); -} +void GamepadService::RemoveConsumer() { + DCHECK(thread_checker_.CalledOnValidThread()); -base::SharedMemoryHandle GamepadService::GetSharedMemoryHandle( - base::ProcessHandle handle) { - return provider_->GetRendererSharedMemoryHandle(handle); -} - -void GamepadService::Stop(const NotificationSource& source) { --num_readers_; DCHECK(num_readers_ >= 0); - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind(&GamepadService::UnregisterForTerminationNotification, - base::Unretained(this), - source)); - if (num_readers_ == 0) provider_->Pause(); } -void GamepadService::UnregisterForTerminationNotification( - const NotificationSource& source) { - registrar_.Remove(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED, source); +void GamepadService::RegisterForUserGesture(const base::Closure& closure) { + DCHECK(num_readers_ > 0); + DCHECK(thread_checker_.CalledOnValidThread()); + provider_->RegisterForUserGesture(closure); } -void GamepadService::Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&GamepadService::Stop, base::Unretained(this), source)); +void GamepadService::Terminate() { + provider_.reset(); +} + +base::SharedMemoryHandle GamepadService::GetSharedMemoryHandleForProcess( + base::ProcessHandle handle) { + DCHECK(thread_checker_.CalledOnValidThread()); + return provider_->GetSharedMemoryHandleForProcess(handle); } -} // namespace content +} // namespace content diff --git a/content/browser/gamepad/gamepad_service.h b/content/browser/gamepad/gamepad_service.h index 4e10112..cc40c5d0 100644 --- a/content/browser/gamepad/gamepad_service.h +++ b/content/browser/gamepad/gamepad_service.h @@ -2,72 +2,76 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Owns the GamepadProvider (the background polling thread) and keeps track of -// the number of renderers currently using the data (and pausing the provider -// when not in use). - #ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H #define CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H #include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/shared_memory.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" +#include "base/threading/thread_checker.h" +#include "content/common/content_export.h" namespace content { class GamepadDataFetcher; class GamepadProvider; +class GamepadServiceTestConstructor; class RenderProcessHost; -class GamepadService : public NotificationObserver { +// Owns the GamepadProvider (the background polling thread) and keeps track of +// the number of consumers currently using the data (and pausing the provider +// when not in use). +class CONTENT_EXPORT GamepadService { public: // Returns the GamepadService singleton. static GamepadService* GetInstance(); - // Called on IO thread from a renderer host. Increments the number of users - // of the provider. The Provider is running when there's > 0 users, and is - // paused when the count drops to 0. There is no stop, the gamepad service - // registers with the RPH to be notified when the associated renderer closes - // (or crashes). - void Start(GamepadDataFetcher* fetcher, - RenderProcessHost* associated_rph); + // Increments the number of users of the provider. The Provider is running + // when there's > 0 users, and is paused when the count drops to 0. + // + // Must be called on the I/O thread. + void AddConsumer(); + + // Removes a consumer. Should be matched with an AddConsumer call. + // + // Must be called on the I/O thread. + void RemoveConsumer(); + + // Registers the given closure for calling when the user has interacted with + // the device. This callback will only be issued once. Should only be called + // while a consumer is active. + void RegisterForUserGesture(const base::Closure& closure); - base::SharedMemoryHandle GetSharedMemoryHandle(base::ProcessHandle handle); + // Returns the shared memory handle of the gamepad data duplicated into the + // given process. + base::SharedMemoryHandle GetSharedMemoryHandleForProcess( + base::ProcessHandle handle); // Stop/join with the background thread in GamepadProvider |provider_|. void Terminate(); private: friend struct DefaultSingletonTraits<GamepadService>; - GamepadService(); - virtual ~GamepadService(); - - // Called when a renderer that Start'd us is closed/crashes. - void Stop(const NotificationSource& source); + friend class GamepadServiceTestConstructor; - // Run on UI thread to receive/stop notifications of renderer closes. - void RegisterForTerminationNotification(RenderProcessHost* rph); - void UnregisterForTerminationNotification(const NotificationSource& source); + GamepadService(); - // NotificationObserver overrides: - virtual void Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) OVERRIDE; + // Constructor for testing. This specifies the data fetcher to use for a + // provider, bypassing the default platform one. + GamepadService(scoped_ptr<GamepadDataFetcher> fetcher); - // A registrar for listening notifications. Used to listen for when an - // associated renderer has gone away (possibly crashed). We don't trust - // the renderers to send a stop message because of the possibility of - // crashing. - NotificationRegistrar registrar_; + virtual ~GamepadService(); int num_readers_; scoped_ptr<GamepadProvider> provider_; + base::ThreadChecker thread_checker_; + DISALLOW_COPY_AND_ASSIGN(GamepadService); }; -} // namespace content +} // namespace content -#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H_ diff --git a/content/browser/gamepad/gamepad_test_helpers.cc b/content/browser/gamepad/gamepad_test_helpers.cc new file mode 100644 index 0000000..abf4187 --- /dev/null +++ b/content/browser/gamepad/gamepad_test_helpers.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/gamepad/gamepad_test_helpers.h" + +#include "base/system_monitor/system_monitor.h" +#include "content/browser/gamepad/gamepad_service.h" + +namespace content { + +MockGamepadDataFetcher::MockGamepadDataFetcher( + const WebKit::WebGamepads& test_data) + : test_data_(test_data), + read_data_(false, false) { +} + +MockGamepadDataFetcher::~MockGamepadDataFetcher() { +} + +void MockGamepadDataFetcher::GetGamepadData(WebKit::WebGamepads* pads, + bool devices_changed_hint) { + { + base::AutoLock lock(lock_); + *pads = test_data_; + } + read_data_.Signal(); +} + +void MockGamepadDataFetcher::WaitForDataRead() { + return read_data_.Wait(); +} + +void MockGamepadDataFetcher::SetTestData(const WebKit::WebGamepads& new_data) { + base::AutoLock lock(lock_); + test_data_ = new_data; +} + +GamepadTestHelper::GamepadTestHelper() { +#if defined(OS_MACOSX) + base::SystemMonitor::AllocateSystemIOPorts(); +#endif + system_monitor_.reset(new base::SystemMonitor); +} + +GamepadTestHelper::~GamepadTestHelper() { +} + +GamepadServiceTestConstructor::GamepadServiceTestConstructor( + const WebKit::WebGamepads& test_data) { + data_fetcher_ = new MockGamepadDataFetcher(test_data); + gamepad_service_ = + new GamepadService(scoped_ptr<GamepadDataFetcher>(data_fetcher_)); +} + +GamepadServiceTestConstructor::~GamepadServiceTestConstructor() { + delete gamepad_service_; +} + +} // namespace content diff --git a/content/browser/gamepad/gamepad_test_helpers.h b/content/browser/gamepad/gamepad_test_helpers.h new file mode 100644 index 0000000..a31b147 --- /dev/null +++ b/content/browser/gamepad/gamepad_test_helpers.h @@ -0,0 +1,90 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_TEST_HELPERS_H_ +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_TEST_HELPERS_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" +#include "content/browser/gamepad/gamepad_data_fetcher.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGamepads.h" + +namespace base { +class SystemMonitor; +} + +namespace content { + +class GamepadService; + +// Data fetcher that returns canned data for the gamepad provider. +class MockGamepadDataFetcher : public GamepadDataFetcher { + public: + // Initializes the fetcher with the given gamepad data, which will be + // returned when the provider queries us. + explicit MockGamepadDataFetcher(const WebKit::WebGamepads& test_data); + + virtual ~MockGamepadDataFetcher(); + + // GamepadDataFetcher. + virtual void GetGamepadData(WebKit::WebGamepads* pads, + bool devices_changed_hint) OVERRIDE; + + // Blocks the current thread until the GamepadProvider reads from this + // fetcher on the background thread. + void WaitForDataRead(); + + // Updates the test data. + void SetTestData(const WebKit::WebGamepads& new_data); + + private: + base::Lock lock_; + WebKit::WebGamepads test_data_; + base::WaitableEvent read_data_; + + DISALLOW_COPY_AND_ASSIGN(MockGamepadDataFetcher); +}; + +// Base class for the other test helpers. This just sets up the system monitor. +class GamepadTestHelper { + public: + GamepadTestHelper(); + virtual ~GamepadTestHelper(); + + MessageLoop& message_loop() { return message_loop_; } + + private: + // This must be constructed before the system monitor. + MessageLoop message_loop_; + + scoped_ptr<base::SystemMonitor> system_monitor_; + + DISALLOW_COPY_AND_ASSIGN(GamepadTestHelper); +}; + +// Constructs a GamepadService with a mock data source. This bypasses the +// global singleton for the gamepad service. +class GamepadServiceTestConstructor : public GamepadTestHelper { + public: + GamepadServiceTestConstructor(const WebKit::WebGamepads& test_data); + virtual ~GamepadServiceTestConstructor(); + + GamepadService* gamepad_service() { return gamepad_service_; } + MockGamepadDataFetcher* data_fetcher() { return data_fetcher_; } + + private: + // Owning pointer (can't be a scoped_ptr due to private destructor). + GamepadService* gamepad_service_; + + // Pointer owned by the provider (which is owned by the gamepad service). + MockGamepadDataFetcher* data_fetcher_; + + DISALLOW_COPY_AND_ASSIGN(GamepadServiceTestConstructor); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_TEST_HELPERS_H_ diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc index 7da9abd..c80ceee 100644 --- a/content/browser/ppapi_plugin_process_host.cc +++ b/content/browser/ppapi_plugin_process_host.cc @@ -141,6 +141,9 @@ PpapiPluginProcessHost::PpapiPluginProcessHost() : is_broker_(true) { process_.reset(new BrowserChildProcessHostImpl( content::PROCESS_TYPE_PPAPI_BROKER, this)); + + ppapi::PpapiPermissions permissions; // No permissions. + host_impl_ = new content::BrowserPpapiHostImpl(this, permissions); } bool PpapiPluginProcessHost::Init(const content::PepperPluginInfo& info) { @@ -243,6 +246,7 @@ void PpapiPluginProcessHost::RequestPluginChannel(Client* client) { } void PpapiPluginProcessHost::OnProcessLaunched() { + host_impl_->set_plugin_process_handle(process_->GetHandle()); } bool PpapiPluginProcessHost::OnMessageReceived(const IPC::Message& msg) { diff --git a/content/browser/renderer_host/gamepad_browser_message_filter.cc b/content/browser/renderer_host/gamepad_browser_message_filter.cc index 3701c7f..c06eec2 100644 --- a/content/browser/renderer_host/gamepad_browser_message_filter.cc +++ b/content/browser/renderer_host/gamepad_browser_message_filter.cc @@ -11,12 +11,14 @@ using content::BrowserMessageFilter; namespace content { -GamepadBrowserMessageFilter::GamepadBrowserMessageFilter( - content::RenderProcessHost* render_process_host) - : render_process_host_(render_process_host) { +GamepadBrowserMessageFilter::GamepadBrowserMessageFilter() + : is_started_(false) { } GamepadBrowserMessageFilter::~GamepadBrowserMessageFilter() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (is_started_) + GamepadService::GetInstance()->RemoveConsumer(); } bool GamepadBrowserMessageFilter::OnMessageReceived( @@ -36,13 +38,25 @@ bool GamepadBrowserMessageFilter::OnMessageReceived( void GamepadBrowserMessageFilter::OnGamepadStartPolling( base::SharedMemoryHandle* renderer_handle) { GamepadService* service = GamepadService::GetInstance(); - service->Start(NULL, render_process_host_); - *renderer_handle = service->GetSharedMemoryHandle(peer_handle()); + if (!is_started_) { + is_started_ = true; + service->AddConsumer(); + *renderer_handle = service->GetSharedMemoryHandleForProcess(peer_handle()); + } else { + // Currently we only expect the renderer to tell us once to start. + NOTREACHED(); + } } void GamepadBrowserMessageFilter::OnGamepadStopPolling() { // TODO(scottmg): Probably get rid of this message. We can't trust it will // arrive anyway if the renderer crashes, etc. + if (is_started_) { + is_started_ = false; + GamepadService::GetInstance()->RemoveConsumer(); + } else { + NOTREACHED(); + } } -} // namespace content +} // namespace content diff --git a/content/browser/renderer_host/gamepad_browser_message_filter.h b/content/browser/renderer_host/gamepad_browser_message_filter.h index 337c444..8f09365 100644 --- a/content/browser/renderer_host/gamepad_browser_message_filter.h +++ b/content/browser/renderer_host/gamepad_browser_message_filter.h @@ -16,12 +16,11 @@ class RenderProcessHost; class GamepadBrowserMessageFilter : public content::BrowserMessageFilter { public: - explicit GamepadBrowserMessageFilter(RenderProcessHost* rph); + GamepadBrowserMessageFilter(); // content::BrowserMessageFilter implementation. virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok) OVERRIDE; - GamepadBrowserMessageFilter(); private: virtual ~GamepadBrowserMessageFilter(); @@ -29,11 +28,11 @@ class GamepadBrowserMessageFilter : public content::BrowserMessageFilter { void OnGamepadStartPolling(base::SharedMemoryHandle* renderer_handle); void OnGamepadStopPolling(); - RenderProcessHost* render_process_host_; + bool is_started_; DISALLOW_COPY_AND_ASSIGN(GamepadBrowserMessageFilter); }; -} // namespace content +} // namespace content #endif // CONTENT_BROWSER_RENDERER_HOST_GAMEPAD_BROWSER_MESSAGE_FILTER_H_ diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc index 8caaaf6..92e5b76 100644 --- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc +++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.cc @@ -12,7 +12,8 @@ BrowserPpapiHostImpl::BrowserPpapiHostImpl( IPC::Sender* sender, const ppapi::PpapiPermissions& permissions) : host_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), - ppapi_host_(sender, &host_factory_, permissions) { + ppapi_host_(sender, &host_factory_, permissions), + plugin_process_handle_(base::kNullProcessHandle) { } BrowserPpapiHostImpl::~BrowserPpapiHostImpl() { @@ -34,4 +35,10 @@ ppapi::host::PpapiHost* BrowserPpapiHostImpl::GetPpapiHost() { return &ppapi_host_; } +base::ProcessHandle BrowserPpapiHostImpl::GetPluginProcessHandle() const { + // Handle should previously have been set before use. + DCHECK(plugin_process_handle_ != base::kNullProcessHandle); + return plugin_process_handle_; +} + } // namespace content diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h index 062755a..98edba1 100644 --- a/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h +++ b/content/browser/renderer_host/pepper/browser_ppapi_host_impl.h @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "content/browser/renderer_host/pepper/content_browser_pepper_host_factory.h" +#include "content/common/content_export.h" #include "content/public/browser/browser_ppapi_host.h" #include "ipc/ipc_channel_proxy.h" #include "ppapi/host/ppapi_host.h" @@ -18,9 +19,13 @@ class Sender; namespace content { -class BrowserPpapiHostImpl : public BrowserPpapiHost, - public IPC::ChannelProxy::MessageFilter { +class CONTENT_EXPORT BrowserPpapiHostImpl + : public BrowserPpapiHost, + public IPC::ChannelProxy::MessageFilter { public: + // The creator is responsible for calling set_plugin_process_handle as soon + // as it is known (we start the process asynchronously so it won't be known + // when this object is created). BrowserPpapiHostImpl(IPC::Sender* sender, const ppapi::PpapiPermissions& permissions); @@ -29,12 +34,20 @@ class BrowserPpapiHostImpl : public BrowserPpapiHost, // BrowserPpapiHost. virtual ppapi::host::PpapiHost* GetPpapiHost() OVERRIDE; + virtual base::ProcessHandle GetPluginProcessHandle() const OVERRIDE; + + void set_plugin_process_handle(base::ProcessHandle handle) { + plugin_process_handle_ = handle; + } private: + friend class BrowserPpapiHostTest; + virtual ~BrowserPpapiHostImpl(); ContentBrowserPepperHostFactory host_factory_; ppapi::host::PpapiHost ppapi_host_; + base::ProcessHandle plugin_process_handle_; DISALLOW_COPY_AND_ASSIGN(BrowserPpapiHostImpl); }; diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc b/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc new file mode 100644 index 0000000..078224a --- /dev/null +++ b/content/browser/renderer_host/pepper/browser_ppapi_host_test.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/renderer_host/pepper/browser_ppapi_host_test.h" + +#include "base/process_util.h" +#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h" + +namespace content { + +BrowserPpapiHostTest::BrowserPpapiHostTest() + : sink_(), + ppapi_host_(new BrowserPpapiHostImpl( + &sink_, + ppapi::PpapiPermissions::AllPermissions())) { + ppapi_host_->set_plugin_process_handle(base::GetCurrentProcessHandle()); +} + +BrowserPpapiHostTest::~BrowserPpapiHostTest() { +} + +BrowserPpapiHost* BrowserPpapiHostTest::GetPpapiHost() { + return ppapi_host_; +} + +} // namespace content diff --git a/content/browser/renderer_host/pepper/browser_ppapi_host_test.h b/content/browser/renderer_host/pepper/browser_ppapi_host_test.h new file mode 100644 index 0000000..eb7971a --- /dev/null +++ b/content/browser/renderer_host/pepper/browser_ppapi_host_test.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_BROWSER_PPAPI_HOST_TEST_H_ +#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_BROWSER_PPAPI_HOST_TEST_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/browser_ppapi_host.h" +#include "ppapi/proxy/resource_message_test_sink.h" + +namespace content { + +class BrowserPpapiHostImpl; + +// Test harness for testing Pepper resource hosts in the browser. This will +// construct a BrowserPpapiHost connected to a test sink for testing messages. +class BrowserPpapiHostTest { + public: + BrowserPpapiHostTest(); + virtual ~BrowserPpapiHostTest(); + + ppapi::proxy::ResourceMessageTestSink& sink() { return sink_; } + BrowserPpapiHost* GetPpapiHost(); + + private: + ppapi::proxy::ResourceMessageTestSink sink_; + + scoped_refptr<BrowserPpapiHostImpl> ppapi_host_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPpapiHostTest); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_BROWSER_PPAPI_HOST_TEST_H_ diff --git a/content/browser/renderer_host/pepper/pepper_gamepad_host.cc b/content/browser/renderer_host/pepper/pepper_gamepad_host.cc index 0fd3c44..a3cd890 100644 --- a/content/browser/renderer_host/pepper/pepper_gamepad_host.cc +++ b/content/browser/renderer_host/pepper/pepper_gamepad_host.cc @@ -4,10 +4,15 @@ #include "content/browser/renderer_host/pepper/pepper_gamepad_host.h" +#include "base/bind.h" +#include "content/browser/gamepad/gamepad_service.h" #include "content/public/browser/browser_ppapi_host.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/host/ppapi_host.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/ppb_gamepad_shared.h" namespace content { @@ -15,10 +20,26 @@ PepperGamepadHost::PepperGamepadHost(BrowserPpapiHost* host, PP_Instance instance, PP_Resource resource) : ResourceHost(host->GetPpapiHost(), instance, resource), - browser_ppapi_host_(host) { + browser_ppapi_host_(host), + gamepad_service_(GamepadService::GetInstance()), + is_started_(false), + weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +PepperGamepadHost::PepperGamepadHost(GamepadService* gamepad_service, + BrowserPpapiHost* host, + PP_Instance instance, + PP_Resource resource) + : ResourceHost(host->GetPpapiHost(), instance, resource), + browser_ppapi_host_(host), + gamepad_service_(gamepad_service), + is_started_(false), + weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { } PepperGamepadHost::~PepperGamepadHost() { + if (is_started_) + gamepad_service_->RemoveConsumer(); } int32_t PepperGamepadHost::OnResourceMessageReceived( @@ -33,7 +54,34 @@ int32_t PepperGamepadHost::OnResourceMessageReceived( int32_t PepperGamepadHost::OnMsgRequestMemory( ppapi::host::HostMessageContext* context) { - return PP_ERROR_FAILED; + if (is_started_) + return PP_ERROR_FAILED; + + gamepad_service_->AddConsumer(); + is_started_ = true; + + // Don't send the shared memory back until the user has interacted with the + // gamepad. This is to prevent fingerprinting and matches what the web + // platform does. + gamepad_service_->RegisterForUserGesture( + base::Bind(&PepperGamepadHost::GotUserGesture, + weak_factory_.GetWeakPtr(), + context->MakeReplyParams())); + return PP_OK_COMPLETIONPENDING; +} + +void PepperGamepadHost::GotUserGesture( + const ppapi::proxy::ResourceMessageReplyParams& in_params) { + base::SharedMemoryHandle handle = + gamepad_service_->GetSharedMemoryHandleForProcess( + browser_ppapi_host_->GetPluginProcessHandle()); + + // The shared memory handle is sent in the params struct, so we have to make + // a copy to mutate it. + ppapi::proxy::ResourceMessageReplyParams params = in_params; + params.AppendHandle(ppapi::proxy::SerializedHandle( + handle, sizeof(ppapi::ContentGamepadHardwareBuffer))); + host()->SendReply(params, PpapiPluginMsg_Gamepad_SendMemory()); } } // namespace content diff --git a/content/browser/renderer_host/pepper/pepper_gamepad_host.h b/content/browser/renderer_host/pepper/pepper_gamepad_host.h index a47d053..50181c7 100644 --- a/content/browser/renderer_host/pepper/pepper_gamepad_host.h +++ b/content/browser/renderer_host/pepper/pepper_gamepad_host.h @@ -6,18 +6,34 @@ #define CONTENT_BROWSER_RENDERER_HOST_PEPPER_PEPPER_GAMEPAD_HOST_H_ #include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "ppapi/host/resource_host.h" +namespace ppapi { +namespace proxy { +class ResourceMessageReplyParams; +} +} + namespace content { class BrowserPpapiHost; +class GamepadService; class CONTENT_EXPORT PepperGamepadHost : public ppapi::host::ResourceHost { public: PepperGamepadHost(BrowserPpapiHost* host, PP_Instance instance, PP_Resource resource); + + // Allows tests to specify a gamepad service to use rather than the global + // singleton. The caller owns the gamepad_service pointer. + PepperGamepadHost(GamepadService* gamepad_service, + BrowserPpapiHost* host, + PP_Instance instance, + PP_Resource resource); + virtual ~PepperGamepadHost(); virtual int32_t OnResourceMessageReceived( @@ -27,8 +43,16 @@ class CONTENT_EXPORT PepperGamepadHost : public ppapi::host::ResourceHost { private: int32_t OnMsgRequestMemory(ppapi::host::HostMessageContext* context); + void GotUserGesture(const ppapi::proxy::ResourceMessageReplyParams& params); + BrowserPpapiHost* browser_ppapi_host_; + GamepadService* gamepad_service_; + + bool is_started_; + + base::WeakPtrFactory<PepperGamepadHost> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(PepperGamepadHost); }; diff --git a/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc b/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc new file mode 100644 index 0000000..c5a09f7 --- /dev/null +++ b/content/browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc @@ -0,0 +1,204 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string.h> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/gamepad/gamepad_test_helpers.h" +#include "content/browser/renderer_host/pepper/browser_ppapi_host_test.h" +#include "content/browser/renderer_host/pepper/pepper_gamepad_host.h" +#include "content/common/gamepad_hardware_buffer.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/host/host_message_context.h" +#include "ppapi/proxy/gamepad_resource.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/resource_message_params.h" +#include "ppapi/shared_impl/ppb_gamepad_shared.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +class PepperGamepadHostTest + : public testing::Test, + public BrowserPpapiHostTest { + public: + PepperGamepadHostTest() { + } + ~PepperGamepadHostTest() { + } + + void ConstructService(const WebKit::WebGamepads& test_data) { + service_.reset(new GamepadServiceTestConstructor(test_data)); + } + + GamepadService* gamepad_service() { return service_->gamepad_service(); } + + protected: + scoped_ptr<GamepadServiceTestConstructor> service_; + + DISALLOW_COPY_AND_ASSIGN(PepperGamepadHostTest); +}; + +inline ptrdiff_t AddressDiff(const void* a, const void* b) { + return static_cast<const char*>(a) - static_cast<const char*>(b); +} + +} // namespace + +// Validate the memory layout of the Pepper proxy struct matches the content +// one. The proxy can't depend on content so has a duplicate definition. This +// code can see both definitions so we do the validation here. +TEST_F(PepperGamepadHostTest, ValidateHardwareBuffersMatch) { + // Hardware buffer. + COMPILE_ASSERT(sizeof(ppapi::ContentGamepadHardwareBuffer) == + sizeof(content::GamepadHardwareBuffer), + gamepad_hardware_buffers_must_match); + ppapi::ContentGamepadHardwareBuffer ppapi_buf; + GamepadHardwareBuffer content_buf; + EXPECT_EQ(AddressDiff(&content_buf.sequence, &content_buf), + AddressDiff(&ppapi_buf.sequence, &ppapi_buf)); + EXPECT_EQ(AddressDiff(&content_buf.buffer, &content_buf), + AddressDiff(&ppapi_buf.buffer, &ppapi_buf)); +} + +TEST_F(PepperGamepadHostTest, ValidateGamepadsMatch) { + // Gamepads. + COMPILE_ASSERT(sizeof(ppapi::WebKitGamepads) == + sizeof(WebKit::WebGamepads), + gamepads_data_must_match); + ppapi::WebKitGamepads ppapi_gamepads; + WebKit::WebGamepads web_gamepads; + EXPECT_EQ(AddressDiff(&web_gamepads.length, &web_gamepads), + AddressDiff(&ppapi_gamepads.length, &ppapi_gamepads)); + + // See comment below on storage & the EXPECT macro. + size_t webkit_items_length_cap = WebKit::WebGamepads::itemsLengthCap; + size_t ppapi_items_length_cap = ppapi::WebKitGamepads::kItemsLengthCap; + EXPECT_EQ(webkit_items_length_cap, ppapi_items_length_cap); + + for (size_t i = 0; i < web_gamepads.itemsLengthCap; i++) { + EXPECT_EQ(AddressDiff(&web_gamepads.items[0], &web_gamepads), + AddressDiff(&ppapi_gamepads.items[0], &ppapi_gamepads)); + } +} + +TEST_F(PepperGamepadHostTest, ValidateGamepadMatch) { + // Gamepad. + COMPILE_ASSERT(sizeof(ppapi::WebKitGamepad) == + sizeof(WebKit::WebGamepad), + gamepad_data_must_match); + ppapi::WebKitGamepad ppapi_gamepad; + WebKit::WebGamepad web_gamepad; + + // Using EXPECT seems to force storage for the parameter, which the constants + // in the WebKit/PPAPI headers don't have. So we have to use temporaries + // before comparing them. + size_t webkit_id_length_cap = WebKit::WebGamepad::idLengthCap; + size_t ppapi_id_length_cap = ppapi::WebKitGamepad::kIdLengthCap; + EXPECT_EQ(webkit_id_length_cap, ppapi_id_length_cap); + + size_t webkit_axes_length_cap = WebKit::WebGamepad::axesLengthCap; + size_t ppapi_axes_length_cap = ppapi::WebKitGamepad::kAxesLengthCap; + EXPECT_EQ(webkit_axes_length_cap, ppapi_axes_length_cap); + + size_t webkit_buttons_length_cap = WebKit::WebGamepad::buttonsLengthCap; + size_t ppapi_buttons_length_cap = ppapi::WebKitGamepad::kButtonsLengthCap; + EXPECT_EQ(webkit_buttons_length_cap, ppapi_buttons_length_cap); + + EXPECT_EQ(AddressDiff(&web_gamepad.connected, &web_gamepad), + AddressDiff(&ppapi_gamepad.connected, &ppapi_gamepad)); + EXPECT_EQ(AddressDiff(&web_gamepad.id, &web_gamepad), + AddressDiff(&ppapi_gamepad.id, &ppapi_gamepad)); + EXPECT_EQ(AddressDiff(&web_gamepad.timestamp, &web_gamepad), + AddressDiff(&ppapi_gamepad.timestamp, &ppapi_gamepad)); + EXPECT_EQ(AddressDiff(&web_gamepad.axesLength, &web_gamepad), + AddressDiff(&ppapi_gamepad.axes_length, &ppapi_gamepad)); + EXPECT_EQ(AddressDiff(&web_gamepad.axes, &web_gamepad), + AddressDiff(&ppapi_gamepad.axes, &ppapi_gamepad)); + EXPECT_EQ(AddressDiff(&web_gamepad.buttonsLength, &web_gamepad), + AddressDiff(&ppapi_gamepad.buttons_length, &ppapi_gamepad)); + EXPECT_EQ(AddressDiff(&web_gamepad.buttons, &web_gamepad), + AddressDiff(&ppapi_gamepad.buttons, &ppapi_gamepad)); +} + +TEST_F(PepperGamepadHostTest, WaitForReply) { + WebKit::WebGamepads default_data; + memset(&default_data, 0, sizeof(WebKit::WebGamepads)); + default_data.length = 1; + default_data.items[0].connected = true; + default_data.items[0].buttonsLength = 1; + ConstructService(default_data); + + PP_Instance pp_instance = 12345; + PP_Resource pp_resource = 67890; + PepperGamepadHost gamepad_host(gamepad_service(), GetPpapiHost(), + pp_instance, pp_resource); + + // Synthesize a request for gamepad data. + ppapi::host::HostMessageContext context( + ppapi::proxy::ResourceMessageCallParams(pp_resource, 1)); + EXPECT_EQ(PP_OK_COMPLETIONPENDING, + gamepad_host.OnResourceMessageReceived( + PpapiHostMsg_Gamepad_RequestMemory(), + &context)); + + // Wait for the gamepad background thread to read twice to make sure we + // don't get a message yet (see below for why). + MockGamepadDataFetcher* fetcher = service_->data_fetcher(); + fetcher->WaitForDataRead(); + fetcher->WaitForDataRead(); + + // It should not have sent the callback message. + service_->message_loop().RunAllPending(); + EXPECT_EQ(0u, sink().message_count()); + + // Set a button down and wait for it to be read twice. + // + // We wait for two reads before calling RunAllPending because the provider + // will read the data on the background thread (setting the event) and *then* + // will issue the callback on our thread. Waiting for it to read twice + // ensures that it was able to issue callbacks for the first read (if it + // issued one) before we try to check for it. + WebKit::WebGamepads button_down_data = default_data; + button_down_data.items[0].buttons[0] = 1.f; + fetcher->SetTestData(button_down_data); + fetcher->WaitForDataRead(); + fetcher->WaitForDataRead(); + + // It should have sent a callback. + service_->message_loop().RunAllPending(); + ppapi::proxy::ResourceMessageReplyParams reply_params; + IPC::Message reply_msg; + ASSERT_TRUE(sink().GetFirstResourceReplyMatching( + PpapiPluginMsg_Gamepad_SendMemory::ID, &reply_params, &reply_msg)); + + // Extract the shared memory handle. + base::SharedMemoryHandle reply_handle; + EXPECT_TRUE(reply_params.GetSharedMemoryHandleAtIndex(0, &reply_handle)); + + // Validate the shared memory. + base::SharedMemory shared_memory(reply_handle, true); + EXPECT_TRUE(shared_memory.Map(sizeof(ppapi::ContentGamepadHardwareBuffer))); + const ppapi::ContentGamepadHardwareBuffer* buffer = + static_cast<const ppapi::ContentGamepadHardwareBuffer*>( + shared_memory.memory()); + EXPECT_EQ(button_down_data.length, buffer->buffer.length); + EXPECT_EQ(button_down_data.items[0].buttonsLength, + buffer->buffer.items[0].buttons_length); + for (size_t i = 0; i < ppapi::WebKitGamepad::kButtonsLengthCap; i++) { + EXPECT_EQ(button_down_data.items[0].buttons[i], + buffer->buffer.items[0].buttons[i]); + } + + // Duplicate requests should be denied. + EXPECT_EQ(PP_ERROR_FAILED, + gamepad_host.OnResourceMessageReceived( + PpapiHostMsg_Gamepad_RequestMemory(), + &context)); +} + +} // namespace content diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 4f7c624..648aabf 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -607,7 +607,7 @@ void RenderProcessHostImpl::CreateMessageFilters() { GetID(), storage_partition_impl_->GetQuotaManager(), GetContentClient()->browser()->CreateQuotaPermissionContext())); - channel_->AddFilter(new GamepadBrowserMessageFilter(this)); + channel_->AddFilter(new GamepadBrowserMessageFilter()); channel_->AddFilter(new ProfilerMessageFilter(PROCESS_TYPE_RENDERER)); channel_->AddFilter(new HistogramMessageFilter()); channel_->AddFilter(new HyphenatorMessageFilter(this)); diff --git a/content/common/gamepad_hardware_buffer.h b/content/common/gamepad_hardware_buffer.h index 077c782..133b751 100644 --- a/content/common/gamepad_hardware_buffer.h +++ b/content/common/gamepad_hardware_buffer.h @@ -28,6 +28,6 @@ struct GamepadHardwareBuffer { WebKit::WebGamepads buffer; }; -} +} // namespace content #endif // CONTENT_COMMON_GAMEPAD_HARDWARE_BUFFER_H_ diff --git a/content/common/gamepad_user_gesture.cc b/content/common/gamepad_user_gesture.cc new file mode 100644 index 0000000..8fa42b5 --- /dev/null +++ b/content/common/gamepad_user_gesture.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/common/gamepad_user_gesture.h" + +#include <algorithm> + +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGamepads.h" + +namespace content { + +bool GamepadsHaveUserGesture(const WebKit::WebGamepads& gamepads) { + for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; i++) { + const WebKit::WebGamepad& pad = gamepads.items[i]; + + // If the device is physically connected, then check the primary 4 buttons + // to see if there is currently an intentional user action. + if (pad.connected) { + const unsigned kPrimaryInteractionButtons = 4; + unsigned buttons_to_check = std::min(pad.buttonsLength, + kPrimaryInteractionButtons); + for (unsigned button_index = 0; button_index < buttons_to_check; + button_index++) { + if (pad.buttons[button_index] > 0.5f) + return true; + } + } + } + return false; +} + +} // namespace content diff --git a/content/common/gamepad_user_gesture.h b/content/common/gamepad_user_gesture.h new file mode 100644 index 0000000..d17c69c --- /dev/null +++ b/content/common/gamepad_user_gesture.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_COMMON_GAMEPAD_USER_GESTURE_H_ +#define CONTENT_COMMON_GAMEPAD_USER_GESTURE_H_ + +namespace WebKit { +class WebGamepads; +} + +namespace content { + +// Returns true if any of the gamepads have a button pressed that would be +// considerd a user gesture for interaction. +bool GamepadsHaveUserGesture(const WebKit::WebGamepads& gamepads); + +} // namespace content + +#endif // CONTENT_COMMON_GAMEPAD_USER_GESTURE_H_ diff --git a/content/content_browser.gypi b/content/content_browser.gypi index a735037..33bc250 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -357,7 +357,14 @@ 'browser/fileapi/fileapi_message_filter.cc', 'browser/fileapi/fileapi_message_filter.h', 'browser/font_list_async.cc', - 'browser/gamepad/data_fetcher.h', + 'browser/gamepad/gamepad_data_fetcher.h', + 'browser/gamepad/gamepad_platform_data_fetcher.h', + 'browser/gamepad/gamepad_platform_data_fetcher_linux.cc', + 'browser/gamepad/gamepad_platform_data_fetcher_linux.h', + 'browser/gamepad/gamepad_platform_data_fetcher_mac.h', + 'browser/gamepad/gamepad_platform_data_fetcher_mac.mm', + 'browser/gamepad/gamepad_platform_data_fetcher_win.cc', + 'browser/gamepad/gamepad_platform_data_fetcher_win.h', 'browser/gamepad/gamepad_provider.cc', 'browser/gamepad/gamepad_provider.h', 'browser/gamepad/gamepad_service.cc', @@ -365,13 +372,6 @@ 'browser/gamepad/gamepad_standard_mappings.h', 'browser/gamepad/gamepad_standard_mappings_linux.cc', 'browser/gamepad/gamepad_standard_mappings_mac.mm', - 'browser/gamepad/platform_data_fetcher.h', - 'browser/gamepad/platform_data_fetcher_linux.cc', - 'browser/gamepad/platform_data_fetcher_linux.h', - 'browser/gamepad/platform_data_fetcher_mac.h', - 'browser/gamepad/platform_data_fetcher_mac.mm', - 'browser/gamepad/platform_data_fetcher_win.cc', - 'browser/gamepad/platform_data_fetcher_win.h', 'browser/geolocation/arbitrator_dependency_factory.cc', 'browser/geolocation/arbitrator_dependency_factory.h', 'browser/geolocation/core_location_data_provider_mac.h', diff --git a/content/content_common.gypi b/content/content_common.gypi index 62c240b..25c561c 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -198,6 +198,8 @@ 'common/gamepad_messages.h', 'common/gamepad_seqlock.cc', 'common/gamepad_seqlock.h', + 'common/gamepad_user_gesture.cc', + 'common/gamepad_user_gesture.h', 'common/geolocation_messages.h', 'common/gpu/client/command_buffer_proxy_impl.cc', 'common/gpu/client/command_buffer_proxy_impl.h', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 215fa23a..446fb47 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -14,6 +14,10 @@ 'content_browser', 'content_common', '../net/net.gyp:net_test_support', + '../ppapi/ppapi_internal.gyp:ppapi_host', + '../ppapi/ppapi_internal.gyp:ppapi_proxy', + '../ppapi/ppapi_internal.gyp:ppapi_shared', + '../ppapi/ppapi_internal.gyp:ppapi_unittest_shared', '../skia/skia.gyp:skia', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', @@ -262,6 +266,8 @@ 'browser/download/file_metadata_unittest_linux.cc', 'browser/download/save_package_unittest.cc', 'browser/gamepad/gamepad_provider_unittest.cc', + 'browser/gamepad/gamepad_test_helpers.cc', + 'browser/gamepad/gamepad_test_helpers.h', 'browser/geolocation/device_data_provider_unittest.cc', 'browser/geolocation/geolocation_provider_unittest.cc', 'browser/geolocation/gps_location_provider_unittest_linux.cc', @@ -291,6 +297,9 @@ 'browser/renderer_host/media/video_capture_controller_unittest.cc', 'browser/renderer_host/media/video_capture_host_unittest.cc', 'browser/renderer_host/media/video_capture_manager_unittest.cc', + 'browser/renderer_host/pepper/browser_ppapi_host_test.cc', + 'browser/renderer_host/pepper/browser_ppapi_host_test.h', + 'browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc', 'browser/renderer_host/render_view_host_unittest.cc', 'browser/renderer_host/render_widget_host_unittest.cc', 'browser/renderer_host/render_widget_host_view_aura_unittest.cc', diff --git a/content/ppapi_plugin/ppapi_thread.cc b/content/ppapi_plugin/ppapi_thread.cc index 970468d..7e1c3bc 100644 --- a/content/ppapi_plugin/ppapi_thread.cc +++ b/content/ppapi_plugin/ppapi_thread.cc @@ -94,6 +94,8 @@ bool PpapiThread::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnMsgLoadPlugin) IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnMsgCreateChannel) + IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnMsgResourceReply) + IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPServerSocket_ListenACK, OnPluginDispatcherMessageReceived(msg)) IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPServerSocket_AcceptACK, @@ -306,6 +308,13 @@ void PpapiThread::OnMsgCreateChannel(int renderer_id, Send(new PpapiHostMsg_ChannelCreated(channel_handle)); } +void PpapiThread::OnMsgResourceReply( + const ppapi::proxy::ResourceMessageReplyParams& reply_params, + const IPC::Message& nested_msg) { + ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params, + nested_msg); +} + void PpapiThread::OnMsgSetNetworkState(bool online) { if (!get_plugin_interface_) return; diff --git a/content/ppapi_plugin/ppapi_thread.h b/content/ppapi_plugin/ppapi_thread.h index 06ed0e3..0329809 100644 --- a/content/ppapi_plugin/ppapi_thread.h +++ b/content/ppapi_plugin/ppapi_thread.h @@ -70,6 +70,9 @@ class PpapiThread : public ChildThread, void OnMsgLoadPlugin(const FilePath& path); void OnMsgCreateChannel(int renderer_id, bool incognito); + void OnMsgResourceReply( + const ppapi::proxy::ResourceMessageReplyParams& reply_params, + const IPC::Message& nested_msg); void OnMsgSetNetworkState(bool online); void OnPluginDispatcherMessageReceived(const IPC::Message& msg); diff --git a/content/public/browser/browser_ppapi_host.h b/content/public/browser/browser_ppapi_host.h index a25a6d0..aa97990 100644 --- a/content/public/browser/browser_ppapi_host.h +++ b/content/public/browser/browser_ppapi_host.h @@ -5,6 +5,9 @@ #ifndef CONTENT_PUBLIC_BROWSER_BROWSER_PPAPI_HOST_H_ #define CONTENT_PUBLIC_BROWSER_BROWSER_PPAPI_HOST_H_ +#include "base/process.h" +#include "content/common/content_export.h" + namespace ppapi { namespace host { class PpapiHost; @@ -18,11 +21,14 @@ namespace content { // // There will be one of these objects in the browser per plugin process. It // lives entirely on the I/O thread. -class BrowserPpapiHost { +class CONTENT_EXPORT BrowserPpapiHost { public: // Returns the PpapiHost object. virtual ppapi::host::PpapiHost* GetPpapiHost() = 0; + // Returns the handle to the plugin process. + virtual base::ProcessHandle GetPluginProcessHandle() const = 0; + protected: virtual ~BrowserPpapiHost() {} }; diff --git a/content/renderer/gamepad_shared_memory_reader.cc b/content/renderer/gamepad_shared_memory_reader.cc index 55c6f62..f4e8194 100644 --- a/content/renderer/gamepad_shared_memory_reader.cc +++ b/content/renderer/gamepad_shared_memory_reader.cc @@ -7,6 +7,7 @@ #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" #include "content/common/gamepad_messages.h" +#include "content/common/gamepad_user_gesture.h" #include "content/public/renderer/render_thread.h" #include "content/common/gamepad_hardware_buffer.h" #include "ipc/ipc_sync_message_filter.h" @@ -14,8 +15,8 @@ namespace content { GamepadSharedMemoryReader::GamepadSharedMemoryReader() - : gamepad_hardware_buffer_(NULL) { - memset(ever_interacted_with_, 0, sizeof(ever_interacted_with_)); + : gamepad_hardware_buffer_(NULL), + ever_interacted_with_(false) { CHECK(RenderThread::Get()->Send(new GamepadHostMsg_StartPolling( &renderer_shared_memory_handle_))); // If we don't get a valid handle from the browser, don't try to Map (we're @@ -35,6 +36,12 @@ GamepadSharedMemoryReader::GamepadSharedMemoryReader() } void GamepadSharedMemoryReader::SampleGamepads(WebKit::WebGamepads& gamepads) { + // ========== + // DANGER + // ========== + // + // This logic is duplicated in Pepper as well. If you change it, that also + // needs to be in sync. See ppapi/proxy/gamepad_resource.cc. WebKit::WebGamepads read_into; TRACE_EVENT0("GAMEPAD", "SampleGamepads"); @@ -58,32 +65,25 @@ void GamepadSharedMemoryReader::SampleGamepads(WebKit::WebGamepads& gamepads) { UMA_HISTOGRAM_COUNTS("Gamepad.ReadContentionCount", contention_count); if (contention_count >= kMaximumContentionCount) { - // We failed to successfully read, presumably because the hardware - // thread was taking unusually long. Don't copy the data to the output - // buffer, and simply leave what was there before. - return; + // We failed to successfully read, presumably because the hardware + // thread was taking unusually long. Don't copy the data to the output + // buffer, and simply leave what was there before. + return; } // New data was read successfully, copy it into the output buffer. memcpy(&gamepads, &read_into, sizeof(gamepads)); - // Override the "connected" with false until the user has interacted - // with the gamepad. This is to prevent fingerprinting on drive-by pages. - for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; ++i) { - WebKit::WebGamepad& pad = gamepads.items[i]; - // If the device is physically connected, then check if we should - // keep it disabled. We track if any of the primary 4 buttons have been - // pressed to determine a reasonable intentional interaction from the user. - if (pad.connected) { - if (ever_interacted_with_[i]) - continue; - const unsigned kPrimaryInteractionButtons = 4; - for (unsigned j = 0; j < kPrimaryInteractionButtons; ++j) - ever_interacted_with_[i] |= pad.buttons[j] > 0.5f; - // If we've not previously set, and the user still hasn't touched - // these buttons, then don't pass the data on to the Chromium port. - if (!ever_interacted_with_[i]) - pad.connected = false; + if (!ever_interacted_with_) { + if (GamepadsHaveUserGesture(gamepads)) { + ever_interacted_with_ = true; + } else { + // Clear the connected flag if the user hasn't interacted with any of the + // gamepads to prevent fingerprinting. The actual data is not cleared. + // WebKit will only copy out data into the JS buffers for connected + // gamepads so this is sufficient. + for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; i++) + gamepads.items[i].connected = false; } } } diff --git a/content/renderer/gamepad_shared_memory_reader.h b/content/renderer/gamepad_shared_memory_reader.h index 9a85025..a217c16 100644 --- a/content/renderer/gamepad_shared_memory_reader.h +++ b/content/renderer/gamepad_shared_memory_reader.h @@ -24,7 +24,7 @@ class GamepadSharedMemoryReader { scoped_ptr<base::SharedMemory> renderer_shared_memory_; GamepadHardwareBuffer* gamepad_hardware_buffer_; - bool ever_interacted_with_[WebKit::WebGamepads::itemsLengthCap]; + bool ever_interacted_with_; }; } // namespace content diff --git a/content/renderer/pepper/pepper_file_chooser_host.cc b/content/renderer/pepper/pepper_file_chooser_host.cc index 4d15151..f4faa9f 100644 --- a/content/renderer/pepper/pepper_file_chooser_host.cc +++ b/content/renderer/pepper/pepper_file_chooser_host.cc @@ -166,9 +166,7 @@ int32_t PepperFileChooserHost::OnMsgShow( return PP_ERROR_NOACCESS; } - reply_params_ = ppapi::proxy::ResourceMessageReplyParams( - context->params.pp_resource(), - context->params.sequence()); + reply_params_ = context->MakeReplyParams(); return PP_OK_COMPLETIONPENDING; } diff --git a/ppapi/examples/gamepad/gamepad.cc b/ppapi/examples/gamepad/gamepad.cc index 96a127c..4fedc0b 100644 --- a/ppapi/examples/gamepad/gamepad.cc +++ b/ppapi/examples/gamepad/gamepad.cc @@ -85,7 +85,7 @@ class MyInstance : public pp::Instance { } pp::ImageData PaintImage(const pp::Size& size) { - pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, false); + pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, true); if (image.is_null()) return image; diff --git a/ppapi/host/dispatch_host_message.h b/ppapi/host/dispatch_host_message.h index 9a3fae5..b01a0ed 100644 --- a/ppapi/host/dispatch_host_message.h +++ b/ppapi/host/dispatch_host_message.h @@ -79,10 +79,9 @@ inline int32_t DispatchResourceCall(ObjT* obj, Method method, #define PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(msg_class, member_func) \ case msg_class::ID: { \ - TRACK_RUN_IN_IPC_HANDLER(member_func); \ - return member_func(context); \ - } - + TRACK_RUN_IN_IPC_HANDLER(member_func); \ + return member_func(context); \ + } } // namespace host } // namespace ppapi diff --git a/ppapi/host/host_message_context.cc b/ppapi/host/host_message_context.cc new file mode 100644 index 0000000..5c1c0d1 --- /dev/null +++ b/ppapi/host/host_message_context.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ppapi/host/host_message_context.h" + +namespace ppapi { +namespace host { + +HostMessageContext::HostMessageContext( + const ppapi::proxy::ResourceMessageCallParams& cp) + : params(cp) { +} + +HostMessageContext::~HostMessageContext() { +} + +ppapi::proxy::ResourceMessageReplyParams HostMessageContext::MakeReplyParams() { + return ppapi::proxy::ResourceMessageReplyParams(params.pp_resource(), + params.sequence()); +} + +} // namespace host +} // namespace ppapi diff --git a/ppapi/host/host_message_context.h b/ppapi/host/host_message_context.h index a8ac526..6708261 100644 --- a/ppapi/host/host_message_context.h +++ b/ppapi/host/host_message_context.h @@ -15,9 +15,13 @@ namespace host { // This context structure provides information about incoming resource message // call requests when passed to resources. struct PPAPI_HOST_EXPORT HostMessageContext { - explicit HostMessageContext(const ppapi::proxy::ResourceMessageCallParams& cp) - : params(cp) { - } + explicit HostMessageContext( + const ppapi::proxy::ResourceMessageCallParams& cp); + ~HostMessageContext(); + + // Returns a "reply params" struct with the same resource and sequence number + // as this request. + ppapi::proxy::ResourceMessageReplyParams MakeReplyParams(); // The original call parameters passed to the resource message call. const ppapi::proxy::ResourceMessageCallParams& params; diff --git a/ppapi/host/ppapi_host.cc b/ppapi/host/ppapi_host.cc index 14a3a4f..247c781 100644 --- a/ppapi/host/ppapi_host.cc +++ b/ppapi/host/ppapi_host.cc @@ -91,7 +91,8 @@ void PpapiHost::OnHostMsgResourceCall( reply_params.set_result(resource_host->OnResourceMessageReceived( nested_msg, &context)); - // Sanity check the resource handler. + // Sanity check the resource handler. Note if the result was + // "completion pending" the resource host may have already sent the reply. if (reply_params.result() == PP_OK_COMPLETIONPENDING) { // Message handler should have only returned a pending result if a // response will be sent to the plugin. diff --git a/ppapi/host/ppapi_host.h b/ppapi/host/ppapi_host.h index 5bc284c..3f3605c5 100644 --- a/ppapi/host/ppapi_host.h +++ b/ppapi/host/ppapi_host.h @@ -10,8 +10,8 @@ #include "base/compiler_specific.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" -#include "base/observer_list.h" #include "base/memory/scoped_vector.h" +#include "base/observer_list.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_sender.h" #include "ppapi/c/pp_instance.h" diff --git a/ppapi/host/resource_host.h b/ppapi/host/resource_host.h index c1e4b74..27d87b3 100644 --- a/ppapi/host/resource_host.h +++ b/ppapi/host/resource_host.h @@ -39,7 +39,9 @@ class PPAPI_HOST_EXPORT ResourceHost { // // You can do a response asynchronously by returning PP_OK_COMPLETIONPENDING. // This will cause the reply to be skipped, and the class implementing this - // function will take responsibility for issuing the callback later. + // function will take responsibility for issuing the callback. The callback + // can be issued inside OnResourceMessageReceived before it returns, or at + // a future time. // // If you don't have a particular reply message, you can just ignore // the reply in the message context. However, if you have a reply more than diff --git a/ppapi/ppapi_host.gypi b/ppapi/ppapi_host.gypi index 079125e..8967e45 100644 --- a/ppapi/ppapi_host.gypi +++ b/ppapi/ppapi_host.gypi @@ -23,6 +23,7 @@ 'sources': [ 'host/dispatch_host_message.h', 'host/host_factory.h', + 'host/host_message_context.cc', 'host/host_message_context.h', 'host/instance_message_filter.cc', 'host/instance_message_filter.h', diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi index 22d2ceb..2e8ca9f 100644 --- a/ppapi/ppapi_shared.gypi +++ b/ppapi/ppapi_shared.gypi @@ -55,6 +55,8 @@ 'shared_impl/ppb_file_ref_shared.h', 'shared_impl/ppb_flash_shared.cc', 'shared_impl/ppb_flash_shared.h', + 'shared_impl/ppb_gamepad_shared.cc', + 'shared_impl/ppb_gamepad_shared.h', 'shared_impl/ppb_graphics_3d_shared.cc', 'shared_impl/ppb_graphics_3d_shared.h', 'shared_impl/ppb_image_data_shared.cc', diff --git a/ppapi/proxy/dispatch_reply_message.h b/ppapi/proxy/dispatch_reply_message.h index 443c523..0dc3739 100644 --- a/ppapi/proxy/dispatch_reply_message.h +++ b/ppapi/proxy/dispatch_reply_message.h @@ -63,16 +63,23 @@ inline void DispatchResourceReply(ObjT* obj, Method method, #define PPAPI_DISPATCH_RESOURCE_REPLY(msg_class, member_func) \ case msg_class::ID: { \ - TRACK_RUN_IN_IPC_HANDLER(member_func); \ - msg_class::Schema::Param p; \ - if (msg_class::Read(&ipc_message__, &p)) { \ - ppapi::proxy::DispatchResourceReply( \ - this, \ - &_IpcMessageHandlerClass::member_func, \ - params, p); \ - } \ + TRACK_RUN_IN_IPC_HANDLER(member_func); \ + msg_class::Schema::Param p; \ + if (msg_class::Read(&ipc_message__, &p)) { \ + ppapi::proxy::DispatchResourceReply( \ + this, \ + &_IpcMessageHandlerClass::member_func, \ + params, p); \ } \ - break; + break; \ + } + +#define PPAPI_DISPATCH_RESOURCE_REPLY_0(msg_class, member_func) \ + case msg_class::ID: { \ + TRACK_RUN_IN_IPC_HANDLER(member_func); \ + member_func(params); \ + break; \ + } } // namespace proxy } // namespace ppapi diff --git a/ppapi/proxy/gamepad_resource.cc b/ppapi/proxy/gamepad_resource.cc index 21ee02d..446c11c 100644 --- a/ppapi/proxy/gamepad_resource.cc +++ b/ppapi/proxy/gamepad_resource.cc @@ -6,16 +6,47 @@ #include <string.h> -#include "ppapi/c/ppb_gamepad.h" +#include "base/threading/platform_thread.h" #include "ppapi/proxy/dispatch_reply_message.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/ppb_gamepad_shared.h" namespace ppapi { namespace proxy { +namespace { + +// This is the read logic from content/common/gamepad_seqlock.h +base::subtle::Atomic32 ReadBegin(const base::subtle::Atomic32* sequence) { + base::subtle::Atomic32 version; + for (;;) { + version = base::subtle::NoBarrier_Load(sequence); + + // If the counter is even, then the associated data might be in a + // consistent state, so we can try to read. + if ((version & 1) == 0) + break; + + // Otherwise, the writer is in the middle of an update. Retry the read. + base::PlatformThread::YieldCurrentThread(); + } + return version; +} + +bool ReadRetry(const base::subtle::Atomic32* sequence, + base::subtle::Atomic32 version) { + // If the sequence number was updated then a read should be re-attempted. + // -- Load fence, read membarrier + return base::subtle::Release_Load(sequence) != version; +} + +} // namespace + GamepadResource::GamepadResource(Connection connection, PP_Instance instance) : PluginResource(connection, instance), buffer_(NULL) { + memset(&last_read_, 0, sizeof(last_read_)); + SendCreateToBrowser(PpapiHostMsg_Gamepad_Create()); CallBrowser(PpapiHostMsg_Gamepad_RequestMemory()); } @@ -28,30 +59,56 @@ void GamepadResource::Sample(PP_GamepadsSampleData* data) { // Browser hasn't sent back our shared memory, give the plugin gamepad // data corresponding to "not connected". memset(data, 0, sizeof(PP_GamepadsSampleData)); - } else { - memcpy(data, buffer_, sizeof(PP_GamepadsSampleData)); + return; } + + // ========== + // DANGER + // ========== + // + // This logic is duplicated in the renderer as well. If you change it, that + // also needs to be in sync. See gamepad_shared_memory_reader.cc. + + // Only try to read this many times before failing to avoid waiting here + // very long in case of contention with the writer. + const int kMaximumContentionCount = 10; + int contention_count = -1; + base::subtle::Atomic32 version; + WebKitGamepads read_into; + do { + version = ReadBegin(&buffer_->sequence); + memcpy(&read_into, &buffer_->buffer, sizeof(read_into)); + ++contention_count; + if (contention_count == kMaximumContentionCount) + break; + } while (ReadRetry(&buffer_->sequence, version)); + + // In the event of a read failure, just leave the last read data as-is (the + // hardware thread is taking unusally long). + if (contention_count < kMaximumContentionCount) + ConvertWebKitGamepadData(read_into, &last_read_); + + memcpy(data, &last_read_, sizeof(PP_GamepadsSampleData)); } void GamepadResource::OnReplyReceived(const ResourceMessageReplyParams& params, const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(GamepadResource, msg) - PPAPI_DISPATCH_RESOURCE_REPLY(PpapiPluginMsg_Gamepad_SendMemory, - OnPluginMsgSendMemory) + PPAPI_DISPATCH_RESOURCE_REPLY_0(PpapiPluginMsg_Gamepad_SendMemory, + OnPluginMsgSendMemory) IPC_END_MESSAGE_MAP() } void GamepadResource::OnPluginMsgSendMemory( - const ResourceMessageReplyParams& params, - base::SharedMemoryHandle shared_memory_handle) { - /* TODO(brettw) implement this when we have shared gamepad code. It would be - something like this: - shared_memory_.reset( - new base::SharedMemory(shared_memory_handle, true)); - CHECK(shared_memory_->Map(sizeof(GamepadHardwareBuffer))); - void *memory = shared_memory_->memory(); - // Use the memory... - */ + const ResourceMessageReplyParams& params) { + // On failure, the handle will be null and the CHECK below will be tripped. + base::SharedMemoryHandle handle; + params.GetSharedMemoryHandleAtIndex(0, &handle); + + shared_memory_.reset(new base::SharedMemory(handle, true)); + CHECK(shared_memory_->Map(sizeof(ContentGamepadHardwareBuffer))); + buffer_ = static_cast<const ContentGamepadHardwareBuffer*>( + shared_memory_->memory()); } } // namespace proxy diff --git a/ppapi/proxy/gamepad_resource.h b/ppapi/proxy/gamepad_resource.h index 61d66b0..f2398ff 100644 --- a/ppapi/proxy/gamepad_resource.h +++ b/ppapi/proxy/gamepad_resource.h @@ -9,8 +9,10 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/shared_memory.h" +#include "ppapi/c/ppb_gamepad.h" #include "ppapi/proxy/plugin_resource.h" #include "ppapi/proxy/ppapi_proxy_export.h" +#include "ppapi/shared_impl/ppb_gamepad_shared.h" #include "ppapi/thunk/ppb_gamepad_api.h" struct PP_GamepadsSampleData; @@ -41,11 +43,13 @@ class PPAPI_PROXY_EXPORT GamepadResource virtual void OnReplyReceived(const ResourceMessageReplyParams& params, const IPC::Message& msg) OVERRIDE; - void OnPluginMsgSendMemory(const ResourceMessageReplyParams& params, - base::SharedMemoryHandle shared_memory_handle); + void OnPluginMsgSendMemory(const ResourceMessageReplyParams& params); scoped_ptr<base::SharedMemory> shared_memory_; - void* buffer_; + const ContentGamepadHardwareBuffer* buffer_; + + // Last data returned so we can use this in the event of a read failure. + PP_GamepadsSampleData last_read_; DISALLOW_COPY_AND_ASSIGN(GamepadResource); }; diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc index 0e406db..d48dbfb 100644 --- a/ppapi/proxy/plugin_dispatcher.cc +++ b/ppapi/proxy/plugin_dispatcher.cc @@ -260,6 +260,19 @@ thunk::ResourceCreationAPI* PluginDispatcher::GetResourceCreationAPI() { GetInterfaceProxy(API_ID_RESOURCE_CREATION)); } +// static +void PluginDispatcher::DispatchResourceReply( + const ppapi::proxy::ResourceMessageReplyParams& reply_params, + const IPC::Message& nested_msg) { + Resource* resource = PpapiGlobals::Get()->GetResourceTracker()->GetResource( + reply_params.pp_resource()); + if (!resource) { + NOTREACHED(); + return; + } + resource->OnReplyReceived(reply_params, nested_msg); +} + void PluginDispatcher::ForceFreeAllInstances() { if (!g_instance_to_dispatcher) return; @@ -281,13 +294,7 @@ void PluginDispatcher::ForceFreeAllInstances() { void PluginDispatcher::OnMsgResourceReply( const ppapi::proxy::ResourceMessageReplyParams& reply_params, const IPC::Message& nested_msg) { - Resource* resource = PpapiGlobals::Get()->GetResourceTracker()->GetResource( - reply_params.pp_resource()); - if (!resource) { - NOTREACHED(); - return; - } - resource->OnReplyReceived(reply_params, nested_msg); + DispatchResourceReply(reply_params, nested_msg); } void PluginDispatcher::OnMsgSupportsInterface( diff --git a/ppapi/proxy/plugin_dispatcher.h b/ppapi/proxy/plugin_dispatcher.h index ba46464..66b5e2d 100644 --- a/ppapi/proxy/plugin_dispatcher.h +++ b/ppapi/proxy/plugin_dispatcher.h @@ -147,6 +147,13 @@ class PPAPI_PROXY_EXPORT PluginDispatcher uint32 plugin_dispatcher_id() const { return plugin_dispatcher_id_; } bool incognito() const { return incognito_; } + // Dispatches the given resource message to the appropriate resource in the + // plugin process. This should be wired to the various channels that messages + // come in from various other processes. + static void DispatchResourceReply( + const ppapi::proxy::ResourceMessageReplyParams& reply_params, + const IPC::Message& nested_msg); + private: friend class PluginDispatcherTest; diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index a532aff..8528a2d 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -1551,9 +1551,15 @@ IPC_MESSAGE_CONTROL1(PpapiPluginMsg_FileChooser_ShowReply, // Gamepad. IPC_MESSAGE_CONTROL0(PpapiHostMsg_Gamepad_Create) + +// Requests that the gamepad host send the shared memory handle to the plugin +// process. IPC_MESSAGE_CONTROL0(PpapiHostMsg_Gamepad_RequestMemory) -IPC_MESSAGE_CONTROL1(PpapiPluginMsg_Gamepad_SendMemory, - base::SharedMemoryHandle /* handle */) + +// Reply to a RequestMemory call. This supplies the shared memory handle. The +// actual handle is passed in the ReplyParams struct. +IPC_MESSAGE_CONTROL0(PpapiPluginMsg_Gamepad_SendMemory) + // Printing. IPC_MESSAGE_CONTROL0(PpapiHostMsg_Printing_Create) IPC_MESSAGE_CONTROL0(PpapiHostMsg_Printing_GetDefaultPrintSettings) diff --git a/ppapi/proxy/resource_message_params.cc b/ppapi/proxy/resource_message_params.cc index 5c70e49..6d3fa02 100644 --- a/ppapi/proxy/resource_message_params.cc +++ b/ppapi/proxy/resource_message_params.cc @@ -27,12 +27,51 @@ ResourceMessageParams::~ResourceMessageParams() { void ResourceMessageParams::Serialize(IPC::Message* msg) const { IPC::ParamTraits<PP_Resource>::Write(msg, pp_resource_); IPC::ParamTraits<int32_t>::Write(msg, sequence_); + IPC::ParamTraits<std::vector<SerializedHandle> >::Write(msg, handles_); } bool ResourceMessageParams::Deserialize(const IPC::Message* msg, PickleIterator* iter) { return IPC::ParamTraits<PP_Resource>::Read(msg, iter, &pp_resource_) && - IPC::ParamTraits<int32_t>::Read(msg, iter, &sequence_); + IPC::ParamTraits<int32_t>::Read(msg, iter, &sequence_) && + IPC::ParamTraits<std::vector<SerializedHandle> >::Read( + msg, iter, &handles_); +} + +const SerializedHandle* ResourceMessageParams::GetHandleOfTypeAtIndex( + size_t index, + SerializedHandle::Type type) const { + if (handles_.size() <= index) + return NULL; + if (handles_[index].type() != type) + return NULL; + return &handles_[index]; +} + +bool ResourceMessageParams::GetSharedMemoryHandleAtIndex( + size_t index, + base::SharedMemoryHandle* handle) const { + const SerializedHandle* serialized = GetHandleOfTypeAtIndex( + index, SerializedHandle::SHARED_MEMORY); + if (!serialized) + return false; + *handle = serialized->shmem(); + return true; +} + +bool ResourceMessageParams::GetSocketHandleAtIndex( + size_t index, + IPC::PlatformFileForTransit* handle) const { + const SerializedHandle* serialized = GetHandleOfTypeAtIndex( + index, SerializedHandle::SOCKET); + if (!serialized) + return false; + *handle = serialized->descriptor(); + return true; +} + +void ResourceMessageParams::AppendHandle(const SerializedHandle& handle) { + handles_.push_back(handle); } ResourceMessageCallParams::ResourceMessageCallParams() diff --git a/ppapi/proxy/resource_message_params.h b/ppapi/proxy/resource_message_params.h index ad1c96a..bc52234 100644 --- a/ppapi/proxy/resource_message_params.h +++ b/ppapi/proxy/resource_message_params.h @@ -5,9 +5,12 @@ #ifndef PPAPI_PROXY_RESOURCE_MESSAGE_PARAMS_H_ #define PPAPI_PROXY_RESOURCE_MESSAGE_PARAMS_H_ +#include <vector> + #include "ipc/ipc_message_utils.h" #include "ppapi/c/pp_resource.h" #include "ppapi/proxy/ppapi_proxy_export.h" +#include "ppapi/proxy/serialized_structs.h" namespace ppapi { namespace proxy { @@ -20,6 +23,33 @@ class PPAPI_PROXY_EXPORT ResourceMessageParams { PP_Resource pp_resource() const { return pp_resource_; } int32_t sequence() const { return sequence_; } + const std::vector<SerializedHandle> handles() const { return handles_; } + + // Returns a pointer to the handle at the given index if it exists and is of + // the given type. If the index doesn't exist or the handle isn't of the + // given type, returns NULL. Note that the pointer will be into an internal + // vector so will be invalidated if the params are mutated. + const SerializedHandle* GetHandleOfTypeAtIndex( + size_t index, + SerializedHandle::Type type) const; + + // Helper functions to return shared memory handles passed in the params + // struct. If the index has a valid handle of the given type, it will be + // placed in the output parameter and the function will return true. If the + // handle doesn't exist or is a different type, the functions will return + // false and the output parameter will be untouched. + // + // Note that the handle could still be a "null" or invalid handle of + // the right type and the functions will succeed. + bool GetSharedMemoryHandleAtIndex(size_t index, + base::SharedMemoryHandle* handle) const; + bool GetSocketHandleAtIndex(size_t index, + IPC::PlatformFileForTransit* handle) const; + + // Appends the given handle to the list of handles sent with the call or + // reply. + void AppendHandle(const SerializedHandle& handle); + protected: ResourceMessageParams(); ResourceMessageParams(PP_Resource resource, int32_t sequence); @@ -44,6 +74,11 @@ class PPAPI_PROXY_EXPORT ResourceMessageParams { // confusion within the plugin and within callbacks on the same resource, // there shouldn't be a security problem. int32_t sequence_; + + // A list of all handles transferred in the message. Handles go here so that + // the NaCl adapter can extract them generally when it rewrites them to + // go between Windows and NaCl (Posix) apps. + std::vector<SerializedHandle> handles_; }; // Parameters common to all ResourceMessage "Call" requests. diff --git a/ppapi/shared_impl/ppapi_permissions.cc b/ppapi/shared_impl/ppapi_permissions.cc index 987fd89..d92f6ba 100644 --- a/ppapi/shared_impl/ppapi_permissions.cc +++ b/ppapi/shared_impl/ppapi_permissions.cc @@ -17,6 +17,14 @@ PpapiPermissions::PpapiPermissions(uint32 perms) : permissions_(perms) { PpapiPermissions::~PpapiPermissions() { } +// static +PpapiPermissions PpapiPermissions::AllPermissions() { + return PpapiPermissions( + PERMISSION_DEV | + PERMISSION_PRIVATE | + PERMISSION_BYPASS_USER_GESTURE); +} + bool PpapiPermissions::HasPermission(Permission perm) const { // Check that "perm" is a power of two to make sure the caller didn't set // more than one permission bit. We may want to change how permissions are diff --git a/ppapi/shared_impl/ppapi_permissions.h b/ppapi/shared_impl/ppapi_permissions.h index 94db0e6..4b1c6a6 100644 --- a/ppapi/shared_impl/ppapi_permissions.h +++ b/ppapi/shared_impl/ppapi_permissions.h @@ -20,6 +20,8 @@ enum Permission { // Allows ability to bypass user-gesture checks for showing things like // file select dialogs. PERMISSION_BYPASS_USER_GESTURE = 1 << 3 + + // NOTE: If you add stuff be sure to update AllPermissions(). }; class PPAPI_SHARED_EXPORT PpapiPermissions { @@ -32,6 +34,10 @@ class PPAPI_SHARED_EXPORT PpapiPermissions { ~PpapiPermissions(); + // Returns a permissions class with all features enabled. This is for testing + // and manually registered plugins. + static PpapiPermissions AllPermissions(); + bool HasPermission(Permission perm) const; private: diff --git a/ppapi/shared_impl/ppb_gamepad_shared.cc b/ppapi/shared_impl/ppb_gamepad_shared.cc new file mode 100644 index 0000000..41c7668 --- /dev/null +++ b/ppapi/shared_impl/ppb_gamepad_shared.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ppapi/shared_impl/ppb_gamepad_shared.h" + +#include "base/basictypes.h" + +namespace ppapi { + +void ConvertWebKitGamepadData(const WebKitGamepads& webkit_data, + PP_GamepadsSampleData* output_data) { + output_data->length = webkit_data.length; + for (unsigned i = 0; i < webkit_data.length; ++i) { + PP_GamepadSampleData& output_pad = output_data->items[i]; + const WebKitGamepad& webkit_pad = webkit_data.items[i]; + output_pad.connected = webkit_pad.connected ? PP_TRUE : PP_FALSE; + if (webkit_pad.connected) { + COMPILE_ASSERT(sizeof(output_pad.id) == sizeof(webkit_pad.id), + id_size_does_not_match); + COMPILE_ASSERT(sizeof(output_pad.axes) == sizeof(webkit_pad.axes), + axes_size_does_not_match); + COMPILE_ASSERT(sizeof(output_pad.buttons) == sizeof(webkit_pad.buttons), + buttons_size_does_not_match); + memcpy(output_pad.id, webkit_pad.id, sizeof(output_pad.id)); + output_pad.timestamp = webkit_pad.timestamp; + output_pad.axes_length = webkit_pad.axes_length; + memcpy(output_pad.axes, webkit_pad.axes, sizeof(output_pad.axes)); + output_pad.buttons_length = webkit_pad.buttons_length; + memcpy(output_pad.buttons, + webkit_pad.buttons, + sizeof(output_pad.buttons)); + } + } +} + +} // namespace ppapi diff --git a/ppapi/shared_impl/ppb_gamepad_shared.h b/ppapi/shared_impl/ppb_gamepad_shared.h new file mode 100644 index 0000000..b3ab882 --- /dev/null +++ b/ppapi/shared_impl/ppb_gamepad_shared.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PPAPI_SHARED_IMPL_PPB_GAMEPAD_SHARED_H_ +#define PPAPI_SHARED_IMPL_PPB_GAMEPAD_SHARED_H_ + +#include "base/atomicops.h" +#include "base/string16.h" +#include "ppapi/c/ppb_gamepad.h" +#include "ppapi/shared_impl/ppapi_shared_export.h" + +namespace ppapi { + +// TODO(brettw) when we remove the non-IPC-based gamepad implementation, this +// code should all move into the GamepadResource. + +#pragma pack(push, 1) + +// This must match the definition of WebKit::Gamepad. The GamepadHost unit test +// has some compile asserts to validate this. +struct WebKitGamepad { + static const size_t kIdLengthCap = 128; + static const size_t kAxesLengthCap = 16; + static const size_t kButtonsLengthCap = 32; + + // Is there a gamepad connected at this index? + bool connected; + + // Device identifier (based on manufacturer, model, etc.). + char16 id[kIdLengthCap]; + + // Monotonically increasing value referring to when the data were last + // updated. + unsigned long long timestamp; + + // Number of valid entries in the axes array. + unsigned axes_length; + + // Normalized values representing axes, in the range [-1..1]. + float axes[kAxesLengthCap]; + + // Number of valid entries in the buttons array. + unsigned buttons_length; + + // Normalized values representing buttons, in the range [0..1]. + float buttons[kButtonsLengthCap]; +}; + +// This must match the definition of WebKit::Gamepads. The GamepadHost unit +// test has some compile asserts to validate this. +struct WebKitGamepads { + static const size_t kItemsLengthCap = 4; + + // Number of valid entries in the items array. + unsigned length; + + // Gamepad data for N separate gamepad devices. + WebKitGamepad items[kItemsLengthCap]; +}; + +// This is the structure store in shared memory. It must match +// content/common/gamepad_hardware_buffer.h. The GamepadHost unit test has +// some compile asserts to validate this. +struct ContentGamepadHardwareBuffer { + base::subtle::Atomic32 sequence; + WebKitGamepads buffer; +}; + +#pragma pack(pop) + +PPAPI_SHARED_EXPORT void ConvertWebKitGamepadData( + const WebKitGamepads& webkit_data, + PP_GamepadsSampleData* output_data); + +} // namespace ppapi + +#endif // PPAPI_SHARED_IMPL_PPB_GAMEPAD_SHARED_H_ diff --git a/webkit/plugins/ppapi/event_conversion.cc b/webkit/plugins/ppapi/event_conversion.cc index e88b25a..3329ccf 100644 --- a/webkit/plugins/ppapi/event_conversion.cc +++ b/webkit/plugins/ppapi/event_conversion.cc @@ -724,31 +724,5 @@ PP_InputEvent_Class ClassifyInputEvent(WebInputEvent::Type type) { } } -void ConvertWebKitGamepadData(WebKit::WebGamepads& webkit_data, - PP_GamepadsSampleData* output_data) { - output_data->length = webkit_data.length; - for (unsigned i = 0; i < webkit_data.length; ++i) { - PP_GamepadSampleData& output_pad = output_data->items[i]; - const WebKit::WebGamepad& webkit_pad = webkit_data.items[i]; - output_pad.connected = webkit_pad.connected ? PP_TRUE : PP_FALSE; - if (webkit_pad.connected) { - COMPILE_ASSERT(sizeof(output_pad.id) == sizeof(webkit_pad.id), - id_size_does_not_match); - COMPILE_ASSERT(sizeof(output_pad.axes) == sizeof(webkit_pad.axes), - axes_size_does_not_match); - COMPILE_ASSERT(sizeof(output_pad.buttons) == sizeof(webkit_pad.buttons), - buttons_size_does_not_match); - memcpy(output_pad.id, webkit_pad.id, sizeof(output_pad.id)); - output_pad.timestamp = webkit_pad.timestamp; - output_pad.axes_length = webkit_pad.axesLength; - memcpy(output_pad.axes, webkit_pad.axes, sizeof(output_pad.axes)); - output_pad.buttons_length = webkit_pad.buttonsLength; - memcpy(output_pad.buttons, - webkit_pad.buttons, - sizeof(output_pad.buttons)); - } - } -} - } // namespace ppapi } // namespace webkit diff --git a/webkit/plugins/ppapi/event_conversion.h b/webkit/plugins/ppapi/event_conversion.h index 1474fee..f289012 100644 --- a/webkit/plugins/ppapi/event_conversion.h +++ b/webkit/plugins/ppapi/event_conversion.h @@ -8,7 +8,6 @@ #include <vector> #include "base/memory/linked_ptr.h" -#include "ppapi/c/ppb_gamepad.h" #include "ppapi/c/ppb_input_event.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "webkit/plugins/webkit_plugins_export.h" @@ -50,11 +49,6 @@ std::vector<linked_ptr<WebKit::WebInputEvent> > CreateSimulatedWebInputEvents( // type should not be "Undefined" since there's no corresponding PPAPI class. PP_InputEvent_Class ClassifyInputEvent(WebKit::WebInputEvent::Type type); -// Translate from WebGamepads to the Gamepad API format -// PP_GamepadsSampleData. -void ConvertWebKitGamepadData(WebKit::WebGamepads& webkit_data, - PP_GamepadsSampleData* output_data); - } // namespace ppapi } // namespace webkit diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.cc b/webkit/plugins/ppapi/ppapi_plugin_instance.cc index 4070262..6a2a3fe 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc @@ -33,6 +33,7 @@ #include "ppapi/c/private/pp_content_decryptor.h" #include "ppapi/c/private/ppp_instance_private.h" #include "ppapi/shared_impl/ppapi_preferences.h" +#include "ppapi/shared_impl/ppb_gamepad_shared.h" #include "ppapi/shared_impl/ppb_input_event_shared.h" #include "ppapi/shared_impl/ppb_url_util_shared.h" #include "ppapi/shared_impl/ppb_view_shared.h" @@ -401,7 +402,8 @@ PluginInstance::GamepadImpl::GamepadImpl(PluginDelegate* delegate) void PluginInstance::GamepadImpl::Sample(PP_GamepadsSampleData* data) { WebKit::WebGamepads webkit_data; delegate_->SampleGamepads(&webkit_data); - ConvertWebKitGamepadData(webkit_data, data); + ConvertWebKitGamepadData( + *reinterpret_cast<const ::ppapi::WebKitGamepads*>(&webkit_data), data); } PluginInstance::PluginInstance( |