OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/external_pref_extension_loader.h" | 5 #include "chrome/browser/extensions/external_pref_extension_loader.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/dir_reader_posix.h" |
8 #include "base/file_path.h" | 9 #include "base/file_path.h" |
9 #include "base/file_util.h" | 10 #include "base/file_util.h" |
10 #include "base/json/json_file_value_serializer.h" | 11 #include "base/json/json_file_value_serializer.h" |
11 #include "base/json/json_string_value_serializer.h" | 12 #include "base/json/json_string_value_serializer.h" |
12 #include "base/logging.h" | 13 #include "base/logging.h" |
13 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
14 #include "base/path_service.h" | 15 #include "base/path_service.h" |
15 #include "chrome/common/chrome_paths.h" | 16 #include "chrome/common/chrome_paths.h" |
16 #include "content/public/browser/browser_thread.h" | 17 #include "content/public/browser/browser_thread.h" |
17 | 18 |
18 using content::BrowserThread; | 19 using content::BrowserThread; |
19 | 20 |
20 namespace { | 21 namespace { |
21 | 22 |
| 23 std::set<FilePath> GetPrefsCandidateFilesFromFolder( |
| 24 const FilePath& external_extension_search_path) { |
| 25 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 26 |
| 27 std::set<FilePath> external_extension_paths; |
| 28 |
| 29 if (!file_util::PathExists(external_extension_search_path)) { |
| 30 // Does not have to exist. |
| 31 return external_extension_paths; |
| 32 } |
| 33 |
| 34 base::DirReaderPosix |
| 35 reader(external_extension_search_path.value().c_str()); |
| 36 |
| 37 if (!reader.IsValid()) { |
| 38 LOG(ERROR) << "Can not read external extensions path '" |
| 39 << external_extension_search_path.LossyDisplayName() |
| 40 << "' although it appears to exist."; |
| 41 return external_extension_paths; |
| 42 } |
| 43 |
| 44 while (reader.Next()) { |
| 45 const FilePath filename(reader.name()); |
| 46 |
| 47 if (filename.MatchesExtension(".json")) { |
| 48 external_extension_paths.insert(filename); |
| 49 } else { |
| 50 DVLOG(1) << "Not considering: " << reader.name() |
| 51 << " (does not have a .json extension)"; |
| 52 } |
| 53 } |
| 54 |
| 55 return external_extension_paths; |
| 56 } |
| 57 |
| 58 // Extracts extension information from a json file serialized by |serializer|. |
| 59 // |path| is only used for informational purposes (outputted when an error |
| 60 // occurs). An empty dictionary is returned in case of failure (e.g. invalid |
| 61 // path or json content). |
22 // Caller takes ownership of the returned dictionary. | 62 // Caller takes ownership of the returned dictionary. |
23 DictionaryValue* ExtractPrefs(const FilePath& path, | 63 DictionaryValue* ExtractExtensionPrefs(base::ValueSerializer* serializer, |
24 base::ValueSerializer* serializer) { | 64 const FilePath& path) { |
25 std::string error_msg; | 65 std::string error_msg; |
26 Value* extensions = serializer->Deserialize(NULL, &error_msg); | 66 Value* extensions = serializer->Deserialize(NULL, &error_msg); |
27 if (!extensions) { | 67 if (!extensions) { |
28 LOG(WARNING) << "Unable to deserialize json data: " << error_msg | 68 LOG(WARNING) << "Unable to deserialize json data: " << error_msg |
29 << " In file " << path.value() << " ."; | 69 << " in file " << path.value() << "."; |
30 } else { | 70 return new DictionaryValue; |
31 if (!extensions->IsType(Value::TYPE_DICTIONARY)) { | |
32 LOG(WARNING) << "Expected a JSON dictionary in file " | |
33 << path.value() << " ."; | |
34 } else { | |
35 return static_cast<DictionaryValue*>(extensions); | |
36 } | |
37 } | 71 } |
| 72 |
| 73 DictionaryValue* ext_dictionary = NULL; |
| 74 if (extensions->GetAsDictionary(&ext_dictionary)) |
| 75 return ext_dictionary; |
| 76 |
| 77 LOG(WARNING) << "Expected a JSON dictionary in file " |
| 78 << path.value() << "."; |
38 return new DictionaryValue; | 79 return new DictionaryValue; |
39 } | 80 } |
40 | 81 |
41 } // namespace | 82 } // namespace { |
42 | 83 |
43 ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_key, | 84 ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_id, |
44 Options options) | 85 Options options) |
45 : base_path_key_(base_path_key), | 86 : base_path_id_(base_path_id), |
46 options_(options){ | 87 options_(options) { |
47 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 88 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
48 } | 89 } |
49 | 90 |
50 const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() { | 91 const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() { |
51 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 92 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
52 | 93 |
53 // |base_path_| was set in LoadOnFileThread(). | 94 // |base_path_| was set in LoadOnFileThread(). |
54 return base_path_; | 95 return base_path_; |
55 } | 96 } |
56 | 97 |
57 void ExternalPrefExtensionLoader::StartLoading() { | 98 void ExternalPrefExtensionLoader::StartLoading() { |
58 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 99 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
59 BrowserThread::PostTask( | 100 BrowserThread::PostTask( |
60 BrowserThread::FILE, FROM_HERE, | 101 BrowserThread::FILE, FROM_HERE, |
61 base::Bind(&ExternalPrefExtensionLoader::LoadOnFileThread, this)); | 102 base::Bind(&ExternalPrefExtensionLoader::LoadOnFileThread, this)); |
62 } | 103 } |
63 | 104 |
64 DictionaryValue* ExternalPrefExtensionLoader::ReadJsonPrefsFile() { | 105 void ExternalPrefExtensionLoader::LoadOnFileThread() { |
65 // TODO(skerner): Some values of base_path_key_ will cause | 106 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 107 |
| 108 // TODO(skerner): Some values of base_path_id_ will cause |
66 // PathService::Get() to return false, because the path does | 109 // PathService::Get() to return false, because the path does |
67 // not exist. Find and fix the build/install scripts so that | 110 // not exist. Find and fix the build/install scripts so that |
68 // this can become a CHECK(). Known examples include chrome | 111 // this can become a CHECK(). Known examples include chrome |
69 // OS developer builds and linux install packages. | 112 // OS developer builds and linux install packages. |
70 // Tracked as crbug.com/70402 . | 113 // Tracked as crbug.com/70402 . |
71 if (!PathService::Get(base_path_key_, &base_path_)) { | 114 if (!PathService::Get(base_path_id_, &base_path_)) |
72 return NULL; | 115 return; |
| 116 |
| 117 scoped_ptr<DictionaryValue> prefs(new DictionaryValue); |
| 118 |
| 119 ReadExternalExtensionPrefFile(prefs.get()); |
| 120 if (!prefs.empty()) |
| 121 LOG(WARNING) << "You are using an old-style extension deployment method " |
| 122 "(external_extensions.json), which will soon be " |
| 123 "deprecated. (see http://code.google.com/chrome/" |
| 124 "extensions/external_extensions.html )"; |
| 125 |
| 126 ReadStandaloneExtensionPrefFiles(prefs.get()); |
| 127 |
| 128 prefs_.reset(prefs.release()); |
| 129 if (!prefs_.get()) |
| 130 prefs_.reset(new DictionaryValue()); |
| 131 |
| 132 if (base_path_id_ == chrome::DIR_EXTERNAL_EXTENSIONS) { |
| 133 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount", |
| 134 prefs_->size()); |
73 } | 135 } |
74 | 136 |
| 137 // If we have any records to process, then we must have |
| 138 // read at least one .json file. If so, then we should have |
| 139 // set |base_path_|. |
| 140 if (!prefs_->empty()) |
| 141 CHECK(!base_path_.empty()); |
| 142 |
| 143 BrowserThread::PostTask( |
| 144 BrowserThread::UI, FROM_HERE, |
| 145 base::Bind(&ExternalPrefExtensionLoader::LoadFinished, this)); |
| 146 } |
| 147 |
| 148 void ExternalPrefExtensionLoader::ReadExternalExtensionPrefFile( |
| 149 DictionaryValue* prefs) { |
| 150 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 151 CHECK(NULL != prefs); |
| 152 |
75 FilePath json_file = base_path_.Append( | 153 FilePath json_file = base_path_.Append( |
76 FILE_PATH_LITERAL("external_extensions.json")); | 154 FILE_PATH_LITERAL("external_extensions.json")); |
77 | 155 |
78 if (!file_util::PathExists(json_file)) { | 156 if (!file_util::PathExists(json_file)) { |
79 // This is not an error. The file does not exist by default. | 157 // This is not an error. The file does not exist by default. |
80 return NULL; | 158 return; |
81 } | 159 } |
82 | 160 |
83 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) { | 161 if (IsOptionSet(ENSURE_PATH_CONTROLLED_BY_ADMIN)) { |
84 #if defined(OS_MACOSX) | 162 #if defined(OS_MACOSX) |
85 if (!file_util::VerifyPathControlledByAdmin(json_file)) { | 163 if (!file_util::VerifyPathControlledByAdmin(json_file)) { |
86 LOG(ERROR) << "Can not read external extensions source. The file " | 164 LOG(ERROR) << "Can not read external extensions source. The file " |
87 << json_file.value() << " and every directory in its path, " | 165 << json_file.value() << " and every directory in its path, " |
88 << "must be owned by root, have group \"admin\", and not be " | 166 << "must be owned by root, have group \"admin\", and not be " |
89 << "writable by all users. These restrictions prevent " | 167 << "writable by all users. These restrictions prevent " |
90 << "unprivleged users from making chrome install extensions " | 168 << "unprivleged users from making chrome install extensions " |
91 << "on other users' accounts."; | 169 << "on other users' accounts."; |
92 return NULL; | 170 return NULL; |
93 } | 171 } |
94 #else | 172 #else |
95 // The only platform that uses this check is Mac OS. If you add one, | 173 // The only platform that uses this check is Mac OS. If you add one, |
96 // you need to implement file_util::VerifyPathControlledByAdmin() for | 174 // you need to implement file_util::VerifyPathControlledByAdmin() for |
97 // that platform. | 175 // that platform. |
98 NOTREACHED(); | 176 NOTREACHED(); |
99 #endif // defined(OS_MACOSX) | 177 #endif // defined(OS_MACOSX) |
100 } | 178 } |
101 | 179 |
102 JSONFileValueSerializer serializer(json_file); | 180 JSONFileValueSerializer serializer(json_file); |
103 DictionaryValue* parsed_json_prefs = ExtractPrefs(json_file, &serializer); | 181 DictionaryValue * ext_prefs = ExtractExtensionPrefs(&serializer, json_file); |
104 return parsed_json_prefs; | 182 if (ext_prefs) |
| 183 prefs->MergeDictionary(ext_prefs); |
105 } | 184 } |
106 | 185 |
107 void ExternalPrefExtensionLoader::LoadOnFileThread() { | 186 void ExternalPrefExtensionLoader::ReadStandaloneExtensionPrefFiles( |
| 187 DictionaryValue* prefs) { |
108 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 188 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 189 CHECK(NULL != prefs); |
109 | 190 |
110 prefs_.reset(ReadJsonPrefsFile()); | 191 // First list the potential .json candidates. |
111 if (!prefs_.get()) | 192 std::set<FilePath> |
112 prefs_.reset(new DictionaryValue()); | 193 candidates = GetPrefsCandidateFilesFromFolder(base_path_); |
113 | 194 if (candidates.empty()) { |
114 if (base_path_key_ == chrome::DIR_EXTERNAL_EXTENSIONS) { | 195 DVLOG(1) << "Extension candidates list empty"; |
115 UMA_HISTOGRAM_COUNTS_100("Extensions.ExternalJsonCount", | 196 return; |
116 prefs_->size()); | |
117 } | 197 } |
118 | 198 |
119 // If we have any records to process, then we must have | 199 // For each file read the json description & build the proper |
120 // read the .json file. If we read the .json file, then | 200 // associated prefs. |
121 // we were should have set |base_path_|. | 201 for (std::set<FilePath>::const_iterator it = candidates.begin(); |
122 if (!prefs_->empty()) | 202 it != candidates.end(); |
123 CHECK(!base_path_.empty()); | 203 ++it) { |
| 204 FilePath extension_candidate_path = base_path_.Append(*it); |
124 | 205 |
125 BrowserThread::PostTask( | 206 FilePath::StringType id = |
126 BrowserThread::UI, FROM_HERE, | 207 extension_candidate_path.RemoveExtension().BaseName().value(); |
127 base::Bind(&ExternalPrefExtensionLoader::LoadFinished, this)); | 208 |
| 209 DVLOG(1) << "Reading json file: " << extension_candidate_path.value(); |
| 210 |
| 211 JSONFileValueSerializer serializer(extension_candidate_path); |
| 212 DictionaryValue* ext_prefs = |
| 213 ExtractExtensionPrefs(&serializer, extension_candidate_path); |
| 214 |
| 215 if (ext_prefs) { |
| 216 DVLOG(1) << "Adding extension with id: " << id; |
| 217 prefs->Set(id, ext_prefs); |
| 218 } |
| 219 } |
128 } | 220 } |
129 | 221 |
130 ExternalTestingExtensionLoader::ExternalTestingExtensionLoader( | 222 ExternalTestingExtensionLoader::ExternalTestingExtensionLoader( |
131 const std::string& json_data, | 223 const std::string& json_data, |
132 const FilePath& fake_base_path) | 224 const FilePath& fake_base_path) |
133 : fake_base_path_(fake_base_path) { | 225 : fake_base_path_(fake_base_path) { |
134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 226 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
135 JSONStringValueSerializer serializer(json_data); | 227 JSONStringValueSerializer serializer(json_data); |
136 FilePath fake_json_path = fake_base_path.AppendASCII("fake.json"); | 228 FilePath fake_json_path = fake_base_path.AppendASCII("fake.json"); |
137 testing_prefs_.reset(ExtractPrefs(fake_json_path, &serializer)); | 229 testing_prefs_.reset(ExtractExtensionPrefs(&serializer, fake_json_path)); |
138 } | 230 } |
139 | 231 |
140 void ExternalTestingExtensionLoader::StartLoading() { | 232 void ExternalTestingExtensionLoader::StartLoading() { |
141 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 233 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
142 prefs_.reset(testing_prefs_->DeepCopy()); | 234 prefs_.reset(testing_prefs_->DeepCopy()); |
143 LoadFinished(); | 235 LoadFinished(); |
144 } | 236 } |
145 | 237 |
146 ExternalTestingExtensionLoader::~ExternalTestingExtensionLoader() {} | 238 ExternalTestingExtensionLoader::~ExternalTestingExtensionLoader() {} |
147 | 239 |
148 const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() { | 240 const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() { |
149 return fake_base_path_; | 241 return fake_base_path_; |
150 } | 242 } |
OLD | NEW |