1 // Copyright 2020 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/upgrade_detector/directory_monitor.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/base_paths.h"
11 #include "base/bind.h"
12 #include "base/check.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_path_watcher.h"
15 #include "base/location.h"
16 #include "base/memory/scoped_refptr.h"
17 #include "base/path_service.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/task/task_traits.h"
20 #include "base/task/thread_pool.h"
21 #include "base/threading/sequenced_task_runner_handle.h"
22 #include "build/build_config.h"
23 #include "chrome/browser/upgrade_detector/installed_version_monitor.h"
24
25 #if defined(OS_MAC)
26 #include "base/mac/bundle_locations.h"
27 #endif
28
29 namespace {
30
GetDefaultMonitorLocation()31 base::FilePath GetDefaultMonitorLocation() {
32 #if defined(OS_MAC)
33 return base::mac::OuterBundlePath();
34 #else
35 return base::PathService::CheckedGet(base::DIR_EXE);
36 #endif
37 }
38
39 } // namespace
40
DirectoryMonitor(base::FilePath install_dir)41 DirectoryMonitor::DirectoryMonitor(base::FilePath install_dir)
42 : install_dir_(std::move(install_dir)) {
43 DCHECK(!install_dir_.empty());
44 }
45
~DirectoryMonitor()46 DirectoryMonitor::~DirectoryMonitor() {
47 if (watcher_)
48 task_runner_->DeleteSoon(FROM_HERE, std::move(watcher_));
49 }
50
Start(Callback on_change_callback)51 void DirectoryMonitor::Start(Callback on_change_callback) {
52 DCHECK(!watcher_);
53 task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
54 {base::TaskPriority::BEST_EFFORT,
55 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock()});
56 watcher_ = std::make_unique<base::FilePathWatcher>();
57
58 #if defined(OS_MAC)
59 // The normal Watch risks triggering a macOS Catalina+ consent dialog, so use
60 // a trivial watch here.
61 const base::FilePathWatcher::Type watch_type =
62 base::FilePathWatcher::Type::kTrivial;
63 #else
64 const base::FilePathWatcher::Type watch_type =
65 base::FilePathWatcher::Type::kNonRecursive;
66 #endif
67
68 // Start the watcher on a background sequence, reporting a failure to start to
69 // |on_change_callback| on the caller's sequence. The watcher is given a
70 // trampoline that will run |on_change_callback| on the caller's sequence.
71 // base::Unretained is safe because the watcher instance lives on the target
72 // sequence and will be destroyed there in a subsequent task.
73 task_runner_->PostTaskAndReplyWithResult(
74 FROM_HERE,
75 base::BindOnce(
76 static_cast<bool (base::FilePathWatcher::*)(
77 const base::FilePath&, base::FilePathWatcher::Type,
78 const base::FilePathWatcher::Callback&)>(
79 &base::FilePathWatcher::Watch),
80 base::Unretained(watcher_.get()), std::move(install_dir_), watch_type,
81 base::BindRepeating(
82 [](base::SequencedTaskRunner* main_sequence,
83 const Callback& on_change_callback, const base::FilePath&,
84 bool error) {
85 main_sequence->PostTask(
86 FROM_HERE, base::BindOnce(on_change_callback, error));
87 },
88 base::RetainedRef(base::SequencedTaskRunnerHandle::Get()),
89 on_change_callback)),
90 base::BindOnce(
91 [](const Callback& on_change_callback, bool start_result) {
92 if (!start_result)
93 on_change_callback.Run(/*error=*/true);
94 },
95 on_change_callback));
96 }
97
98 // static
Create()99 std::unique_ptr<InstalledVersionMonitor> InstalledVersionMonitor::Create() {
100 return std::make_unique<DirectoryMonitor>(GetDefaultMonitorLocation());
101 }
102