Implement CrashUploadListWin to enable chrome://crashes on Windows.

Added new class CrashUploadListWin, subclass of CrashUploadList which
has the Windows platform-specific logic to read crash logs from Windows
Event Log.

Added static CrashUploadList::Create() factory method which returns an
instance of CrashUploadListWin on Windows and an instance of
CrashUploadList on other platforms.

Also implemented the max_count parameter in
CrashUploadList::GetUploadedCrashes(), which was part of
the interface but was ignored by the code.

BUG=73468
TEST=Open chrome://crashes under Windows after one or more
     crash reports were uploaded. See the crash reports listed.


Review URL: http://codereview.chromium.org/6771021

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80165 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/crash_upload_list.cc b/chrome/browser/crash_upload_list.cc
index d10980f6..fa0e8ab 100644
--- a/chrome/browser/crash_upload_list.cc
+++ b/chrome/browser/crash_upload_list.cc
@@ -9,6 +9,9 @@
 #include "base/file_util.h"
 #include "base/string_number_conversions.h"
 #include "base/string_split.h"
+#if defined(OS_WIN)
+#include "chrome/browser/crash_upload_list_win.h"
+#endif
 #include "chrome/common/chrome_paths.h"
 #include "content/browser/browser_thread.h"
 
@@ -17,20 +20,37 @@
 
 CrashUploadList::CrashInfo::~CrashInfo() {}
 
+// static
+CrashUploadList* CrashUploadList::Create(Delegate* delegate) {
+#if defined(OS_WIN)
+  return new CrashUploadListWin(delegate);
+#else
+  return new CrashUploadList(delegate);
+#endif
+}
+
 CrashUploadList::CrashUploadList(Delegate* delegate) : delegate_(delegate) {}
 
 CrashUploadList::~CrashUploadList() {}
 
 void CrashUploadList::LoadCrashListAsynchronously() {
   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-      NewRunnableMethod(this, &CrashUploadList::LoadUploadLog));
+      NewRunnableMethod(this,
+        &CrashUploadList::LoadCrashListAndInformDelegateOfCompletion));
 }
 
 void CrashUploadList::ClearDelegate() {
   delegate_ = NULL;
 }
 
-void CrashUploadList::LoadUploadLog() {
+
+void CrashUploadList::LoadCrashListAndInformDelegateOfCompletion() {
+  LoadCrashList();
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+      NewRunnableMethod(this, &CrashUploadList::InformDelegateOfCompletion));
+}
+
+void CrashUploadList::LoadCrashList() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
   FilePath crash_dir_path;
   PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dir_path);
@@ -38,10 +58,27 @@
   if (file_util::PathExists(upload_log_path)) {
     std::string contents;
     file_util::ReadFileToString(upload_log_path, &contents);
-    base::SplitStringAlongWhitespace(contents, &log_entries_);
+    std::vector<std::string> log_entries;
+    base::SplitStringAlongWhitespace(contents, &log_entries);
+    ParseLogEntries(log_entries);
   }
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-      NewRunnableMethod(this, &CrashUploadList::InformDelegateOfCompletion));
+}
+
+void CrashUploadList::ParseLogEntries(
+    const std::vector<std::string>& log_entries) {
+  std::vector<std::string>::const_reverse_iterator i;
+  for (i = log_entries.rbegin(); i != log_entries.rend(); ++i) {
+    std::vector<std::string> components;
+    base::SplitString(*i, ',', &components);
+    // Skip any blank (or corrupted) lines.
+    if (components.size() != 2)
+      continue;
+    double seconds_since_epoch;
+    if (!base::StringToDouble(components[0], &seconds_since_epoch))
+      continue;
+    CrashInfo info(components[1], base::Time::FromDoubleT(seconds_since_epoch));
+    crashes_.push_back(info);
+  }
 }
 
 void CrashUploadList::InformDelegateOfCompletion() {
@@ -52,17 +89,11 @@
 
 void CrashUploadList::GetUploadedCrashes(unsigned int max_count,
                                          std::vector<CrashInfo>* crashes) {
-  std::vector<std::string>::reverse_iterator i;
-  for (i = log_entries_.rbegin(); i != log_entries_.rend(); ++i) {
-    std::vector<std::string> components;
-    base::SplitString(*i, ',', &components);
-    // Skip any blank (or corrupted) lines.
-    if (components.size() != 2)
-      continue;
-    double seconds_since_epoch;
-    if (!base::StringToDouble(components[0], &seconds_since_epoch))
-      continue;
-    CrashInfo info(components[1], base::Time::FromDoubleT(seconds_since_epoch));
-    crashes->push_back(info);
-  }
+  std::copy(crashes_.begin(),
+            crashes_.begin() + std::min<size_t>(crashes_.size(), max_count),
+            std::back_inserter(*crashes));
+}
+
+std::vector<CrashUploadList::CrashInfo>& CrashUploadList::crashes() {
+  return crashes_;
 }
diff --git a/chrome/browser/crash_upload_list.h b/chrome/browser/crash_upload_list.h
index 9d5d243..c8b30d1 100644
--- a/chrome/browser/crash_upload_list.h
+++ b/chrome/browser/crash_upload_list.h
@@ -32,6 +32,10 @@
     virtual ~Delegate() {}
   };
 
+  // Static factory method that creates the platform-specific implementation
+  // of the crash upload list with the given callback delegate.
+  static CrashUploadList* Create(Delegate* delegate);
+
   // Creates a new crash upload list with the given callback delegate.
   explicit CrashUploadList(Delegate* delegate);
 
@@ -49,17 +53,28 @@
   void GetUploadedCrashes(unsigned int max_count,
                           std::vector<CrashInfo>* crashes);
 
- private:
-  friend class base::RefCountedThreadSafe<CrashUploadList>;
+ protected:
   virtual ~CrashUploadList();
 
-  // Reads the upload log and stores the lines in log_entries_.
-  void LoadUploadLog();
+  // Reads the upload log and stores the entries in crashes_.
+  virtual void LoadCrashList();
+
+  // Returns a reference to the list of crashes.
+  std::vector<CrashInfo>& crashes();
+
+ private:
+  friend class base::RefCountedThreadSafe<CrashUploadList>;
+
+  // Manages the background thread work for LoadCrashListAsynchronously().
+  void LoadCrashListAndInformDelegateOfCompletion();
 
   // Calls the delegate's callback method, if there is a delegate.
   void InformDelegateOfCompletion();
 
-  std::vector<std::string> log_entries_;
+  // Parses crash log lines, converting them to CrashInfo entries.
+  void ParseLogEntries(const std::vector<std::string>& log_entries);
+
+  std::vector<CrashInfo> crashes_;
   Delegate* delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(CrashUploadList);
diff --git a/chrome/browser/crash_upload_list_win.cc b/chrome/browser/crash_upload_list_win.cc
new file mode 100644
index 0000000..7c4e948
--- /dev/null
+++ b/chrome/browser/crash_upload_list_win.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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 "chrome/browser/crash_upload_list_win.h"
+
+#include "base/stringprintf.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+
+CrashUploadListWin::CrashUploadListWin(Delegate* delegate)
+    : CrashUploadList(delegate) {}
+
+void CrashUploadListWin::LoadCrashList() {
+  std::vector<uint8> buffer(1024);
+  HANDLE event_log = OpenEventLog(NULL, L"Application");
+  if (event_log) {
+    while (true) {
+      DWORD bytes_read;
+      DWORD bytes_needed;
+      BOOL success =
+          ReadEventLog(event_log,
+                       EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
+                       0,
+                       &buffer[0],
+                       buffer.size(),
+                       &bytes_read,
+                       &bytes_needed);
+      if (success) {
+        DWORD record_offset = 0;
+        // The ReadEventLog() API docs imply, but do not explicitly state that
+        // partial records will not be returned. Use DCHECK() to affirm this.
+        while (record_offset < bytes_read) {
+          DCHECK(record_offset + sizeof(EVENTLOGRECORD) <= bytes_read);
+          EVENTLOGRECORD* record = (EVENTLOGRECORD*)&buffer[record_offset];
+          DCHECK(record_offset + record->Length <= bytes_read);
+          if (IsPossibleCrashLogRecord(record))
+            ProcessPossibleCrashLogRecord(record);
+          record_offset += record->Length;
+        }
+      } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+        // Resize buffer to the required minimum size.
+        buffer.resize(bytes_needed);
+      } else {
+        // Stop on any other error, including the expected case
+        // of ERROR_HANDLE_EOF.
+        DCHECK(GetLastError() == ERROR_HANDLE_EOF);
+        break;
+      }
+    }
+    CloseEventLog(event_log);
+  }
+}
+
+bool CrashUploadListWin::IsPossibleCrashLogRecord(
+    EVENTLOGRECORD* record) const {
+  LPWSTR provider_name = (LPWSTR)((uint8*)record + sizeof(EVENTLOGRECORD));
+  return !wcscmp(L"Chrome", provider_name) &&
+      record->EventType == EVENTLOG_INFORMATION_TYPE &&
+      record->NumStrings >= 1;
+}
+
+void CrashUploadListWin::ProcessPossibleCrashLogRecord(EVENTLOGRECORD* record) {
+  // Add the crash if the message matches the expected pattern.
+  const std::wstring pattern_prefix(L"Id=");
+  const std::wstring pattern_suffix(L".");
+  std::wstring message((LPWSTR)((uint8*)record + record->StringOffset));
+  size_t start_index = message.find(pattern_prefix);
+  if (start_index != std::wstring::npos) {
+    start_index += pattern_prefix.size();
+    size_t end_index = message.find(pattern_suffix, start_index);
+    if (end_index != std::wstring::npos) {
+      std::wstring crash_id =
+          message.substr(start_index, end_index - start_index);
+      crashes().push_back(
+          CrashInfo(base::SysWideToUTF8(crash_id),
+                    base::Time::FromDoubleT(record->TimeGenerated)));
+    }
+  }
+}
diff --git a/chrome/browser/crash_upload_list_win.h b/chrome/browser/crash_upload_list_win.h
new file mode 100644
index 0000000..e395a27
--- /dev/null
+++ b/chrome/browser/crash_upload_list_win.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_CRASH_UPLOAD_LIST_WIN_H_
+#define CHROME_BROWSER_CRASH_UPLOAD_LIST_WIN_H_
+#pragma once
+
+#include "chrome/browser/crash_upload_list.h"
+#include "base/compiler_specific.h"
+
+// A CrashUploadList that retrieves the list of reported crashes
+// from the Windows Event Log.
+class CrashUploadListWin : public CrashUploadList {
+ public:
+  explicit CrashUploadListWin(Delegate* delegate);
+
+ protected:
+  // Loads the list of crashes from the Windows Event Log.
+  virtual void LoadCrashList() OVERRIDE;
+
+ private:
+  // Returns whether the event record is likely a Chrome crash log.
+  bool IsPossibleCrashLogRecord(EVENTLOGRECORD* record) const;
+
+  // Parses the event record and adds it to the crash list.
+  void ProcessPossibleCrashLogRecord(EVENTLOGRECORD* record);
+
+  DISALLOW_COPY_AND_ASSIGN(CrashUploadListWin);
+};
+
+#endif  // CHROME_BROWSER_CRASH_UPLOAD_LIST_WIN_H_
diff --git a/chrome/browser/ui/webui/crashes_ui.cc b/chrome/browser/ui/webui/crashes_ui.cc
index 0758652..6f2327a 100644
--- a/chrome/browser/ui/webui/crashes_ui.cc
+++ b/chrome/browser/ui/webui/crashes_ui.cc
@@ -127,7 +127,7 @@
 
 CrashesDOMHandler::CrashesDOMHandler()
     : list_available_(false), js_request_pending_(false) {
-  upload_list_ = new CrashUploadList(this);
+  upload_list_ = CrashUploadList::Create(this);
 }
 
 CrashesDOMHandler::~CrashesDOMHandler() {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 5889df3..c4bda76 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -746,6 +746,8 @@
         'browser/cookies_tree_model.h',
         'browser/crash_upload_list.cc',
         'browser/crash_upload_list.h',
+        'browser/crash_upload_list_win.cc',
+        'browser/crash_upload_list_win.h',
         'browser/custom_home_pages_table_model.cc',
         'browser/custom_home_pages_table_model.h',
         'browser/custom_handlers/protocol_handler.cc',