OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/gamepad_shared_memory_reader.h" | 5 #include "content/renderer/gamepad_shared_memory_reader.h" |
6 | 6 |
7 #include "base/debug/trace_event.h" | 7 #include "base/debug/trace_event.h" |
8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
9 #include "content/common/gamepad_messages.h" | 9 #include "content/common/gamepad_messages.h" |
| 10 #include "content/common/gamepad_user_gesture.h" |
10 #include "content/public/renderer/render_thread.h" | 11 #include "content/public/renderer/render_thread.h" |
11 #include "content/common/gamepad_hardware_buffer.h" | 12 #include "content/common/gamepad_hardware_buffer.h" |
12 #include "ipc/ipc_sync_message_filter.h" | 13 #include "ipc/ipc_sync_message_filter.h" |
13 | 14 |
14 namespace content { | 15 namespace content { |
15 | 16 |
16 GamepadSharedMemoryReader::GamepadSharedMemoryReader() | 17 GamepadSharedMemoryReader::GamepadSharedMemoryReader() |
17 : gamepad_hardware_buffer_(NULL) { | 18 : gamepad_hardware_buffer_(NULL), |
18 memset(ever_interacted_with_, 0, sizeof(ever_interacted_with_)); | 19 ever_interacted_with_(false) { |
19 CHECK(RenderThread::Get()->Send(new GamepadHostMsg_StartPolling( | 20 CHECK(RenderThread::Get()->Send(new GamepadHostMsg_StartPolling( |
20 &renderer_shared_memory_handle_))); | 21 &renderer_shared_memory_handle_))); |
21 // If we don't get a valid handle from the browser, don't try to Map (we're | 22 // If we don't get a valid handle from the browser, don't try to Map (we're |
22 // probably out of memory or file handles). | 23 // probably out of memory or file handles). |
23 bool valid_handle = base::SharedMemory::IsHandleValid( | 24 bool valid_handle = base::SharedMemory::IsHandleValid( |
24 renderer_shared_memory_handle_); | 25 renderer_shared_memory_handle_); |
25 UMA_HISTOGRAM_BOOLEAN("Gamepad.ValidSharedMemoryHandle", valid_handle); | 26 UMA_HISTOGRAM_BOOLEAN("Gamepad.ValidSharedMemoryHandle", valid_handle); |
26 if (!valid_handle) | 27 if (!valid_handle) |
27 return; | 28 return; |
28 renderer_shared_memory_.reset( | 29 renderer_shared_memory_.reset( |
29 new base::SharedMemory(renderer_shared_memory_handle_, true)); | 30 new base::SharedMemory(renderer_shared_memory_handle_, true)); |
30 CHECK(renderer_shared_memory_->Map(sizeof(GamepadHardwareBuffer))); | 31 CHECK(renderer_shared_memory_->Map(sizeof(GamepadHardwareBuffer))); |
31 void *memory = renderer_shared_memory_->memory(); | 32 void *memory = renderer_shared_memory_->memory(); |
32 CHECK(memory); | 33 CHECK(memory); |
33 gamepad_hardware_buffer_ = | 34 gamepad_hardware_buffer_ = |
34 static_cast<GamepadHardwareBuffer*>(memory); | 35 static_cast<GamepadHardwareBuffer*>(memory); |
35 } | 36 } |
36 | 37 |
37 void GamepadSharedMemoryReader::SampleGamepads(WebKit::WebGamepads& gamepads) { | 38 void GamepadSharedMemoryReader::SampleGamepads(WebKit::WebGamepads& gamepads) { |
| 39 // ========== |
| 40 // DANGER |
| 41 // ========== |
| 42 // |
| 43 // This logic is duplicated in Pepper as well. If you change it, that also |
| 44 // needs to be in sync. See ppapi/proxy/gamepad_resource.cc. |
38 WebKit::WebGamepads read_into; | 45 WebKit::WebGamepads read_into; |
39 TRACE_EVENT0("GAMEPAD", "SampleGamepads"); | 46 TRACE_EVENT0("GAMEPAD", "SampleGamepads"); |
40 | 47 |
41 if (!base::SharedMemory::IsHandleValid(renderer_shared_memory_handle_)) | 48 if (!base::SharedMemory::IsHandleValid(renderer_shared_memory_handle_)) |
42 return; | 49 return; |
43 | 50 |
44 // Only try to read this many times before failing to avoid waiting here | 51 // Only try to read this many times before failing to avoid waiting here |
45 // very long in case of contention with the writer. TODO(scottmg) Tune this | 52 // very long in case of contention with the writer. TODO(scottmg) Tune this |
46 // number (as low as 1?) if histogram shows distribution as mostly | 53 // number (as low as 1?) if histogram shows distribution as mostly |
47 // 0-and-maximum. | 54 // 0-and-maximum. |
48 const int kMaximumContentionCount = 10; | 55 const int kMaximumContentionCount = 10; |
49 int contention_count = -1; | 56 int contention_count = -1; |
50 base::subtle::Atomic32 version; | 57 base::subtle::Atomic32 version; |
51 do { | 58 do { |
52 version = gamepad_hardware_buffer_->sequence.ReadBegin(); | 59 version = gamepad_hardware_buffer_->sequence.ReadBegin(); |
53 memcpy(&read_into, &gamepad_hardware_buffer_->buffer, sizeof(read_into)); | 60 memcpy(&read_into, &gamepad_hardware_buffer_->buffer, sizeof(read_into)); |
54 ++contention_count; | 61 ++contention_count; |
55 if (contention_count == kMaximumContentionCount) | 62 if (contention_count == kMaximumContentionCount) |
56 break; | 63 break; |
57 } while (gamepad_hardware_buffer_->sequence.ReadRetry(version)); | 64 } while (gamepad_hardware_buffer_->sequence.ReadRetry(version)); |
58 UMA_HISTOGRAM_COUNTS("Gamepad.ReadContentionCount", contention_count); | 65 UMA_HISTOGRAM_COUNTS("Gamepad.ReadContentionCount", contention_count); |
59 | 66 |
60 if (contention_count >= kMaximumContentionCount) { | 67 if (contention_count >= kMaximumContentionCount) { |
61 // We failed to successfully read, presumably because the hardware | 68 // We failed to successfully read, presumably because the hardware |
62 // thread was taking unusually long. Don't copy the data to the output | 69 // thread was taking unusually long. Don't copy the data to the output |
63 // buffer, and simply leave what was there before. | 70 // buffer, and simply leave what was there before. |
64 return; | 71 return; |
65 } | 72 } |
66 | 73 |
67 // New data was read successfully, copy it into the output buffer. | 74 // New data was read successfully, copy it into the output buffer. |
68 memcpy(&gamepads, &read_into, sizeof(gamepads)); | 75 memcpy(&gamepads, &read_into, sizeof(gamepads)); |
69 | 76 |
70 // Override the "connected" with false until the user has interacted | 77 if (!ever_interacted_with_) { |
71 // with the gamepad. This is to prevent fingerprinting on drive-by pages. | 78 if (GamepadsHaveUserGesture(gamepads)) { |
72 for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; ++i) { | 79 ever_interacted_with_ = true; |
73 WebKit::WebGamepad& pad = gamepads.items[i]; | 80 } else { |
74 // If the device is physically connected, then check if we should | 81 // Clear the connected flag if the user hasn't interacted with any of the |
75 // keep it disabled. We track if any of the primary 4 buttons have been | 82 // gamepads to prevent fingerprinting. The actual data is not cleared. |
76 // pressed to determine a reasonable intentional interaction from the user. | 83 // WebKit will only copy out data into the JS buffers for connected |
77 if (pad.connected) { | 84 // gamepads so this is sufficient. |
78 if (ever_interacted_with_[i]) | 85 for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; i++) |
79 continue; | 86 gamepads.items[i].connected = false; |
80 const unsigned kPrimaryInteractionButtons = 4; | |
81 for (unsigned j = 0; j < kPrimaryInteractionButtons; ++j) | |
82 ever_interacted_with_[i] |= pad.buttons[j] > 0.5f; | |
83 // If we've not previously set, and the user still hasn't touched | |
84 // these buttons, then don't pass the data on to the Chromium port. | |
85 if (!ever_interacted_with_[i]) | |
86 pad.connected = false; | |
87 } | 87 } |
88 } | 88 } |
89 } | 89 } |
90 | 90 |
91 GamepadSharedMemoryReader::~GamepadSharedMemoryReader() { | 91 GamepadSharedMemoryReader::~GamepadSharedMemoryReader() { |
92 RenderThread::Get()->Send(new GamepadHostMsg_StopPolling()); | 92 RenderThread::Get()->Send(new GamepadHostMsg_StopPolling()); |
93 } | 93 } |
94 | 94 |
95 } // namespace content | 95 } // namespace content |
OLD | NEW |