diff options
author | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-05 04:38:06 +0000 |
---|---|---|
committer | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-05 04:38:06 +0000 |
commit | 6a8ddba5ea1e8972c2954b138aeb14d77a548a3c (patch) | |
tree | 4c1a21857eca96cea8a8743a28209041de389699 /chrome/renderer | |
parent | e333e8d07ee32668fc132a21c13cdf674443d1e9 (diff) | |
download | chromium_src-6a8ddba5ea1e8972c2954b138aeb14d77a548a3c.zip chromium_src-6a8ddba5ea1e8972c2954b138aeb14d77a548a3c.tar.gz chromium_src-6a8ddba5ea1e8972c2954b138aeb14d77a548a3c.tar.bz2 |
Adding a class to track when a node in a page gets clicked.
This is going to be used by the password/form autofill to trigger the
suggestion popup, as part of the effort in moving that code out of WebKit.
BUG=None
TEST=Run unit-tests.
Review URL: http://codereview.chromium.org/3090007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58597 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/autofill_helper.cc | 13 | ||||
-rw-r--r-- | chrome/renderer/autofill_helper.h | 8 | ||||
-rw-r--r-- | chrome/renderer/form_autocomplete_browsertest.cc | 1 | ||||
-rw-r--r-- | chrome/renderer/page_click_listener.h | 30 | ||||
-rw-r--r-- | chrome/renderer/page_click_tracker.cc | 127 | ||||
-rw-r--r-- | chrome/renderer/page_click_tracker.h | 87 | ||||
-rw-r--r-- | chrome/renderer/page_click_tracker_unittest.cc | 120 | ||||
-rw-r--r-- | chrome/renderer/password_autocomplete_manager.cc | 16 | ||||
-rw-r--r-- | chrome/renderer/password_autocomplete_manager.h | 15 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 65 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 22 | ||||
-rw-r--r-- | chrome/renderer/render_widget.cc | 5 | ||||
-rw-r--r-- | chrome/renderer/render_widget.h | 5 | ||||
-rw-r--r-- | chrome/renderer/translate_helper.cc | 22 |
14 files changed, 479 insertions, 57 deletions
diff --git a/chrome/renderer/autofill_helper.cc b/chrome/renderer/autofill_helper.cc index 6e8659b..3737726 100644 --- a/chrome/renderer/autofill_helper.cc +++ b/chrome/renderer/autofill_helper.cc @@ -9,10 +9,12 @@ #include "chrome/renderer/render_view.h" #include "grit/generated_resources.h" #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" -#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/WebKit/chromium/public/WebFormControlElement.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" #include "third_party/WebKit/WebKit/chromium/public/WebView.h" +#include "webkit/glue/form_data.h" +#include "webkit/glue/form_field.h" #include "webkit/glue/password_form.h" using WebKit::WebFormControlElement; @@ -216,12 +218,15 @@ void AutoFillHelper::TextDidChangeInTextField(const WebInputElement& element) { ShowSuggestions(element, false, true); } -void AutoFillHelper::InputElementClicked(const WebInputElement& element, - bool already_focused) { - if (already_focused) +bool AutoFillHelper::InputElementClicked(const WebInputElement& element, + bool was_focused, + bool is_focused) { + if (was_focused) ShowSuggestions(element, true, false); + return false; } + void AutoFillHelper::ShowSuggestions( const WebInputElement& const_element, bool autofill_on_empty_values, diff --git a/chrome/renderer/autofill_helper.h b/chrome/renderer/autofill_helper.h index 64b04c8..d4940d0 100644 --- a/chrome/renderer/autofill_helper.h +++ b/chrome/renderer/autofill_helper.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "chrome/renderer/form_manager.h" +#include "chrome/renderer/page_click_listener.h" #include "third_party/WebKit/WebKit/chromium/public/WebNode.h" class RenderView; @@ -26,7 +27,7 @@ class WebString; // - single text field suggestions, that we usually refer to as Autocomplete // - entire form fill based on one field entry, referred to as form AutoFill. -class AutoFillHelper { +class AutoFillHelper : public PageClickListener { public: explicit AutoFillHelper(RenderView* render_view); @@ -99,6 +100,11 @@ class AutoFillHelper { AUTOFILL_PREVIEW, // Preview the AutoFill form data. }; + // PageClickListener implementation: + virtual bool InputElementClicked(const WebKit::WebInputElement& element, + bool was_focused, + bool is_focused); + // Shows the autocomplete suggestions for |element|. // This call is asynchronous and may or may not lead to the showing of a // suggestion popup (no popup is shown if there are no available suggestions). diff --git a/chrome/renderer/form_autocomplete_browsertest.cc b/chrome/renderer/form_autocomplete_browsertest.cc index 164afa2..55429bd 100644 --- a/chrome/renderer/form_autocomplete_browsertest.cc +++ b/chrome/renderer/form_autocomplete_browsertest.cc @@ -6,6 +6,7 @@ #include "chrome/test/render_view_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFormElement.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/WebKit/chromium/public/WebURLError.h" #include "webkit/glue/form_data.h" diff --git a/chrome/renderer/page_click_listener.h b/chrome/renderer/page_click_listener.h new file mode 100644 index 0000000..fca52c8 --- /dev/null +++ b/chrome/renderer/page_click_listener.h @@ -0,0 +1,30 @@ +// 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 CHROME_RENDERER_PAGE_CLICK_LISTENER_H_ +#define CHROME_RENDERER_PAGE_CLICK_LISTENER_H_ + +namespace WebKit { +class WebInputElement; +} + +// Interface that should be implemented by classes interested in getting +// notifications for clicks on a page. +// Register on the PageListenerTracker object. +class PageClickListener { + public: + // Notification that |element| was clicked. + // |was_focused| is true if |element| had focus BEFORE the click. + // |is_focused| is true if |element| has focus AFTER the click was processed. + // If this method returns true, the notification will not be propagated to + // other listeners. + virtual bool InputElementClicked(const WebKit::WebInputElement& element, + bool was_focused, + bool is_focused) = 0; + + protected: + virtual ~PageClickListener() {} +}; + +#endif // CHROME_RENDERER_PAGE_CLICK_LISTENER_H_ diff --git a/chrome/renderer/page_click_tracker.cc b/chrome/renderer/page_click_tracker.cc new file mode 100644 index 0000000..e059bd3 --- /dev/null +++ b/chrome/renderer/page_click_tracker.cc @@ -0,0 +1,127 @@ +// 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 "chrome/renderer/page_click_tracker.h" + +#include "chrome/renderer/page_click_listener.h" +#include "chrome/renderer/render_view.h" +#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" +#include "third_party/WebKit/WebKit/chromium/public/WebDOMMouseEvent.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" +#include "third_party/WebKit/WebKit/chromium/public/WebString.h" +#include "third_party/WebKit/WebKit/chromium/public/WebView.h" + +using WebKit::WebDOMEvent; +using WebKit::WebDOMMouseEvent; +using WebKit::WebElement; +using WebKit::WebFormControlElement; +using WebKit::WebFrame; +using WebKit::WebInputElement; +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; +using WebKit::WebNode; +using WebKit::WebString; +using WebKit::WebView; + +PageClickTracker::PageClickTracker(RenderView* render_view) + : render_view_(render_view), + was_focused_(false) { +} + +PageClickTracker::~PageClickTracker() { + // Note that even though RenderView calls StopTrackingFrame when notified that + // a frame was closed, it might not always get that notification from WebKit + // for all frames. + // By the time we get here, the frame could have been destroyed so we cannot + // unregister listeners in frames remaining in tracked_frames_ as they might + // be invalid. +} + +void PageClickTracker::StartTrackingFrame(WebFrame* frame) { + tracked_frames_.push_back(frame); + frame->document().addEventListener("mousedown", this, false); +} + +void PageClickTracker::StopTrackingFrame(WebFrame* frame, bool frame_detached) { + FrameList::iterator iter = + std::find(tracked_frames_.begin(), tracked_frames_.end(), frame); + if (iter == tracked_frames_.end()) { + // Some frames might never load contents so we may not have a listener on + // them. Calling removeEventListener() on them would trigger an assert, so + // we need to keep track of which frames we are listening to. + return; + } + tracked_frames_.erase(iter); + // If the frame has been detached, all event listeners have already been + // removed. + if (!frame_detached) + frame->document().removeEventListener("mousedown", this, false); +} + +void PageClickTracker::DidHandleMouseEvent(const WebMouseEvent& event) { + if (event.type != WebInputEvent::MouseDown || + last_node_clicked_.isNull()) { + return; + } + + // We are only interested in text field clicks. + if (!last_node_clicked_.isElementNode()) + return; + const WebElement& element = last_node_clicked_.toConst<WebElement>(); + if (!element.isFormControlElement()) + return; + const WebFormControlElement& control = + element.toConst<WebFormControlElement>(); + if (control.formControlType() != WebString::fromUTF8("text")) + return; + + const WebInputElement& input_element = element.toConst<WebInputElement>(); + + bool is_focused = (last_node_clicked_ == GetFocusedNode()); + ObserverListBase<PageClickListener>::Iterator it(listeners_); + PageClickListener* listener; + while ((listener = it.GetNext()) != NULL) { + if (listener->InputElementClicked(input_element, was_focused_, is_focused)) + break; + } + + last_node_clicked_.reset(); +} + +void PageClickTracker::AddListener(PageClickListener* listener) { + listeners_.AddObserver(listener); +} + +void PageClickTracker::RemoveListener(PageClickListener* listener) { + listeners_.RemoveObserver(listener); +} + +void PageClickTracker::handleEvent(const WebDOMEvent& event) { + last_node_clicked_.reset(); + + DCHECK(event.isMouseEvent()); + const WebDOMMouseEvent mouse_event = event.toConst<WebDOMMouseEvent>(); + DCHECK(mouse_event.buttonDown()); + if (mouse_event.button() != 0) + return; // We are only interested in left clicks. + + // Remember which node has focus before the click is processed. + // We'll get a notification once the mouse event has been processed + // (DidHandleMouseEvent), we'll notify the listener at that point. + last_node_clicked_ = mouse_event.target(); + was_focused_ = (GetFocusedNode() == last_node_clicked_); +} + +WebNode PageClickTracker::GetFocusedNode() { + WebView* web_view = render_view_->webview(); + if (!web_view) + return WebNode(); + + WebFrame* web_frame = web_view->focusedFrame(); + if (!web_frame) + return WebNode(); + + return web_frame->document().focusedNode(); +} diff --git a/chrome/renderer/page_click_tracker.h b/chrome/renderer/page_click_tracker.h new file mode 100644 index 0000000..492237c --- /dev/null +++ b/chrome/renderer/page_click_tracker.h @@ -0,0 +1,87 @@ +// 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 CHROME_RENDERER_PAGE_CLICK_TRACKER_H_ +#define CHROME_RENDERER_PAGE_CLICK_TRACKER_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "third_party/WebKit/WebKit/chromium/public/WebDOMEventListener.h" +#include "third_party/WebKit/WebKit/chromium/public/WebNode.h" + +class PageClickListener; +class RenderView; + +namespace WebKit { +class WebFrame; +class WebDOMEvent; +class WebMouseEvent; +} + +// This class is responsible for tracking clicks on elements in web pages and +// notifiying the associated listener when a node is clicked. +// Compared to a simple WebDOMEventListener, it offers the added capability of +// notifying the listeners of whether the clicked node was already focused +// before it was clicked. +// +// This is useful for password/form autofill where we want to trigger a +// suggestion popup when a text input is clicked. +// It only notifies of WebInputElement that are text inputs being clicked, but +// could easily be changed to report click on any type of WebNode. +// +// There is one PageClickTracker per RenderView. + +class PageClickTracker : public WebKit::WebDOMEventListener { + public: + explicit PageClickTracker(RenderView* render_view); + virtual ~PageClickTracker(); + + // Starts reporting node clicks for |frame| on the listener previously + // specified with SetListener(). + void StartTrackingFrame(WebKit::WebFrame* frame); + + // Stops reporting node clicks for |frame|. |frame_detached| should be true + // if the frame has already been detached. + void StopTrackingFrame(WebKit::WebFrame* frame, bool frame_detached); + + // Called after the mouse event |event| has been processed by WebKit. + void DidHandleMouseEvent(const WebKit::WebMouseEvent& event); + + // Adds/removes a listener for getting notification when an element is + // clicked. Note that the order of insertion is important as a listener when + // notified can decide to stop the propagation of the event (so that listeners + // inserted after don't get the notification). + void AddListener(PageClickListener* listener); + void RemoveListener(PageClickListener* listener); + + private: + // WebKit::WebDOMEventListener implementation. + virtual void handleEvent(const WebKit::WebDOMEvent& event); + + // Returns the currently focused node in the associated render view. + // That node may be null. + WebKit::WebNode GetFocusedNode(); + + // The last node that was clicked and had focus. + WebKit::WebNode last_node_clicked_; + + // The render view we are associated with. + RenderView* render_view_; + + // Whether the last clicked node had focused before it was clicked. + bool was_focused_; + + // The frames we are listening to for mouse events. + typedef std::vector<WebKit::WebFrame*> FrameList; + FrameList tracked_frames_; + + // The listener getting the actual notifications. + ObserverList<PageClickListener> listeners_; + + DISALLOW_COPY_AND_ASSIGN(PageClickTracker); +}; + +#endif // CHROME_RENDERER_PAGE_CLICK_TRACKER_H_ diff --git a/chrome/renderer/page_click_tracker_unittest.cc b/chrome/renderer/page_click_tracker_unittest.cc new file mode 100644 index 0000000..dcba0bb --- /dev/null +++ b/chrome/renderer/page_click_tracker_unittest.cc @@ -0,0 +1,120 @@ +// 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 "base/basictypes.h" + +#include "chrome/common/render_messages.h" +#include "chrome/renderer/page_click_listener.h" +#include "chrome/renderer/page_click_tracker.h" +#include "chrome/test/render_view_test.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" +#include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSize.h" +#include "third_party/WebKit/WebKit/chromium/public/WebView.h" + +class TestPageClickListener : public PageClickListener { + public: + TestPageClickListener() + : called_(false), + was_focused_(false), + is_focused_(false), + notification_response_(false) { + } + + virtual bool InputElementClicked(const WebKit::WebInputElement& element, + bool was_focused, + bool is_focused) { + called_ = true; + element_clicked_ = element; + was_focused_ = was_focused; + is_focused_ = is_focused; + return notification_response_; + } + + void ClearResults() { + called_ = false; + element_clicked_.reset(); + was_focused_ = false; + is_focused_ = false; + } + + bool called_; + WebKit::WebInputElement element_clicked_; + bool was_focused_; + bool is_focused_; + bool notification_response_; +}; + +// Tests that PageClickTracker does notify correctly when a node is clicked. +TEST_F(RenderViewTest, PageClickTracker) { + TestPageClickListener test_listener1; + TestPageClickListener test_listener2; + view_->page_click_tracker()->AddListener(&test_listener1); + view_->page_click_tracker()->AddListener(&test_listener2); + + LoadHTML("<form>" + " <input type='text' id='text'></input><br>" + " <input type='button' id='button'></input><br>" + "</form>"); + view_->webwidget()->resize(WebKit::WebSize(500, 500)); + view_->webwidget()->setFocus(true); + WebKit::WebDocument document = view_->webview()->mainFrame()->document(); + WebKit::WebElement text = document.getElementById("text"); + ASSERT_FALSE(text.isNull()); + WebKit::WebElement button = document.getElementById("button"); + ASSERT_FALSE(button.isNull()); + + // Click the text field once. + EXPECT_TRUE(SimulateElementClick("text")); + EXPECT_TRUE(test_listener1.called_); + EXPECT_TRUE(test_listener2.called_); + EXPECT_FALSE(test_listener1.was_focused_); + EXPECT_FALSE(test_listener2.was_focused_); + EXPECT_TRUE(test_listener1.is_focused_); + EXPECT_TRUE(test_listener2.is_focused_); + EXPECT_TRUE(text == test_listener1.element_clicked_); + EXPECT_TRUE(text == test_listener2.element_clicked_); + test_listener1.ClearResults(); + test_listener2.ClearResults(); + + // Click the text field again to test that was_focused_ is set correctly. + EXPECT_TRUE(SimulateElementClick("text")); + EXPECT_TRUE(test_listener1.called_); + EXPECT_TRUE(test_listener2.called_); + EXPECT_TRUE(test_listener1.was_focused_); + EXPECT_TRUE(test_listener2.was_focused_); + EXPECT_TRUE(test_listener1.is_focused_); + EXPECT_TRUE(test_listener2.is_focused_); + EXPECT_TRUE(text == test_listener1.element_clicked_); + EXPECT_TRUE(text == test_listener2.element_clicked_); + test_listener1.ClearResults(); + test_listener2.ClearResults(); + + // Click the button, no notification should happen (this is not a text-input). + EXPECT_TRUE(SimulateElementClick("button")); + EXPECT_FALSE(test_listener1.called_); + EXPECT_FALSE(test_listener2.called_); + + // Make the first listener stop the event propagation, click the text field + // and make sure only the first listener is notified. + test_listener1.notification_response_ = true; + EXPECT_TRUE(SimulateElementClick("text")); + EXPECT_TRUE(test_listener1.called_); + EXPECT_FALSE(test_listener2.called_); + test_listener1.ClearResults(); + + // Make sure removing a listener work. + view_->page_click_tracker()->RemoveListener(&test_listener1); + EXPECT_TRUE(SimulateElementClick("text")); + EXPECT_FALSE(test_listener1.called_); + EXPECT_TRUE(test_listener2.called_); + test_listener2.ClearResults(); + + // Make sure we don't choke when no listeners are registered. + view_->page_click_tracker()->RemoveListener(&test_listener2); + EXPECT_TRUE(SimulateElementClick("text")); + EXPECT_FALSE(test_listener1.called_); + EXPECT_FALSE(test_listener2.called_); +} diff --git a/chrome/renderer/password_autocomplete_manager.cc b/chrome/renderer/password_autocomplete_manager.cc index c460db4..3e49a6d 100644 --- a/chrome/renderer/password_autocomplete_manager.cc +++ b/chrome/renderer/password_autocomplete_manager.cc @@ -305,11 +305,6 @@ void PasswordAutocompleteManager::TextFieldHandlingKeyDown( (win_key_code == app::VKEY_BACK || win_key_code == app::VKEY_DELETE); } -bool PasswordAutocompleteManager::InputElementClicked( - const WebKit::WebInputElement& element, bool already_focused) { - return false; -} - bool PasswordAutocompleteManager::FillPassword( const WebKit::WebInputElement& user_input) { LoginToPasswordInfoMap::iterator iter = @@ -386,6 +381,17 @@ void PasswordAutocompleteManager::SendPasswordForms(WebKit::WebFrame* frame, } //////////////////////////////////////////////////////////////////////////////// +// PageClickListener implementation: + +bool PasswordAutocompleteManager::InputElementClicked( + const WebKit::WebInputElement& element, + bool was_focused, + bool is_focused) { + // TODO(jcivelli): http://crbug.com/51644 Implement behavior. + return false; +} + +//////////////////////////////////////////////////////////////////////////////// // PasswordAutocompleteManager, private: void PasswordAutocompleteManager::GetSuggestions( diff --git a/chrome/renderer/password_autocomplete_manager.h b/chrome/renderer/password_autocomplete_manager.h index 40cc893..cf6c4eb 100644 --- a/chrome/renderer/password_autocomplete_manager.h +++ b/chrome/renderer/password_autocomplete_manager.h @@ -10,6 +10,7 @@ #include <vector> #include "base/task.h" +#include "chrome/renderer/page_click_listener.h" #include "webkit/glue/password_form_dom_manager.h" #include "third_party/WebKit/WebKit/chromium/public/WebInputElement.h" @@ -21,7 +22,7 @@ class WebView; // This class is responsible for filling password forms. // There is one PasswordAutocompleteManager per RenderView. -class PasswordAutocompleteManager { +class PasswordAutocompleteManager : public PageClickListener { public: explicit PasswordAutocompleteManager(RenderView* render_view); virtual ~PasswordAutocompleteManager(); @@ -61,13 +62,6 @@ class PasswordAutocompleteManager { void TextFieldHandlingKeyDown(const WebKit::WebInputElement& element, const WebKit::WebKeyboardEvent& event); - // Called when an input element in the page has been clicked. - // |already_focused| is true if |element| was focused before it was clicked. - // Returns true if the call triggered a suggestion popup. - // TODO(jcivelli): http://crbug.com/51644 Implement behavior. - bool InputElementClicked(const WebKit::WebInputElement& element, - bool already_focused); - private: struct PasswordInfo { WebKit::WebInputElement password_field; @@ -77,6 +71,11 @@ class PasswordAutocompleteManager { }; typedef std::map<WebKit::WebElement, PasswordInfo> LoginToPasswordInfoMap; + // PageClickListener implementation: + virtual bool InputElementClicked(const WebKit::WebInputElement& element, + bool was_focused, + bool is_focused); + void GetSuggestions( const webkit_glue::PasswordFormFillData& fill_data, const string16& input, diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 9185d48..0c2eb5a 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -44,6 +44,7 @@ #include "chrome/common/window_container_type.h" #include "chrome/renderer/about_handler.h" #include "chrome/renderer/audio_message_filter.h" +#include "chrome/renderer/autofill_helper.h" #include "chrome/renderer/blocked_plugin.h" #include "chrome/renderer/device_orientation_dispatcher.h" #include "chrome/renderer/devtools_agent.h" @@ -60,6 +61,8 @@ #include "chrome/renderer/media/ipc_video_renderer.h" #include "chrome/renderer/navigation_state.h" #include "chrome/renderer/notification_provider.h" +#include "chrome/renderer/page_click_tracker.h" +#include "chrome/renderer/password_autocomplete_manager.h" #include "chrome/renderer/plugin_channel_host.h" #include "chrome/renderer/print_web_view_helper.h" #include "chrome/renderer/render_process.h" @@ -458,13 +461,19 @@ RenderView::RenderView(RenderThreadBase* render_thread, ALLOW_THIS_IN_INITIALIZER_LIST(page_info_method_factory_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(autofill_method_factory_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(translate_helper_(this)), - ALLOW_THIS_IN_INITIALIZER_LIST(password_autocomplete_manager_(this)), - ALLOW_THIS_IN_INITIALIZER_LIST(autofill_helper_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(cookie_jar_(this)), ALLOW_THIS_IN_INITIALIZER_LIST( notification_provider_(new NotificationProvider(this))), session_storage_namespace_id_(session_storage_namespace_id), decrement_shared_popup_at_destruction_(false) { + password_autocomplete_manager_.reset(new PasswordAutocompleteManager(this)); + autofill_helper_.reset(new AutoFillHelper(this)); + page_click_tracker_.reset(new PageClickTracker(this)); + // Note that the order of insertion of the listeners is important. + // The password_autocomplete_manager_ takes the first shot at processing the + // notification and can stop the propagation. + page_click_tracker_->AddListener(password_autocomplete_manager_.get()); + page_click_tracker_->AddListener(autofill_helper_.get()); ClearBlockedContentSettings(); } @@ -1605,13 +1614,13 @@ void RenderView::OnAutoFillSuggestionsReturned( const std::vector<string16>& labels, const std::vector<string16>& icons, const std::vector<int>& unique_ids) { - autofill_helper_.SuggestionsReceived( + autofill_helper_->SuggestionsReceived( query_id, values, labels, icons, unique_ids); } void RenderView::OnAutoFillFormDataFilled(int query_id, const webkit_glue::FormData& form) { - autofill_helper_.FormDataFilled(query_id, form); + autofill_helper_->FormDataFilled(query_id, form); } void RenderView::OnAllowScriptToClose(bool script_can_close) { @@ -1873,7 +1882,7 @@ void RenderView::didExecuteCommand(const WebString& command_name) { void RenderView::textFieldDidEndEditing( const WebKit::WebInputElement& element) { #if defined(WEBKIT_BUG_41283_IS_FIXED) - password_autocomplete_manager_.TextFieldDidEndEditing(element); + password_autocomplete_manager_->TextFieldDidEndEditing(element); #endif } @@ -1892,16 +1901,16 @@ void RenderView::textFieldDidChange(const WebKit::WebInputElement& element) { void RenderView::TextFieldDidChangeImpl( const WebKit::WebInputElement& element) { - if (password_autocomplete_manager_.TextDidChangeInTextField(element)) + if (password_autocomplete_manager_->TextDidChangeInTextField(element)) return; - autofill_helper_.TextDidChangeInTextField(element); + autofill_helper_->TextDidChangeInTextField(element); } void RenderView::textFieldDidReceiveKeyDown( const WebKit::WebInputElement& element, const WebKit::WebKeyboardEvent& event) { #if defined(WEBKIT_BUG_41283_IS_FIXED) - password_autocomplete_manager_.TextFieldHandlingKeyDown(element, event); + password_autocomplete_manager_->TextFieldHandlingKeyDown(element, event); #endif } @@ -1933,11 +1942,11 @@ bool RenderView::handleCurrentKeyboardEvent() { void RenderView::inputElementClicked(const WebKit::WebInputElement& element, bool already_focused) { #if defined(WEBKIT_BUG_41283_IS_FIXED) - if (password_autocomplete_manager_.InputElementClicked(element, - already_focused)) { + if (password_autocomplete_manager_->InputElementClicked(element, + already_focused)) { return; } - autofill_helper_.InputElementClicked(element, already_focused); + autofill_helper_->InputElementClicked(element, already_focused); #endif } @@ -2198,12 +2207,12 @@ void RenderView::didUpdateInspectorSetting(const WebString& key, void RenderView::queryAutofillSuggestions(const WebNode& node, const WebString& name, const WebString& value) { - autofill_helper_.QueryAutoFillSuggestions(node, name, value); + autofill_helper_->QueryAutoFillSuggestions(node, name, value); } void RenderView::removeAutofillSuggestions(const WebString& name, const WebString& value) { - autofill_helper_.RemoveAutocompleteSuggestion(name, value); + autofill_helper_->RemoveAutocompleteSuggestion(name, value); } void RenderView::didAcceptAutoFillSuggestion(const WebKit::WebNode& node, @@ -2211,25 +2220,25 @@ void RenderView::didAcceptAutoFillSuggestion(const WebKit::WebNode& node, const WebKit::WebString& label, int unique_id, unsigned index) { - autofill_helper_.DidAcceptAutoFillSuggestion(node, value, label, unique_id, - index); + autofill_helper_->DidAcceptAutoFillSuggestion(node, value, label, unique_id, + index); } void RenderView::didSelectAutoFillSuggestion(const WebKit::WebNode& node, const WebKit::WebString& value, const WebKit::WebString& label, int unique_id) { - autofill_helper_.DidSelectAutoFillSuggestion(node, value, label, unique_id); + autofill_helper_->DidSelectAutoFillSuggestion(node, value, label, unique_id); } void RenderView::didClearAutoFillSelection(const WebKit::WebNode& node) { - autofill_helper_.DidClearAutoFillSelection(node); + autofill_helper_->DidClearAutoFillSelection(node); } void RenderView::didAcceptAutocompleteSuggestion( const WebKit::WebInputElement& user_element) { #if defined(WEBKIT_BUG_41283_IS_FIXED) - bool result = password_autocomplete_manager_.FillPassword(user_element); + bool result = password_autocomplete_manager_->FillPassword(user_element); // Since this user name was selected from a suggestion list, we should always // have password for it. DCHECK(result); @@ -2489,7 +2498,8 @@ WebCookieJar* RenderView::cookieJar() { } void RenderView::frameDetached(WebFrame* frame) { - autofill_helper_.FrameDetached(frame); + autofill_helper_->FrameDetached(frame); + page_click_tracker_->StopTrackingFrame(frame, true); } void RenderView::willClose(WebFrame* frame) { @@ -2511,7 +2521,7 @@ void RenderView::willClose(WebFrame* frame) { navigation_state->user_script_idle_scheduler()->Cancel(); // TODO(jhawkins): Remove once frameDetached is called by WebKit. - autofill_helper_.FrameWillClose(frame); + autofill_helper_->FrameWillClose(frame); } bool RenderView::allowImages(WebFrame* frame, bool enabled_per_settings) { @@ -3089,10 +3099,11 @@ void RenderView::didFinishDocumentLoad(WebFrame* frame) { Send(new ViewHostMsg_DocumentLoadedInFrame(routing_id_)); + page_click_tracker_->StartTrackingFrame(frame); // The document has now been fully loaded. Scan for forms to be sent up to // the browser. - autofill_helper_.FrameContentsAvailable(frame); - password_autocomplete_manager_.SendPasswordForms(frame, false); + autofill_helper_->FrameContentsAvailable(frame); + password_autocomplete_manager_->SendPasswordForms(frame, false); // Check whether we have new encoding name. UpdateEncoding(frame, frame->view()->pageEncoding().utf8()); @@ -3139,7 +3150,7 @@ void RenderView::didFinishLoad(WebFrame* frame) { navigation_state->user_script_idle_scheduler()->DidFinishLoad(); // Let the password manager know which password forms are actually visible. - password_autocomplete_manager_.SendPasswordForms(frame, true); + password_autocomplete_manager_->SendPasswordForms(frame, true); } void RenderView::didNavigateWithinPage( @@ -4148,7 +4159,7 @@ void RenderView::OnDragSourceSystemDragEnded() { void RenderView::OnFillPasswordForm( const webkit_glue::PasswordFormFillData& form_data) { #if defined(WEBKIT_BUG_41283_IS_FIXED) - password_autocomplete_manager_.ReceivedPasswordFormFillData(webview(), + password_autocomplete_manager_->ReceivedPasswordFormFillData(webview(), form_data); #else webkit_glue::FillPasswordForm(this->webview(), form_data); @@ -5455,6 +5466,10 @@ void RenderView::DidHandleKeyEvent() { edit_commands_.clear(); } +void RenderView::DidHandleMouseEvent(const WebKit::WebMouseEvent& event) { + page_click_tracker_->DidHandleMouseEvent(event); +} + #if defined(OS_MACOSX) void RenderView::OnWasHidden() { RenderWidget::OnWasHidden(); @@ -5589,7 +5604,7 @@ void RenderView::OnPageTranslated() { return; // The page is translated, so try to extract the form data again. - autofill_helper_.FrameContentsAvailable(frame); + autofill_helper_->FrameContentsAvailable(frame); } WebKit::WebGeolocationService* RenderView::geolocationService() { diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index b318548..cf06c09 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -28,11 +28,9 @@ #include "chrome/common/render_messages.h" #include "chrome/common/renderer_preferences.h" #include "chrome/common/view_types.h" -#include "chrome/renderer/autofill_helper.h" #include "chrome/renderer/automation/dom_automation_controller.h" #include "chrome/renderer/dom_ui_bindings.h" #include "chrome/renderer/external_host_bindings.h" -#include "chrome/renderer/password_autocomplete_manager.h" #include "chrome/renderer/pepper_plugin_delegate_impl.h" #include "chrome/renderer/render_widget.h" #include "chrome/renderer/renderer_webcookiejar_impl.h" @@ -56,6 +54,7 @@ #endif class AudioMessageFilter; +class AutoFillHelper; class DictionaryValue; class DeviceOrientationDispatcher; class DevToolsAgent; @@ -66,6 +65,8 @@ class GURL; class ListValue; class NavigationState; class NotificationProvider; +class PageClickTracker; +class PasswordAutocompleteManager; class PepperDeviceTest; class PluginGroup; class PrintWebViewHelper; @@ -111,6 +112,7 @@ class WebInputElement; class WebKeyboardEvent; class WebMediaPlayer; class WebMediaPlayerClient; +class WebMouseEvent; class WebNode; class WebPlugin; class WebSpeechInputController; @@ -209,6 +211,10 @@ class RenderView : public RenderWidget, send_content_state_immediately_ = value; } + PageClickTracker* page_click_tracker() const { + return page_click_tracker_.get(); + } + // Called from JavaScript window.external.AddSearchProvider() to add a // keyword for a provider described in the given OpenSearch document. void AddSearchProvider(const std::string& url); @@ -592,6 +598,8 @@ class RenderView : public RenderWidget, virtual void DidInitiatePaint(); virtual void DidFlushPaint(); virtual void DidHandleKeyEvent(); + virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event); + #if OS_MACOSX virtual void OnSetFocus(bool enable); virtual void OnWasHidden(); @@ -1211,11 +1219,17 @@ class RenderView : public RenderWidget, TranslateHelper translate_helper_; // Responsible for automatically filling login and password textfields. - PasswordAutocompleteManager password_autocomplete_manager_; + scoped_ptr<PasswordAutocompleteManager> password_autocomplete_manager_; // Responsible for filling forms (AutoFill) and single text entries // (Autocomplete). - AutoFillHelper autofill_helper_; + scoped_ptr<AutoFillHelper> autofill_helper_; + + // Tracks when text input controls get clicked. + // IMPORTANT: this should be declared after autofill_helper_ and + // password_autocomplete_manager_ so the tracker is deleted first (so we won't + // run the risk of notifying deleted objects). + scoped_ptr<PageClickTracker> page_click_tracker_; RendererWebCookieJarImpl cookie_jar_; diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index 9387230..480285a 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -41,6 +41,7 @@ using WebKit::WebCompositionUnderline; using WebKit::WebCursorInfo; using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; using WebKit::WebNavigationPolicy; using WebKit::WebPopupMenu; using WebKit::WebPopupMenuInfo; @@ -131,7 +132,7 @@ void RenderWidget::Init(int32 opener_id) { void RenderWidget::DoInit(int32 opener_id, - WebKit::WebWidget* web_widget, + WebWidget* web_widget, IPC::SyncMessage* create_widget_message) { DCHECK(!webwidget_); @@ -374,6 +375,8 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) { if (WebInputEvent::isKeyboardEventType(input_event->type)) DidHandleKeyEvent(); + if (WebInputEvent::isMouseEventType(input_event->type)) + DidHandleMouseEvent(*(static_cast<const WebMouseEvent*>(input_event))); } void RenderWidget::OnMouseCaptureLost() { diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 2d8ea717..2fa2308 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -42,6 +42,7 @@ class PlatformCanvas; } namespace WebKit { +class WebMouseEvent; class WebWidget; struct WebPopupMenuInfo; } @@ -238,6 +239,10 @@ class RenderWidget : public IPC::Channel::Listener, // just handled. virtual void DidHandleKeyEvent() {} + // Called by OnHandleInputEvent() to notify subclasses that a mouse event was + // just handled. + virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event) {} + // Routing ID that allows us to communicate to the parent browser process // RenderWidgetHost. When MSG_ROUTING_NONE, no messages may be sent. int32 routing_id_; diff --git a/chrome/renderer/translate_helper.cc b/chrome/renderer/translate_helper.cc index ab8bcdf..a630f51 100644 --- a/chrome/renderer/translate_helper.cc +++ b/chrome/renderer/translate_helper.cc @@ -8,14 +8,19 @@ #include "base/utf_string_conversions.h" #include "chrome/common/chrome_constants.h" #include "chrome/renderer/render_view.h" +#include "third_party/WebKit/WebKit/chromium/public/WebElement.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/WebKit/chromium/public/WebScriptSource.h" #include "third_party/WebKit/WebKit/chromium/public/WebView.h" #include "v8/include/v8.h" #include "webkit/glue/dom_operations.h" +using WebKit::WebDocument; +using WebKit::WebElement; using WebKit::WebFrame; using WebKit::WebScriptSource; +using WebKit::WebString; +using WebKit::WebView; // The delay in millliseconds that we'll wait before checking to see if the // translate library injected in the page is ready. @@ -108,15 +113,15 @@ void TranslateHelper::CancelPendingTranslation() { } // static -bool TranslateHelper::IsPageTranslatable(WebKit::WebDocument* document) { - std::vector<WebKit::WebElement> meta_elements; +bool TranslateHelper::IsPageTranslatable(WebDocument* document) { + std::vector<WebElement> meta_elements; webkit_glue::GetMetaElementsWithAttribute(document, ASCIIToUTF16("name"), ASCIIToUTF16("google"), &meta_elements); - std::vector<WebKit::WebElement>::const_iterator iter; + std::vector<WebElement>::const_iterator iter; for (iter = meta_elements.begin(); iter != meta_elements.end(); ++iter) { - WebKit::WebString attribute = iter->getAttribute("value"); + WebString attribute = iter->getAttribute("value"); if (attribute.isNull()) // We support both 'value' and 'content'. attribute = iter->getAttribute("content"); if (attribute.isNull()) @@ -128,13 +133,12 @@ bool TranslateHelper::IsPageTranslatable(WebKit::WebDocument* document) { } // static -std::string TranslateHelper::GetPageLanguageFromMetaTag( - WebKit::WebDocument* document) { +std::string TranslateHelper::GetPageLanguageFromMetaTag(WebDocument* document) { // The META language tag looks like: // <meta http-equiv="content-language" content="en"> // It can contain more than one language: // <meta http-equiv="content-language" content="en, fr"> - std::vector<WebKit::WebElement> meta_elements; + std::vector<WebElement> meta_elements; webkit_glue::GetMetaElementsWithAttribute(document, ASCIIToUTF16("http-equiv"), ASCIIToUTF16("content-language"), @@ -144,7 +148,7 @@ std::string TranslateHelper::GetPageLanguageFromMetaTag( // We don't expect more than one such tag. If there are several, just use the // first one. - WebKit::WebString attribute = meta_elements[0].getAttribute("content"); + WebString attribute = meta_elements[0].getAttribute("content"); if (attribute.isEmpty()) return std::string(); @@ -360,7 +364,7 @@ void TranslateHelper::NotifyBrowserTranslationFailed( } WebFrame* TranslateHelper::GetMainFrame() { - WebKit::WebView* web_view = render_view_->webview(); + WebView* web_view = render_view_->webview(); if (!web_view) { // When the WebView is going away, the render view should have called // CancelPendingTranslation() which should have stopped any pending work, so |