OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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/extensions/convert_web_app.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/base64.h" | |
11 #include "base/file_path.h" | |
12 #include "base/file_util.h" | |
13 #include "base/path_service.h" | |
14 #include "base/scoped_temp_dir.h" | |
15 #include "base/sha2.h" | |
16 #include "base/stringprintf.h" | |
17 #include "base/time.h" | |
18 #include "base/utf_string_conversions.h" | |
19 #include "chrome/common/chrome_paths.h" | |
20 #include "chrome/common/extensions/extension.h" | |
21 #include "chrome/common/extensions/extension_constants.h" | |
22 #include "chrome/common/json_value_serializer.h" | |
23 #include "gfx/codec/png_codec.h" | |
24 #include "googleurl/src/gurl.h" | |
25 #include "third_party/skia/include/core/SkBitmap.h" | |
26 #include "webkit/glue/dom_operations.h" | |
27 | |
28 namespace keys = extension_manifest_keys; | |
29 | |
30 namespace { | |
31 | |
32 const char kIconsDirName[] = "_icons"; | |
33 | |
34 // Create the public key for the converted web app. | |
35 // | |
36 // Web apps are not signed, but the public key for an extension doubles as | |
37 // its unique identity, and we need one of those. A web app's unique identity | |
38 // is its manifest URL, so we hash that to create a public key. There will be | |
39 // no corresponding private key, which means that these extensions cannot be | |
40 // auto-updated using ExtensionUpdater. But Chrome does notice updates to the | |
41 // manifest and regenerates these extensions. | |
42 std::string GenerateKey(const GURL& manifest_url) { | |
43 char raw[base::SHA256_LENGTH] = {0}; | |
44 std::string key; | |
45 base::SHA256HashString(manifest_url.spec().c_str(), | |
46 raw, | |
47 base::SHA256_LENGTH); | |
48 base::Base64Encode(std::string(raw, base::SHA256_LENGTH), &key); | |
49 return key; | |
50 } | |
51 | |
52 } | |
53 | |
54 | |
55 // Generates a version for the converted app using the current date. This isn't | |
56 // really needed, but it seems like useful information. | |
57 std::string ConvertTimeToExtensionVersion(const base::Time& create_time) { | |
58 base::Time::Exploded create_time_exploded; | |
59 create_time.LocalExplode(&create_time_exploded); | |
60 | |
61 uint32 timestamp = static_cast<uint>(create_time.ToDoubleT()); | |
62 uint16 timestamp_lower = static_cast<uint16>(timestamp); | |
63 uint16 timestamp_upper = static_cast<uint16>(timestamp >> 16); | |
64 | |
65 return base::StringPrintf("%i.%i%.2i.%i.%i", | |
66 create_time_exploded.year, | |
67 create_time_exploded.month, | |
68 create_time_exploded.day_of_month, | |
69 timestamp_upper, | |
70 timestamp_lower); | |
71 } | |
72 | |
73 scoped_refptr<Extension> ConvertWebAppToExtension( | |
74 const webkit_glue::WebApplicationInfo& web_app, | |
75 const base::Time& create_time) { | |
76 FilePath user_data_temp_dir; | |
77 CHECK(PathService::Get(chrome::DIR_USER_DATA_TEMP, &user_data_temp_dir)); | |
78 | |
79 ScopedTempDir temp_dir; | |
80 if (!temp_dir.CreateUniqueTempDirUnderPath(user_data_temp_dir)) { | |
81 LOG(ERROR) << "Could not create temporary directory."; | |
82 return NULL; | |
83 } | |
84 | |
85 // Create the manifest | |
86 scoped_ptr<DictionaryValue> root(new DictionaryValue); | |
87 root->SetString(keys::kPublicKey, GenerateKey(web_app.manifest_url)); | |
88 root->SetString(keys::kName, UTF16ToUTF8(web_app.title)); | |
89 root->SetString(keys::kVersion, ConvertTimeToExtensionVersion(create_time)); | |
90 root->SetString(keys::kDescription, UTF16ToUTF8(web_app.description)); | |
91 root->SetString(keys::kLaunchWebURL, web_app.app_url.spec()); | |
92 | |
93 // Add the icons. | |
94 DictionaryValue* icons = new DictionaryValue(); | |
95 root->Set(keys::kIcons, icons); | |
96 for (size_t i = 0; i < web_app.icons.size(); ++i) { | |
97 std::string size = StringPrintf("%i", web_app.icons[i].width); | |
98 std::string icon_path = StringPrintf("%s/%s.png", kIconsDirName, | |
99 size.c_str()); | |
100 icons->SetString(size, icon_path); | |
101 } | |
102 | |
103 // Add the permissions. | |
104 ListValue* permissions = new ListValue(); | |
105 root->Set(keys::kPermissions, permissions); | |
106 for (size_t i = 0; i < web_app.permissions.size(); ++i) { | |
107 permissions->Append(Value::CreateStringValue(web_app.permissions[i])); | |
108 } | |
109 | |
110 // Add the URLs. | |
111 ListValue* urls = new ListValue(); | |
112 root->Set(keys::kWebURLs, urls); | |
113 for (size_t i = 0; i < web_app.urls.size(); ++i) { | |
114 urls->Append(Value::CreateStringValue(web_app.urls[i].spec())); | |
115 } | |
116 | |
117 // Write the manifest. | |
118 FilePath manifest_path = temp_dir.path().Append( | |
119 Extension::kManifestFilename); | |
120 JSONFileValueSerializer serializer(manifest_path); | |
121 if (!serializer.Serialize(*root)) { | |
122 LOG(ERROR) << "Could not serialize manifest."; | |
123 return NULL; | |
124 } | |
125 | |
126 // Write the icon files. | |
127 FilePath icons_dir = temp_dir.path().AppendASCII(kIconsDirName); | |
128 if (!file_util::CreateDirectory(icons_dir)) { | |
129 LOG(ERROR) << "Could not create icons directory."; | |
130 return NULL; | |
131 } | |
132 for (size_t i = 0; i < web_app.icons.size(); ++i) { | |
133 FilePath icon_file = icons_dir.AppendASCII( | |
134 StringPrintf("%i.png", web_app.icons[i].width)); | |
135 std::vector<unsigned char> image_data; | |
136 if (!gfx::PNGCodec::EncodeBGRASkBitmap(web_app.icons[i].data, | |
137 false, | |
138 &image_data)) { | |
139 LOG(ERROR) << "Could not create icon file."; | |
140 return NULL; | |
141 } | |
142 | |
143 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]); | |
144 if (!file_util::WriteFile(icon_file, image_data_ptr, image_data.size())) { | |
145 LOG(ERROR) << "Could not write icon file."; | |
146 return NULL; | |
147 } | |
148 } | |
149 | |
150 // Finally, create the extension object to represent the unpacked directory. | |
151 std::string error; | |
152 scoped_refptr<Extension> extension = Extension::Create( | |
153 temp_dir.path(), Extension::INTERNAL, *root, false, &error); | |
154 if (!extension) { | |
155 LOG(ERROR) << error; | |
156 return NULL; | |
157 } | |
158 | |
159 temp_dir.Take(); // The caller takes ownership of the directory. | |
160 return extension; | |
161 } | |
OLD | NEW |