OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/test/render_view_test.h" | |
6 | |
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" | |
31 | |
32 #if defined(OS_LINUX) | |
33 #include "ui/base/gtk/event_synthesis_gtk.h" | |
34 #endif | |
35 | |
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; | |
45 | |
46 namespace { | |
47 const int32 kRouteId = 5; | |
48 const int32 kOpenerId = 7; | |
49 } // namespace | |
50 | |
51 RenderViewTest::RenderViewTest() : extension_dispatcher_(NULL) { | |
52 } | |
53 | |
54 RenderViewTest::~RenderViewTest() { | |
55 } | |
56 | |
57 void RenderViewTest::ProcessPendingMessages() { | |
58 msg_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask()); | |
59 msg_loop_.Run(); | |
60 } | |
61 | |
62 WebFrame* RenderViewTest::GetMainFrame() { | |
63 return view_->webview()->mainFrame(); | |
64 } | |
65 | |
66 void RenderViewTest::ExecuteJavaScript(const char* js) { | |
67 GetMainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(js))); | |
68 } | |
69 | |
70 bool RenderViewTest::ExecuteJavaScriptAndReturnIntValue( | |
71 const string16& script, | |
72 int* int_result) { | |
73 v8::Handle<v8::Value> result = | |
74 GetMainFrame()->executeScriptAndReturnValue(WebScriptSource(script)); | |
75 if (result.IsEmpty() || !result->IsInt32()) | |
76 return false; | |
77 | |
78 if (int_result) | |
79 *int_result = result->Int32Value(); | |
80 | |
81 return true; | |
82 } | |
83 | |
84 void RenderViewTest::LoadHTML(const char* html) { | |
85 std::string url_str = "data:text/html;charset=utf-8,"; | |
86 url_str.append(html); | |
87 GURL url(url_str); | |
88 | |
89 GetMainFrame()->loadRequest(WebURLRequest(url)); | |
90 | |
91 // The load actually happens asynchronously, so we pump messages to process | |
92 // the pending continuation. | |
93 ProcessPendingMessages(); | |
94 } | |
95 | |
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_, | |
103 NULL)); | |
104 platform_.reset(new RendererMainPlatformDelegate(*params_)); | |
105 platform_->PlatformInitialize(); | |
106 | |
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_); | |
120 | |
121 mock_process_.reset(new MockRenderProcess); | |
122 | |
123 render_thread_.set_routing_id(kRouteId); | |
124 | |
125 // This needs to pass the mock render thread to the view. | |
126 view_ = RenderView::Create(&render_thread_, | |
127 0, | |
128 kOpenerId, | |
129 RendererPreferences(), | |
130 WebPreferences(), | |
131 new SharedRenderViewCounter(0), | |
132 kRouteId, | |
133 kInvalidSessionStorageNamespaceId, | |
134 string16()); | |
135 | |
136 // Attach a pseudo keyboard device to this object. | |
137 mock_keyboard_.reset(new MockKeyboard()); | |
138 | |
139 // RenderView doesn't expose it's PasswordAutofillManager or | |
140 // AutofillAgent objects, because it has no need to store them directly | |
141 // (they're stored as RenderViewObserver*). So just create another set. | |
142 password_autofill_ = new PasswordAutofillManager(view_); | |
143 autofill_agent_ = new AutofillAgent(view_, password_autofill_); | |
144 } | |
145 | |
146 void RenderViewTest::TearDown() { | |
147 // Try very hard to collect garbage before shutting down. | |
148 GetMainFrame()->collectGarbage(); | |
149 GetMainFrame()->collectGarbage(); | |
150 | |
151 render_thread_.SendCloseMessage(); | |
152 | |
153 // Run the loop so the release task from the renderwidget executes. | |
154 ProcessPendingMessages(); | |
155 | |
156 EventBindings::SetRenderThread(NULL); | |
157 | |
158 view_ = NULL; | |
159 | |
160 mock_process_.reset(); | |
161 | |
162 // After resetting the view_ and mock_process_ we may get some new tasks | |
163 // which need to be processed before shutting down WebKit | |
164 // (http://crbug.com/21508). | |
165 msg_loop_.RunAllPending(); | |
166 | |
167 WebKit::shutdown(); | |
168 | |
169 mock_keyboard_.reset(); | |
170 | |
171 platform_->PlatformUninitialize(); | |
172 platform_.reset(); | |
173 params_.reset(); | |
174 command_line_.reset(); | |
175 sandbox_init_wrapper_.reset(); | |
176 | |
177 extension_dispatcher_->OnRenderProcessShutdown(); | |
178 extension_dispatcher_ = NULL; | |
179 } | |
180 | |
181 int RenderViewTest::SendKeyEvent(MockKeyboard::Layout layout, | |
182 int key_code, | |
183 MockKeyboard::Modifiers modifiers, | |
184 std::wstring* output) { | |
185 #if defined(OS_WIN) | |
186 // Retrieve the Unicode character for the given tuple (keyboard-layout, | |
187 // key-code, and modifiers). | |
188 // Exit when a keyboard-layout driver cannot assign a Unicode character to | |
189 // the tuple to prevent sending an invalid key code to the RenderView object. | |
190 CHECK(mock_keyboard_.get()); | |
191 CHECK(output); | |
192 int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers, | |
193 output); | |
194 if (length != 1) | |
195 return -1; | |
196 | |
197 // Create IPC messages from Windows messages and send them to our | |
198 // back-end. | |
199 // A keyboard event of Windows consists of three Windows messages: | |
200 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP. | |
201 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand, | |
202 // WM_CHAR sends a composed Unicode character. | |
203 NativeWebKeyboardEvent keydown_event(NULL, WM_KEYDOWN, key_code, 0); | |
204 SendNativeKeyEvent(keydown_event); | |
205 | |
206 NativeWebKeyboardEvent char_event(NULL, WM_CHAR, (*output)[0], 0); | |
207 SendNativeKeyEvent(char_event); | |
208 | |
209 NativeWebKeyboardEvent keyup_event(NULL, WM_KEYUP, key_code, 0); | |
210 SendNativeKeyEvent(keyup_event); | |
211 | |
212 return length; | |
213 #elif defined(OS_LINUX) | |
214 // We ignore |layout|, which means we are only testing the layout of the | |
215 // current locale. TODO(estade): fix this to respect |layout|. | |
216 std::vector<GdkEvent*> events; | |
217 ui::SynthesizeKeyPressEvents( | |
218 NULL, static_cast<ui::KeyboardCode>(key_code), | |
219 modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL), | |
220 modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT), | |
221 modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT), | |
222 &events); | |
223 | |
224 guint32 unicode_key = 0; | |
225 for (size_t i = 0; i < events.size(); ++i) { | |
226 // Only send the up/down events for key press itself (skip the up/down | |
227 // events for the modifier keys). | |
228 if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) { | |
229 unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval); | |
230 NativeWebKeyboardEvent webkit_event(&events[i]->key); | |
231 SendNativeKeyEvent(webkit_event); | |
232 | |
233 // Need to add a char event after the key down. | |
234 if (webkit_event.type == WebKit::WebInputEvent::RawKeyDown) { | |
235 NativeWebKeyboardEvent char_event = webkit_event; | |
236 char_event.type = WebKit::WebInputEvent::Char; | |
237 char_event.skip_in_browser = true; | |
238 SendNativeKeyEvent(char_event); | |
239 } | |
240 } | |
241 gdk_event_free(events[i]); | |
242 } | |
243 | |
244 *output = std::wstring(1, unicode_key); | |
245 return 1; | |
246 #else | |
247 NOTIMPLEMENTED(); | |
248 return L'\0'; | |
249 #endif | |
250 } | |
251 | |
252 void RenderViewTest::SendNativeKeyEvent( | |
253 const NativeWebKeyboardEvent& key_event) { | |
254 scoped_ptr<IPC::Message> input_message(new ViewMsg_HandleInputEvent(0)); | |
255 input_message->WriteData(reinterpret_cast<const char*>(&key_event), | |
256 sizeof(WebKit::WebKeyboardEvent)); | |
257 view_->OnMessageReceived(*input_message); | |
258 } | |
259 | |
260 const char* const kGetCoordinatesScript = | |
261 "(function() {" | |
262 " function GetCoordinates(elem) {" | |
263 " if (!elem)" | |
264 " return [ 0, 0];" | |
265 " var coordinates = [ elem.offsetLeft, elem.offsetTop];" | |
266 " var parent_coordinates = GetCoordinates(elem.offsetParent);" | |
267 " coordinates[0] += parent_coordinates[0];" | |
268 " coordinates[1] += parent_coordinates[1];" | |
269 " return coordinates;" | |
270 " };" | |
271 " var elem = document.getElementById('$1');" | |
272 " if (!elem)" | |
273 " return null;" | |
274 " var bounds = GetCoordinates(elem);" | |
275 " bounds[2] = elem.offsetWidth;" | |
276 " bounds[3] = elem.offsetHeight;" | |
277 " return bounds;" | |
278 "})();"; | |
279 gfx::Rect RenderViewTest::GetElementBounds(const std::string& element_id) { | |
280 std::vector<std::string> params; | |
281 params.push_back(element_id); | |
282 std::string script = | |
283 ReplaceStringPlaceholders(kGetCoordinatesScript, params, NULL); | |
284 | |
285 v8::HandleScope handle_scope; | |
286 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue( | |
287 WebScriptSource(WebString::fromUTF8(script))); | |
288 if (value.IsEmpty() || !value->IsArray()) | |
289 return gfx::Rect(); | |
290 | |
291 v8::Handle<v8::Array> array = value.As<v8::Array>(); | |
292 if (array->Length() != 4) | |
293 return gfx::Rect(); | |
294 std::vector<int> coords; | |
295 for (int i = 0; i < 4; ++i) { | |
296 v8::Handle<v8::Number> index = v8::Number::New(i); | |
297 v8::Local<v8::Value> value = array->Get(index); | |
298 if (value.IsEmpty() || !value->IsInt32()) | |
299 return gfx::Rect(); | |
300 coords.push_back(value->Int32Value()); | |
301 } | |
302 return gfx::Rect(coords[0], coords[1], coords[2], coords[3]); | |
303 } | |
304 | |
305 bool RenderViewTest::SimulateElementClick(const std::string& element_id) { | |
306 gfx::Rect bounds = GetElementBounds(element_id); | |
307 if (bounds.IsEmpty()) | |
308 return false; | |
309 WebMouseEvent mouse_event; | |
310 mouse_event.type = WebInputEvent::MouseDown; | |
311 mouse_event.button = WebMouseEvent::ButtonLeft; | |
312 mouse_event.x = bounds.CenterPoint().x(); | |
313 mouse_event.y = bounds.CenterPoint().y(); | |
314 mouse_event.clickCount = 1; | |
315 ViewMsg_HandleInputEvent input_event(0); | |
316 scoped_ptr<IPC::Message> input_message(new ViewMsg_HandleInputEvent(0)); | |
317 input_message->WriteData(reinterpret_cast<const char*>(&mouse_event), | |
318 sizeof(WebMouseEvent)); | |
319 view_->OnMessageReceived(*input_message); | |
320 return true; | |
321 } | |
OLD | NEW |