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

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