pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 1 | // Copyright 2017 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. |
| 4 | |
| 5 | #include "chrome/browser/conflicts/module_inspector_win.h" |
| 6 | |
| 7 | #include <utility> |
| 8 | |
| 9 | #include "base/bind.h" |
Gabriel Charette | 1452023 | 2018-04-30 23:27:22 | [diff] [blame] | 10 | #include "base/sequenced_task_runner.h" |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 11 | #include "base/task_scheduler/post_task.h" |
Patrick Monette | ab307408 | 2018-04-12 22:40:13 | [diff] [blame] | 12 | #include "base/threading/sequenced_task_runner_handle.h" |
| 13 | #include "chrome/browser/after_startup_task_utils.h" |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 14 | |
| 15 | namespace { |
| 16 | |
| 17 | StringMapping GetPathMapping() { |
| 18 | return GetEnvironmentVariablesMapping({ |
| 19 | L"LOCALAPPDATA", L"ProgramFiles", L"ProgramData", L"USERPROFILE", |
| 20 | L"SystemRoot", L"TEMP", L"TMP", L"CommonProgramFiles", |
| 21 | }); |
| 22 | } |
| 23 | |
Patrick Monette | ab307408 | 2018-04-12 22:40:13 | [diff] [blame] | 24 | // Does the inspection of the module and replies with the result by calling |
| 25 | // |on_inspection_finished_callback| on |reply_task_runner|. |
| 26 | // The StringMapping is wrapped in a RefCountedData to save a copy per |
| 27 | // invocation. |
| 28 | // |
| 29 | // TODO(pmonette): When the Task Scheduler starts supporting after-startup |
| 30 | // background sequences, change this to use base::PostTaskAndReplyWithResult(). |
| 31 | void InspectModuleOnBlockingSequenceAndReply( |
pmonette | a2751f9 | 2017-06-02 02:28:01 | [diff] [blame] | 32 | scoped_refptr<base::RefCountedData<StringMapping>> env_variable_mapping, |
Patrick Monette | ab307408 | 2018-04-12 22:40:13 | [diff] [blame] | 33 | const ModuleInfoKey& module_key, |
| 34 | scoped_refptr<base::SequencedTaskRunner> reply_task_runner, |
| 35 | base::OnceCallback<void(std::unique_ptr<ModuleInspectionResult>)> |
| 36 | on_inspection_finished_callback) { |
| 37 | reply_task_runner->PostTask( |
| 38 | FROM_HERE, |
| 39 | base::BindOnce(std::move(on_inspection_finished_callback), |
| 40 | InspectModule(env_variable_mapping->data, module_key))); |
pmonette | a2751f9 | 2017-06-02 02:28:01 | [diff] [blame] | 41 | } |
| 42 | |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 43 | } // namespace |
| 44 | |
| 45 | ModuleInspector::ModuleInspector( |
| 46 | const OnModuleInspectedCallback& on_module_inspected_callback) |
| 47 | : on_module_inspected_callback_(on_module_inspected_callback), |
Patrick Monette | ab307408 | 2018-04-12 22:40:13 | [diff] [blame] | 48 | task_runner_(base::CreateSequencedTaskRunnerWithTraits( |
| 49 | {base::MayBlock(), base::TaskPriority::BACKGROUND, |
| 50 | base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), |
pmonette | a2751f9 | 2017-06-02 02:28:01 | [diff] [blame] | 51 | path_mapping_(base::MakeRefCounted<base::RefCountedData<StringMapping>>( |
| 52 | GetPathMapping())), |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 53 | weak_ptr_factory_(this) {} |
| 54 | |
| 55 | ModuleInspector::~ModuleInspector() = default; |
| 56 | |
| 57 | void ModuleInspector::AddModule(const ModuleInfoKey& module_key) { |
| 58 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 59 | queue_.push(module_key); |
| 60 | if (queue_.size() == 1) |
| 61 | StartInspectingModule(); |
| 62 | } |
| 63 | |
| 64 | void ModuleInspector::IncreaseInspectionPriority() { |
| 65 | DCHECK(thread_checker_.CalledOnValidThread()); |
Patrick Monette | ab307408 | 2018-04-12 22:40:13 | [diff] [blame] | 66 | // Create a task runner with higher priority so that future inspections are |
| 67 | // done faster. |
| 68 | task_runner_ = base::CreateSequencedTaskRunnerWithTraits( |
| 69 | {base::MayBlock(), base::TaskPriority::USER_VISIBLE, |
| 70 | base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}); |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 71 | } |
| 72 | |
Patrick Monette | 0decf1e8 | 2017-06-19 21:51:04 | [diff] [blame] | 73 | bool ModuleInspector::IsIdle() { |
| 74 | return queue_.empty(); |
| 75 | } |
| 76 | |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 77 | void ModuleInspector::StartInspectingModule() { |
| 78 | ModuleInfoKey module_key = queue_.front(); |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 79 | |
Patrick Monette | ab307408 | 2018-04-12 22:40:13 | [diff] [blame] | 80 | // There is a small priority inversion that happens when |
| 81 | // IncreaseInspectionPriority() is called while a module is currently being |
| 82 | // inspected. |
| 83 | // |
| 84 | // This is because all the subsequent tasks will be posted at a higher |
| 85 | // priority, but they are waiting on the current task that is currently |
| 86 | // running at a lower priority. |
| 87 | // |
| 88 | // In practice, this is not an issue because the only caller of |
| 89 | // IncreaseInspectionPriority() (chrome://conflicts) does not depend on the |
| 90 | // inspection to finish synchronously and is not blocking anything else. |
| 91 | AfterStartupTaskUtils::PostTask( |
| 92 | FROM_HERE, task_runner_, |
| 93 | base::BindOnce( |
| 94 | &InspectModuleOnBlockingSequenceAndReply, path_mapping_, module_key, |
| 95 | base::SequencedTaskRunnerHandle::Get(), |
| 96 | base::BindOnce(&ModuleInspector::OnInspectionFinished, |
| 97 | weak_ptr_factory_.GetWeakPtr(), module_key))); |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 98 | } |
| 99 | |
| 100 | void ModuleInspector::OnInspectionFinished( |
| 101 | const ModuleInfoKey& module_key, |
| 102 | std::unique_ptr<ModuleInspectionResult> inspection_result) { |
Patrick Monette | d9d2e64 | 2018-02-06 15:04:03 | [diff] [blame] | 103 | DCHECK(thread_checker_.CalledOnValidThread()); |
| 104 | |
Patrick Monette | 0decf1e8 | 2017-06-19 21:51:04 | [diff] [blame] | 105 | // Pop first, because the callback may want to know if there is any work left |
| 106 | // to be done, which is caracterized by a non-empty queue. |
| 107 | queue_.pop(); |
| 108 | |
pmonette | f693974 | 2017-03-07 17:15:31 | [diff] [blame] | 109 | on_module_inspected_callback_.Run(module_key, std::move(inspection_result)); |
| 110 | |
| 111 | // Continue the work. |
| 112 | if (!queue_.empty()) |
| 113 | StartInspectingModule(); |
| 114 | } |