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