| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/android/select_popup.h" |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/containers/heap_array.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/browser/web_contents/web_contents_view_android.h" |
| #include "content/public/android/content_jni_headers/SelectPopup_jni.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| |
| using base::android::AttachCurrentThread; |
| using base::android::JavaParamRef; |
| using base::android::ScopedJavaLocalRef; |
| |
| namespace content { |
| |
| namespace { |
| |
| // Describes the type and enabled state of a select popup item. |
| // |
| // A Java counterpart will be generated for this enum. |
| // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.content.browser.input |
| enum PopupItemType { |
| // Popup item is of type group |
| POPUP_ITEM_TYPE_GROUP, |
| |
| // Popup item is disabled |
| POPUP_ITEM_TYPE_DISABLED, |
| |
| // Popup item is enabled |
| POPUP_ITEM_TYPE_ENABLED, |
| }; |
| |
| } // namespace |
| |
| SelectPopup::SelectPopup(WebContentsImpl* web_contents) |
| : web_contents_(web_contents) { |
| JNIEnv* env = AttachCurrentThread(); |
| java_obj_ = JavaObjectWeakGlobalRef( |
| env, Java_SelectPopup_create(env, web_contents_->GetJavaWebContents(), |
| reinterpret_cast<intptr_t>(this))); |
| } |
| |
| SelectPopup::~SelectPopup() { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_obj_.get(env); |
| if (j_obj.is_null()) |
| return; |
| Java_SelectPopup_onNativeDestroyed(env, j_obj); |
| popup_client_.reset(); |
| } |
| |
| void SelectPopup::ShowMenu( |
| mojo::PendingRemote<blink::mojom::PopupMenuClient> popup_client, |
| const gfx::Rect& bounds, |
| std::vector<blink::mojom::MenuItemPtr> items, |
| int selected_item, |
| bool multiple, |
| bool right_aligned) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_obj_.get(env); |
| if (j_obj.is_null()) |
| return; |
| |
| // Hide the popup menu if the mojo connection is still open. |
| if (popup_client_) |
| HideMenu(); |
| |
| // For multi-select list popups we find the list of previous selections by |
| // iterating through the items. But for single selection popups we take the |
| // given |selected_item| as is. |
| ScopedJavaLocalRef<jintArray> selected_array; |
| if (multiple) { |
| auto native_selected_array = base::HeapArray<jint>::WithSize(items.size()); |
| size_t selected_count = 0; |
| for (size_t i = 0; i < items.size(); ++i) { |
| if (items[i]->checked) |
| native_selected_array[selected_count++] = i; |
| } |
| |
| selected_array = |
| ScopedJavaLocalRef<jintArray>(env, env->NewIntArray(selected_count)); |
| env->SetIntArrayRegion(selected_array.obj(), 0, selected_count, |
| native_selected_array.data()); |
| } else { |
| selected_array = ScopedJavaLocalRef<jintArray>(env, env->NewIntArray(1)); |
| jint value = selected_item; |
| env->SetIntArrayRegion(selected_array.obj(), 0, 1, &value); |
| } |
| |
| ScopedJavaLocalRef<jintArray> enabled_array(env, |
| env->NewIntArray(items.size())); |
| std::vector<std::string> labels; |
| labels.reserve(items.size()); |
| for (size_t i = 0; i < items.size(); ++i) { |
| labels.push_back(items[i]->label.value_or("")); |
| jint enabled = (items[i]->type == blink::mojom::MenuItem::Type::kGroup |
| ? POPUP_ITEM_TYPE_GROUP |
| : (items[i]->enabled ? POPUP_ITEM_TYPE_ENABLED |
| : POPUP_ITEM_TYPE_DISABLED)); |
| env->SetIntArrayRegion(enabled_array.obj(), i, 1, &enabled); |
| } |
| ScopedJavaLocalRef<jobjectArray> items_array( |
| base::android::ToJavaArrayOfStrings(env, labels)); |
| ui::ViewAndroid* view = web_contents_->GetNativeView(); |
| popup_view_ = view->AcquireAnchorView(); |
| const ScopedJavaLocalRef<jobject> popup_view = popup_view_.view(); |
| if (popup_view.is_null()) |
| return; |
| |
| popup_client_.Bind(std::move(popup_client)); |
| popup_client_.set_disconnect_handler( |
| base::BindOnce(&SelectPopup::HideMenu, base::Unretained(this))); |
| |
| // |bounds| is in physical pixels. |
| gfx::RectF bounds_dip = gfx::RectF(bounds); |
| bounds_dip.Scale(1 / web_contents_->GetNativeView()->GetDipScale()); |
| view->SetAnchorRect(popup_view, bounds_dip); |
| Java_SelectPopup_show( |
| env, j_obj, popup_view, reinterpret_cast<jlong>(popup_client_.get()), |
| items_array, enabled_array, multiple, selected_array, right_aligned); |
| } |
| |
| void SelectPopup::HideMenu() { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_obj = java_obj_.get(env); |
| if (!j_obj.is_null()) |
| Java_SelectPopup_hideWithoutCancel(env, j_obj); |
| popup_view_.Reset(); |
| popup_client_.reset(); |
| } |
| |
| void SelectPopup::SelectMenuItems(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jlong selectPopupDelegate, |
| const JavaParamRef<jintArray>& indices) { |
| blink::mojom::PopupMenuClient* popup_client_raw_ptr = |
| reinterpret_cast<blink::mojom::PopupMenuClient*>(selectPopupDelegate); |
| DCHECK(popup_client_raw_ptr && popup_client_.get() == popup_client_raw_ptr); |
| |
| if (indices == NULL) { |
| popup_client_->DidCancel(); |
| return; |
| } |
| |
| std::vector<int> selected_indices; |
| base::android::JavaIntArrayToIntVector(env, indices, &selected_indices); |
| popup_client_->DidAcceptIndices(selected_indices); |
| } |
| |
| } // namespace content |