OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/android/webapk/manifest_upgrade_detector_fetcher.h" | |
6 | |
7 #include <jni.h> | |
8 #include <vector> | |
9 | |
10 #include "base/android/jni_array.h" | |
11 #include "base/android/jni_string.h" | |
12 #include "chrome/browser/android/shortcut_helper.h" | |
13 #include "chrome/browser/android/webapk/webapk_icon_hasher.h" | |
14 #include "chrome/browser/android/webapk/webapk_web_manifest_checker.h" | |
15 #include "chrome/browser/installable/installable_manager.h" | |
16 #include "chrome/browser/profiles/profile.h" | |
17 #include "content/public/browser/web_contents.h" | |
18 #include "content/public/common/manifest.h" | |
19 #include "jni/ManifestUpgradeDetectorFetcher_jni.h" | |
20 #include "third_party/smhasher/src/MurmurHash2.h" | |
21 #include "ui/gfx/android/java_bitmap.h" | |
22 #include "ui/gfx/codec/png_codec.h" | |
23 #include "url/gurl.h" | |
24 | |
25 using base::android::JavaParamRef; | |
26 using base::android::ScopedJavaLocalRef; | |
27 | |
28 namespace { | |
29 | |
30 // Returns whether the given |url| is within the scope of the |scope| url. | |
31 bool IsInScope(const GURL& url, const GURL& scope) { | |
32 return base::StartsWith(url.spec(), scope.spec(), | |
33 base::CompareCase::SENSITIVE); | |
34 } | |
35 | |
36 } // anonymous namespace | |
37 | |
38 jlong Initialize(JNIEnv* env, | |
39 const JavaParamRef<jobject>& obj, | |
40 const JavaParamRef<jstring>& java_scope_url, | |
41 const JavaParamRef<jstring>& java_web_manifest_url) { | |
42 GURL scope(base::android::ConvertJavaStringToUTF8(env, java_scope_url)); | |
43 GURL web_manifest_url(base::android::ConvertJavaStringToUTF8( | |
44 env, java_web_manifest_url)); | |
45 ManifestUpgradeDetectorFetcher* fetcher = | |
46 new ManifestUpgradeDetectorFetcher(env, obj, scope, web_manifest_url); | |
47 return reinterpret_cast<intptr_t>(fetcher); | |
48 } | |
49 | |
50 ManifestUpgradeDetectorFetcher::ManifestUpgradeDetectorFetcher( | |
51 JNIEnv* env, | |
52 jobject obj, | |
53 const GURL& scope, | |
54 const GURL& web_manifest_url) | |
55 : content::WebContentsObserver(nullptr), | |
56 scope_(scope), | |
57 web_manifest_url_(web_manifest_url), | |
58 info_(GURL()), | |
59 weak_ptr_factory_(this) { | |
60 java_ref_.Reset(env, obj); | |
61 } | |
62 | |
63 ManifestUpgradeDetectorFetcher::~ManifestUpgradeDetectorFetcher() { | |
64 } | |
65 | |
66 // static | |
67 bool ManifestUpgradeDetectorFetcher::Register(JNIEnv* env) { | |
68 return RegisterNativesImpl(env); | |
69 } | |
70 | |
71 void ManifestUpgradeDetectorFetcher::ReplaceWebContents( | |
72 JNIEnv* env, | |
73 const JavaParamRef<jobject>& obj, | |
74 const JavaParamRef<jobject>& java_web_contents) { | |
75 content::WebContents* web_contents = | |
76 content::WebContents::FromJavaWebContents(java_web_contents); | |
77 content::WebContentsObserver::Observe(web_contents); | |
78 } | |
79 | |
80 void ManifestUpgradeDetectorFetcher::Destroy(JNIEnv* env, | |
81 const JavaParamRef<jobject>& obj) { | |
82 delete this; | |
83 } | |
84 | |
85 void ManifestUpgradeDetectorFetcher::Start( | |
86 JNIEnv* env, | |
87 const JavaParamRef<jobject>& obj, | |
88 const JavaParamRef<jobject>& java_web_contents) { | |
89 ReplaceWebContents(env, obj, java_web_contents); | |
90 if (!web_contents()->IsLoading()) | |
91 FetchInstallableData(); | |
92 } | |
93 | |
94 void ManifestUpgradeDetectorFetcher::DidStopLoading() { | |
95 FetchInstallableData(); | |
96 } | |
97 | |
98 void ManifestUpgradeDetectorFetcher::FetchInstallableData() { | |
99 GURL url = web_contents()->GetLastCommittedURL(); | |
100 | |
101 // DidStopLoading() can be called multiple times for a single URL. Only fetch | |
102 // installable data the first time. | |
103 if (url == last_fetched_url_) | |
104 return; | |
105 last_fetched_url_ = url; | |
106 | |
107 if (!IsInScope(url, scope_)) | |
108 return; | |
109 | |
110 InstallableParams params; | |
111 params.ideal_icon_size_in_dp = | |
112 ShortcutHelper::GetIdealHomescreenIconSizeInDp(); | |
113 params.minimum_icon_size_in_dp = | |
114 ShortcutHelper::GetMinimumHomescreenIconSizeInDp(); | |
115 params.check_installable = true; | |
116 params.fetch_valid_icon = true; | |
117 InstallableManager::CreateForWebContents(web_contents()); | |
118 InstallableManager* installable_manager = | |
119 InstallableManager::FromWebContents(web_contents()); | |
120 installable_manager->GetData( | |
121 params, | |
122 base::Bind(&ManifestUpgradeDetectorFetcher::OnDidGetInstallableData, | |
123 weak_ptr_factory_.GetWeakPtr())); | |
124 } | |
125 | |
126 void ManifestUpgradeDetectorFetcher::OnDidGetInstallableData( | |
127 const InstallableData& data) { | |
128 // If the manifest is empty, it means the current WebContents doesn't | |
129 // associate with a Web Manifest. In such case, we ignore the empty manifest | |
130 // and continue observing the WebContents's loading until we find a page that | |
131 // links to the Web Manifest that we are looking for. | |
132 // If the manifest URL is different from the current one, we will continue | |
133 // observing too. It is based on our assumption that it is invalid for | |
134 // web developers to change the Web Manifest location. When it does | |
135 // change, we will treat the new Web Manifest as the one of another WebAPK. | |
136 if (data.manifest.IsEmpty() || web_manifest_url_ != data.manifest_url) | |
137 return; | |
138 | |
139 // TODO(pkotwicz): Tell Java side that the Web Manifest was fetched but the | |
140 // Web Manifest is not WebAPK-compatible. (http://crbug.com/639536) | |
141 if (data.error_code != NO_ERROR_DETECTED || | |
142 !AreWebManifestUrlsWebApkCompatible(data.manifest)) { | |
143 return; | |
144 } | |
145 | |
146 info_.UpdateFromManifest(data.manifest); | |
147 info_.manifest_url = data.manifest_url; | |
148 info_.best_icon_url = data.icon_url; | |
149 best_icon_ = *data.icon; | |
150 | |
151 icon_hasher_.reset(new WebApkIconHasher()); | |
152 Profile* profile = | |
153 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); | |
154 icon_hasher_->DownloadAndComputeMurmur2Hash( | |
155 profile->GetRequestContext(), | |
156 data.icon_url, | |
157 base::Bind(&ManifestUpgradeDetectorFetcher::OnGotIconMurmur2Hash, | |
158 weak_ptr_factory_.GetWeakPtr())); | |
159 } | |
160 | |
161 void ManifestUpgradeDetectorFetcher::OnGotIconMurmur2Hash( | |
162 const std::string& best_icon_murmur2_hash) { | |
163 icon_hasher_.reset(); | |
164 | |
165 if (best_icon_murmur2_hash.empty()) { | |
166 // TODO(pkotwicz): Tell Java side that the Web Manifest was fetched but the | |
167 // Web Manifest is not WebAPK-compatible. (http://crbug.com/639536) | |
168 return; | |
169 } | |
170 | |
171 OnDataAvailable(info_, best_icon_murmur2_hash, best_icon_); | |
172 } | |
173 | |
174 void ManifestUpgradeDetectorFetcher::OnDataAvailable( | |
175 const ShortcutInfo& info, | |
176 const std::string& best_icon_murmur2_hash, | |
177 const SkBitmap& best_icon_bitmap) { | |
178 JNIEnv* env = base::android::AttachCurrentThread(); | |
179 | |
180 ScopedJavaLocalRef<jstring> java_url = | |
181 base::android::ConvertUTF8ToJavaString(env, info.url.spec()); | |
182 ScopedJavaLocalRef<jstring> java_scope = | |
183 base::android::ConvertUTF8ToJavaString(env, info.scope.spec()); | |
184 ScopedJavaLocalRef<jstring> java_name = | |
185 base::android::ConvertUTF16ToJavaString(env, info.name); | |
186 ScopedJavaLocalRef<jstring> java_short_name = | |
187 base::android::ConvertUTF16ToJavaString(env, info.short_name); | |
188 ScopedJavaLocalRef<jstring> java_best_icon_url = | |
189 base::android::ConvertUTF8ToJavaString(env, info.best_icon_url.spec()); | |
190 ScopedJavaLocalRef<jstring> java_best_icon_murmur2_hash = | |
191 base::android::ConvertUTF8ToJavaString(env, best_icon_murmur2_hash); | |
192 ScopedJavaLocalRef<jobject> java_best_bitmap = | |
193 gfx::ConvertToJavaBitmap(&best_icon_bitmap); | |
194 | |
195 ScopedJavaLocalRef<jobjectArray> java_icon_urls = | |
196 base::android::ToJavaArrayOfStrings(env, info.icon_urls); | |
197 | |
198 Java_ManifestUpgradeDetectorFetcher_onDataAvailable( | |
199 env, java_ref_, java_url, java_scope, java_name, java_short_name, | |
200 java_best_icon_url, java_best_icon_murmur2_hash, java_best_bitmap, | |
201 java_icon_urls, info.display, info.orientation, info.theme_color, | |
202 info.background_color); | |
203 } | |
OLD | NEW |