1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/external_registry_loader_win.h"
8 #include "base/file_util.h"
9 #include "base/files/file_path.h"
10 #include "base/memory/scoped_handle.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "base/values.h"
16 #include "base/version.h"
17 #include "base/win/registry.h"
18 #include "chrome/browser/extensions/external_provider_impl.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "extensions/common/extension.h"
22 using content::BrowserThread
;
26 // The Registry subkey that contains information about external extensions.
27 const char kRegistryExtensions
[] = "Software\\Google\\Chrome\\Extensions";
29 // Registry value of the key that defines the path to the .crx file.
30 const wchar_t kRegistryExtensionPath
[] = L
"path";
32 // Registry value of that key that defines the current version of the .crx file.
33 const wchar_t kRegistryExtensionVersion
[] = L
"version";
35 // Registry value of the key that defines an external update URL.
36 const wchar_t kRegistryExtensionUpdateUrl
[] = L
"update_url";
38 bool CanOpenFileForReading(const base::FilePath
& path
) {
39 ScopedStdioHandle
file_handle(base::OpenFile(path
, "rb"));
40 return file_handle
.get() != NULL
;
45 namespace extensions
{
47 void ExternalRegistryLoader::StartLoading() {
48 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
49 BrowserThread::PostTask(
50 BrowserThread::FILE, FROM_HERE
,
51 base::Bind(&ExternalRegistryLoader::LoadOnFileThread
, this));
54 void ExternalRegistryLoader::LoadOnFileThread() {
55 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
56 base::TimeTicks start_time
= base::TimeTicks::Now();
57 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
59 // A map of IDs, to weed out duplicates between HKCU and HKLM.
60 std::set
<base::string16
> keys
;
61 base::win::RegistryKeyIterator
iterator_machine_key(
62 HKEY_LOCAL_MACHINE
, base::ASCIIToWide(kRegistryExtensions
).c_str());
63 for (; iterator_machine_key
.Valid(); ++iterator_machine_key
)
64 keys
.insert(iterator_machine_key
.Name());
65 base::win::RegistryKeyIterator
iterator_user_key(
66 HKEY_CURRENT_USER
, base::ASCIIToWide(kRegistryExtensions
).c_str());
67 for (; iterator_user_key
.Valid(); ++iterator_user_key
)
68 keys
.insert(iterator_user_key
.Name());
70 // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
71 // policy conventions. We only fall back to HKCU if the HKLM key cannot be
72 // opened, not if the data within the key is invalid, for example.
73 for (std::set
<base::string16
>::const_iterator it
= keys
.begin();
74 it
!= keys
.end(); ++it
) {
75 base::win::RegKey key
;
76 base::string16 key_path
= base::ASCIIToWide(kRegistryExtensions
);
77 key_path
.append(L
"\\");
79 if (key
.Open(HKEY_LOCAL_MACHINE
,
80 key_path
.c_str(), KEY_READ
) != ERROR_SUCCESS
) {
81 if (key
.Open(HKEY_CURRENT_USER
,
82 key_path
.c_str(), KEY_READ
) != ERROR_SUCCESS
) {
83 LOG(ERROR
) << "Unable to read registry key at path (HKLM & HKCU): "
89 std::string id
= WideToASCII(*it
);
90 StringToLowerASCII(&id
);
91 if (!Extension::IdIsValid(id
)) {
92 LOG(ERROR
) << "Invalid id value " << id
93 << " for key " << key_path
<< ".";
97 // If there is an update URL present, copy it to prefs and ignore
98 // path and version keys for this entry.
99 base::string16 extension_update_url
;
100 if (key
.ReadValue(kRegistryExtensionUpdateUrl
, &extension_update_url
)
103 id
+ "." + ExternalProviderImpl::kExternalUpdateUrl
,
104 WideToASCII(extension_update_url
));
108 base::string16 extension_path_str
;
109 if (key
.ReadValue(kRegistryExtensionPath
, &extension_path_str
)
111 // TODO(erikkay): find a way to get this into about:extensions
112 LOG(ERROR
) << "Missing value " << kRegistryExtensionPath
113 << " for key " << key_path
<< ".";
117 base::FilePath
extension_path(extension_path_str
);
118 if (!extension_path
.IsAbsolute()) {
119 LOG(ERROR
) << "File path " << extension_path_str
120 << " needs to be absolute in key "
125 if (!base::PathExists(extension_path
)) {
126 LOG(ERROR
) << "File " << extension_path_str
127 << " for key " << key_path
128 << " does not exist or is not readable.";
132 if (!CanOpenFileForReading(extension_path
)) {
133 LOG(ERROR
) << "File " << extension_path_str
134 << " for key " << key_path
<< " can not be read. "
135 << "Check that users who should have the extension "
136 << "installed have permission to read it.";
140 base::string16 extension_version
;
141 if (key
.ReadValue(kRegistryExtensionVersion
, &extension_version
)
143 // TODO(erikkay): find a way to get this into about:extensions
144 LOG(ERROR
) << "Missing value " << kRegistryExtensionVersion
145 << " for key " << key_path
<< ".";
149 Version
version(WideToASCII(extension_version
));
150 if (!version
.IsValid()) {
151 LOG(ERROR
) << "Invalid version value " << extension_version
152 << " for key " << key_path
<< ".";
157 id
+ "." + ExternalProviderImpl::kExternalVersion
,
158 WideToASCII(extension_version
));
160 id
+ "." + ExternalProviderImpl::kExternalCrx
,
164 prefs_
.reset(prefs
.release());
165 HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
166 base::TimeTicks::Now() - start_time
);
167 BrowserThread::PostTask(
168 BrowserThread::UI
, FROM_HERE
,
169 base::Bind(&ExternalRegistryLoader::LoadFinished
, this));
172 } // namespace extensions