| // 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/renderer/gamepad_shared_memory_reader.h" |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/common/gamepad_hardware_buffer.h" |
| #include "content/public/renderer/render_thread.h" |
| #include "content/renderer/renderer_blink_platform_impl.h" |
| #include "ipc/ipc_sync_message_filter.h" |
| #include "third_party/WebKit/public/platform/WebGamepadListener.h" |
| #include "third_party/WebKit/public/platform/WebPlatformEventListener.h" |
| |
| namespace content { |
| |
| GamepadSharedMemoryReader::GamepadSharedMemoryReader(RenderThread* thread) |
| : RendererGamepadProvider(thread), |
| gamepad_hardware_buffer_(NULL), |
| ever_interacted_with_(false) { |
| } |
| |
| void GamepadSharedMemoryReader::SendStartMessage() { |
| CHECK(RenderThread::Get()->Send(new GamepadHostMsg_StartPolling( |
| &renderer_shared_memory_handle_))); |
| } |
| |
| void GamepadSharedMemoryReader::SendStopMessage() { |
| RenderThread::Get()->Send(new GamepadHostMsg_StopPolling()); |
| } |
| |
| void GamepadSharedMemoryReader::Start( |
| blink::WebPlatformEventListener* listener) { |
| PlatformEventObserver::Start(listener); |
| |
| // If we don't get a valid handle from the browser, don't try to Map (we're |
| // probably out of memory or file handles). |
| bool valid_handle = base::SharedMemory::IsHandleValid( |
| renderer_shared_memory_handle_); |
| UMA_HISTOGRAM_BOOLEAN("Gamepad.ValidSharedMemoryHandle", valid_handle); |
| if (!valid_handle) |
| return; |
| |
| renderer_shared_memory_.reset( |
| new base::SharedMemory(renderer_shared_memory_handle_, true)); |
| CHECK(renderer_shared_memory_->Map(sizeof(GamepadHardwareBuffer))); |
| void *memory = renderer_shared_memory_->memory(); |
| CHECK(memory); |
| gamepad_hardware_buffer_ = |
| static_cast<GamepadHardwareBuffer*>(memory); |
| } |
| |
| void GamepadSharedMemoryReader::SampleGamepads(blink::WebGamepads& gamepads) { |
| // Blink should have started observing at that point. |
| CHECK(is_observing()); |
| |
| // ========== |
| // 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. |
| blink::WebGamepads read_into; |
| TRACE_EVENT0("GAMEPAD", "SampleGamepads"); |
| |
| if (!base::SharedMemory::IsHandleValid(renderer_shared_memory_handle_)) |
| return; |
| |
| // Only try to read this many times before failing to avoid waiting here |
| // very long in case of contention with the writer. TODO(scottmg) Tune this |
| // number (as low as 1?) if histogram shows distribution as mostly |
| // 0-and-maximum. |
| const int kMaximumContentionCount = 10; |
| int contention_count = -1; |
| base::subtle::Atomic32 version; |
| do { |
| version = gamepad_hardware_buffer_->sequence.ReadBegin(); |
| memcpy(&read_into, &gamepad_hardware_buffer_->buffer, sizeof(read_into)); |
| ++contention_count; |
| if (contention_count == kMaximumContentionCount) |
| break; |
| } while (gamepad_hardware_buffer_->sequence.ReadRetry(version)); |
| 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; |
| } |
| |
| // New data was read successfully, copy it into the output buffer. |
| memcpy(&gamepads, &read_into, sizeof(gamepads)); |
| |
| if (!ever_interacted_with_) { |
| // 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 < blink::WebGamepads::itemsLengthCap; i++) |
| gamepads.items[i].connected = false; |
| } |
| } |
| |
| GamepadSharedMemoryReader::~GamepadSharedMemoryReader() { |
| StopIfObserving(); |
| } |
| |
| bool GamepadSharedMemoryReader::OnControlMessageReceived( |
| const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(GamepadSharedMemoryReader, message) |
| IPC_MESSAGE_HANDLER(GamepadMsg_GamepadConnected, OnGamepadConnected) |
| IPC_MESSAGE_HANDLER(GamepadMsg_GamepadDisconnected, OnGamepadDisconnected) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| void GamepadSharedMemoryReader::OnGamepadConnected( |
| int index, |
| const blink::WebGamepad& gamepad) { |
| // The browser already checks if the user actually interacted with a device. |
| ever_interacted_with_ = true; |
| |
| if (listener()) |
| listener()->didConnectGamepad(index, gamepad); |
| } |
| |
| void GamepadSharedMemoryReader::OnGamepadDisconnected( |
| int index, |
| const blink::WebGamepad& gamepad) { |
| if (listener()) |
| listener()->didDisconnectGamepad(index, gamepad); |
| } |
| |
| } // namespace content |