// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "android_webview/native/aw_web_contents_delegate.h" #include "android_webview/browser/aw_javascript_dialog_manager.h" #include "android_webview/browser/find_helper.h" #include "android_webview/native/aw_contents.h" #include "android_webview/native/aw_contents_io_thread_client_impl.h" #include "android_webview/native/permission/media_access_permission_request.h" #include "android_webview/native/permission/permission_request_handler.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/lazy_instance.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/file_chooser_file_info.h" #include "content/public/common/file_chooser_params.h" #include "content/public/common/media_stream_request.h" #include "jni/AwWebContentsDelegate_jni.h" using base::android::AttachCurrentThread; using base::android::ConvertUTF16ToJavaString; using base::android::ConvertUTF8ToJavaString; using base::android::ScopedJavaLocalRef; using content::FileChooserParams; using content::WebContents; namespace android_webview { namespace { // WARNING: these constants are exposed in the public interface Java side, so // must remain in sync with what clients are expecting. const int kFileChooserModeOpenMultiple = 1 << 0; const int kFileChooserModeOpenFolder = 1 << 1; base::LazyInstance::Leaky g_javascript_dialog_manager = LAZY_INSTANCE_INITIALIZER; } AwWebContentsDelegate::AwWebContentsDelegate( JNIEnv* env, jobject obj) : WebContentsDelegateAndroid(env, obj), is_fullscreen_(false) { } AwWebContentsDelegate::~AwWebContentsDelegate() { } content::JavaScriptDialogManager* AwWebContentsDelegate::GetJavaScriptDialogManager(WebContents* source) { return g_javascript_dialog_manager.Pointer(); } void AwWebContentsDelegate::FindReply(WebContents* web_contents, int request_id, int number_of_matches, const gfx::Rect& selection_rect, int active_match_ordinal, bool final_update) { AwContents* aw_contents = AwContents::FromWebContents(web_contents); if (!aw_contents) return; aw_contents->GetFindHelper()->HandleFindReply(request_id, number_of_matches, active_match_ordinal, final_update); } void AwWebContentsDelegate::CanDownload( content::RenderViewHost* source, const GURL& url, const std::string& request_method, const base::Callback& callback) { // Android webview intercepts download in its resource dispatcher host // delegate, so should not reach here. NOTREACHED(); callback.Run(false); } void AwWebContentsDelegate::RunFileChooser(WebContents* web_contents, const FileChooserParams& params) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef java_delegate = GetJavaDelegate(env); if (!java_delegate.obj()) return; int mode_flags = 0; if (params.mode == FileChooserParams::OpenMultiple) { mode_flags |= kFileChooserModeOpenMultiple; } else if (params.mode == FileChooserParams::UploadFolder) { // Folder implies multiple in Chrome. mode_flags |= kFileChooserModeOpenMultiple | kFileChooserModeOpenFolder; } else if (params.mode == FileChooserParams::Save) { // Save not supported, so cancel it. web_contents->GetRenderViewHost()->FilesSelectedInChooser( std::vector(), params.mode); return; } else { DCHECK_EQ(FileChooserParams::Open, params.mode); } Java_AwWebContentsDelegate_runFileChooser(env, java_delegate.obj(), web_contents->GetRenderProcessHost()->GetID(), web_contents->GetRenderViewHost()->GetRoutingID(), mode_flags, ConvertUTF16ToJavaString(env, JoinString(params.accept_types, ',')).obj(), params.title.empty() ? NULL : ConvertUTF16ToJavaString(env, params.title).obj(), params.default_file_name.empty() ? NULL : ConvertUTF8ToJavaString(env, params.default_file_name.value()).obj(), params.capture); } void AwWebContentsDelegate::AddNewContents(WebContents* source, WebContents* new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_rect, bool user_gesture, bool* was_blocked) { JNIEnv* env = AttachCurrentThread(); bool is_dialog = disposition == NEW_POPUP; ScopedJavaLocalRef java_delegate = GetJavaDelegate(env); bool create_popup = false; if (java_delegate.obj()) { create_popup = Java_AwWebContentsDelegate_addNewContents(env, java_delegate.obj(), is_dialog, user_gesture); } if (create_popup) { // The embedder would like to display the popup and we will receive // a callback from them later with an AwContents to use to display // it. The source AwContents takes ownership of the new WebContents // until then, and when the callback is made we will swap the WebContents // out into the new AwContents. AwContents::FromWebContents(source)->SetPendingWebContentsForPopup( make_scoped_ptr(new_contents)); // Hide the WebContents for the pop up now, we will show it again // when the user calls us back with an AwContents to use to show it. new_contents->WasHidden(); } else { // The embedder has forgone their chance to display this popup // window, so we're done with the WebContents now. We use // DeleteSoon as WebContentsImpl may call methods on |new_contents| // after this method returns. base::MessageLoop::current()->DeleteSoon(FROM_HERE, new_contents); } if (was_blocked) { *was_blocked = !create_popup; } } // Notifies the delegate about the creation of a new WebContents. This // typically happens when popups are created. void AwWebContentsDelegate::WebContentsCreated( WebContents* source_contents, int opener_render_frame_id, const base::string16& frame_name, const GURL& target_url, content::WebContents* new_contents) { AwContentsIoThreadClientImpl::RegisterPendingContents(new_contents); } void AwWebContentsDelegate::CloseContents(WebContents* source) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef java_delegate = GetJavaDelegate(env); if (java_delegate.obj()) { Java_AwWebContentsDelegate_closeContents(env, java_delegate.obj()); } } void AwWebContentsDelegate::ActivateContents(WebContents* contents) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef java_delegate = GetJavaDelegate(env); if (java_delegate.obj()) { Java_AwWebContentsDelegate_activateContents(env, java_delegate.obj()); } } void AwWebContentsDelegate::RequestMediaAccessPermission( WebContents* web_contents, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) { AwContents* aw_contents = AwContents::FromWebContents(web_contents); if (!aw_contents) { callback.Run(content::MediaStreamDevices(), content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, scoped_ptr().Pass()); return; } aw_contents->GetPermissionRequestHandler()->SendRequest( scoped_ptr( new MediaAccessPermissionRequest(request, callback))); } void AwWebContentsDelegate::EnterFullscreenModeForTab( content::WebContents* web_contents, const GURL& origin) { WebContentsDelegateAndroid::EnterFullscreenModeForTab(web_contents, origin); is_fullscreen_ = true; web_contents->GetRenderViewHost()->WasResized(); } void AwWebContentsDelegate::ExitFullscreenModeForTab( content::WebContents* web_contents) { WebContentsDelegateAndroid::ExitFullscreenModeForTab(web_contents); is_fullscreen_ = false; web_contents->GetRenderViewHost()->WasResized(); } bool AwWebContentsDelegate::IsFullscreenForTabOrPending( const content::WebContents* web_contents) const { return is_fullscreen_; } static void FilesSelectedInChooser( JNIEnv* env, jclass clazz, jint process_id, jint render_id, jint mode_flags, jobjectArray file_paths, jobjectArray display_names) { content::RenderViewHost* rvh = content::RenderViewHost::FromID(process_id, render_id); if (!rvh) return; std::vector file_path_str; std::vector display_name_str; // Note file_paths maybe NULL, but this will just yield a zero-length vector. base::android::AppendJavaStringArrayToStringVector(env, file_paths, &file_path_str); base::android::AppendJavaStringArrayToStringVector(env, display_names, &display_name_str); std::vector files; files.reserve(file_path_str.size()); for (size_t i = 0; i < file_path_str.size(); ++i) { GURL url(file_path_str[i]); if (!url.is_valid()) continue; base::FilePath path(url.SchemeIsFile() ? url.path() : file_path_str[i]); content::FileChooserFileInfo file_info; file_info.file_path = path; if (!display_name_str[i].empty()) file_info.display_name = display_name_str[i]; files.push_back(file_info); } FileChooserParams::Mode mode; if (mode_flags & kFileChooserModeOpenFolder) { mode = FileChooserParams::UploadFolder; } else if (mode_flags & kFileChooserModeOpenMultiple) { mode = FileChooserParams::OpenMultiple; } else { mode = FileChooserParams::Open; } DVLOG(0) << "File Chooser result: mode = " << mode << ", file paths = " << JoinString(file_path_str, ":"); rvh->FilesSelectedInChooser(files, mode); } bool RegisterAwWebContentsDelegate(JNIEnv* env) { return RegisterNativesImpl(env); } } // namespace android_webview