diff options
Diffstat (limited to 'content/browser/gamepad')
16 files changed, 434 insertions, 184 deletions
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_ |