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 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 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 227 WebKitTestController::~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 236 bool 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 295 bool 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 317 void WebKitTestController::SetTempPath(const base::FilePath& temp_path) { 318 temp_path_ = temp_path; 319 } 320 321 void WebKitTestController::RendererUnresponsive() { 322 DCHECK(CalledOnValidThread()); 323 LOG(WARNING) << "renderer unresponsive"; 324 } 325 326 void WebKitTestController::WorkerCrashed() { 327 DCHECK(CalledOnValidThread()); 328 printer_->AddErrorMessage("#CRASHED - worker"); 329 DiscardMainWindow(); 330 } 331 332 void 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 346 void 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 357 void 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 364 bool WebKitTestController::IsMainWindow(WebContents* web_contents) const { 365 return main_window_ && web_contents == main_window_->web_contents(); 366 } 367 368 bool 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 398 void 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 409 void 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 421 void 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 432 void WebKitTestController::DevToolsProcessCrashed() { 433 DCHECK(CalledOnValidThread()); 434 printer_->AddErrorMessage("#CRASHED - devtools"); 435 DiscardMainWindow(); 436 } 437 438 void WebKitTestController::WebContentsDestroyed() { 439 DCHECK(CalledOnValidThread()); 440 printer_->AddErrorMessage("FAIL: main window was destroyed"); 441 DiscardMainWindow(); 442 } 443 444 void 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 468 void WebKitTestController::OnGpuProcessCrashed( 469 base::TerminationStatus exit_code) { 470 DCHECK(CalledOnValidThread()); 471 printer_->AddErrorMessage("#CRASHED - gpu"); 472 DiscardMainWindow(); 473 } 474 475 void 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 492 void 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 508 void 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 521 void 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 559 void WebKitTestController::OnAudioDump(const std::vector<unsigned char>& dump) { 560 printer_->PrintAudioHeader(); 561 printer_->PrintAudioBlock(dump); 562 printer_->PrintAudioFooter(); 563 } 564 565 void WebKitTestController::OnTextDump(const std::string& dump) { 566 printer_->PrintTextHeader(); 567 printer_->PrintTextBlock(dump); 568 printer_->PrintTextFooter(); 569 } 570 571 void WebKitTestController::OnPrintMessage(const std::string& message) { 572 printer_->AddMessageRaw(message); 573 } 574 575 void WebKitTestController::OnOverridePreferences(const WebPreferences& prefs) { 576 should_override_prefs_ = true; 577 prefs_ = prefs; 578 } 579 580 void 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 589 void WebKitTestController::OnShowDevTools(const std::string& settings, 590 const std::string& frontend_url) { 591 main_window_->ShowDevToolsForTest(settings, frontend_url); 592 } 593 594 void WebKitTestController::OnCloseDevTools() { 595 main_window_->CloseDevTools(); 596 } 597 598 void WebKitTestController::OnGoToOffset(int offset) { 599 main_window_->GoBackOrForward(offset); 600 } 601 602 void WebKitTestController::OnReload() { 603 main_window_->Reload(); 604 } 605 606 void WebKitTestController::OnLoadURLForFrame(const GURL& url, 607 const std::string& frame_name) { 608 main_window_->LoadURLForFrame(url, frame_name); 609 } 610 611 void 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 655 void 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 665 void 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 680 void 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