diff options
-rw-r--r-- | chrome/common/ipc_message_utils.h | 4 | ||||
-rw-r--r-- | chrome/renderer/mock_render_process.h | 25 | ||||
-rw-r--r-- | chrome/renderer/mock_render_thread.cc | 135 | ||||
-rw-r--r-- | chrome/renderer/mock_render_thread.h | 116 | ||||
-rw-r--r-- | chrome/renderer/render_thread.cc | 6 | ||||
-rw-r--r-- | chrome/renderer/render_thread.h | 14 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 86 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 26 | ||||
-rw-r--r-- | chrome/renderer/render_view_unittest.cc | 128 | ||||
-rw-r--r-- | chrome/renderer/render_widget_unittest.cc | 171 | ||||
-rw-r--r-- | chrome/test/unit/unittests.vcproj | 40 | ||||
-rw-r--r-- | webkit/glue/chrome_client_impl.cc | 6 | ||||
-rw-r--r-- | webkit/glue/chrome_client_impl.h | 3 | ||||
-rw-r--r-- | webkit/glue/chromium_bridge_impl.cc | 6 | ||||
-rw-r--r-- | webkit/glue/webkit_glue.h | 5 |
15 files changed, 545 insertions, 226 deletions
diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h index 4234737..476d7f9 100644 --- a/chrome/common/ipc_message_utils.h +++ b/chrome/common/ipc_message_utils.h @@ -996,9 +996,11 @@ struct ParamTraits<webkit_glue::WebApplicationInfo> { // Generic message subclasses // Used for asynchronous messages. -template <class Param> +template <class ParamType> class MessageWithTuple : public Message { public: + typedef ParamType Param; + MessageWithTuple(int32 routing_id, uint16 type, const Param& p) : Message(routing_id, type, PRIORITY_NORMAL) { WriteParam(this, p); diff --git a/chrome/renderer/mock_render_process.h b/chrome/renderer/mock_render_process.h new file mode 100644 index 0000000..d7bd178 --- /dev/null +++ b/chrome/renderer/mock_render_process.h @@ -0,0 +1,25 @@ +// Copyright (c) 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 CHROME_RENDERER_MOCK_RENDER_PROCESS_H_ +#define CHROME_RENDERER_MOCK_RENDER_PROCESS_H_ + +#include <string> + +#include "chrome/common/child_process.h" + +// This class is a trivial mock of the child process singleton. It is necessary +// so we don't trip DCHECKs in ChildProcess::ReleaseProcess() when destroying +// a render widget instance. +class MockProcess : public ChildProcess { + public: + explicit MockProcess(const std::wstring& channel_name) {} + static void GlobalInit() { + ChildProcessFactory<MockProcess> factory; + ChildProcess::GlobalInit(L"dummy", &factory); + } +}; + +#endif // CHROME_RENDERER_MOCK_RENDER_PROCESS_H_ + diff --git a/chrome/renderer/mock_render_thread.cc b/chrome/renderer/mock_render_thread.cc new file mode 100644 index 0000000..050774e --- /dev/null +++ b/chrome/renderer/mock_render_thread.cc @@ -0,0 +1,135 @@ +// Copyright (c) 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 "chrome/renderer/mock_render_thread.h" + +#include "base/logging.h" +#include "chrome/common/ipc_message_utils.h" +#include "chrome/common/render_messages.h" +#include "testing/gtest/include/gtest/gtest.h" + +MockRenderThread::MockRenderThread() + : routing_id_(0), + opener_id_(0), + widget_(NULL), + reply_deserializer_(NULL) { +} + +MockRenderThread::~MockRenderThread() { + // Don't leak the allocated message bodies. + ClearMessages(); +} + +// Called by the Widget. The routing_id must match the routing id assigned +// to the Widget in reply to ViewHostMsg_CreateWidget message. +void MockRenderThread::AddRoute(int32 routing_id, + IPC::Channel::Listener* listener) { + EXPECT_EQ(routing_id_, routing_id); + widget_ = listener; +} + +// Called by the Widget. The routing id must match the routing id of AddRoute. +void MockRenderThread::RemoveRoute(int32 routing_id) { + EXPECT_EQ(routing_id_, routing_id); + widget_ = NULL; +} + +// Called by the Widget. Used to send messages to the browser. +// We short-circuit the mechanim and handle the messages right here on this +// class. +bool MockRenderThread::Send(IPC::Message* msg) { + // We need to simulate a synchronous channel, thus we are going to receive + // through this function messages, messages with reply and reply messages. + // We can only handle one synchronous message at a time. + if (msg->is_reply()) { + if (reply_deserializer_) { + reply_deserializer_->SerializeOutputParameters(*msg); + delete reply_deserializer_; + reply_deserializer_ = NULL; + } + } else { + if (msg->is_sync()) { + reply_deserializer_ = + static_cast<IPC::SyncMessage*>(msg)->GetReplyDeserializer(); + } + OnMessageReceived(*msg); + } + delete msg; + return true; +} + +void MockRenderThread::ClearMessages() { + for (size_t i = 0; i < messages_.size(); i++) + delete[] messages_[i].second; + messages_.clear(); +} + +const IPC::Message* MockRenderThread::GetMessageAt(size_t index) const { + if (index >= messages_.size()) + return NULL; + return &messages_[index].first; +} + +const IPC::Message* MockRenderThread::GetFirstMessageMatching(uint16 id) const { + for (size_t i = 0; i < messages_.size(); i++) { + if (messages_[i].first.type() == id) + return &messages_[i].first; + } + return NULL; +} + +const IPC::Message* MockRenderThread::GetUniqueMessageMatching( + uint16 id) const { + size_t found_index = 0; + size_t found_count = 0; + for (size_t i = 0; i < messages_.size(); i++) { + if (messages_[i].first.type() == id) { + found_count++; + found_index = i; + } + } + if (found_count != 1) + return NULL; // Didn't find a unique one. + return &messages_[found_index].first; +} + +void MockRenderThread::SendCloseMessage() { + ViewMsg_Close msg(routing_id_); + widget_->OnMessageReceived(msg); +} + +void MockRenderThread::OnMessageReceived(const IPC::Message& msg) { + // Copy the message into a pair. This is tricky since the message doesn't + // manage its data itself. + char* data_copy; + if (msg.size()) { + data_copy = new char[msg.size()]; + memcpy(data_copy, msg.data(), msg.size()); + } else { + // Dummy data so we can treat everything the same. + data_copy = new char[1]; + data_copy[0] = 0; + } + + // Save the message. + messages_.push_back( + std::make_pair(IPC::Message(data_copy, msg.size()), data_copy)); + + // Some messages we do special handling. + bool handled = true; + bool msg_is_ok = true; + IPC_BEGIN_MESSAGE_MAP_EX(MockRenderThread, msg, msg_is_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWidget, OnMsgCreateWidget); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() +} + +// The Widget expects to be returned valid route_id. +void MockRenderThread::OnMsgCreateWidget(int opener_id, + bool focus_on_show, + int* route_id) { + opener_id_ = opener_id; + *route_id = routing_id_; +} + diff --git a/chrome/renderer/mock_render_thread.h b/chrome/renderer/mock_render_thread.h new file mode 100644 index 0000000..950353a --- /dev/null +++ b/chrome/renderer/mock_render_thread.h @@ -0,0 +1,116 @@ +// Copyright (c) 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 CHROME_RENDERER_MOCK_RENDER_THREAD_H_ +#define CHROME_RENDERER_MOCK_RENDER_THREAD_H_ + +#include <vector> + +#include "chrome/renderer/render_thread.h" + +// This class is very simple mock of RenderThread. It simulates an IPC channel +// which supports only two messages: +// ViewHostMsg_CreateWidget : sync message sent by the Widget. +// ViewMsg_Close : async, send to the Widget. +class MockRenderThread : public RenderThreadBase { + public: + // Encapusulates an IPC message and its associated data (which is not + // otherwise bound to the lifetime of the message). + typedef std::pair<IPC::Message, char*> MessagePair; + + MockRenderThread(); + virtual ~MockRenderThread(); + + // Called by the Widget. Not used in the test. + virtual bool InSend() const { + return false; + } + + // Called by the Widget. The routing_id must match the routing id assigned + // to the Widget in reply to ViewHostMsg_CreateWidget message. + virtual void AddRoute(int32 routing_id, IPC::Channel::Listener* listener); + + // Called by the Widget. The routing id must match the routing id of AddRoute. + virtual void RemoveRoute(int32 routing_id); + + // Called by the Widget. Used to send messages to the browser. + // We short-circuit the mechanim and handle the messages right here on this + // class. + virtual bool Send(IPC::Message* msg); + + // Our mock thread doesn't do filtering. + virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter) { + } + virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { + } + + ////////////////////////////////////////////////////////////////////////// + // The following functions are called by the test itself. + + void set_routing_id(int32 id) { + routing_id_ = id; + } + + int32 opener_id() const { + return opener_id_; + } + + bool has_widget() const { + return widget_ ? true : false; + } + + // Returns the number of messages in the queue. + size_t message_count() const { return messages_.size(); } + + // Clears the message queue of saved messages. + void ClearMessages(); + + // Returns the message at the given index in the queue. The index may be out + // of range, in which case the return value is NULL. The returned pointer will + // only be valid until another message is received or the list is cleared. + const IPC::Message* GetMessageAt(size_t index) const; + + // Returns the first message with the given ID in the queue. If there is no + // message with the given ID, returns NULL. The returned pointer will only be + // valid until another message is received or the list is cleared. + const IPC::Message* GetFirstMessageMatching(uint16 id) const; + + // Returns the message with the given ID in the queue. If there is no such + // message or there is more than one of that message, this will return NULL + // (with the expectation that you'll do an ASSERT_TRUE() on the result). + // The returned pointer will only be valid until another message is received + // or the list is cleared. + const IPC::Message* GetUniqueMessageMatching(uint16 id) const; + + // Simulates the Widget receiving a close message. This should result + // on releasing the internal reference counts and destroying the internal + // state. + void SendCloseMessage(); + + private: + // This function operates as a regular IPC listener. + void OnMessageReceived(const IPC::Message& msg); + + // The Widget expects to be returned valid route_id. + void OnMsgCreateWidget(int opener_id, + bool focus_on_show, + int* route_id); + + // Routing id what will be assigned to the Widget. + int32 routing_id_; + + // Opener id reported by the Widget. + int32 opener_id_; + + // We only keep track of one Widget, we learn its pointer when it + // adds a new route. + IPC::Channel::Listener* widget_; + + // The last known good deserializer for sync messages. + IPC::MessageReplyDeserializer* reply_deserializer_; + + std::vector<MessagePair> messages_; +}; + +#endif // CHROME_RENDERER_MOCK_RENDER_THREAD_H_ diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 9a4e4ed..c5be113 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -71,8 +71,8 @@ void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { } void RenderThread::Resolve(const char* name, size_t length) { - return render_dns_master_->Resolve(name, length); - } + return render_dns_master_->Resolve(name, length); +} void RenderThread::AddRoute(int32 routing_id, IPC::Channel::Listener* listener) { @@ -199,7 +199,7 @@ void RenderThread::OnCreateNewView(HWND parent_hwnd, // TODO(darin): once we have a RenderThread per RenderView, this will need to // change to assert that we are not creating more than one view. RenderView::Create( - parent_hwnd, modal_dialog_event, MSG_ROUTING_NONE, webkit_prefs, + this, parent_hwnd, modal_dialog_event, MSG_ROUTING_NONE, webkit_prefs, new SharedRenderViewCounter(0), view_id); } diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index 68c51cf..ae94815 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -20,9 +20,9 @@ class RenderDnsMaster; class NotificationService; class GreasemonkeySlave; -// The RenderThreadBase is the minimal interface that a RenderWidget expects -// from a render thread. The interface basically abstracts a way to send and -// receive messages. It is currently only used for testing. +// The RenderThreadBase is the minimal interface that a RenderView/Widget +// expects from a render thread. The interface basically abstracts a way to send +// and receive messages. class RenderThreadBase : public IPC::Message::Sender { public: virtual ~RenderThreadBase() {} @@ -34,6 +34,9 @@ class RenderThreadBase : public IPC::Message::Sender { // These methods normally get delegated to a MessageRouter. virtual void AddRoute(int32 routing_id, IPC::Channel::Listener* listener) = 0; virtual void RemoveRoute(int32 routing_id) = 0; + + virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter) = 0; + virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) = 0; }; // The RenderThread class represents a background thread where RenderView @@ -59,8 +62,9 @@ class RenderThread : public IPC::Channel::Listener, // IPC::Message::Sender implementation: virtual bool Send(IPC::Message* msg); - void AddFilter(IPC::ChannelProxy::MessageFilter* filter); - void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter); + // Overridded from RenderThreadBase. + virtual void AddFilter(IPC::ChannelProxy::MessageFilter* filter); + virtual void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter); // Gets the VisitedLinkSlave instance for this thread VisitedLinkSlave* visited_link_slave() const { return visited_link_slave_; } diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 161c482..dfd6b8f 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -89,9 +89,9 @@ static const int kDelayForCaptureMs = 500; // delay. static const int kDelayForForcedCaptureMs = 6000; -// How often we will sync the navigation state when the user is changing form -// elements or scroll position. -const TimeDelta kDelayForNavigationSync = TimeDelta::FromSeconds(5); +// The default value for RenderView.delay_seconds_for_form_state_sync_, see +// that variable for more. +const int kDefaultDelaySecondsForFormStateSync = 5; // The next available page ID to use. This ensures that the page IDs are // globally unique in the renderer. @@ -142,29 +142,30 @@ class RenderViewExtraRequestData : public WebRequest::ExtraData { /////////////////////////////////////////////////////////////////////////////// -RenderView::RenderView() - : RenderWidget(g_render_thread, true), - is_loading_(false), - page_id_(-1), - last_page_id_sent_to_browser_(-1), - last_indexed_page_id_(-1), - method_factory_(this), - opened_by_user_gesture_(true), - enable_dom_automation_(false), - enable_dom_ui_bindings_(false), - target_url_status_(TARGET_NONE), - printed_document_width_(0), - first_default_plugin_(NULL), - navigation_gesture_(NavigationGestureUnknown), - history_back_list_count_(0), - history_forward_list_count_(0), - disable_popup_blocking_(false), - has_unload_listener_(false), - decrement_shared_popup_at_destruction_(false), - greasemonkey_enabled_(false), - waiting_for_create_window_ack_(false), - form_field_autofill_request_id_(0), - popup_notification_visible_(false) { +RenderView::RenderView(RenderThreadBase* render_thread) + : RenderWidget(render_thread, true), + is_loading_(false), + page_id_(-1), + last_page_id_sent_to_browser_(-1), + last_indexed_page_id_(-1), + method_factory_(this), + opened_by_user_gesture_(true), + enable_dom_automation_(false), + enable_dom_ui_bindings_(false), + target_url_status_(TARGET_NONE), + printed_document_width_(0), + first_default_plugin_(NULL), + navigation_gesture_(NavigationGestureUnknown), + history_back_list_count_(0), + history_forward_list_count_(0), + disable_popup_blocking_(false), + has_unload_listener_(false), + decrement_shared_popup_at_destruction_(false), + greasemonkey_enabled_(false), + waiting_for_create_window_ack_(false), + form_field_autofill_request_id_(0), + popup_notification_visible_(false), + delay_seconds_for_form_state_sync_(kDefaultDelaySecondsForFormStateSync) { resource_dispatcher_ = new ResourceDispatcher(this); #ifdef CHROME_PERSONALIZATION personalization_ = Personalization::CreateRendererPersonalization(); @@ -183,7 +184,7 @@ RenderView::~RenderView() { it = plugin_delegates_.erase(it); } - g_render_thread->RemoveFilter(debug_message_handler_); + render_thread_->RemoveFilter(debug_message_handler_); #ifdef CHROME_PERSONALIZATION Personalization::CleanupRendererPersonalization(personalization_); @@ -193,6 +194,7 @@ RenderView::~RenderView() { /*static*/ RenderView* RenderView::Create( + RenderThreadBase* render_thread, HWND parent_hwnd, HANDLE modal_dialog_event, int32 opener_id, @@ -200,7 +202,7 @@ RenderView* RenderView::Create( SharedRenderViewCounter* counter, int32 routing_id) { DCHECK(routing_id != MSG_ROUTING_NONE); - scoped_refptr<RenderView> view = new RenderView(); + scoped_refptr<RenderView> view = new RenderView(render_thread); view->Init(parent_hwnd, modal_dialog_event, opener_id, @@ -271,7 +273,7 @@ void RenderView::Init(HWND parent_hwnd, webview()->SetBackForwardListSize(1); routing_id_ = routing_id; - g_render_thread->AddRoute(routing_id_, this); + render_thread_->AddRoute(routing_id_, this); // Take a reference on behalf of the RenderThread. This will be balanced // when we receive ViewMsg_Close. AddRef(); @@ -295,7 +297,7 @@ void RenderView::Init(HWND parent_hwnd, command_line.HasSwitch(switches::kEnableGreasemonkey); debug_message_handler_ = new DebugMessageHandler(this); - g_render_thread->AddFilter(debug_message_handler_); + render_thread_->AddFilter(debug_message_handler_); } void RenderView::OnMessageReceived(const IPC::Message& message) { @@ -1458,9 +1460,10 @@ void RenderView::DidFinishDocumentLoadForFrame(WebView* webview, // do inject into any other document. if (greasemonkey_enabled_) { const GURL &gurl = frame->GetURL(); - if (gurl.SchemeIs("file") || - gurl.SchemeIs("http") || - gurl.SchemeIs("https")) { + if (g_render_thread && // Will be NULL when testing. + (gurl.SchemeIs("file") || + gurl.SchemeIs("http") || + gurl.SchemeIs("https"))) { g_render_thread->greasemonkey_slave()->InjectScripts(frame); } } @@ -1785,7 +1788,7 @@ WebView* RenderView::CreateWebView(WebView* webview, bool user_gesture) { int32 routing_id = MSG_ROUTING_NONE; HANDLE modal_dialog_event = NULL; - bool result = g_render_thread->Send( + bool result = render_thread_->Send( new ViewHostMsg_CreateWindow(routing_id_, user_gesture, &routing_id, &modal_dialog_event)); if (routing_id == MSG_ROUTING_NONE) { @@ -1795,7 +1798,8 @@ WebView* RenderView::CreateWebView(WebView* webview, bool user_gesture) { // The WebView holds a reference to this new RenderView const WebPreferences& prefs = webview->GetPreferences(); - RenderView* view = RenderView::Create(NULL, modal_dialog_event, routing_id_, + RenderView* view = RenderView::Create(render_thread_, + NULL, modal_dialog_event, routing_id_, prefs, shared_popup_counter_, routing_id); view->set_opened_by_user_gesture(user_gesture); @@ -1811,7 +1815,7 @@ WebView* RenderView::CreateWebView(WebView* webview, bool user_gesture) { WebWidget* RenderView::CreatePopupWidget(WebView* webview, bool focus_on_show) { RenderWidget* widget = RenderWidget::Create(routing_id_, - g_render_thread, + render_thread_, focus_on_show); return widget->webwidget(); } @@ -1839,7 +1843,7 @@ WebPluginDelegate* RenderView::CreatePluginDelegate( bool is_gears = false; if (ShouldLoadPluginInProcess(mime_type, &is_gears)) { std::wstring path; - g_render_thread->Send( + render_thread_->Send( new ViewHostMsg_GetPluginPath(url, mime_type, clsid, &path, actual_mime_type)); if (path.empty()) @@ -2369,9 +2373,11 @@ int RenderView::GetHistoryForwardListCount() { } void RenderView::OnNavStateChanged(WebView* webview) { - if (!nav_state_sync_timer_.IsRunning()) - nav_state_sync_timer_.Start(kDelayForNavigationSync, this, - &RenderView::SyncNavigationState); + if (!nav_state_sync_timer_.IsRunning()) { + nav_state_sync_timer_.Start( + TimeDelta::FromSeconds(delay_seconds_for_form_state_sync_), this, + &RenderView::SyncNavigationState); + } } void RenderView::SetTooltipText(WebView* webview, diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index cea0ce1..24f8f9b 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -25,6 +25,7 @@ #include "chrome/renderer/external_js_object.h" #include "chrome/renderer/render_process.h" #include "chrome/renderer/render_widget.h" +#include "testing/gtest/include/gtest/gtest_prod.h" #include "webkit/glue/console_message_level.h" #include "webkit/glue/dom_serializer_delegate.h" #include "webkit/glue/glue_accessibility.h" @@ -38,6 +39,7 @@ class DebugMessageHandler; class GURL; +class RenderThread; class SkBitmap; struct ThumbnailScore; class WebError; @@ -68,7 +70,8 @@ typedef base::RefCountedData<int> SharedRenderViewCounter; // RenderView is an object that manages a WebView object, and provides a // communication interface with an embedding application process // -class RenderView : public RenderWidget, public WebViewDelegate, +class RenderView : public RenderWidget, + public WebViewDelegate, public webkit_glue::DomSerializerDelegate { public: // Creates a new RenderView. The parent_hwnd specifies a HWND to use as the @@ -80,6 +83,7 @@ class RenderView : public RenderWidget, public WebViewDelegate, // parent_hwnd). |counter| is either a currently initialized counter, or NULL // (in which case we treat this RenderView as a top level window). static RenderView* Create( + RenderThreadBase* render_thread, HWND parent_hwnd, HANDLE modal_dialog_event, int32 opener_id, @@ -305,12 +309,18 @@ class RenderView : public RenderWidget, public WebViewDelegate, // This is called from within the renderer, not via an IPC message. void OnDebugDetach(); + int delay_seconds_for_form_state_sync() const { + return delay_seconds_for_form_state_sync_; + } + void set_delay_seconds_for_form_state_sync(int delay_in_seconds) { + delay_seconds_for_form_state_sync_ = delay_in_seconds; + } + private: - RenderView(); + FRIEND_TEST(RenderViewTest, OnLoadAlternateHTMLText); + FRIEND_TEST(RenderViewTest, OnNavStateChanged); - // When we are created from window.open from an already existing view, this - // constructor stores that view ID. - explicit RenderView(int32 opener_id); + explicit RenderView(RenderThreadBase* render_thread); // Initializes this view with the given parent and ID. The |routing_id| can be // set to 'MSG_ROUTING_NONE' if the true ID is not yet known. In this case, @@ -711,6 +721,12 @@ class RenderView : public RenderWidget, public WebViewDelegate, // from the Browser process telling us otherwise. bool popup_notification_visible_; + // Time in seconds of the delay between syncing page state such as form + // elements and scroll position. This timeout allows us to avoid spamming the + // browser process with every little thing that changes. This normally doesn't + // change but is overridden by tests. + int delay_seconds_for_form_state_sync_; + DISALLOW_COPY_AND_ASSIGN(RenderView); }; diff --git a/chrome/renderer/render_view_unittest.cc b/chrome/renderer/render_view_unittest.cc new file mode 100644 index 0000000..71ab2e2 --- /dev/null +++ b/chrome/renderer/render_view_unittest.cc @@ -0,0 +1,128 @@ +// Copyright (c) 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 "base/scoped_ptr.h" +#include "chrome/renderer/mock_render_process.h" +#include "chrome/renderer/mock_render_thread.h" +#include "chrome/renderer/render_view.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/weburlrequest.h" +#include "webkit/glue/webview.h" + +namespace { + +const int32 kRouteId = 5; +const int32 kOpenerId = 7; + +class RenderViewTest : public testing::Test { + public: + RenderViewTest() {} + ~RenderViewTest() {} + + protected: + // Spins the message loop to process all messages that are currently pending. + void ProcessPendingMessages() { + msg_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask()); + msg_loop_.Run(); + } + + // Returns a pointer to the main frame. + WebFrame* GetMainFrame() { + return view_->webview()->GetMainFrame(); + } + + // Executes the given JavaScript in the context of the main frame. The input + // is a NULL-terminated UTF-8 string. + void ExecuteJavaScript(const char* js) { + GetMainFrame()->ExecuteJavaScript(js, GURL()); + } + + // Loads the given HTML into the main frame as a data: URL. + void LoadHTML(const char* html) { + std::string url_str = "data:text/html;charset=utf-8,"; + url_str.append(html); + GURL url(url_str); + + scoped_ptr<WebRequest> request(WebRequest::Create(url)); + GetMainFrame()->LoadRequest(request.get()); + + // The load actually happens asynchronously, so we pump messages to process + // the pending continuation. + ProcessPendingMessages(); + } + + // testing::Test + virtual void SetUp() { + MockProcess::GlobalInit(); + + render_thread_.set_routing_id(kRouteId); + + // This needs to pass the mock render thread to the view. + view_ = RenderView::Create(&render_thread_, NULL, NULL, kOpenerId, + WebPreferences(), + new SharedRenderViewCounter(0), kRouteId); + } + virtual void TearDown() { + render_thread_.SendCloseMessage(); + + // Run the loop so the release task from the renderwidget executes. + ProcessPendingMessages(); + + view_ = NULL; + + // There is a delayed task that the child process posts to terminate the + // message loop so we need to spin the message loop to delete the task. + MockProcess::GlobalCleanup(); + msg_loop_.Run(); + } + + MessageLoop msg_loop_; + MockRenderThread render_thread_; + + scoped_refptr<RenderView> view_; +}; + +} // namespace + +TEST_F(RenderViewTest, OnLoadAlternateHTMLText) { + // Test a new navigation. + GURL test_url("http://www.google.com/some_test_url"); + view_->OnLoadAlternateHTMLText("<html></html>", true, test_url, + std::string()); + + // We should have gotten two different types of start messages in the + // following order. + ASSERT_EQ(2, render_thread_.message_count()); + const IPC::Message* msg = render_thread_.GetMessageAt(0); + EXPECT_EQ(ViewHostMsg_DidStartLoading::ID, msg->type()); + + msg = render_thread_.GetMessageAt(1); + EXPECT_EQ(ViewHostMsg_DidStartProvisionalLoadForFrame::ID, msg->type()); + ViewHostMsg_DidStartProvisionalLoadForFrame::Param start_params; + ViewHostMsg_DidStartProvisionalLoadForFrame::Read(msg, &start_params); + EXPECT_EQ(GURL("chrome://chromewebdata/"), start_params.b); +} + +// Test that we get form state change notifications when input fields change. +TEST_F(RenderViewTest, OnNavStateChanged) { + // Don't want any delay for form state sync changes. This will still post a + // message so updates will get coalesced, but as soon as we spin the message + // loop, it will generate an update. + view_->set_delay_seconds_for_form_state_sync(0); + + LoadHTML("<input type=\"text\" id=\"elt_text\"></input>"); + + // We should NOT have gotten a form state change notification yet. + EXPECT_FALSE(render_thread_.GetFirstMessageMatching( + ViewHostMsg_UpdateState::ID)); + render_thread_.ClearMessages(); + + // Change the value of the input. We should have gotten an update state + // notification. We need to spin the message loop to catch this update. + ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';"); + ProcessPendingMessages(); + EXPECT_TRUE(render_thread_.GetUniqueMessageMatching( + ViewHostMsg_UpdateState::ID)); +} diff --git a/chrome/renderer/render_widget_unittest.cc b/chrome/renderer/render_widget_unittest.cc index f280aa7..6c2b24f 100644 --- a/chrome/renderer/render_widget_unittest.cc +++ b/chrome/renderer/render_widget_unittest.cc @@ -5,163 +5,58 @@ #include "testing/gtest/include/gtest/gtest.h" #include "base/ref_counted.h" -#include "chrome/common/child_process.h" +#include "chrome/renderer/mock_render_process.h" +#include "chrome/renderer/mock_render_thread.h" #include "chrome/renderer/render_widget.h" #include "chrome/renderer/render_thread.h" -// This class is a trivial mock of the child process singleton. It is necessary -// so we don't trip DCHECKs in ChildProcess::ReleaseProcess() when destroying -// a render widget instance. -class MockProcess : public ChildProcess { - public: - explicit MockProcess(const std::wstring& channel_name) {} - static void GlobalInit() { - ChildProcessFactory<MockProcess> factory; - ChildProcess::GlobalInit(L"dummy", &factory); - } -}; - -// This class is very simple mock of RenderThread. It simulates an IPC channel -// which supports only two messages: -// ViewHostMsg_CreateWidget : sync message sent by the Widget. -// ViewMsg_Close : async, send to the Widget. -class MockRenderThread : public RenderThreadBase { - public: - MockRenderThread() - : routing_id_(0), opener_id_(0), widget_(NULL), - reply_deserializer_(NULL) { - } - virtual ~MockRenderThread() { - } +namespace { - // Called by the Widget. Not used in the test. - virtual bool InSend() const { - return false; - } - - // Called by the Widget. The routing_id must match the routing id assigned - // to the Widget in reply to ViewHostMsg_CreateWidget message. - virtual void AddRoute(int32 routing_id, IPC::Channel::Listener* listener) { - EXPECT_EQ(routing_id_, routing_id); - widget_ = listener; - } - - // Called by the Widget. The routing id must match the routing id of AddRoute. - virtual void RemoveRoute(int32 routing_id) { - EXPECT_EQ(routing_id_, routing_id); - widget_ = NULL; - } - - // Called by the Widget. Used to send messages to the browser. - // We short-circuit the mechanim and handle the messages right here on this - // class. - virtual bool Send(IPC::Message* msg) { - // We need to simulate a synchronous channel, thus we are going to receive - // through this function messages, messages with reply and reply messages. - // We can only handle one synchronous message at a time. - if (msg->is_reply()) { - if (reply_deserializer_) - reply_deserializer_->SerializeOutputParameters(*msg); - delete reply_deserializer_; - reply_deserializer_ = NULL; - } else { - if (msg->is_sync()) { - reply_deserializer_ = - static_cast<IPC::SyncMessage*>(msg)->GetReplyDeserializer(); - } - OnMessageReceived(*msg); - } - delete msg; - return true; - } +const int32 kRouteId = 5; +const int32 kOpenerId = 7; - ////////////////////////////////////////////////////////////////////////// - // The following functions are called by the test itself. - - void set_routing_id(int32 id) { - routing_id_ = id; - } - - int32 opener_id() const { - return opener_id_; - } +class RenderWidgetTest : public testing::Test { + public: - bool has_widget() const { - return widget_ ? true : false; - } + protected: + MessageLoop msg_loop_; + MockRenderThread render_thread_; - // Simulates the Widget receiving a close message. This should result - // on releasing the internal reference counts and destroying the internal - // state. - void SendCloseMessage() { - ViewMsg_Close msg(routing_id_); - widget_->OnMessageReceived(msg); - } + // The widget, each test should verify this is non-NULL before continuing. + scoped_refptr<RenderWidget> widget_; private: + // testing::Test + virtual void SetUp() { + MockProcess::GlobalInit(); - // This function operates as a regular IPC listener. - void OnMessageReceived(const IPC::Message& msg) { - bool handled = true; - bool msg_is_ok = true; - IPC_BEGIN_MESSAGE_MAP_EX(MockRenderThread, msg, msg_is_ok) - IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWidget, OnMsgCreateWidget); - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP_EX() - - // This test must handle all messages that RenderWidget generates. - EXPECT_TRUE(handled); + render_thread_.set_routing_id(kRouteId); + widget_ = RenderWidget::Create(kOpenerId, &render_thread_, true); + ASSERT_TRUE(widget_); } + virtual void TearDown() { + widget_ = NULL; - // The Widget expects to be returned valid route_id. - void OnMsgCreateWidget(int opener_id, - bool focus_on_show, - int* route_id) { - opener_id_ = opener_id; - *route_id = routing_id_; + // There is a delayed task that the child process posts to terminate the + // message loop so we need to spin the message loop to delete the task. + MockProcess::GlobalCleanup(); + msg_loop_.Run(); } - - // Routing id what will be assigned to the Widget. - int32 routing_id_; - // Opener id reported by the Widget. - int32 opener_id_; - // We only keep track of one Widget, we learn its pointer when it - // adds a new route. - IPC::Channel::Listener* widget_; - // The last known good deserializer for sync messages. - IPC::MessageReplyDeserializer* reply_deserializer_; }; -TEST(RenderWidgetTest, CreateAndCloseWidget) { - MessageLoop msg_loop; - MockRenderThread render_thread; - MockProcess::GlobalInit(); - - const int32 kRouteId = 5; - const int32 kOpenerId = 7; - render_thread.set_routing_id(kRouteId); - - { - scoped_refptr<RenderWidget> rw = - RenderWidget::Create(kOpenerId, &render_thread, true); - ASSERT_TRUE(rw != NULL); +} // namespace +TEST_F(RenderWidgetTest, CreateAndCloseWidget) { // After the RenderWidget it must have sent a message to the render thread // that sets the opener id. - EXPECT_EQ(kOpenerId, render_thread.opener_id()); - ASSERT_TRUE(render_thread.has_widget()); + EXPECT_EQ(kOpenerId, render_thread_.opener_id()); + ASSERT_TRUE(render_thread_.has_widget()); // Now simulate a close of the Widget. - render_thread.SendCloseMessage(); - EXPECT_FALSE(render_thread.has_widget()); - - // Run the loop so the release task from the renderwidget executes. - msg_loop.PostTask(FROM_HERE, new MessageLoop::QuitTask()); - msg_loop.Run(); -} + render_thread_.SendCloseMessage(); + EXPECT_FALSE(render_thread_.has_widget()); - // There is a delayed task that the child process posts to terminate the - // message loop so we need to spin the message loop to delete the task. - MockProcess::GlobalCleanup(); - msg_loop.Run(); + // Run the loop so the release task from the renderwidget executes. + msg_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask()); + msg_loop_.Run(); } diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index f3fa1c0..d8baadc 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -17,7 +17,7 @@ <Configuration Name="Debug|Win32" ConfigurationType="1" - InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;..\..\tools\build\win\precompiled_wtl.vsprops;..\..\tools\build\win\unit_test.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;..\..\..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;..\..\third_party\hunspell\using_hunspell.vsprops" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;..\..\tools\build\win\precompiled_wtl.vsprops;..\..\tools\build\win\unit_test.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;..\..\third_party\hunspell\using_hunspell.vsprops;..\..\..\third_party\npapi\using_npapi.vsprops" > <Tool Name="VCPreBuildEventTool" @@ -79,7 +79,7 @@ <Configuration Name="Release|Win32" ConfigurationType="1" - InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;..\..\tools\build\win\unit_test.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;..\..\..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;..\..\third_party\hunspell\using_hunspell.vsprops" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;..\..\tools\build\win\unit_test.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;..\..\third_party\hunspell\using_hunspell.vsprops;..\..\..\third_party\npapi\using_npapi.vsprops" > <Tool Name="VCPreBuildEventTool" @@ -292,24 +292,6 @@ <File RelativePath="..\..\browser\renderer_host\resource_dispatcher_host_unittest.cc" > - <FileConfiguration - Name="Debug|Win32" - > - <Tool - Name="VCCLCompilerTool" - ObjectFile="$(IntDir)\$(InputName)1.obj" - XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - > - <Tool - Name="VCCLCompilerTool" - ObjectFile="$(IntDir)\$(InputName)1.obj" - XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc" - /> - </FileConfiguration> </File> <File RelativePath="..\..\common\resource_dispatcher_unittest.cc" @@ -1049,9 +1031,25 @@ </File> </Filter> <Filter - Name="TestRenderWidget" + Name="TestRenderer" > <File + RelativePath="..\..\renderer\mock_render_process.h" + > + </File> + <File + RelativePath="..\..\renderer\mock_render_thread.cc" + > + </File> + <File + RelativePath="..\..\renderer\mock_render_thread.h" + > + </File> + <File + RelativePath="..\..\renderer\render_view_unittest.cc" + > + </File> + <File RelativePath="..\..\renderer\render_widget_unittest.cc" > </File> diff --git a/webkit/glue/chrome_client_impl.cc b/webkit/glue/chrome_client_impl.cc index 860d9b1..b7cc3ae 100644 --- a/webkit/glue/chrome_client_impl.cc +++ b/webkit/glue/chrome_client_impl.cc @@ -488,3 +488,9 @@ void ChromeClientImpl::disableSuddenTermination() { if (d) d->DisableSuddenTermination(); } + +void ChromeClientImpl::formStateDidChange(const WebCore::Node*) { + WebViewDelegate* d = webview_->delegate(); + if (d) + d->OnNavStateChanged(webview_); +} diff --git a/webkit/glue/chrome_client_impl.h b/webkit/glue/chrome_client_impl.h index 7d69ee1..27fd2c7 100644 --- a/webkit/glue/chrome_client_impl.h +++ b/webkit/glue/chrome_client_impl.h @@ -123,8 +123,7 @@ public: virtual void enableSuddenTermination(); virtual void disableSuddenTermination(); - // TODO(brettw): Implement me! - virtual void formStateDidChange(const WebCore::Node*) { } + virtual void formStateDidChange(const WebCore::Node*); private: WebViewImpl* webview_; // weak pointer diff --git a/webkit/glue/chromium_bridge_impl.cc b/webkit/glue/chromium_bridge_impl.cc index b178d55..c75ab15 100644 --- a/webkit/glue/chromium_bridge_impl.cc +++ b/webkit/glue/chromium_bridge_impl.cc @@ -244,12 +244,6 @@ bool ChromiumBridge::ensureFontLoaded(HFONT font) { } #endif -// Forms ---------------------------------------------------------------------- - -void ChromiumBridge::notifyFormStateChanged(const Document* doc) { - webkit_glue::NotifyFormStateChanged(doc); -} - // JavaScript ----------------------------------------------------------------- void ChromiumBridge::notifyJSOutOfMemory(Frame* frame) { diff --git a/webkit/glue/webkit_glue.h b/webkit/glue/webkit_glue.h index 78ad7ee..f3335b5 100644 --- a/webkit/glue/webkit_glue.h +++ b/webkit/glue/webkit_glue.h @@ -245,11 +245,6 @@ ScreenInfo GetScreenInfo(gfx::NativeView window); // Functions implemented by webkit_glue for WebKit ---------------------------- -// Notifies the embedder that a form element value has changed. The document -// pointer, which MAY BE NULL, indicates the document that owns the form -// element that changed, if any. -void NotifyFormStateChanged(const WebCore::Document* document); - // Returns a bool indicating if the Null plugin should be enabled or not. bool IsDefaultPluginEnabled(); |