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