1 // Copyright 2020 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/context_menu/context_menu_native_delegate_impl.h"
6
7 #include "base/android/callback_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "chrome/browser/contextmenu/jni_headers/ContextMenuNativeDelegateImpl_jni.h"
11 #include "chrome/browser/download/android/download_controller_base.h"
12 #include "chrome/browser/image_decoder/image_decoder.h"
13 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
14 #include "components/embedder_support/android/contextmenu/context_menu_builder.h"
15 #include "content/public/browser/web_contents.h"
16 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
17 #include "ui/gfx/android/java_bitmap.h"
18
19 using base::android::JavaParamRef;
20 using base::android::JavaRef;
21
22 namespace {
23
24 class ContextMenuImageRequest : public ImageDecoder::ImageRequest {
25 public:
Start(const JavaRef<jobject> & jcallback,const std::vector<uint8_t> & thumbnail_data)26 static void Start(const JavaRef<jobject>& jcallback,
27 const std::vector<uint8_t>& thumbnail_data) {
28 auto* request = new ContextMenuImageRequest(jcallback);
29 ImageDecoder::Start(request, thumbnail_data);
30 }
31
32 protected:
OnImageDecoded(const SkBitmap & decoded_image)33 void OnImageDecoded(const SkBitmap& decoded_image) override {
34 base::android::RunObjectCallbackAndroid(
35 jcallback_, gfx::ConvertToJavaBitmap(decoded_image));
36 delete this;
37 }
38
OnDecodeImageFailed()39 void OnDecodeImageFailed() override {
40 base::android::ScopedJavaLocalRef<jobject> j_bitmap;
41 base::android::RunObjectCallbackAndroid(jcallback_, j_bitmap);
42 delete this;
43 }
44
45 private:
ContextMenuImageRequest(const JavaRef<jobject> & jcallback)46 explicit ContextMenuImageRequest(const JavaRef<jobject>& jcallback)
47 : jcallback_(jcallback) {}
48
49 const base::android::ScopedJavaGlobalRef<jobject> jcallback_;
50
51 DISALLOW_IMPLICIT_CONSTRUCTORS(ContextMenuImageRequest);
52 };
53
ToChromeMojomImageFormat(int image_format)54 chrome::mojom::ImageFormat ToChromeMojomImageFormat(int image_format) {
55 auto format = static_cast<ContextMenuImageFormat>(image_format);
56 switch (format) {
57 case ContextMenuImageFormat::JPEG:
58 return chrome::mojom::ImageFormat::JPEG;
59 case ContextMenuImageFormat::PNG:
60 return chrome::mojom::ImageFormat::PNG;
61 case ContextMenuImageFormat::ORIGINAL:
62 return chrome::mojom::ImageFormat::ORIGINAL;
63 }
64
65 NOTREACHED();
66 return chrome::mojom::ImageFormat::JPEG;
67 }
68
OnRetrieveImageForShare(mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame,const JavaRef<jobject> & jcallback,const std::vector<uint8_t> & thumbnail_data,const gfx::Size & original_size,const std::string & image_extension)69 void OnRetrieveImageForShare(
70 mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame>
71 chrome_render_frame,
72 const JavaRef<jobject>& jcallback,
73 const std::vector<uint8_t>& thumbnail_data,
74 const gfx::Size& original_size,
75 const std::string& image_extension) {
76 JNIEnv* env = base::android::AttachCurrentThread();
77 auto j_data = base::android::ToJavaByteArray(env, thumbnail_data);
78 auto j_extension =
79 base::android::ConvertUTF8ToJavaString(env, image_extension);
80 base::android::RunObjectCallbackAndroid(
81 jcallback, Java_ContextMenuNativeDelegateImpl_createImageCallbackResult(
82 env, j_data, j_extension));
83 }
84
OnRetrieveImageForContextMenu(mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame,const JavaRef<jobject> & jcallback,const std::vector<uint8_t> & thumbnail_data,const gfx::Size & original_size,const std::string & filename_extension)85 void OnRetrieveImageForContextMenu(
86 mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame>
87 chrome_render_frame,
88 const JavaRef<jobject>& jcallback,
89 const std::vector<uint8_t>& thumbnail_data,
90 const gfx::Size& original_size,
91 const std::string& filename_extension) {
92 ContextMenuImageRequest::Start(jcallback, thumbnail_data);
93 }
94
95 } // namespace
96
ContextMenuNativeDelegateImpl(content::WebContents * const web_contents,content::ContextMenuParams * const context_menu_params)97 ContextMenuNativeDelegateImpl::ContextMenuNativeDelegateImpl(
98 content::WebContents* const web_contents,
99 content::ContextMenuParams* const context_menu_params)
100 : web_contents_(web_contents), context_menu_params_(context_menu_params) {}
101
StartDownload(JNIEnv * env,const JavaParamRef<jobject> & obj,jboolean jis_link)102 void ContextMenuNativeDelegateImpl::StartDownload(
103 JNIEnv* env,
104 const JavaParamRef<jobject>& obj,
105 jboolean jis_link) {
106 DownloadControllerBase::Get()->StartContextMenuDownload(
107 *context_menu_params_, web_contents_, jis_link);
108 }
109
SearchForImage(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & jrender_frame_host)110 void ContextMenuNativeDelegateImpl::SearchForImage(
111 JNIEnv* env,
112 const JavaParamRef<jobject>& obj,
113 const JavaParamRef<jobject>& jrender_frame_host) {
114 auto* render_frame_host =
115 content::RenderFrameHost::FromJavaRenderFrameHost(jrender_frame_host);
116 if (!render_frame_host)
117 return;
118
119 CoreTabHelper::FromWebContents(web_contents_)
120 ->SearchByImageInNewTab(render_frame_host, context_menu_params_->src_url);
121 }
122
RetrieveImageForShare(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & jrender_frame_host,const JavaParamRef<jobject> & jcallback,jint max_width_px,jint max_height_px,jint jimage_format)123 void ContextMenuNativeDelegateImpl::RetrieveImageForShare(
124 JNIEnv* env,
125 const JavaParamRef<jobject>& obj,
126 const JavaParamRef<jobject>& jrender_frame_host,
127 const JavaParamRef<jobject>& jcallback,
128 jint max_width_px,
129 jint max_height_px,
130 jint jimage_format) {
131 RetrieveImageInternal(env, base::BindOnce(&OnRetrieveImageForShare),
132 jrender_frame_host, jcallback, max_width_px,
133 max_height_px, ToChromeMojomImageFormat(jimage_format));
134 }
135
RetrieveImageForContextMenu(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & jrender_frame_host,const JavaParamRef<jobject> & jcallback,jint max_width_px,jint max_height_px)136 void ContextMenuNativeDelegateImpl::RetrieveImageForContextMenu(
137 JNIEnv* env,
138 const JavaParamRef<jobject>& obj,
139 const JavaParamRef<jobject>& jrender_frame_host,
140 const JavaParamRef<jobject>& jcallback,
141 jint max_width_px,
142 jint max_height_px) {
143 // For context menu, Image needs to be PNG for receiving transparency pixels.
144 RetrieveImageInternal(env, base::BindOnce(&OnRetrieveImageForContextMenu),
145 jrender_frame_host, jcallback, max_width_px,
146 max_height_px, chrome::mojom::ImageFormat::PNG);
147 }
148
RetrieveImageInternal(JNIEnv * env,ImageRetrieveCallback retrieve_callback,const JavaParamRef<jobject> & jrender_frame_host,const JavaParamRef<jobject> & jcallback,jint max_width_px,jint max_height_px,chrome::mojom::ImageFormat image_format)149 void ContextMenuNativeDelegateImpl::RetrieveImageInternal(
150 JNIEnv* env,
151 ImageRetrieveCallback retrieve_callback,
152 const JavaParamRef<jobject>& jrender_frame_host,
153 const JavaParamRef<jobject>& jcallback,
154 jint max_width_px,
155 jint max_height_px,
156 chrome::mojom::ImageFormat image_format) {
157 auto* render_frame_host =
158 content::RenderFrameHost::FromJavaRenderFrameHost(jrender_frame_host);
159 if (!render_frame_host)
160 return;
161 mojo::AssociatedRemote<chrome::mojom::ChromeRenderFrame> chrome_render_frame;
162 render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
163 &chrome_render_frame);
164
165 // Bind the InterfacePtr into the callback so that it's kept alive
166 // until there's either a connection error or a response.
167 auto* thumbnail_capturer_proxy = chrome_render_frame.get();
168 thumbnail_capturer_proxy->RequestImageForContextNode(
169 max_width_px * max_height_px, gfx::Size(max_width_px, max_height_px),
170 image_format,
171 base::BindOnce(
172 std::move(retrieve_callback), base::Passed(&chrome_render_frame),
173 base::android::ScopedJavaGlobalRef<jobject>(env, jcallback)));
174 }
175
JNI_ContextMenuNativeDelegateImpl_Init(JNIEnv * env,const JavaParamRef<jobject> & jweb_contents,const JavaParamRef<jobject> & jcontext_menu_params)176 static jlong JNI_ContextMenuNativeDelegateImpl_Init(
177 JNIEnv* env,
178 const JavaParamRef<jobject>& jweb_contents,
179 const JavaParamRef<jobject>& jcontext_menu_params) {
180 if (jweb_contents.is_null())
181 return reinterpret_cast<intptr_t>(nullptr);
182 auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
183 DCHECK(web_contents);
184 auto* params =
185 context_menu::ContextMenuParamsFromJavaObject(jcontext_menu_params);
186 return reinterpret_cast<intptr_t>(
187 new ContextMenuNativeDelegateImpl(web_contents, params));
188 }
189