Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Side by Side Diff: chrome/browser/conflicts/installed_programs_win.cc

Issue 2854983002: Add the ThirdPartyModules.Uninstallable histogram. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/installed_programs_win.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/callback.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/task_scheduler/post_task.h"
15 #include "base/win/registry.h"
16 #include "chrome/browser/conflicts/msi_util_win.h"
17
18 namespace {
19
20 // Fetches a string |value| out of |key|. Return false if a non-empty value
21 // could not be retrived.
chrisha 2017/05/02 21:28:24 retrieved*
Patrick Monette 2017/05/29 22:04:54 Done.
22 bool GetRegistryKeyValue(const base::win::RegKey& key,
23 const wchar_t* value,
24 base::string16* result) {
25 return key.ReadValue(value, result) == ERROR_SUCCESS && !result->empty();
26 }
27
28 // Returns true if |candidate| is registered as a system component.
29 bool IsSystemComponent(const base::win::RegKey& candidate) {
30 DWORD system_component = 0;
31 return candidate.ReadValueDW(L"SystemComponent", &system_component) ==
32 ERROR_SUCCESS &&
33 system_component == 1;
34 }
35
36 // Fetches |value| out of |key|. Return false if a non-empty value could not
37 // be retrived.
chrisha 2017/05/02 21:28:25 retrieved*
Patrick Monette 2017/05/29 22:04:54 Done.
38 bool GetValue(const base::win::RegKey& key,
39 const wchar_t* value,
40 base::string16* result) {
41 return key.ReadValue(value, result) == ERROR_SUCCESS && !result->empty();
42 }
43
44 // Try to get the |install_path| from |candidate| using the InstallLocation
45 // value. Return true on success.
46 bool GetInstallPathUsingInstallLocation(const base::win::RegKey& candidate,
47 base::FilePath* install_path) {
48 base::string16 install_location;
49 if (GetValue(candidate, L"InstallLocation", &install_location)) {
50 *install_path = base::FilePath(std::move(install_location));
51 return true;
52 }
53 return false;
54 }
55
56 // Returns true if the |component_path| points to a registry key. Registry key
57 // paths are characterized by a number instead of a drive letter.
58 // See the documentation for ::MsiGetComponentPath():
59 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa370112(v=vs.85).as px
60 bool IsRegistryComponentPath(const base::string16& component_path) {
61 base::string16 drive_letter =
62 component_path.substr(0, component_path.find(':'));
63
64 for (const auto& registry_drive_letter :
65 {L"00", L"01", L"02", L"03", L"20", L"21", L"22", L"23"}) {
66 if (drive_letter == registry_drive_letter)
67 return true;
68 }
69
70 return false;
71 }
72
73 // Returns all the dlls installed by the product identified by |product_guid|.
74 // Returns true on success.
75 bool GetInstalledDllsUsingMsiGuid(const base::string16& product_guid,
76 std::vector<base::FilePath>* dlls) {
77 // An invalid product guid may have been passed to this function. In those
chrisha 2017/05/02 21:28:24 In this*
Patrick Monette 2017/05/29 22:04:55 Done.
78 // case, GetMsiComponentPaths() will return false so it is not necessary to
79 // specifically filter those out.
80 std::vector<base::string16> component_paths;
81 if (!GetMsiComponentPaths(product_guid, &component_paths))
82 return false;
83
84 for (const auto& component_path : component_paths) {
85 // Exclude registry component paths.
86 if (!IsRegistryComponentPath(component_path)) {
chrisha 2017/05/02 21:28:25 I have a very minor preference: if (...) contin
Patrick Monette 2017/05/29 22:04:55 Done.
87 base::FilePath file_path(std::move(component_path));
88 if (base::EqualsCaseInsensitiveASCII(file_path.Extension(), L".dll"))
chrisha 2017/05/02 21:28:24 This might be an artificial constraint. Loadable m
Patrick Monette 2017/05/29 22:04:54 This was mainly done so that searching for the ass
89 dlls->push_back(std::move(file_path));
90 }
91 }
92
93 return true;
94 }
95
96 // Checks if the registry key that the first 3 parameters refers to references
97 // an installed program in the Apps & Features settings page. If so, populates
98 // |internal_data| with the program name and its related file paths.
99 void CheckRegistryKeyForInstalledProgram(
100 HKEY hkey,
101 const base::string16& key_path,
102 const base::string16& key_name,
103 InstalledPrograms::InternalData* internal_data) {
104 base::win::RegKey candidate(
105 hkey,
106 base::StringPrintf(L"%ls\\%ls", key_path.c_str(), key_name.c_str())
107 .c_str(),
108 KEY_READ);
109
110 if (!candidate.Valid())
111 return;
112
113 // System components are not displayed in the Add or remove programs list.
114 if (IsSystemComponent(candidate))
115 return;
116
117 // If there is no UninstallString, the Uninstall button is grayed out.
118 base::string16 uninstall_string;
119 if (!GetValue(candidate, L"UninstallString", &uninstall_string))
120 return;
121
122 // Ignore Microsoft programs.
123 base::string16 publisher;
124 if (GetValue(candidate, L"Publisher", &publisher) &&
125 publisher == L"Microsoft Corporation") {
126 return;
127 }
128
129 // Candidates with no display names are ignored.
130 // Because this class is used to display warning to the user, not having the
chrisha 2017/05/02 21:28:25 s/the//
Patrick Monette 2017/05/29 22:04:54 Done.
131 // a display name renders the warning somewhat useless.
132 base::string16 display_name;
133 if (!GetValue(candidate, L"DisplayName", &display_name))
chrisha 2017/05/02 21:28:25 In this case could we directly check the image fil
Patrick Monette 2017/05/29 22:04:54 It would be possible. But remember that having no
134 return;
135
136 base::FilePath install_path;
137 if (GetInstallPathUsingInstallLocation(candidate, &install_path)) {
138 internal_data->program_names.push_back(std::move(display_name));
139 internal_data->install_locations.push_back(std::make_pair(
140 std::move(install_path), internal_data->program_names.size() - 1));
141 return;
chrisha 2017/05/02 21:28:25 So if we find an InstallPath then we don't want to
Patrick Monette 2017/05/29 22:04:54 If the InstallPath is good, it's faster to check v
142 }
143
144 std::vector<base::FilePath> dlls;
145 if (GetInstalledDllsUsingMsiGuid(key_name, &dlls)) {
146 internal_data->program_names.push_back(std::move(display_name));
147 for (size_t i = 0; i < dlls.size(); i++) {
148 internal_data->dll_map.push_back(std::make_pair(
149 std::move(dlls[i]), internal_data->program_names.size() - 1));
150 }
151 }
152 }
153
154 // Returns the index of the matching parent directory of |file| in |container|.
155 // Returns false if no matches were found.
156 bool BinarySearch(
chrisha 2017/05/02 21:28:25 Yay, you can write a binary search! You're hired :
Patrick Monette 2017/05/29 22:04:55 Yay!
157 const std::vector<std::pair<base::FilePath, size_t>>& container,
158 const base::FilePath& file,
159 size_t* result) {
160 int min = 0;
161 int max = container.size() - 1;
162 while (min <= max) {
163 int mid_index = (max + min) / 2;
164 const auto& mid = container[mid_index];
165 if (mid.first.IsParent(file)) {
166 *result = mid.second;
167 return true;
168 }
169
170 if (base::FilePath::CompareLessIgnoreCase(file.value(), mid.first.value()))
171 max = mid_index - 1;
172 else
173 min = mid_index + 1;
174 }
175
176 return false;
177 }
178
179 } // namespace
180
181 InstalledPrograms::InstalledPrograms()
182 : initialized_(false), weak_ptr_factory_(this) {}
183
184 InstalledPrograms::~InstalledPrograms() = default;
185
186 bool InstalledPrograms::GetInstalledProgramName(const base::FilePath& file,
187 base::string16* program_name) {
188 DCHECK(initialized_);
189
190 // First check if the exact file path can be found.
191 auto iter = dll_map_.find(file);
192 if (iter != dll_map_.end()) {
193 *program_name = program_names_[iter->second];
194 return true;
195 }
196
197 // Then check if one of the install locations matches the file.
198 if (install_locations_.empty())
199 return false;
200
201 size_t index;
202 if (!BinarySearch(install_locations_, file, &index))
203 return false;
204
205 *program_name = program_names_[index];
206 return true;
207 }
208
209 // static
210 std::unique_ptr<InstalledPrograms::InternalData>
211 InstalledPrograms::GetInternalData() {
212 auto internal_data = base::MakeUnique<InternalData>();
213
214 const wchar_t* kUninstallKeyPaths[] = {
215 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
216 L"SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
217 };
218
219 for (const auto& uninstall_key_path : kUninstallKeyPaths) {
220 for (const auto& hkey : {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER}) {
221 for (base::win::RegistryKeyIterator i(hkey, uninstall_key_path);
222 i.Valid(); ++i) {
223 CheckRegistryKeyForInstalledProgram(hkey, uninstall_key_path, i.Name(),
224 internal_data.get());
225 }
226 }
227 }
228
229 return internal_data;
230 }
231
232 void InstalledPrograms::OnInternalDataReceived(
233 const base::Closure& on_initialized_callback,
234 std::unique_ptr<InternalData> internal_data) {
235 program_names_ = std::move(internal_data->program_names);
236 base::flat_map<base::FilePath, size_t, FilePathLess> flat_map(
237 internal_data->dll_map, base::KEEP_FIRST_OF_DUPES);
238 dll_map_ = std::move(flat_map);
239 install_locations_ = std::move(internal_data->install_locations);
chrisha 2017/05/02 21:28:25 Just std::move the entire InternalData object?
Patrick Monette 2017/05/29 22:04:54 Done.
240
241 // Sort |install_locations_| so that it is possible to use binary search.
242 std::sort(install_locations_.begin(), install_locations_.end(),
243 [](const auto& lhs, const auto& rhs) {
244 return base::FilePath::CompareLessIgnoreCase(lhs.first.value(),
245 rhs.first.value());
246 });
247
248 initialized_ = true;
249 if (on_initialized_callback)
250 on_initialized_callback.Run();
251 }
252
253 void InstalledPrograms::Initialize(
254 const base::Closure& on_initialized_callback) {
255 DCHECK(!initialized_);
256 base::PostTaskWithTraitsAndReplyWithResult(
257 FROM_HERE,
258 base::TaskTraits()
259 .MayBlock()
260 .WithPriority(base::TaskPriority::BACKGROUND)
261 .WithShutdownBehavior(
262 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN),
263 base::Bind(&InstalledPrograms::GetInternalData),
264 base::Bind(&InstalledPrograms::OnInternalDataReceived,
265 weak_ptr_factory_.GetWeakPtr(), on_initialized_callback));
266 }
267 bool InstalledPrograms::FilePathLess::operator()(
268 const base::FilePath& lhs,
269 const base::FilePath& rhs) const {
270 return base::FilePath::CompareLessIgnoreCase(lhs.value(), rhs.value());
271 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698