1 // Copyright (c) 2012 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 "content/public/test/render_view_test.h" 6 7 #include "base/run_loop.h" 8 #include "content/common/dom_storage/dom_storage_types.h" 9 #include "content/common/input_messages.h" 10 #include "content/common/view_messages.h" 11 #include "content/public/browser/native_web_keyboard_event.h" 12 #include "content/public/common/renderer_preferences.h" 13 #include "content/public/renderer/history_item_serialization.h" 14 #include "content/renderer/render_thread_impl.h" 15 #include "content/renderer/render_view_impl.h" 16 #include "content/renderer/renderer_main_platform_delegate.h" 17 #include "content/renderer/renderer_webkitplatformsupport_impl.h" 18 #include "content/test/mock_render_process.h" 19 #include "third_party/WebKit/public/platform/WebURLRequest.h" 20 #include "third_party/WebKit/public/web/WebFrame.h" 21 #include "third_party/WebKit/public/web/WebHistoryItem.h" 22 #include "third_party/WebKit/public/web/WebInputEvent.h" 23 #include "third_party/WebKit/public/web/WebKit.h" 24 #include "third_party/WebKit/public/web/WebScreenInfo.h" 25 #include "third_party/WebKit/public/web/WebScriptController.h" 26 #include "third_party/WebKit/public/web/WebScriptSource.h" 27 #include "third_party/WebKit/public/web/WebView.h" 28 #include "ui/base/resource/resource_bundle.h" 29 #include "webkit/glue/webkit_glue.h" 30 31 using WebKit::WebFrame; 32 using WebKit::WebInputEvent; 33 using WebKit::WebMouseEvent; 34 using WebKit::WebScriptController; 35 using WebKit::WebScriptSource; 36 using WebKit::WebString; 37 using WebKit::WebURLRequest; 38 39 namespace { 40 const int32 kOpenerId = -2; 41 const int32 kRouteId = 5; 42 const int32 kMainFrameRouteId = 6; 43 const int32 kNewWindowRouteId = 7; 44 const int32 kSurfaceId = 42; 45 46 } // namespace 47 48 namespace content { 49 50 class RendererWebKitPlatformSupportImplNoSandboxImpl 51 : public RendererWebKitPlatformSupportImpl { 52 public: 53 virtual WebKit::WebSandboxSupport* sandboxSupport() { 54 return NULL; 55 } 56 }; 57 58 RenderViewTest::RendererWebKitPlatformSupportImplNoSandbox:: 59 RendererWebKitPlatformSupportImplNoSandbox() { 60 webkit_platform_support_.reset( 61 new RendererWebKitPlatformSupportImplNoSandboxImpl()); 62 } 63 64 RenderViewTest::RendererWebKitPlatformSupportImplNoSandbox:: 65 ~RendererWebKitPlatformSupportImplNoSandbox() { 66 } 67 68 WebKit::Platform* 69 RenderViewTest::RendererWebKitPlatformSupportImplNoSandbox::Get() { 70 return webkit_platform_support_.get(); 71 } 72 73 RenderViewTest::RenderViewTest() 74 : view_(NULL) { 75 } 76 77 RenderViewTest::~RenderViewTest() { 78 } 79 80 void RenderViewTest::ProcessPendingMessages() { 81 msg_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 82 msg_loop_.Run(); 83 } 84 85 WebFrame* RenderViewTest::GetMainFrame() { 86 return view_->GetWebView()->mainFrame(); 87 } 88 89 void RenderViewTest::ExecuteJavaScript(const char* js) { 90 GetMainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(js))); 91 } 92 93 bool RenderViewTest::ExecuteJavaScriptAndReturnIntValue( 94 const string16& script, 95 int* int_result) { 96 v8::HandleScope handle_scope; 97 v8::Handle<v8::Value> result = 98 GetMainFrame()->executeScriptAndReturnValue(WebScriptSource(script)); 99 if (result.IsEmpty() || !result->IsInt32()) 100 return false; 101 102 if (int_result) 103 *int_result = result->Int32Value(); 104 105 return true; 106 } 107 108 void RenderViewTest::LoadHTML(const char* html) { 109 std::string url_str = "data:text/html;charset=utf-8,"; 110 url_str.append(html); 111 GURL url(url_str); 112 113 GetMainFrame()->loadRequest(WebURLRequest(url)); 114 115 // The load actually happens asynchronously, so we pump messages to process 116 // the pending continuation. 117 ProcessPendingMessages(); 118 } 119 120 void RenderViewTest::GoBack(const WebKit::WebHistoryItem& item) { 121 GoToOffset(-1, item); 122 } 123 124 void RenderViewTest::GoForward(const WebKit::WebHistoryItem& item) { 125 GoToOffset(1, item); 126 } 127 128 void RenderViewTest::SetUp() { 129 // Subclasses can set the ContentClient's renderer before calling 130 // RenderViewTest::SetUp(). 131 ContentRendererClient* old_client = 132 SetRendererClientForTesting(&content_renderer_client_); 133 if (old_client) 134 SetRendererClientForTesting(old_client); 135 136 // Subclasses can set render_thread_ with their own implementation before 137 // calling RenderViewTest::SetUp(). 138 if (!render_thread_) 139 render_thread_.reset(new MockRenderThread()); 140 render_thread_->set_routing_id(kRouteId); 141 render_thread_->set_surface_id(kSurfaceId); 142 render_thread_->set_new_window_routing_id(kNewWindowRouteId); 143 144 command_line_.reset(new CommandLine(CommandLine::NO_PROGRAM)); 145 params_.reset(new MainFunctionParams(*command_line_)); 146 platform_.reset(new RendererMainPlatformDelegate(*params_)); 147 platform_->PlatformInitialize(); 148 149 // Setting flags and really doing anything with WebKit is fairly fragile and 150 // hacky, but this is the world we live in... 151 webkit_glue::SetJavaScriptFlags(" --expose-gc"); 152 WebKit::initialize(webkit_platform_support_.Get()); 153 154 // Ensure that we register any necessary schemes when initializing WebKit, 155 // since we are using a MockRenderThread. 156 RenderThreadImpl::RegisterSchemes(); 157 158 // This check is needed because when run under content_browsertests, 159 // ResourceBundle isn't initialized (since we have to use a diferent test 160 // suite implementation than for content_unittests). For browser_tests, this 161 // is already initialized. 162 if (!ResourceBundle::HasSharedInstance()) 163 ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); 164 165 mock_process_.reset(new MockRenderProcess); 166 167 // This needs to pass the mock render thread to the view. 168 RenderViewImpl* view = RenderViewImpl::Create( 169 kOpenerId, 170 RendererPreferences(), 171 WebPreferences(), 172 new SharedRenderViewCounter(0), 173 kRouteId, 174 kMainFrameRouteId, 175 kSurfaceId, 176 kInvalidSessionStorageNamespaceId, 177 string16(), 178 false, 179 false, 180 1, 181 WebKit::WebScreenInfo(), 182 AccessibilityModeOff, 183 true); 184 view->AddRef(); 185 view_ = view; 186 } 187 188 void RenderViewTest::TearDown() { 189 // Try very hard to collect garbage before shutting down. 190 GetMainFrame()->collectGarbage(); 191 GetMainFrame()->collectGarbage(); 192 193 // Run the loop so the release task from the renderwidget executes. 194 ProcessPendingMessages(); 195 196 render_thread_->SendCloseMessage(); 197 view_ = NULL; 198 mock_process_.reset(); 199 200 // After telling the view to close and resetting mock_process_ we may get 201 // some new tasks which need to be processed before shutting down WebKit 202 // (http://crbug.com/21508). 203 base::RunLoop().RunUntilIdle(); 204 205 WebKit::shutdown(); 206 207 platform_->PlatformUninitialize(); 208 platform_.reset(); 209 params_.reset(); 210 command_line_.reset(); 211 } 212 213 void RenderViewTest::SendNativeKeyEvent( 214 const NativeWebKeyboardEvent& key_event) { 215 SendWebKeyboardEvent(key_event); 216 } 217 218 void RenderViewTest::SendWebKeyboardEvent( 219 const WebKit::WebKeyboardEvent& key_event) { 220 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 221 impl->OnMessageReceived( 222 InputMsg_HandleInputEvent(0, &key_event, ui::LatencyInfo(), false)); 223 } 224 225 void RenderViewTest::SendWebMouseEvent( 226 const WebKit::WebMouseEvent& mouse_event) { 227 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 228 impl->OnMessageReceived( 229 InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), false)); 230 } 231 232 const char* const kGetCoordinatesScript = 233 "(function() {" 234 " function GetCoordinates(elem) {" 235 " if (!elem)" 236 " return [ 0, 0];" 237 " var coordinates = [ elem.offsetLeft, elem.offsetTop];" 238 " var parent_coordinates = GetCoordinates(elem.offsetParent);" 239 " coordinates[0] += parent_coordinates[0];" 240 " coordinates[1] += parent_coordinates[1];" 241 " return coordinates;" 242 " };" 243 " var elem = document.getElementById('$1');" 244 " if (!elem)" 245 " return null;" 246 " var bounds = GetCoordinates(elem);" 247 " bounds[2] = elem.offsetWidth;" 248 " bounds[3] = elem.offsetHeight;" 249 " return bounds;" 250 "})();"; 251 gfx::Rect RenderViewTest::GetElementBounds(const std::string& element_id) { 252 std::vector<std::string> params; 253 params.push_back(element_id); 254 std::string script = 255 ReplaceStringPlaceholders(kGetCoordinatesScript, params, NULL); 256 257 v8::HandleScope handle_scope; 258 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue( 259 WebScriptSource(WebString::fromUTF8(script))); 260 if (value.IsEmpty() || !value->IsArray()) 261 return gfx::Rect(); 262 263 v8::Handle<v8::Array> array = value.As<v8::Array>(); 264 if (array->Length() != 4) 265 return gfx::Rect(); 266 std::vector<int> coords; 267 for (int i = 0; i < 4; ++i) { 268 v8::Handle<v8::Number> index = v8::Number::New(i); 269 v8::Local<v8::Value> value = array->Get(index); 270 if (value.IsEmpty() || !value->IsInt32()) 271 return gfx::Rect(); 272 coords.push_back(value->Int32Value()); 273 } 274 return gfx::Rect(coords[0], coords[1], coords[2], coords[3]); 275 } 276 277 bool RenderViewTest::SimulateElementClick(const std::string& element_id) { 278 gfx::Rect bounds = GetElementBounds(element_id); 279 if (bounds.IsEmpty()) 280 return false; 281 WebMouseEvent mouse_event; 282 mouse_event.type = WebInputEvent::MouseDown; 283 mouse_event.button = WebMouseEvent::ButtonLeft; 284 mouse_event.x = bounds.CenterPoint().x(); 285 mouse_event.y = bounds.CenterPoint().y(); 286 mouse_event.clickCount = 1; 287 scoped_ptr<IPC::Message> input_message( 288 new InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(), false)); 289 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 290 impl->OnMessageReceived(*input_message); 291 return true; 292 } 293 294 void RenderViewTest::SetFocused(const WebKit::WebNode& node) { 295 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 296 impl->focusedNodeChanged(node); 297 } 298 299 void RenderViewTest::ClearHistory() { 300 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 301 impl->page_id_ = -1; 302 impl->history_list_offset_ = -1; 303 impl->history_list_length_ = 0; 304 impl->history_page_ids_.clear(); 305 } 306 307 void RenderViewTest::Reload(const GURL& url) { 308 ViewMsg_Navigate_Params params; 309 params.url = url; 310 params.navigation_type = ViewMsg_Navigate_Type::RELOAD; 311 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 312 impl->OnNavigate(params); 313 } 314 315 uint32 RenderViewTest::GetNavigationIPCType() { 316 return ViewHostMsg_FrameNavigate::ID; 317 } 318 319 void RenderViewTest::Resize(gfx::Size new_size, 320 gfx::Rect resizer_rect, 321 bool is_fullscreen) { 322 ViewMsg_Resize_Params params; 323 params.screen_info = WebKit::WebScreenInfo(); 324 params.new_size = new_size; 325 params.physical_backing_size = new_size; 326 params.overdraw_bottom_height = 0.f; 327 params.resizer_rect = resizer_rect; 328 params.is_fullscreen = is_fullscreen; 329 scoped_ptr<IPC::Message> resize_message(new ViewMsg_Resize(0, params)); 330 OnMessageReceived(*resize_message); 331 } 332 333 bool RenderViewTest::OnMessageReceived(const IPC::Message& msg) { 334 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 335 return impl->OnMessageReceived(msg); 336 } 337 338 void RenderViewTest::DidNavigateWithinPage(WebKit::WebFrame* frame, 339 bool is_new_navigation) { 340 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 341 impl->didNavigateWithinPage(frame, is_new_navigation); 342 } 343 344 void RenderViewTest::SendContentStateImmediately() { 345 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 346 impl->set_send_content_state_immediately(true); 347 } 348 349 WebKit::WebWidget* RenderViewTest::GetWebWidget() { 350 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 351 return impl->webwidget(); 352 } 353 354 void RenderViewTest::GoToOffset(int offset, 355 const WebKit::WebHistoryItem& history_item) { 356 RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_); 357 358 int history_list_length = impl->historyBackListCount() + 359 impl->historyForwardListCount() + 1; 360 int pending_offset = offset + impl->history_list_offset(); 361 362 ViewMsg_Navigate_Params navigate_params; 363 navigate_params.navigation_type = ViewMsg_Navigate_Type::NORMAL; 364 navigate_params.transition = PAGE_TRANSITION_FORWARD_BACK; 365 navigate_params.current_history_list_length = history_list_length; 366 navigate_params.current_history_list_offset = impl->history_list_offset(); 367 navigate_params.pending_history_list_offset = pending_offset; 368 navigate_params.page_id = impl->GetPageId() + offset; 369 navigate_params.page_state = HistoryItemToPageState(history_item); 370 navigate_params.request_time = base::Time::Now(); 371 372 ViewMsg_Navigate navigate_message(impl->GetRoutingID(), navigate_params); 373 OnMessageReceived(navigate_message); 374 375 // The load actually happens asynchronously, so we pump messages to process 376 // the pending continuation. 377 ProcessPendingMessages(); 378 } 379 380 } // namespace content 381