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

Side by Side Diff: chrome/browser/android/webapk/webapk_installer.cc

Issue 2231843003: Take Murmur2 hash of untransformed icon when creating WebAPK part 1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Merge branch 'master' into webapk_builder_impl2_hash Created 4 years, 4 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/android/webapk/webapk_installer.h" 5 #include "chrome/browser/android/webapk/webapk_installer.h"
6 6
7 #include "base/android/build_info.h" 7 #include "base/android/build_info.h"
8 #include "base/android/jni_android.h" 8 #include "base/android/jni_android.h"
9 #include "base/android/jni_string.h" 9 #include "base/android/jni_string.h"
10 #include "base/android/path_utils.h" 10 #include "base/android/path_utils.h"
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/files/file_path.h" 13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h" 14 #include "base/files/file_util.h"
15 #include "base/memory/ref_counted.h" 15 #include "base/memory/ref_counted.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h" 18 #include "base/strings/utf_string_conversions.h"
20 #include "base/task_runner_util.h" 19 #include "base/task_runner_util.h"
21 #include "base/threading/sequenced_worker_pool.h" 20 #include "base/threading/sequenced_worker_pool.h"
22 #include "chrome/browser/android/shortcut_helper.h" 21 #include "chrome/browser/android/shortcut_helper.h"
23 #include "chrome/browser/android/webapk/webapk.pb.h" 22 #include "chrome/browser/android/webapk/webapk.pb.h"
23 #include "chrome/browser/android/webapk/webapk_icon_hasher.h"
24 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/chrome_switches.h"
26 #include "components/version_info/version_info.h" 26 #include "components/version_info/version_info.h"
27 #include "content/public/browser/browser_thread.h" 27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/common/manifest_util.h" 28 #include "content/public/common/manifest_util.h"
29 #include "jni/WebApkInstaller_jni.h" 29 #include "jni/WebApkInstaller_jni.h"
30 #include "net/http/http_status_code.h" 30 #include "net/http/http_status_code.h"
31 #include "net/url_request/url_fetcher.h" 31 #include "net/url_request/url_fetcher.h"
32 #include "third_party/smhasher/src/MurmurHash2.h"
33 #include "ui/gfx/codec/png_codec.h" 32 #include "ui/gfx/codec/png_codec.h"
34 #include "url/gurl.h" 33 #include "url/gurl.h"
35 34
36 namespace { 35 namespace {
37 36
38 // The default WebAPK server URL. 37 // The default WebAPK server URL.
39 const char kDefaultWebApkServerUrl[] = 38 const char kDefaultWebApkServerUrl[] =
40 "https://webapk.googleapis.com/v1alpha/webApks?alt=proto"; 39 "https://webapk.googleapis.com/v1alpha/webApks?alt=proto";
41 40
42 // The MIME type of the POST data sent to the server. 41 // The MIME type of the POST data sent to the server.
43 const char kProtoMimeType[] = "application/x-protobuf"; 42 const char kProtoMimeType[] = "application/x-protobuf";
44 43
45 // The seed to use the murmur2 hash of the app icon.
46 const uint32_t kMurmur2HashSeed = 0;
47
48 // The default number of milliseconds to wait for the WebAPK download URL from 44 // The default number of milliseconds to wait for the WebAPK download URL from
49 // the WebAPK server. 45 // the WebAPK server.
50 const int kWebApkDownloadUrlTimeoutMs = 60000; 46 const int kWebApkDownloadUrlTimeoutMs = 60000;
51 47
52 // The default number of milliseconds to wait for the WebAPK download to 48 // The default number of milliseconds to wait for the WebAPK download to
53 // complete. 49 // complete.
54 const int kDownloadTimeoutMs = 60000; 50 const int kDownloadTimeoutMs = 60000;
55 51
56 // Returns the scope from |info| if it is specified. Otherwise, returns the 52 // Returns the scope from |info| if it is specified. Otherwise, returns the
57 // default scope. 53 // default scope.
58 GURL GetScope(const ShortcutInfo& info) { 54 GURL GetScope(const ShortcutInfo& info) {
59 return (info.scope.is_valid()) 55 return (info.scope.is_valid())
60 ? info.scope 56 ? info.scope
61 : ShortcutHelper::GetScopeFromURL(info.url); 57 : ShortcutHelper::GetScopeFromURL(info.url);
62 } 58 }
63 59
64 // Computes a murmur2 hash of |bitmap|'s PNG encoded bytes.
65 std::string ComputeBitmapHash(const SkBitmap& bitmap) {
66 std::vector<unsigned char> png_bytes;
67 gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_bytes);
68 uint64_t hash =
69 MurmurHash64B(&png_bytes.front(), png_bytes.size(), kMurmur2HashSeed);
70 return base::Uint64ToString(hash);
71 }
72
73 // Converts a color from the format specified in content::Manifest to a CSS 60 // Converts a color from the format specified in content::Manifest to a CSS
74 // string. 61 // string.
75 std::string ColorToString(int64_t color) { 62 std::string ColorToString(int64_t color) {
76 if (color == content::Manifest::kInvalidOrMissingColor) 63 if (color == content::Manifest::kInvalidOrMissingColor)
77 return ""; 64 return "";
78 65
79 SkColor sk_color = reinterpret_cast<uint32_t&>(color); 66 SkColor sk_color = reinterpret_cast<uint32_t&>(color);
80 int r = SkColorGetR(sk_color); 67 int r = SkColorGetR(sk_color);
81 int g = SkColorGetG(sk_color); 68 int g = SkColorGetG(sk_color);
82 int b = SkColorGetB(sk_color); 69 int b = SkColorGetB(sk_color);
83 double a = SkColorGetA(sk_color) / 255.0; 70 double a = SkColorGetA(sk_color) / 255.0;
84 return base::StringPrintf("rgba(%d,%d,%d,%.2f)", r, g, b, a); 71 return base::StringPrintf("rgba(%d,%d,%d,%.2f)", r, g, b, a);
85 } 72 }
86 73
74 // Populates webapk::WebApk and returns it.
75 // Must be called on a worker thread because it encodes an SkBitmap.
76 std::unique_ptr<webapk::WebApk> BuildWebApkProtoInBackground(
77 const ShortcutInfo& shortcut_info,
78 const SkBitmap& shortcut_icon,
79 const std::string& shortcut_icon_murmur2_hash) {
80 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
81
82 std::unique_ptr<webapk::WebApk> webapk(new webapk::WebApk);
83 webapk->set_manifest_url(shortcut_info.manifest_url.spec());
84 webapk->set_requester_application_package(
85 base::android::BuildInfo::GetInstance()->package_name());
86 webapk->set_requester_application_version(version_info::GetVersionNumber());
87
88 webapk::WebAppManifest* web_app_manifest = webapk->mutable_manifest();
89 web_app_manifest->set_name(base::UTF16ToUTF8(shortcut_info.name));
90 web_app_manifest->set_short_name(
91 base::UTF16ToUTF8(shortcut_info.short_name));
92 web_app_manifest->set_start_url(shortcut_info.url.spec());
93 web_app_manifest->set_orientation(
94 content::WebScreenOrientationLockTypeToString(
95 shortcut_info.orientation));
96 web_app_manifest->set_display_mode(
97 content::WebDisplayModeToString(shortcut_info.display));
98 web_app_manifest->set_background_color(
99 ColorToString(shortcut_info.background_color));
100 web_app_manifest->set_theme_color(ColorToString(shortcut_info.theme_color));
101
102 std::string* scope = web_app_manifest->add_scopes();
103 scope->assign(GetScope(shortcut_info).spec());
104 webapk::Image* image = web_app_manifest->add_icons();
105 image->set_src(shortcut_info.icon_url.spec());
106 image->set_hash(shortcut_icon_murmur2_hash);
107 std::vector<unsigned char> png_bytes;
108 gfx::PNGCodec::EncodeBGRASkBitmap(shortcut_icon, false, &png_bytes);
109 image->set_image_data(&png_bytes.front(), png_bytes.size());
110
111 return webapk;
112 }
113
87 // Returns task runner for running background tasks. 114 // Returns task runner for running background tasks.
88 scoped_refptr<base::TaskRunner> GetBackgroundTaskRunner() { 115 scoped_refptr<base::TaskRunner> GetBackgroundTaskRunner() {
89 return content::BrowserThread::GetBlockingPool() 116 return content::BrowserThread::GetBlockingPool()
90 ->GetTaskRunnerWithShutdownBehavior( 117 ->GetTaskRunnerWithShutdownBehavior(
91 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); 118 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
92 } 119 }
93 120
94 } // anonymous namespace 121 } // anonymous namespace
95 122
96 WebApkInstaller::WebApkInstaller(const ShortcutInfo& shortcut_info, 123 WebApkInstaller::WebApkInstaller(const ShortcutInfo& shortcut_info,
(...skipping 18 matching lines...) Expand all
115 Profile::FromBrowserContext(browser_context)->GetRequestContext(), 142 Profile::FromBrowserContext(browser_context)->GetRequestContext(),
116 finish_callback); 143 finish_callback);
117 } 144 }
118 145
119 void WebApkInstaller::InstallAsyncWithURLRequestContextGetter( 146 void WebApkInstaller::InstallAsyncWithURLRequestContextGetter(
120 net::URLRequestContextGetter* request_context_getter, 147 net::URLRequestContextGetter* request_context_getter,
121 const FinishCallback& finish_callback) { 148 const FinishCallback& finish_callback) {
122 request_context_getter_ = request_context_getter; 149 request_context_getter_ = request_context_getter;
123 finish_callback_ = finish_callback; 150 finish_callback_ = finish_callback;
124 151
125 // base::Unretained() is safe because WebApkInstaller owns itself and does not 152 if (!shortcut_info_.icon_url.is_valid()) {
126 // start the timeout timer till after SendCreateWebApkRequest() is called. 153 OnFailure();
127 base::PostTaskAndReplyWithResult( 154 return;
128 GetBackgroundTaskRunner().get(), FROM_HERE, 155 }
129 base::Bind(&WebApkInstaller::BuildWebApkProtoInBackground, 156
130 base::Unretained(this)), 157 // We need to take the hash of the bitmap at the icon URL prior to any
131 base::Bind(&WebApkInstaller::SendCreateWebApkRequest, 158 // transformations being applied to the bitmap (such as encoding/decoding
132 base::Unretained(this))); 159 // the bitmap). The icon hash is used to determine whether the icon that
160 // the user sees matches the icon of a WebAPK that the WebAPK server
161 // generated for another user. (The icon can be dynamically generated.)
162 //
163 // We redownload the icon in order to take the Murmur2 hash. The redownload
164 // should be fast because the icon should be in the HTTP cache.
165 DownloadAppIconAndComputeMurmur2Hash();
133 } 166 }
134 167
135 void WebApkInstaller::SetTimeoutMs(int timeout_ms) { 168 void WebApkInstaller::SetTimeoutMs(int timeout_ms) {
136 webapk_download_url_timeout_ms_ = timeout_ms; 169 webapk_download_url_timeout_ms_ = timeout_ms;
137 download_timeout_ms_ = timeout_ms; 170 download_timeout_ms_ = timeout_ms;
138 } 171 }
139 172
140 bool WebApkInstaller::StartDownloadedWebApkInstall( 173 bool WebApkInstaller::StartDownloadedWebApkInstall(
141 JNIEnv* env, 174 JNIEnv* env,
142 const base::android::ScopedJavaLocalRef<jstring>& java_file_path, 175 const base::android::ScopedJavaLocalRef<jstring>& java_file_path,
(...skipping 22 matching lines...) Expand all
165 } 198 }
166 199
167 GURL signed_download_url(response->signed_download_url()); 200 GURL signed_download_url(response->signed_download_url());
168 if (!signed_download_url.is_valid() || response->package_name().empty()) { 201 if (!signed_download_url.is_valid() || response->package_name().empty()) {
169 OnFailure(); 202 OnFailure();
170 return; 203 return;
171 } 204 }
172 OnGotWebApkDownloadUrl(signed_download_url, response->package_name()); 205 OnGotWebApkDownloadUrl(signed_download_url, response->package_name());
173 } 206 }
174 207
208 void WebApkInstaller::DownloadAppIconAndComputeMurmur2Hash() {
209 timer_.Start(
210 FROM_HERE, base::TimeDelta::FromMilliseconds(download_timeout_ms_),
211 base::Bind(&WebApkInstaller::OnTimeout, weak_ptr_factory_.GetWeakPtr()));
212
213 icon_hasher_.reset(new WebApkIconHasher());
214 icon_hasher_->DownloadAndComputeMurmur2Hash(
215 request_context_getter_, shortcut_info_.icon_url,
216 base::Bind(&WebApkInstaller::OnGotIconMurmur2Hash,
217 weak_ptr_factory_.GetWeakPtr()));
218 }
219
220 void WebApkInstaller::OnGotIconMurmur2Hash(
221 const std::string& icon_murmur2_hash) {
222 timer_.Stop();
223 icon_hasher_.reset();
224
225 shortcut_icon_murmur2_hash_ = icon_murmur2_hash;
226
227 // An empty hash indicates that |icon_hasher_| encountered an error.
228 if (icon_murmur2_hash.empty()) {
229 OnFailure();
230 return;
231 }
232
233 base::PostTaskAndReplyWithResult(
234 GetBackgroundTaskRunner().get(), FROM_HERE,
235 base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
236 shortcut_icon_murmur2_hash_),
237 base::Bind(&WebApkInstaller::SendCreateWebApkRequest,
238 weak_ptr_factory_.GetWeakPtr()));
239 }
240
175 void WebApkInstaller::SendCreateWebApkRequest( 241 void WebApkInstaller::SendCreateWebApkRequest(
176 std::unique_ptr<webapk::WebApk> webapk_proto) { 242 std::unique_ptr<webapk::WebApk> webapk_proto) {
177 timer_.Start( 243 timer_.Start(
178 FROM_HERE, 244 FROM_HERE,
179 base::TimeDelta::FromMilliseconds(webapk_download_url_timeout_ms_), 245 base::TimeDelta::FromMilliseconds(webapk_download_url_timeout_ms_),
180 base::Bind(&WebApkInstaller::OnTimeout, weak_ptr_factory_.GetWeakPtr())); 246 base::Bind(&WebApkInstaller::OnTimeout, weak_ptr_factory_.GetWeakPtr()));
181 247
182 url_fetcher_ = 248 url_fetcher_ =
183 net::URLFetcher::Create(server_url_, net::URLFetcher::POST, this); 249 net::URLFetcher::Create(server_url_, net::URLFetcher::POST, this);
184 url_fetcher_->SetRequestContext(request_context_getter_); 250 url_fetcher_->SetRequestContext(request_context_getter_);
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 base::android::ScopedJavaLocalRef<jstring> java_package_name = 309 base::android::ScopedJavaLocalRef<jstring> java_package_name =
244 base::android::ConvertUTF8ToJavaString(env, package_name); 310 base::android::ConvertUTF8ToJavaString(env, package_name);
245 bool success = 311 bool success =
246 StartDownloadedWebApkInstall(env, java_file_path, java_package_name); 312 StartDownloadedWebApkInstall(env, java_file_path, java_package_name);
247 if (success) 313 if (success)
248 OnSuccess(); 314 OnSuccess();
249 else 315 else
250 OnFailure(); 316 OnFailure();
251 } 317 }
252 318
253 std::unique_ptr<webapk::WebApk>
254 WebApkInstaller::BuildWebApkProtoInBackground() {
255 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
256
257 std::unique_ptr<webapk::WebApk> webapk(new webapk::WebApk);
258 webapk->set_manifest_url(shortcut_info_.manifest_url.spec());
259 webapk->set_requester_application_package(
260 base::android::BuildInfo::GetInstance()->package_name());
261 webapk->set_requester_application_version(version_info::GetVersionNumber());
262
263 webapk::WebAppManifest* web_app_manifest = webapk->mutable_manifest();
264 web_app_manifest->set_name(base::UTF16ToUTF8(shortcut_info_.name));
265 web_app_manifest->set_short_name(
266 base::UTF16ToUTF8(shortcut_info_.short_name));
267 web_app_manifest->set_start_url(shortcut_info_.url.spec());
268 web_app_manifest->set_orientation(
269 content::WebScreenOrientationLockTypeToString(
270 shortcut_info_.orientation));
271 web_app_manifest->set_display_mode(
272 content::WebDisplayModeToString(shortcut_info_.display));
273 web_app_manifest->set_background_color(
274 ColorToString(shortcut_info_.background_color));
275 web_app_manifest->set_theme_color(ColorToString(shortcut_info_.theme_color));
276
277 std::string* scope = web_app_manifest->add_scopes();
278 scope->assign(GetScope(shortcut_info_).spec());
279 webapk::Image* image = web_app_manifest->add_icons();
280 image->set_src(shortcut_info_.icon_url.spec());
281 // TODO(pkotwicz): Get Murmur2 hash of untransformed icon's bytes (with no
282 // encoding/decoding).
283 image->set_hash(ComputeBitmapHash(shortcut_icon_));
284 std::vector<unsigned char> png_bytes;
285 gfx::PNGCodec::EncodeBGRASkBitmap(shortcut_icon_, false, &png_bytes);
286 image->set_image_data(&png_bytes.front(), png_bytes.size());
287
288 return webapk;
289 }
290
291 void WebApkInstaller::OnTimeout() { 319 void WebApkInstaller::OnTimeout() {
292 OnFailure(); 320 OnFailure();
293 } 321 }
294 322
295 void WebApkInstaller::OnSuccess() { 323 void WebApkInstaller::OnSuccess() {
296 finish_callback_.Run(true); 324 FinishCallback callback = finish_callback_;
297 delete this; 325 delete this;
326 callback.Run(true);
298 } 327 }
299 328
300 void WebApkInstaller::OnFailure() { 329 void WebApkInstaller::OnFailure() {
301 finish_callback_.Run(false); 330 FinishCallback callback = finish_callback_;
302 delete this; 331 delete this;
332 callback.Run(false);
303 } 333 }
OLDNEW
« no previous file with comments | « chrome/browser/android/webapk/webapk_installer.h ('k') | chrome/browser/android/webapk/webapk_installer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698