1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/gamepad_shared_memory_reader.h"
6
7#include "base/debug/trace_event.h"
8#include "base/metrics/histogram.h"
9#include "content/common/gamepad_hardware_buffer.h"
10#include "content/common/gamepad_user_gesture.h"
11#include "content/public/renderer/render_thread.h"
12#include "content/renderer/renderer_webkitplatformsupport_impl.h"
13#include "ipc/ipc_sync_message_filter.h"
14#include "third_party/WebKit/public/platform/WebGamepadListener.h"
15#include "third_party/WebKit/public/platform/WebPlatformEventListener.h"
16
17namespace content {
18
19GamepadSharedMemoryReader::GamepadSharedMemoryReader(RenderThread* thread)
20    : RendererGamepadProvider(thread),
21      gamepad_hardware_buffer_(NULL),
22      ever_interacted_with_(false) {
23}
24
25void GamepadSharedMemoryReader::SendStartMessage() {
26  CHECK(RenderThread::Get()->Send(new GamepadHostMsg_StartPolling(
27      &renderer_shared_memory_handle_)));
28}
29
30void GamepadSharedMemoryReader::SendStopMessage() {
31    RenderThread::Get()->Send(new GamepadHostMsg_StopPolling());
32}
33
34void GamepadSharedMemoryReader::Start(
35    blink::WebPlatformEventListener* listener) {
36  PlatformEventObserver::Start(listener);
37
38  // If we don't get a valid handle from the browser, don't try to Map (we're
39  // probably out of memory or file handles).
40  bool valid_handle = base::SharedMemory::IsHandleValid(
41      renderer_shared_memory_handle_);
42  UMA_HISTOGRAM_BOOLEAN("Gamepad.ValidSharedMemoryHandle", valid_handle);
43  if (!valid_handle)
44    return;
45
46  renderer_shared_memory_.reset(
47      new base::SharedMemory(renderer_shared_memory_handle_, true));
48  CHECK(renderer_shared_memory_->Map(sizeof(GamepadHardwareBuffer)));
49  void *memory = renderer_shared_memory_->memory();
50  CHECK(memory);
51  gamepad_hardware_buffer_ =
52      static_cast<GamepadHardwareBuffer*>(memory);
53}
54
55void GamepadSharedMemoryReader::SampleGamepads(blink::WebGamepads& gamepads) {
56  // Blink should have started observing at that point.
57  CHECK(is_observing());
58
59  // ==========
60  //   DANGER
61  // ==========
62  //
63  // This logic is duplicated in Pepper as well. If you change it, that also
64  // needs to be in sync. See ppapi/proxy/gamepad_resource.cc.
65  blink::WebGamepads read_into;
66  TRACE_EVENT0("GAMEPAD", "SampleGamepads");
67
68  if (!base::SharedMemory::IsHandleValid(renderer_shared_memory_handle_))
69    return;
70
71  // Only try to read this many times before failing to avoid waiting here
72  // very long in case of contention with the writer. TODO(scottmg) Tune this
73  // number (as low as 1?) if histogram shows distribution as mostly
74  // 0-and-maximum.
75  const int kMaximumContentionCount = 10;
76  int contention_count = -1;
77  base::subtle::Atomic32 version;
78  do {
79    version = gamepad_hardware_buffer_->sequence.ReadBegin();
80    memcpy(&read_into, &gamepad_hardware_buffer_->buffer, sizeof(read_into));
81    ++contention_count;
82    if (contention_count == kMaximumContentionCount)
83      break;
84  } while (gamepad_hardware_buffer_->sequence.ReadRetry(version));
85  UMA_HISTOGRAM_COUNTS("Gamepad.ReadContentionCount", contention_count);
86
87  if (contention_count >= kMaximumContentionCount) {
88    // We failed to successfully read, presumably because the hardware
89    // thread was taking unusually long. Don't copy the data to the output
90    // buffer, and simply leave what was there before.
91    return;
92  }
93
94  // New data was read successfully, copy it into the output buffer.
95  memcpy(&gamepads, &read_into, sizeof(gamepads));
96
97  if (!ever_interacted_with_) {
98    // Clear the connected flag if the user hasn't interacted with any of the
99    // gamepads to prevent fingerprinting. The actual data is not cleared.
100    // WebKit will only copy out data into the JS buffers for connected
101    // gamepads so this is sufficient.
102    for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; i++)
103      gamepads.items[i].connected = false;
104  }
105}
106
107GamepadSharedMemoryReader::~GamepadSharedMemoryReader() {
108}
109
110bool GamepadSharedMemoryReader::OnControlMessageReceived(
111    const IPC::Message& message) {
112  bool handled = true;
113  IPC_BEGIN_MESSAGE_MAP(GamepadSharedMemoryReader, message)
114    IPC_MESSAGE_HANDLER(GamepadMsg_GamepadConnected, OnGamepadConnected)
115    IPC_MESSAGE_HANDLER(GamepadMsg_GamepadDisconnected, OnGamepadDisconnected)
116    IPC_MESSAGE_UNHANDLED(handled = false)
117  IPC_END_MESSAGE_MAP()
118  return handled;
119}
120
121void GamepadSharedMemoryReader::OnGamepadConnected(
122    int index,
123    const blink::WebGamepad& gamepad) {
124  // The browser already checks if the user actually interacted with a device.
125  ever_interacted_with_ = true;
126
127  if (listener())
128    listener()->didConnectGamepad(index, gamepad);
129}
130
131void GamepadSharedMemoryReader::OnGamepadDisconnected(
132    int index,
133    const blink::WebGamepad& gamepad) {
134  if (listener())
135    listener()->didDisconnectGamepad(index, gamepad);
136}
137
138} // namespace content
139