Always drain blocked modules log

Since it is possible for a module to be blocked even though the feature
is disabled, the class that drains the log of blocked modules is now
part of the ModuleDatabase and always initialized.

Also, the ModuleDatabase is now aware of blocked modules and they are
shown in the chrome://conflicts page.

Bug: 846953
Change-Id: I993a51571e4d4f630ab6852b54920598bad26ac3
Reviewed-on: https://chromium-review.googlesource.com/1141158
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Reviewed-by: Alexei Svitkine <asvitkine@chromium.org>
Reviewed-by: Bernhard Bauer <bauerb@chromium.org>
Reviewed-by: Chris Hamilton <chrisha@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576925}
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
index e2e6b447..28fdfc3 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
@@ -123,6 +123,30 @@
   return result;
 }
 
+// Inserts a module into |modules|. Also does the type conversion.
+void InsertPackedListModule(
+    ModuleInfoKey module_key,
+    std::vector<third_party_dlls::PackedListModule>* modules) {
+  DCHECK(modules);
+
+  // Do the insertion.
+  modules->emplace_back();
+  third_party_dlls::PackedListModule& module = modules->back();
+
+  // Hash the basename.
+  const std::string module_basename = base::UTF16ToUTF8(
+      base::i18n::ToLower(module_key.module_path.BaseName().value()));
+  base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_basename.data()),
+                      module_basename.length(), module.basename_hash);
+
+  // Hash the code id.
+  const std::string module_code_id = GenerateCodeId(module_key);
+  base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_code_id.data()),
+                      module_code_id.length(), module.code_id_hash);
+
+  module.time_date_stamp = CalculateTimeDateStamp(base::Time::Now());
+}
+
 }  // namespace
 
 // static
@@ -140,11 +164,6 @@
       background_sequence_(base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BACKGROUND,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
-      // The use of base::Unretained() is safe here because the callback can
-      // only be invoked while |module_load_attempt_log_listener_| is alive.
-      module_load_attempt_log_listener_(
-          base::BindRepeating(&ModuleBlacklistCacheUpdater::OnNewModulesBlocked,
-                              base::Unretained(this))),
       weak_ptr_factory_(this) {
   DCHECK(module_list_filter_);
   module_database_event_source_->AddObserver(this);
@@ -189,6 +208,14 @@
   if (module_key.module_id + 1 > module_blocking_decisions_.size())
     module_blocking_decisions_.resize(module_key.module_id + 1);
 
+  if (module_data.module_properties & ModuleInfoData::kPropertyBlocked) {
+    InsertPackedListModule(module_key, &blocked_modules_);
+
+    module_blocking_decisions_[module_key.module_id] =
+        ModuleBlockingDecision::kBlocked;
+    return;
+  }
+
   // Only consider loaded modules.
   if ((module_data.module_properties & ModuleInfoData::kPropertyLoadedModule) ==
       0) {
@@ -261,22 +288,7 @@
       ModuleBlockingDecision::kBlacklisted;
 
   // Insert the blacklisted module.
-  newly_blacklisted_modules_.emplace_back();
-  third_party_dlls::PackedListModule& module =
-      newly_blacklisted_modules_.back();
-
-  // Hash the basename.
-  const std::string module_basename = base::UTF16ToUTF8(
-      base::i18n::ToLower(module_key.module_path.BaseName().value()));
-  base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_basename.data()),
-                      module_basename.length(), module.basename_hash);
-
-  // Hash the code id.
-  const std::string module_code_id = GenerateCodeId(module_key);
-  base::SHA1HashBytes(reinterpret_cast<const uint8_t*>(module_code_id.data()),
-                      module_code_id.length(), module.code_id_hash);
-
-  module.time_date_stamp = CalculateTimeDateStamp(base::Time::Now());
+  InsertPackedListModule(module_key, &newly_blacklisted_modules_);
 
   // Signal the module database that this module will be added to the cache.
   // Note that observers that care about this information should register to
@@ -304,21 +316,6 @@
   StartModuleBlacklistCacheUpdate();
 }
 
-void ModuleBlacklistCacheUpdater::OnNewModulesBlocked(
-    std::vector<third_party_dlls::PackedListModule>&& blocked_modules) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  blocked_modules_.insert(blocked_modules_.begin(),
-                          std::make_move_iterator(blocked_modules.begin()),
-                          std::make_move_iterator(blocked_modules.end()));
-
-  // Start the timer.
-  timer_.Start(FROM_HERE,
-               kUpdateTimerDuration,
-               base::Bind(&ModuleBlacklistCacheUpdater::OnTimerExpired,
-                          base::Unretained(this)));
-}
-
 ModuleBlacklistCacheUpdater::ModuleBlockingDecision
 ModuleBlacklistCacheUpdater::GetModuleBlockingDecision(
     ModuleInfoKey module_key) const {
@@ -337,8 +334,6 @@
 void ModuleBlacklistCacheUpdater::StartModuleBlacklistCacheUpdate() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  timer_.Stop();
-
   base::FilePath cache_file_path = GetModuleBlacklistCachePath();
   if (cache_file_path.empty())
     return;
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win.h b/chrome/browser/conflicts/module_blacklist_cache_updater_win.h
index cad149e..846cd9f2 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win.h
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win.h
@@ -16,7 +16,6 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
-#include "chrome/browser/conflicts/module_load_attempt_log_listener_win.h"
 #include "chrome/browser/conflicts/proto/module_list.pb.h"
 #include "chrome_elf/third_party_dlls/packed_list_format.h"
 
@@ -99,6 +98,8 @@
     kTolerated,
     // Blacklisted and will be blocked next launch.
     kBlacklisted,
+    // The module was blocked from loading into the process.
+    kBlocked,
   };
 
   struct CacheUpdateResult {
@@ -143,10 +144,6 @@
                            const ModuleInfoData& module_data) override;
   void OnModuleDatabaseIdle() override;
 
-  // Callback for |module_load_attempt_log_listener_|;
-  void OnNewModulesBlocked(
-      std::vector<third_party_dlls::PackedListModule>&& blocked_modules);
-
   // Returns the blocking decision for a module.
   ModuleBlockingDecision GetModuleBlockingDecision(
       ModuleInfoKey module_key) const;
@@ -174,8 +171,6 @@
   // module blacklist cache.
   std::vector<third_party_dlls::PackedListModule> newly_blacklisted_modules_;
 
-  ModuleLoadAttemptLogListener module_load_attempt_log_listener_;
-
   // Temporarily holds modules that were blocked from loading into the browser
   // until they are used to update the cache.
   std::vector<third_party_dlls::PackedListModule> blocked_modules_;
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
index afbf4e98..c8a662f 100644
--- a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
@@ -351,25 +351,3 @@
 
   EXPECT_TRUE(internal::ModuleEqual()(expected, blacklisted_modules[0]));
 }
-
-// This tests that if a new blocked load attempt arrives, an update will still
-// be triggered even if the Module Database never goes idle afterwards.
-TEST_F(ModuleBlacklistCacheUpdaterTest, NewModulesBlockedOnly) {
-  EXPECT_FALSE(base::PathExists(module_blacklist_cache_path()));
-
-  auto module_blacklist_cache_updater = CreateModuleBlacklistCacheUpdater();
-
-  // Simulate a new blocked load attempt.
-  std::vector<third_party_dlls::PackedListModule> blocked_modules;
-  const third_party_dlls::PackedListModule module = {
-      {}, {}, 123456u,
-  };
-  blocked_modules.push_back(module);
-  module_blacklist_cache_updater->OnNewModulesBlocked(
-      std::move(blocked_modules));
-
-  FastForwardBy(ModuleBlacklistCacheUpdater::kUpdateTimerDuration);
-  EXPECT_TRUE(base::PathExists(module_blacklist_cache_path()));
-  EXPECT_TRUE(on_cache_updated_callback_invoked());
-  EXPECT_TRUE(RegistryKeyExists());
-}
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc
index 3b5bfab..07d6662a 100644
--- a/chrome/browser/conflicts/module_database_win.cc
+++ b/chrome/browser/conflicts/module_database_win.cc
@@ -51,8 +51,13 @@
       has_started_processing_(false),
       shell_extensions_enumerated_(false),
       ime_enumerated_(false),
-      // ModuleDatabase owns |module_inspector_|, so it is safe to use
-      // base::Unretained().
+// ModuleDatabase owns both |module_load_attempt_log_listener_| and
+// |module_inspector_|, so it is safe to use base::Unretained().
+#if defined(GOOGLE_CHROME_BUILD)
+      module_load_attempt_log_listener_(
+          base::BindRepeating(&ModuleDatabase::OnModuleBlocked,
+                              base::Unretained(this))),
+#endif  // defined(GOOGLE_CHROME_BUILD)
       module_inspector_(base::Bind(&ModuleDatabase::OnModuleInspected,
                                    base::Unretained(this))) {
   AddObserver(&third_party_metrics_);
@@ -176,6 +181,16 @@
   }
 }
 
+void ModuleDatabase::OnModuleBlocked(const base::FilePath& module_path,
+                                     uint32_t module_size,
+                                     uint32_t module_time_date_stamp) {
+  ModuleInfo* module_info = nullptr;
+  FindOrCreateModuleInfo(module_path, module_size, module_time_date_stamp,
+                         &module_info);
+
+  module_info->second.module_properties |= ModuleInfoData::kPropertyBlocked;
+}
+
 void ModuleDatabase::OnModuleAddedToBlacklist(const base::FilePath& module_path,
                                               uint32_t module_size,
                                               uint32_t module_time_date_stamp) {
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h
index 4c1f084..b1705a3 100644
--- a/chrome/browser/conflicts/module_database_win.h
+++ b/chrome/browser/conflicts/module_database_win.h
@@ -18,6 +18,10 @@
 #include "chrome/browser/conflicts/third_party_metrics_recorder_win.h"
 #include "content/public/common/process_type.h"
 
+#if defined(GOOGLE_CHROME_BUILD)
+#include "chrome/browser/conflicts/module_load_attempt_log_listener_win.h"
+#endif
+
 class ModuleDatabaseObserver;
 
 #if defined(GOOGLE_CHROME_BUILD)
@@ -98,6 +102,10 @@
                     uint32_t module_time_date_stamp,
                     uintptr_t module_load_address);
 
+  void OnModuleBlocked(const base::FilePath& module_path,
+                       uint32_t module_size,
+                       uint32_t module_time_date_stamp);
+
   // Marks the module as added to the module blacklist cache, which means it
   // will be blocked on the next browser launch.
   void OnModuleAddedToBlacklist(const base::FilePath& module_path,
@@ -221,6 +229,10 @@
   // Indicates if all input method editors have been enumerated.
   bool ime_enumerated_;
 
+#if defined(GOOGLE_CHROME_BUILD)
+  ModuleLoadAttemptLogListener module_load_attempt_log_listener_;
+#endif
+
   // Inspects new modules on a blocking task runner.
   ModuleInspector module_inspector_;
 
diff --git a/chrome/browser/conflicts/module_info_win.h b/chrome/browser/conflicts/module_info_win.h
index 2ee4d77c..438a4843 100644
--- a/chrome/browser/conflicts/module_info_win.h
+++ b/chrome/browser/conflicts/module_info_win.h
@@ -89,6 +89,8 @@
     kPropertyIme = 1 << 2,
     // The module was added to the module blacklist cache.
     kPropertyAddedToBlacklist = 1 << 3,
+    // These modules were blocked from loading into the process.
+    kPropertyBlocked = 1 << 4,
   };
 
   ModuleInfoData();
diff --git a/chrome/browser/conflicts/module_load_attempt_log_listener_win.cc b/chrome/browser/conflicts/module_load_attempt_log_listener_win.cc
index 89e77a2..9d26544 100644
--- a/chrome/browser/conflicts/module_load_attempt_log_listener_win.cc
+++ b/chrome/browser/conflicts/module_load_attempt_log_listener_win.cc
@@ -7,12 +7,13 @@
 #include <algorithm>
 #include <memory>
 #include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/i18n/case_conversion.h"
 #include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/sha1.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task_runner_util.h"
 #include "base/task_scheduler/post_task.h"
@@ -22,7 +23,12 @@
 
 namespace {
 
-std::vector<third_party_dlls::PackedListModule> DrainLogOnBackgroundTask() {
+// Drains the log of blocked modules from chrome_elf.dll.
+// Note that the paths returned are device paths, which starts with
+// "\Device\Harddisk". They need to be translated to their drive letter path
+// equivalent.
+std::vector<std::tuple<base::FilePath, uint32_t, uint32_t>>
+DrainLogOnBackgroundTask() {
   // Query the number of bytes needed.
   uint32_t bytes_needed = 0;
   DrainLog(nullptr, 0, &bytes_needed);
@@ -32,10 +38,8 @@
   uint32_t bytes_written = DrainLog(buffer.get(), bytes_needed, nullptr);
   DCHECK_EQ(bytes_needed, bytes_written);
 
-  auto now_time_date_stamp = CalculateTimeDateStamp(base::Time::Now());
-
   // Parse the data using the recommanded pattern for iterating over the log.
-  std::vector<third_party_dlls::PackedListModule> blocked_modules;
+  std::vector<std::tuple<base::FilePath, uint32_t, uint32_t>> blocked_modules;
   uint8_t* tracker = buffer.get();
   uint8_t* buffer_end = buffer.get() + bytes_written;
   while (tracker < buffer_end) {
@@ -51,29 +55,9 @@
     if (entry->type == third_party_dlls::LogType::kBlocked) {
       // No log path should be empty.
       DCHECK(entry->path_len);
-      blocked_modules.emplace_back();
-      third_party_dlls::PackedListModule& module = blocked_modules.back();
-      // Fill in a PackedListModule from the log entry.
-      std::string hash_string =
-          base::SHA1HashString(third_party_dlls::GetFingerprintString(
-              entry->time_date_stamp, entry->module_size));
-      std::copy(std::begin(hash_string), std::end(hash_string),
-                std::begin(module.code_id_hash));
-      // |entry->path| is a UTF-8 device path.  A hash of the
-      // lowercase, UTF-8 basename is needed for |module.basename_hash|.
-      base::FilePath file_path(base::UTF8ToUTF16(entry->path));
-      std::wstring basename = base::i18n::ToLower(file_path.BaseName().value());
-      hash_string = base::UTF16ToUTF8(basename);
-      hash_string = base::SHA1HashString(hash_string);
-      std::copy(std::begin(hash_string), std::end(hash_string),
-                std::begin(module.basename_hash));
-      module.time_date_stamp = now_time_date_stamp;
-
-      // TODO(pmonette): |file_path| is ready for
-      //                 base::DevicePathToDriveLetterPath() here if needed.
-      //                 Consider making these path conversions more efficient
-      //                 by caching the local mounted devices and corresponding
-      //                 drive paths once.
+      blocked_modules.emplace_back(
+          base::UTF8ToUTF16(base::StringPiece(entry->path, entry->path_len)),
+          entry->module_size, entry->time_date_stamp);
     }
 
     tracker += third_party_dlls::GetLogEntrySize(entry->path_len);
@@ -85,9 +69,8 @@
 }  // namespace
 
 ModuleLoadAttemptLogListener::ModuleLoadAttemptLogListener(
-    OnNewModulesBlockedCallback on_new_modules_blocked_callback)
-    : on_new_modules_blocked_callback_(
-          std::move(on_new_modules_blocked_callback)),
+    OnModuleBlockedCallback on_module_blocked_callback)
+    : on_module_blocked_callback_(std::move(on_module_blocked_callback)),
       background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
           {base::TaskPriority::BACKGROUND,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
@@ -118,6 +101,69 @@
 }
 
 void ModuleLoadAttemptLogListener::OnLogDrained(
-    std::vector<third_party_dlls::PackedListModule>&& blocked_modules) {
-  on_new_modules_blocked_callback_.Run(std::move(blocked_modules));
+    std::vector<std::tuple<base::FilePath, uint32_t, uint32_t>>&&
+        blocked_modules) {
+  for (auto& entry : blocked_modules) {
+    // Translate the device path to their drive letter equivalent then notify
+    // via the callback. The callback is invoked regardless of the result of
+    // GetDriveLetterPath() so that the module still shows up in
+    // chrome://conflicts.
+    base::FilePath module_path = std::move(std::get<0>(entry));
+    bool drive_letter_path_found =
+        GetDriveLetterPath(module_path, &module_path);
+    UMA_HISTOGRAM_BOOLEAN("ThirdPartyModules.GetDriveLetterPathFound",
+                          drive_letter_path_found);
+    on_module_blocked_callback_.Run(std::move(module_path), std::get<1>(entry),
+                                    std::get<2>(entry));
+  }
+}
+
+bool ModuleLoadAttemptLogListener::GetDriveLetterPath(
+    const base::FilePath& device_path,
+    base::FilePath* drive_letter_path) {
+  for (size_t retry_count = 0; retry_count < 2; ++retry_count) {
+    // Only update the mapping if a matching device root wasn't found.
+    if (retry_count > 0)
+      UpdateDeviceToLetterPathMapping();
+
+    for (const auto& element : device_to_letter_path_mapping_) {
+      const base::FilePath& device_root = element.first;
+      const base::string16& drive_letter_root = element.second;
+      if (device_root.IsParent(device_path)) {
+        *drive_letter_path = base::FilePath(
+            drive_letter_root +
+            device_path.value().substr(device_root.value().length()));
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+void ModuleLoadAttemptLogListener::UpdateDeviceToLetterPathMapping() {
+  const int kDriveMappingSize = 1024;
+  wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
+  if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping))
+    return;
+
+  device_to_letter_path_mapping_.clear();
+
+  wchar_t* drive_map_ptr = drive_mapping;
+  wchar_t device_path_as_string[MAX_PATH];
+  wchar_t drive[] = L" :";
+
+  while (*drive_map_ptr) {
+    drive[0] = drive_map_ptr[0];  // Copy the drive letter.
+
+    if (::QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
+      device_to_letter_path_mapping_.emplace_back(
+          base::FilePath(device_path_as_string), drive);
+    }
+
+    // Move to the next drive letter string, which starts one
+    // increment after the '\0' that terminates the current string.
+    while (*drive_map_ptr++) {
+    }
+  }
 }
diff --git a/chrome/browser/conflicts/module_load_attempt_log_listener_win.h b/chrome/browser/conflicts/module_load_attempt_log_listener_win.h
index 468d8588..4538db3 100644
--- a/chrome/browser/conflicts/module_load_attempt_log_listener_win.h
+++ b/chrome/browser/conflicts/module_load_attempt_log_listener_win.h
@@ -5,9 +5,12 @@
 #ifndef CHROME_BROWSER_CONFLICTS_MODULE_LOAD_ATTEMPT_LOG_LISTENER_WIN_H_
 #define CHROME_BROWSER_CONFLICTS_MODULE_LOAD_ATTEMPT_LOG_LISTENER_WIN_H_
 
+#include <tuple>
+#include <utility>
 #include <vector>
 
 #include "base/callback.h"
+#include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/synchronization/waitable_event.h"
@@ -23,11 +26,13 @@
 // notifies its delegate for all modules that were blocked.
 class ModuleLoadAttemptLogListener : public base::win::ObjectWatcher::Delegate {
  public:
-  using OnNewModulesBlockedCallback = base::RepeatingCallback<void(
-      std::vector<third_party_dlls::PackedListModule>&& blocked_modules)>;
+  using OnModuleBlockedCallback =
+      base::RepeatingCallback<void(const base::FilePath& module_path,
+                                   uint32_t module_size,
+                                   uint32_t module_time_date_stamp)>;
 
   explicit ModuleLoadAttemptLogListener(
-      OnNewModulesBlockedCallback on_new_modules_blocked_callback);
+      OnModuleBlockedCallback on_module_blocked_callback);
   ~ModuleLoadAttemptLogListener() override;
 
   // base::win::ObjectWatcher::Delegate:
@@ -37,10 +42,20 @@
   void StartDrainingLogs();
 
   void OnLogDrained(
-      std::vector<third_party_dlls::PackedListModule>&& blocked_modules);
+      std::vector<std::tuple<base::FilePath, uint32_t, uint32_t>>&&
+          blocked_modules);
 
-  // Invoked everytime the log is drained with the new blocked entries.
-  OnNewModulesBlockedCallback on_new_modules_blocked_callback_;
+  // Translates a path of the form "\Device\HarddiskVolumeXX\..." into its
+  // equivalent that starts with a drive letter ("C:\..."). Returns false on
+  // failure.
+  bool GetDriveLetterPath(const base::FilePath& device_path,
+                          base::FilePath* drive_letter_path);
+
+  // Update the |device_to_letter_path_mapping_|.
+  void UpdateDeviceToLetterPathMapping();
+
+  // Invoked once per blocked module every time the log is drained.
+  OnModuleBlockedCallback on_module_blocked_callback_;
 
   // The sequence in which the log is drained.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
@@ -51,6 +66,11 @@
   // Watches |waitable_event_|.
   base::win::ObjectWatcher object_watcher_;
 
+  // A cache of the mapping of device path roots to their drive letter root
+  // equivalent.
+  std::vector<std::pair<base::FilePath, base::string16>>
+      device_to_letter_path_mapping_;
+
   base::WeakPtrFactory<ModuleLoadAttemptLogListener> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ModuleLoadAttemptLogListener);
diff --git a/chrome/browser/conflicts/module_load_attempt_log_listener_win_unittest.cc b/chrome/browser/conflicts/module_load_attempt_log_listener_win_unittest.cc
index 40337cd..4000404b 100644
--- a/chrome/browser/conflicts/module_load_attempt_log_listener_win_unittest.cc
+++ b/chrome/browser/conflicts/module_load_attempt_log_listener_win_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/conflicts/module_load_attempt_log_listener_win.h"
 
 #include <memory>
+#include <tuple>
 #include <utility>
 
 #include "base/bind.h"
@@ -23,15 +24,17 @@
 
   std::unique_ptr<ModuleLoadAttemptLogListener>
   CreateModuleLoadAttemptLogListener() {
-    return std::make_unique<ModuleLoadAttemptLogListener>(base::BindRepeating(
-        &ModuleLoadAttemptLogListenerTest::OnNewModulesBlocked,
-        base::Unretained(this)));
+    return std::make_unique<ModuleLoadAttemptLogListener>(
+        base::BindRepeating(&ModuleLoadAttemptLogListenerTest::OnModuleBlocked,
+                            base::Unretained(this)));
   }
 
   // ModuleLoadAttemptLogListener::Delegate:
-  void OnNewModulesBlocked(
-      std::vector<third_party_dlls::PackedListModule>&& blocked_modules) {
-    blocked_modules_ = std::move(blocked_modules);
+  void OnModuleBlocked(const base::FilePath& module_path,
+                       uint32_t module_size,
+                       uint32_t module_time_date_stamp) {
+    blocked_modules_.emplace_back(module_path, module_size,
+                                  module_time_date_stamp);
 
     notified_ = true;
 
@@ -48,7 +51,8 @@
     run_loop.Run();
   }
 
-  const std::vector<third_party_dlls::PackedListModule>& blocked_modules() {
+  const std::vector<std::tuple<base::FilePath, uint32_t, uint32_t>>&
+  blocked_modules() {
     return blocked_modules_;
   }
 
@@ -59,7 +63,7 @@
 
   base::Closure quit_closure_;
 
-  std::vector<third_party_dlls::PackedListModule> blocked_modules_;
+  std::vector<std::tuple<base::FilePath, uint32_t, uint32_t>> blocked_modules_;
 
   DISALLOW_COPY_AND_ASSIGN(ModuleLoadAttemptLogListenerTest);
 };
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0a6fd64..3b35f12 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2687,10 +2687,10 @@
       "views/try_chrome_dialog_win/try_chrome_dialog.h",
       "views/uninstall_view.cc",
       "views/uninstall_view.h",
-      "webui/conflicts_handler.cc",
-      "webui/conflicts_handler.h",
-      "webui/conflicts_ui.cc",
-      "webui/conflicts_ui.h",
+      "webui/conflicts/conflicts_handler.cc",
+      "webui/conflicts/conflicts_handler.h",
+      "webui/conflicts/conflicts_ui.cc",
+      "webui/conflicts/conflicts_ui.h",
       "webui/settings/chrome_cleanup_handler.cc",
       "webui/settings/chrome_cleanup_handler.h",
       "webui/settings_utils_win.cc",
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 97eb43c..be8b073 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -180,7 +180,7 @@
 #endif
 
 #if defined(OS_WIN)
-#include "chrome/browser/ui/webui/conflicts_ui.h"
+#include "chrome/browser/ui/webui/conflicts/conflicts_ui.h"
 #include "chrome/browser/ui/webui/set_as_default_browser_ui_win.h"
 #include "chrome/browser/ui/webui/welcome_win10_ui.h"
 #endif
diff --git a/chrome/browser/ui/webui/conflicts/OWNERS b/chrome/browser/ui/webui/conflicts/OWNERS
new file mode 100644
index 0000000..4495bb1
--- /dev/null
+++ b/chrome/browser/ui/webui/conflicts/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/conflicts/OWNERS
diff --git a/chrome/browser/ui/webui/conflicts_handler.cc b/chrome/browser/ui/webui/conflicts/conflicts_handler.cc
similarity index 96%
rename from chrome/browser/ui/webui/conflicts_handler.cc
rename to chrome/browser/ui/webui/conflicts/conflicts_handler.cc
index 4e278e7..5009ff7 100644
--- a/chrome/browser/ui/webui/conflicts_handler.cc
+++ b/chrome/browser/ui/webui/conflicts/conflicts_handler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/conflicts_handler.h"
+#include "chrome/browser/ui/webui/conflicts/conflicts_handler.h"
 
 #include <utility>
 
@@ -72,6 +72,8 @@
         return "Tolerated - Will be blocked in the future";
       case BlockingDecision::kBlacklisted:
         return "Disallowed - Added to the blacklist";
+      case BlockingDecision::kBlocked:
+        return "Disallowed - Blocked";
       case BlockingDecision::kUnknown:
         NOTREACHED();
         break;
@@ -156,9 +158,14 @@
   }
 #endif  // defined(GOOGLE_CHROME_BUILD)
 
-  base::string16 type_string;
+  std::string type_string;
   if (module_data.module_properties & ModuleInfoData::kPropertyShellExtension)
-    type_string = L"Shell extension";
+    type_string = "Shell extension";
+  if (module_data.module_properties & ModuleInfoData::kPropertyBlocked) {
+    if (!type_string.empty())
+      type_string += ", ";
+    type_string += "blocked";
+  }
   data->SetString("type_description", type_string);
 
   const auto& inspection_result = *module_data.inspection_result;
diff --git a/chrome/browser/ui/webui/conflicts_handler.h b/chrome/browser/ui/webui/conflicts/conflicts_handler.h
similarity index 94%
rename from chrome/browser/ui/webui/conflicts_handler.h
rename to chrome/browser/ui/webui/conflicts/conflicts_handler.h
index f6eeab2..51f0630 100644
--- a/chrome/browser/ui/webui/conflicts_handler.h
+++ b/chrome/browser/ui/webui/conflicts/conflicts_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CONFLICTS_HANDLER_H_
-#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_HANDLER_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_CONFLICTS_CONFLICTS_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_CONFLICTS_HANDLER_H_
 
 #include <memory>
 #include <string>
@@ -93,4 +93,4 @@
   DISALLOW_COPY_AND_ASSIGN(ConflictsHandler);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CONFLICTS_HANDLER_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_CONFLICTS_CONFLICTS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/conflicts_ui.cc b/chrome/browser/ui/webui/conflicts/conflicts_ui.cc
similarity index 91%
rename from chrome/browser/ui/webui/conflicts_ui.cc
rename to chrome/browser/ui/webui/conflicts/conflicts_ui.cc
index 7e6761cf..38b4f7f1 100644
--- a/chrome/browser/ui/webui/conflicts_ui.cc
+++ b/chrome/browser/ui/webui/conflicts/conflicts_ui.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/webui/conflicts_ui.h"
+#include "chrome/browser/ui/webui/conflicts/conflicts_ui.h"
 
 #include <memory>
 
 #include "base/memory/ref_counted_memory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/webui/conflicts_handler.h"
+#include "chrome/browser/ui/webui/conflicts/conflicts_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
@@ -50,7 +50,7 @@
 
 // static
 base::RefCountedMemory* ConflictsUI::GetFaviconResourceBytes(
-      ui::ScaleFactor scale_factor) {
+    ui::ScaleFactor scale_factor) {
   return static_cast<base::RefCountedMemory*>(
       ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
           IDR_CONFLICT_FAVICON, scale_factor));
diff --git a/chrome/browser/ui/webui/conflicts_ui.h b/chrome/browser/ui/webui/conflicts/conflicts_ui.h
similarity index 77%
rename from chrome/browser/ui/webui/conflicts_ui.h
rename to chrome/browser/ui/webui/conflicts/conflicts_ui.h
index c13d471e..64599d5 100644
--- a/chrome/browser/ui/webui/conflicts_ui.h
+++ b/chrome/browser/ui/webui/conflicts/conflicts_ui.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
-#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
+#ifndef CHROME_BROWSER_UI_WEBUI_CONFLICTS_CONFLICTS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_CONFLICTS_UI_H_
 
 #include "base/macros.h"
 #include "content/public/browser/web_ui_controller.h"
@@ -25,4 +25,4 @@
   DISALLOW_COPY_AND_ASSIGN(ConflictsUI);
 };
 
-#endif  // CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_
+#endif  // CHROME_BROWSER_UI_WEBUI_CONFLICTS_CONFLICTS_UI_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 075c7be..0596c82 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4089,6 +4089,11 @@
   <int value="1" label="Lacks Form Manager"/>
 </enum>
 
+<enum name="BooleanFound">
+  <int value="0" label="Not found"/>
+  <int value="1" label="Found"/>
+</enum>
+
 <enum name="BooleanFrameAsOverlay">
   <int value="0" label="Frame was not allow_overlay"/>
   <int value="1" label="Frame was allow_overlay"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index bb4a9ec..c3d9756 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -103455,6 +103455,14 @@
   </summary>
 </histogram>
 
+<histogram name="ThirdPartyModules.GetDriveLetterPathFound" enum="BooleanFound">
+  <owner>pmonette@chromium.org</owner>
+  <summary>
+    Records whether an equivalent driver letter path was found for a device
+    path.
+  </summary>
+</histogram>
+
 <histogram name="ThirdPartyModules.InputMethodEditorsCount" units="counts">
   <owner>pmonette@chromium.org</owner>
   <summary>