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