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