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.

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

mercurial