1// Copyright (c) 2012 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/chrome_web_contents_delegate_android.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_string.h"
9#include "base/command_line.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/file_select_helper.h"
12#include "chrome/browser/media/media_capture_devices_dispatcher.h"
13#include "chrome/browser/media/protected_media_identifier_permission_context.h"
14#include "chrome/browser/media/protected_media_identifier_permission_context_factory.h"
15#include "chrome/browser/prerender/prerender_manager.h"
16#include "chrome/browser/prerender/prerender_manager_factory.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_manager.h"
19#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
20#include "chrome/browser/ui/browser_navigator.h"
21#include "chrome/browser/ui/find_bar/find_notification_details.h"
22#include "chrome/browser/ui/find_bar/find_tab_helper.h"
23#include "chrome/browser/ui/tab_helpers.h"
24#include "chrome/common/chrome_switches.h"
25#include "content/public/browser/notification_details.h"
26#include "content/public/browser/notification_service.h"
27#include "content/public/browser/notification_source.h"
28#include "content/public/browser/render_process_host.h"
29#include "content/public/browser/render_view_host.h"
30#include "content/public/browser/web_contents.h"
31#include "content/public/common/file_chooser_params.h"
32#include "jni/ChromeWebContentsDelegateAndroid_jni.h"
33#include "third_party/WebKit/public/web/WebWindowFeatures.h"
34#include "ui/gfx/rect.h"
35#include "ui/gfx/rect_f.h"
36
37#if defined(ENABLE_PLUGINS)
38#include "chrome/browser/pepper_broker_infobar_delegate.h"
39#endif
40
41using base::android::AttachCurrentThread;
42using base::android::ScopedJavaLocalRef;
43using content::FileChooserParams;
44using content::WebContents;
45
46namespace {
47
48ScopedJavaLocalRef<jobject> CreateJavaRectF(
49    JNIEnv* env,
50    const gfx::RectF& rect) {
51  return ScopedJavaLocalRef<jobject>(
52      Java_ChromeWebContentsDelegateAndroid_createRectF(env,
53                                                        rect.x(),
54                                                        rect.y(),
55                                                        rect.right(),
56                                                        rect.bottom()));
57}
58
59ScopedJavaLocalRef<jobject> CreateJavaRect(
60    JNIEnv* env,
61    const gfx::Rect& rect) {
62  return ScopedJavaLocalRef<jobject>(
63      Java_ChromeWebContentsDelegateAndroid_createRect(
64          env,
65          static_cast<int>(rect.x()),
66          static_cast<int>(rect.y()),
67          static_cast<int>(rect.right()),
68          static_cast<int>(rect.bottom())));
69}
70
71}  // anonymous namespace
72
73namespace chrome {
74namespace android {
75
76ChromeWebContentsDelegateAndroid::ChromeWebContentsDelegateAndroid(JNIEnv* env,
77                                                                   jobject obj)
78    : WebContentsDelegateAndroid(env, obj) {
79}
80
81ChromeWebContentsDelegateAndroid::~ChromeWebContentsDelegateAndroid() {
82  notification_registrar_.RemoveAll();
83}
84
85// Register native methods.
86bool RegisterChromeWebContentsDelegateAndroid(JNIEnv* env) {
87  return RegisterNativesImpl(env);
88}
89
90void ChromeWebContentsDelegateAndroid::LoadingStateChanged(
91    WebContents* source, bool to_different_document) {
92  bool has_stopped = source == NULL || !source->IsLoading();
93  WebContentsDelegateAndroid::LoadingStateChanged(
94      source, to_different_document);
95  LoadProgressChanged(source, has_stopped ? 1 : 0);
96}
97
98void ChromeWebContentsDelegateAndroid::RunFileChooser(
99    WebContents* web_contents,
100    const FileChooserParams& params) {
101  FileSelectHelper::RunFileChooser(web_contents, params);
102}
103
104void ChromeWebContentsDelegateAndroid::CloseContents(
105    WebContents* web_contents) {
106  // Prevent dangling registrations assigned to closed web contents.
107  if (notification_registrar_.IsRegistered(this,
108      chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
109      content::Source<WebContents>(web_contents))) {
110    notification_registrar_.Remove(this,
111        chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
112        content::Source<WebContents>(web_contents));
113  }
114
115  WebContentsDelegateAndroid::CloseContents(web_contents);
116}
117
118void ChromeWebContentsDelegateAndroid::Observe(
119    int type,
120    const content::NotificationSource& source,
121    const content::NotificationDetails& details) {
122  switch (type) {
123    case chrome::NOTIFICATION_FIND_RESULT_AVAILABLE:
124      OnFindResultAvailable(
125          content::Source<WebContents>(source).ptr(),
126          content::Details<FindNotificationDetails>(details).ptr());
127      break;
128    default:
129      NOTREACHED() << "Unexpected notification: " << type;
130      break;
131  }
132}
133
134void ChromeWebContentsDelegateAndroid::FindReply(
135    WebContents* web_contents,
136    int request_id,
137    int number_of_matches,
138    const gfx::Rect& selection_rect,
139    int active_match_ordinal,
140    bool final_update) {
141  if (!notification_registrar_.IsRegistered(this,
142      chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
143      content::Source<WebContents>(web_contents))) {
144    notification_registrar_.Add(this,
145        chrome::NOTIFICATION_FIND_RESULT_AVAILABLE,
146        content::Source<WebContents>(web_contents));
147  }
148
149  FindTabHelper* find_tab_helper = FindTabHelper::FromWebContents(web_contents);
150  find_tab_helper->HandleFindReply(request_id,
151                                   number_of_matches,
152                                   selection_rect,
153                                   active_match_ordinal,
154                                   final_update);
155}
156
157void ChromeWebContentsDelegateAndroid::OnFindResultAvailable(
158    WebContents* web_contents,
159    const FindNotificationDetails* find_result) {
160  JNIEnv* env = base::android::AttachCurrentThread();
161  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
162  if (obj.is_null())
163    return;
164
165  ScopedJavaLocalRef<jobject> selection_rect = CreateJavaRect(
166      env, find_result->selection_rect());
167
168  // Create the details object.
169  ScopedJavaLocalRef<jobject> details_object =
170      Java_ChromeWebContentsDelegateAndroid_createFindNotificationDetails(
171          env,
172          find_result->number_of_matches(),
173          selection_rect.obj(),
174          find_result->active_match_ordinal(),
175          find_result->final_update());
176
177  Java_ChromeWebContentsDelegateAndroid_onFindResultAvailable(
178      env,
179      obj.obj(),
180      details_object.obj());
181}
182
183void ChromeWebContentsDelegateAndroid::FindMatchRectsReply(
184    WebContents* web_contents,
185    int version,
186    const std::vector<gfx::RectF>& rects,
187    const gfx::RectF& active_rect) {
188  JNIEnv* env = base::android::AttachCurrentThread();
189  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
190  if (obj.is_null())
191    return;
192
193  // Create the details object.
194  ScopedJavaLocalRef<jobject> details_object =
195      Java_ChromeWebContentsDelegateAndroid_createFindMatchRectsDetails(
196          env,
197          version,
198          rects.size(),
199          CreateJavaRectF(env, active_rect).obj());
200
201  // Add the rects
202  for (size_t i = 0; i < rects.size(); ++i) {
203      Java_ChromeWebContentsDelegateAndroid_setMatchRectByIndex(
204          env,
205          details_object.obj(),
206          i,
207          CreateJavaRectF(env, rects[i]).obj());
208  }
209
210  Java_ChromeWebContentsDelegateAndroid_onFindMatchRectsAvailable(
211      env,
212      obj.obj(),
213      details_object.obj());
214}
215
216content::JavaScriptDialogManager*
217ChromeWebContentsDelegateAndroid::GetJavaScriptDialogManager() {
218  return GetJavaScriptDialogManagerInstance();
219}
220
221void ChromeWebContentsDelegateAndroid::RequestMediaAccessPermission(
222    content::WebContents* web_contents,
223    const content::MediaStreamRequest& request,
224    const content::MediaResponseCallback& callback) {
225  MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
226      web_contents, request, callback, NULL);
227}
228
229bool ChromeWebContentsDelegateAndroid::CheckMediaAccessPermission(
230    content::WebContents* web_contents,
231    const GURL& security_origin,
232    content::MediaStreamType type) {
233  return MediaCaptureDevicesDispatcher::GetInstance()
234      ->CheckMediaAccessPermission(web_contents, security_origin, type);
235}
236
237bool ChromeWebContentsDelegateAndroid::RequestPpapiBrokerPermission(
238    WebContents* web_contents,
239    const GURL& url,
240    const base::FilePath& plugin_path,
241    const base::Callback<void(bool)>& callback) {
242#if defined(ENABLE_PLUGINS)
243    PepperBrokerInfoBarDelegate::Create(
244        web_contents, url, plugin_path, callback);
245    return true;
246#else
247    return false;
248#endif
249}
250
251WebContents* ChromeWebContentsDelegateAndroid::OpenURLFromTab(
252    WebContents* source,
253    const content::OpenURLParams& params) {
254  WindowOpenDisposition disposition = params.disposition;
255  if (!source || (disposition != CURRENT_TAB &&
256                  disposition != NEW_FOREGROUND_TAB &&
257                  disposition != NEW_BACKGROUND_TAB &&
258                  disposition != OFF_THE_RECORD &&
259                  disposition != NEW_POPUP &&
260                  disposition != NEW_WINDOW)) {
261    // We can't handle this here.  Give the parent a chance.
262    return WebContentsDelegateAndroid::OpenURLFromTab(source, params);
263  }
264
265  Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext());
266  chrome::NavigateParams nav_params(profile,
267                                    params.url,
268                                    params.transition);
269  FillNavigateParamsFromOpenURLParams(&nav_params, params);
270  nav_params.source_contents = source;
271  nav_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
272  nav_params.user_gesture = params.user_gesture;
273
274  PopupBlockerTabHelper* popup_blocker_helper =
275      PopupBlockerTabHelper::FromWebContents(source);
276  DCHECK(popup_blocker_helper);
277
278  if ((params.disposition == NEW_POPUP ||
279       params.disposition == NEW_FOREGROUND_TAB ||
280       params.disposition == NEW_BACKGROUND_TAB ||
281       params.disposition == NEW_WINDOW) &&
282      !params.user_gesture &&
283      !CommandLine::ForCurrentProcess()->HasSwitch(
284          switches::kDisablePopupBlocking)) {
285    if (popup_blocker_helper->MaybeBlockPopup(nav_params,
286                                              blink::WebWindowFeatures())) {
287      return NULL;
288    }
289  }
290
291  if (disposition == CURRENT_TAB) {
292    // Only prerender for a current-tab navigation to avoid session storage
293    // namespace issues.
294    nav_params.target_contents = source;
295    prerender::PrerenderManager* prerender_manager =
296        prerender::PrerenderManagerFactory::GetForProfile(profile);
297    if (prerender_manager &&
298        prerender_manager->MaybeUsePrerenderedPage(params.url, &nav_params)) {
299      return nav_params.target_contents;
300    }
301  }
302
303  return WebContentsDelegateAndroid::OpenURLFromTab(source, params);
304}
305
306void ChromeWebContentsDelegateAndroid::AddNewContents(
307    WebContents* source,
308    WebContents* new_contents,
309    WindowOpenDisposition disposition,
310    const gfx::Rect& initial_pos,
311    bool user_gesture,
312    bool* was_blocked) {
313  // No code for this yet.
314  DCHECK_NE(disposition, SAVE_TO_DISK);
315  // Can't create a new contents for the current tab - invalid case.
316  DCHECK_NE(disposition, CURRENT_TAB);
317
318  TabHelpers::AttachTabHelpers(new_contents);
319
320  JNIEnv* env = AttachCurrentThread();
321  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
322  bool handled = false;
323  if (!obj.is_null()) {
324    handled = Java_ChromeWebContentsDelegateAndroid_addNewContents(
325        env,
326        obj.obj(),
327        reinterpret_cast<intptr_t>(source),
328        reinterpret_cast<intptr_t>(new_contents),
329        static_cast<jint>(disposition),
330        NULL,
331        user_gesture);
332  }
333
334  if (was_blocked)
335    *was_blocked = !handled;
336  if (!handled)
337    delete new_contents;
338}
339
340void ChromeWebContentsDelegateAndroid::WebContentsCreated(
341    content::WebContents* source_contents, int opener_render_frame_id,
342    const base::string16& frame_name, const GURL& target_url,
343    content::WebContents* new_contents) {
344  JNIEnv* env = AttachCurrentThread();
345  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
346  if (obj.is_null())
347    return;
348  Java_ChromeWebContentsDelegateAndroid_webContentsCreated(env, obj.obj(),
349      reinterpret_cast<intptr_t>(source_contents), opener_render_frame_id,
350      base::android::ConvertUTF16ToJavaString(env, frame_name).Release(),
351      base::android::ConvertUTF8ToJavaString(env, target_url.spec()).Release(),
352      reinterpret_cast<intptr_t>(new_contents));
353}
354
355}  // namespace android
356}  // namespace chrome
357