diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-07 21:35:03 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-07 21:35:03 +0000 |
commit | 0ebf38756f3a68b30fe0d8e9336dbfafda52b5d5 (patch) | |
tree | a9ce0236b330fab3f39124c52fa0c1f184eb3965 | |
parent | 5e91242859811aef980a929253e6c33eb2cfec6e (diff) | |
download | chromium_src-0ebf38756f3a68b30fe0d8e9336dbfafda52b5d5.zip chromium_src-0ebf38756f3a68b30fe0d8e9336dbfafda52b5d5.tar.gz chromium_src-0ebf38756f3a68b30fe0d8e9336dbfafda52b5d5.tar.bz2 |
Landing this again as I cannot reproduce the perf regression locally.
Will investigate on the bot.
TBR=nsylvain
Review URL: http://codereview.chromium.org/9700
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5018 0039d316-1c4b-4281-b951-d872f2087c98
39 files changed, 739 insertions, 124 deletions
diff --git a/chrome/browser/render_view_host.cc b/chrome/browser/render_view_host.cc index 6c127a7..71c9574 100644 --- a/chrome/browser/render_view_host.cc +++ b/chrome/browser/render_view_host.cc @@ -696,6 +696,8 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_ShouldClose_ACK, OnMsgShouldCloseACK); IPC_MESSAGE_HANDLER(ViewHostMsg_UnloadListenerChanged, OnUnloadListenerChanged); + IPC_MESSAGE_HANDLER(ViewHostMsg_QueryFormFieldAutofill, + OnQueryFormFieldAutofill) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidgetHost::OnMessageReceived(msg)) IPC_END_MESSAGE_MAP_EX() @@ -725,10 +727,11 @@ void RenderViewHost::OnMsgCreateWindow(int route_id, view->CreateNewWindow(route_id, modal_dialog_event); } -void RenderViewHost::OnMsgCreateWidget(int route_id) { +void RenderViewHost::OnMsgCreateWidget(int route_id, + bool focus_on_show) { RenderViewHostDelegate::View* view = delegate_->GetViewDelegate(); if (view) - view->CreateNewWidget(route_id); + view->CreateNewWidget(route_id, focus_on_show); } void RenderViewHost::OnMsgShowView(int route_id, @@ -1226,6 +1229,43 @@ void RenderViewHost::OnUnloadListenerChanged(bool has_listener) { has_unload_listener_ = has_listener; } +void RenderViewHost::OnQueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& user_text, + int64 node_id, + int request_id) { + // TODO(jcampan): this is where the suggestions should be queried from the + // database. The sample code commented below is left here in the meantime for + // testing purpose. +#ifndef TEST_AUTOFILL + static std::vector<std::wstring>* suggestions = NULL; + if (!suggestions) { + suggestions = new std::vector<std::wstring>(); + suggestions->push_back(L"Alice"); + suggestions->push_back(L"Jay"); + suggestions->push_back(L"Jason"); + suggestions->push_back(L"Jasmine"); + suggestions->push_back(L"Jamel"); + suggestions->push_back(L"Jamelo"); + suggestions->push_back(L"Volvo"); + suggestions->push_back(L"Volswagen"); + } + + + std::vector<std::wstring> result; + for (std::vector<std::wstring>::iterator iter = suggestions->begin(); + iter != suggestions->end(); ++iter) { + if (StartsWith(*iter, user_text, false)) + result.push_back(*iter); + } + Send(new ViewMsg_AutofillSuggestions(routing_id_, + node_id, request_id, result, 0)); +#else + Send(new ViewMsg_AutofillSuggestions(routing_id_, + node_id, request_id, + std::vector<std::wstring>(), 0)); +#endif +} + void RenderViewHost::NotifyRendererUnresponsive() { // If the debugger is attached, we're going to be unresponsive anytime it's // stopped at a breakpoint. diff --git a/chrome/browser/render_view_host.h b/chrome/browser/render_view_host.h index ae0a729..6c9fede 100644 --- a/chrome/browser/render_view_host.h +++ b/chrome/browser/render_view_host.h @@ -399,7 +399,7 @@ class RenderViewHost : public RenderWidgetHost { // IPC message handlers: void OnMsgCreateWindow(int route_id, HANDLE modal_dialog_event); - void OnMsgCreateWidget(int route_id); + void OnMsgCreateWidget(int route_id, bool focus_on_show); void OnMsgShowView(int route_id, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, @@ -498,7 +498,10 @@ class RenderViewHost : public RenderWidgetHost { const webkit_glue::WebApplicationInfo& info); void OnMsgShouldCloseACK(bool proceed); void OnUnloadListenerChanged(bool has_handler); - + void OnQueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& user_text, + int64 node_id, + int request_id); virtual void NotifyRendererUnresponsive(); virtual void NotifyRendererResponsive(); diff --git a/chrome/browser/render_view_host_delegate.h b/chrome/browser/render_view_host_delegate.h index 27910d7..4230438 100644 --- a/chrome/browser/render_view_host_delegate.h +++ b/chrome/browser/render_view_host_delegate.h @@ -60,7 +60,10 @@ class RenderViewHostDelegate { // The page is trying to open a new widget (e.g. a select popup). The // widget should be created associated with the given route, but it should // not be shown yet. That should happen in response to ShowCreatedWidget. - virtual void CreateNewWidget(int route_id) = 0; + // If |focus_on_show| is true, the focus is given to the widget when shown, + // otherwise the focus is not changed. + virtual void CreateNewWidget(int route_id, + bool focus_on_show) = 0; // Show a previously created page with the specified disposition and bounds. // The window is identified by the route_id passed to CreateNewWindow. diff --git a/chrome/browser/render_widget_helper.cc b/chrome/browser/render_widget_helper.cc index d6e2bd7..4b87080 100644 --- a/chrome/browser/render_widget_helper.cc +++ b/chrome/browser/render_widget_helper.cc @@ -218,9 +218,11 @@ void RenderWidgetHelper::CreateNewWindow(int opener_id, this, &RenderWidgetHelper::OnSimulateReceivedMessage, msg)); } -void RenderWidgetHelper::CreateNewWidget(int opener_id, int* route_id) { +void RenderWidgetHelper::CreateNewWidget(int opener_id, + bool focus_on_show, + int* route_id) { *route_id = GetNextRoutingID(); - ViewHostMsg_CreateWidgetWithRoute msg(opener_id, *route_id); + ViewHostMsg_CreateWidgetWithRoute msg(opener_id, *route_id, focus_on_show); ui_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &RenderWidgetHelper::OnSimulateReceivedMessage, msg)); } diff --git a/chrome/browser/render_widget_helper.h b/chrome/browser/render_widget_helper.h index 7b6295f..c09462a 100644 --- a/chrome/browser/render_widget_helper.h +++ b/chrome/browser/render_widget_helper.h @@ -113,7 +113,7 @@ class RenderWidgetHelper : void CreateNewWindow(int opener_id, bool user_gesture, int* route_id, HANDLE* modal_dialog_event, HANDLE render_process); - void CreateNewWidget(int opener_id, int* route_id); + void CreateNewWidget(int opener_id, bool focus_on_show, int* route_id); private: // A class used to proxy a paint message. PaintMsgProxy objects are created diff --git a/chrome/browser/render_widget_host_view_win.cc b/chrome/browser/render_widget_host_view_win.cc index ece55bc..3c86595 100644 --- a/chrome/browser/render_widget_host_view_win.cc +++ b/chrome/browser/render_widget_host_view_win.cc @@ -77,7 +77,8 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) tooltip_showing_(false), shutdown_factory_(this), parent_hwnd_(NULL), - is_loading_(false) { + is_loading_(false), + focus_on_show_(true) { render_widget_host_->set_view(this); renderer_accessible_ = CommandLine().HasSwitch(switches::kEnableRendererAccessibility); diff --git a/chrome/browser/render_widget_host_view_win.h b/chrome/browser/render_widget_host_view_win.h index ff609f3..d1b7730 100644 --- a/chrome/browser/render_widget_host_view_win.h +++ b/chrome/browser/render_widget_host_view_win.h @@ -65,6 +65,11 @@ class RenderWidgetHostViewWin : close_on_deactivate_ = close_on_deactivate; } + void set_focus_on_show(bool focus) { + focus_on_show_ = focus; + } + bool focus_on_show() const { return focus_on_show_; } + void set_parent_hwnd(HWND parent) { parent_hwnd_ = parent; } DECLARE_WND_CLASS_EX(kRenderWidgetHostHWNDClass, CS_DBLCLKS, 0); @@ -260,6 +265,9 @@ class RenderWidgetHostViewWin : // value returns true for is_null() if we are not recording whiteout times. base::TimeTicks whiteout_start_time_; + // Whether the window should get focus when shown. Default is true. + bool focus_on_show_; + // Whether the renderer is made accessible. // TODO(jcampan): http://b/issue?id=1432077 This is a temporary work-around // until that bug is fixed. diff --git a/chrome/browser/resource_message_filter.cc b/chrome/browser/resource_message_filter.cc index 2e25e92..24eb964 100644 --- a/chrome/browser/resource_message_filter.cc +++ b/chrome/browser/resource_message_filter.cc @@ -234,8 +234,10 @@ void ResourceMessageFilter::OnMsgCreateWindow(int opener_id, modal_dialog_event, render_handle_); } -void ResourceMessageFilter::OnMsgCreateWidget(int opener_id, int* route_id) { - render_widget_helper_->CreateNewWidget(opener_id, route_id); +void ResourceMessageFilter::OnMsgCreateWidget(int opener_id, + bool focus_on_show, + int* route_id) { + render_widget_helper_->CreateNewWidget(opener_id, focus_on_show, route_id); } void ResourceMessageFilter::OnRequestResource( diff --git a/chrome/browser/resource_message_filter.h b/chrome/browser/resource_message_filter.h index 7dd3513..1b068e4 100644 --- a/chrome/browser/resource_message_filter.h +++ b/chrome/browser/resource_message_filter.h @@ -77,7 +77,7 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, private: void OnMsgCreateWindow(int opener_id, bool user_gesture, int* route_id, HANDLE* modal_dialog_event); - void OnMsgCreateWidget(int opener_id, int* route_id); + void OnMsgCreateWidget(int opener_id, bool focus_on_show, int* route_id); void OnRequestResource(const IPC::Message& msg, int request_id, const ViewHostMsg_Resource_Request& request); void OnCancelRequest(int request_id); diff --git a/chrome/browser/web_contents_view.cc b/chrome/browser/web_contents_view.cc index 28dd88b..931e36c 100644 --- a/chrome/browser/web_contents_view.cc +++ b/chrome/browser/web_contents_view.cc @@ -10,9 +10,11 @@ void WebContentsView::CreateNewWindow(int route_id, HANDLE modal_dialog_event) { modal_dialog_event); } -void WebContentsView::CreateNewWidget(int route_id) { +void WebContentsView::CreateNewWidget(int route_id, + bool focus_on_show) { // Save the created widget associated with the route so we can show it later. - pending_widget_views_[route_id] = CreateNewWidgetInternal(route_id); + pending_widget_views_[route_id] = CreateNewWidgetInternal(route_id, + focus_on_show); } void WebContentsView::ShowCreatedWindow(int route_id, diff --git a/chrome/browser/web_contents_view.h b/chrome/browser/web_contents_view.h index 6ebf84c..e6b24c2 100644 --- a/chrome/browser/web_contents_view.h +++ b/chrome/browser/web_contents_view.h @@ -172,7 +172,8 @@ class WebContentsView : public RenderViewHostDelegate::View { // the Show functions rather than the route ID. virtual WebContents* CreateNewWindowInternal(int route_id, HANDLE modal_dialog_event) = 0; - virtual RenderWidgetHostView* CreateNewWidgetInternal(int route_id) = 0; + virtual RenderWidgetHostView* CreateNewWidgetInternal(int route_id, + bool focus_on_show) = 0; virtual void ShowCreatedWindowInternal(WebContents* new_web_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, @@ -185,7 +186,8 @@ class WebContentsView : public RenderViewHostDelegate::View { // do some book-keeping associated with the request. The request is then // forwarded to *Internal which does platform-specific work. virtual void CreateNewWindow(int route_id, HANDLE modal_dialog_event); - virtual void CreateNewWidget(int route_id); + virtual void CreateNewWidget(int route_id, + bool focus_on_show); virtual void ShowCreatedWindow(int route_id, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, diff --git a/chrome/browser/web_contents_view_win.cc b/chrome/browser/web_contents_view_win.cc index 082fb89..3ba7173 100644 --- a/chrome/browser/web_contents_view_win.cc +++ b/chrome/browser/web_contents_view_win.cc @@ -378,7 +378,8 @@ WebContents* WebContentsViewWin::CreateNewWindowInternal( } RenderWidgetHostView* WebContentsViewWin::CreateNewWidgetInternal( - int route_id) { + int route_id, + bool focus_on_show) { // Create the widget and its associated view. // TODO(brettw) can widget creation be cross-platform? RenderWidgetHost* widget_host = @@ -394,6 +395,7 @@ RenderWidgetHostView* WebContentsViewWin::CreateNewWidgetInternal( widget_view->set_parent_hwnd( web_contents_->render_widget_host_view()->GetPluginHWND()); widget_view->set_close_on_deactivate(true); + widget_view->set_focus_on_show(focus_on_show); return widget_view; } @@ -438,7 +440,8 @@ void WebContentsViewWin::ShowCreatedWidgetInternal( widget_host_view_win->MoveWindow(initial_pos.x(), initial_pos.y(), initial_pos.width(), initial_pos.height(), TRUE); - widget_host_view_win->ShowWindow(SW_SHOW); + widget_host_view_win->ShowWindow(widget_host_view_win->focus_on_show() ? + SW_SHOW : SW_SHOWNOACTIVATE); widget_host->Init(); } diff --git a/chrome/browser/web_contents_view_win.h b/chrome/browser/web_contents_view_win.h index d1ea193..ca03829 100644 --- a/chrome/browser/web_contents_view_win.h +++ b/chrome/browser/web_contents_view_win.h @@ -56,7 +56,8 @@ class WebContentsViewWin : public WebContentsView, // Backend implementation of RenderViewHostDelegate::View. virtual WebContents* CreateNewWindowInternal( int route_id, HANDLE modal_dialog_event); - virtual RenderWidgetHostView* CreateNewWidgetInternal(int route_id); + virtual RenderWidgetHostView* CreateNewWidgetInternal(int route_id, + bool focus_on_show); virtual void ShowCreatedWindowInternal(WebContents* new_web_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 579a318..a4098a0 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -463,6 +463,14 @@ IPC_BEGIN_MESSAGES(View, 1) // into a full window). IPC_MESSAGE_ROUTED0(ViewMsg_DisassociateFromPopupCount) + // Reply to the ViewHostMsg_QueryFormFieldAutofill message with the autofill + // suggestions. + IPC_MESSAGE_ROUTED4(ViewMsg_AutofillSuggestions, + int64 /* id of the text input field */, + int /* id of the request message */, + std::vector<std::wstring> /* suggestions */, + int /* index of default suggestion */) + IPC_END_MESSAGES(View) @@ -484,8 +492,9 @@ IPC_BEGIN_MESSAGES(ViewHost, 2) // Similar to ViewHostMsg_CreateView, except used for sub-widgets, like // <select> dropdowns. This message is sent to the WebContents that // contains the widget being created. - IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_CreateWidget, + IPC_SYNC_MESSAGE_CONTROL2_1(ViewHostMsg_CreateWidget, int /* opener_id */, + bool /* focus on show */, int /* route_id */) // These two messages are sent as a result of the above two, in the browser @@ -494,8 +503,9 @@ IPC_BEGIN_MESSAGES(ViewHost, 2) int /* route_id */, HANDLE /* modal_dialog_event */) - IPC_MESSAGE_ROUTED1(ViewHostMsg_CreateWidgetWithRoute, - int /* route_id */) + IPC_MESSAGE_ROUTED2(ViewHostMsg_CreateWidgetWithRoute, + int /* route_id */, + bool /* focus on show */) // These two messages are sent to the parent RenderViewHost to display the // page/widget that was created by CreateView/CreateWidget. routing_id @@ -1064,4 +1074,11 @@ IPC_BEGIN_MESSAGES(ViewHost, 2) HWND /* window */, gfx::Rect /* Out: Window location */) + // Queries the browser for suggestion for autofill in a form input field. + IPC_MESSAGE_ROUTED4(ViewHostMsg_QueryFormFieldAutofill, + std::wstring /* field name */, + std::wstring /* user entered text */, + int64 /* id of the text input field */, + int /* id of this message */) + IPC_END_MESSAGES(ViewHost) diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 7cebc16..cbae9d0 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -142,7 +142,7 @@ class RenderViewExtraRequestData : public WebRequest::ExtraData { /////////////////////////////////////////////////////////////////////////////// RenderView::RenderView() - : RenderWidget(RenderThread::current()), + : RenderWidget(RenderThread::current(), true), is_loading_(false), page_id_(-1), last_page_id_sent_to_browser_(-1), @@ -161,7 +161,8 @@ RenderView::RenderView() has_unload_listener_(false), decrement_shared_popup_at_destruction_(false), greasemonkey_enabled_(false), - waiting_for_create_window_ack_(false) { + waiting_for_create_window_ack_(false), + form_field_autofill_request_id_(0) { resource_dispatcher_ = new ResourceDispatcher(this); #ifdef CHROME_PERSONALIZATION personalization_ = Personalization::CreateRendererPersonalization(); @@ -384,6 +385,8 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { OnMessageFromExternalHost) IPC_MESSAGE_HANDLER(ViewMsg_DisassociateFromPopupCount, OnDisassociateFromPopupCount) + IPC_MESSAGE_HANDLER(ViewMsg_AutofillSuggestions, + OnReceivedAutofillSuggestions) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(RenderWidget::OnMessageReceived(message)) IPC_END_MESSAGE_MAP() @@ -1664,6 +1667,29 @@ void RenderView::OnUnloadListenerChanged(WebView* webview, WebFrame* webframe) { } } +void RenderView::QueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& text, + int64 node_id) { + static int message_id_counter = 0; + form_field_autofill_request_id_ = message_id_counter++; + Send(new ViewHostMsg_QueryFormFieldAutofill(routing_id_, + field_name, text, + node_id, + form_field_autofill_request_id_)); +} + +void RenderView::OnReceivedAutofillSuggestions( + int64 node_id, + int request_id, + const std::vector<std::wstring> suggestions, + int default_suggestion_index) { + if (!webview() || request_id != form_field_autofill_request_id_) + return; + + webview()->AutofillSuggestionsForNode(node_id, suggestions, + default_suggestion_index); +} + void RenderView::ShowModalHTMLDialog(const GURL& url, int width, int height, const std::string& json_arguments, std::string* json_retval) { @@ -1762,9 +1788,11 @@ WebView* RenderView::CreateWebView(WebView* webview, bool user_gesture) { return view->webview(); } -WebWidget* RenderView::CreatePopupWidget(WebView* webview) { +WebWidget* RenderView::CreatePopupWidget(WebView* webview, + bool focus_on_show) { RenderWidget* widget = RenderWidget::Create(routing_id_, - RenderThread::current()); + RenderThread::current(), + focus_on_show); return widget->webwidget(); } diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index eee08e1..8484afd 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -127,6 +127,9 @@ class RenderView : public RenderWidget, public WebViewDelegate, virtual bool RunBeforeUnloadConfirm(WebView* webview, const std::wstring& message); virtual void OnUnloadListenerChanged(WebView* webview, WebFrame* webframe); + virtual void QueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& text, + int64 node_id); virtual void UpdateTargetURL(WebView* webview, const GURL& url); virtual void RunFileChooser(const std::wstring& default_filename, @@ -197,7 +200,8 @@ class RenderView : public RenderWidget, public WebViewDelegate, bool is_redirect); virtual WebView* CreateWebView(WebView* webview, bool user_gesture); - virtual WebWidget* CreatePopupWidget(WebView* webview); + virtual WebWidget* CreatePopupWidget(WebView* webview, + bool focus_on_show); virtual WebPluginDelegate* CreatePluginDelegate( WebView* webview, const GURL& url, @@ -457,6 +461,13 @@ class RenderView : public RenderWidget, public WebViewDelegate, // Notification about ui theme changes. void OnThemeChanged(); + // Notification that we have received autofill suggestion. + void OnReceivedAutofillSuggestions( + int64 node_id, + int request_id, + const std::vector<std::wstring> suggestions, + int default_suggestion_index); + #ifdef CHROME_PERSONALIZATION void OnPersonalizationEvent(std::string event_name, std::string event_args); #endif @@ -673,6 +684,10 @@ class RenderView : public RenderWidget, public WebViewDelegate, // Set if we are waiting for an ack for ViewHostMsg_CreateWindow bool waiting_for_create_window_ack_; + // The id of the last request sent for form field autofill. Used to ignore + // out of date responses. + int form_field_autofill_request_id_; + // A cached WebHistoryItem used for back/forward navigations initiated by // WebCore (via the window.history.go API). We only have one such navigation // pending at a time. diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index 3cffb60f..ab1a270 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -69,7 +69,8 @@ DeferredCloses* DeferredCloses::current_ = NULL; /////////////////////////////////////////////////////////////////////////////// -RenderWidget::RenderWidget(RenderThreadBase* render_thread) +RenderWidget::RenderWidget(RenderThreadBase* render_thread, + bool focus_on_show) : routing_id_(MSG_ROUTING_NONE), opener_id_(MSG_ROUTING_NONE), render_thread_(render_thread), @@ -88,7 +89,8 @@ RenderWidget::RenderWidget(RenderThreadBase* render_thread) ime_control_x_(-1), ime_control_y_(-1), ime_control_new_state_(false), - ime_control_updated_(false) { + ime_control_updated_(false), + focus_on_show_(focus_on_show) { RenderProcess::AddRefProcess(); DCHECK(render_thread_); } @@ -107,9 +109,11 @@ RenderWidget::~RenderWidget() { /*static*/ RenderWidget* RenderWidget::Create(int32 opener_id, - RenderThreadBase* render_thread) { + RenderThreadBase* render_thread, + bool focus_on_show) { DCHECK(opener_id != MSG_ROUTING_NONE); - scoped_refptr<RenderWidget> widget = new RenderWidget(render_thread); + scoped_refptr<RenderWidget> widget = new RenderWidget(render_thread, + focus_on_show); widget->Init(opener_id); // adds reference return widget; } @@ -125,7 +129,7 @@ void RenderWidget::Init(int32 opener_id) { webwidget_.swap(&webwidget); bool result = render_thread_->Send( - new ViewHostMsg_CreateWidget(opener_id, &routing_id_)); + new ViewHostMsg_CreateWidget(opener_id, focus_on_show_, &routing_id_)); if (result) { render_thread_->AddRoute(routing_id_, this); // Take a reference on behalf of the RenderThread. This will be balanced diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 98d0377..f597dff 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -30,7 +30,9 @@ class RenderWidget : public IPC::Channel::Listener, // Creates a new RenderWidget. The opener_id is the routing ID of the // RenderView that this widget lives inside. The render_thread is any // RenderThreadBase implementation, mostly commonly RenderThread::current(). - static RenderWidget* Create(int32 opener_id, RenderThreadBase* render_thread); + static RenderWidget* Create(int32 opener_id, + RenderThreadBase* render_thread, + bool focus_on_show); // The routing ID assigned by the RenderProcess. Will be MSG_ROUTING_NONE if // not yet assigned a view ID, in which case, the process MUST NOT send @@ -86,7 +88,7 @@ class RenderWidget : public IPC::Channel::Listener, // without ref-counting is an error. friend class base::RefCounted<RenderWidget>; - RenderWidget(RenderThreadBase* render_thread); + RenderWidget(RenderThreadBase* render_thread, bool focus_on_show); virtual ~RenderWidget(); // Initializes this view with the given opener. CompleteInit must be called @@ -261,6 +263,9 @@ class RenderWidget : public IPC::Channel::Listener, bool ime_control_new_state_; bool ime_control_updated_; + // Whether the window for this RenderWidget should be focused when shown. + bool focus_on_show_; + // Holds all the needed plugin window moves for a scroll. std::vector<WebPluginGeometry> plugin_window_moves_; diff --git a/chrome/renderer/render_widget_unittest.cc b/chrome/renderer/render_widget_unittest.cc index 086f8bf..f280aa7 100644 --- a/chrome/renderer/render_widget_unittest.cc +++ b/chrome/renderer/render_widget_unittest.cc @@ -114,7 +114,9 @@ class MockRenderThread : public RenderThreadBase { } // The Widget expects to be returned valid route_id. - void OnMsgCreateWidget(int opener_id, int* route_id) { + void OnMsgCreateWidget(int opener_id, + bool focus_on_show, + int* route_id) { opener_id_ = opener_id; *route_id = routing_id_; } @@ -141,7 +143,7 @@ TEST(RenderWidgetTest, CreateAndCloseWidget) { { scoped_refptr<RenderWidget> rw = - RenderWidget::Create(kOpenerId, &render_thread); + RenderWidget::Create(kOpenerId, &render_thread, true); ASSERT_TRUE(rw != NULL); // After the RenderWidget it must have sent a message to the render thread diff --git a/webkit/build/glue/glue.vcproj b/webkit/build/glue/glue.vcproj index 879bf66..dcae341 100644 --- a/webkit/build/glue/glue.vcproj +++ b/webkit/build/glue/glue.vcproj @@ -381,6 +381,14 @@ > </File> <File + RelativePath="..\..\glue\form_autocomplete_listener.cc" + > + </File> + <File + RelativePath="..\..\glue\form_autocomplete_listener.h" + > + </File> + <File RelativePath="..\..\glue\glue_accessibility.cc" > </File> diff --git a/webkit/build/port/port.vcproj b/webkit/build/port/port.vcproj index 3a42435..b18530a 100644 --- a/webkit/build/port/port.vcproj +++ b/webkit/build/port/port.vcproj @@ -807,6 +807,10 @@ > </File> <File + RelativePath="..\..\port\platform\chromium\PopupMenuChromium.h" + > + </File> + <File RelativePath="..\..\port\platform\chromium\SearchPopupMenuChromium.cpp" > </File> diff --git a/webkit/glue/SConscript b/webkit/glue/SConscript index c82a3c8..0870d6b 100644 --- a/webkit/glue/SConscript +++ b/webkit/glue/SConscript @@ -35,6 +35,7 @@ input_files = [ 'editor_client_impl.cc', 'entity_map.cc', 'event_conversion.cc', + 'form_autocomplete_listener.cc', 'feed_preview.cc', 'glue_util.cc', 'glue_serialize.cc', diff --git a/webkit/glue/autocomplete_input_listener.h b/webkit/glue/autocomplete_input_listener.h index 3b92b86..fe412f1 100644 --- a/webkit/glue/autocomplete_input_listener.h +++ b/webkit/glue/autocomplete_input_listener.h @@ -16,7 +16,6 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "EventListener.h" MSVC_POP_WARNING(); -#undef LOG #include "base/basictypes.h" #include "base/scoped_ptr.h" diff --git a/webkit/glue/chrome_client_impl.cc b/webkit/glue/chrome_client_impl.cc index 387f307..081d4c7 100644 --- a/webkit/glue/chrome_client_impl.cc +++ b/webkit/glue/chrome_client_impl.cc @@ -449,12 +449,14 @@ void ChromeClientImpl::runFileChooser(const WebCore::String& default_path, delegate->RunFileChooser(suggestion, chooser); } -void ChromeClientImpl::popupOpened( - WebCore::FramelessScrollView* popup_view, const WebCore::IntRect& bounds) { +void ChromeClientImpl::popupOpened(WebCore::FramelessScrollView* popup_view, + const WebCore::IntRect& bounds, + bool focus_on_show) { WebViewDelegate* d = webview_->delegate(); if (d) { WebWidgetImpl* webwidget = - static_cast<WebWidgetImpl*>(d->CreatePopupWidget(webview_)); + static_cast<WebWidgetImpl*>(d->CreatePopupWidget(webview_, + focus_on_show)); webwidget->Init(popup_view, webkit_glue::FromIntRect(bounds)); } } diff --git a/webkit/glue/chrome_client_impl.h b/webkit/glue/chrome_client_impl.h index 88cf83c..7bb132b 100644 --- a/webkit/glue/chrome_client_impl.h +++ b/webkit/glue/chrome_client_impl.h @@ -115,7 +115,8 @@ public: virtual void runFileChooser(const WebCore::String&, PassRefPtr<WebCore::FileChooser>); virtual void popupOpened(WebCore::FramelessScrollView* popup_view, - const WebCore::IntRect& bounds); + const WebCore::IntRect& bounds, + bool focus_on_show); void SetCursor(const WebCursor& cursor); diff --git a/webkit/glue/form_autocomplete_listener.cc b/webkit/glue/form_autocomplete_listener.cc new file mode 100644 index 0000000..aaaf0f70 --- /dev/null +++ b/webkit/glue/form_autocomplete_listener.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2006-2008 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 "config.h" + +#include "webkit/glue/form_autocomplete_listener.h" + +MSVC_PUSH_WARNING_LEVEL(0); +#include "HTMLInputElement.h" +MSVC_POP_WARNING(); + +#undef LOG + +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webview_delegate.h" + +namespace webkit_glue { + +FormAutocompleteListener::FormAutocompleteListener( + WebViewDelegate* webview_delegate, + WebCore::HTMLInputElement* input_element) + : AutocompleteInputListener(new HTMLInputDelegate(input_element)), + webview_delegate_(webview_delegate), + name_(webkit_glue::StringToStdWString(input_element->name().string())), + node_id_(reinterpret_cast<int64>(input_element)) { + DCHECK(input_element->isTextField() && !input_element->isPasswordField() && + input_element->autoComplete()); +} + +void FormAutocompleteListener::OnInlineAutocompleteNeeded( + const std::wstring& user_input) { + webview_delegate_->QueryFormFieldAutofill(name_, user_input, node_id_); +} + +} // namespace diff --git a/webkit/glue/form_autocomplete_listener.h b/webkit/glue/form_autocomplete_listener.h new file mode 100644 index 0000000..e55522e --- /dev/null +++ b/webkit/glue/form_autocomplete_listener.h @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 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_FORM_AUTOCOMPLETE_LISTENER_ +#define WEBKIT_GLUE_FORM_AUTOCOMPLETE_LISTENER_ + +#include <string> + +#include "webkit/glue/autocomplete_input_listener.h" + +class WebViewDelegate; + +namespace webkit_glue { + +// This class listens for the user typing in a text input in a form and queries +// the browser for autofill information. + +class FormAutocompleteListener : public AutocompleteInputListener { + public: + FormAutocompleteListener(WebViewDelegate* webview_delegate, + WebCore::HTMLInputElement* input_element); + virtual ~FormAutocompleteListener() { } + + // AutocompleteInputListener implementation. + virtual void OnBlur(const std::wstring& user_input) { } + virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input); + + private: + // The delegate associated with the WebView that contains thhe input we are + // listening to. + WebViewDelegate* webview_delegate_; + + // The name of the input node we are listening to. + std::wstring name_; + + // An id to represent the input element. That ID is passed to the call that + // queries for suggestions. + int64 node_id_; + + DISALLOW_COPY_AND_ASSIGN(FormAutocompleteListener); +}; + +} // webkit_glue + +#endif // WEBKIT_GLUE_FORM_AUTOCOMPLETE_LISTENER_ diff --git a/webkit/glue/password_autocomplete_listener.cc b/webkit/glue/password_autocomplete_listener.cc index aaf6125..0a88ab3 100644 --- a/webkit/glue/password_autocomplete_listener.cc +++ b/webkit/glue/password_autocomplete_listener.cc @@ -6,6 +6,7 @@ // component. #include "webkit/glue/password_autocomplete_listener.h" +#undef LOG #include "base/logging.h" namespace webkit_glue { diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc index 8138900..06e0645 100644 --- a/webkit/glue/webframeloaderclient_impl.cc +++ b/webkit/glue/webframeloaderclient_impl.cc @@ -16,6 +16,9 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "Element.h" #include "HistoryItem.h" #include "HTMLFormElement.h" // needed by FormState.h +#include "HTMLFormControlElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" #include "FormState.h" #include "FrameLoader.h" #include "FrameLoadRequest.h" @@ -39,8 +42,9 @@ MSVC_POP_WARNING(); #if defined(OS_WIN) #include "webkit/activex_shim/activex_shared.h" #endif -#include "webkit/glue/webframeloaderclient_impl.h" #include "webkit/glue/alt_404_page_resource_fetcher.h" +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/form_autocomplete_listener.h" #include "webkit/glue/glue_util.h" #include "webkit/glue/password_form_dom_manager.h" #include "webkit/glue/plugins/plugin_list.h" @@ -48,6 +52,7 @@ MSVC_POP_WARNING(); #include "webkit/glue/webdatasource_impl.h" #include "webkit/glue/webdocumentloader_impl.h" #include "webkit/glue/weberror_impl.h" +#include "webkit/glue/webframeloaderclient_impl.h" #include "webkit/glue/webhistoryitem_impl.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webplugin_impl.h" @@ -351,10 +356,26 @@ void WebFrameLoaderClient::dispatchDidFinishDocumentLoad() { if (!form->autoComplete()) continue; + std::set<std::wstring> password_related_fields; scoped_ptr<PasswordForm> data( PasswordFormDomManager::CreatePasswordForm(form)); - if (data.get()) + if (data.get()) { actions.push_back(*data); + // Let's remember the names of password related fields so we do not + // autofill them with the regular form autofill. + DCHECK(!data->username_element.empty()); + DCHECK(!data->password_element.empty()); + password_related_fields.insert(data->username_element); + password_related_fields.insert(data->password_element); + if (!data->old_password_element.empty()) + password_related_fields.insert(data->old_password_element); + } + + // Now let's register for any text input. + // TODO(jcampan): bug #3847 merge password and form autofill so we + // traverse the form elements only once. + // Disabling the autofill to investigate the perf regression on build bot. + // RegisterAutofillListeners(form, password_related_fields); } } @@ -686,6 +707,40 @@ NavigationGesture WebFrameLoaderClient::NavigationGestureForLastLoad() { NavigationGestureAuto; } +void WebFrameLoaderClient::RegisterAutofillListeners( + WebCore::HTMLFormElement* form, + const std::set<std::wstring>& excluded_fields) { + + WebViewDelegate* webview_delegate = webframe_->webview_impl()->delegate(); + if (!webview_delegate) + return; + + for (size_t i = 0; i < form->formElements.size(); i++) { + WebCore::HTMLFormControlElement* form_element = form->formElements[i]; + if (!form_element->hasLocalName(WebCore::HTMLNames::inputTag)) + continue; + + WebCore::HTMLInputElement* input_element = + static_cast<WebCore::HTMLInputElement*>(form_element); + if (!input_element->isEnabled() || !input_element->isTextField() || + input_element->isPasswordField() || !input_element->autoComplete()) { + continue; + } + + std::wstring name = webkit_glue::StringToStdWString(input_element->name()); + if (excluded_fields.find(name) != excluded_fields.end()) + continue; + +#if !defined(OS_MACOSX) + // FIXME on Mac + webkit_glue::FormAutocompleteListener* listener = + new webkit_glue::FormAutocompleteListener(webview_delegate, + input_element); + webkit_glue::AttachForInlineAutocomplete(input_element, listener); +#endif + } +} + void WebFrameLoaderClient::dispatchDidReceiveTitle(const String& title) { WebViewImpl* webview = webframe_->webview_impl(); WebViewDelegate* d = webview->delegate(); @@ -1478,4 +1533,3 @@ bool WebFrameLoaderClient::ActionSpecifiesDisposition( *disposition = shift ? NEW_WINDOW : SAVE_TO_DISK; return true; } - diff --git a/webkit/glue/webframeloaderclient_impl.h b/webkit/glue/webframeloaderclient_impl.h index 20b12ee..6010ba8 100644 --- a/webkit/glue/webframeloaderclient_impl.h +++ b/webkit/glue/webframeloaderclient_impl.h @@ -5,6 +5,8 @@ #ifndef WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ #define WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ +#include <set> + #include "base/compiler_specific.h" MSVC_PUSH_WARNING_LEVEL(0); @@ -18,6 +20,7 @@ MSVC_POP_WARNING(); namespace WebCore { class Frame; +class HTMLFormElement; class Widget; } @@ -209,6 +212,11 @@ class WebFrameLoaderClient : public WebCore::FrameLoaderClient { // otherwise returns NavigationGestureUnknown. NavigationGesture NavigationGestureForLastLoad(); + // Registers the text input fields in the passed form for autofill, with the + // exclusion of any field whose name is contained in |excluded_fields|. + void RegisterAutofillListeners(WebCore::HTMLFormElement* form, + const std::set<std::wstring>& excluded_fields); + // The WebFrame that owns this object and manages its lifetime. Therefore, // the web frame object is guaranteed to exist. WebFrameImpl* webframe_; diff --git a/webkit/glue/webplugin_impl_mac.mm b/webkit/glue/webplugin_impl_mac.mm index 191ebb7..1a420ea 100644 --- a/webkit/glue/webplugin_impl_mac.mm +++ b/webkit/glue/webplugin_impl_mac.mm @@ -4,6 +4,9 @@ #include "config.h" +#include "wtf/ASCIICType.h" + +#undef LOG #include "webkit/glue/webplugin_impl.h" // TODO(pinkerton): all of this needs to be filled in. webplugin_impl.cc has diff --git a/webkit/glue/webview.h b/webkit/glue/webview.h index e47ad0e..1c67c4f4 100644 --- a/webkit/glue/webview.h +++ b/webkit/glue/webview.h @@ -6,6 +6,7 @@ #define WEBKIT_GLUE_WEBVIEW_H__ #include <string> +#include <vector> #include "base/basictypes.h" #include "base/ref_counted.h" @@ -195,6 +196,12 @@ class WebView : public WebWidget { virtual void DragTargetDrop( int client_x, int client_y, int screen_x, int screen_y) = 0; + // Notifies the webview that autofill suggestions are available for a node. + virtual void AutofillSuggestionsForNode( + int64 node_id, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index) = 0; + private: DISALLOW_EVIL_CONSTRUCTORS(WebView); }; diff --git a/webkit/glue/webview_delegate.h b/webkit/glue/webview_delegate.h index 97944dd..1ce50c5 100644 --- a/webkit/glue/webview_delegate.h +++ b/webkit/glue/webview_delegate.h @@ -108,7 +108,8 @@ class WebViewDelegate : virtual public WebWidgetDelegate { // This method is called to create a new WebWidget to act as a popup // (like a drop-down menu). - virtual WebWidget* CreatePopupWidget(WebView* webview) { + virtual WebWidget* CreatePopupWidget(WebView* webview, + bool focus_on_show) { return NULL; } @@ -457,6 +458,14 @@ class WebViewDelegate : virtual public WebWidgetDelegate { virtual void OnUnloadListenerChanged(WebView* webview, WebFrame* webframe) { } + // Queries the browser for suggestions to be shown for the form text field + // named |field_name|. |text| is the text entered by the user so far and + // |node_id| is the id of the node of the input field. + virtual void QueryFormFieldAutofill(const std::wstring& field_name, + const std::wstring& text, + int64 node_id) { + } + // UIDelegate -------------------------------------------------------------- // Asks the browser to show a modal HTML dialog. The dialog is passed the diff --git a/webkit/glue/webview_impl.cc b/webkit/glue/webview_impl.cc index 6375fa3..c807b31 100644 --- a/webkit/glue/webview_impl.cc +++ b/webkit/glue/webview_impl.cc @@ -35,6 +35,7 @@ #include "base/compiler_specific.h" MSVC_PUSH_WARNING_LEVEL(0); +#include "CSSStyleSelector.h" #if defined(OS_WIN) #include "Cursor.h" #endif @@ -50,20 +51,25 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "FrameTree.h" #include "FrameView.h" #include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLInputElement.h" #include "HitTestResult.h" #include "Image.h" #include "InspectorController.h" #include "IntRect.h" #include "KeyboardEvent.h" #include "MIMETypeRegistry.h" +#include "NodeRenderStyle.h" #include "Page.h" #include "PlatformKeyboardEvent.h" #include "PlatformMouseEvent.h" #include "PlatformWheelEvent.h" #include "PluginInfoStore.h" +#include "PopupMenuClient.h" #if defined(OS_WIN) #include "RenderThemeWin.h" #endif +#include "RenderView.h" #include "ResourceHandle.h" #include "SelectionController.h" #include "Settings.h" @@ -94,6 +100,7 @@ MSVC_POP_WARNING(); #include "webkit/glue/webview_delegate.h" #include "webkit/glue/webview_impl.h" #include "webkit/glue/webwidget_impl.h" +#include "webkit/port/platform/chromium/PopupMenuChromium.h" #include "webkit/port/platform/graphics/PlatformContextSkia.h" // Get rid of WTF's pow define so we can use std::pow. @@ -115,6 +122,126 @@ static const double kMaxTextSizeMultiplier = 3.0; static const WebCore::DragOperation kDropTargetOperation = static_cast<WebCore::DragOperation>(DragOperationCopy | DragOperationLink); +// AutocompletePopupMenuClient +class AutocompletePopupMenuClient + : public RefCounted<AutocompletePopupMenuClient>, + public WebCore::PopupMenuClient { + public: + AutocompletePopupMenuClient(WebViewImpl* webview, + WebCore::HTMLInputElement* text_field, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index) + : text_field_(text_field), + selected_index_(default_suggestion_index), + webview_(webview) { + for (std::vector<std::wstring>::const_iterator iter = suggestions.begin(); + iter != suggestions.end(); ++iter) { + suggestions_.push_back(webkit_glue::StdWStringToString(*iter)); + } + } + virtual ~AutocompletePopupMenuClient() { + } + + virtual void valueChanged(unsigned listIndex, bool fireEvents = true) { + text_field_->setValue(suggestions_[listIndex]); + } + + virtual WebCore::String itemText(unsigned list_index) const { + return suggestions_[list_index]; + } + + virtual bool itemIsEnabled(unsigned listIndex) const { + return true; + } + + virtual PopupMenuStyle itemStyle(unsigned listIndex) const { + return menuStyle(); + } + + virtual PopupMenuStyle menuStyle() const { + RenderStyle* style = text_field_->renderStyle() ? + text_field_->renderStyle() : + text_field_->computedStyle(); + return PopupMenuStyle(style->color(), Color::white, style->font(), + style->visibility() == VISIBLE); + } + + virtual int clientInsetLeft() const { + return 0; + } + virtual int clientInsetRight() const { + return 0; + } + virtual int clientPaddingLeft() const { +#if defined(OS_WIN) + return theme()->popupInternalPaddingLeft(text_field_->computedStyle()); +#else + NOTIMPLEMENTED(); + return 0; +#endif + } + virtual int clientPaddingRight() const { +#if defined(OS_WIN) + return theme()->popupInternalPaddingRight(text_field_->computedStyle()); +#else + NOTIMPLEMENTED(); + return 0; +#endif + } + virtual int listSize() const { + return suggestions_.size(); + } + virtual int selectedIndex() const { + return selected_index_; + } + virtual void hidePopup() { + webview_->HideAutoCompletePopup(); + } + virtual bool itemIsSeparator(unsigned listIndex) const { + return false; + } + virtual bool itemIsLabel(unsigned listIndex) const { + return false; + } + virtual bool itemIsSelected(unsigned listIndex) const { + return false; + } + virtual bool shouldPopOver() const { + return false; + } + virtual bool valueShouldChangeOnHotTrack() const { + return false; + } + + virtual FontSelector* fontSelector() const { + return text_field_->document()->styleSelector()->fontSelector(); + } + + virtual void setTextFromItem(unsigned listIndex) { + text_field_->setValue(suggestions_[listIndex]); + } + + virtual HostWindow* hostWindow() const { + return text_field_->document()->view()->hostWindow(); + } + + virtual PassRefPtr<Scrollbar> createScrollbar( + ScrollbarClient* client, + ScrollbarOrientation orientation, + ScrollbarControlSize size) { + RefPtr<Scrollbar> widget = Scrollbar::createNativeScrollbar(client, + orientation, + size); + return widget.release(); + } + + private: + RefPtr<WebCore::HTMLInputElement> text_field_; + std::vector<WebCore::String> suggestions_; + int selected_index_; + WebViewImpl* webview_; +}; + // WebView ---------------------------------------------------------------- /*static*/ @@ -292,6 +419,18 @@ bool WebViewImpl::KeyEvent(const WebKeyboardEvent& event) { // event. suppress_next_keypress_event_ = false; + // Give autocomplete a chance to consume the key events it is interested in. + if (autocomplete_popup_ && + autocomplete_popup_->isInterestedInEventForKey(event.key_code)) { + if (autocomplete_popup_->handleKeyEvent(MakePlatformKeyboardEvent(event))) + return true; + return false; + } + + // A new key being pressed should hide the popup. + if (event.type == WebInputEvent::KEY_DOWN) + HideAutoCompletePopup(); + Frame* frame = GetFocusedWebCoreFrame(); if (!frame) return false; @@ -714,7 +853,6 @@ bool WebViewImpl::HandleInputEvent(const WebInputEvent* input_event) { // we're done. if (doing_drag_and_drop_) return true; - // TODO(eseidel): Remove g_current_input_event. // This only exists to allow ChromeClient::show() to know which mouse button // triggered a window.open event. @@ -782,6 +920,14 @@ void WebViewImpl::SetBackForwardListSize(int size) { void WebViewImpl::SetFocus(bool enable) { if (enable) { + // Hide the popup menu if any. + // TODO(jcampan): bug #3844: we should do that when we lose focus. The + // reason we are not doing it is because when clicking on the autofill + // popup, the page first loses focus before the mouse click is sent to the + // popup. So if we close when the focus is lost, the mouse click does not + // do anything. + HideAutoCompletePopup(); + // Getting the focused frame will have the side-effect of setting the main // frame as the focused frame if it is not already focused. Otherwise, if // there is already a focused frame, then this does nothing. @@ -803,8 +949,6 @@ void WebViewImpl::SetFocus(bool enable) { // updated below. ReleaseFocusReferences(); - // Clear focus on the currently focused frame if any. - if (!main_frame_) return; @@ -1018,7 +1162,8 @@ void WebViewImpl::SetInitialFocus(bool reverse) { // We have to set the key type explicitly to avoid an assert in the // KeyboardEvent constructor. platform_event.SetKeyType(PlatformKeyboardEvent::RawKeyDown); - RefPtr<KeyboardEvent> webkit_event = KeyboardEvent::create(platform_event, NULL); + RefPtr<KeyboardEvent> webkit_event = KeyboardEvent::create(platform_event, + NULL); page()->focusController()->setInitialFocus( reverse ? WebCore::FocusDirectionBackward : WebCore::FocusDirectionForward, @@ -1331,6 +1476,59 @@ SearchableFormData* WebViewImpl::CreateSearchableFormDataForFocusedNode() { return NULL; } +void WebViewImpl::AutofillSuggestionsForNode( + int64 node_id, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index) { + if (!main_frame_ || suggestions.empty()) + return; + + DCHECK(default_suggestion_index < static_cast<int>(suggestions.size())); + + Frame* frame = main_frame_->frame(); + if (!frame) + return; + + if (RefPtr<Frame> focused = + frame->page()->focusController()->focusedFrame()) { + RefPtr<Document> document = focused->document(); + if (!document.get()) + return; + + RefPtr<Node> focused_node = document->focusedNode(); + // If the node for which we queried the autofill suggestions is not the + // focused node, then we have nothing to do. + // TODO(jcampan): also check the carret is at the end and that the text has + // not changed. + if (!focused_node.get() || + reinterpret_cast<int64>(focused_node.get()) != node_id) + return; + + if (!focused_node->hasTagName(WebCore::HTMLNames::inputTag)) { + NOTREACHED(); + return; + } + + WebCore::HTMLInputElement* input_elem = + static_cast<WebCore::HTMLInputElement*>(focused_node.get()); + // Hide any current autocomplete popup. + HideAutoCompletePopup(); + + if (suggestions.size() > 0) { + autocomplete_popup_client_ = + adoptRef(new AutocompletePopupMenuClient(this, input_elem, + suggestions, + default_suggestion_index)); + // Autocomplete popup does not get focused. We need the page to still + // have focus so the user can keep typing when the popup is showing. + autocomplete_popup_ = + WebCore::PopupContainer::create(autocomplete_popup_client_.get(), + false); + autocomplete_popup_->show(focused_node->getRect(), frame->view(), 0); + } + } +} + void WebViewImpl::DidCommitLoad(bool* is_new_navigation) { if (is_new_navigation) *is_new_navigation = observed_new_navigation_; @@ -1479,3 +1677,11 @@ void WebViewImpl::DeleteImageResourceFetcher(ImageResourceFetcher* fetcher) { // deletion. MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher); } + +void WebViewImpl::HideAutoCompletePopup() { + if (autocomplete_popup_) { + autocomplete_popup_->hidePopup(); + autocomplete_popup_.clear(); + autocomplete_popup_client_.clear(); + } +} diff --git a/webkit/glue/webview_impl.h b/webkit/glue/webview_impl.h index da0d533..83cddebd 100644 --- a/webkit/glue/webview_impl.h +++ b/webkit/glue/webview_impl.h @@ -27,10 +27,12 @@ class HistoryItem; class KeyboardEvent; class Page; class PlatformKeyboardEvent; +class PopupContainer; class Range; class Widget; } +class AutocompletePopupMenuClient; class ImageResourceFetcher; class SearchableFormData; struct WebDropData; @@ -98,6 +100,10 @@ class WebViewImpl : public WebView, public WebCore::BackForwardListClient { virtual void DragTargetDragLeave(); virtual void DragTargetDrop( int client_x, int client_y, int screen_x, int screen_y); + virtual void AutofillSuggestionsForNode( + int64 node_id, + const std::vector<std::wstring>& suggestions, + int default_suggestion_index); // WebViewImpl @@ -178,6 +184,9 @@ class WebViewImpl : public WebView, public WebCore::BackForwardListClient { bool errored, const SkBitmap& image); + // Hides the autocomplete popup if it is showing. + void HideAutoCompletePopup(); + protected: friend class WebView; // So WebView::Create can call our constructor @@ -287,6 +296,13 @@ class WebViewImpl : public WebView, public WebCore::BackForwardListClient { // Represents whether or not this object should process incoming IME events. bool ime_accept_events_; + // The currently shown autocomplete popup. + RefPtr<WebCore::PopupContainer> autocomplete_popup_; + + // The popup client of the currently shown autocomplete popup. Necessary for + // managing the life of the client. + RefPtr<AutocompletePopupMenuClient> autocomplete_popup_client_; + // HACK: current_input_event is for ChromeClientImpl::show(), until we can fix // WebKit to pass enough information up into ChromeClient::show() so we can // decide if the window.open event was caused by a middle-mouse click diff --git a/webkit/port/page/chromium/ChromeClientChromium.h b/webkit/port/page/chromium/ChromeClientChromium.h index 5813770..783b5b5 100644 --- a/webkit/port/page/chromium/ChromeClientChromium.h +++ b/webkit/port/page/chromium/ChromeClientChromium.h @@ -26,7 +26,7 @@ namespace WebCore { // Notifies the client of a new popup widget. The client should place // and size the widget with the given bounds, relative to the screen. - virtual void popupOpened(FramelessScrollView* popupView, const IntRect& bounds) = 0; + virtual void popupOpened(FramelessScrollView* popupView, const IntRect& bounds, bool focus_on_show) = 0; }; } diff --git a/webkit/port/platform/chromium/PopupMenuChromium.cpp b/webkit/port/platform/chromium/PopupMenuChromium.cpp index d30df68..ae9f164 100644 --- a/webkit/port/platform/chromium/PopupMenuChromium.cpp +++ b/webkit/port/platform/chromium/PopupMenuChromium.cpp @@ -36,9 +36,9 @@ #include "ChromeClientChromium.h" #include "Document.h" #include "Font.h" -#include "Frame.h" #include "FrameView.h" #include "FontSelector.h" +#include "Frame.h" #include "FramelessScrollView.h" #include "FramelessScrollViewClient.h" #include "GraphicsContext.h" @@ -57,6 +57,8 @@ #include "Widget.h" #pragma warning(pop) +#include "webkit/port/platform/chromium/PopupMenuChromium.h" + using namespace WTF; using namespace Unicode; @@ -72,51 +74,6 @@ static const int kMaxHeight = 500; static const int kBorderSize = 1; static const TimeStamp kTypeAheadTimeoutMs = 1000; -class PopupListBox; - -// TODO(darin): Our FramelessScrollView classes need to implement HostWindow! - -// This class holds a PopupListBox. Its sole purpose is to be able to draw -// a border around its child. All its paint/event handling is just forwarded -// to the child listBox (with the appropriate transforms). -class PopupContainer : public FramelessScrollView, public RefCounted<PopupContainer> { -public: - static PassRefPtr<PopupContainer> create(PopupMenuClient* client); - - // FramelessScrollView - virtual void paint(GraphicsContext* gc, const IntRect& rect); - virtual void hide(); - virtual bool handleMouseDownEvent(const PlatformMouseEvent& event); - virtual bool handleMouseMoveEvent(const PlatformMouseEvent& event); - virtual bool handleMouseReleaseEvent(const PlatformMouseEvent& event); - virtual bool handleWheelEvent(const PlatformWheelEvent& event); - virtual bool handleKeyEvent(const PlatformKeyboardEvent& event); - - // PopupContainer methods - - // Show the popup - void showPopup(FrameView* view); - - // Hide the popup. Do not call this directly: use client->hidePopup(). - void hidePopup(); - - // Compute size of widget and children. - void layout(); - - PopupListBox* listBox() const { return m_listBox.get(); } - -private: - friend class RefCounted<PopupContainer>; - - PopupContainer(PopupMenuClient* client); - ~PopupContainer(); - - // Paint the border. - void paintBorder(GraphicsContext* gc, const IntRect& rect); - - RefPtr<PopupListBox> m_listBox; -}; - // This class uses WebCore code to paint and handle events for a drop-down list // box ("combobox" on Windows). class PopupListBox : public FramelessScrollView, public RefCounted<PopupListBox> { @@ -170,6 +127,9 @@ public: // Compute size of widget and children. void layout(); + // Returns whether the popup wants to process events for the passed key. + bool isInterestedInEventForKey(int key_code); + private: friend class PopupContainer; friend class RefCounted<PopupListBox>; @@ -327,13 +287,15 @@ static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& // PopupContainer implementation // static -PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client) +PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, + bool focusOnShow) { - return adoptRef(new PopupContainer(client)); + return adoptRef(new PopupContainer(client, focusOnShow)); } -PopupContainer::PopupContainer(PopupMenuClient* client) - : m_listBox(new PopupListBox(client)) +PopupContainer::PopupContainer(PopupMenuClient* client, bool focusOnShow) + : m_listBox(new PopupListBox(client)), + m_focusOnShow(focusOnShow) { // FrameViews are created with a refcount of 1 so it needs releasing after we // assign it to a RefPtr. @@ -367,7 +329,7 @@ void PopupContainer::showPopup(FrameView* view) if (widgetRect.bottom() > static_cast<int>(screen.bottom())) widgetRect.move(0, -(widgetRect.height() + selectHeight)); - chromeClient->popupOpened(this, widgetRect); + chromeClient->popupOpened(this, widgetRect, m_focusOnShow); } // Must get called after we have a client and containingWindow. @@ -474,6 +436,33 @@ void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect) gc->drawRect(IntRect(tx + width() - kBorderSize, ty, kBorderSize, height())); } +bool PopupContainer::isInterestedInEventForKey(int key_code) { + return m_listBox->isInterestedInEventForKey(key_code); +} + +void PopupContainer::show(const IntRect& r, FrameView* v, int index) { + // The rect is the size of the select box. It's usually larger than we need. + // subtract border size so that usually the container will be displayed + // exactly the same width as the select box. + listBox()->setBaseWidth(max(r.width() - kBorderSize * 2, 0)); + + listBox()->updateFromElement(); + + // We set the selected item in updateFromElement(), and disregard the + // index passed into this function (same as Webkit's PopupMenuWin.cpp) + // TODO(ericroman): make sure this is correct, and add an assertion. + // DCHECK(popupWindow(popup)->listBox()->selectedIndex() == index); + + // Convert point to main window coords. + IntPoint location = v->contentsToWindow(r.location()); + + // Move it below the select widget. + location.move(0, r.height()); + + IntRect popupRect(location, r.size()); + setFrameRect(popupRect); + showPopup(v); +} /////////////////////////////////////////////////////////////////////////////// // PopupListBox implementation @@ -548,6 +537,23 @@ bool PopupListBox::handleWheelEvent(const PlatformWheelEvent& event) return true; } +// Should be kept in sync with handleKeyEvent(). +bool PopupListBox::isInterestedInEventForKey(int key_code) { + switch (key_code) { + case VKEY_ESCAPE: + case VKEY_RETURN: + case VKEY_UP: + case VKEY_DOWN: + case VKEY_PRIOR: + case VKEY_NEXT: + case VKEY_HOME: + case VKEY_END: + return true; + default: + return false; + } +} + bool PopupListBox::handleKeyEvent(const PlatformKeyboardEvent& event) { if (event.type() == PlatformKeyboardEvent::KeyUp) @@ -1036,29 +1042,8 @@ PopupMenu::~PopupMenu() void PopupMenu::show(const IntRect& r, FrameView* v, int index) { - p.m_popup = PopupContainer::create(client()); - - // The rect is the size of the select box. It's usually larger than we need. - // subtract border size so that usually the container will be displayed - // exactly the same width as the select box. - p.m_popup->listBox()->setBaseWidth(max(r.width() - kBorderSize * 2, 0)); - - updateFromElement(); - - // We set the selected item in updateFromElement(), and disregard the - // index passed into this function (same as Webkit's PopupMenuWin.cpp) - // TODO(ericroman): make sure this is correct, and add an assertion. - // DCHECK(popupWindow(m_popup)->listBox()->selectedIndex() == index); - - // Convert point to main window coords. - IntPoint location = v->contentsToWindow(r.location()); - - // Move it below the select widget. - location.move(0, r.height()); - - IntRect popupRect(location, r.size()); - p.m_popup->setFrameRect(popupRect); - p.m_popup->showPopup(v); + p.m_popup = PopupContainer::create(client(), true); + p.m_popup->show(r, v, index); } void PopupMenu::hide() diff --git a/webkit/port/platform/chromium/PopupMenuChromium.h b/webkit/port/platform/chromium/PopupMenuChromium.h index e69de29..d419468 100644 --- a/webkit/port/platform/chromium/PopupMenuChromium.h +++ b/webkit/port/platform/chromium/PopupMenuChromium.h @@ -0,0 +1,82 @@ +// Copyright (c) 2006-2008 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 PopupMenuChromium_h +#define PopupMenuChromium_h + +#pragma warning(push, 0) +#include "PopupMenuClient.h" + +#include "FramelessScrollView.h" +#include "IntRect.h" +#pragma warning(pop) + + +namespace WebCore { + +class PopupListBox; + +// TODO(darin): Our FramelessScrollView classes need to implement HostWindow! + +// This class holds a PopupListBox (see cpp file). Its sole purpose is to be +// able to draw a border around its child. All its paint/event handling is just +// forwarded to the child listBox (with the appropriate transforms). +// NOTE: this class is exposed so it can be instantiated direcly for the +// autofill popup. We cannot use the Popup class directly in that case as the +// autofill popup should not be focused when shown and we want to forward the +// key events to it (through handleKeyEvent). + +class PopupContainer : public FramelessScrollView, public RefCounted<PopupContainer> { +public: + static PassRefPtr<PopupContainer> create(PopupMenuClient* client, + bool focusOnShow); + + // Whether a key event should be sent to this popup. + virtual bool isInterestedInEventForKey(int key_code); + + // FramelessScrollView + virtual void paint(GraphicsContext* gc, const IntRect& rect); + virtual void hide(); + virtual bool handleMouseDownEvent(const PlatformMouseEvent& event); + virtual bool handleMouseMoveEvent(const PlatformMouseEvent& event); + virtual bool handleMouseReleaseEvent(const PlatformMouseEvent& event); + virtual bool handleWheelEvent(const PlatformWheelEvent& event); + virtual bool handleKeyEvent(const PlatformKeyboardEvent& event); + + // PopupContainer methods + + // Show the popup + void showPopup(FrameView* view); + + // Show the popup in the specified rect for the specified frame. + // Note: this code was somehow arbitrarily factored-out of the Popup class + // so WebViewImpl can create a PopupContainer. + void show(const IntRect& r, FrameView* v, int index); + + // Hide the popup. Do not call this directly: use client->hidePopup(). + void hidePopup(); + + // Compute size of widget and children. + void layout(); + + PopupListBox* listBox() const { return m_listBox.get(); } + +private: + friend class WTF::RefCounted<PopupContainer>; + + PopupContainer(PopupMenuClient* client, bool focusOnShow); + ~PopupContainer(); + + // Paint the border. + void paintBorder(GraphicsContext* gc, const IntRect& rect); + + RefPtr<PopupListBox> m_listBox; + + // Whether the window showing this popup should be focused when shown. + bool m_focusOnShow; +}; + +} + +#endif // PopupMenuChromium_h diff --git a/webkit/webkit.xcodeproj/project.pbxproj b/webkit/webkit.xcodeproj/project.pbxproj index 990ba1b..77e4813 100644 --- a/webkit/webkit.xcodeproj/project.pbxproj +++ b/webkit/webkit.xcodeproj/project.pbxproj @@ -61,6 +61,7 @@ 4D16381F0EBFA4FB008F024E /* StorageArea.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D1638060EBFA4FB008F024E /* StorageArea.cpp */; }; 4D1638200EBFA4FC008F024E /* StorageEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D1638080EBFA4FB008F024E /* StorageEvent.cpp */; }; 4D1638210EBFA4FC008F024E /* StorageMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D16380A0EBFA4FB008F024E /* StorageMap.cpp */; }; + 4D163E990EC23D9D008F024E /* form_autocomplete_listener.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D163E970EC23D9D008F024E /* form_autocomplete_listener.cc */; }; 4D2A63EB0EBBBEDF00B55603 /* MediaPlayerPrivateChromium.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D2A63E90EBBBEDF00B55603 /* MediaPlayerPrivateChromium.cpp */; }; 4D2A64400EBBC0A700B55603 /* V8HTMLVideoElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D2A643A0EBBC0A700B55603 /* V8HTMLVideoElement.cpp */; }; 4D2A64410EBBC0A700B55603 /* V8MediaError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4D2A643C0EBBC0A700B55603 /* V8MediaError.cpp */; }; @@ -1483,6 +1484,9 @@ 4D1638090EBFA4FB008F024E /* StorageEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageEvent.h; sourceTree = "<group>"; }; 4D16380A0EBFA4FB008F024E /* StorageMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StorageMap.cpp; sourceTree = "<group>"; }; 4D16380B0EBFA4FB008F024E /* StorageMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageMap.h; sourceTree = "<group>"; }; + 4D163E970EC23D9D008F024E /* form_autocomplete_listener.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = form_autocomplete_listener.cc; sourceTree = "<group>"; }; + 4D163E980EC23D9D008F024E /* form_autocomplete_listener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = form_autocomplete_listener.h; sourceTree = "<group>"; }; + 4D163E9D0EC23DC6008F024E /* PopupMenuChromium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupMenuChromium.h; sourceTree = "<group>"; }; 4D2A63E90EBBBEDF00B55603 /* MediaPlayerPrivateChromium.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaPlayerPrivateChromium.cpp; sourceTree = "<group>"; }; 4D2A63EA0EBBBEDF00B55603 /* MediaPlayerPrivateChromium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaPlayerPrivateChromium.h; sourceTree = "<group>"; }; 4D2A643A0EBBC0A700B55603 /* V8HTMLVideoElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = V8HTMLVideoElement.cpp; sourceTree = "<group>"; }; @@ -7443,6 +7447,8 @@ 8254050F0D92E3DA0006B936 /* feed_preview.cc */, 825405100D92E3DA0006B936 /* feed_preview.h */, 825405110D92E3DA0006B936 /* find_in_page_request.h */, + 4D163E970EC23D9D008F024E /* form_autocomplete_listener.cc */, + 4D163E980EC23D9D008F024E /* form_autocomplete_listener.h */, 825405120D92E3DA0006B936 /* form_data.h */, 825405140D92E3DA0006B936 /* glue_serialize.cc */, 825405150D92E3DA0006B936 /* glue_serialize.h */, @@ -7782,6 +7788,7 @@ E473F6520EAE002F006C2098 /* PlatformCursor.h */, E473F65D0EAE009A006C2098 /* PlatformWidget.h */, E473F65E0EAE00A6006C2098 /* PopupMenuChromium.cpp */, + 4D163E9D0EC23DC6008F024E /* PopupMenuChromium.h */, E473F65F0EAE00A6006C2098 /* PopupMenuPrivate.h */, E473F6630EAE00E4006C2098 /* SearchPopupMenuChromium.cpp */, E473F6650EAE00F4006C2098 /* SharedTimerChromium.cpp */, @@ -9261,6 +9268,7 @@ E45626FE0E268F03005E4685 /* editor_client_impl.cc in Sources */, E456270A0E268F03005E4685 /* event_conversion.cc in Sources */, E45628370E269DBB005E4685 /* feed_preview.cc in Sources */, + 4D163E990EC23D9D008F024E /* form_autocomplete_listener.cc in Sources */, E45626FA0E268F03005E4685 /* glue_serialize.cc in Sources */, E45626F50E268F03005E4685 /* glue_util.cc in Sources */, E4A238C70E3FA19C002BDE14 /* image_decoder.cc in Sources */, |