web_contents_delegate_android.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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 "components/web_contents_delegate_android/web_contents_delegate_android.h"
6
7#include <android/keycodes.h>
8
9#include "base/android/jni_android.h"
10#include "base/android/jni_array.h"
11#include "base/android/jni_string.h"
12#include "components/web_contents_delegate_android/color_chooser_android.h"
13#include "content/public/browser/android/content_view_core.h"
14#include "content/public/browser/color_chooser.h"
15#include "content/public/browser/invalidate_type.h"
16#include "content/public/browser/native_web_keyboard_event.h"
17#include "content/public/browser/page_navigator.h"
18#include "content/public/browser/render_widget_host_view.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/common/page_transition_types.h"
21#include "content/public/common/referrer.h"
22#include "jni/WebContentsDelegateAndroid_jni.h"
23#include "ui/base/window_open_disposition.h"
24#include "ui/gfx/rect.h"
25#include "url/gurl.h"
26
27using base::android::AttachCurrentThread;
28using base::android::ConvertUTF8ToJavaString;
29using base::android::ConvertUTF16ToJavaString;
30using base::android::HasClass;
31using base::android::ScopedJavaLocalRef;
32using content::ColorChooser;
33using content::WebContents;
34using content::WebContentsDelegate;
35
36namespace web_contents_delegate_android {
37
38WebContentsDelegateAndroid::WebContentsDelegateAndroid(JNIEnv* env, jobject obj)
39    : weak_java_delegate_(env, obj) {
40}
41
42WebContentsDelegateAndroid::~WebContentsDelegateAndroid() {
43}
44
45ScopedJavaLocalRef<jobject>
46WebContentsDelegateAndroid::GetJavaDelegate(JNIEnv* env) const {
47  return weak_java_delegate_.get(env);
48}
49
50// ----------------------------------------------------------------------------
51// WebContentsDelegate methods
52// ----------------------------------------------------------------------------
53
54ColorChooser* WebContentsDelegateAndroid::OpenColorChooser(WebContents* source,
55                                                           SkColor color)  {
56  return new ColorChooserAndroid(source, color);
57}
58
59// OpenURLFromTab() will be called when we're performing a browser-intiated
60// navigation. The most common scenario for this is opening new tabs (see
61// RenderViewImpl::decidePolicyForNavigation for more details).
62WebContents* WebContentsDelegateAndroid::OpenURLFromTab(
63    WebContents* source,
64    const content::OpenURLParams& params) {
65  const GURL& url = params.url;
66  WindowOpenDisposition disposition = params.disposition;
67  content::PageTransition transition(
68      PageTransitionFromInt(params.transition));
69
70  if (!source || (disposition != CURRENT_TAB &&
71                  disposition != NEW_FOREGROUND_TAB &&
72                  disposition != NEW_BACKGROUND_TAB &&
73                  disposition != OFF_THE_RECORD)) {
74    NOTIMPLEMENTED();
75    return NULL;
76  }
77
78  JNIEnv* env = AttachCurrentThread();
79  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
80  if (obj.is_null())
81    return WebContentsDelegate::OpenURLFromTab(source, params);
82
83  if (disposition == NEW_FOREGROUND_TAB ||
84      disposition == NEW_BACKGROUND_TAB ||
85      disposition == OFF_THE_RECORD) {
86    JNIEnv* env = AttachCurrentThread();
87    ScopedJavaLocalRef<jstring> java_url =
88        ConvertUTF8ToJavaString(env, url.spec());
89    ScopedJavaLocalRef<jstring> extra_headers =
90            ConvertUTF8ToJavaString(env, params.extra_headers);
91    ScopedJavaLocalRef<jbyteArray> post_data;
92    if (params.uses_post &&
93        params.browser_initiated_post_data.get() &&
94        params.browser_initiated_post_data.get()->size()) {
95      post_data = base::android::ToJavaByteArray(
96          env,
97          reinterpret_cast<const uint8*>(
98              params.browser_initiated_post_data.get()->front()),
99          params.browser_initiated_post_data.get()->size());
100    }
101    Java_WebContentsDelegateAndroid_openNewTab(env,
102                                               obj.obj(),
103                                               java_url.obj(),
104                                               extra_headers.obj(),
105                                               post_data.obj(),
106                                               disposition == OFF_THE_RECORD);
107    return NULL;
108  }
109
110  source->GetController().LoadURL(url, params.referrer, transition,
111                                  std::string());
112  return source;
113}
114
115void WebContentsDelegateAndroid::NavigationStateChanged(
116    const WebContents* source, unsigned changed_flags) {
117  JNIEnv* env = AttachCurrentThread();
118  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
119  if (obj.is_null())
120    return;
121  Java_WebContentsDelegateAndroid_navigationStateChanged(
122      env,
123      obj.obj(),
124      changed_flags);
125}
126
127void WebContentsDelegateAndroid::AddNewContents(
128    WebContents* source,
129    WebContents* new_contents,
130    WindowOpenDisposition disposition,
131    const gfx::Rect& initial_pos,
132    bool user_gesture,
133    bool* was_blocked) {
134  JNIEnv* env = AttachCurrentThread();
135  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
136  bool handled = false;
137  if (!obj.is_null()) {
138    handled = Java_WebContentsDelegateAndroid_addNewContents(
139        env,
140        obj.obj(),
141        reinterpret_cast<jint>(source),
142        reinterpret_cast<jint>(new_contents),
143        static_cast<jint>(disposition),
144        NULL,
145        user_gesture);
146  }
147  if (!handled)
148    delete new_contents;
149}
150
151void WebContentsDelegateAndroid::ActivateContents(WebContents* contents) {
152  JNIEnv* env = AttachCurrentThread();
153  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
154  if (obj.is_null())
155    return;
156  Java_WebContentsDelegateAndroid_activateContents(env, obj.obj());
157}
158
159void WebContentsDelegateAndroid::DeactivateContents(WebContents* contents) {
160  // On desktop the current window is deactivated here, bringing the next window
161  // to focus. Not implemented on Android.
162}
163
164void WebContentsDelegateAndroid::LoadingStateChanged(WebContents* source) {
165  JNIEnv* env = AttachCurrentThread();
166  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
167  if (obj.is_null())
168    return;
169  bool has_stopped = source == NULL || !source->IsLoading();
170
171  if (has_stopped)
172    Java_WebContentsDelegateAndroid_onLoadStopped(env, obj.obj());
173  else
174    Java_WebContentsDelegateAndroid_onLoadStarted(env, obj.obj());
175}
176
177void WebContentsDelegateAndroid::LoadProgressChanged(WebContents* source,
178                                                     double progress) {
179  JNIEnv* env = AttachCurrentThread();
180  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
181  if (obj.is_null())
182    return;
183  Java_WebContentsDelegateAndroid_notifyLoadProgressChanged(
184      env,
185      obj.obj(),
186      progress);
187}
188
189void WebContentsDelegateAndroid::RendererUnresponsive(WebContents* source) {
190  JNIEnv* env = AttachCurrentThread();
191  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
192  if (obj.is_null())
193    return;
194  Java_WebContentsDelegateAndroid_rendererUnresponsive(env, obj.obj());
195}
196
197void WebContentsDelegateAndroid::RendererResponsive(WebContents* source) {
198  JNIEnv* env = AttachCurrentThread();
199  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
200  if (obj.is_null())
201    return;
202  Java_WebContentsDelegateAndroid_rendererResponsive(env, obj.obj());
203}
204
205void WebContentsDelegateAndroid::CloseContents(WebContents* source) {
206  JNIEnv* env = AttachCurrentThread();
207  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
208  if (obj.is_null())
209    return;
210  Java_WebContentsDelegateAndroid_closeContents(env, obj.obj());
211}
212
213void WebContentsDelegateAndroid::MoveContents(WebContents* source,
214                                              const gfx::Rect& pos) {
215  // Do nothing.
216}
217
218bool WebContentsDelegateAndroid::AddMessageToConsole(
219    WebContents* source,
220    int32 level,
221    const base::string16& message,
222    int32 line_no,
223    const base::string16& source_id) {
224  JNIEnv* env = AttachCurrentThread();
225  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
226  if (obj.is_null())
227    return WebContentsDelegate::AddMessageToConsole(source, level, message,
228                                                    line_no, source_id);
229  ScopedJavaLocalRef<jstring> jmessage(ConvertUTF16ToJavaString(env, message));
230  ScopedJavaLocalRef<jstring> jsource_id(
231      ConvertUTF16ToJavaString(env, source_id));
232  int jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG;
233  switch (level) {
234    case logging::LOG_VERBOSE:
235      jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG;
236      break;
237    case logging::LOG_INFO:
238      jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_LOG;
239      break;
240    case logging::LOG_WARNING:
241      jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_WARNING;
242      break;
243    case logging::LOG_ERROR:
244      jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_ERROR;
245      break;
246    default:
247      NOTREACHED();
248  }
249  return Java_WebContentsDelegateAndroid_addMessageToConsole(
250      env,
251      GetJavaDelegate(env).obj(),
252      jlevel,
253      jmessage.obj(),
254      line_no,
255      jsource_id.obj());
256}
257
258// This is either called from TabContents::DidNavigateMainFramePostCommit() with
259// an empty GURL or responding to RenderViewHost::OnMsgUpateTargetURL(). In
260// Chrome, the latter is not always called, especially not during history
261// navigation. So we only handle the first case and pass the source TabContents'
262// url to Java to update the UI.
263void WebContentsDelegateAndroid::UpdateTargetURL(WebContents* source,
264                                                 int32 page_id,
265                                                 const GURL& url) {
266  if (!url.is_empty())
267    return;
268  JNIEnv* env = AttachCurrentThread();
269  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
270  if (obj.is_null())
271    return;
272  ScopedJavaLocalRef<jstring> java_url =
273      ConvertUTF8ToJavaString(env, source->GetURL().spec());
274  Java_WebContentsDelegateAndroid_onUpdateUrl(env,
275                                              obj.obj(),
276                                              java_url.obj());
277}
278
279void WebContentsDelegateAndroid::HandleKeyboardEvent(
280    WebContents* source,
281    const content::NativeWebKeyboardEvent& event) {
282  jobject key_event = event.os_event;
283  if (key_event) {
284    JNIEnv* env = AttachCurrentThread();
285    ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
286    if (obj.is_null())
287      return;
288    Java_WebContentsDelegateAndroid_handleKeyboardEvent(
289        env, obj.obj(), key_event);
290  }
291}
292
293bool WebContentsDelegateAndroid::TakeFocus(WebContents* source, bool reverse) {
294  JNIEnv* env = AttachCurrentThread();
295  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
296  if (obj.is_null())
297    return WebContentsDelegate::TakeFocus(source, reverse);
298  return Java_WebContentsDelegateAndroid_takeFocus(
299      env, obj.obj(), reverse);
300}
301
302void WebContentsDelegateAndroid::ShowRepostFormWarningDialog(
303    WebContents* source) {
304  JNIEnv* env = AttachCurrentThread();
305  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
306  if (obj.is_null())
307    return;
308  ScopedJavaLocalRef<jobject> content_view_core =
309      content::ContentViewCore::FromWebContents(source)->GetJavaObject();
310  if (content_view_core.is_null())
311    return;
312  Java_WebContentsDelegateAndroid_showRepostFormWarningDialog(env, obj.obj(),
313      content_view_core.obj());
314}
315
316void WebContentsDelegateAndroid::ToggleFullscreenModeForTab(
317    WebContents* web_contents,
318    bool enter_fullscreen) {
319  JNIEnv* env = AttachCurrentThread();
320  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
321  if (obj.is_null())
322    return;
323  Java_WebContentsDelegateAndroid_toggleFullscreenModeForTab(
324      env, obj.obj(), enter_fullscreen);
325}
326
327bool WebContentsDelegateAndroid::IsFullscreenForTabOrPending(
328    const WebContents* web_contents) const {
329  JNIEnv* env = AttachCurrentThread();
330  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
331  if (obj.is_null())
332    return false;
333  return Java_WebContentsDelegateAndroid_isFullscreenForTabOrPending(
334      env, obj.obj());
335}
336
337void WebContentsDelegateAndroid::DidProgrammaticallyScroll(
338    WebContents* web_contents, const gfx::Vector2d& scroll_point) {
339  JNIEnv* env = AttachCurrentThread();
340  ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
341  if (obj.is_null())
342    return;
343  Java_WebContentsDelegateAndroid_didProgrammaticallyScroll(
344      env, obj.obj(), scroll_point.x(), scroll_point.y());
345}
346
347// ----------------------------------------------------------------------------
348// Native JNI methods
349// ----------------------------------------------------------------------------
350
351// Register native methods
352
353bool RegisterWebContentsDelegateAndroid(JNIEnv* env) {
354  if (!HasClass(env, kWebContentsDelegateAndroidClassPath)) {
355    DLOG(ERROR) << "Unable to find class WebContentsDelegateAndroid!";
356    return false;
357  }
358  return RegisterNativesImpl(env);
359}
360
361}  // namespace web_contents_delegate_android
362