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