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

Side by Side Diff: chrome/common/extensions/extension_unpacker.cc

Issue 10704060: Move ExtensionUnpacker into extensions:: namespace, rename it to Unpacker (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Even later master Created 8 years, 5 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 (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.
4
5 #include "chrome/common/extensions/extension_unpacker.h"
6
7 #include <set>
8
9 #include "base/file_util.h"
10 #include "base/json/json_file_value_serializer.h"
11 #include "base/memory/scoped_handle.h"
12 #include "base/scoped_temp_dir.h"
13 #include "base/string_util.h"
14 #include "base/threading/thread.h"
15 #include "base/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_manifest_constants.h"
19 #include "chrome/common/extensions/extension_file_util.h"
20 #include "chrome/common/extensions/extension_l10n_util.h"
21 #include "chrome/common/url_constants.h"
22 #include "chrome/common/zip.h"
23 #include "content/public/common/common_param_traits.h"
24 #include "ipc/ipc_message_utils.h"
25 #include "net/base/file_stream.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "webkit/glue/image_decoder.h"
28
29 namespace errors = extension_manifest_errors;
30 namespace keys = extension_manifest_keys;
31 namespace filenames = extension_filenames;
32
33 using extensions::Extension;
34
35 namespace {
36
37 // Errors
38 const char* kCouldNotCreateDirectoryError =
39 "Could not create directory for unzipping: ";
40 const char* kCouldNotDecodeImageError = "Could not decode theme image.";
41 const char* kCouldNotUnzipExtension = "Could not unzip extension.";
42 const char* kPathNamesMustBeAbsoluteOrLocalError =
43 "Path names must not be absolute or contain '..'.";
44
45 // A limit to stop us passing dangerously large canvases to the browser.
46 const int kMaxImageCanvas = 4096 * 4096;
47
48 SkBitmap DecodeImage(const FilePath& path) {
49 // Read the file from disk.
50 std::string file_contents;
51 if (!file_util::PathExists(path) ||
52 !file_util::ReadFileToString(path, &file_contents)) {
53 return SkBitmap();
54 }
55
56 // Decode the image using WebKit's image decoder.
57 const unsigned char* data =
58 reinterpret_cast<const unsigned char*>(file_contents.data());
59 webkit_glue::ImageDecoder decoder;
60 SkBitmap bitmap = decoder.Decode(data, file_contents.length());
61 Sk64 bitmap_size = bitmap.getSize64();
62 if (!bitmap_size.is32() || bitmap_size.get32() > kMaxImageCanvas)
63 return SkBitmap();
64 return bitmap;
65 }
66
67 bool PathContainsParentDirectory(const FilePath& path) {
68 const FilePath::StringType kSeparators(FilePath::kSeparators);
69 const FilePath::StringType kParentDirectory(FilePath::kParentDirectory);
70 const size_t npos = FilePath::StringType::npos;
71 const FilePath::StringType& value = path.value();
72
73 for (size_t i = 0; i < value.length(); ) {
74 i = value.find(kParentDirectory, i);
75 if (i != npos) {
76 if ((i == 0 || kSeparators.find(value[i-1]) == npos) &&
77 (i+1 < value.length() || kSeparators.find(value[i+1]) == npos)) {
78 return true;
79 }
80 ++i;
81 }
82 }
83
84 return false;
85 }
86
87 } // namespace
88
89 ExtensionUnpacker::ExtensionUnpacker(const FilePath& extension_path,
90 const std::string& extension_id,
91 Extension::Location location,
92 int creation_flags)
93 : extension_path_(extension_path),
94 extension_id_(extension_id),
95 location_(location),
96 creation_flags_(creation_flags) {
97 }
98
99 ExtensionUnpacker::~ExtensionUnpacker() {
100 }
101
102 DictionaryValue* ExtensionUnpacker::ReadManifest() {
103 FilePath manifest_path =
104 temp_install_dir_.Append(Extension::kManifestFilename);
105 if (!file_util::PathExists(manifest_path)) {
106 SetError(errors::kInvalidManifest);
107 return NULL;
108 }
109
110 JSONFileValueSerializer serializer(manifest_path);
111 std::string error;
112 scoped_ptr<Value> root(serializer.Deserialize(NULL, &error));
113 if (!root.get()) {
114 SetError(error);
115 return NULL;
116 }
117
118 if (!root->IsType(Value::TYPE_DICTIONARY)) {
119 SetError(errors::kInvalidManifest);
120 return NULL;
121 }
122
123 return static_cast<DictionaryValue*>(root.release());
124 }
125
126 bool ExtensionUnpacker::ReadAllMessageCatalogs(
127 const std::string& default_locale) {
128 FilePath locales_path =
129 temp_install_dir_.Append(Extension::kLocaleFolder);
130
131 // Not all folders under _locales have to be valid locales.
132 file_util::FileEnumerator locales(locales_path,
133 false,
134 file_util::FileEnumerator::DIRECTORIES);
135
136 std::set<std::string> all_locales;
137 extension_l10n_util::GetAllLocales(&all_locales);
138 FilePath locale_path;
139 while (!(locale_path = locales.Next()).empty()) {
140 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
141 all_locales))
142 continue;
143
144 FilePath messages_path =
145 locale_path.Append(Extension::kMessagesFilename);
146
147 if (!ReadMessageCatalog(messages_path))
148 return false;
149 }
150
151 return true;
152 }
153
154 bool ExtensionUnpacker::Run() {
155 DVLOG(1) << "Installing extension " << extension_path_.value();
156
157 // <profile>/Extensions/INSTALL_TEMP/<version>
158 temp_install_dir_ =
159 extension_path_.DirName().AppendASCII(filenames::kTempExtensionName);
160
161 if (!file_util::CreateDirectory(temp_install_dir_)) {
162 #if defined(OS_WIN)
163 std::string dir_string = WideToUTF8(temp_install_dir_.value());
164 #else
165 std::string dir_string = temp_install_dir_.value();
166 #endif
167
168 SetError(kCouldNotCreateDirectoryError + dir_string);
169 return false;
170 }
171
172 if (!zip::Unzip(extension_path_, temp_install_dir_)) {
173 SetError(kCouldNotUnzipExtension);
174 return false;
175 }
176
177 // Parse the manifest.
178 parsed_manifest_.reset(ReadManifest());
179 if (!parsed_manifest_.get())
180 return false; // Error was already reported.
181
182 std::string error;
183 scoped_refptr<Extension> extension(Extension::Create(
184 temp_install_dir_,
185 location_,
186 *parsed_manifest_,
187 creation_flags_,
188 extension_id_,
189 &error));
190 if (!extension.get()) {
191 SetError(error);
192 return false;
193 }
194
195 Extension::InstallWarningVector warnings;
196 if (!extension_file_util::ValidateExtension(extension.get(),
197 &error, &warnings)) {
198 SetError(error);
199 return false;
200 }
201 extension->AddInstallWarnings(warnings);
202
203 // Decode any images that the browser needs to display.
204 std::set<FilePath> image_paths = extension->GetBrowserImages();
205 for (std::set<FilePath>::iterator it = image_paths.begin();
206 it != image_paths.end(); ++it) {
207 if (!AddDecodedImage(*it))
208 return false; // Error was already reported.
209 }
210
211 // Parse all message catalogs (if any).
212 parsed_catalogs_.reset(new DictionaryValue);
213 if (!extension->default_locale().empty()) {
214 if (!ReadAllMessageCatalogs(extension->default_locale()))
215 return false; // Error was already reported.
216 }
217
218 return true;
219 }
220
221 bool ExtensionUnpacker::DumpImagesToFile() {
222 IPC::Message pickle; // We use a Message so we can use WriteParam.
223 IPC::WriteParam(&pickle, decoded_images_);
224
225 FilePath path = extension_path_.DirName().AppendASCII(
226 filenames::kDecodedImagesFilename);
227 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
228 pickle.size())) {
229 SetError("Could not write image data to disk.");
230 return false;
231 }
232
233 return true;
234 }
235
236 bool ExtensionUnpacker::DumpMessageCatalogsToFile() {
237 IPC::Message pickle;
238 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
239
240 FilePath path = extension_path_.DirName().AppendASCII(
241 filenames::kDecodedMessageCatalogsFilename);
242 if (!file_util::WriteFile(path, static_cast<const char*>(pickle.data()),
243 pickle.size())) {
244 SetError("Could not write message catalogs to disk.");
245 return false;
246 }
247
248 return true;
249 }
250
251 // static
252 bool ExtensionUnpacker::ReadImagesFromFile(const FilePath& extension_path,
253 DecodedImages* images) {
254 FilePath path = extension_path.AppendASCII(filenames::kDecodedImagesFilename);
255 std::string file_str;
256 if (!file_util::ReadFileToString(path, &file_str))
257 return false;
258
259 IPC::Message pickle(file_str.data(), file_str.size());
260 PickleIterator iter(pickle);
261 return IPC::ReadParam(&pickle, &iter, images);
262 }
263
264 // static
265 bool ExtensionUnpacker::ReadMessageCatalogsFromFile(
266 const FilePath& extension_path, DictionaryValue* catalogs) {
267 FilePath path = extension_path.AppendASCII(
268 filenames::kDecodedMessageCatalogsFilename);
269 std::string file_str;
270 if (!file_util::ReadFileToString(path, &file_str))
271 return false;
272
273 IPC::Message pickle(file_str.data(), file_str.size());
274 PickleIterator iter(pickle);
275 return IPC::ReadParam(&pickle, &iter, catalogs);
276 }
277
278 bool ExtensionUnpacker::AddDecodedImage(const FilePath& path) {
279 // Make sure it's not referencing a file outside the extension's subdir.
280 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
281 SetError(kPathNamesMustBeAbsoluteOrLocalError);
282 return false;
283 }
284
285 SkBitmap image_bitmap = DecodeImage(temp_install_dir_.Append(path));
286 if (image_bitmap.isNull()) {
287 SetError(kCouldNotDecodeImageError);
288 return false;
289 }
290
291 decoded_images_.push_back(MakeTuple(image_bitmap, path));
292 return true;
293 }
294
295 bool ExtensionUnpacker::ReadMessageCatalog(const FilePath& message_path) {
296 std::string error;
297 JSONFileValueSerializer serializer(message_path);
298 scoped_ptr<DictionaryValue> root(
299 static_cast<DictionaryValue*>(serializer.Deserialize(NULL, &error)));
300 if (!root.get()) {
301 string16 messages_file = message_path.LossyDisplayName();
302 if (error.empty()) {
303 // If file is missing, Deserialize will fail with empty error.
304 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
305 UTF16ToUTF8(messages_file).c_str()));
306 } else {
307 SetError(base::StringPrintf("%s: %s",
308 UTF16ToUTF8(messages_file).c_str(),
309 error.c_str()));
310 }
311 return false;
312 }
313
314 FilePath relative_path;
315 // message_path was created from temp_install_dir. This should never fail.
316 if (!temp_install_dir_.AppendRelativePath(message_path, &relative_path)) {
317 NOTREACHED();
318 return false;
319 }
320
321 std::string dir_name = relative_path.DirName().MaybeAsASCII();
322 if (dir_name.empty()) {
323 NOTREACHED();
324 return false;
325 }
326 parsed_catalogs_->Set(dir_name, root.release());
327
328 return true;
329 }
330
331 void ExtensionUnpacker::SetError(const std::string &error) {
332 error_message_ = UTF8ToUTF16(error);
333 }
OLDNEW
« no previous file with comments | « chrome/common/extensions/extension_unpacker.h ('k') | chrome/common/extensions/extension_unpacker_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698