Home | History | Annotate | Download | only in shell
      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/shell/webkit_test_controller.h"
      6 
      7 #include <iostream>
      8 
      9 #include "base/base64.h"
     10 #include "base/command_line.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/run_loop.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "content/public/browser/devtools_manager.h"
     16 #include "content/public/browser/gpu_data_manager.h"
     17 #include "content/public/browser/navigation_controller.h"
     18 #include "content/public/browser/navigation_entry.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/browser/notification_types.h"
     21 #include "content/public/browser/render_process_host.h"
     22 #include "content/public/browser/render_view_host.h"
     23 #include "content/public/browser/render_widget_host_view.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "content/public/browser/web_contents_view.h"
     26 #include "content/public/common/content_switches.h"
     27 #include "content/shell/common/shell_messages.h"
     28 #include "content/shell/common/shell_switches.h"
     29 #include "content/shell/common/webkit_test_helpers.h"
     30 #include "content/shell/shell.h"
     31 #include "content/shell/shell_browser_context.h"
     32 #include "content/shell/shell_content_browser_client.h"
     33 #include "ui/gfx/codec/png_codec.h"
     34 
     35 namespace content {
     36 
     37 const int kTestSVGWindowWidthDip = 480;
     38 const int kTestSVGWindowHeightDip = 360;
     39 
     40 // WebKitTestResultPrinter ----------------------------------------------------
     41 
     42 WebKitTestResultPrinter::WebKitTestResultPrinter(
     43     std::ostream* output, std::ostream* error)
     44     : state_(DURING_TEST),
     45       capture_text_only_(false),
     46       encode_binary_data_(false),
     47       output_(output),
     48       error_(error) {
     49 }
     50 
     51 WebKitTestResultPrinter::~WebKitTestResultPrinter() {
     52 }
     53 
     54 void WebKitTestResultPrinter::PrintTextHeader() {
     55   if (state_ != DURING_TEST)
     56     return;
     57   if (!capture_text_only_)
     58     *output_ << "Content-Type: text/plain\n";
     59   state_ = IN_TEXT_BLOCK;
     60 }
     61 
     62 void WebKitTestResultPrinter::PrintTextBlock(const std::string& block) {
     63   if (state_ != IN_TEXT_BLOCK)
     64     return;
     65   *output_ << block;
     66 }
     67 
     68 void WebKitTestResultPrinter::PrintTextFooter() {
     69   if (state_ != IN_TEXT_BLOCK)
     70     return;
     71   if (!capture_text_only_) {
     72     *output_ << "#EOF\n";
     73     output_->flush();
     74   }
     75   state_ = IN_IMAGE_BLOCK;
     76 }
     77 
     78 void WebKitTestResultPrinter::PrintImageHeader(
     79     const std::string& actual_hash,
     80     const std::string& expected_hash) {
     81   if (state_ != IN_IMAGE_BLOCK || capture_text_only_)
     82     return;
     83   *output_ << "\nActualHash: " << actual_hash << "\n";
     84   if (!expected_hash.empty())
     85     *output_ << "\nExpectedHash: " << expected_hash << "\n";
     86 }
     87 
     88 void WebKitTestResultPrinter::PrintImageBlock(
     89     const std::vector<unsigned char>& png_image) {
     90   if (state_ != IN_IMAGE_BLOCK || capture_text_only_)
     91     return;
     92   *output_ << "Content-Type: image/png\n";
     93   if (encode_binary_data_) {
     94     PrintEncodedBinaryData(png_image);
     95     return;
     96   }
     97 
     98   *output_ << "Content-Length: " << png_image.size() << "\n";
     99   output_->write(
    100       reinterpret_cast<const char*>(&png_image[0]), png_image.size());
    101 }
    102 
    103 void WebKitTestResultPrinter::PrintImageFooter() {
    104   if (state_ != IN_IMAGE_BLOCK)
    105     return;
    106   if (!capture_text_only_) {
    107     *output_ << "#EOF\n";
    108     *error_ << "#EOF\n";
    109     output_->flush();
    110     error_->flush();
    111   }
    112   state_ = AFTER_TEST;
    113 }
    114 
    115 void WebKitTestResultPrinter::PrintAudioHeader() {
    116   DCHECK_EQ(state_, DURING_TEST);
    117   if (!capture_text_only_)
    118     *output_ << "Content-Type: audio/wav\n";
    119   state_ = IN_AUDIO_BLOCK;
    120 }
    121 
    122 void WebKitTestResultPrinter::PrintAudioBlock(
    123     const std::vector<unsigned char>& audio_data) {
    124   if (state_ != IN_AUDIO_BLOCK || capture_text_only_)
    125     return;
    126   if (encode_binary_data_) {
    127     PrintEncodedBinaryData(audio_data);
    128     return;
    129   }
    130 
    131   *output_ << "Content-Length: " << audio_data.size() << "\n";
    132   output_->write(
    133       reinterpret_cast<const char*>(&audio_data[0]), audio_data.size());
    134 }
    135 
    136 void WebKitTestResultPrinter::PrintAudioFooter() {
    137   if (state_ != IN_AUDIO_BLOCK)
    138     return;
    139   if (!capture_text_only_) {
    140     *output_ << "#EOF\n";
    141     *error_ << "#EOF\n";
    142     output_->flush();
    143     error_->flush();
    144   }
    145   state_ = IN_IMAGE_BLOCK;
    146 }
    147 
    148 void WebKitTestResultPrinter::AddMessage(const std::string& message) {
    149   AddMessageRaw(message + "\n");
    150 }
    151 
    152 void WebKitTestResultPrinter::AddMessageRaw(const std::string& message) {
    153   if (state_ != DURING_TEST)
    154     return;
    155   *output_ << message;
    156 }
    157 
    158 void WebKitTestResultPrinter::AddErrorMessage(const std::string& message) {
    159   if (!capture_text_only_)
    160     *error_ << message << "\n";
    161   if (state_ != DURING_TEST)
    162     return;
    163   PrintTextHeader();
    164   *output_ << message << "\n";
    165   PrintTextFooter();
    166   PrintImageFooter();
    167 }
    168 
    169 void WebKitTestResultPrinter::PrintEncodedBinaryData(
    170     const std::vector<unsigned char>& data) {
    171   *output_ << "Content-Transfer-Encoding: base64\n";
    172 
    173   std::string data_base64;
    174   const bool success = base::Base64Encode(
    175       base::StringPiece(reinterpret_cast<const char*>(&data[0]), data.size()),
    176       &data_base64);
    177   DCHECK(success);
    178 
    179   *output_ << "Content-Length: " << data_base64.length() << "\n";
    180   output_->write(data_base64.c_str(), data_base64.length());
    181 }
    182 
    183 
    184 // WebKitTestController -------------------------------------------------------
    185 
    186 WebKitTestController* WebKitTestController::instance_ = NULL;
    187 
    188 // static
    189 WebKitTestController* WebKitTestController::Get() {
    190   DCHECK(instance_);
    191   return instance_;
    192 }
    193 
    194 WebKitTestController::WebKitTestController()
    195     : main_window_(NULL),
    196       test_phase_(BETWEEN_TESTS) {
    197   CHECK(!instance_);
    198   instance_ = this;
    199   printer_.reset(new WebKitTestResultPrinter(&std::cout, &std::cerr));
    200   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEncodeBinary))
    201     printer_->set_encode_binary_data(true);
    202   registrar_.Add(this,
    203                  NOTIFICATION_RENDERER_PROCESS_CREATED,
    204                  NotificationService::AllSources());
    205   GpuDataManager::GetInstance()->AddObserver(this);
    206   ResetAfterLayoutTest();
    207 }
    208 
    209 WebKitTestController::~WebKitTestController() {
    210   DCHECK(CalledOnValidThread());
    211   CHECK(instance_ == this);
    212   CHECK(test_phase_ == BETWEEN_TESTS);
    213   GpuDataManager::GetInstance()->RemoveObserver(this);
    214   DiscardMainWindow();
    215   instance_ = NULL;
    216 }
    217 
    218 bool WebKitTestController::PrepareForLayoutTest(
    219     const GURL& test_url,
    220     const base::FilePath& current_working_directory,
    221     bool enable_pixel_dumping,
    222     const std::string& expected_pixel_hash) {
    223   DCHECK(CalledOnValidThread());
    224   test_phase_ = DURING_TEST;
    225   current_working_directory_ = current_working_directory;
    226   enable_pixel_dumping_ = enable_pixel_dumping;
    227   expected_pixel_hash_ = expected_pixel_hash;
    228   test_url_ = test_url;
    229   printer_->reset();
    230   ShellBrowserContext* browser_context =
    231       ShellContentBrowserClient::Get()->browser_context();
    232   if (test_url.spec().find("compositing/") != std::string::npos)
    233     is_compositing_test_ = true;
    234   initial_size_ = gfx::Size(
    235       Shell::kDefaultTestWindowWidthDip, Shell::kDefaultTestWindowHeightDip);
    236   // The W3C SVG layout tests use a different size than the other layout tests.
    237   if (test_url.spec().find("W3C-SVG-1.1") != std::string::npos)
    238     initial_size_ = gfx::Size(kTestSVGWindowWidthDip, kTestSVGWindowHeightDip);
    239   if (!main_window_) {
    240     main_window_ = content::Shell::CreateNewWindow(
    241         browser_context,
    242         GURL(),
    243         NULL,
    244         MSG_ROUTING_NONE,
    245         initial_size_);
    246     WebContentsObserver::Observe(main_window_->web_contents());
    247     send_configuration_to_next_host_ = true;
    248     current_pid_ = base::kNullProcessId;
    249     main_window_->LoadURL(test_url);
    250   } else {
    251 #if (defined(OS_WIN) && !defined(USE_AURA)) || \
    252     defined(TOOLKIT_GTK) || defined(OS_MACOSX)
    253     // Shell::SizeTo is not implemented on all platforms.
    254     main_window_->SizeTo(initial_size_.width(), initial_size_.height());
    255 #endif
    256     main_window_->web_contents()->GetRenderViewHost()->GetView()
    257         ->SetSize(initial_size_);
    258     main_window_->web_contents()->GetRenderViewHost()->WasResized();
    259     RenderViewHost* render_view_host =
    260         main_window_->web_contents()->GetRenderViewHost();
    261     WebPreferences prefs = render_view_host->GetWebkitPreferences();
    262     OverrideWebkitPrefs(&prefs);
    263     render_view_host->UpdateWebkitPreferences(prefs);
    264     SendTestConfiguration();
    265 
    266     NavigationController::LoadURLParams params(test_url);
    267     params.transition_type = PageTransitionFromInt(
    268         PAGE_TRANSITION_TYPED | PAGE_TRANSITION_FROM_ADDRESS_BAR);
    269     params.should_clear_history_list = true;
    270     main_window_->web_contents()->GetController().LoadURLWithParams(params);
    271     main_window_->web_contents()->GetView()->Focus();
    272   }
    273   main_window_->web_contents()->GetRenderViewHost()->SetActive(true);
    274   main_window_->web_contents()->GetRenderViewHost()->Focus();
    275   return true;
    276 }
    277 
    278 bool WebKitTestController::ResetAfterLayoutTest() {
    279   DCHECK(CalledOnValidThread());
    280   printer_->PrintTextFooter();
    281   printer_->PrintImageFooter();
    282   send_configuration_to_next_host_ = false;
    283   test_phase_ = BETWEEN_TESTS;
    284   is_compositing_test_ = false;
    285   enable_pixel_dumping_ = false;
    286   expected_pixel_hash_.clear();
    287   test_url_ = GURL();
    288   prefs_ = WebPreferences();
    289   should_override_prefs_ = false;
    290   return true;
    291 }
    292 
    293 void WebKitTestController::SetTempPath(const base::FilePath& temp_path) {
    294   temp_path_ = temp_path;
    295 }
    296 
    297 void WebKitTestController::RendererUnresponsive() {
    298   DCHECK(CalledOnValidThread());
    299   LOG(WARNING) << "renderer unresponsive";
    300 }
    301 
    302 void WebKitTestController::WorkerCrashed() {
    303   DCHECK(CalledOnValidThread());
    304   printer_->AddErrorMessage("#CRASHED - worker");
    305   DiscardMainWindow();
    306 }
    307 
    308 void WebKitTestController::OverrideWebkitPrefs(WebPreferences* prefs) {
    309   if (should_override_prefs_) {
    310     *prefs = prefs_;
    311   } else {
    312     ApplyLayoutTestDefaultPreferences(prefs);
    313     if (is_compositing_test_) {
    314       CommandLine& command_line = *CommandLine::ForCurrentProcess();
    315       if (!command_line.HasSwitch(switches::kEnableSoftwareCompositing))
    316         prefs->accelerated_2d_canvas_enabled = true;
    317       prefs->accelerated_compositing_for_video_enabled = true;
    318       prefs->mock_scrollbars_enabled = true;
    319     }
    320   }
    321 }
    322 
    323 void WebKitTestController::OpenURL(const GURL& url) {
    324   if (test_phase_ != DURING_TEST)
    325     return;
    326 
    327   Shell::CreateNewWindow(main_window_->web_contents()->GetBrowserContext(),
    328                          url,
    329                          main_window_->web_contents()->GetSiteInstance(),
    330                          MSG_ROUTING_NONE,
    331                          gfx::Size());
    332 }
    333 
    334 void WebKitTestController::TestFinishedInSecondaryWindow() {
    335   RenderViewHost* render_view_host =
    336       main_window_->web_contents()->GetRenderViewHost();
    337   render_view_host->Send(
    338       new ShellViewMsg_NotifyDone(render_view_host->GetRoutingID()));
    339 }
    340 
    341 bool WebKitTestController::IsMainWindow(WebContents* web_contents) const {
    342   return main_window_ && web_contents == main_window_->web_contents();
    343 }
    344 
    345 bool WebKitTestController::OnMessageReceived(const IPC::Message& message) {
    346   DCHECK(CalledOnValidThread());
    347   bool handled = true;
    348   IPC_BEGIN_MESSAGE_MAP(WebKitTestController, message)
    349     IPC_MESSAGE_HANDLER(ShellViewHostMsg_PrintMessage, OnPrintMessage)
    350     IPC_MESSAGE_HANDLER(ShellViewHostMsg_TextDump, OnTextDump)
    351     IPC_MESSAGE_HANDLER(ShellViewHostMsg_ImageDump, OnImageDump)
    352     IPC_MESSAGE_HANDLER(ShellViewHostMsg_AudioDump, OnAudioDump)
    353     IPC_MESSAGE_HANDLER(ShellViewHostMsg_OverridePreferences,
    354                         OnOverridePreferences)
    355     IPC_MESSAGE_HANDLER(ShellViewHostMsg_TestFinished, OnTestFinished)
    356     IPC_MESSAGE_HANDLER(ShellViewHostMsg_ShowDevTools, OnShowDevTools)
    357     IPC_MESSAGE_HANDLER(ShellViewHostMsg_CloseDevTools, OnCloseDevTools)
    358     IPC_MESSAGE_HANDLER(ShellViewHostMsg_GoToOffset, OnGoToOffset)
    359     IPC_MESSAGE_HANDLER(ShellViewHostMsg_Reload, OnReload)
    360     IPC_MESSAGE_HANDLER(ShellViewHostMsg_LoadURLForFrame, OnLoadURLForFrame)
    361     IPC_MESSAGE_HANDLER(ShellViewHostMsg_CaptureSessionHistory,
    362                         OnCaptureSessionHistory)
    363     IPC_MESSAGE_HANDLER(ShellViewHostMsg_CloseRemainingWindows,
    364                         OnCloseRemainingWindows)
    365     IPC_MESSAGE_HANDLER(ShellViewHostMsg_ResetDone, OnResetDone)
    366     IPC_MESSAGE_UNHANDLED(handled = false)
    367   IPC_END_MESSAGE_MAP()
    368 
    369   return handled;
    370 }
    371 
    372 void WebKitTestController::PluginCrashed(const base::FilePath& plugin_path,
    373                                          base::ProcessId plugin_pid) {
    374   DCHECK(CalledOnValidThread());
    375   printer_->AddErrorMessage(
    376       base::StringPrintf("#CRASHED - plugin (pid %d)", plugin_pid));
    377   base::MessageLoop::current()->PostTask(
    378       FROM_HERE,
    379       base::Bind(base::IgnoreResult(&WebKitTestController::DiscardMainWindow),
    380                  base::Unretained(this)));
    381 }
    382 
    383 void WebKitTestController::RenderViewCreated(RenderViewHost* render_view_host) {
    384   DCHECK(CalledOnValidThread());
    385   // Might be kNullProcessHandle, in which case we will receive a notification
    386   // later when the RenderProcessHost was created.
    387   if (render_view_host->GetProcess()->GetHandle() != base::kNullProcessHandle)
    388     current_pid_ = base::GetProcId(render_view_host->GetProcess()->GetHandle());
    389   if (!send_configuration_to_next_host_)
    390     return;
    391   send_configuration_to_next_host_ = false;
    392   SendTestConfiguration();
    393 }
    394 
    395 void WebKitTestController::RenderProcessGone(base::TerminationStatus status) {
    396   DCHECK(CalledOnValidThread());
    397   if (current_pid_ != base::kNullProcessId) {
    398     printer_->AddErrorMessage(std::string("#CRASHED - renderer (pid ") +
    399                               base::IntToString(current_pid_) + ")");
    400   } else {
    401     printer_->AddErrorMessage("#CRASHED - renderer");
    402   }
    403   DiscardMainWindow();
    404 }
    405 
    406 void WebKitTestController::WebContentsDestroyed(WebContents* web_contents) {
    407   DCHECK(CalledOnValidThread());
    408   printer_->AddErrorMessage("FAIL: main window was destroyed");
    409   DiscardMainWindow();
    410 }
    411 
    412 void WebKitTestController::Observe(int type,
    413                                    const NotificationSource& source,
    414                                    const NotificationDetails& details) {
    415   DCHECK(CalledOnValidThread());
    416   switch (type) {
    417     case NOTIFICATION_RENDERER_PROCESS_CREATED: {
    418       if (!main_window_)
    419         return;
    420       RenderViewHost* render_view_host =
    421           main_window_->web_contents()->GetRenderViewHost();
    422       if (!render_view_host)
    423         return;
    424       RenderProcessHost* render_process_host =
    425           Source<RenderProcessHost>(source).ptr();
    426       if (render_process_host != render_view_host->GetProcess())
    427         return;
    428       current_pid_ = base::GetProcId(render_process_host->GetHandle());
    429       break;
    430     }
    431     default:
    432       NOTREACHED();
    433   }
    434 }
    435 
    436 void WebKitTestController::OnGpuProcessCrashed(
    437     base::TerminationStatus exit_code) {
    438   DCHECK(CalledOnValidThread());
    439   printer_->AddErrorMessage("#CRASHED - gpu");
    440   DiscardMainWindow();
    441 }
    442 
    443 void WebKitTestController::TimeoutHandler() {
    444   DCHECK(CalledOnValidThread());
    445   printer_->AddErrorMessage(
    446       "FAIL: Timed out waiting for notifyDone to be called");
    447   DiscardMainWindow();
    448 }
    449 
    450 void WebKitTestController::DiscardMainWindow() {
    451   // If we're running a test, we need to close all windows and exit the message
    452   // loop. Otherwise, we're already outside of the message loop, and we just
    453   // discard the main window.
    454   WebContentsObserver::Observe(NULL);
    455   if (test_phase_ != BETWEEN_TESTS) {
    456     Shell::CloseAllWindows();
    457     base::MessageLoop::current()->PostTask(FROM_HERE,
    458                                            base::MessageLoop::QuitClosure());
    459     test_phase_ = CLEAN_UP;
    460   } else if (main_window_) {
    461     main_window_->Close();
    462   }
    463   main_window_ = NULL;
    464   current_pid_ = base::kNullProcessId;
    465 }
    466 
    467 void WebKitTestController::SendTestConfiguration() {
    468   RenderViewHost* render_view_host =
    469       main_window_->web_contents()->GetRenderViewHost();
    470   ShellTestConfiguration params;
    471   params.current_working_directory = current_working_directory_;
    472   params.temp_path = temp_path_;
    473   params.test_url = test_url_;
    474   params.enable_pixel_dumping = enable_pixel_dumping_;
    475   params.allow_external_pages = CommandLine::ForCurrentProcess()->HasSwitch(
    476       switches::kAllowExternalPages);
    477   params.expected_pixel_hash = expected_pixel_hash_;
    478   params.initial_size = initial_size_;
    479   render_view_host->Send(new ShellViewMsg_SetTestConfiguration(
    480       render_view_host->GetRoutingID(), params));
    481 }
    482 
    483 void WebKitTestController::OnTestFinished() {
    484   test_phase_ = CLEAN_UP;
    485   if (!printer_->output_finished())
    486     printer_->PrintImageFooter();
    487   RenderViewHost* render_view_host =
    488       main_window_->web_contents()->GetRenderViewHost();
    489   base::MessageLoop::current()->PostTask(
    490       FROM_HERE,
    491       base::Bind(base::IgnoreResult(&WebKitTestController::Send),
    492                  base::Unretained(this),
    493                  new ShellViewMsg_Reset(render_view_host->GetRoutingID())));
    494 }
    495 
    496 void WebKitTestController::OnImageDump(
    497     const std::string& actual_pixel_hash,
    498     const SkBitmap& image) {
    499   SkAutoLockPixels image_lock(image);
    500 
    501   printer_->PrintImageHeader(actual_pixel_hash, expected_pixel_hash_);
    502 
    503   // Only encode and dump the png if the hashes don't match. Encoding the
    504   // image is really expensive.
    505   if (actual_pixel_hash != expected_pixel_hash_) {
    506     std::vector<unsigned char> png;
    507 
    508     // Only the expected PNGs for Mac have a valid alpha channel.
    509 #if defined(OS_MACOSX)
    510     bool discard_transparency = false;
    511 #else
    512     bool discard_transparency = true;
    513 #endif
    514 
    515     std::vector<gfx::PNGCodec::Comment> comments;
    516     comments.push_back(gfx::PNGCodec::Comment("checksum", actual_pixel_hash));
    517     bool success = gfx::PNGCodec::Encode(
    518         static_cast<const unsigned char*>(image.getPixels()),
    519         gfx::PNGCodec::FORMAT_BGRA,
    520         gfx::Size(image.width(), image.height()),
    521         static_cast<int>(image.rowBytes()),
    522         discard_transparency,
    523         comments,
    524         &png);
    525     if (success)
    526       printer_->PrintImageBlock(png);
    527   }
    528   printer_->PrintImageFooter();
    529 }
    530 
    531 void WebKitTestController::OnAudioDump(const std::vector<unsigned char>& dump) {
    532   printer_->PrintAudioHeader();
    533   printer_->PrintAudioBlock(dump);
    534   printer_->PrintAudioFooter();
    535 }
    536 
    537 void WebKitTestController::OnTextDump(const std::string& dump) {
    538   printer_->PrintTextHeader();
    539   printer_->PrintTextBlock(dump);
    540   printer_->PrintTextFooter();
    541 }
    542 
    543 void WebKitTestController::OnPrintMessage(const std::string& message) {
    544   printer_->AddMessageRaw(message);
    545 }
    546 
    547 void WebKitTestController::OnOverridePreferences(const WebPreferences& prefs) {
    548   should_override_prefs_ = true;
    549   prefs_ = prefs;
    550 }
    551 
    552 void WebKitTestController::OnShowDevTools() {
    553   main_window_->ShowDevTools();
    554 }
    555 
    556 void WebKitTestController::OnCloseDevTools() {
    557   main_window_->CloseDevTools();
    558 }
    559 
    560 void WebKitTestController::OnGoToOffset(int offset) {
    561   main_window_->GoBackOrForward(offset);
    562 }
    563 
    564 void WebKitTestController::OnReload() {
    565   main_window_->Reload();
    566 }
    567 
    568 void WebKitTestController::OnLoadURLForFrame(const GURL& url,
    569                                              const std::string& frame_name) {
    570   main_window_->LoadURLForFrame(url, frame_name);
    571 }
    572 
    573 void WebKitTestController::OnCaptureSessionHistory() {
    574   std::vector<int> routing_ids;
    575   std::vector<std::vector<PageState> > session_histories;
    576   std::vector<unsigned> current_entry_indexes;
    577 
    578   RenderViewHost* render_view_host =
    579       main_window_->web_contents()->GetRenderViewHost();
    580 
    581   for (std::vector<Shell*>::iterator window = Shell::windows().begin();
    582        window != Shell::windows().end();
    583        ++window) {
    584     WebContents* web_contents = (*window)->web_contents();
    585     // Only capture the history from windows in the same process as the main
    586     // window. During layout tests, we only use two processes when an
    587     // devtools window is open. This should not happen during history navigation
    588     // tests.
    589     if (render_view_host->GetProcess() !=
    590         web_contents->GetRenderViewHost()->GetProcess()) {
    591       NOTREACHED();
    592       continue;
    593     }
    594     routing_ids.push_back(web_contents->GetRenderViewHost()->GetRoutingID());
    595     current_entry_indexes.push_back(
    596         web_contents->GetController().GetCurrentEntryIndex());
    597     std::vector<PageState> history;
    598     for (int entry = 0; entry < web_contents->GetController().GetEntryCount();
    599          ++entry) {
    600       PageState state = web_contents->GetController().GetEntryAtIndex(entry)->
    601           GetPageState();
    602       if (!state.IsValid()) {
    603         state = PageState::CreateFromURL(
    604             web_contents->GetController().GetEntryAtIndex(entry)->GetURL());
    605       }
    606       history.push_back(state);
    607     }
    608     session_histories.push_back(history);
    609   }
    610 
    611   Send(new ShellViewMsg_SessionHistory(render_view_host->GetRoutingID(),
    612                                        routing_ids,
    613                                        session_histories,
    614                                        current_entry_indexes));
    615 }
    616 
    617 void WebKitTestController::OnCloseRemainingWindows() {
    618   DevToolsManager::GetInstance()->CloseAllClientHosts();
    619   std::vector<Shell*> open_windows(Shell::windows());
    620   for (size_t i = 0; i < open_windows.size(); ++i) {
    621     if (open_windows[i] != main_window_)
    622       open_windows[i]->Close();
    623   }
    624   base::MessageLoop::current()->RunUntilIdle();
    625 }
    626 
    627 void WebKitTestController::OnResetDone() {
    628   base::MessageLoop::current()->PostTask(FROM_HERE,
    629                                          base::MessageLoop::QuitClosure());
    630 }
    631 
    632 }  // namespace content
    633