Refactor management of overview window copy lifetime into a separate class.
[chromium-blink-merge.git] / content / shell / browser / webkit_test_controller.cc
blob434a2f9c1350322637c7297b86bb000660e30b89
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.
5 #include "content/shell/browser/webkit_test_controller.h"
7 #include <iostream>
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/browser/shell.h"
28 #include "content/shell/browser/shell_browser_context.h"
29 #include "content/shell/browser/shell_content_browser_client.h"
30 #include "content/shell/common/shell_messages.h"
31 #include "content/shell/common/shell_switches.h"
32 #include "content/shell/common/webkit_test_helpers.h"
33 #include "ui/gfx/codec/png_codec.h"
35 namespace content {
37 const int kTestSVGWindowWidthDip = 480;
38 const int kTestSVGWindowHeightDip = 360;
40 // WebKitTestResultPrinter ----------------------------------------------------
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) {
51 WebKitTestResultPrinter::~WebKitTestResultPrinter() {
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;
62 void WebKitTestResultPrinter::PrintTextBlock(const std::string& block) {
63 if (state_ != IN_TEXT_BLOCK)
64 return;
65 *output_ << block;
68 void WebKitTestResultPrinter::PrintTextFooter() {
69 if (state_ != IN_TEXT_BLOCK)
70 return;
71 if (!capture_text_only_) {
72 *output_ << "#EOF\n";
73 output_->flush();
75 state_ = IN_IMAGE_BLOCK;
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";
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;
98 *output_ << "Content-Length: " << png_image.size() << "\n";
99 output_->write(
100 reinterpret_cast<const char*>(&png_image[0]), png_image.size());
103 void WebKitTestResultPrinter::PrintImageFooter() {
104 if (state_ != IN_IMAGE_BLOCK)
105 return;
106 if (!capture_text_only_) {
107 *output_ << "#EOF\n";
108 output_->flush();
110 state_ = AFTER_TEST;
113 void WebKitTestResultPrinter::PrintAudioHeader() {
114 DCHECK_EQ(state_, DURING_TEST);
115 if (!capture_text_only_)
116 *output_ << "Content-Type: audio/wav\n";
117 state_ = IN_AUDIO_BLOCK;
120 void WebKitTestResultPrinter::PrintAudioBlock(
121 const std::vector<unsigned char>& audio_data) {
122 if (state_ != IN_AUDIO_BLOCK || capture_text_only_)
123 return;
124 if (encode_binary_data_) {
125 PrintEncodedBinaryData(audio_data);
126 return;
129 *output_ << "Content-Length: " << audio_data.size() << "\n";
130 output_->write(
131 reinterpret_cast<const char*>(&audio_data[0]), audio_data.size());
134 void WebKitTestResultPrinter::PrintAudioFooter() {
135 if (state_ != IN_AUDIO_BLOCK)
136 return;
137 if (!capture_text_only_) {
138 *output_ << "#EOF\n";
139 output_->flush();
141 state_ = IN_IMAGE_BLOCK;
144 void WebKitTestResultPrinter::AddMessage(const std::string& message) {
145 AddMessageRaw(message + "\n");
148 void WebKitTestResultPrinter::AddMessageRaw(const std::string& message) {
149 if (state_ != DURING_TEST)
150 return;
151 *output_ << message;
154 void WebKitTestResultPrinter::AddErrorMessage(const std::string& message) {
155 if (!capture_text_only_)
156 *error_ << message << "\n";
157 if (state_ != DURING_TEST)
158 return;
159 PrintTextHeader();
160 *output_ << message << "\n";
161 PrintTextFooter();
162 PrintImageFooter();
165 void WebKitTestResultPrinter::PrintEncodedBinaryData(
166 const std::vector<unsigned char>& data) {
167 *output_ << "Content-Transfer-Encoding: base64\n";
169 std::string data_base64;
170 const bool success = base::Base64Encode(
171 base::StringPiece(reinterpret_cast<const char*>(&data[0]), data.size()),
172 &data_base64);
173 DCHECK(success);
175 *output_ << "Content-Length: " << data_base64.length() << "\n";
176 output_->write(data_base64.c_str(), data_base64.length());
179 void WebKitTestResultPrinter::CloseStderr() {
180 if (state_ != AFTER_TEST)
181 return;
182 if (!capture_text_only_) {
183 *error_ << "#EOF\n";
184 error_->flush();
189 // WebKitTestController -------------------------------------------------------
191 WebKitTestController* WebKitTestController::instance_ = NULL;
193 // static
194 WebKitTestController* WebKitTestController::Get() {
195 DCHECK(instance_);
196 return instance_;
199 WebKitTestController::WebKitTestController()
200 : main_window_(NULL),
201 test_phase_(BETWEEN_TESTS) {
202 CHECK(!instance_);
203 instance_ = this;
204 printer_.reset(new WebKitTestResultPrinter(&std::cout, &std::cerr));
205 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEncodeBinary))
206 printer_->set_encode_binary_data(true);
207 registrar_.Add(this,
208 NOTIFICATION_RENDERER_PROCESS_CREATED,
209 NotificationService::AllSources());
210 GpuDataManager::GetInstance()->AddObserver(this);
211 ResetAfterLayoutTest();
214 WebKitTestController::~WebKitTestController() {
215 DCHECK(CalledOnValidThread());
216 CHECK(instance_ == this);
217 CHECK(test_phase_ == BETWEEN_TESTS);
218 GpuDataManager::GetInstance()->RemoveObserver(this);
219 DiscardMainWindow();
220 instance_ = NULL;
223 bool WebKitTestController::PrepareForLayoutTest(
224 const GURL& test_url,
225 const base::FilePath& current_working_directory,
226 bool enable_pixel_dumping,
227 const std::string& expected_pixel_hash) {
228 DCHECK(CalledOnValidThread());
229 test_phase_ = DURING_TEST;
230 current_working_directory_ = current_working_directory;
231 enable_pixel_dumping_ = enable_pixel_dumping;
232 expected_pixel_hash_ = expected_pixel_hash;
233 test_url_ = test_url;
234 printer_->reset();
235 ShellBrowserContext* browser_context =
236 ShellContentBrowserClient::Get()->browser_context();
237 if (test_url.spec().find("compositing/") != std::string::npos)
238 is_compositing_test_ = true;
239 initial_size_ = gfx::Size(
240 Shell::kDefaultTestWindowWidthDip, Shell::kDefaultTestWindowHeightDip);
241 // The W3C SVG layout tests use a different size than the other layout tests.
242 if (test_url.spec().find("W3C-SVG-1.1") != std::string::npos)
243 initial_size_ = gfx::Size(kTestSVGWindowWidthDip, kTestSVGWindowHeightDip);
244 if (!main_window_) {
245 main_window_ = content::Shell::CreateNewWindow(
246 browser_context,
247 GURL(),
248 NULL,
249 MSG_ROUTING_NONE,
250 initial_size_);
251 WebContentsObserver::Observe(main_window_->web_contents());
252 send_configuration_to_next_host_ = true;
253 current_pid_ = base::kNullProcessId;
254 main_window_->LoadURL(test_url);
255 } else {
256 #if (defined(OS_WIN) && !defined(USE_AURA)) || \
257 defined(TOOLKIT_GTK) || defined(OS_MACOSX)
258 // Shell::SizeTo is not implemented on all platforms.
259 main_window_->SizeTo(initial_size_.width(), initial_size_.height());
260 #endif
261 main_window_->web_contents()->GetRenderViewHost()->GetView()
262 ->SetSize(initial_size_);
263 main_window_->web_contents()->GetRenderViewHost()->WasResized();
264 RenderViewHost* render_view_host =
265 main_window_->web_contents()->GetRenderViewHost();
266 WebPreferences prefs = render_view_host->GetWebkitPreferences();
267 OverrideWebkitPrefs(&prefs);
268 render_view_host->UpdateWebkitPreferences(prefs);
269 SendTestConfiguration();
271 NavigationController::LoadURLParams params(test_url);
272 params.transition_type = PageTransitionFromInt(
273 PAGE_TRANSITION_TYPED | PAGE_TRANSITION_FROM_ADDRESS_BAR);
274 params.should_clear_history_list = true;
275 main_window_->web_contents()->GetController().LoadURLWithParams(params);
276 main_window_->web_contents()->GetView()->Focus();
278 main_window_->web_contents()->GetRenderViewHost()->SetActive(true);
279 main_window_->web_contents()->GetRenderViewHost()->Focus();
280 return true;
283 bool WebKitTestController::ResetAfterLayoutTest() {
284 DCHECK(CalledOnValidThread());
285 printer_->PrintTextFooter();
286 printer_->PrintImageFooter();
287 printer_->CloseStderr();
288 send_configuration_to_next_host_ = false;
289 test_phase_ = BETWEEN_TESTS;
290 is_compositing_test_ = false;
291 enable_pixel_dumping_ = false;
292 expected_pixel_hash_.clear();
293 test_url_ = GURL();
294 prefs_ = WebPreferences();
295 should_override_prefs_ = false;
297 #if defined(OS_ANDROID)
298 // Re-using the shell's main window on Android causes issues with networking
299 // requests never succeeding. See http://crbug.com/277652.
300 DiscardMainWindow();
301 #endif
302 return true;
305 void WebKitTestController::SetTempPath(const base::FilePath& temp_path) {
306 temp_path_ = temp_path;
309 void WebKitTestController::RendererUnresponsive() {
310 DCHECK(CalledOnValidThread());
311 LOG(WARNING) << "renderer unresponsive";
314 void WebKitTestController::WorkerCrashed() {
315 DCHECK(CalledOnValidThread());
316 printer_->AddErrorMessage("#CRASHED - worker");
317 DiscardMainWindow();
320 void WebKitTestController::OverrideWebkitPrefs(WebPreferences* prefs) {
321 if (should_override_prefs_) {
322 *prefs = prefs_;
323 } else {
324 ApplyLayoutTestDefaultPreferences(prefs);
325 if (is_compositing_test_) {
326 CommandLine& command_line = *CommandLine::ForCurrentProcess();
327 if (!command_line.HasSwitch(switches::kEnableSoftwareCompositing))
328 prefs->accelerated_2d_canvas_enabled = true;
329 prefs->accelerated_compositing_for_video_enabled = true;
330 prefs->mock_scrollbars_enabled = true;
335 void WebKitTestController::OpenURL(const GURL& url) {
336 if (test_phase_ != DURING_TEST)
337 return;
339 Shell::CreateNewWindow(main_window_->web_contents()->GetBrowserContext(),
340 url,
341 main_window_->web_contents()->GetSiteInstance(),
342 MSG_ROUTING_NONE,
343 gfx::Size());
346 void WebKitTestController::TestFinishedInSecondaryWindow() {
347 RenderViewHost* render_view_host =
348 main_window_->web_contents()->GetRenderViewHost();
349 render_view_host->Send(
350 new ShellViewMsg_NotifyDone(render_view_host->GetRoutingID()));
353 bool WebKitTestController::IsMainWindow(WebContents* web_contents) const {
354 return main_window_ && web_contents == main_window_->web_contents();
357 bool WebKitTestController::OnMessageReceived(const IPC::Message& message) {
358 DCHECK(CalledOnValidThread());
359 bool handled = true;
360 IPC_BEGIN_MESSAGE_MAP(WebKitTestController, message)
361 IPC_MESSAGE_HANDLER(ShellViewHostMsg_PrintMessage, OnPrintMessage)
362 IPC_MESSAGE_HANDLER(ShellViewHostMsg_TextDump, OnTextDump)
363 IPC_MESSAGE_HANDLER(ShellViewHostMsg_ImageDump, OnImageDump)
364 IPC_MESSAGE_HANDLER(ShellViewHostMsg_AudioDump, OnAudioDump)
365 IPC_MESSAGE_HANDLER(ShellViewHostMsg_OverridePreferences,
366 OnOverridePreferences)
367 IPC_MESSAGE_HANDLER(ShellViewHostMsg_TestFinished, OnTestFinished)
368 IPC_MESSAGE_HANDLER(ShellViewHostMsg_ShowDevTools, OnShowDevTools)
369 IPC_MESSAGE_HANDLER(ShellViewHostMsg_CloseDevTools, OnCloseDevTools)
370 IPC_MESSAGE_HANDLER(ShellViewHostMsg_GoToOffset, OnGoToOffset)
371 IPC_MESSAGE_HANDLER(ShellViewHostMsg_Reload, OnReload)
372 IPC_MESSAGE_HANDLER(ShellViewHostMsg_LoadURLForFrame, OnLoadURLForFrame)
373 IPC_MESSAGE_HANDLER(ShellViewHostMsg_CaptureSessionHistory,
374 OnCaptureSessionHistory)
375 IPC_MESSAGE_HANDLER(ShellViewHostMsg_CloseRemainingWindows,
376 OnCloseRemainingWindows)
377 IPC_MESSAGE_HANDLER(ShellViewHostMsg_ResetDone, OnResetDone)
378 IPC_MESSAGE_UNHANDLED(handled = false)
379 IPC_END_MESSAGE_MAP()
381 return handled;
384 void WebKitTestController::PluginCrashed(const base::FilePath& plugin_path,
385 base::ProcessId plugin_pid) {
386 DCHECK(CalledOnValidThread());
387 printer_->AddErrorMessage(
388 base::StringPrintf("#CRASHED - plugin (pid %d)", plugin_pid));
389 base::MessageLoop::current()->PostTask(
390 FROM_HERE,
391 base::Bind(base::IgnoreResult(&WebKitTestController::DiscardMainWindow),
392 base::Unretained(this)));
395 void WebKitTestController::RenderViewCreated(RenderViewHost* render_view_host) {
396 DCHECK(CalledOnValidThread());
397 // Might be kNullProcessHandle, in which case we will receive a notification
398 // later when the RenderProcessHost was created.
399 if (render_view_host->GetProcess()->GetHandle() != base::kNullProcessHandle)
400 current_pid_ = base::GetProcId(render_view_host->GetProcess()->GetHandle());
401 if (!send_configuration_to_next_host_)
402 return;
403 send_configuration_to_next_host_ = false;
404 SendTestConfiguration();
407 void WebKitTestController::RenderProcessGone(base::TerminationStatus status) {
408 DCHECK(CalledOnValidThread());
409 if (current_pid_ != base::kNullProcessId) {
410 printer_->AddErrorMessage(std::string("#CRASHED - renderer (pid ") +
411 base::IntToString(current_pid_) + ")");
412 } else {
413 printer_->AddErrorMessage("#CRASHED - renderer");
415 DiscardMainWindow();
418 void WebKitTestController::WebContentsDestroyed(WebContents* web_contents) {
419 DCHECK(CalledOnValidThread());
420 printer_->AddErrorMessage("FAIL: main window was destroyed");
421 DiscardMainWindow();
424 void WebKitTestController::Observe(int type,
425 const NotificationSource& source,
426 const NotificationDetails& details) {
427 DCHECK(CalledOnValidThread());
428 switch (type) {
429 case NOTIFICATION_RENDERER_PROCESS_CREATED: {
430 if (!main_window_)
431 return;
432 RenderViewHost* render_view_host =
433 main_window_->web_contents()->GetRenderViewHost();
434 if (!render_view_host)
435 return;
436 RenderProcessHost* render_process_host =
437 Source<RenderProcessHost>(source).ptr();
438 if (render_process_host != render_view_host->GetProcess())
439 return;
440 current_pid_ = base::GetProcId(render_process_host->GetHandle());
441 break;
443 default:
444 NOTREACHED();
448 void WebKitTestController::OnGpuProcessCrashed(
449 base::TerminationStatus exit_code) {
450 DCHECK(CalledOnValidThread());
451 printer_->AddErrorMessage("#CRASHED - gpu");
452 DiscardMainWindow();
455 void WebKitTestController::TimeoutHandler() {
456 DCHECK(CalledOnValidThread());
457 printer_->AddErrorMessage(
458 "FAIL: Timed out waiting for notifyDone to be called");
459 DiscardMainWindow();
462 void WebKitTestController::DiscardMainWindow() {
463 // If we're running a test, we need to close all windows and exit the message
464 // loop. Otherwise, we're already outside of the message loop, and we just
465 // discard the main window.
466 WebContentsObserver::Observe(NULL);
467 if (test_phase_ != BETWEEN_TESTS) {
468 Shell::CloseAllWindows();
469 base::MessageLoop::current()->PostTask(FROM_HERE,
470 base::MessageLoop::QuitClosure());
471 test_phase_ = CLEAN_UP;
472 } else if (main_window_) {
473 main_window_->Close();
475 main_window_ = NULL;
476 current_pid_ = base::kNullProcessId;
479 void WebKitTestController::SendTestConfiguration() {
480 RenderViewHost* render_view_host =
481 main_window_->web_contents()->GetRenderViewHost();
482 ShellTestConfiguration params;
483 params.current_working_directory = current_working_directory_;
484 params.temp_path = temp_path_;
485 params.test_url = test_url_;
486 params.enable_pixel_dumping = enable_pixel_dumping_;
487 params.allow_external_pages = CommandLine::ForCurrentProcess()->HasSwitch(
488 switches::kAllowExternalPages);
489 params.expected_pixel_hash = expected_pixel_hash_;
490 params.initial_size = initial_size_;
491 render_view_host->Send(new ShellViewMsg_SetTestConfiguration(
492 render_view_host->GetRoutingID(), params));
495 void WebKitTestController::OnTestFinished() {
496 test_phase_ = CLEAN_UP;
497 if (!printer_->output_finished())
498 printer_->PrintImageFooter();
499 RenderViewHost* render_view_host =
500 main_window_->web_contents()->GetRenderViewHost();
501 base::MessageLoop::current()->PostTask(
502 FROM_HERE,
503 base::Bind(base::IgnoreResult(&WebKitTestController::Send),
504 base::Unretained(this),
505 new ShellViewMsg_Reset(render_view_host->GetRoutingID())));
508 void WebKitTestController::OnImageDump(
509 const std::string& actual_pixel_hash,
510 const SkBitmap& image) {
511 SkAutoLockPixels image_lock(image);
513 printer_->PrintImageHeader(actual_pixel_hash, expected_pixel_hash_);
515 // Only encode and dump the png if the hashes don't match. Encoding the
516 // image is really expensive.
517 if (actual_pixel_hash != expected_pixel_hash_) {
518 std::vector<unsigned char> png;
520 // Only the expected PNGs for Mac have a valid alpha channel.
521 #if defined(OS_MACOSX)
522 bool discard_transparency = false;
523 #else
524 bool discard_transparency = true;
525 #endif
526 if (CommandLine::ForCurrentProcess()->HasSwitch(
527 switches::kEnableOverlayFullscreenVideo))
528 discard_transparency = false;
530 std::vector<gfx::PNGCodec::Comment> comments;
531 comments.push_back(gfx::PNGCodec::Comment("checksum", actual_pixel_hash));
532 bool success = gfx::PNGCodec::Encode(
533 static_cast<const unsigned char*>(image.getPixels()),
534 gfx::PNGCodec::FORMAT_BGRA,
535 gfx::Size(image.width(), image.height()),
536 static_cast<int>(image.rowBytes()),
537 discard_transparency,
538 comments,
539 &png);
540 if (success)
541 printer_->PrintImageBlock(png);
543 printer_->PrintImageFooter();
546 void WebKitTestController::OnAudioDump(const std::vector<unsigned char>& dump) {
547 printer_->PrintAudioHeader();
548 printer_->PrintAudioBlock(dump);
549 printer_->PrintAudioFooter();
552 void WebKitTestController::OnTextDump(const std::string& dump) {
553 printer_->PrintTextHeader();
554 printer_->PrintTextBlock(dump);
555 printer_->PrintTextFooter();
558 void WebKitTestController::OnPrintMessage(const std::string& message) {
559 printer_->AddMessageRaw(message);
562 void WebKitTestController::OnOverridePreferences(const WebPreferences& prefs) {
563 should_override_prefs_ = true;
564 prefs_ = prefs;
567 void WebKitTestController::OnShowDevTools() {
568 main_window_->ShowDevTools();
571 void WebKitTestController::OnCloseDevTools() {
572 main_window_->CloseDevTools();
575 void WebKitTestController::OnGoToOffset(int offset) {
576 main_window_->GoBackOrForward(offset);
579 void WebKitTestController::OnReload() {
580 main_window_->Reload();
583 void WebKitTestController::OnLoadURLForFrame(const GURL& url,
584 const std::string& frame_name) {
585 main_window_->LoadURLForFrame(url, frame_name);
588 void WebKitTestController::OnCaptureSessionHistory() {
589 std::vector<int> routing_ids;
590 std::vector<std::vector<PageState> > session_histories;
591 std::vector<unsigned> current_entry_indexes;
593 RenderViewHost* render_view_host =
594 main_window_->web_contents()->GetRenderViewHost();
596 for (std::vector<Shell*>::iterator window = Shell::windows().begin();
597 window != Shell::windows().end();
598 ++window) {
599 WebContents* web_contents = (*window)->web_contents();
600 // Only capture the history from windows in the same process as the main
601 // window. During layout tests, we only use two processes when an
602 // devtools window is open. This should not happen during history navigation
603 // tests.
604 if (render_view_host->GetProcess() !=
605 web_contents->GetRenderViewHost()->GetProcess()) {
606 NOTREACHED();
607 continue;
609 routing_ids.push_back(web_contents->GetRenderViewHost()->GetRoutingID());
610 current_entry_indexes.push_back(
611 web_contents->GetController().GetCurrentEntryIndex());
612 std::vector<PageState> history;
613 for (int entry = 0; entry < web_contents->GetController().GetEntryCount();
614 ++entry) {
615 PageState state = web_contents->GetController().GetEntryAtIndex(entry)->
616 GetPageState();
617 if (!state.IsValid()) {
618 state = PageState::CreateFromURL(
619 web_contents->GetController().GetEntryAtIndex(entry)->GetURL());
621 history.push_back(state);
623 session_histories.push_back(history);
626 Send(new ShellViewMsg_SessionHistory(render_view_host->GetRoutingID(),
627 routing_ids,
628 session_histories,
629 current_entry_indexes));
632 void WebKitTestController::OnCloseRemainingWindows() {
633 DevToolsManager::GetInstance()->CloseAllClientHosts();
634 std::vector<Shell*> open_windows(Shell::windows());
635 for (size_t i = 0; i < open_windows.size(); ++i) {
636 if (open_windows[i] != main_window_)
637 open_windows[i]->Close();
639 base::MessageLoop::current()->RunUntilIdle();
642 void WebKitTestController::OnResetDone() {
643 base::MessageLoop::current()->PostTask(FROM_HERE,
644 base::MessageLoop::QuitClosure());
647 } // namespace content