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