diff options
18 files changed, 416 insertions, 93 deletions
diff --git a/chrome/browser/renderer_host/mock_render_process_host.cc b/chrome/browser/renderer_host/mock_render_process_host.cc index b18cc50..21fa34d 100644 --- a/chrome/browser/renderer_host/mock_render_process_host.cc +++ b/chrome/browser/renderer_host/mock_render_process_host.cc @@ -4,13 +4,19 @@ #include "chrome/browser/renderer_host/mock_render_process_host.h" +#include "chrome/browser/child_process_security_policy.h" + MockRenderProcessHost::MockRenderProcessHost(Profile* profile) : RenderProcessHost(profile), transport_dib_(NULL), bad_msg_count_(0) { + // Child process security operations can't be unit tested unless we add + // ourselves as an existing child process. + ChildProcessSecurityPolicy::GetInstance()->Add(id()); } MockRenderProcessHost::~MockRenderProcessHost() { + ChildProcessSecurityPolicy::GetInstance()->Remove(id()); delete transport_dib_; } diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc index c8c4df0..ddc754f 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -657,16 +657,9 @@ void RenderViewHost::InstallMissingPlugin() { Send(new ViewMsg_InstallMissingPlugin(routing_id())); } -void RenderViewHost::FileSelected(const FilePath& path) { - ChildProcessSecurityPolicy::GetInstance()->GrantUploadFile( - process()->id(), path); - std::vector<FilePath> files; - files.push_back(path); - Send(new ViewMsg_RunFileChooserResponse(routing_id(), files)); -} - -void RenderViewHost::MultiFilesSelected( - const std::vector<FilePath>& files) { +void RenderViewHost::FilesSelectedInChooser( + const std::vector<FilePath>& files) { + // Grant the security access requested to the given files. for (std::vector<FilePath>::const_iterator file = files.begin(); file != files.end(); ++file) { ChildProcessSecurityPolicy::GetInstance()->GrantUploadFile( @@ -1316,10 +1309,9 @@ void RenderViewHost::OnMsgSelectionChanged(const std::string& text) { view()->SelectionChanged(text); } -void RenderViewHost::OnMsgRunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file) { - delegate_->RunFileChooser(multiple_files, title, default_file); +void RenderViewHost::OnMsgRunFileChooser( + const ViewHostMsg_RunFileChooser_Params& params) { + delegate_->RunFileChooser(params); } void RenderViewHost::OnMsgRunJavaScriptMessage( diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h index d628a79..831c446 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -34,6 +34,7 @@ struct ContextMenuParams; struct MediaPlayerAction; struct ThumbnailScore; struct ViewHostMsg_DidPrintPage_Params; +struct ViewHostMsg_RunFileChooser_Params; struct ViewMsg_Navigate_Params; struct WebDropData; struct WebPreferences; @@ -355,13 +356,9 @@ class RenderViewHost : public RenderWidgetHost { const std::vector<FilePath>& local_paths, const FilePath& local_directory_name); - // Notifies the RenderViewHost that a file has been chosen by the user from - // an Open File dialog for the form. - void FileSelected(const FilePath& path); - - // Notifies the Listener that many files have been chosen by the user from - // an Open File dialog for the form. - void MultiFilesSelected(const std::vector<FilePath>& files); + // Notifies the Listener that one or more files have been chosen by the user + // from an Open File dialog for the form. + void FilesSelectedInChooser(const std::vector<FilePath>& files); // Notifies the RenderViewHost that its load state changed. void LoadStateChanged(const GURL& url, net::LoadState load_state, @@ -531,9 +528,7 @@ class RenderViewHost : public RenderWidgetHost { WebKit::WebTextDirection text_direction_hint); void OnMsgSelectionChanged(const std::string& text); void OnMsgPasteFromSelectionClipboard(); - void OnMsgRunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file); + void OnMsgRunFileChooser(const ViewHostMsg_RunFileChooser_Params& params); void OnMsgRunJavaScriptMessage(const std::wstring& message, const std::wstring& default_prompt, const GURL& frame_url, @@ -621,7 +616,6 @@ class RenderViewHost : public RenderWidgetHost { const std::string& original_lang, const std::string& translated_lang, TranslateErrors::Type error_type); - void OnContentBlocked(ContentSettingsType type); private: diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h index 8c9127b..1f93d31 100644 --- a/chrome/browser/renderer_host/render_view_host_delegate.h +++ b/chrome/browser/renderer_host/render_view_host_delegate.h @@ -39,6 +39,7 @@ struct ThumbnailScore; class Value; struct ViewHostMsg_DidPrintPage_Params; struct ViewHostMsg_FrameNavigate_Params; +struct ViewHostMsg_RunFileChooser_Params; struct WebDropData; class WebKeyboardEvent; struct WebPreferences; @@ -556,9 +557,8 @@ class RenderViewHostDelegate { const std::string& target) {} // A file chooser should be shown. - virtual void RunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file) {} + virtual void RunFileChooser( + const ViewHostMsg_RunFileChooser_Params& params) {} // A javascript message, confirmation or prompt should be shown. virtual void RunJavaScriptMessage(const std::wstring& message, diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index a69fd78..4c6e2e6 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -325,7 +325,6 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, const std::string& extension_id, const std::string& default_locale, IPC::Message* reply_msg); - void OnTranslateText(ViewHostMsg_TranslateTextParam param); void OnEstablishGpuChannel(); diff --git a/chrome/browser/renderer_host/test/site_instance_unittest.cc b/chrome/browser/renderer_host/test/site_instance_unittest.cc index 7206476..2c99835 100644 --- a/chrome/browser/renderer_host/test/site_instance_unittest.cc +++ b/chrome/browser/renderer_host/test/site_instance_unittest.cc @@ -425,7 +425,6 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) { // Create some extension instances and make sure they share a process. scoped_refptr<SiteInstance> extension1_instance( CreateSiteInstance(&rph_factory, GURL("chrome-extension://foo/bar"))); - policy->Add(extension1_instance->GetProcess()->id()); policy->GrantExtensionBindings(extension1_instance->GetProcess()->id()); scoped_refptr<SiteInstance> extension2_instance( @@ -439,7 +438,6 @@ TEST_F(SiteInstanceTest, ProcessSharingByType) { // Create some DOMUI instances and make sure they share a process. scoped_refptr<SiteInstance> dom1_instance( CreateSiteInstance(&rph_factory, GURL("chrome://newtab"))); - policy->Add(dom1_instance->GetProcess()->id()); policy->GrantDOMUIBindings(dom1_instance->GetProcess()->id()); scoped_refptr<SiteInstance> dom2_instance( diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc index 3bbbbf9..55eaadd 100644 --- a/chrome/browser/tab_contents/tab_contents.cc +++ b/chrome/browser/tab_contents/tab_contents.cc @@ -2468,15 +2468,27 @@ void TabContents::ProcessExternalHostMessage(const std::string& message, delegate()->ForwardMessageToExternalHost(message, origin, target); } -void TabContents::RunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file) { +void TabContents::RunFileChooser( + const ViewHostMsg_RunFileChooser_Params ¶ms) { if (!select_file_dialog_.get()) select_file_dialog_ = SelectFileDialog::Create(this); - SelectFileDialog::Type dialog_type = - multiple_files ? SelectFileDialog::SELECT_OPEN_MULTI_FILE : - SelectFileDialog::SELECT_OPEN_FILE; - select_file_dialog_->SelectFile(dialog_type, title, default_file, + + SelectFileDialog::Type dialog_type; + switch (params.mode) { + case ViewHostMsg_RunFileChooser_Params::Open: + dialog_type = SelectFileDialog::SELECT_OPEN_FILE; + break; + case ViewHostMsg_RunFileChooser_Params::OpenMultiple: + dialog_type = SelectFileDialog::SELECT_OPEN_MULTI_FILE; + break; + case ViewHostMsg_RunFileChooser_Params::Save: + dialog_type = SelectFileDialog::SELECT_SAVEAS_FILE; + break; + default: + NOTREACHED(); + } + select_file_dialog_->SelectFile(dialog_type, params.title, + params.default_file_name, NULL, 0, FILE_PATH_LITERAL(""), view_->GetTopLevelNativeWindow(), NULL); } @@ -2732,18 +2744,20 @@ void TabContents::FocusedNodeChanged() { void TabContents::FileSelected(const FilePath& path, int index, void* params) { - render_view_host()->FileSelected(path); + std::vector<FilePath> files; + files.push_back(path); + render_view_host()->FilesSelectedInChooser(files); } void TabContents::MultiFilesSelected(const std::vector<FilePath>& files, void* params) { - render_view_host()->MultiFilesSelected(files); + render_view_host()->FilesSelectedInChooser(files); } void TabContents::FileSelectionCanceled(void* params) { // If the user cancels choosing a file to upload we pass back an // empty vector. - render_view_host()->MultiFilesSelected(std::vector<FilePath>()); + render_view_host()->FilesSelectedInChooser(std::vector<FilePath>()); } void TabContents::BeforeUnloadFiredFromRenderManager( diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h index 385ed6d..62547d6 100644 --- a/chrome/browser/tab_contents/tab_contents.h +++ b/chrome/browser/tab_contents/tab_contents.h @@ -87,8 +87,9 @@ class SiteInstance; class TabContents; class TabContentsView; struct ThumbnailScore; -struct ViewHostMsg_FrameNavigate_Params; struct ViewHostMsg_DidPrintPage_Params; +struct ViewHostMsg_FrameNavigate_Params; +struct ViewHostMsg_RunFileChooser_Params; // Describes what goes in the main content area of a tab. TabContents is // the only type of TabContents, and these should be merged together. @@ -916,9 +917,7 @@ class TabContents : public PageNavigator, virtual void ProcessExternalHostMessage(const std::string& message, const std::string& origin, const std::string& target); - virtual void RunFileChooser(bool multiple_files, - const string16& title, - const FilePath& default_file); + virtual void RunFileChooser(const ViewHostMsg_RunFileChooser_Params& params); virtual void RunJavaScriptMessage(const std::wstring& message, const std::wstring& default_prompt, const GURL& frame_url, diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 808f668..46cd37b 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -631,6 +631,29 @@ struct ViewHostMsg_TranslateTextParam { bool secure; }; +struct ViewHostMsg_RunFileChooser_Params { + enum Mode { + // Requires that the file exists before allowing the user to pick it. + Open, + + // Like Open, but allows picking multiple files to open. + OpenMultiple, + + // Allows picking a nonexistant file, and prompts to overwrite if the file + // already exists. + Save, + }; + + Mode mode; + + // Title to be used for the dialog. This may be empty for the default title, + // which will be either "Open" or "Save" depending on the mode. + string16 title; + + // Default file name to select in the dialog. + FilePath default_file_name; +}; + namespace IPC { template <> @@ -2666,6 +2689,47 @@ struct SimilarTypeTraits<TranslateErrors::Type> { typedef int Type; }; +template<> +struct ParamTraits<ViewHostMsg_RunFileChooser_Params> { + typedef ViewHostMsg_RunFileChooser_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.mode)); + WriteParam(m, p.title); + WriteParam(m, p.default_file_name); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int mode; + if (!ReadParam(m, iter, &mode)) + return false; + if (mode != param_type::Open && + mode != param_type::OpenMultiple && + mode != param_type::Save) + return false; + p->mode = static_cast<param_type::Mode>(mode); + return + ReadParam(m, iter, &p->title) && + ReadParam(m, iter, &p->default_file_name); + }; + static void Log(const param_type& p, std::wstring* l) { + switch (p.mode) { + case param_type::Open: + l->append(L"(Open, "); + break; + case param_type::OpenMultiple: + l->append(L"(OpenMultiple, "); + break; + case param_type::Save: + l->append(L"(Save, "); + break; + default: + l->append(L"(UNKNOWN, "); + } + LogParam(p.title, l); + l->append(L", "); + LogParam(p.default_file_name, l); + } +}; + } // namespace IPC diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 94f2fcb..bf2ac2b 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1428,10 +1428,8 @@ IPC_BEGIN_MESSAGES(ViewHost) // Asks the browser to display the file chooser. The result is returned in a // ViewHost_RunFileChooserResponse message. - IPC_MESSAGE_ROUTED3(ViewHostMsg_RunFileChooser, - bool /* multiple_files */, - string16 /* title */, - FilePath /* Default file name */) + IPC_MESSAGE_ROUTED1(ViewHostMsg_RunFileChooser, + ViewHostMsg_RunFileChooser_Params) // Notification that forms have been seen that are candidates for // filling/submitting by the AutoFillManager. diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 9698299..bff25a9 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -155,6 +155,7 @@ using WebKit::WebDragData; using WebKit::WebDragOperation; using WebKit::WebDragOperationsMask; using WebKit::WebEditingAction; +using WebKit::WebFileChooserCompletion; using WebKit::WebFindOptions; using WebKit::WebFormElement; using WebKit::WebFrame; @@ -293,6 +294,16 @@ static double CalculateBoringScore(SkBitmap* bitmap) { int32 RenderView::next_page_id_ = 1; +struct RenderView::PendingFileChooser { + PendingFileChooser(const ViewHostMsg_RunFileChooser_Params& p, + WebFileChooserCompletion* c) + : params(p), + completion(c) { + } + ViewHostMsg_RunFileChooser_Params params; + WebFileChooserCompletion* completion; // MAY BE NULL to skip callback. +}; + RenderView::RenderView(RenderThreadBase* render_thread, const WebPreferences& webkit_preferences, int64 session_storage_namespace_id) @@ -309,7 +320,6 @@ RenderView::RenderView(RenderThreadBase* render_thread, ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), devtools_agent_(NULL), devtools_client_(NULL), - file_chooser_completion_(NULL), history_list_offset_(-1), history_list_length_(0), has_unload_listener_(false), @@ -349,8 +359,13 @@ RenderView::~RenderView() { } // If file chooser is still waiting for answer, dispatch empty answer. - if (file_chooser_completion_) - file_chooser_completion_->didChooseFile(WebVector<WebString>()); + 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_MACOSX) // Tell the spellchecker that the document is closed. @@ -1743,21 +1758,16 @@ void RenderView::updateSpellingUIWithMisspelledWord(const WebString& word) { bool RenderView::runFileChooser( const WebKit::WebFileChooserParams& params, - WebKit::WebFileChooserCompletion* chooser_completion) { - if (file_chooser_completion_) { - // TODO(brettw): bug 1235154: This should be a synchronous message to deal - // with the fact that web pages can programatically trigger this. With the - // asnychronous messages, we can get an additional call when one is pending, - // which this test is for. For now, we just ignore the additional file - // chooser request. WebKit doesn't do anything to expect the callback, so - // we can just ignore calling it. - return false; - } - file_chooser_completion_ = chooser_completion; - Send(new ViewHostMsg_RunFileChooser( - routing_id_, params.multiSelect, params.title, - webkit_glue::WebStringToFilePath(params.initialValue))); - return true; + WebFileChooserCompletion* chooser_completion) { + ViewHostMsg_RunFileChooser_Params ipc_params; + ipc_params.mode = params.multiSelect ? + ViewHostMsg_RunFileChooser_Params::OpenMultiple : + ViewHostMsg_RunFileChooser_Params::Open; + ipc_params.title = params.title; + ipc_params.default_file_name = + webkit_glue::WebStringToFilePath(params.initialValue); + + return ScheduleFileChooser(ipc_params, chooser_completion); } void RenderView::runModalAlertDialog( @@ -3656,6 +3666,24 @@ void RenderView::OnPepperPluginDestroy( return; } current_pepper_plugins_.erase(found_pepper); + + // The plugin could have been destroyed while it was waiting for a file + // choose callback, so check all pending completion callbacks and NULL them. + for (std::deque< linked_ptr<PendingFileChooser> >::iterator i = + file_chooser_completions_.begin(); + i != file_chooser_completions_.end(); /* nothing */) { + if ((*i)->completion == pepper_plugin) { + // We NULL the first one instead of deleting it because the plugin might + // be the one waiting for a file choose callback. If the callback later + // comes, we don't want to send the result to the next callback in line. + if (i == file_chooser_completions_.begin()) + (*i)->completion = NULL; + else + i = file_chooser_completions_.erase(i); + } else { + ++i; + } + } } void RenderView::OnScriptEvalRequest(const std::wstring& frame_xpath, @@ -3785,21 +3813,25 @@ void RenderView::OnInstallMissingPlugin() { first_default_plugin_->InstallMissingPlugin(); } -void RenderView::OnFileChooserResponse( - const std::vector<FilePath>& file_names) { +void RenderView::OnFileChooserResponse(const std::vector<FilePath>& paths) { // This could happen if we navigated to a different page before the user // closed the chooser. - if (!file_chooser_completion_) + if (file_chooser_completions_.empty()) return; - WebVector<WebString> ws_file_names(file_names.size()); - for (size_t i = 0; i < file_names.size(); ++i) { - ws_file_names[i] = webkit_glue::FilePathToWebString(file_names[i]); - } + WebVector<WebString> ws_file_names(paths.size()); + for (size_t i = 0; i < paths.size(); ++i) + ws_file_names[i] = webkit_glue::FilePathToWebString(paths[i]); + + if (file_chooser_completions_.front()->completion) + file_chooser_completions_.front()->completion->didChooseFile(ws_file_names); + file_chooser_completions_.pop_front(); - file_chooser_completion_->didChooseFile(ws_file_names); - // Reset the chooser pointer - file_chooser_completion_ = NULL; + // If there are more pending file chooser requests, schedule one now. + if (!file_chooser_completions_.empty()) { + Send(new ViewHostMsg_RunFileChooser(routing_id_, + file_chooser_completions_.front()->params)); + } } void RenderView::OnEnableViewSourceMode() { @@ -4889,6 +4921,31 @@ void RenderView::AcceleratedSurfaceBuffersSwapped( } #endif +bool RenderView::ScheduleFileChooser( + const ViewHostMsg_RunFileChooser_Params& 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(linked_ptr<PendingFileChooser>( + 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(routing_id_, params)); + } + return true; +} + WebKit::WebGeolocationServiceInterface* RenderView::getGeolocationService() { if (!geolocation_dispatcher_.get()) geolocation_dispatcher_.reset(new GeolocationDispatcher(this)); diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 5ef764c..c390a09 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -5,6 +5,7 @@ #ifndef CHROME_RENDERER_RENDER_VIEW_H_ #define CHROME_RENDERER_RENDER_VIEW_H_ +#include <deque> #include <map> #include <set> #include <string> @@ -507,6 +508,15 @@ class RenderView : public RenderWidget, void AcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window); #endif + // 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 ViewHostMsg_RunFileChooser_Params& params, + WebKit::WebFileChooserCompletion* completion); + protected: // RenderWidget overrides: virtual void Close(); @@ -697,7 +707,7 @@ class RenderView : public RenderWidget, WebKit::WebDragOperation drag_operation); void OnDragSourceSystemDragEnded(); void OnInstallMissingPlugin(); - void OnFileChooserResponse(const std::vector<FilePath>& file_names); + void OnFileChooserResponse(const std::vector<FilePath>& paths); void OnEnableViewSourceMode(); void OnEnablePreferredSizeChangedMode(); void OnDisableScrollbarsForSmallWindows( @@ -1009,9 +1019,12 @@ class RenderView : public RenderWidget, // render views. scoped_ptr<DevToolsClient> devtools_client_; - // A pointer to a file chooser completion object. When not empty, file - // choosing operation is underway. - WebKit::WebFileChooserCompletion* file_chooser_completion_; + // 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< linked_ptr<PendingFileChooser> > file_chooser_completions_; int history_list_offset_; int history_list_length_; diff --git a/chrome/renderer/webplugin_delegate_pepper.cc b/chrome/renderer/webplugin_delegate_pepper.cc index 37b169d..9c4db6c 100644 --- a/chrome/renderer/webplugin_delegate_pepper.cc +++ b/chrome/renderer/webplugin_delegate_pepper.cc @@ -146,6 +146,30 @@ WebPluginDelegatePepper* WebPluginDelegatePepper::Create( instance.get()); } +void WebPluginDelegatePepper::didChooseFile( + const WebKit::WebVector<WebKit::WebString>& file_names) { + if (file_names.isEmpty()) { + current_choose_file_callback_(NULL, 0, current_choose_file_user_data_); + } else { + // Construct a bunch of 8-bit strings for the callback. + std::vector<std::string> file_strings; + file_strings.resize(file_names.size()); + for (size_t i = 0; i < file_names.size(); i++) + file_strings[i] = file_names[0].utf8(); + + // Construct an array of pointers to each of the strings. + std::vector<const char*> pointers_to_strings; + pointers_to_strings.resize(file_strings.size()); + for (size_t i = 0; i < file_strings.size(); i++) + pointers_to_strings[i] = file_strings[i].c_str(); + + current_choose_file_callback_( + &pointers_to_strings[0], + static_cast<int>(pointers_to_strings.size()), + current_choose_file_user_data_); + } +} + bool WebPluginDelegatePepper::Initialize( const GURL& url, const std::vector<std::string>& arg_names, @@ -368,6 +392,38 @@ void WebPluginDelegatePepper::Zoom(int factor) { extensions->zoom(instance()->npp(), factor); } +bool WebPluginDelegatePepper::ChooseFile(const char* mime_types, + int mode, + NPChooseFileCallback callback, + void* user_data) { + if (!render_view_ || !callback) + return false; + + if (current_choose_file_callback_) + return false; // Reentrant call to browse, only one can be outstanding + // per plugin. + + // TODO(brettw) do something with the mime types! + current_choose_file_callback_ = callback; + current_choose_file_user_data_ = user_data; + + ViewHostMsg_RunFileChooser_Params ipc_params; + switch (mode) { + case NPChooseFile_Open: + ipc_params.mode = ViewHostMsg_RunFileChooser_Params::Open; + break; + case NPChooseFile_OpenMultiple: + ipc_params.mode = ViewHostMsg_RunFileChooser_Params::OpenMultiple; + break; + case NPChooseFile_Save: + ipc_params.mode = ViewHostMsg_RunFileChooser_Params::Save; + break; + default: + return false; + } + return render_view_->ScheduleFileChooser(ipc_params, this); +} + NPError WebPluginDelegatePepper::Device2DQueryCapability(int32 capability, int32* value) { return NPERR_GENERIC_ERROR; @@ -1045,7 +1101,9 @@ WebPluginDelegatePepper::WebPluginDelegatePepper( command_buffer_(NULL), #endif find_identifier_(-1), - method_factory3d_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { + method_factory3d_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + current_choose_file_callback_(NULL), + current_choose_file_user_data_(NULL) { // For now we keep a window struct, although it isn't used. memset(&window_, 0, sizeof(window_)); // All Pepper plugins are windowless and transparent. diff --git a/chrome/renderer/webplugin_delegate_pepper.h b/chrome/renderer/webplugin_delegate_pepper.h index 1ffc457..ce57940 100644 --- a/chrome/renderer/webplugin_delegate_pepper.h +++ b/chrome/renderer/webplugin_delegate_pepper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -22,6 +22,7 @@ #include "gfx/native_widget_types.h" #include "gfx/rect.h" #include "third_party/npapi/bindings/npapi.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFileChooserCompletion.h" #include "webkit/glue/webcursor.h" #include "webkit/glue/webplugin_delegate.h" @@ -30,7 +31,8 @@ class PluginInstance; } // An implementation of WebPluginDelegate for Pepper in-process plugins. -class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { +class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate, + public WebKit::WebFileChooserCompletion { public: static WebPluginDelegatePepper* Create( const FilePath& filename, @@ -39,6 +41,10 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { NPAPI::PluginInstance* instance() { return instance_.get(); } + // WebKit::WebFileChooserCompletion implementation. + virtual void didChooseFile( + const WebKit::WebVector<WebKit::WebString>& file_names); + // WebPluginDelegate implementation virtual bool Initialize(const GURL& url, const std::vector<std::string>& arg_names, @@ -83,6 +89,10 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { virtual void NumberOfFindResultsChanged(int total, bool final_result); virtual void SelectedFindResultChanged(int index); virtual void Zoom(int factor); + virtual bool ChooseFile(const char* mime_types, + int mode, + NPChooseFileCallback callback, + void* user_data); // WebPlugin2DDeviceDelegate implementation. virtual NPError Device2DQueryCapability(int32 capability, int32* value); @@ -221,6 +231,12 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { void* user_data); #endif + // Tells the browser out-of-band where the nested delegate lives on + // the page. + void SendNestedDelegateGeometryToBrowser(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect); + + base::WeakPtr<RenderView> render_view_; webkit_glue::WebPlugin* plugin_; @@ -258,13 +274,14 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { // The id of the current find operation, or -1 if none is in process. int find_identifier_; - // Tells the browser out-of-band where the nested delegate lives on - // the page. - void SendNestedDelegateGeometryToBrowser(const gfx::Rect& window_rect, - const gfx::Rect& clip_rect); - // Runnable methods that must be cancelled when the 3D context is destroyed. ScopedRunnableMethodFactory<WebPluginDelegatePepper> method_factory3d_; + + // When a choose file operation is outstanding, this will contain a + // pointer to the callback specified by the plugin. Will be NULL otherwise. + NPChooseFileCallback current_choose_file_callback_; + void* current_choose_file_user_data_; + DISALLOW_COPY_AND_ASSIGN(WebPluginDelegatePepper); }; diff --git a/third_party/npapi/bindings/npapi_extensions.h b/third_party/npapi/bindings/npapi_extensions.h index f7ac489..f8c05f4 100644 --- a/third_party/npapi/bindings/npapi_extensions.h +++ b/third_party/npapi/bindings/npapi_extensions.h @@ -197,6 +197,63 @@ typedef void (*NPSelectedFindResultChangedPtr)( NPP instance, int index); +/* Supports opening files anywhere on the system after prompting the user to + * pick one. + * + * This API is asynchronous. It will return immediately and the user will be + * prompted in parallel to pick a file. The plugin may continue to receive + * events while the open file dialog is up, and may continue to paint. Plugins + * may want to ignore input events between the call and the callback to avoid + * reentrant behavior. If the return value is not NPERR_NO_ERROR, the callback + * will NOT be executed. + * + * It is an error to call BrowseForFile before a previous call has executed + * the callback. + * + * Setting the flags to "Open" requires that the file exist to allow picking. + * Setting the flags to "Save" allows selecting nonexistant files (which will + * then be created), and will prompt the user if they want to overwrite an + * existing file if it exists. + * + * The plugin may specify a comma-separated list of possible mime types in + * the "extensions" parameter. If no extensions are specified, the dialog box + * will default to allowing all extensions. The first extension in the list + * will be the default. + * + * TODO(brettw) On Windows the extensions traditionally include a text + * description with the extension in the popup, do we want to allow this? + * We should probably also allow the ability to put "All files" in the + * list on Windows. + * + * Once the user has picked a file or has canceled the dialog box, the given + * callback will be called with the results of the operation and the passed in + * "user data" pointer. If the user successfully picked a file, the filename + * will be non-NULL and will contain a pointer to an array of strings, one for + * each file picked (the first file will be file_paths[0]). This buffer will + * become invalid as soon as the call completes, so it is the plugin's + * responsibility to copy the filename(sp if it needs future access to them. + * A NULL file_paths in the callback means the user canceled the dialog box. + * + * The filename will be in UTF-8. It may not actually correspond to the actual + * file on disk on a Linux system, because we'll do our best to convert it from + * the filesystem's locale to UTF-8. Instead, the string will be appropriate for + * displaying to the user which file they picked. + * */ +typedef enum { + NPChooseFile_Open = 1, + NPChooseFile_OpenMultiple = 2, + NPChooseFile_Save = 3, +} NPChooseFileMode; +typedef void (*NPChooseFileCallback)(const char** filePaths, + uint32 pathCount, + void* userData); +typedef NPError (*NPChooseFilePtr)( + NPP instance, + const char* mimeTypes, + NPChooseFileMode mode, + NPChooseFileCallback callback, + void* userData); + /* Pepper extensions */ struct NPNExtensions { /* Device interface acquisition */ @@ -206,6 +263,8 @@ struct NPNExtensions { /* Find */ NPNumberOfFindResultsChangedPtr numberOfFindResultsChanged; NPSelectedFindResultChangedPtr selectedFindResultChanged; + /* File I/O extensions */ + NPChooseFilePtr chooseFile; }; /* Events -------------------------------------------------------------------*/ diff --git a/webkit/glue/plugins/npapi_extension_thunk.cc b/webkit/glue/plugins/npapi_extension_thunk.cc index d653b35..0c25655 100644 --- a/webkit/glue/plugins/npapi_extension_thunk.cc +++ b/webkit/glue/plugins/npapi_extension_thunk.cc @@ -1,10 +1,11 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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 "webkit/glue/plugins/npapi_extension_thunk.h" #include "base/logging.h" +#include "base/string_util.h" #include "base/utf_string_conversions.h" #include "third_party/npapi/bindings/npapi_extensions.h" #include "webkit/glue/plugins/plugin_instance.h" @@ -13,7 +14,6 @@ #include "webkit/glue/webplugin.h" #include "webkit/glue/webplugin_delegate.h" - // FindInstance() // Finds a PluginInstance from an NPP. // The caller must take a reference if needed. @@ -414,6 +414,23 @@ static void CopyTextToClipboard(NPP id, const char* content) { scw.WriteText(UTF8ToUTF16(content)); } +static NPError ChooseFile(NPP id, + const char* mime_types, + NPChooseFileMode mode, + NPChooseFileCallback callback, + void* user_data) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (!plugin) + return NPERR_GENERIC_ERROR; + + if (!plugin->webplugin()->delegate()->ChooseFile(mime_types, + static_cast<int>(mode), + callback, user_data)) + return NPERR_GENERIC_ERROR; + + return NPERR_NO_ERROR; +} + static void NumberOfFindResultsChanged(NPP id, int total, bool final_result) { scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); if (plugin) { @@ -436,6 +453,7 @@ NPError GetPepperExtensionsFunctions(void* value) { &CopyTextToClipboard, &NumberOfFindResultsChanged, &SelectedFindResultChanged, + &ChooseFile, }; // Return a pointer to the canonical function table. diff --git a/webkit/glue/plugins/webplugin_file_delegate.h b/webkit/glue/plugins/webplugin_file_delegate.h new file mode 100644 index 0000000..162516c --- /dev/null +++ b/webkit/glue/plugins/webplugin_file_delegate.h @@ -0,0 +1,35 @@ +// Copyright (c) 2010 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. + +#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_ +#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_ + +#include "base/basictypes.h" +#include "third_party/npapi/bindings/npapi_extensions.h" + +namespace webkit_glue { + +// Interface for the NPAPI file extensions. This class implements "NOP" +// versions of all these functions so it can be used seamlessly by the +// "regular" plugin delegate while being overridden by the "pepper" one. +class WebPluginFileDelegate { + public: + // See NPChooseFilePtr in npapi_extensions.h. Returns true on success, on + // cancel, returns true but *filename will be filled with an empty FilePath + // and *handle will be 0. + virtual bool ChooseFile(const char* mime_types, + int mode, + NPChooseFileCallback callback, + void* user_data) { + return false; + } + + protected: + WebPluginFileDelegate() {} + virtual ~WebPluginFileDelegate() {} +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_ diff --git a/webkit/glue/webplugin_delegate.h b/webkit/glue/webplugin_delegate.h index 31ebe19..0b08c9e 100644 --- a/webkit/glue/webplugin_delegate.h +++ b/webkit/glue/webplugin_delegate.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -17,6 +17,7 @@ #include "webkit/glue/plugins/webplugin_2d_device_delegate.h" #include "webkit/glue/plugins/webplugin_3d_device_delegate.h" #include "webkit/glue/plugins/webplugin_audio_device_delegate.h" +#include "webkit/glue/plugins/webplugin_file_delegate.h" #include "webkit/glue/plugins/webplugin_print_delegate.h" @@ -42,7 +43,8 @@ class WebPluginResourceClient; class WebPluginDelegate : public WebPlugin2DDeviceDelegate, public WebPlugin3DDeviceDelegate, public WebPluginAudioDeviceDelegate, - public WebPluginPrintDelegate { + public WebPluginPrintDelegate, + public WebPluginFileDelegate { public: virtual ~WebPluginDelegate() {} |