Move file chooser from RenderView(Host) to RenderFrame(Host).
The file chooser does not work with out-of-process iframes, since it works through RenderView(Host). It needs to be moved to user RenderFrame(Host) at which point it works for subframes in a process different from its parent.
BUG=615575
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_site_isolation
[email protected]
Review-Url: https://codereview.chromium.org/2050623005
Cr-Commit-Position: refs/heads/master@{#399373}
diff --git a/content/renderer/pepper/mock_renderer_ppapi_host.cc b/content/renderer/pepper/mock_renderer_ppapi_host.cc
index 0a4bc6e..6f39f0fb 100644
--- a/content/renderer/pepper/mock_renderer_ppapi_host.cc
+++ b/content/renderer/pepper/mock_renderer_ppapi_host.cc
@@ -4,6 +4,7 @@
#include "content/renderer/pepper/mock_renderer_ppapi_host.h"
+#include "content/public/renderer/render_view.h"
#include "content/renderer/pepper/fake_pepper_plugin_instance.h"
#include "ui/gfx/geometry/point.h"
@@ -16,7 +17,10 @@
render_view_(render_view),
pp_instance_(instance),
has_user_gesture_(false),
- plugin_instance_(new FakePepperPluginInstance) {}
+ plugin_instance_(new FakePepperPluginInstance) {
+ if (render_view)
+ render_frame_ = render_view->GetMainRenderFrame();
+}
MockRendererPpapiHost::~MockRendererPpapiHost() {}
@@ -35,6 +39,8 @@
RenderFrame* MockRendererPpapiHost::GetRenderFrameForInstance(
PP_Instance instance) const {
+ if (instance == pp_instance_)
+ return render_frame_;
return NULL;
}
diff --git a/content/renderer/pepper/mock_renderer_ppapi_host.h b/content/renderer/pepper/mock_renderer_ppapi_host.h
index 84e6ff4..30dd766 100644
--- a/content/renderer/pepper/mock_renderer_ppapi_host.h
+++ b/content/renderer/pepper/mock_renderer_ppapi_host.h
@@ -65,6 +65,7 @@
ppapi::host::PpapiHost ppapi_host_;
RenderView* render_view_;
+ RenderFrame* render_frame_;
PP_Instance pp_instance_;
bool has_user_gesture_;
diff --git a/content/renderer/pepper/pepper_file_chooser_host.cc b/content/renderer/pepper/pepper_file_chooser_host.cc
index b70ab83..f2f8be3b 100644
--- a/content/renderer/pepper/pepper_file_chooser_host.cc
+++ b/content/renderer/pepper/pepper_file_chooser_host.cc
@@ -154,9 +154,10 @@
params.requestor = renderer_ppapi_host_->GetDocumentURL(pp_instance());
handler_ = new CompletionHandler(AsWeakPtr());
- RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
- renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
- if (!render_view || !render_view->runFileChooser(params, handler_)) {
+ RenderFrameImpl* render_frame = static_cast<RenderFrameImpl*>(
+ renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance()));
+
+ if (!render_frame || !render_frame->runFileChooser(params, handler_)) {
delete handler_;
handler_ = NULL;
return PP_ERROR_NOACCESS;
diff --git a/content/renderer/pepper/pepper_file_chooser_host_unittest.cc b/content/renderer/pepper/pepper_file_chooser_host_unittest.cc
index 85d8c91..d610636 100644
--- a/content/renderer/pepper/pepper_file_chooser_host_unittest.cc
+++ b/content/renderer/pepper/pepper_file_chooser_host_unittest.cc
@@ -8,6 +8,7 @@
#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/common/file_chooser_file_info.h"
#include "content/public/common/file_chooser_params.h"
@@ -92,10 +93,10 @@
// The render view should have sent a chooser request to the browser
// (caught by the render thread's test message sink).
const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
- ViewHostMsg_RunFileChooser::ID);
+ FrameHostMsg_RunFileChooser::ID);
ASSERT_TRUE(msg);
- ViewHostMsg_RunFileChooser::Schema::Param call_msg_param;
- ASSERT_TRUE(ViewHostMsg_RunFileChooser::Read(msg, &call_msg_param));
+ FrameHostMsg_RunFileChooser::Schema::Param call_msg_param;
+ ASSERT_TRUE(FrameHostMsg_RunFileChooser::Read(msg, &call_msg_param));
const FileChooserParams& chooser_params = std::get<0>(call_msg_param);
// Basic validation of request.
@@ -110,10 +111,11 @@
selected_info.file_path = base::FilePath(FILE_PATH_LITERAL("myp\\ath/foo"));
std::vector<content::FileChooserFileInfo> selected_info_vector;
selected_info_vector.push_back(selected_info);
- RenderViewImpl* view_impl = static_cast<RenderViewImpl*>(view_);
- ViewMsg_RunFileChooserResponse response(view_impl->GetRoutingID(),
- selected_info_vector);
- EXPECT_TRUE(view_impl->OnMessageReceived(response));
+ RenderFrameImpl* frame_impl =
+ static_cast<RenderFrameImpl*>(view_->GetMainRenderFrame());
+ FrameMsg_RunFileChooserResponse response(frame_impl->GetRoutingID(),
+ selected_info_vector);
+ EXPECT_TRUE(frame_impl->OnMessageReceived(response));
// This should have sent the Pepper reply to our test sink.
ppapi::proxy::ResourceMessageReplyParams reply_params;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 13392d1..06b064f6 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -69,6 +69,8 @@
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/context_menu_params.h"
+#include "content/public/common/file_chooser_file_info.h"
+#include "content/public/common/file_chooser_params.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/page_state.h"
#include "content/public/common/resource_response.h"
@@ -151,6 +153,7 @@
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/http/http_util.h"
#include "storage/common/data_element.h"
+#include "third_party/WebKit/public/platform/FilePathConversion.h"
#include "third_party/WebKit/public/platform/URLConversion.h"
#include "third_party/WebKit/public/platform/WebCachePolicy.h"
#include "third_party/WebKit/public/platform/WebData.h"
@@ -851,6 +854,14 @@
} // namespace
+struct RenderFrameImpl::PendingFileChooser {
+ PendingFileChooser(const FileChooserParams& p,
+ blink::WebFileChooserCompletion* c)
+ : params(p), completion(c) {}
+ FileChooserParams params;
+ blink::WebFileChooserCompletion* completion; // MAY BE NULL to skip callback.
+};
+
// static
RenderFrameImpl* RenderFrameImpl::Create(RenderViewImpl* render_view,
int32_t routing_id) {
@@ -1114,6 +1125,15 @@
}
RenderFrameImpl::~RenderFrameImpl() {
+ // If file chooser is still waiting for answer, dispatch empty answer.
+ while (!file_chooser_completions_.empty()) {
+ if (file_chooser_completions_.front()->completion) {
+ file_chooser_completions_.front()->completion->didChooseFile(
+ WebVector<WebString>());
+ }
+ file_chooser_completions_.pop_front();
+ }
+
FOR_EACH_OBSERVER(RenderFrameObserver, observers_, RenderFrameGone());
FOR_EACH_OBSERVER(RenderFrameObserver, observers_, OnDestruct());
@@ -1505,6 +1525,7 @@
IPC_MESSAGE_HANDLER(FrameMsg_EnableViewSourceMode, OnEnableViewSourceMode)
IPC_MESSAGE_HANDLER(FrameMsg_SuppressFurtherDialogs,
OnSuppressFurtherDialogs)
+ IPC_MESSAGE_HANDLER(FrameMsg_RunFileChooserResponse, OnFileChooserResponse)
#if defined(OS_ANDROID)
IPC_MESSAGE_HANDLER(FrameMsg_ActivateNearestFindResult,
OnActivateNearestFindResult)
@@ -2218,6 +2239,31 @@
return success;
}
+bool RenderFrameImpl::ScheduleFileChooser(
+ const FileChooserParams& params,
+ blink::WebFileChooserCompletion* completion) {
+ static const size_t kMaximumPendingFileChooseRequests = 4;
+ if (file_chooser_completions_.size() > kMaximumPendingFileChooseRequests) {
+ // This sanity check prevents too many file choose requests from getting
+ // queued which could DoS the user. Getting these is most likely a
+ // programming error (there are many ways to DoS the user so it's not
+ // considered a "real" security check), either in JS requesting many file
+ // choosers to pop up, or in a plugin.
+ //
+ // TODO(brettw): We might possibly want to require a user gesture to open
+ // a file picker, which will address this issue in a better way.
+ return false;
+ }
+
+ file_chooser_completions_.push_back(
+ base::WrapUnique(new PendingFileChooser(params, completion)));
+ if (file_chooser_completions_.size() == 1) {
+ // Actually show the browse dialog when this is the first request.
+ Send(new FrameHostMsg_RunFileChooser(routing_id_, params));
+ }
+ return true;
+}
+
void RenderFrameImpl::LoadNavigationErrorPage(
const WebURLRequest& failed_request,
const WebURLError& error,
@@ -3744,6 +3790,37 @@
return success;
}
+bool RenderFrameImpl::runFileChooser(
+ const blink::WebFileChooserParams& params,
+ blink::WebFileChooserCompletion* chooser_completion) {
+ // Do not open the file dialog in a hidden RenderView.
+ if (render_view_->is_hidden())
+ return false;
+
+ FileChooserParams ipc_params;
+ if (params.directory)
+ ipc_params.mode = FileChooserParams::UploadFolder;
+ else if (params.multiSelect)
+ ipc_params.mode = FileChooserParams::OpenMultiple;
+ else if (params.saveAs)
+ ipc_params.mode = FileChooserParams::Save;
+ else
+ ipc_params.mode = FileChooserParams::Open;
+ ipc_params.title = params.title;
+ ipc_params.default_file_name =
+ blink::WebStringToFilePath(params.initialValue).BaseName();
+ ipc_params.accept_types.reserve(params.acceptTypes.size());
+ for (const auto& type : params.acceptTypes)
+ ipc_params.accept_types.push_back(type);
+ ipc_params.need_local_path = params.needLocalPath;
+#if defined(OS_ANDROID)
+ ipc_params.capture = params.useMediaCapture;
+#endif
+ ipc_params.requestor = params.requestor;
+
+ return ScheduleFileChooser(ipc_params, chooser_completion);
+}
+
void RenderFrameImpl::showContextMenu(const blink::WebContextMenuData& data) {
ContextMenuParams params = ContextMenuParamsBuilder::Build(data);
blink::WebRect position_in_window(params.x, params.y, 0, 0);
@@ -5129,6 +5206,43 @@
suppress_further_dialogs_ = true;
}
+void RenderFrameImpl::OnFileChooserResponse(
+ const std::vector<content::FileChooserFileInfo>& files) {
+ // This could happen if we navigated to a different page before the user
+ // closed the chooser.
+ if (file_chooser_completions_.empty())
+ return;
+
+ // Convert Chrome's SelectedFileInfo list to WebKit's.
+ WebVector<blink::WebFileChooserCompletion::SelectedFileInfo> selected_files(
+ files.size());
+ for (size_t i = 0; i < files.size(); ++i) {
+ blink::WebFileChooserCompletion::SelectedFileInfo selected_file;
+ selected_file.path = files[i].file_path.AsUTF16Unsafe();
+ selected_file.displayName =
+ base::FilePath(files[i].display_name).AsUTF16Unsafe();
+ if (files[i].file_system_url.is_valid()) {
+ selected_file.fileSystemURL = files[i].file_system_url;
+ selected_file.length = files[i].length;
+ selected_file.modificationTime = files[i].modification_time.ToDoubleT();
+ selected_file.isDirectory = files[i].is_directory;
+ }
+ selected_files[i] = selected_file;
+ }
+
+ if (file_chooser_completions_.front()->completion) {
+ file_chooser_completions_.front()->completion->didChooseFile(
+ selected_files);
+ }
+ file_chooser_completions_.pop_front();
+
+ // If there are more pending file chooser requests, schedule one now.
+ if (!file_chooser_completions_.empty()) {
+ Send(new FrameHostMsg_RunFileChooser(
+ routing_id_, file_chooser_completions_.front()->params));
+ }
+}
+
#if defined(OS_ANDROID)
void RenderFrameImpl::OnActivateNearestFindResult(int request_id,
float x,
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 261ed1c2..a53eb87 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -152,6 +152,8 @@
class WakeLockDispatcher;
struct CommonNavigationParams;
struct CustomContextMenuContext;
+struct FileChooserFileInfo;
+struct FileChooserParams;
struct FrameReplicationState;
struct NavigationParams;
struct RequestNavigationParams;
@@ -541,6 +543,9 @@
const blink::WebString& default_value,
blink::WebString* actual_value) override;
bool runModalBeforeUnloadDialog(bool is_reload) override;
+ bool runFileChooser(
+ const blink::WebFileChooserParams& params,
+ blink::WebFileChooserCompletion* chooser_completion) override;
void showContextMenu(const blink::WebContextMenuData& data) override;
void willSendRequest(blink::WebLocalFrame* frame,
unsigned identifier,
@@ -822,6 +827,8 @@
void OnStopFinding(StopFindAction action);
void OnEnableViewSourceMode();
void OnSuppressFurtherDialogs();
+ void OnFileChooserResponse(
+ const std::vector<content::FileChooserFileInfo>& files);
#if defined(OS_ANDROID)
void OnActivateNearestFindResult(int request_id, float x, float y);
void OnGetNearestFindResult(int request_id, float x, float y);
@@ -893,6 +900,15 @@
const GURL& frame_url,
base::string16* result);
+ // Adds the given file chooser request to the file_chooser_completion_ queue
+ // (see that var for more) and requests the chooser be displayed if there are
+ // no other waiting items in the queue.
+ //
+ // Returns true if the chooser was successfully scheduled. False means we
+ // didn't schedule anything.
+ bool ScheduleFileChooser(const FileChooserParams& params,
+ blink::WebFileChooserCompletion* completion);
+
// Loads the appropriate error page for the specified failure into the frame.
void LoadNavigationErrorPage(const blink::WebURLRequest& failed_request,
const blink::WebURLError& error,
@@ -1229,6 +1245,13 @@
// stack that interferes with swapping out.
bool suppress_further_dialogs_;
+ // The current and pending file chooser completion objects. If the queue is
+ // nonempty, the first item represents the currently running file chooser
+ // callback, and the remaining elements are the other file chooser completion
+ // still waiting to be run (in order).
+ struct PendingFileChooser;
+ std::deque<std::unique_ptr<PendingFileChooser>> file_chooser_completions_;
+
#if defined(USE_EXTERNAL_POPUP_MENU)
// The external popup for the currently showing select popup.
std::unique_ptr<ExternalPopupMenu> external_popup_menu_;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index b0a36e27..c2537c9 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -61,8 +61,6 @@
#include "content/public/common/content_switches.h"
#include "content/public/common/drop_data.h"
#include "content/public/common/favicon_url.h"
-#include "content/public/common/file_chooser_file_info.h"
-#include "content/public/common/file_chooser_params.h"
#include "content/public/common/page_importance_signals.h"
#include "content/public/common/page_state.h"
#include "content/public/common/page_zoom.h"
@@ -392,15 +390,6 @@
///////////////////////////////////////////////////////////////////////////////
-struct RenderViewImpl::PendingFileChooser {
- PendingFileChooser(const FileChooserParams& p, WebFileChooserCompletion* c)
- : params(p),
- completion(c) {
- }
- FileChooserParams params;
- WebFileChooserCompletion* completion; // MAY BE NULL to skip callback.
-};
-
namespace {
WebDragData DropDataToWebDragData(const DropData& drop_data) {
@@ -809,15 +798,6 @@
++it)
delete it->second;
- // If file chooser is still waiting for answer, dispatch empty answer.
- while (!file_chooser_completions_.empty()) {
- if (file_chooser_completions_.front()->completion) {
- file_chooser_completions_.front()->completion->didChooseFile(
- WebVector<WebString>());
- }
- file_chooser_completions_.pop_front();
- }
-
#if defined(OS_ANDROID)
// The date/time picker client is both a std::unique_ptr member of this class
// and a RenderViewObserver. Reset it to prevent double deletion.
@@ -1277,7 +1257,6 @@
IPC_MESSAGE_HANDLER(ViewMsg_UpdateWebPreferences, OnUpdateWebPreferences)
IPC_MESSAGE_HANDLER(ViewMsg_EnumerateDirectoryResponse,
OnEnumerateDirectoryResponse)
- IPC_MESSAGE_HANDLER(ViewMsg_RunFileChooserResponse, OnFileChooserResponse)
IPC_MESSAGE_HANDLER(ViewMsg_ClosePage, OnClosePage)
IPC_MESSAGE_HANDLER(ViewMsg_ThemeChanged, OnThemeChanged)
IPC_MESSAGE_HANDLER(ViewMsg_MoveOrResizeStarted, OnMoveOrResizeStarted)
@@ -1668,36 +1647,6 @@
return did_execute_command;
}
-bool RenderViewImpl::runFileChooser(
- const blink::WebFileChooserParams& params,
- WebFileChooserCompletion* chooser_completion) {
- // Do not open the file dialog in a hidden RenderView.
- if (is_hidden())
- return false;
- FileChooserParams ipc_params;
- if (params.directory)
- ipc_params.mode = FileChooserParams::UploadFolder;
- else if (params.multiSelect)
- ipc_params.mode = FileChooserParams::OpenMultiple;
- else if (params.saveAs)
- ipc_params.mode = FileChooserParams::Save;
- else
- ipc_params.mode = FileChooserParams::Open;
- ipc_params.title = params.title;
- ipc_params.default_file_name =
- blink::WebStringToFilePath(params.initialValue).BaseName();
- ipc_params.accept_types.reserve(params.acceptTypes.size());
- for (size_t i = 0; i < params.acceptTypes.size(); ++i)
- ipc_params.accept_types.push_back(params.acceptTypes[i]);
- ipc_params.need_local_path = params.needLocalPath;
-#if defined(OS_ANDROID)
- ipc_params.capture = params.useMediaCapture;
-#endif
- ipc_params.requestor = params.requestor;
-
- return ScheduleFileChooser(ipc_params, chooser_completion);
-}
-
void RenderViewImpl::SetValidationMessageDirection(
base::string16* wrapped_main_text,
blink::WebTextDirection main_text_hint,
@@ -2444,42 +2393,6 @@
enumeration_completions_.erase(id);
}
-void RenderViewImpl::OnFileChooserResponse(
- const std::vector<content::FileChooserFileInfo>& files) {
- // This could happen if we navigated to a different page before the user
- // closed the chooser.
- if (file_chooser_completions_.empty())
- return;
-
- // Convert Chrome's SelectedFileInfo list to WebKit's.
- WebVector<WebFileChooserCompletion::SelectedFileInfo> selected_files(
- files.size());
- for (size_t i = 0; i < files.size(); ++i) {
- WebFileChooserCompletion::SelectedFileInfo selected_file;
- selected_file.path = files[i].file_path.AsUTF16Unsafe();
- selected_file.displayName =
- base::FilePath(files[i].display_name).AsUTF16Unsafe();
- if (files[i].file_system_url.is_valid()) {
- selected_file.fileSystemURL = files[i].file_system_url;
- selected_file.length = files[i].length;
- selected_file.modificationTime = files[i].modification_time.ToDoubleT();
- selected_file.isDirectory = files[i].is_directory;
- }
- selected_files[i] = selected_file;
- }
-
- if (file_chooser_completions_.front()->completion)
- file_chooser_completions_.front()->completion->didChooseFile(
- selected_files);
- file_chooser_completions_.pop_front();
-
- // If there are more pending file chooser requests, schedule one now.
- if (!file_chooser_completions_.empty()) {
- Send(new ViewHostMsg_RunFileChooser(
- GetRoutingID(), file_chooser_completions_.front()->params));
- }
-}
-
void RenderViewImpl::OnEnableAutoResize(const gfx::Size& min_size,
const gfx::Size& max_size) {
DCHECK(disable_scrollbars_size_limit_.IsEmpty());
@@ -2958,31 +2871,6 @@
}
}
-bool RenderViewImpl::ScheduleFileChooser(
- const FileChooserParams& params,
- WebFileChooserCompletion* completion) {
- static const size_t kMaximumPendingFileChooseRequests = 4;
- if (file_chooser_completions_.size() > kMaximumPendingFileChooseRequests) {
- // This sanity check prevents too many file choose requests from getting
- // queued which could DoS the user. Getting these is most likely a
- // programming error (there are many ways to DoS the user so it's not
- // considered a "real" security check), either in JS requesting many file
- // choosers to pop up, or in a plugin.
- //
- // TODO(brettw) we might possibly want to require a user gesture to open
- // a file picker, which will address this issue in a better way.
- return false;
- }
-
- file_chooser_completions_.push_back(
- base::WrapUnique(new PendingFileChooser(params, completion)));
- if (file_chooser_completions_.size() == 1) {
- // Actually show the browse dialog when this is the first request.
- Send(new ViewHostMsg_RunFileChooser(GetRoutingID(), params));
- }
- return true;
-}
-
blink::WebSpeechRecognizer* RenderViewImpl::speechRecognizer() {
if (!speech_recognition_dispatcher_)
speech_recognition_dispatcher_ = new SpeechRecognitionDispatcher(this);
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 636f1c2e4..e60e4c3 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -341,9 +341,6 @@
void saveImageFromDataURL(const blink::WebString& data_url) override;
void didCancelCompositionOnSelectionChange() override;
bool handleCurrentKeyboardEvent() override;
- bool runFileChooser(
- const blink::WebFileChooserParams& params,
- blink::WebFileChooserCompletion* chooser_completion) override;
void SetValidationMessageDirection(base::string16* main_text,
blink::WebTextDirection main_text_hint,
base::string16* sub_text,
@@ -637,8 +634,6 @@
void OnDisableAutoResize(const gfx::Size& new_size);
void OnEnumerateDirectoryResponse(int id,
const std::vector<base::FilePath>& paths);
- void OnFileChooserResponse(
- const std::vector<content::FileChooserFileInfo>& files);
void OnMediaPlayerActionAt(const gfx::Point& location,
const blink::WebMediaPlayerAction& action);
void OnPluginActionAt(const gfx::Point& location,
@@ -910,13 +905,6 @@
// Misc ----------------------------------------------------------------------
- // The current and pending file chooser completion objects. If the queue is
- // nonempty, the first item represents the currently running file chooser
- // callback, and the remaining elements are the other file chooser completion
- // still waiting to be run (in order).
- struct PendingFileChooser;
- std::deque<std::unique_ptr<PendingFileChooser>> file_chooser_completions_;
-
// The current directory enumeration callback
std::map<int, blink::WebFileChooserCompletion*> enumeration_completions_;
int enumeration_completion_id_;