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/web_test/browser/web_test_control_host.h"
6 
7 #include <stddef.h>
8 #include <string.h>
9 
10 #include <algorithm>
11 #include <iostream>
12 #include <memory>
13 #include <queue>
14 #include <set>
15 #include <utility>
16 #include <vector>
17 
18 #include "base/barrier_closure.h"
19 #include "base/base64.h"
20 #include "base/bind.h"
21 #include "base/callback.h"
22 #include "base/command_line.h"
23 #include "base/location.h"
24 #include "base/logging.h"
25 #include "base/path_service.h"
26 #include "base/run_loop.h"
27 #include "base/single_thread_task_runner.h"
28 #include "base/stl_util.h"
29 #include "base/strings/nullable_string16.h"
30 #include "base/strings/string16.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/stringprintf.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "base/task/post_task.h"
36 #include "base/threading/thread_task_runner_handle.h"
37 #include "build/build_config.h"
38 #include "cc/paint/skia_paint_canvas.h"
39 #include "content/browser/renderer_host/navigation_request.h"
40 #include "content/public/browser/browser_task_traits.h"
41 #include "content/public/browser/child_process_security_policy.h"
42 #include "content/public/browser/child_process_termination_info.h"
43 #include "content/public/browser/client_hints_controller_delegate.h"
44 #include "content/public/browser/content_index_context.h"
45 #include "content/public/browser/devtools_agent_host.h"
46 #include "content/public/browser/gpu_data_manager.h"
47 #include "content/public/browser/navigation_controller.h"
48 #include "content/public/browser/navigation_entry.h"
49 #include "content/public/browser/network_service_instance.h"
50 #include "content/public/browser/notification_service.h"
51 #include "content/public/browser/notification_types.h"
52 #include "content/public/browser/render_frame_host.h"
53 #include "content/public/browser/render_view_host.h"
54 #include "content/public/browser/render_widget_host.h"
55 #include "content/public/browser/render_widget_host_view.h"
56 #include "content/public/browser/service_worker_context.h"
57 #include "content/public/browser/storage_partition.h"
58 #include "content/public/browser/web_contents.h"
59 #include "content/public/common/bindings_policy.h"
60 #include "content/public/common/content_switches.h"
61 #include "content/public/common/url_constants.h"
62 #include "content/public/test/blink_test_browser_support.h"
63 #include "content/shell/browser/shell.h"
64 #include "content/shell/browser/shell_browser_context.h"
65 #include "content/shell/browser/shell_content_browser_client.h"
66 #include "content/shell/browser/shell_content_index_provider.h"
67 #include "content/shell/browser/shell_devtools_frontend.h"
68 #include "content/test/mock_platform_notification_service.h"
69 #include "content/test/storage_partition_test_helpers.h"
70 #include "content/web_test/browser/devtools_protocol_test_bindings.h"
71 #include "content/web_test/browser/fake_bluetooth_chooser.h"
72 #include "content/web_test/browser/test_info_extractor.h"
73 #include "content/web_test/browser/web_test_bluetooth_chooser_factory.h"
74 #include "content/web_test/browser/web_test_browser_context.h"
75 #include "content/web_test/browser/web_test_content_browser_client.h"
76 #include "content/web_test/browser/web_test_devtools_bindings.h"
77 #include "content/web_test/browser/web_test_first_device_bluetooth_chooser.h"
78 #include "content/web_test/browser/web_test_permission_manager.h"
79 #include "content/web_test/common/web_test_constants.h"
80 #include "content/web_test/common/web_test_string_util.h"
81 #include "content/web_test/common/web_test_switches.h"
82 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
83 #include "services/network/public/cpp/features.h"
84 #include "services/network/public/mojom/network_context.mojom.h"
85 #include "services/network/public/mojom/network_service.mojom.h"
86 #include "storage/browser/database/database_tracker.h"
87 #include "storage/browser/file_system/isolated_context.h"
88 #include "storage/browser/quota/quota_manager.h"
89 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
90 #include "third_party/blink/public/common/page_state/page_state.h"
91 #include "third_party/blink/public/common/page_state/page_state_serialization.h"
92 #include "third_party/blink/public/common/switches.h"
93 #include "third_party/blink/public/common/unique_name/unique_name_helper.h"
94 #include "third_party/blink/public/platform/web_rect.h"
95 #include "ui/base/ui_base_switches.h"
96 #include "ui/gfx/codec/png_codec.h"
97 #include "ui/shell_dialogs/select_file_dialog.h"
98 #include "ui/shell_dialogs/select_file_dialog_factory.h"
99 #include "ui/shell_dialogs/select_file_policy.h"
100 
101 #if defined(OS_MAC)
102 #include "base/mac/foundation_util.h"
103 #endif
104 
105 namespace content {
106 
107 namespace {
108 
DumpFrameState(const blink::ExplodedFrameState & frame_state,size_t indent,bool is_current_index)109 std::string DumpFrameState(const blink::ExplodedFrameState& frame_state,
110                            size_t indent,
111                            bool is_current_index) {
112   std::string result;
113   if (is_current_index) {
114     constexpr const char kCurrentMarker[] = "curr->";
115     result.append(kCurrentMarker);
116     result.append(indent - strlen(kCurrentMarker), ' ');
117   } else {
118     result.append(indent, ' ');
119   }
120 
121   std::string url = web_test_string_util::NormalizeWebTestURL(
122       base::UTF16ToUTF8(frame_state.url_string.value_or(base::string16())));
123   result.append(url);
124   DCHECK(frame_state.target);
125   if (!frame_state.target->empty()) {
126     std::string unique_name = base::UTF16ToUTF8(*frame_state.target);
127     result.append(" (in frame \"");
128     result.append(
129         blink::UniqueNameHelper::ExtractStableNameForTesting(unique_name));
130     result.append("\")");
131   }
132   result.append("\n");
133 
134   std::vector<blink::ExplodedFrameState> sorted_children = frame_state.children;
135   std::sort(
136       sorted_children.begin(), sorted_children.end(),
137       [](const blink::ExplodedFrameState& lhs,
138          const blink::ExplodedFrameState& rhs) {
139         // Child nodes should always have a target (aka unique name).
140         DCHECK(lhs.target);
141         DCHECK(rhs.target);
142         std::string lhs_name =
143             blink::UniqueNameHelper::ExtractStableNameForTesting(
144                 base::UTF16ToUTF8(*lhs.target));
145         std::string rhs_name =
146             blink::UniqueNameHelper::ExtractStableNameForTesting(
147                 base::UTF16ToUTF8(*rhs.target));
148         if (!base::EqualsCaseInsensitiveASCII(lhs_name, rhs_name))
149           return base::CompareCaseInsensitiveASCII(lhs_name, rhs_name) < 0;
150 
151         return lhs.item_sequence_number < rhs.item_sequence_number;
152       });
153   for (const auto& child : sorted_children)
154     result += DumpFrameState(child, indent + 4, false);
155 
156   return result;
157 }
158 
DumpNavigationEntry(NavigationEntry * navigation_entry,bool is_current_index)159 std::string DumpNavigationEntry(NavigationEntry* navigation_entry,
160                                 bool is_current_index) {
161   // This is silly, but it's currently the best way to extract the information.
162   blink::PageState page_state = navigation_entry->GetPageState();
163   blink::ExplodedPageState exploded_page_state;
164   CHECK(
165       blink::DecodePageState(page_state.ToEncodedData(), &exploded_page_state));
166   return DumpFrameState(exploded_page_state.top, 8, is_current_index);
167 }
168 
DumpHistoryForWebContents(WebContents * web_contents)169 std::string DumpHistoryForWebContents(WebContents* web_contents) {
170   std::string result;
171   const int current_index =
172       web_contents->GetController().GetCurrentEntryIndex();
173   for (int i = 0; i < web_contents->GetController().GetEntryCount(); ++i) {
174     result += DumpNavigationEntry(
175         web_contents->GetController().GetEntryAtIndex(i), i == current_index);
176   }
177   return result;
178 }
179 
DumpTitleWasSet(WebContents * web_contents)180 std::vector<std::string> DumpTitleWasSet(WebContents* web_contents) {
181   base::Optional<bool> load = WebTestControlHost::Get()
182                                   ->accumulated_web_test_runtime_flags_changes()
183                                   .FindBoolPath("dump_frame_load_callbacks");
184 
185   base::Optional<bool> title_changed =
186       WebTestControlHost::Get()
187           ->accumulated_web_test_runtime_flags_changes()
188           .FindBoolPath("dump_title_changes");
189 
190   std::vector<std::string> logs;
191 
192   if (load.has_value() && load.value()) {
193     // TitleWasSet is only available on top-level frames.
194     std::string log = "main frame";
195     logs.emplace_back(
196         log + " - TitleWasSet: " + base::UTF16ToUTF8(web_contents->GetTitle()));
197   }
198 
199   if (title_changed.has_value() && title_changed.value()) {
200     logs.emplace_back("TITLE CHANGED: '" +
201                       base::UTF16ToUTF8(web_contents->GetTitle()) + "'");
202   }
203   return logs;
204 }
205 
DumpFailLoad(WebContents * web_contents,RenderFrameHost * render_frame_host)206 std::string DumpFailLoad(WebContents* web_contents,
207                          RenderFrameHost* render_frame_host) {
208   base::Optional<bool> result =
209       WebTestControlHost::Get()
210           ->accumulated_web_test_runtime_flags_changes()
211           .FindBoolPath("dump_frame_load_callbacks");
212 
213   if (!result.has_value())
214     return std::string();
215 
216   std::string log = (web_contents->GetMainFrame() == render_frame_host)
217                         ? "main frame "
218                         : "frame ";
219   std::string name = GetFrameNameFromBrowserForWebTests(render_frame_host);
220   log += !name.empty() ? "\"" + name + "\"" : "(anonymous)";
221   return log + " - DidFailLoad";
222 }
223 
224 // Draws a selection rect into a bitmap.
DrawSelectionRect(const SkBitmap & bitmap,const blink::WebRect & wr)225 void DrawSelectionRect(const SkBitmap& bitmap, const blink::WebRect& wr) {
226   // Render a red rectangle bounding selection rect
227   cc::SkiaPaintCanvas canvas(bitmap);
228   cc::PaintFlags flags;
229   flags.setColor(0xFFFF0000);  // Fully opaque red
230   flags.setStyle(cc::PaintFlags::kStroke_Style);
231   flags.setAntiAlias(true);
232   flags.setStrokeWidth(1.0f);
233   SkIRect rect;  // Bounding rect
234   rect.setXYWH(wr.x, wr.y, wr.width, wr.height);
235   canvas.drawIRect(rect, flags);
236 }
237 
238 // Applies settings that differ between web tests and regular mode. Some
239 // of the defaults are controlled via command line flags which are
240 // automatically set for web tests.
ApplyWebTestDefaultPreferences(blink::web_pref::WebPreferences * prefs)241 void ApplyWebTestDefaultPreferences(blink::web_pref::WebPreferences* prefs) {
242   const base::CommandLine& command_line =
243       *base::CommandLine::ForCurrentProcess();
244 
245   prefs->allow_universal_access_from_file_urls = false;
246   prefs->dom_paste_enabled = true;
247   prefs->javascript_can_access_clipboard = true;
248   prefs->xslt_enabled = true;
249   prefs->application_cache_enabled = true;
250   prefs->tabs_to_links = false;
251   prefs->hyperlink_auditing_enabled = false;
252   prefs->allow_running_insecure_content = false;
253   prefs->disable_reading_from_canvas = false;
254   prefs->strict_mixed_content_checking = false;
255   prefs->strict_powerful_feature_restrictions = false;
256   prefs->webgl_errors_to_console_enabled = false;
257   prefs->enable_scroll_animator =
258       !command_line.HasSwitch(switches::kDisableSmoothScrolling);
259   prefs->threaded_scrolling_enabled =
260       command_line.HasSwitch(switches::kEnableThreadedCompositing) &&
261       !command_line.HasSwitch(blink::switches::kDisableThreadedScrolling);
262   prefs->minimum_logical_font_size = 9;
263   prefs->accelerated_2d_canvas_enabled =
264       command_line.HasSwitch(switches::kEnableAccelerated2DCanvas);
265   prefs->smart_insert_delete_enabled = true;
266   prefs->viewport_enabled = command_line.HasSwitch(switches::kEnableViewport);
267   prefs->default_minimum_page_scale_factor = 1.f;
268   prefs->default_maximum_page_scale_factor = 4.f;
269   prefs->presentation_receiver =
270       command_line.HasSwitch(switches::kForcePresentationReceiverForTesting);
271   prefs->translate_service_available = true;
272 
273 #if defined(OS_MAC)
274   prefs->editing_behavior = blink::mojom::EditingBehavior::kEditingMacBehavior;
275 #else
276   prefs->editing_behavior =
277       blink::mojom::EditingBehavior::kEditingWindowsBehavior;
278 #endif
279 
280 #if defined(OS_MAC)
281   prefs->cursive_font_family_map[blink::web_pref::kCommonScript] =
282       base::ASCIIToUTF16("Apple Chancery");
283   prefs->fantasy_font_family_map[blink::web_pref::kCommonScript] =
284       base::ASCIIToUTF16("Papyrus");
285   prefs->serif_font_family_map[blink::web_pref::kCommonScript] =
286       base::ASCIIToUTF16("Times");
287   prefs->standard_font_family_map[blink::web_pref::kCommonScript] =
288       base::ASCIIToUTF16("Times");
289 #else
290   prefs->cursive_font_family_map[blink::web_pref::kCommonScript] =
291       base::ASCIIToUTF16("Comic Sans MS");
292   prefs->fantasy_font_family_map[blink::web_pref::kCommonScript] =
293       base::ASCIIToUTF16("Impact");
294   prefs->serif_font_family_map[blink::web_pref::kCommonScript] =
295       base::ASCIIToUTF16("times new roman");
296   prefs->standard_font_family_map[blink::web_pref::kCommonScript] =
297       base::ASCIIToUTF16("times new roman");
298 #endif
299   prefs->fixed_font_family_map[blink::web_pref::kCommonScript] =
300       base::ASCIIToUTF16("Courier");
301   prefs->sans_serif_font_family_map[blink::web_pref::kCommonScript] =
302       base::ASCIIToUTF16("Helvetica");
303 }
304 
305 }  // namespace
306 
307 // WebTestResultPrinter ----------------------------------------------------
308 
WebTestResultPrinter(std::ostream * output,std::ostream * error)309 WebTestResultPrinter::WebTestResultPrinter(std::ostream* output,
310                                            std::ostream* error)
311     : state_(DURING_TEST),
312       capture_text_only_(false),
313       encode_binary_data_(false),
314       output_(output),
315       error_(error) {}
316 
StartStateDump()317 void WebTestResultPrinter::StartStateDump() {
318   state_ = DURING_STATE_DUMP;
319 }
320 
PrintTextHeader()321 void WebTestResultPrinter::PrintTextHeader() {
322   if (state_ != DURING_STATE_DUMP)
323     return;
324   if (!capture_text_only_)
325     *output_ << "Content-Type: text/plain\n";
326   state_ = IN_TEXT_BLOCK;
327 }
328 
PrintTextBlock(const std::string & block)329 void WebTestResultPrinter::PrintTextBlock(const std::string& block) {
330   if (state_ != IN_TEXT_BLOCK)
331     return;
332   *output_ << block;
333 }
334 
PrintTextFooter()335 void WebTestResultPrinter::PrintTextFooter() {
336   if (state_ != IN_TEXT_BLOCK)
337     return;
338   if (!capture_text_only_) {
339     *output_ << "#EOF\n";
340     output_->flush();
341   }
342   state_ = IN_IMAGE_BLOCK;
343 }
344 
PrintImageHeader(const std::string & actual_hash,const std::string & expected_hash)345 void WebTestResultPrinter::PrintImageHeader(const std::string& actual_hash,
346                                             const std::string& expected_hash) {
347   if (state_ != IN_IMAGE_BLOCK || capture_text_only_)
348     return;
349   *output_ << "\nActualHash: " << actual_hash << "\n";
350   if (!expected_hash.empty())
351     *output_ << "\nExpectedHash: " << expected_hash << "\n";
352 }
353 
PrintImageBlock(const std::vector<unsigned char> & png_image)354 void WebTestResultPrinter::PrintImageBlock(
355     const std::vector<unsigned char>& png_image) {
356   if (state_ != IN_IMAGE_BLOCK || capture_text_only_)
357     return;
358   *output_ << "Content-Type: image/png\n";
359   if (encode_binary_data_) {
360     PrintEncodedBinaryData(png_image);
361     return;
362   }
363 
364   *output_ << "Content-Length: " << png_image.size() << "\n";
365   output_->write(reinterpret_cast<const char*>(&png_image[0]),
366                  png_image.size());
367 }
368 
PrintImageFooter()369 void WebTestResultPrinter::PrintImageFooter() {
370   if (state_ != IN_IMAGE_BLOCK)
371     return;
372   if (!capture_text_only_) {
373     *output_ << "#EOF\n";
374     output_->flush();
375   }
376   state_ = AFTER_TEST;
377 }
378 
PrintAudioHeader()379 void WebTestResultPrinter::PrintAudioHeader() {
380   DCHECK_EQ(state_, DURING_STATE_DUMP);
381   if (!capture_text_only_)
382     *output_ << "Content-Type: audio/wav\n";
383   state_ = IN_AUDIO_BLOCK;
384 }
385 
PrintAudioBlock(const std::vector<unsigned char> & audio_data)386 void WebTestResultPrinter::PrintAudioBlock(
387     const std::vector<unsigned char>& audio_data) {
388   if (state_ != IN_AUDIO_BLOCK || capture_text_only_)
389     return;
390   if (encode_binary_data_) {
391     PrintEncodedBinaryData(audio_data);
392     return;
393   }
394 
395   *output_ << "Content-Length: " << audio_data.size() << "\n";
396   output_->write(reinterpret_cast<const char*>(&audio_data[0]),
397                  audio_data.size());
398 }
399 
PrintAudioFooter()400 void WebTestResultPrinter::PrintAudioFooter() {
401   if (state_ != IN_AUDIO_BLOCK)
402     return;
403   if (!capture_text_only_) {
404     *output_ << "#EOF\n";
405     output_->flush();
406   }
407   state_ = IN_IMAGE_BLOCK;
408 }
409 
AddMessageToStderr(const std::string & message)410 void WebTestResultPrinter::AddMessageToStderr(const std::string& message) {
411   *error_ << message;
412 }
413 
AddMessage(const std::string & message)414 void WebTestResultPrinter::AddMessage(const std::string& message) {
415   AddMessageRaw(message + "\n");
416 }
417 
AddMessageRaw(const std::string & message)418 void WebTestResultPrinter::AddMessageRaw(const std::string& message) {
419   if (state_ != DURING_TEST)
420     return;
421   *output_ << message;
422 }
423 
AddErrorMessage(const std::string & message)424 void WebTestResultPrinter::AddErrorMessage(const std::string& message) {
425   if (!capture_text_only_)
426     *error_ << message << "\n";
427   if (state_ != DURING_TEST && state_ != DURING_STATE_DUMP)
428     return;
429   PrintTextHeader();
430   *output_ << message << "\n";
431   PrintTextFooter();
432   PrintImageFooter();
433 }
434 
PrintEncodedBinaryData(const std::vector<unsigned char> & data)435 void WebTestResultPrinter::PrintEncodedBinaryData(
436     const std::vector<unsigned char>& data) {
437   *output_ << "Content-Transfer-Encoding: base64\n";
438 
439   std::string data_base64;
440   base::Base64Encode(
441       base::StringPiece(reinterpret_cast<const char*>(&data[0]), data.size()),
442       &data_base64);
443 
444   *output_ << "Content-Length: " << data_base64.length() << "\n";
445   output_->write(data_base64.c_str(), data_base64.length());
446 }
447 
CloseStderr()448 void WebTestResultPrinter::CloseStderr() {
449   if (state_ != AFTER_TEST)
450     return;
451   if (!capture_text_only_) {
452     *error_ << "#EOF\n";
453     error_->flush();
454   }
455 }
456 
457 // WebTestWindowObserver -----------------------------------------------------
458 
459 class WebTestControlHost::WebTestWindowObserver : WebContentsObserver {
460  public:
WebTestWindowObserver(WebContents * web_contents,WebTestControlHost * web_test_control)461   WebTestWindowObserver(WebContents* web_contents,
462                         WebTestControlHost* web_test_control)
463       : WebContentsObserver(web_contents), web_test_control_(web_test_control) {
464     // If the WebContents was already set up before given to the Shell, it may
465     // have a set of RenderFrames already, and we need to notify about them
466     // here.
467     if (web_contents->GetMainFrame()->IsRenderFrameLive()) {
468       for (RenderFrameHost* frame : web_contents->GetAllFrames()) {
469         if (frame->IsRenderFrameLive())
470           RenderFrameCreated(frame);
471       }
472     }
473   }
474 
WebContentsDestroyed()475   void WebContentsDestroyed() override {
476     // Deletes |this| and removes the pointer to it from WebTestControlHost.
477     web_test_control_->test_opened_window_observers_.erase(web_contents());
478   }
479 
RenderFrameCreated(RenderFrameHost * render_frame_host)480   void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
481     web_test_control_->HandleNewRenderFrameHost(render_frame_host);
482   }
483 
484  private:
485   WebTestControlHost* const web_test_control_;
486 };
487 
488 // WebTestControlHost -------------------------------------------------------
489 
490 WebTestControlHost* WebTestControlHost::instance_ = nullptr;
491 
492 // static
Get()493 WebTestControlHost* WebTestControlHost::Get() {
494   return instance_;
495 }
496 
WebTestControlHost()497 WebTestControlHost::WebTestControlHost()
498     : main_window_(nullptr),
499       secondary_window_(nullptr),
500       test_phase_(BETWEEN_TESTS),
501       crash_when_leak_found_(false) {
502   CHECK(!instance_);
503   instance_ = this;
504 
505   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
506           switches::kEnableLeakDetection)) {
507     leak_detector_ = std::make_unique<LeakDetector>();
508     std::string switchValue =
509         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
510             switches::kEnableLeakDetection);
511     crash_when_leak_found_ = switchValue == switches::kCrashOnFailure;
512   }
513 
514   printer_ = std::make_unique<WebTestResultPrinter>(&std::cout, &std::cerr);
515   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
516           switches::kEncodeBinary))
517     printer_->set_encode_binary_data(true);
518 
519   // Print text only (without binary dumps and headers/footers for run_web_tests
520   // protocol) until we enter the protocol mode (see TestInfo::protocol_mode).
521   printer_->set_capture_text_only(true);
522 
523   InjectTestSharedWorkerService(BrowserContext::GetStoragePartition(
524       ShellContentBrowserClient::Get()->browser_context(), nullptr));
525 
526   GpuDataManager::GetInstance()->AddObserver(this);
527   ResetBrowserAfterWebTest();
528 }
529 
~WebTestControlHost()530 WebTestControlHost::~WebTestControlHost() {
531   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
532   CHECK(instance_ == this);
533   CHECK(test_phase_ == BETWEEN_TESTS);
534   GpuDataManager::GetInstance()->RemoveObserver(this);
535   instance_ = nullptr;
536   // The |main_window_| and |secondary_window_| are leaked here, but the
537   // WebTestBrowserMainRunner will close all Shell windows including those.
538 }
539 
PrepareForWebTest(const TestInfo & test_info)540 bool WebTestControlHost::PrepareForWebTest(const TestInfo& test_info) {
541   TRACE_EVENT0("shell", "WebTestControlHost::PrepareForWebTest");
542   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
543   current_working_directory_ = test_info.current_working_directory;
544   expected_pixel_hash_ = test_info.expected_pixel_hash;
545   bool is_devtools_js_test = false;
546   test_url_ = WebTestDevToolsBindings::MapTestURLIfNeeded(test_info.url,
547                                                           &is_devtools_js_test);
548   bool is_devtools_protocol_test = false;
549   test_url_ = DevToolsProtocolTestBindings::MapTestURLIfNeeded(
550       test_url_, &is_devtools_protocol_test);
551 
552   protocol_mode_ = test_info.protocol_mode;
553   if (protocol_mode_)
554     printer_->set_capture_text_only(false);
555   printer_->reset();
556 
557   accumulated_web_test_runtime_flags_changes_.Clear();
558   main_window_render_view_hosts_.clear();
559   main_window_render_process_hosts_.clear();
560   all_observed_render_process_hosts_.clear();
561   render_process_host_observer_.RemoveAll();
562   frame_to_layout_dump_map_.clear();
563 
564   ShellBrowserContext* browser_context =
565       ShellContentBrowserClient::Get()->browser_context();
566 
567   browser_context->GetClientHintsControllerDelegate()->ResetForTesting();
568 
569   const gfx::Size window_size = Shell::GetShellDefaultSize();
570 
571   if (!main_window_) {
572     TRACE_EVENT0("shell",
573                  "WebTestControlHost::PrepareForWebTest::CreateMainWindow");
574     main_window_ = content::Shell::CreateNewWindow(
575         browser_context, GURL(url::kAboutBlankURL), nullptr, window_size);
576     WebContentsObserver::Observe(main_window_->web_contents());
577 
578     default_prefs_ = main_window_->web_contents()->GetOrCreateWebPreferences();
579   } else {
580     // Set a different size first to reset the possibly inconsistent state
581     // caused by the previous test using unfortunate synchronous resize mode.
582     // This forces SetSize() not to early return which would otherwise happen
583     // when we set the size to |window_size| which is the same as its current
584     // size. See http://crbug.com/1011191 for more details.
585     // TODO(crbug.com/309760): This resize to half-size could go away if
586     // testRunner.useUnfortunateSynchronousResizeMode() goes away.
587     main_window_->ResizeWebContentForTests(
588         gfx::ScaleToCeiledSize(window_size, 0.5f, 1));
589     main_window_->ResizeWebContentForTests(window_size);
590 
591     main_window_->web_contents()->SetWebPreferences(default_prefs_);
592 
593     main_window_->web_contents()->WasShown();
594   }
595 
596   // Tests should always start with the browser controls hidden.
597   // TODO(danakj): We no longer run web tests on android, and this is an android
598   // feature, so maybe this isn't needed anymore.
599   main_window_->web_contents()->GetMainFrame()->UpdateBrowserControlsState(
600       BROWSER_CONTROLS_STATE_BOTH, BROWSER_CONTROLS_STATE_HIDDEN, false);
601 
602   // We did not track the |main_window_| RenderFrameHost during the creation of
603   // |main_window_|, since we need the pointer value in this class set first. So
604   // we update the |test_phase_| here allowing us to now track the RenderFrames
605   // in that window, and call HandleNewRenderFrameHost() explicitly.
606   test_phase_ = DURING_TEST;
607   HandleNewRenderFrameHost(main_window_->web_contents()->GetMainFrame());
608 
609   if (is_devtools_protocol_test) {
610     devtools_protocol_test_bindings_ =
611         std::make_unique<DevToolsProtocolTestBindings>(
612             main_window_->web_contents());
613   }
614 
615   // We don't go down the normal system path of focusing RenderWidgetHostView
616   // because on mac headless, there are no system windows and that path does
617   // not do anything. Instead we go through the Shell::ActivateContents() path
618   // which knows how to perform the activation correctly on all platforms and in
619   // headless mode.
620   main_window_->ActivateContents(main_window_->web_contents());
621 
622   RenderViewHost* main_render_view_host =
623       main_window_->web_contents()->GetMainFrame()->GetRenderViewHost();
624   {
625     TRACE_EVENT0("shell", "WebTestControlHost::PrepareForWebTest::Flush");
626     // Round-trip through the InputHandler mojom interface to the compositor
627     // thread, in order to ensure that any input events (moving the mouse at the
628     // start of the test, focus coming from ActivateContents() above, etc) are
629     // handled and bounced if appropriate to the main thread, before we continue
630     // and start the test. This will ensure they are handled on the main thread
631     // before the test runs, which would otherwise race against them.
632     main_render_view_host->GetWidget()->FlushForTesting();
633   }
634 
635   if (is_devtools_js_test) {
636     secondary_window_ = content::Shell::CreateNewWindow(
637         ShellContentBrowserClient::Get()->browser_context(),
638         GURL(url::kAboutBlankURL), nullptr, window_size);
639     // This navigates the secondary (devtools inspector) window, and then
640     // navigates the main window once that has loaded to a devtools html test
641     // page, based on the test url.
642     devtools_bindings_ = std::make_unique<WebTestDevToolsBindings>(
643         main_window_->web_contents(), secondary_window_->web_contents(),
644         test_url_);
645   } else {
646     // Loading the URL will immediately start the web test. Manually call
647     // LoadURLWithParams on the WebContents to avoid extraneous calls from
648     // content::Shell such as SetFocus(), which could race with the web
649     // test.
650     NavigationController::LoadURLParams params(test_url_);
651 
652     // Using PAGE_TRANSITION_TYPED replicates an omnibox navigation.
653     params.transition_type =
654         ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED);
655 
656     // Clear history to purge the prior navigation to about:blank.
657     params.should_clear_history_list = true;
658     main_window_->web_contents()->GetController().LoadURLWithParams(params);
659   }
660 
661   return true;
662 }
663 
ResetBrowserAfterWebTest()664 bool WebTestControlHost::ResetBrowserAfterWebTest() {
665   TRACE_EVENT0("shell", "WebTestControlHost::ResetBrowserAfterWebTest");
666   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
667 
668   // Close any windows opened by the test to avoid them polluting the next
669   // test.
670   CloseTestOpenedWindows();
671 
672   // Close IPC channels to avoid unexpected messages or replies after the test
673   // is done. New channels will be opened for the next test.
674   web_test_render_frame_map_.clear();
675   web_test_render_thread_map_.clear();
676   receiver_bindings_.Clear();
677 
678   printer_->PrintTextFooter();
679   printer_->PrintImageFooter();
680   printer_->CloseStderr();
681   test_phase_ = BETWEEN_TESTS;
682   expected_pixel_hash_.clear();
683   test_url_ = GURL();
684   prefs_ = blink::web_pref::WebPreferences();
685   should_override_prefs_ = false;
686   WebTestContentBrowserClient::Get()->SetPopupBlockingEnabled(false);
687   WebTestContentBrowserClient::Get()->ResetMockClipboardHosts();
688   WebTestContentBrowserClient::Get()->SetScreenOrientationChanged(false);
689   WebTestContentBrowserClient::Get()->ResetFakeBluetoothDelegate();
690   WebTestContentBrowserClient::Get()
691       ->GetWebTestBrowserContext()
692       ->GetWebTestPermissionManager()
693       ->ResetPermissions();
694   check_for_leaked_windows_ = false;
695   renderer_dump_result_ = nullptr;
696   navigation_history_dump_ = "";
697   layout_dump_.reset();
698   waiting_for_layout_dumps_ = 0;
699   pixel_dump_.reset();
700   actual_pixel_hash_ = "";
701   waiting_for_pixel_results_ = false;
702   composite_all_frames_node_queue_ = std::queue<Node*>();
703   composite_all_frames_node_storage_.clear();
704   next_pointer_lock_action_ = NextPointerLockAction::kWillSucceed;
705 
706   BlockThirdPartyCookies(false);
707   SetBluetoothManualChooser(false);
708   SetDatabaseQuota(content::kDefaultDatabaseQuota);
709 
710   // Delete all cookies.
711   {
712     BrowserContext* browser_context =
713         ShellContentBrowserClient::Get()->browser_context();
714     StoragePartition* storage_partition =
715         BrowserContext::GetStoragePartition(browser_context, nullptr);
716     storage_partition->GetCookieManagerForBrowserProcess()->DeleteCookies(
717         network::mojom::CookieDeletionFilter::New(),
718         base::BindOnce([](uint32_t) {}));
719   }
720 
721   ui::SelectFileDialog::SetFactory(nullptr);
722   {
723     base::ScopedAllowBlockingForTesting allow_blocking;
724     if (writable_directory_for_tests_.IsValid()) {
725       if (!writable_directory_for_tests_.Delete())
726         LOG(ERROR) << "Failed to delete temporary directory";
727     }
728   }
729 
730   weak_factory_.InvalidateWeakPtrs();
731 
732   return true;
733 }
734 
DidCreateOrAttachWebContents(WebContents * web_contents)735 void WebTestControlHost::DidCreateOrAttachWebContents(
736     WebContents* web_contents) {
737   auto result = test_opened_window_observers_.emplace(
738       web_contents,
739       std::make_unique<WebTestWindowObserver>(web_contents, this));
740   CHECK(result.second);  // The WebContents should not already be in the map!
741 }
742 
SetTempPath(const base::FilePath & temp_path)743 void WebTestControlHost::SetTempPath(const base::FilePath& temp_path) {
744   temp_path_ = temp_path;
745 }
746 
OverrideWebkitPrefs(blink::web_pref::WebPreferences * prefs)747 void WebTestControlHost::OverrideWebkitPrefs(
748     blink::web_pref::WebPreferences* prefs) {
749   if (should_override_prefs_) {
750     *prefs = prefs_;
751   } else {
752     ApplyWebTestDefaultPreferences(prefs);
753   }
754   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
755           switches::kForceDarkMode)) {
756     prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kDark;
757   } else {
758     prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kLight;
759   }
760 
761   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
762           switches::kForceHighContrast)) {
763     prefs->preferred_contrast = blink::mojom::PreferredContrast::kMore;
764   } else {
765     prefs->preferred_contrast = blink::mojom::PreferredContrast::kNoPreference;
766   }
767 }
768 
OpenURL(const GURL & url)769 void WebTestControlHost::OpenURL(const GURL& url) {
770   if (test_phase_ != DURING_TEST)
771     return;
772 
773   Shell::CreateNewWindow(main_window_->web_contents()->GetBrowserContext(), url,
774                          main_window_->web_contents()->GetSiteInstance(),
775                          gfx::Size());
776 }
777 
InitiateCaptureDump(mojom::WebTestRendererDumpResultPtr renderer_dump_result,bool capture_navigation_history,bool capture_pixels)778 void WebTestControlHost::InitiateCaptureDump(
779     mojom::WebTestRendererDumpResultPtr renderer_dump_result,
780     bool capture_navigation_history,
781     bool capture_pixels) {
782   if (test_phase_ != DURING_TEST)
783     return;
784 
785   renderer_dump_result_ = std::move(renderer_dump_result);
786 
787   if (capture_navigation_history) {
788     for (auto* window : Shell::windows()) {
789       WebContents* web_contents = window->web_contents();
790       // Only dump the main test window, and windows that it opened. This avoids
791       // devtools windows specifically.
792       if (window == main_window_ || web_contents->HasOpener()) {
793         navigation_history_dump_ +=
794             "\n============== Back Forward List ==============\n";
795         navigation_history_dump_ += DumpHistoryForWebContents(web_contents);
796         navigation_history_dump_ +=
797             "===============================================\n";
798       }
799     }
800   }
801 
802   // Grab a layout dump if the renderer was not able to provide one.
803   if (!renderer_dump_result_->layout) {
804     DCHECK_EQ(0, waiting_for_layout_dumps_);
805 
806     for (RenderFrameHost* rfh : main_window_->web_contents()->GetAllFrames()) {
807       if (!rfh->IsRenderFrameLive())
808         continue;
809 
810       ++waiting_for_layout_dumps_;
811       GetWebTestRenderFrameRemote(rfh)->DumpFrameLayout(base::BindOnce(
812           &WebTestControlHost::OnDumpFrameLayoutResponse,
813           weak_factory_.GetWeakPtr(), rfh->GetFrameTreeNodeId()));
814     }
815   }
816 
817   if (capture_pixels) {
818     waiting_for_pixel_results_ = true;
819     auto* rwhv = main_window_->web_contents()->GetRenderWidgetHostView();
820     // If we're running in threaded mode, then the frames will be produced via a
821     // scheduler elsewhere, all we need to do is to ensure that the surface is
822     // synchronized before we copy from it. In single threaded mode, we have to
823     // force each renderer to produce a frame.
824     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
825             switches::kEnableThreadedCompositing)) {
826       rwhv->EnsureSurfaceSynchronizedForWebTest();
827       EnqueueSurfaceCopyRequest();
828     } else {
829       CompositeAllFramesThen(
830           base::BindOnce(&WebTestControlHost::EnqueueSurfaceCopyRequest,
831                          weak_factory_.GetWeakPtr()));
832     }
833   }
834 
835   // Try to report results now, if we aren't waiting for anything.
836   ReportResults();
837 }
838 
TestFinishedInSecondaryRenderer()839 void WebTestControlHost::TestFinishedInSecondaryRenderer() {
840   GetWebTestRenderThreadRemote(main_window_->web_contents()
841                                    ->GetMainFrame()
842                                    ->GetRenderViewHost()
843                                    ->GetProcess())
844       ->TestFinishedFromSecondaryRenderer();
845 }
846 
847 // Enqueue an image copy output request.
EnqueueSurfaceCopyRequest()848 void WebTestControlHost::EnqueueSurfaceCopyRequest() {
849   // Under fuzzing, the renderer may close the |main_window_| while we're
850   // capturing test results, as demonstrated by https://crbug.com/1098835.
851   // We must handle this bad behaviour, and we just end the test without
852   // recording any results.
853   if (!main_window_) {
854     OnTestFinished();
855     return;
856   }
857 
858   auto* rwhv = main_window_->web_contents()->GetRenderWidgetHostView();
859   rwhv->CopyFromSurface(gfx::Rect(), gfx::Size(),
860                         base::BindOnce(&WebTestControlHost::OnPixelDumpCaptured,
861                                        weak_factory_.GetWeakPtr()));
862 }
863 
CompositeAllFramesThen(base::OnceCallback<void ()> callback)864 void WebTestControlHost::CompositeAllFramesThen(
865     base::OnceCallback<void()> callback) {
866   // Only allow a single call to CompositeAllFramesThen(), without a call to
867   // ResetBrowserAfterWebTest() in between. More than once risks overlapping
868   // calls, due to the asynchronous nature of CompositeNodeQueueThen(), which
869   // can lead to use-after-free, e.g.
870   // https://clusterfuzz.com/v2/testcase-detail/4929420383748096
871   if (!composite_all_frames_node_storage_.empty() ||
872       !composite_all_frames_node_queue_.empty()) {
873     // Using NOTREACHED + return here because we want to disallow the second
874     // call if this happens in release builds, while still catching this
875     // condition in debug builds.
876     NOTREACHED();
877     return;
878   }
879   // Build the frame storage and depth first queue.
880   Node* root = BuildFrameTree(main_window_->web_contents());
881   BuildDepthFirstQueue(root);
882   // Now asynchronously run through the node queue.
883   CompositeNodeQueueThen(std::move(callback));
884 }
885 
CompositeNodeQueueThen(base::OnceCallback<void ()> callback)886 void WebTestControlHost::CompositeNodeQueueThen(
887     base::OnceCallback<void()> callback) {
888   RenderFrameHost* frame = nullptr;
889   while (!frame) {
890     if (composite_all_frames_node_queue_.empty()) {
891       // Done with the queue - call the callback.
892       std::move(callback).Run();
893       return;
894     }
895 
896     frame = composite_all_frames_node_queue_.front()->render_frame_host;
897     GlobalFrameRoutingId routing_id =
898         composite_all_frames_node_queue_.front()->render_frame_host_id;
899     composite_all_frames_node_queue_.pop();
900 
901     if (!RenderFrameHost::FromID(routing_id.child_id,
902                                  routing_id.frame_routing_id)) {
903       // The frame is gone. Frames can get detached by a parent frame during or
904       // in between SynchronouslyCompositeAfterTest() calls, after the test
905       // claims it has finished. That would be bad test behaviour but the fuzzer
906       // can do it. See crbug.com/899465 for an example of this problem.
907       frame = nullptr;
908     } else if (!frame->IsRenderFrameLive()) {
909       // The renderer is gone. Frames can also crash the renderer after the test
910       // claims to be finished.
911       frame = nullptr;
912     } else if (frame->GetParent() && frame->GetParent()->GetSiteInstance() ==
913                                          frame->GetSiteInstance()) {
914       // The frame is not a local root, so nothing to do.
915       frame = nullptr;
916     }
917   }
918 
919   GetWebTestRenderFrameRemote(frame)->SynchronouslyCompositeAfterTest(
920       base::BindOnce(&WebTestControlHost::CompositeNodeQueueThen,
921                      weak_factory_.GetWeakPtr(), std::move(callback)));
922 }
923 
BuildDepthFirstQueue(Node * node)924 void WebTestControlHost::BuildDepthFirstQueue(Node* node) {
925   for (auto* child : node->children)
926     BuildDepthFirstQueue(child);
927   composite_all_frames_node_queue_.push(node);
928 }
929 
BuildFrameTree(WebContents * web_contents)930 WebTestControlHost::Node* WebTestControlHost::BuildFrameTree(
931     WebContents* web_contents) {
932   // Returns a Node for a given RenderFrameHost, or nullptr if doesn't exist.
933   auto node_for_frame = [this](RenderFrameHost* rfh) {
934     auto it = std::find_if(
935         composite_all_frames_node_storage_.begin(),
936         composite_all_frames_node_storage_.end(),
937         [rfh](auto& node) { return node->render_frame_host == rfh; });
938     return it == composite_all_frames_node_storage_.end() ? nullptr : it->get();
939   };
940 
941   Node* outer_root = nullptr;
942   std::vector<WebContents*> all_web_contents(1, web_contents);
943   for (unsigned i = 0; i < all_web_contents.size(); i++) {
944     WebContents* contents = all_web_contents[i];
945 
946     //  Collect all live frames in contents.
947     std::vector<RenderFrameHost*> frames;
948     for (auto* frame : contents->GetAllFrames()) {
949       if (frame->IsRenderFrameLive())
950         frames.push_back(frame);
951     }
952 
953     // Add all of the frames to storage.
954     for (auto* frame : frames) {
955       DCHECK(!node_for_frame(frame)) << "Frame seen multiple times.";
956       composite_all_frames_node_storage_.emplace_back(
957           std::make_unique<Node>(frame));
958     }
959 
960     // Construct a tree rooted at |root|.
961     Node* root = nullptr;
962     for (auto* frame : frames) {
963       Node* node = node_for_frame(frame);
964       DCHECK(node);
965       if (!frame->GetParent()) {
966         DCHECK(!root) << "Multiple roots found.";
967         root = node;
968       } else {
969         Node* parent = node_for_frame(frame->GetParent());
970         DCHECK(parent);
971         parent->children.push_back(node);
972       }
973     }
974     DCHECK(root) << "No root found.";
975 
976     // Connect the inner root to the outer node.
977     if (auto* outer_frame = contents->GetOuterWebContentsFrame()) {
978       Node* parent = node_for_frame(outer_frame);
979       DCHECK(parent);
980       parent->children.push_back(root);
981     } else {
982       DCHECK(!outer_root) << "Multiple outer roots found.";
983       outer_root = root;
984     }
985 
986     // Traverse all inner contents.
987     for (auto* inner_contents : contents->GetInnerWebContents())
988       all_web_contents.push_back(inner_contents);
989   }
990   DCHECK(outer_root) << "No outer root found";
991 
992   return outer_root;
993 }
994 
IsMainWindow(WebContents * web_contents) const995 bool WebTestControlHost::IsMainWindow(WebContents* web_contents) const {
996   return main_window_ && web_contents == main_window_->web_contents();
997 }
998 
RunBluetoothChooser(RenderFrameHost * frame,const BluetoothChooser::EventHandler & event_handler)999 std::unique_ptr<BluetoothChooser> WebTestControlHost::RunBluetoothChooser(
1000     RenderFrameHost* frame,
1001     const BluetoothChooser::EventHandler& event_handler) {
1002   // TODO(https://crbug.com/509038): Remove |bluetooth_chooser_factory_| once
1003   // all of the Web Bluetooth tests are migrated to external/wpt/.
1004   if (bluetooth_chooser_factory_) {
1005     return bluetooth_chooser_factory_->RunBluetoothChooser(frame,
1006                                                            event_handler);
1007   }
1008 
1009   auto next_fake_bluetooth_chooser =
1010       WebTestContentBrowserClient::Get()->GetNextFakeBluetoothChooser();
1011   if (next_fake_bluetooth_chooser) {
1012     const url::Origin origin = frame->GetLastCommittedOrigin();
1013     DCHECK(!origin.opaque());
1014     next_fake_bluetooth_chooser->OnRunBluetoothChooser(event_handler, origin);
1015     return next_fake_bluetooth_chooser;
1016   }
1017 
1018   return std::make_unique<WebTestFirstDeviceBluetoothChooser>(event_handler);
1019 }
1020 
RequestToLockMouse(WebContents * web_contents)1021 void WebTestControlHost::RequestToLockMouse(WebContents* web_contents) {
1022   if (next_pointer_lock_action_ == NextPointerLockAction::kTestWillRespond)
1023     return;
1024 
1025   web_contents->GotResponseToLockMouseRequest(
1026       next_pointer_lock_action_ == NextPointerLockAction::kWillSucceed
1027           ? blink::mojom::PointerLockResult::kSuccess
1028           : blink::mojom::PointerLockResult::kPermissionDenied);
1029 
1030   next_pointer_lock_action_ = NextPointerLockAction::kWillSucceed;
1031 }
1032 
PluginCrashed(const base::FilePath & plugin_path,base::ProcessId plugin_pid)1033 void WebTestControlHost::PluginCrashed(const base::FilePath& plugin_path,
1034                                        base::ProcessId plugin_pid) {
1035   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1036   printer_->AddErrorMessage(
1037       base::StringPrintf("#CRASHED - plugin (pid %" CrPRIdPid ")", plugin_pid));
1038   base::ThreadTaskRunnerHandle::Get()->PostTask(
1039       FROM_HERE,
1040       base::BindOnce(base::IgnoreResult(&WebTestControlHost::DiscardMainWindow),
1041                      weak_factory_.GetWeakPtr()));
1042 }
1043 
TitleWasSet(NavigationEntry * entry)1044 void WebTestControlHost::TitleWasSet(NavigationEntry* entry) {
1045   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1046   std::vector<std::string> logs = DumpTitleWasSet(main_window_->web_contents());
1047   if (logs.empty())
1048     return;
1049   for (auto log : logs)
1050     printer_->AddMessage(log);
1051 }
1052 
DidFailLoad(RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code)1053 void WebTestControlHost::DidFailLoad(RenderFrameHost* render_frame_host,
1054                                      const GURL& validated_url,
1055                                      int error_code) {
1056   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1057   std::string log =
1058       DumpFailLoad(main_window_->web_contents(), render_frame_host);
1059   if (log.empty())
1060     return;
1061   printer_->AddMessage(log);
1062 }
1063 
WebContentsDestroyed()1064 void WebTestControlHost::WebContentsDestroyed() {
1065   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1066   printer_->AddErrorMessage("FAIL: main window was destroyed");
1067   DiscardMainWindow();
1068 }
1069 
DidUpdateFaviconURL(RenderFrameHost * render_frame_host,const std::vector<blink::mojom::FaviconURLPtr> & candidates)1070 void WebTestControlHost::DidUpdateFaviconURL(
1071     RenderFrameHost* render_frame_host,
1072     const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
1073   bool should_dump_icon_changes = false;
1074   accumulated_web_test_runtime_flags_changes_.GetBoolean(
1075       "dump_icon_changes", &should_dump_icon_changes);
1076   if (should_dump_icon_changes) {
1077     std::string log = IsMainWindow(web_contents()) ? "main frame " : "frame ";
1078     printer_->AddMessageRaw(log + "- didChangeIcons\n");
1079   }
1080 }
1081 
RenderViewHostChanged(RenderViewHost * old_host,RenderViewHost * new_host)1082 void WebTestControlHost::RenderViewHostChanged(RenderViewHost* old_host,
1083                                                RenderViewHost* new_host) {
1084   // Notifies the main frame of |old_host| that it is deactivated while it's
1085   // kept alive in back-forward cache.
1086   GetWebTestRenderFrameRemote(old_host->GetMainFrame())->OnDeactivated();
1087 }
1088 
RenderViewDeleted(RenderViewHost * render_view_host)1089 void WebTestControlHost::RenderViewDeleted(RenderViewHost* render_view_host) {
1090   main_window_render_view_hosts_.erase(render_view_host);
1091 }
1092 
DidFinishNavigation(NavigationHandle * navigation_handle)1093 void WebTestControlHost::DidFinishNavigation(
1094     NavigationHandle* navigation_handle) {
1095   NavigationRequest* request = NavigationRequest::From(navigation_handle);
1096   RenderFrameHostImpl* rfh = request->rfh_restored_from_back_forward_cache();
1097   if (rfh)
1098     GetWebTestRenderFrameRemote(rfh)->OnReactivated();
1099 }
1100 
RenderProcessHostDestroyed(RenderProcessHost * render_process_host)1101 void WebTestControlHost::RenderProcessHostDestroyed(
1102     RenderProcessHost* render_process_host) {
1103   render_process_host_observer_.Remove(render_process_host);
1104   all_observed_render_process_hosts_.erase(render_process_host);
1105   web_test_render_thread_map_.erase(render_process_host);
1106   main_window_render_process_hosts_.erase(render_process_host);
1107 }
1108 
RenderProcessExited(RenderProcessHost * render_process_host,const ChildProcessTerminationInfo & info)1109 void WebTestControlHost::RenderProcessExited(
1110     RenderProcessHost* render_process_host,
1111     const ChildProcessTerminationInfo& info) {
1112   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1113   switch (info.status) {
1114     case base::TerminationStatus::TERMINATION_STATUS_NORMAL_TERMINATION:
1115     case base::TerminationStatus::TERMINATION_STATUS_STILL_RUNNING:
1116       break;
1117 
1118     case base::TerminationStatus::TERMINATION_STATUS_ABNORMAL_TERMINATION:
1119     case base::TerminationStatus::TERMINATION_STATUS_LAUNCH_FAILED:
1120     case base::TerminationStatus::TERMINATION_STATUS_PROCESS_CRASHED:
1121     case base::TerminationStatus::TERMINATION_STATUS_PROCESS_WAS_KILLED:
1122     default: {
1123       const base::Process& process = render_process_host->GetProcess();
1124       if (process.IsValid()) {
1125         printer_->AddErrorMessage(std::string("#CRASHED - renderer (pid ") +
1126                                   base::NumberToString(process.Pid()) + ")");
1127       } else {
1128         printer_->AddErrorMessage("#CRASHED - renderer");
1129       }
1130 
1131       DiscardMainWindow();
1132       break;
1133     }
1134   }
1135 }
1136 
OnGpuProcessCrashed(base::TerminationStatus exit_code)1137 void WebTestControlHost::OnGpuProcessCrashed(
1138     base::TerminationStatus exit_code) {
1139   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1140   printer_->AddErrorMessage("#CRASHED - gpu");
1141   DiscardMainWindow();
1142 }
1143 
DiscardMainWindow()1144 void WebTestControlHost::DiscardMainWindow() {
1145   // We can get here for 2 different reasons:
1146   // 1. The |main_window_| Shell is destroying, and the WebContents inside it
1147   // has been destroyed. Then we dare not call Shell::Close() on the
1148   // |main_window_|.
1149   // 2. Some other fatal error has occurred. We can't tell this apart from the
1150   // Shell destroying, since that is also something a test can do, and
1151   // destroying the WebContents can also happen in order ways (like activating a
1152   // portal).
1153   //
1154   // Since we can't tell at this point if |main_window_| is okay to use, we
1155   // don't touch it, and we stop observing its WebContents.
1156   WebContentsObserver::Observe(nullptr);
1157   main_window_ = nullptr;
1158 
1159   // We don't want to leak any open windows when we finish a test, and the next
1160   // test will create its own |main_window_|. So at this point we close all
1161   // Shell windows, to avoid using the potentially-bad pointer.
1162   Shell::CloseAllWindows();
1163 
1164   // Then we immediately end the current test instead of timing out. This is
1165   // like ReportResults() except we report only messages added to the
1166   // |printer_| and no other test results.
1167   printer_->StartStateDump();
1168   printer_->PrintTextHeader();
1169   printer_->PrintTextFooter();
1170   OnTestFinished();
1171 }
1172 
HandleNewRenderFrameHost(RenderFrameHost * frame)1173 void WebTestControlHost::HandleNewRenderFrameHost(RenderFrameHost* frame) {
1174   // When creating the main window, we don't have a |main_window_| pointer yet.
1175   // So we will explicitly call this for the main window after moving  to
1176   // DURING_TEST.
1177   if (test_phase_ != DURING_TEST)
1178     return;
1179 
1180   const bool main_window =
1181       WebContents::FromRenderFrameHost(frame) == main_window_->web_contents();
1182 
1183   RenderProcessHost* process_host = frame->GetProcess();
1184   RenderViewHost* view_host = frame->GetRenderViewHost();
1185 
1186   // If this the first time this renderer contains parts of the main test
1187   // window, we need to make sure that it gets configured correctly (including
1188   // letting it know that it's part of the main test window).
1189   // We consider the renderer as new when we see either a new RenderProcessHost
1190   // or a new RenderViewHost, as it is possible that a new renderer (with a new
1191   // RenderViewHost) reuses a renderer process, and it's also possible that we
1192   // reuse RenderViewHosts (in some fetch tests).
1193   // TODO(rakina): Understand the fetch tests to figure out if it's possible to
1194   // remove RenderProcessHost tracking here.
1195   if (main_window &&
1196       (!base::Contains(main_window_render_view_hosts_, view_host) ||
1197        !base::Contains(main_window_render_process_hosts_, process_host))) {
1198     // When we find the main window's main frame for the first time, we mark the
1199     // test as starting for the renderer.
1200     const bool starting_test = main_window_render_process_hosts_.empty();
1201     DCHECK_EQ(main_window_render_process_hosts_.empty(),
1202               main_window_render_view_hosts_.empty());
1203 
1204     main_window_render_view_hosts_.insert(view_host);
1205     main_window_render_process_hosts_.insert(process_host);
1206 
1207     // Make sure the new renderer process_host has a test configuration shared
1208     // with other renderers.
1209     mojom::WebTestRunTestConfigurationPtr params =
1210         mojom::WebTestRunTestConfiguration::New();
1211     params->allow_external_pages = false;
1212     params->current_working_directory = current_working_directory_;
1213     params->temp_path = temp_path_;
1214     params->test_url = test_url_;
1215     params->allow_external_pages =
1216         base::CommandLine::ForCurrentProcess()->HasSwitch(
1217             switches::kAllowExternalPages);
1218     params->expected_pixel_hash = expected_pixel_hash_;
1219     params->protocol_mode = protocol_mode_;
1220 
1221     GetWebTestRenderFrameRemote(frame)->SetTestConfiguration(std::move(params),
1222                                                              starting_test);
1223   }
1224 
1225   // Is this a previously unknown renderer process_host?
1226   if (!render_process_host_observer_.IsObserving(process_host)) {
1227     render_process_host_observer_.Add(process_host);
1228     all_observed_render_process_hosts_.insert(process_host);
1229 
1230     if (!main_window) {
1231       GetWebTestRenderThreadRemote(process_host)
1232           ->SetupRendererProcessForNonTestWindow();
1233     }
1234 
1235     GetWebTestRenderThreadRemote(process_host)
1236         ->ReplicateWebTestRuntimeFlagsChanges(
1237             accumulated_web_test_runtime_flags_changes_.Clone());
1238     GetWebTestRenderThreadRemote(process_host)
1239         ->ReplicateWorkQueueStates(work_queue_states_.Clone());
1240   }
1241 }
1242 
OnTestFinished()1243 void WebTestControlHost::OnTestFinished() {
1244   test_phase_ = CLEAN_UP;
1245   if (!printer_->output_finished())
1246     printer_->PrintImageFooter();
1247   if (main_window_)
1248     main_window_->web_contents()->ExitFullscreen(/*will_cause_resize=*/false);
1249   devtools_bindings_.reset();
1250   devtools_protocol_test_bindings_.reset();
1251   accumulated_web_test_runtime_flags_changes_.Clear();
1252   work_queue_states_.Clear();
1253 
1254   ShellBrowserContext* browser_context =
1255       ShellContentBrowserClient::Get()->browser_context();
1256 
1257   base::RepeatingClosure barrier_closure = base::BarrierClosure(
1258       2, base::BindOnce(&WebTestControlHost::ResetRendererAfterWebTest,
1259                         weak_factory_.GetWeakPtr()));
1260 
1261   StoragePartition* storage_partition =
1262       BrowserContext::GetStoragePartition(browser_context, nullptr);
1263   storage_partition->GetServiceWorkerContext()->ClearAllServiceWorkersForTest(
1264       barrier_closure);
1265   storage_partition->ClearBluetoothAllowedDevicesMapForTesting();
1266 
1267   // TODO(nhiroki): Add a comment about the reason why we terminate all shared
1268   // workers here.
1269   TerminateAllSharedWorkers(
1270       BrowserContext::GetStoragePartition(
1271           ShellContentBrowserClient::Get()->browser_context(), nullptr),
1272       barrier_closure);
1273 }
1274 
OnDumpFrameLayoutResponse(int frame_tree_node_id,const std::string & dump)1275 void WebTestControlHost::OnDumpFrameLayoutResponse(int frame_tree_node_id,
1276                                                    const std::string& dump) {
1277   // Store the result.
1278   auto pair = frame_to_layout_dump_map_.emplace(frame_tree_node_id, dump);
1279   bool insertion_took_place = pair.second;
1280   DCHECK(insertion_took_place);
1281 
1282   // See if we need to wait for more responses.
1283   if (--waiting_for_layout_dumps_ > 0)
1284     return;
1285 
1286   // Stitch the frame-specific results in the right order.
1287   std::string stitched_layout_dump;
1288   for (auto* render_frame_host : web_contents()->GetAllFrames()) {
1289     auto it =
1290         frame_to_layout_dump_map_.find(render_frame_host->GetFrameTreeNodeId());
1291     if (it != frame_to_layout_dump_map_.end()) {
1292       const std::string& dump = it->second;
1293       stitched_layout_dump.append(dump);
1294     }
1295   }
1296 
1297   layout_dump_.emplace(std::move(stitched_layout_dump));
1298   ReportResults();
1299 }
1300 
OnPixelDumpCaptured(const SkBitmap & snapshot)1301 void WebTestControlHost::OnPixelDumpCaptured(const SkBitmap& snapshot) {
1302   DCHECK(!snapshot.drawsNothing());
1303   pixel_dump_ = snapshot;
1304   waiting_for_pixel_results_ = false;
1305   ReportResults();
1306 }
1307 
ReportResults()1308 void WebTestControlHost::ReportResults() {
1309   if (waiting_for_layout_dumps_ || waiting_for_pixel_results_)
1310     return;
1311 
1312   printer_->StartStateDump();
1313 
1314   // Audio results only come from the renderer.
1315   if (renderer_dump_result_->audio)
1316     OnAudioDump(*renderer_dump_result_->audio);
1317 
1318   // Use the browser-generated |layout_dump_| if present, else use the
1319   // renderer's.
1320   if (layout_dump_)
1321     OnTextDump(*layout_dump_);
1322   else if (renderer_dump_result_->layout)
1323     OnTextDump(*renderer_dump_result_->layout);
1324   else
1325     NOTREACHED();
1326 
1327   // Use the browser-generated |pixel_dump_| if present, else use the
1328   // renderer's.
1329   if (pixel_dump_) {
1330     // See if we need to draw the selection bounds rect on top of the snapshot.
1331     if (!renderer_dump_result_->selection_rect.IsEmpty())
1332       DrawSelectionRect(*pixel_dump_, renderer_dump_result_->selection_rect);
1333     // The snapshot arrives from the GPU process via shared memory. Because MSan
1334     // can't track initializedness across processes, we must assure it that the
1335     // pixels are in fact initialized.
1336     MSAN_UNPOISON(pixel_dump_->getPixels(), pixel_dump_->computeByteSize());
1337     base::MD5Digest digest;
1338     base::MD5Sum(pixel_dump_->getPixels(), pixel_dump_->computeByteSize(),
1339                  &digest);
1340     actual_pixel_hash_ = base::MD5DigestToBase16(digest);
1341 
1342     OnImageDump(actual_pixel_hash_, *pixel_dump_);
1343   } else if (!renderer_dump_result_->actual_pixel_hash.empty()) {
1344     OnImageDump(renderer_dump_result_->actual_pixel_hash,
1345                 renderer_dump_result_->pixels);
1346   }
1347 
1348   OnTestFinished();
1349 }
1350 
OnImageDump(const std::string & actual_pixel_hash,const SkBitmap & image)1351 void WebTestControlHost::OnImageDump(const std::string& actual_pixel_hash,
1352                                      const SkBitmap& image) {
1353   printer_->PrintImageHeader(actual_pixel_hash, expected_pixel_hash_);
1354 
1355   // Only encode and dump the png if the hashes don't match. Encoding the
1356   // image is really expensive.
1357   if (actual_pixel_hash != expected_pixel_hash_) {
1358     std::vector<unsigned char> png;
1359 
1360     bool discard_transparency = true;
1361     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1362             switches::kForceOverlayFullscreenVideo)) {
1363       discard_transparency = false;
1364     }
1365 
1366     gfx::PNGCodec::ColorFormat pixel_format;
1367     switch (image.info().colorType()) {
1368       case kBGRA_8888_SkColorType:
1369         pixel_format = gfx::PNGCodec::FORMAT_BGRA;
1370         break;
1371       case kRGBA_8888_SkColorType:
1372         pixel_format = gfx::PNGCodec::FORMAT_RGBA;
1373         break;
1374       default:
1375         NOTREACHED();
1376         return;
1377     }
1378 
1379     std::vector<gfx::PNGCodec::Comment> comments;
1380     comments.push_back(gfx::PNGCodec::Comment("checksum", actual_pixel_hash));
1381     bool success = gfx::PNGCodec::Encode(
1382         static_cast<const unsigned char*>(image.getPixels()), pixel_format,
1383         gfx::Size(image.width(), image.height()),
1384         static_cast<int>(image.rowBytes()), discard_transparency, comments,
1385         &png);
1386     if (success)
1387       printer_->PrintImageBlock(png);
1388   }
1389   printer_->PrintImageFooter();
1390 }
1391 
OnAudioDump(const std::vector<unsigned char> & dump)1392 void WebTestControlHost::OnAudioDump(const std::vector<unsigned char>& dump) {
1393   printer_->PrintAudioHeader();
1394   printer_->PrintAudioBlock(dump);
1395   printer_->PrintAudioFooter();
1396 }
1397 
OnTextDump(const std::string & dump)1398 void WebTestControlHost::OnTextDump(const std::string& dump) {
1399   printer_->PrintTextHeader();
1400   printer_->PrintTextBlock(dump);
1401   if (!navigation_history_dump_.empty())
1402     printer_->PrintTextBlock(navigation_history_dump_);
1403   printer_->PrintTextFooter();
1404 }
1405 
PrintMessageToStderr(const std::string & message)1406 void WebTestControlHost::PrintMessageToStderr(const std::string& message) {
1407   printer_->AddMessageToStderr(message);
1408 }
1409 
PrintMessage(const std::string & message)1410 void WebTestControlHost::PrintMessage(const std::string& message) {
1411   printer_->AddMessageRaw(message);
1412 }
1413 
OverridePreferences(const blink::web_pref::WebPreferences & prefs)1414 void WebTestControlHost::OverridePreferences(
1415     const blink::web_pref::WebPreferences& prefs) {
1416   should_override_prefs_ = true;
1417   prefs_ = prefs;
1418 
1419   // Notifies the WebContents that Blink preferences changed so
1420   // immediately apply the new settings and to avoid re-usage of cached
1421   // preferences that are now stale. WebContents::UpdateWebPreferences is
1422   // not used here because it would send an unneeded preferences update to the
1423   // renderer.
1424   main_window_->web_contents()->OnWebPreferencesChanged();
1425 }
1426 
SetPopupBlockingEnabled(bool block_popups)1427 void WebTestControlHost::SetPopupBlockingEnabled(bool block_popups) {
1428   WebTestContentBrowserClient::Get()->SetPopupBlockingEnabled(block_popups);
1429 }
1430 
SetScreenOrientationChanged()1431 void WebTestControlHost::SetScreenOrientationChanged() {
1432   WebTestContentBrowserClient::Get()->SetScreenOrientationChanged(true);
1433 }
1434 
SetPermission(const std::string & name,blink::mojom::PermissionStatus status,const GURL & origin,const GURL & embedding_origin)1435 void WebTestControlHost::SetPermission(const std::string& name,
1436                                        blink::mojom::PermissionStatus status,
1437                                        const GURL& origin,
1438                                        const GURL& embedding_origin) {
1439   content::PermissionType type;
1440   if (name == "midi") {
1441     type = PermissionType::MIDI;
1442   } else if (name == "midi-sysex") {
1443     type = PermissionType::MIDI_SYSEX;
1444   } else if (name == "push-messaging" || name == "notifications") {
1445     type = PermissionType::NOTIFICATIONS;
1446   } else if (name == "geolocation") {
1447     type = PermissionType::GEOLOCATION;
1448   } else if (name == "protected-media-identifier") {
1449     type = PermissionType::PROTECTED_MEDIA_IDENTIFIER;
1450   } else if (name == "background-sync") {
1451     type = PermissionType::BACKGROUND_SYNC;
1452   } else if (name == "accessibility-events") {
1453     type = PermissionType::ACCESSIBILITY_EVENTS;
1454   } else if (name == "clipboard-read-write") {
1455     type = PermissionType::CLIPBOARD_READ_WRITE;
1456   } else if (name == "clipboard-sanitized-write") {
1457     type = PermissionType::CLIPBOARD_SANITIZED_WRITE;
1458   } else if (name == "payment-handler") {
1459     type = PermissionType::PAYMENT_HANDLER;
1460   } else if (name == "accelerometer" || name == "gyroscope" ||
1461              name == "magnetometer" || name == "ambient-light-sensor") {
1462     type = PermissionType::SENSORS;
1463   } else if (name == "background-fetch") {
1464     type = PermissionType::BACKGROUND_FETCH;
1465   } else if (name == "periodic-background-sync") {
1466     type = PermissionType::PERIODIC_BACKGROUND_SYNC;
1467   } else if (name == "wake-lock-screen") {
1468     type = PermissionType::WAKE_LOCK_SCREEN;
1469   } else if (name == "wake-lock-system") {
1470     type = PermissionType::WAKE_LOCK_SYSTEM;
1471   } else if (name == "nfc") {
1472     type = PermissionType::NFC;
1473   } else if (name == "storage-access") {
1474     type = PermissionType::STORAGE_ACCESS_GRANT;
1475   } else {
1476     NOTREACHED();
1477     type = PermissionType::NOTIFICATIONS;
1478   }
1479 
1480   WebTestContentBrowserClient::Get()
1481       ->GetWebTestBrowserContext()
1482       ->GetWebTestPermissionManager()
1483       ->SetPermission(type, status, origin, embedding_origin);
1484 }
1485 
GetWritableDirectory(GetWritableDirectoryCallback reply)1486 void WebTestControlHost::GetWritableDirectory(
1487     GetWritableDirectoryCallback reply) {
1488   base::ScopedAllowBlockingForTesting allow_blocking;
1489   if (!writable_directory_for_tests_.IsValid()) {
1490     if (!writable_directory_for_tests_.CreateUniqueTempDir()) {
1491       LOG(ERROR) << "Failed to create temporary directory, test might not work "
1492                     "correctly";
1493     }
1494   }
1495   std::move(reply).Run(writable_directory_for_tests_.GetPath());
1496 }
1497 
1498 namespace {
1499 
1500 // A fake ui::SelectFileDialog, which will select a single pre-determined path.
1501 class FakeSelectFileDialog : public ui::SelectFileDialog {
1502  public:
FakeSelectFileDialog(base::FilePath result,Listener * listener,std::unique_ptr<ui::SelectFilePolicy> policy)1503   FakeSelectFileDialog(base::FilePath result,
1504                        Listener* listener,
1505                        std::unique_ptr<ui::SelectFilePolicy> policy)
1506       : ui::SelectFileDialog(listener, std::move(policy)),
1507         result_(std::move(result)) {}
1508 
1509  protected:
1510   ~FakeSelectFileDialog() override = default;
1511 
SelectFileImpl(Type type,const base::string16 & title,const base::FilePath & default_path,const FileTypeInfo * file_types,int file_type_index,const base::FilePath::StringType & default_extension,gfx::NativeWindow owning_window,void * params)1512   void SelectFileImpl(Type type,
1513                       const base::string16& title,
1514                       const base::FilePath& default_path,
1515                       const FileTypeInfo* file_types,
1516                       int file_type_index,
1517                       const base::FilePath::StringType& default_extension,
1518                       gfx::NativeWindow owning_window,
1519                       void* params) override {
1520     listener_->FileSelected(result_, 0, params);
1521   }
1522 
IsRunning(gfx::NativeWindow owning_window) const1523   bool IsRunning(gfx::NativeWindow owning_window) const override {
1524     return false;
1525   }
ListenerDestroyed()1526   void ListenerDestroyed() override {}
HasMultipleFileTypeChoicesImpl()1527   bool HasMultipleFileTypeChoicesImpl() override { return false; }
1528 
1529  private:
1530   base::FilePath result_;
1531 };
1532 
1533 class FakeSelectFileDialogFactory : public ui::SelectFileDialogFactory {
1534  public:
FakeSelectFileDialogFactory(base::FilePath result)1535   explicit FakeSelectFileDialogFactory(base::FilePath result)
1536       : result_(std::move(result)) {}
1537   ~FakeSelectFileDialogFactory() override = default;
1538 
Create(ui::SelectFileDialog::Listener * listener,std::unique_ptr<ui::SelectFilePolicy> policy)1539   ui::SelectFileDialog* Create(
1540       ui::SelectFileDialog::Listener* listener,
1541       std::unique_ptr<ui::SelectFilePolicy> policy) override {
1542     return new FakeSelectFileDialog(result_, listener, std::move(policy));
1543   }
1544 
1545  private:
1546   base::FilePath result_;
1547 };
1548 
1549 }  // namespace
1550 
SetFilePathForMockFileDialog(const base::FilePath & path)1551 void WebTestControlHost::SetFilePathForMockFileDialog(
1552     const base::FilePath& path) {
1553   ui::SelectFileDialog::SetFactory(new FakeSelectFileDialogFactory(path));
1554 }
1555 
FocusDevtoolsSecondaryWindow()1556 void WebTestControlHost::FocusDevtoolsSecondaryWindow() {
1557   CHECK(secondary_window_);
1558   // We don't go down the normal system path of focusing RenderWidgetHostView
1559   // because on mac headless, there are no system windows and that path does
1560   // not do anything. Instead we go through the Shell::ActivateContents() path
1561   // which knows how to perform the activation correctly on all platforms and in
1562   // headless mode.
1563   secondary_window_->ActivateContents(secondary_window_->web_contents());
1564 }
1565 
SetTrustTokenKeyCommitments(const std::string & raw_commitments,base::OnceClosure callback)1566 void WebTestControlHost::SetTrustTokenKeyCommitments(
1567     const std::string& raw_commitments,
1568     base::OnceClosure callback) {
1569   GetNetworkService()->SetTrustTokenKeyCommitments(raw_commitments,
1570                                                    std::move(callback));
1571 }
1572 
ClearTrustTokenState(base::OnceClosure callback)1573 void WebTestControlHost::ClearTrustTokenState(base::OnceClosure callback) {
1574   BrowserContext* browser_context =
1575       ShellContentBrowserClient::Get()->browser_context();
1576   StoragePartition* storage_partition =
1577       BrowserContext::GetStoragePartition(browser_context, nullptr);
1578   storage_partition->GetNetworkContext()->ClearTrustTokenData(
1579       nullptr,  // A wildcard filter.
1580       std::move(callback));
1581 }
1582 
SetDatabaseQuota(int32_t quota)1583 void WebTestControlHost::SetDatabaseQuota(int32_t quota) {
1584   auto run_on_io_thread = [](scoped_refptr<storage::QuotaManager> quota_manager,
1585                              int32_t quota) {
1586     DCHECK_CURRENTLY_ON(BrowserThread::IO);
1587     if (quota == kDefaultDatabaseQuota) {
1588       // Reset quota to settings with a zero refresh interval to force
1589       // QuotaManager to refresh settings immediately.
1590       storage::QuotaSettings default_settings;
1591       default_settings.refresh_interval = base::TimeDelta();
1592       quota_manager->SetQuotaSettings(default_settings);
1593     } else {
1594       DCHECK_GE(quota, 0);
1595       quota_manager->SetQuotaSettings(storage::GetHardCodedSettings(quota));
1596     }
1597   };
1598 
1599   BrowserContext* browser_context =
1600       ShellContentBrowserClient::Get()->browser_context();
1601   StoragePartition* storage_partition =
1602       BrowserContext::GetStoragePartition(browser_context, nullptr);
1603   scoped_refptr<storage::QuotaManager> quota_manager =
1604       base::WrapRefCounted(storage_partition->GetQuotaManager());
1605 
1606   content::GetIOThreadTaskRunner({})->PostTask(
1607       FROM_HERE,
1608       base::BindOnce(run_on_io_thread, std::move(quota_manager), quota));
1609 }
1610 
ClearAllDatabases()1611 void WebTestControlHost::ClearAllDatabases() {
1612   auto run_on_database_sequence =
1613       [](scoped_refptr<storage::DatabaseTracker> db_tracker) {
1614         DCHECK(db_tracker->task_runner()->RunsTasksInCurrentSequence());
1615         db_tracker->DeleteDataModifiedSince(base::Time(),
1616                                             net::CompletionOnceCallback());
1617       };
1618 
1619   BrowserContext* browser_context =
1620       ShellContentBrowserClient::Get()->browser_context();
1621   StoragePartition* storage_partition =
1622       BrowserContext::GetStoragePartition(browser_context, nullptr);
1623   scoped_refptr<storage::DatabaseTracker> db_tracker =
1624       base::WrapRefCounted(storage_partition->GetDatabaseTracker());
1625 
1626   base::SequencedTaskRunner* task_runner = db_tracker->task_runner();
1627   task_runner->PostTask(FROM_HERE, base::BindOnce(run_on_database_sequence,
1628                                                   std::move(db_tracker)));
1629 }
1630 
SimulateWebNotificationClick(const std::string & title,int32_t action_index,const base::Optional<base::string16> & reply)1631 void WebTestControlHost::SimulateWebNotificationClick(
1632     const std::string& title,
1633     int32_t action_index,
1634     const base::Optional<base::string16>& reply) {
1635   auto* client = WebTestContentBrowserClient::Get();
1636   auto* context = client->GetWebTestBrowserContext();
1637   auto* service = client->GetPlatformNotificationService(context);
1638   static_cast<MockPlatformNotificationService*>(service)->SimulateClick(
1639       title,
1640       action_index == std::numeric_limits<int32_t>::min()
1641           ? base::Optional<int>()
1642           : base::Optional<int>(action_index),
1643       reply);
1644 }
1645 
SimulateWebNotificationClose(const std::string & title,bool by_user)1646 void WebTestControlHost::SimulateWebNotificationClose(const std::string& title,
1647                                                       bool by_user) {
1648   auto* client = WebTestContentBrowserClient::Get();
1649   auto* context = client->GetWebTestBrowserContext();
1650   auto* service = client->GetPlatformNotificationService(context);
1651   static_cast<MockPlatformNotificationService*>(service)->SimulateClose(
1652       title, by_user);
1653 }
1654 
SimulateWebContentIndexDelete(const std::string & id)1655 void WebTestControlHost::SimulateWebContentIndexDelete(const std::string& id) {
1656   BrowserContext* browser_context =
1657       ShellContentBrowserClient::Get()->browser_context();
1658   auto* content_index_provider = static_cast<ShellContentIndexProvider*>(
1659       browser_context->GetContentIndexProvider());
1660 
1661   std::pair<int64_t, url::Origin> registration_data =
1662       content_index_provider->GetRegistrationDataFromId(id);
1663 
1664   StoragePartition* storage_partition =
1665       BrowserContext::GetStoragePartitionForSite(
1666           browser_context, registration_data.second.GetURL(),
1667           /*can_create=*/false);
1668   storage_partition->GetContentIndexContext()->OnUserDeletedItem(
1669       registration_data.first, registration_data.second, id);
1670 }
1671 
WebTestRuntimeFlagsChanged(base::Value changed_web_test_runtime_flags)1672 void WebTestControlHost::WebTestRuntimeFlagsChanged(
1673     base::Value changed_web_test_runtime_flags) {
1674   const int render_process_id = receiver_bindings_.current_context();
1675 
1676   // Stash the accumulated changes for future, not-yet-created renderers.
1677   accumulated_web_test_runtime_flags_changes_.MergeDictionary(
1678       &changed_web_test_runtime_flags);
1679 
1680   // Propagate the changes to all the tracked renderer processes.
1681   for (RenderProcessHost* process : all_observed_render_process_hosts_) {
1682     // Do not propagate the changes back to the process that originated
1683     // them. Propagating them back could also clobber subsequent changes in the
1684     // originator.
1685     if (process->GetID() == render_process_id)
1686       continue;
1687 
1688     GetWebTestRenderThreadRemote(process)->ReplicateWebTestRuntimeFlagsChanges(
1689         changed_web_test_runtime_flags.Clone());
1690   }
1691 }
1692 
RegisterIsolatedFileSystem(const std::vector<base::FilePath> & file_paths,RegisterIsolatedFileSystemCallback callback)1693 void WebTestControlHost::RegisterIsolatedFileSystem(
1694     const std::vector<base::FilePath>& file_paths,
1695     RegisterIsolatedFileSystemCallback callback) {
1696   const int render_process_id = receiver_bindings_.current_context();
1697 
1698   ChildProcessSecurityPolicy* policy =
1699       ChildProcessSecurityPolicy::GetInstance();
1700 
1701   storage::IsolatedContext::FileInfoSet file_info_set;
1702   for (auto& path : file_paths) {
1703     file_info_set.AddPath(path, nullptr);
1704     if (!policy->CanReadFile(render_process_id, path))
1705       policy->GrantReadFile(render_process_id, path);
1706   }
1707 
1708   std::string filesystem_id =
1709       storage::IsolatedContext::GetInstance()->RegisterDraggedFileSystem(
1710           file_info_set);
1711   policy->GrantReadFileSystem(render_process_id, filesystem_id);
1712 
1713   std::move(callback).Run(filesystem_id);
1714 }
1715 
DropPointerLock()1716 void WebTestControlHost::DropPointerLock() {
1717   main_window_->web_contents()->DropMouseLockForTesting();
1718 }
1719 
SetPointerLockWillFail()1720 void WebTestControlHost::SetPointerLockWillFail() {
1721   next_pointer_lock_action_ = NextPointerLockAction::kWillFail;
1722 }
1723 
SetPointerLockWillRespondAsynchronously()1724 void WebTestControlHost::SetPointerLockWillRespondAsynchronously() {
1725   next_pointer_lock_action_ = NextPointerLockAction::kTestWillRespond;
1726 }
1727 
AllowPointerLock()1728 void WebTestControlHost::AllowPointerLock() {
1729   DCHECK_EQ(next_pointer_lock_action_, NextPointerLockAction::kTestWillRespond);
1730   main_window_->web_contents()->GotResponseToLockMouseRequest(
1731       blink::mojom::PointerLockResult::kSuccess);
1732   next_pointer_lock_action_ = NextPointerLockAction::kWillSucceed;
1733 }
1734 
WorkItemAdded(mojom::WorkItemPtr work_item)1735 void WebTestControlHost::WorkItemAdded(mojom::WorkItemPtr work_item) {
1736   // TODO(peria): Check if |work_item| comes from the main window's main frame.
1737   // TODO(peria): Reject the item if the work queue is frozen.
1738   work_queue_.push_back(std::move(work_item));
1739 }
1740 
RequestWorkItem()1741 void WebTestControlHost::RequestWorkItem() {
1742   DCHECK(main_window_);
1743   RenderProcessHost* main_frame_process =
1744       main_window_->web_contents()->GetRenderViewHost()->GetProcess();
1745   if (work_queue_.empty()) {
1746     work_queue_states_.SetBoolPath(kDictKeyWorkQueueHasItems, false);
1747     GetWebTestRenderThreadRemote(main_frame_process)
1748         ->ReplicateWorkQueueStates(work_queue_states_.Clone());
1749   } else {
1750     GetWebTestRenderThreadRemote(main_frame_process)
1751         ->ProcessWorkItem(work_queue_.front()->Clone());
1752     work_queue_.pop_front();
1753   }
1754 }
1755 
WorkQueueStatesChanged(base::Value changed_work_queue_states)1756 void WebTestControlHost::WorkQueueStatesChanged(
1757     base::Value changed_work_queue_states) {
1758   work_queue_states_.MergeDictionary(&changed_work_queue_states);
1759 }
1760 
GoToOffset(int offset)1761 void WebTestControlHost::GoToOffset(int offset) {
1762   main_window_->GoBackOrForward(offset);
1763 }
1764 
Reload()1765 void WebTestControlHost::Reload() {
1766   main_window_->Reload();
1767 }
1768 
LoadURLForFrame(const GURL & url,const std::string & frame_name)1769 void WebTestControlHost::LoadURLForFrame(const GURL& url,
1770                                          const std::string& frame_name) {
1771   main_window_->LoadURLForFrame(url, frame_name, ui::PAGE_TRANSITION_LINK);
1772 }
1773 
SetMainWindowHidden(bool hidden)1774 void WebTestControlHost::SetMainWindowHidden(bool hidden) {
1775   if (hidden)
1776     main_window_->web_contents()->WasHidden();
1777   else
1778     main_window_->web_contents()->WasShown();
1779 }
1780 
CheckForLeakedWindows()1781 void WebTestControlHost::CheckForLeakedWindows() {
1782   check_for_leaked_windows_ = true;
1783 }
1784 
ResetRendererAfterWebTest()1785 void WebTestControlHost::ResetRendererAfterWebTest() {
1786   if (main_window_) {
1787     main_window_->web_contents()->Stop();
1788 
1789     RenderProcessHost* main_frame_process = main_window_->web_contents()
1790                                                 ->GetMainFrame()
1791                                                 ->GetRenderViewHost()
1792                                                 ->GetProcess();
1793     GetWebTestRenderThreadRemote(main_frame_process)
1794         ->ResetRendererAfterWebTest(
1795             base::BindOnce(&WebTestControlHost::ResetRendererAfterWebTestDone,
1796                            weak_factory_.GetWeakPtr()));
1797   } else {
1798     // If the window is gone, due to crashes or whatever, we need to make
1799     // progress.
1800     ResetRendererAfterWebTestDone();
1801   }
1802 }
1803 
ResetRendererAfterWebTestDone()1804 void WebTestControlHost::ResetRendererAfterWebTestDone() {
1805   if (leak_detector_ && main_window_) {
1806     // When doing leak detection, we don't want to count opened windows as
1807     // leaks, unless the test specifies that it expects to have closed them
1808     // all and wants to look for them as leaks.
1809     if (!check_for_leaked_windows_)
1810       CloseTestOpenedWindows();
1811 
1812     RenderViewHost* rvh =
1813         main_window_->web_contents()->GetMainFrame()->GetRenderViewHost();
1814     RenderProcessHost* rph = rvh->GetProcess();
1815     CHECK(rph->GetProcess().IsValid());
1816     leak_detector_->TryLeakDetection(
1817         rph,
1818         base::BindOnce(&WebTestControlHost::OnLeakDetectionDone,
1819                        weak_factory_.GetWeakPtr(), rph->GetProcess().Pid()));
1820     return;
1821   }
1822 
1823   Shell::QuitMainMessageLoopForTesting();
1824 }
1825 
OnLeakDetectionDone(int pid,const LeakDetector::LeakDetectionReport & report)1826 void WebTestControlHost::OnLeakDetectionDone(
1827     int pid,
1828     const LeakDetector::LeakDetectionReport& report) {
1829   if (report.leaked) {
1830     printer_->AddErrorMessage(base::StringPrintf("#LEAK - renderer pid %d (%s)",
1831                                                  pid, report.detail.c_str()));
1832     CHECK(!crash_when_leak_found_);
1833     DiscardMainWindow();
1834   }
1835 
1836   Shell::QuitMainMessageLoopForTesting();
1837 }
1838 
CloseTestOpenedWindows()1839 void WebTestControlHost::CloseTestOpenedWindows() {
1840   DevToolsAgentHost::DetachAllClients();
1841   std::vector<Shell*> open_windows(Shell::windows());
1842   for (auto* shell : open_windows) {
1843     if (shell != main_window_)
1844       shell->Close();
1845   }
1846   secondary_window_ = nullptr;
1847   base::RunLoop().RunUntilIdle();
1848 }
1849 
SetBluetoothManualChooser(bool enable)1850 void WebTestControlHost::SetBluetoothManualChooser(bool enable) {
1851   if (enable) {
1852     bluetooth_chooser_factory_ =
1853         std::make_unique<WebTestBluetoothChooserFactory>();
1854   } else {
1855     bluetooth_chooser_factory_.reset();
1856   }
1857 }
1858 
GetBluetoothManualChooserEvents(GetBluetoothManualChooserEventsCallback reply)1859 void WebTestControlHost::GetBluetoothManualChooserEvents(
1860     GetBluetoothManualChooserEventsCallback reply) {
1861   if (!bluetooth_chooser_factory_) {
1862     printer_->AddErrorMessage(
1863         "FAIL: Must call setBluetoothManualChooser before "
1864         "getBluetoothManualChooserEvents.");
1865     std::move(reply).Run({});
1866     return;
1867   }
1868   std::move(reply).Run(bluetooth_chooser_factory_->GetAndResetEvents());
1869 }
1870 
SendBluetoothManualChooserEvent(const std::string & event_name,const std::string & argument)1871 void WebTestControlHost::SendBluetoothManualChooserEvent(
1872     const std::string& event_name,
1873     const std::string& argument) {
1874   if (!bluetooth_chooser_factory_) {
1875     printer_->AddErrorMessage(
1876         "FAIL: Must call setBluetoothManualChooser before "
1877         "sendBluetoothManualChooserEvent.");
1878     return;
1879   }
1880   BluetoothChooserEvent event;
1881   if (event_name == "cancelled") {
1882     event = BluetoothChooserEvent::CANCELLED;
1883   } else if (event_name == "selected") {
1884     event = BluetoothChooserEvent::SELECTED;
1885   } else if (event_name == "rescan") {
1886     event = BluetoothChooserEvent::RESCAN;
1887   } else {
1888     printer_->AddErrorMessage(base::StringPrintf(
1889         "FAIL: Unexpected sendBluetoothManualChooserEvent() event name '%s'.",
1890         event_name.c_str()));
1891     return;
1892   }
1893   bluetooth_chooser_factory_->SendEvent(event, argument);
1894 }
1895 
BlockThirdPartyCookies(bool block)1896 void WebTestControlHost::BlockThirdPartyCookies(bool block) {
1897   ShellBrowserContext* browser_context =
1898       ShellContentBrowserClient::Get()->browser_context();
1899   StoragePartition* storage_partition =
1900       BrowserContext::GetStoragePartition(browser_context, nullptr);
1901   storage_partition->GetCookieManagerForBrowserProcess()
1902       ->BlockThirdPartyCookies(block);
1903 }
1904 
BindWebTestControlHostForRenderer(int render_process_id,mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver)1905 void WebTestControlHost::BindWebTestControlHostForRenderer(
1906     int render_process_id,
1907     mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver) {
1908   receiver_bindings_.Add(this, std::move(receiver), render_process_id);
1909 }
1910 
1911 mojo::AssociatedRemote<mojom::WebTestRenderFrame>&
GetWebTestRenderFrameRemote(RenderFrameHost * frame)1912 WebTestControlHost::GetWebTestRenderFrameRemote(RenderFrameHost* frame) {
1913   GlobalFrameRoutingId key(frame->GetProcess()->GetID(), frame->GetRoutingID());
1914   if (web_test_render_frame_map_.find(key) ==
1915       web_test_render_frame_map_.end()) {
1916     mojo::AssociatedRemote<mojom::WebTestRenderFrame>& new_ptr =
1917         web_test_render_frame_map_[key];
1918     frame->GetRemoteAssociatedInterfaces()->GetInterface(&new_ptr);
1919     new_ptr.set_disconnect_handler(
1920         base::BindOnce(&WebTestControlHost::HandleWebTestRenderFrameRemoteError,
1921                        weak_factory_.GetWeakPtr(), key));
1922   }
1923   DCHECK(web_test_render_frame_map_[key].get());
1924   return web_test_render_frame_map_[key];
1925 }
1926 
1927 mojo::AssociatedRemote<mojom::WebTestRenderThread>&
GetWebTestRenderThreadRemote(RenderProcessHost * process)1928 WebTestControlHost::GetWebTestRenderThreadRemote(RenderProcessHost* process) {
1929   if (web_test_render_thread_map_.find(process) ==
1930       web_test_render_thread_map_.end()) {
1931     IPC::ChannelProxy* channel = process->GetChannel();
1932     // channel might be null in tests.
1933     if (process->IsInitializedAndNotDead() && channel) {
1934       mojo::AssociatedRemote<mojom::WebTestRenderThread>& new_ptr =
1935           web_test_render_thread_map_[process];
1936       channel->GetRemoteAssociatedInterface(&new_ptr);
1937       new_ptr.set_disconnect_handler(base::BindOnce(
1938           &WebTestControlHost::HandleWebTestRenderThreadRemoteError,
1939           weak_factory_.GetWeakPtr(), process));
1940     }
1941   }
1942   DCHECK(web_test_render_thread_map_[process].get());
1943   return web_test_render_thread_map_[process];
1944 }
1945 
HandleWebTestRenderFrameRemoteError(const GlobalFrameRoutingId & key)1946 void WebTestControlHost::HandleWebTestRenderFrameRemoteError(
1947     const GlobalFrameRoutingId& key) {
1948   web_test_render_frame_map_.erase(key);
1949 }
1950 
HandleWebTestRenderThreadRemoteError(RenderProcessHost * key)1951 void WebTestControlHost::HandleWebTestRenderThreadRemoteError(
1952     RenderProcessHost* key) {
1953   web_test_render_thread_map_.erase(key);
1954 }
1955 
Node(RenderFrameHost * host)1956 WebTestControlHost::Node::Node(RenderFrameHost* host)
1957     : render_frame_host(host),
1958       render_frame_host_id(host->GetProcess()->GetID(), host->GetRoutingID()) {}
1959 
1960 WebTestControlHost::Node::Node(Node&& other) = default;
1961 WebTestControlHost::Node& WebTestControlHost::Node::operator=(Node&& other) =
1962     default;
1963 
1964 WebTestControlHost::Node::~Node() = default;
1965 
1966 }  // namespace content
1967