| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/omnibox/browser/autocomplete_match.h" |
| |
| #include <vector> |
| |
| #include "base/android/callback_android.h" |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/functional/bind.h" |
| #include "base/uuid.h" |
| #include "components/omnibox/browser/actions/omnibox_action.h" |
| #include "components/omnibox/browser/actions/omnibox_action_factory_android.h" |
| #include "components/omnibox/browser/clipboard_provider.h" |
| #include "components/omnibox/browser/search_suggestion_parser.h" |
| #include "components/omnibox/common/omnibox_feature_configs.h" |
| #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h" |
| #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h" |
| #include "third_party/omnibox_proto/suggest_template_info.pb.h" |
| #include "url/android/gurl_android.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "components/omnibox/browser/jni_headers/AutocompleteMatch_jni.h" |
| |
| using base::android::ConvertUTF16ToJavaString; |
| using base::android::ConvertUTF8ToJavaString; |
| using base::android::RunRunnableAndroid; |
| using base::android::ScopedJavaGlobalRef; |
| using base::android::ScopedJavaLocalRef; |
| using base::android::ToJavaArrayOfStrings; |
| using base::android::ToJavaByteArray; |
| using base::android::ToJavaIntArray; |
| |
| // static |
| jclass AutocompleteMatch::GetClazz(JNIEnv* env) { |
| return org_chromium_components_omnibox_AutocompleteMatch_clazz(env); |
| } |
| |
| ScopedJavaLocalRef<jobject> AutocompleteMatch::GetOrCreateJavaObject( |
| JNIEnv* env) const { |
| // Short circuit if we already built the match. |
| if (java_match_) |
| return ScopedJavaLocalRef<jobject>(*java_match_); |
| |
| std::vector<int> contents_class_offsets; |
| std::vector<int> contents_class_styles; |
| for (auto contents_class_item : contents_class) { |
| contents_class_offsets.push_back(contents_class_item.offset); |
| contents_class_styles.push_back(contents_class_item.style); |
| } |
| |
| std::vector<int> description_class_offsets; |
| std::vector<int> description_class_styles; |
| for (auto description_class_item : description_class) { |
| description_class_offsets.push_back(description_class_item.offset); |
| description_class_styles.push_back(description_class_item.style); |
| } |
| |
| ScopedJavaLocalRef<jbyteArray> j_answer_template; |
| if (answer_template) { |
| std::string str_answer_template; |
| if (answer_template->SerializeToString(&str_answer_template)) { |
| j_answer_template = |
| base::android::ToJavaByteArray(env, str_answer_template); |
| } |
| } |
| |
| ScopedJavaLocalRef<jstring> j_image_dominant_color; |
| ScopedJavaLocalRef<jstring> j_post_content_type; |
| ScopedJavaLocalRef<jbyteArray> j_post_content; |
| std::string clipboard_image_data; |
| |
| if (!image_dominant_color.empty()) { |
| j_image_dominant_color = ConvertUTF8ToJavaString(env, image_dominant_color); |
| } |
| |
| if (post_content && !post_content->first.empty() && |
| !post_content->second.empty()) { |
| j_post_content_type = ConvertUTF8ToJavaString(env, post_content->first); |
| j_post_content = ToJavaByteArray(env, post_content->second); |
| } |
| |
| if (search_terms_args.get()) { |
| clipboard_image_data = search_terms_args->image_thumbnail_content; |
| } |
| |
| std::vector<int> temp_subtypes(subtypes.begin(), subtypes.end()); |
| |
| std::vector<jni_zero::ScopedJavaLocalRef<jobject>> actions_list; |
| if (actions.empty() && takeover_action) { |
| actions_list = ToJavaOmniboxActionsList(env, {takeover_action}); |
| } else { |
| actions_list = ToJavaOmniboxActionsList(env, actions); |
| } |
| |
| int icon_type = omnibox::SuggestTemplateInfo::IconType:: |
| SuggestTemplateInfo_IconType_ICON_TYPE_UNSPECIFIED; |
| |
| ScopedJavaLocalRef<jbyteArray> j_suggest_template; |
| |
| if (suggest_template.has_value()) { |
| icon_type = suggest_template.value().type_icon(); |
| |
| std::string str_suggest_template; |
| if (suggest_template->SerializeToString(&str_suggest_template)) { |
| j_suggest_template = |
| base::android::ToJavaByteArray(env, str_suggest_template); |
| } |
| } |
| |
| java_match_ = std::make_unique<ScopedJavaGlobalRef<jobject>>( |
| Java_AutocompleteMatch_build( |
| env, reinterpret_cast<intptr_t>(this), type, |
| ToJavaIntArray(env, temp_subtypes), IsSearchType(type), icon_type, |
| transition, ConvertUTF16ToJavaString(env, contents), |
| ToJavaIntArray(env, contents_class_offsets), |
| ToJavaIntArray(env, contents_class_styles), |
| ConvertUTF16ToJavaString(env, description), |
| ToJavaIntArray(env, description_class_offsets), |
| ToJavaIntArray(env, description_class_styles), j_answer_template, |
| answer_type, ConvertUTF16ToJavaString(env, fill_into_edit), |
| url::GURLAndroid::FromNativeGURL(env, destination_url), |
| url::GURLAndroid::FromNativeGURL(env, image_url), |
| j_image_dominant_color, SupportsDeletion(), j_post_content_type, |
| j_post_content, suggestion_group_id.value_or(omnibox::GROUP_INVALID), |
| ToJavaByteArray(env, clipboard_image_data), |
| has_tab_match.value_or(false), actions_list, |
| allowed_to_be_default_match, |
| ConvertUTF16ToJavaString(env, inline_autocompletion), |
| ConvertUTF16ToJavaString(env, additional_text), |
| tab_groups::UuidToJavaString( |
| env, matching_tab_group_uuid.value_or(base::Uuid())), |
| j_suggest_template)); |
| |
| return ScopedJavaLocalRef<jobject>(*java_match_); |
| } |
| |
| void AutocompleteMatch::UpdateJavaObjectNativeRef() { |
| if (!java_match_) |
| return; |
| |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_AutocompleteMatch_updateNativeObjectRef( |
| env, *java_match_, reinterpret_cast<intptr_t>(this)); |
| } |
| |
| void AutocompleteMatch::DestroyJavaObject() { |
| if (!java_match_) |
| return; |
| |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_AutocompleteMatch_destroy(env, *java_match_); |
| java_match_.reset(); |
| } |
| |
| void AutocompleteMatch::UpdateWithClipboardContent( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& j_callback) { |
| DCHECK(provider) << "No provider available"; |
| DCHECK(provider->type() == AutocompleteProvider::TYPE_CLIPBOARD) |
| << "Invalid provider type: " << provider->type(); |
| |
| ClipboardProvider* clipboard_provider = |
| static_cast<ClipboardProvider*>(provider); |
| clipboard_provider->UpdateClipboardMatchWithContent( |
| this, |
| base::BindOnce(&AutocompleteMatch::OnClipboardSuggestionContentUpdated, |
| weak_ptr_factory_.GetWeakPtr(), |
| base::android::ScopedJavaGlobalRef<jobject>(j_callback))); |
| } |
| |
| void AutocompleteMatch::OnClipboardSuggestionContentUpdated( |
| const base::android::JavaRef<jobject>& j_callback) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| UpdateClipboardContent(env); |
| RunRunnableAndroid(j_callback); |
| } |
| |
| void AutocompleteMatch::UpdateMatchingJavaTab( |
| const JavaObjectWeakGlobalRef& tab) { |
| matching_java_tab_ = tab; |
| |
| // Default state is: we don't have a matching tab. If that default state has |
| // changed, reflect it in the UI. |
| // TODO(crbug.com/40204147): when Tab.java is relocated to Components, pass |
| // the Tab object directly to Java. This is not possible right now due to |
| // //components being explicitly denied to depend on //chrome targets. |
| if (!java_match_ || !has_tab_match.value_or(false)) |
| return; |
| |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_AutocompleteMatch_updateMatchingTab(env, *java_match_, true); |
| } |
| |
| JavaObjectWeakGlobalRef AutocompleteMatch::GetMatchingJavaTab() const { |
| return matching_java_tab_; |
| } |
| |
| void AutocompleteMatch::UpdateClipboardContent(JNIEnv* env) { |
| if (!java_match_) |
| return; |
| |
| std::string clipboard_image_data; |
| if (search_terms_args.get()) { |
| clipboard_image_data = search_terms_args->image_thumbnail_content; |
| } |
| |
| ScopedJavaLocalRef<jstring> j_post_content_type; |
| ScopedJavaLocalRef<jbyteArray> j_post_content; |
| if (post_content && !post_content->first.empty() && |
| !post_content->second.empty()) { |
| j_post_content_type = ConvertUTF8ToJavaString(env, post_content->first); |
| j_post_content = ToJavaByteArray(env, post_content->second); |
| } |
| |
| Java_AutocompleteMatch_updateClipboardContent( |
| env, *java_match_, ConvertUTF16ToJavaString(env, contents), |
| url::GURLAndroid::FromNativeGURL(env, destination_url), |
| j_post_content_type, j_post_content, |
| ToJavaByteArray(env, clipboard_image_data)); |
| } |
| |
| void AutocompleteMatch::UpdateJavaNavigationDetails() { |
| if (java_match_) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| |
| std::vector<std::string> header_keys; |
| std::vector<std::string> header_vals; |
| for (const auto& [key, val] : extra_headers) { |
| header_keys.emplace_back(key); |
| header_vals.emplace_back(val); |
| } |
| |
| Java_AutocompleteMatch_updateNavigationDetails( |
| env, *java_match_, |
| url::GURLAndroid::FromNativeGURL(env, destination_url), |
| ToJavaArrayOfStrings(env, header_keys), |
| ToJavaArrayOfStrings(env, header_vals)); |
| } |
| } |
| |
| void AutocompleteMatch::UpdateJavaAnswer() { |
| if (java_match_) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| if (answer_template) { |
| ScopedJavaLocalRef<jbyteArray> j_answer_template; |
| std::string str_answer_template; |
| if (answer_template->SerializeToString(&str_answer_template)) { |
| j_answer_template = |
| base::android::ToJavaByteArray(env, str_answer_template); |
| } |
| Java_AutocompleteMatch_setAnswerTemplate( |
| env, *java_match_, answer_template ? j_answer_template : nullptr); |
| } |
| Java_AutocompleteMatch_setAnswerType(env, *java_match_, answer_type); |
| } |
| } |
| |
| void AutocompleteMatch::UpdateJavaDescription() { |
| if (java_match_) { |
| std::vector<int> description_class_offsets; |
| std::vector<int> description_class_styles; |
| for (auto description_class_item : description_class) { |
| description_class_offsets.push_back(description_class_item.offset); |
| description_class_styles.push_back(description_class_item.style); |
| } |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_AutocompleteMatch_setDescription( |
| env, *java_match_, ConvertUTF16ToJavaString(env, description), |
| ToJavaIntArray(env, description_class_offsets), |
| ToJavaIntArray(env, description_class_styles)); |
| } |
| } |