| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <jni.h> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/android/callback_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/android/chrome_jni_headers/WebApkUpdateManager_jni.h" |
| #include "chrome/browser/android/webapk/webapk_install_service.h" |
| #include "chrome/browser/android/webapk/webapk_installer.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "components/webapps/browser/android/shortcut_info.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "third_party/blink/public/mojom/manifest/manifest.mojom.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/android/color_helpers.h" |
| #include "ui/gfx/android/java_bitmap.h" |
| #include "url/gurl.h" |
| |
| using base::android::JavaParamRef; |
| using base::android::JavaRef; |
| using base::android::ScopedJavaGlobalRef; |
| |
| namespace { |
| |
| // Called after the update either succeeds or fails. |
| void OnUpdated(const JavaRef<jobject>& java_callback, |
| WebApkInstallResult result, |
| bool relax_updates, |
| const std::string& webapk_package) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_WebApkUpdateCallback_onResultFromNative( |
| env, java_callback, static_cast<int>(result), relax_updates); |
| } |
| |
| } // anonymous namespace |
| |
| // static JNI method. |
| static void JNI_WebApkUpdateManager_StoreWebApkUpdateRequestToFile( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& java_update_request_path, |
| const JavaParamRef<jstring>& java_start_url, |
| const JavaParamRef<jstring>& java_scope, |
| const JavaParamRef<jstring>& java_name, |
| const JavaParamRef<jstring>& java_short_name, |
| const JavaParamRef<jstring>& java_primary_icon_url, |
| const JavaParamRef<jobject>& java_primary_icon_bitmap, |
| jboolean java_is_primary_icon_maskable, |
| const JavaParamRef<jstring>& java_splash_icon_url, |
| const JavaParamRef<jobject>& java_splash_icon_bitmap, |
| jboolean java_is_splash_icon_maskable, |
| const JavaParamRef<jobjectArray>& java_icon_urls, |
| const JavaParamRef<jobjectArray>& java_icon_hashes, |
| jint java_display_mode, |
| jint java_orientation, |
| jlong java_theme_color, |
| jlong java_background_color, |
| const JavaParamRef<jstring>& java_share_target_action, |
| const JavaParamRef<jstring>& java_share_target_param_title, |
| const JavaParamRef<jstring>& java_share_target_param_text, |
| const jboolean java_share_target_param_is_method_post, |
| const jboolean java_share_target_param_is_enctype_multipart, |
| const JavaParamRef<jobjectArray>& java_share_target_param_file_names, |
| const JavaParamRef<jobjectArray>& java_share_target_param_accepts, |
| const JavaParamRef<jobjectArray>& java_shortcuts, |
| const JavaParamRef<jobjectArray>& java_shortcut_icon_data, |
| const JavaParamRef<jstring>& java_web_manifest_url, |
| const JavaParamRef<jstring>& java_webapk_package, |
| jint java_webapk_version, |
| jboolean java_is_manifest_stale, |
| jboolean java_is_app_identity_update_supported, |
| const JavaParamRef<jintArray>& java_update_reasons, |
| const JavaParamRef<jobject>& java_callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| std::string update_request_path = |
| ConvertJavaStringToUTF8(env, java_update_request_path); |
| |
| webapps::ShortcutInfo info( |
| GURL(ConvertJavaStringToUTF8(env, java_start_url))); |
| info.scope = GURL(ConvertJavaStringToUTF8(env, java_scope)); |
| info.name = ConvertJavaStringToUTF16(env, java_name); |
| info.short_name = ConvertJavaStringToUTF16(env, java_short_name); |
| info.user_title = info.short_name; |
| info.display = static_cast<blink::mojom::DisplayMode>(java_display_mode); |
| info.orientation = |
| static_cast<device::mojom::ScreenOrientationLockType>(java_orientation); |
| info.theme_color = ui::JavaColorToOptionalSkColor(java_theme_color); |
| info.background_color = ui::JavaColorToOptionalSkColor(java_background_color); |
| info.best_primary_icon_url = |
| GURL(ConvertJavaStringToUTF8(env, java_primary_icon_url)); |
| info.splash_image_url = |
| GURL(ConvertJavaStringToUTF8(env, java_splash_icon_url)); |
| info.is_splash_image_maskable = java_is_splash_icon_maskable; |
| info.manifest_url = GURL(ConvertJavaStringToUTF8(env, java_web_manifest_url)); |
| |
| GURL share_target_action = |
| GURL(ConvertJavaStringToUTF8(env, java_share_target_action)); |
| if (!share_target_action.is_empty()) { |
| info.share_target = webapps::ShareTarget(); |
| info.share_target->action = share_target_action; |
| info.share_target->params.title = |
| ConvertJavaStringToUTF16(java_share_target_param_title); |
| info.share_target->params.text = |
| ConvertJavaStringToUTF16(java_share_target_param_text); |
| info.share_target->method = |
| java_share_target_param_is_method_post == JNI_TRUE |
| ? blink::mojom::ManifestShareTarget_Method::kPost |
| : blink::mojom::ManifestShareTarget_Method::kGet; |
| |
| info.share_target->enctype = |
| java_share_target_param_is_enctype_multipart == JNI_TRUE |
| ? blink::mojom::ManifestShareTarget_Enctype::kMultipartFormData |
| : blink::mojom::ManifestShareTarget_Enctype::kFormUrlEncoded; |
| |
| std::vector<std::u16string> fileNames; |
| base::android::AppendJavaStringArrayToStringVector( |
| env, java_share_target_param_file_names, &fileNames); |
| |
| std::vector<std::vector<std::u16string>> accepts; |
| base::android::Java2dStringArrayTo2dStringVector( |
| env, java_share_target_param_accepts, &accepts); |
| |
| // The length of fileNames and accepts should always be the same, but here |
| // we just want to be safe. |
| for (size_t i = 0; i < std::min(fileNames.size(), accepts.size()); ++i) { |
| webapps::ShareTargetParamsFile file; |
| file.name = fileNames[i]; |
| file.accept.swap(accepts[i]); |
| info.share_target->params.files.push_back(file); |
| } |
| } |
| |
| base::android::AppendJavaStringArrayToStringVector(env, java_icon_urls, |
| &info.icon_urls); |
| |
| std::vector<std::string> icon_hashes; |
| base::android::AppendJavaStringArrayToStringVector(env, java_icon_hashes, |
| &icon_hashes); |
| |
| std::map<std::string, webapps::WebApkIconHasher::Icon> |
| icon_url_to_murmur2_hash; |
| for (size_t i = 0; i < info.icon_urls.size(); ++i) { |
| icon_url_to_murmur2_hash[info.icon_urls[i]] = |
| webapps::WebApkIconHasher::Icon{/* data= */ "", icon_hashes[i]}; |
| } |
| |
| gfx::JavaBitmap java_primary_icon_bitmap_lock(java_primary_icon_bitmap); |
| SkBitmap primary_icon = |
| gfx::CreateSkBitmapFromJavaBitmap(java_primary_icon_bitmap_lock); |
| primary_icon.setImmutable(); |
| |
| SkBitmap splash_icon; |
| if (!java_splash_icon_bitmap.is_null()) { |
| gfx::JavaBitmap java_splash_icon_bitmap_lock(java_splash_icon_bitmap); |
| splash_icon = |
| gfx::CreateSkBitmapFromJavaBitmap(java_splash_icon_bitmap_lock); |
| splash_icon.setImmutable(); |
| } |
| |
| std::string webapk_package; |
| ConvertJavaStringToUTF8(env, java_webapk_package, &webapk_package); |
| |
| std::vector<std::vector<std::u16string>> shortcuts; |
| std::vector<std::string> shortcut_icon_data; |
| base::android::Java2dStringArrayTo2dStringVector(env, java_shortcuts, |
| &shortcuts); |
| base::android::JavaArrayOfByteArrayToStringVector( |
| env, java_shortcut_icon_data, &shortcut_icon_data); |
| |
| DCHECK_EQ(shortcuts.size(), shortcut_icon_data.size()); |
| |
| for (size_t i = 0; i < shortcuts.size(); i++) { |
| const auto& shortcut_data = shortcuts[i]; |
| DCHECK_EQ(shortcut_data.size(), 5u); |
| |
| blink::Manifest::ShortcutItem shortcut_item; |
| shortcut_item.name = shortcut_data[0]; |
| shortcut_item.short_name = shortcut_data[1]; |
| shortcut_item.url = GURL(base::UTF16ToUTF8(shortcut_data[2])); |
| |
| blink::Manifest::ImageResource icon; |
| GURL icon_src(base::UTF16ToUTF8(shortcut_data[3])); |
| icon.src = icon_src; |
| icon.purpose.push_back(blink::mojom::ManifestImageResource_Purpose::ANY); |
| shortcut_item.icons.push_back(std::move(icon)); |
| |
| if (icon_src.is_valid()) { |
| icon_url_to_murmur2_hash[icon_src.spec()] = |
| webapps::WebApkIconHasher::Icon{ |
| /* data= */ std::move(shortcut_icon_data[i]), |
| /* hash= */ base::UTF16ToUTF8(shortcut_data[4])}; |
| } |
| info.best_shortcut_icon_urls.push_back(std::move(icon_src)); |
| info.shortcut_items.push_back(std::move(shortcut_item)); |
| } |
| |
| std::vector<int> int_update_reasons; |
| base::android::JavaIntArrayToIntVector(env, java_update_reasons, |
| &int_update_reasons); |
| std::vector<webapps::WebApkUpdateReason> update_reasons; |
| for (int update_reason : int_update_reasons) |
| update_reasons.push_back( |
| static_cast<webapps::WebApkUpdateReason>(update_reason)); |
| |
| WebApkInstaller::StoreUpdateRequestToFile( |
| base::FilePath(update_request_path), info, primary_icon, |
| java_is_primary_icon_maskable, splash_icon, webapk_package, |
| base::NumberToString(java_webapk_version), |
| std::move(icon_url_to_murmur2_hash), java_is_manifest_stale, |
| java_is_app_identity_update_supported, std::move(update_reasons), |
| base::BindOnce(&base::android::RunBooleanCallbackAndroid, |
| ScopedJavaGlobalRef<jobject>(java_callback))); |
| } |
| |
| // static JNI method. |
| static void JNI_WebApkUpdateManager_UpdateWebApkFromFile( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& java_update_request_path, |
| const JavaParamRef<jobject>& java_callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| ScopedJavaGlobalRef<jobject> callback_ref(java_callback); |
| |
| Profile* profile = ProfileManager::GetLastUsedProfile(); |
| if (profile == nullptr) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&OnUpdated, callback_ref, WebApkInstallResult::FAILURE, |
| false /* relax_updates */, "" /* webapk_package */)); |
| return; |
| } |
| |
| std::string update_request_path = |
| ConvertJavaStringToUTF8(env, java_update_request_path); |
| WebApkInstallService::Get(profile)->UpdateAsync( |
| base::FilePath(update_request_path), |
| base::BindOnce(&OnUpdated, callback_ref)); |
| } |