1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/test/render_view_test.h"
7 #include "chrome/browser/extensions/extension_function_dispatcher.h"
8 #include "chrome/common/extensions/extension.h"
9 #include "chrome/common/print_messages.h"
10 #include "chrome/common/render_messages.h"
11 #include "chrome/renderer/autofill/password_autofill_manager.h"
12 #include "chrome/renderer/extensions/event_bindings.h"
13 #include "chrome/renderer/extensions/extension_dispatcher.h"
14 #include "chrome/renderer/extensions/extension_process_bindings.h"
15 #include "chrome/renderer/extensions/js_only_v8_extensions.h"
16 #include "chrome/renderer/extensions/renderer_extension_bindings.h"
17 #include "chrome/renderer/mock_render_process.h"
18 #include "content/common/dom_storage_common.h"
19 #include "content/common/native_web_keyboard_event.h"
20 #include "content/common/renderer_preferences.h"
21 #include "content/common/view_messages.h"
22 #include "content/renderer/renderer_main_platform_delegate.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
25 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptController.h"
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h"
28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLRequest.h"
29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
30 #include "webkit/glue/webkit_glue.h"
33 #include "ui/base/gtk/event_synthesis_gtk.h"
36 using WebKit::WebFrame
;
37 using WebKit::WebInputEvent
;
38 using WebKit::WebMouseEvent
;
39 using WebKit::WebScriptController
;
40 using WebKit::WebScriptSource
;
41 using WebKit::WebString
;
42 using WebKit::WebURLRequest
;
43 using autofill::AutofillAgent
;
44 using autofill::PasswordAutofillManager
;
47 const int32 kRouteId
= 5;
48 const int32 kOpenerId
= 7;
51 RenderViewTest::RenderViewTest() : extension_dispatcher_(NULL
) {
54 RenderViewTest::~RenderViewTest() {
57 void RenderViewTest::ProcessPendingMessages() {
58 msg_loop_
.PostTask(FROM_HERE
, new MessageLoop::QuitTask());
62 WebFrame
* RenderViewTest::GetMainFrame() {
63 return view_
->webview()->mainFrame();
66 void RenderViewTest::ExecuteJavaScript(const char* js
) {
67 GetMainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(js
)));
70 bool RenderViewTest::ExecuteJavaScriptAndReturnIntValue(
71 const string16
& script
,
73 v8::Handle
<v8::Value
> result
=
74 GetMainFrame()->executeScriptAndReturnValue(WebScriptSource(script
));
75 if (result
.IsEmpty() || !result
->IsInt32())
79 *int_result
= result
->Int32Value();
84 void RenderViewTest::LoadHTML(const char* html
) {
85 std::string url_str
= "data:text/html;charset=utf-8,";
89 GetMainFrame()->loadRequest(WebURLRequest(url
));
91 // The load actually happens asynchronously, so we pump messages to process
92 // the pending continuation.
93 ProcessPendingMessages();
96 void RenderViewTest::SetUp() {
97 content::GetContentClient()->set_renderer(&chrome_content_renderer_client_
);
98 extension_dispatcher_
= new ExtensionDispatcher();
99 chrome_content_renderer_client_
.SetExtensionDispatcher(extension_dispatcher_
);
100 sandbox_init_wrapper_
.reset(new SandboxInitWrapper());
101 command_line_
.reset(new CommandLine(CommandLine::NO_PROGRAM
));
102 params_
.reset(new MainFunctionParams(*command_line_
, *sandbox_init_wrapper_
,
104 platform_
.reset(new RendererMainPlatformDelegate(*params_
));
105 platform_
->PlatformInitialize();
107 // Setting flags and really doing anything with WebKit is fairly fragile and
108 // hacky, but this is the world we live in...
109 webkit_glue::SetJavaScriptFlags(" --expose-gc");
110 WebKit::initialize(&webkitclient_
);
111 WebScriptController::registerExtension(JsonSchemaJsV8Extension::Get());
112 WebScriptController::registerExtension(EventBindings::Get(
113 extension_dispatcher_
));
114 WebScriptController::registerExtension(ExtensionApiTestV8Extension::Get());
115 WebScriptController::registerExtension(ExtensionProcessBindings::Get(
116 extension_dispatcher_
));
117 WebScriptController::registerExtension(RendererExtensionBindings::Get(
118 extension_dispatcher_
));
119 EventBindings::SetRenderThread(&render_thread_
);
121 mock_process_
.reset(new MockRenderProcess
);
123 render_thread_
.set_routing_id(kRouteId
);
125 // This needs to pass the mock render thread to the view.
126 view_
= RenderView::Create(&render_thread_
,
128 gfx::kNullPluginWindow
,
130 RendererPreferences(),
132 new SharedRenderViewCounter(0),
134 kInvalidSessionStorageNamespaceId
,
137 // Attach a pseudo keyboard device to this object.
138 mock_keyboard_
.reset(new MockKeyboard());
140 // RenderView doesn't expose it's PasswordAutofillManager or
141 // AutofillAgent objects, because it has no need to store them directly
142 // (they're stored as RenderViewObserver*). So just create another set.
143 password_autofill_
= new PasswordAutofillManager(view_
);
144 autofill_agent_
= new AutofillAgent(view_
, password_autofill_
);
147 void RenderViewTest::TearDown() {
148 // Try very hard to collect garbage before shutting down.
149 GetMainFrame()->collectGarbage();
150 GetMainFrame()->collectGarbage();
152 render_thread_
.SendCloseMessage();
154 // Run the loop so the release task from the renderwidget executes.
155 ProcessPendingMessages();
157 EventBindings::SetRenderThread(NULL
);
161 mock_process_
.reset();
163 // After resetting the view_ and mock_process_ we may get some new tasks
164 // which need to be processed before shutting down WebKit
165 // (http://crbug.com/21508).
166 msg_loop_
.RunAllPending();
170 mock_keyboard_
.reset();
172 platform_
->PlatformUninitialize();
175 command_line_
.reset();
176 sandbox_init_wrapper_
.reset();
178 extension_dispatcher_
->OnRenderProcessShutdown();
179 extension_dispatcher_
= NULL
;
182 int RenderViewTest::SendKeyEvent(MockKeyboard::Layout layout
,
184 MockKeyboard::Modifiers modifiers
,
185 std::wstring
* output
) {
187 // Retrieve the Unicode character for the given tuple (keyboard-layout,
188 // key-code, and modifiers).
189 // Exit when a keyboard-layout driver cannot assign a Unicode character to
190 // the tuple to prevent sending an invalid key code to the RenderView object.
191 CHECK(mock_keyboard_
.get());
193 int length
= mock_keyboard_
->GetCharacters(layout
, key_code
, modifiers
,
198 // Create IPC messages from Windows messages and send them to our
200 // A keyboard event of Windows consists of three Windows messages:
201 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP.
202 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand,
203 // WM_CHAR sends a composed Unicode character.
204 NativeWebKeyboardEvent
keydown_event(NULL
, WM_KEYDOWN
, key_code
, 0);
205 SendNativeKeyEvent(keydown_event
);
207 NativeWebKeyboardEvent
char_event(NULL
, WM_CHAR
, (*output
)[0], 0);
208 SendNativeKeyEvent(char_event
);
210 NativeWebKeyboardEvent
keyup_event(NULL
, WM_KEYUP
, key_code
, 0);
211 SendNativeKeyEvent(keyup_event
);
214 #elif defined(OS_LINUX)
215 // We ignore |layout|, which means we are only testing the layout of the
216 // current locale. TODO(estade): fix this to respect |layout|.
217 std::vector
<GdkEvent
*> events
;
218 ui::SynthesizeKeyPressEvents(
219 NULL
, static_cast<ui::KeyboardCode
>(key_code
),
220 modifiers
& (MockKeyboard::LEFT_CONTROL
| MockKeyboard::RIGHT_CONTROL
),
221 modifiers
& (MockKeyboard::LEFT_SHIFT
| MockKeyboard::RIGHT_SHIFT
),
222 modifiers
& (MockKeyboard::LEFT_ALT
| MockKeyboard::RIGHT_ALT
),
225 guint32 unicode_key
= 0;
226 for (size_t i
= 0; i
< events
.size(); ++i
) {
227 // Only send the up/down events for key press itself (skip the up/down
228 // events for the modifier keys).
229 if ((i
+ 1) == (events
.size() / 2) || i
== (events
.size() / 2)) {
230 unicode_key
= gdk_keyval_to_unicode(events
[i
]->key
.keyval
);
231 NativeWebKeyboardEvent
webkit_event(&events
[i
]->key
);
232 SendNativeKeyEvent(webkit_event
);
234 // Need to add a char event after the key down.
235 if (webkit_event
.type
== WebKit::WebInputEvent::RawKeyDown
) {
236 NativeWebKeyboardEvent char_event
= webkit_event
;
237 char_event
.type
= WebKit::WebInputEvent::Char
;
238 char_event
.skip_in_browser
= true;
239 SendNativeKeyEvent(char_event
);
242 gdk_event_free(events
[i
]);
245 *output
= std::wstring(1, unicode_key
);
253 void RenderViewTest::SendNativeKeyEvent(
254 const NativeWebKeyboardEvent
& key_event
) {
255 scoped_ptr
<IPC::Message
> input_message(new ViewMsg_HandleInputEvent(0));
256 input_message
->WriteData(reinterpret_cast<const char*>(&key_event
),
257 sizeof(WebKit::WebKeyboardEvent
));
258 view_
->OnMessageReceived(*input_message
);
261 const char* const kGetCoordinatesScript
=
263 " function GetCoordinates(elem) {"
266 " var coordinates = [ elem.offsetLeft, elem.offsetTop];"
267 " var parent_coordinates = GetCoordinates(elem.offsetParent);"
268 " coordinates[0] += parent_coordinates[0];"
269 " coordinates[1] += parent_coordinates[1];"
270 " return coordinates;"
272 " var elem = document.getElementById('$1');"
275 " var bounds = GetCoordinates(elem);"
276 " bounds[2] = elem.offsetWidth;"
277 " bounds[3] = elem.offsetHeight;"
280 gfx::Rect
RenderViewTest::GetElementBounds(const std::string
& element_id
) {
281 std::vector
<std::string
> params
;
282 params
.push_back(element_id
);
284 ReplaceStringPlaceholders(kGetCoordinatesScript
, params
, NULL
);
286 v8::HandleScope handle_scope
;
287 v8::Handle
<v8::Value
> value
= GetMainFrame()->executeScriptAndReturnValue(
288 WebScriptSource(WebString::fromUTF8(script
)));
289 if (value
.IsEmpty() || !value
->IsArray())
292 v8::Handle
<v8::Array
> array
= value
.As
<v8::Array
>();
293 if (array
->Length() != 4)
295 std::vector
<int> coords
;
296 for (int i
= 0; i
< 4; ++i
) {
297 v8::Handle
<v8::Number
> index
= v8::Number::New(i
);
298 v8::Local
<v8::Value
> value
= array
->Get(index
);
299 if (value
.IsEmpty() || !value
->IsInt32())
301 coords
.push_back(value
->Int32Value());
303 return gfx::Rect(coords
[0], coords
[1], coords
[2], coords
[3]);
306 bool RenderViewTest::SimulateElementClick(const std::string
& element_id
) {
307 gfx::Rect bounds
= GetElementBounds(element_id
);
308 if (bounds
.IsEmpty())
310 WebMouseEvent mouse_event
;
311 mouse_event
.type
= WebInputEvent::MouseDown
;
312 mouse_event
.button
= WebMouseEvent::ButtonLeft
;
313 mouse_event
.x
= bounds
.CenterPoint().x();
314 mouse_event
.y
= bounds
.CenterPoint().y();
315 mouse_event
.clickCount
= 1;
316 ViewMsg_HandleInputEvent
input_event(0);
317 scoped_ptr
<IPC::Message
> input_message(new ViewMsg_HandleInputEvent(0));
318 input_message
->WriteData(reinterpret_cast<const char*>(&mouse_event
),
319 sizeof(WebMouseEvent
));
320 view_
->OnMessageReceived(*input_message
);