summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/common/ipc_message_utils.h4
-rw-r--r--chrome/renderer/mock_render_process.h25
-rw-r--r--chrome/renderer/mock_render_thread.cc135
-rw-r--r--chrome/renderer/mock_render_thread.h116
-rw-r--r--chrome/renderer/render_thread.cc6
-rw-r--r--chrome/renderer/render_thread.h14
-rw-r--r--chrome/renderer/render_view.cc86
-rw-r--r--chrome/renderer/render_view.h26
-rw-r--r--chrome/renderer/render_view_unittest.cc128
-rw-r--r--chrome/renderer/render_widget_unittest.cc171
-rw-r--r--chrome/test/unit/unittests.vcproj40
-rw-r--r--webkit/glue/chrome_client_impl.cc6
-rw-r--r--webkit/glue/chrome_client_impl.h3
-rw-r--r--webkit/glue/chromium_bridge_impl.cc6
-rw-r--r--webkit/glue/webkit_glue.h5
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();