ipc/chromium/src/base/event_recorder.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include "build/build_config.h"
michael@0 6
michael@0 7 #include <windows.h>
michael@0 8 #include <mmsystem.h>
michael@0 9
michael@0 10 #include "base/event_recorder.h"
michael@0 11 #include "base/file_util.h"
michael@0 12 #include "base/logging.h"
michael@0 13
michael@0 14 // A note about time.
michael@0 15 // For perfect playback of events, you'd like a very accurate timer
michael@0 16 // so that events are played back at exactly the same time that
michael@0 17 // they were recorded. However, windows has a clock which is only
michael@0 18 // granular to ~15ms. We see more consistent event playback when
michael@0 19 // using a higher resolution timer. To do this, we use the
michael@0 20 // timeGetTime API instead of the default GetTickCount() API.
michael@0 21
michael@0 22 namespace base {
michael@0 23
michael@0 24 EventRecorder* EventRecorder::current_ = NULL;
michael@0 25
michael@0 26 LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
michael@0 27 LPARAM lParam) {
michael@0 28 CHECK(EventRecorder::current());
michael@0 29 return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
michael@0 30 }
michael@0 31
michael@0 32 LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
michael@0 33 LPARAM lParam) {
michael@0 34 CHECK(EventRecorder::current());
michael@0 35 return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
michael@0 36 }
michael@0 37
michael@0 38 EventRecorder::~EventRecorder() {
michael@0 39 // Try to assert early if the caller deletes the recorder
michael@0 40 // while it is still in use.
michael@0 41 DCHECK(!journal_hook_);
michael@0 42 DCHECK(!is_recording_ && !is_playing_);
michael@0 43 }
michael@0 44
michael@0 45 bool EventRecorder::StartRecording(const FilePath& filename) {
michael@0 46 if (journal_hook_ != NULL)
michael@0 47 return false;
michael@0 48 if (is_recording_ || is_playing_)
michael@0 49 return false;
michael@0 50
michael@0 51 // Open the recording file.
michael@0 52 DCHECK(file_ == NULL);
michael@0 53 file_ = file_util::OpenFile(filename, "wb+");
michael@0 54 if (!file_) {
michael@0 55 DLOG(ERROR) << "EventRecorder could not open log file";
michael@0 56 return false;
michael@0 57 }
michael@0 58
michael@0 59 // Set the faster clock, if possible.
michael@0 60 ::timeBeginPeriod(1);
michael@0 61
michael@0 62 // Set the recording hook. JOURNALRECORD can only be used as a global hook.
michael@0 63 journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
michael@0 64 GetModuleHandle(NULL), 0);
michael@0 65 if (!journal_hook_) {
michael@0 66 DLOG(ERROR) << "EventRecorder Record Hook failed";
michael@0 67 file_util::CloseFile(file_);
michael@0 68 return false;
michael@0 69 }
michael@0 70
michael@0 71 is_recording_ = true;
michael@0 72 return true;
michael@0 73 }
michael@0 74
michael@0 75 void EventRecorder::StopRecording() {
michael@0 76 if (is_recording_) {
michael@0 77 DCHECK(journal_hook_ != NULL);
michael@0 78
michael@0 79 if (!::UnhookWindowsHookEx(journal_hook_)) {
michael@0 80 DLOG(ERROR) << "EventRecorder Unhook failed";
michael@0 81 // Nothing else we can really do here.
michael@0 82 return;
michael@0 83 }
michael@0 84
michael@0 85 ::timeEndPeriod(1);
michael@0 86
michael@0 87 DCHECK(file_ != NULL);
michael@0 88 file_util::CloseFile(file_);
michael@0 89 file_ = NULL;
michael@0 90
michael@0 91 journal_hook_ = NULL;
michael@0 92 is_recording_ = false;
michael@0 93 }
michael@0 94 }
michael@0 95
michael@0 96 bool EventRecorder::StartPlayback(const FilePath& filename) {
michael@0 97 if (journal_hook_ != NULL)
michael@0 98 return false;
michael@0 99 if (is_recording_ || is_playing_)
michael@0 100 return false;
michael@0 101
michael@0 102 // Open the recording file.
michael@0 103 DCHECK(file_ == NULL);
michael@0 104 file_ = file_util::OpenFile(filename, "rb");
michael@0 105 if (!file_) {
michael@0 106 DLOG(ERROR) << "EventRecorder Playback could not open log file";
michael@0 107 return false;
michael@0 108 }
michael@0 109 // Read the first event from the record.
michael@0 110 if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
michael@0 111 DLOG(ERROR) << "EventRecorder Playback has no records!";
michael@0 112 file_util::CloseFile(file_);
michael@0 113 return false;
michael@0 114 }
michael@0 115
michael@0 116 // Set the faster clock, if possible.
michael@0 117 ::timeBeginPeriod(1);
michael@0 118
michael@0 119 // Playback time is tricky. When playing back, we read a series of events,
michael@0 120 // each with timeouts. Simply subtracting the delta between two timers will
michael@0 121 // lead to fast playback (about 2x speed). The API has two events, one
michael@0 122 // which advances to the next event (HC_SKIP), and another that requests the
michael@0 123 // event (HC_GETNEXT). The same event will be requested multiple times.
michael@0 124 // Each time the event is requested, we must calculate the new delay.
michael@0 125 // To do this, we track the start time of the playback, and constantly
michael@0 126 // re-compute the delay. I mention this only because I saw two examples
michael@0 127 // of how to use this code on the net, and both were broken :-)
michael@0 128 playback_start_time_ = timeGetTime();
michael@0 129 playback_first_msg_time_ = playback_msg_.time;
michael@0 130
michael@0 131 // Set the hook. JOURNALPLAYBACK can only be used as a global hook.
michael@0 132 journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
michael@0 133 GetModuleHandle(NULL), 0);
michael@0 134 if (!journal_hook_) {
michael@0 135 DLOG(ERROR) << "EventRecorder Playback Hook failed";
michael@0 136 return false;
michael@0 137 }
michael@0 138
michael@0 139 is_playing_ = true;
michael@0 140
michael@0 141 return true;
michael@0 142 }
michael@0 143
michael@0 144 void EventRecorder::StopPlayback() {
michael@0 145 if (is_playing_) {
michael@0 146 DCHECK(journal_hook_ != NULL);
michael@0 147
michael@0 148 if (!::UnhookWindowsHookEx(journal_hook_)) {
michael@0 149 DLOG(ERROR) << "EventRecorder Unhook failed";
michael@0 150 // Nothing else we can really do here.
michael@0 151 }
michael@0 152
michael@0 153 DCHECK(file_ != NULL);
michael@0 154 file_util::CloseFile(file_);
michael@0 155 file_ = NULL;
michael@0 156
michael@0 157 ::timeEndPeriod(1);
michael@0 158
michael@0 159 journal_hook_ = NULL;
michael@0 160 is_playing_ = false;
michael@0 161 }
michael@0 162 }
michael@0 163
michael@0 164 // Windows callback hook for the recorder.
michael@0 165 LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
michael@0 166 static bool recording_enabled = true;
michael@0 167 EVENTMSG* msg_ptr = NULL;
michael@0 168
michael@0 169 // The API says we have to do this.
michael@0 170 // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
michael@0 171 if (nCode < 0)
michael@0 172 return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
michael@0 173
michael@0 174 // Check for the break key being pressed and stop recording.
michael@0 175 if (::GetKeyState(VK_CANCEL) & 0x8000) {
michael@0 176 StopRecording();
michael@0 177 return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
michael@0 178 }
michael@0 179
michael@0 180 // The Journal Recorder must stop recording events when system modal
michael@0 181 // dialogs are present. (see msdn link above)
michael@0 182 switch(nCode) {
michael@0 183 case HC_SYSMODALON:
michael@0 184 recording_enabled = false;
michael@0 185 break;
michael@0 186 case HC_SYSMODALOFF:
michael@0 187 recording_enabled = true;
michael@0 188 break;
michael@0 189 }
michael@0 190
michael@0 191 if (nCode == HC_ACTION && recording_enabled) {
michael@0 192 // Aha - we have an event to record.
michael@0 193 msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
michael@0 194 msg_ptr->time = timeGetTime();
michael@0 195 fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
michael@0 196 fflush(file_);
michael@0 197 }
michael@0 198
michael@0 199 return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
michael@0 200 }
michael@0 201
michael@0 202 // Windows callback for the playback mode.
michael@0 203 LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam,
michael@0 204 LPARAM lParam) {
michael@0 205 static bool playback_enabled = true;
michael@0 206 int delay = 0;
michael@0 207
michael@0 208 switch(nCode) {
michael@0 209 // A system modal dialog box is being displayed. Stop playing back
michael@0 210 // messages.
michael@0 211 case HC_SYSMODALON:
michael@0 212 playback_enabled = false;
michael@0 213 break;
michael@0 214
michael@0 215 // A system modal dialog box is destroyed. We can start playing back
michael@0 216 // messages again.
michael@0 217 case HC_SYSMODALOFF:
michael@0 218 playback_enabled = true;
michael@0 219 break;
michael@0 220
michael@0 221 // Prepare to copy the next mouse or keyboard event to playback.
michael@0 222 case HC_SKIP:
michael@0 223 if (!playback_enabled)
michael@0 224 break;
michael@0 225
michael@0 226 // Read the next event from the record.
michael@0 227 if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
michael@0 228 this->StopPlayback();
michael@0 229 break;
michael@0 230
michael@0 231 // Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
michael@0 232 case HC_GETNEXT:
michael@0 233 if (!playback_enabled)
michael@0 234 break;
michael@0 235
michael@0 236 memcpy(reinterpret_cast<void*>(lParam), &playback_msg_,
michael@0 237 sizeof(playback_msg_));
michael@0 238
michael@0 239 // The return value is the amount of time (in milliseconds) to wait
michael@0 240 // before playing back the next message in the playback queue. Each
michael@0 241 // time this is called, we recalculate the delay relative to our current
michael@0 242 // wall clock.
michael@0 243 delay = (playback_msg_.time - playback_first_msg_time_) -
michael@0 244 (timeGetTime() - playback_start_time_);
michael@0 245 if (delay < 0)
michael@0 246 delay = 0;
michael@0 247 return delay;
michael@0 248
michael@0 249 // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
michael@0 250 // indicating that the message is not removed from the message queue after
michael@0 251 // PeekMessage processing.
michael@0 252 case HC_NOREMOVE:
michael@0 253 break;
michael@0 254 }
michael@0 255
michael@0 256 return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
michael@0 257 }
michael@0 258
michael@0 259 } // namespace base

mercurial