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 #ifndef CONTENT_WEB_TEST_BROWSER_WEB_TEST_CONTROL_HOST_H_
6 #define CONTENT_WEB_TEST_BROWSER_WEB_TEST_CONTROL_HOST_H_
7 
8 #include <map>
9 #include <memory>
10 #include <ostream>
11 #include <set>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/cancelable_callback.h"
17 #include "base/files/file_path.h"
18 #include "base/files/scoped_temp_dir.h"
19 #include "base/macros.h"
20 #include "base/memory/weak_ptr.h"
21 #include "base/scoped_observer.h"
22 #include "base/sequence_checker.h"
23 #include "base/synchronization/lock.h"
24 #include "base/values.h"
25 #include "build/build_config.h"
26 #include "content/public/browser/bluetooth_chooser.h"
27 #include "content/public/browser/global_routing_id.h"
28 #include "content/public/browser/gpu_data_manager_observer.h"
29 #include "content/public/browser/notification_observer.h"
30 #include "content/public/browser/notification_registrar.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "content/public/browser/render_process_host_observer.h"
33 #include "content/public/browser/web_contents_observer.h"
34 #include "content/web_test/browser/leak_detector.h"
35 #include "content/web_test/common/web_test.mojom.h"
36 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
37 #include "mojo/public/cpp/bindings/associated_remote.h"
38 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
39 #include "ui/gfx/geometry/size.h"
40 
41 class SkBitmap;
42 
43 namespace content {
44 class DevToolsProtocolTestBindings;
45 class RenderFrameHost;
46 class Shell;
47 class WebTestBluetoothChooserFactory;
48 class WebTestDevToolsBindings;
49 struct TestInfo;
50 
51 class WebTestResultPrinter {
52  public:
53   WebTestResultPrinter(std::ostream* output, std::ostream* error);
54   ~WebTestResultPrinter() = default;
55 
reset()56   void reset() { state_ = DURING_TEST; }
output_finished()57   bool output_finished() const { return state_ == AFTER_TEST; }
set_capture_text_only(bool capture_text_only)58   void set_capture_text_only(bool capture_text_only) {
59     capture_text_only_ = capture_text_only;
60   }
61 
set_encode_binary_data(bool encode_binary_data)62   void set_encode_binary_data(bool encode_binary_data) {
63     encode_binary_data_ = encode_binary_data;
64   }
65 
66   void PrintTextHeader();
67   void PrintTextBlock(const std::string& block);
68   void PrintTextFooter();
69 
70   void PrintImageHeader(const std::string& actual_hash,
71                         const std::string& expected_hash);
72   void PrintImageBlock(const std::vector<unsigned char>& png_image);
73   void PrintImageFooter();
74 
75   void PrintAudioHeader();
76   void PrintAudioBlock(const std::vector<unsigned char>& audio_data);
77   void PrintAudioFooter();
78 
79   void AddMessageToStderr(const std::string& message);
80   void AddMessage(const std::string& message);
81   void AddMessageRaw(const std::string& message);
82   void AddErrorMessage(const std::string& message);
83 
84   void CloseStderr();
85   void StartStateDump();
86 
87  private:
88   void PrintEncodedBinaryData(const std::vector<unsigned char>& data);
89 
90   enum State {
91     DURING_TEST,
92     DURING_STATE_DUMP,
93     IN_TEXT_BLOCK,
94     IN_AUDIO_BLOCK,
95     IN_IMAGE_BLOCK,
96     AFTER_TEST
97   };
98   State state_;
99 
100   bool capture_text_only_;
101   bool encode_binary_data_;
102 
103   std::ostream* output_;
104   std::ostream* error_;
105 
106   DISALLOW_COPY_AND_ASSIGN(WebTestResultPrinter);
107 };
108 
109 class WebTestControlHost : public WebContentsObserver,
110                            public RenderProcessHostObserver,
111                            public GpuDataManagerObserver,
112                            public mojom::WebTestControlHost {
113  public:
114   static WebTestControlHost* Get();
115 
116   WebTestControlHost();
117   ~WebTestControlHost() override;
118 
119   WebTestControlHost(const WebTestControlHost&) = delete;
120   WebTestControlHost& operator=(const WebTestControlHost&) = delete;
121 
122   // True if the controller is ready for testing.
123   bool PrepareForWebTest(const TestInfo& test_info);
124   // True if the controller was reset successfully.
125   bool ResetBrowserAfterWebTest();
126 
127   // Allows WebTestControlHost to track all WebContents created by tests, either
128   // by Javascript or by C++ code in the browser.
129   void DidCreateOrAttachWebContents(WebContents* web_contents);
130 
131   void SetTempPath(const base::FilePath& temp_path);
132   void OverrideWebkitPrefs(blink::web_pref::WebPreferences* prefs);
133   void OpenURL(const GURL& url);
134   bool IsMainWindow(WebContents* web_contents) const;
135   std::unique_ptr<BluetoothChooser> RunBluetoothChooser(
136       RenderFrameHost* frame,
137       const BluetoothChooser::EventHandler& event_handler);
138   void RequestToLockMouse(WebContents* web_contents);
139 
printer()140   WebTestResultPrinter* printer() { return printer_.get(); }
set_printer(WebTestResultPrinter * printer)141   void set_printer(WebTestResultPrinter* printer) { printer_.reset(printer); }
142 
143   void DevToolsProcessCrashed();
144 
145   // Called when a renderer wants to bind a connection to the
146   // WebTestControlHost.
147   void BindWebTestControlHostForRenderer(
148       int render_process_id,
149       mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver);
150 
151   // WebContentsObserver implementation.
152   void PluginCrashed(const base::FilePath& plugin_path,
153                      base::ProcessId plugin_pid) override;
154   void TitleWasSet(NavigationEntry* entry) override;
155   void DidFailLoad(RenderFrameHost* render_frame_host,
156                    const GURL& validated_url,
157                    int error_code) override;
158   void WebContentsDestroyed() override;
159   void DidUpdateFaviconURL(
160       RenderFrameHost* render_frame_host,
161       const std::vector<blink::mojom::FaviconURLPtr>& candidates) override;
162   void RenderViewHostChanged(RenderViewHost* old_host,
163                              RenderViewHost* new_host) override;
164   void RenderViewDeleted(RenderViewHost* render_view_host) override;
165   void DidFinishNavigation(NavigationHandle* navigation_handle) override;
166 
167   // RenderProcessHostObserver implementation.
168   void RenderProcessHostDestroyed(
169       RenderProcessHost* render_process_host) override;
170   void RenderProcessExited(RenderProcessHost* render_process_host,
171                            const ChildProcessTerminationInfo& info) override;
172 
173   // GpuDataManagerObserver implementation.
174   void OnGpuProcessCrashed(base::TerminationStatus exit_code) override;
175 
accumulated_web_test_runtime_flags_changes()176   const base::DictionaryValue& accumulated_web_test_runtime_flags_changes()
177       const {
178     return accumulated_web_test_runtime_flags_changes_;
179   }
180 
181  private:
182   enum TestPhase { BETWEEN_TESTS, DURING_TEST, CLEAN_UP };
183 
184   // Node structure to construct a RenderFrameHost tree.
185   struct Node {
186     explicit Node(RenderFrameHost* host);
187     ~Node();
188 
189     Node(Node&& other);
190     Node& operator=(Node&& other);
191 
192     RenderFrameHost* render_frame_host = nullptr;
193     GlobalFrameRoutingId render_frame_host_id;
194     std::vector<Node*> children;
195   };
196 
197   class WebTestWindowObserver;
198 
199   static WebTestControlHost* instance_;
200 
201   // WebTestControlHost implementation.
202   void InitiateCaptureDump(
203       mojom::WebTestRendererDumpResultPtr renderer_dump_result,
204       bool capture_navigation_history,
205       bool capture_pixels) override;
206   void TestFinishedInSecondaryRenderer() override;
207   void PrintMessageToStderr(const std::string& message) override;
208   void PrintMessage(const std::string& message) override;
209   void Reload() override;
210   void OverridePreferences(
211       const blink::web_pref::WebPreferences& web_preferences) override;
212   void SetMainWindowHidden(bool hidden) override;
213   void CheckForLeakedWindows() override;
214   void GoToOffset(int offset) override;
215   void SendBluetoothManualChooserEvent(const std::string& event,
216                                        const std::string& argument) override;
217   void SetBluetoothManualChooser(bool enable) override;
218   void GetBluetoothManualChooserEvents(
219       GetBluetoothManualChooserEventsCallback reply) override;
220   void SetPopupBlockingEnabled(bool block_popups) override;
221   void LoadURLForFrame(const GURL& url, const std::string& frame_name) override;
222   void SetScreenOrientationChanged() override;
223   void SetPermission(const std::string& name,
224                      blink::mojom::PermissionStatus status,
225                      const GURL& origin,
226                      const GURL& embedding_origin) override;
227   void BlockThirdPartyCookies(bool block) override;
228   void GetWritableDirectory(GetWritableDirectoryCallback reply) override;
229   void SetFilePathForMockFileDialog(const base::FilePath& path) override;
230   void FocusDevtoolsSecondaryWindow() override;
231   void SetTrustTokenKeyCommitments(const std::string& raw_commitments,
232                                    base::OnceClosure callback) override;
233   void ClearTrustTokenState(base::OnceClosure callback) override;
234   void SetDatabaseQuota(int32_t quota) override;
235   void ClearAllDatabases() override;
236   void SimulateWebNotificationClick(
237       const std::string& title,
238       int32_t action_index,
239       const base::Optional<base::string16>& reply) override;
240   void SimulateWebNotificationClose(const std::string& title,
241                                     bool by_user) override;
242   void SimulateWebContentIndexDelete(const std::string& id) override;
243   void WebTestRuntimeFlagsChanged(
244       base::Value changed_web_test_runtime_flags) override;
245   void RegisterIsolatedFileSystem(
246       const std::vector<base::FilePath>& file_paths,
247       RegisterIsolatedFileSystemCallback callback) override;
248   void DropPointerLock() override;
249   void SetPointerLockWillFail() override;
250   void SetPointerLockWillRespondAsynchronously() override;
251   void AllowPointerLock() override;
252   void WorkItemAdded(mojom::WorkItemPtr work_item) override;
253   void RequestWorkItem() override;
254   void WorkQueueStatesChanged(base::Value changed_work_queue_states) override;
255 
256   void DiscardMainWindow();
257   void CloseTestOpenedWindows();
258 
259   // Makes sure that the potentially new renderer associated with |frame| is 1)
260   // initialized for the test, 2) kept up to date wrt test flags and 3)
261   // monitored for crashes.
262   void HandleNewRenderFrameHost(RenderFrameHost* frame);
263 
264   // Message handlers.
265   void OnAudioDump(const std::vector<unsigned char>& audio_dump);
266   void OnImageDump(const std::string& actual_pixel_hash, const SkBitmap& image);
267   void OnTextDump(const std::string& dump);
268   void OnDumpFrameLayoutResponse(int frame_tree_node_id,
269                                  const std::string& dump);
270   void OnTestFinished();
271   void OnCaptureSessionHistory();
272   void OnLeakDetectionDone(int pid,
273                            const LeakDetector::LeakDetectionReport& report);
274 
275   // At the end of the test, once browser-side cleanup is done, commence reset
276   // of the renderer process that will stick around.
277   void ResetRendererAfterWebTest();
278   // Callback for when the renderer completes its reset at the end of the test.
279   void ResetRendererAfterWebTestDone();
280   void OnPixelDumpCaptured(const SkBitmap& snapshot);
281   void ReportResults();
282   void EnqueueSurfaceCopyRequest();
283 
284   mojo::AssociatedRemote<mojom::WebTestRenderFrame>&
285   GetWebTestRenderFrameRemote(RenderFrameHost* frame);
286   mojo::AssociatedRemote<mojom::WebTestRenderThread>&
287   GetWebTestRenderThreadRemote(RenderProcessHost* process);
288   void HandleWebTestRenderFrameRemoteError(const GlobalFrameRoutingId& key);
289   void HandleWebTestRenderThreadRemoteError(RenderProcessHost* key);
290 
291   // CompositeAllFramesThen() first builds a frame tree based on
292   // frame->GetParent(). Then, it builds a queue of frames in depth-first order,
293   // so that compositing happens from the leaves up. Finally,
294   // CompositeNodeQueueThen() is used to composite one frame at a time,
295   // asynchronously, continuing on to the next frame once each composite
296   // finishes. Once all nodes have been composited, the final callback is run.
297   // Each call to CompositeWithRaster() is an asynchronous Mojo call, to avoid
298   // reentrancy problems.
299   void CompositeAllFramesThen(base::OnceCallback<void()> callback);
300 
301  private:
302   Node* BuildFrameTree(WebContents* web_contents);
303   void CompositeNodeQueueThen(base::OnceCallback<void()> callback);
304   void BuildDepthFirstQueue(Node* node);
305 
306 #if defined(OS_MAC)
307   // Bypasses system APIs to force a resize on the RenderWidgetHostView when in
308   // headless web tests.
309   static void PlatformResizeWindowMac(Shell* shell, const gfx::Size& size);
310 #endif
311 
312  public:
313   std::unique_ptr<WebTestResultPrinter> printer_;
314 
315   base::FilePath current_working_directory_;
316   base::FilePath temp_path_;
317 
318   Shell* main_window_;
319   Shell* secondary_window_;
320 
321   std::unique_ptr<WebTestDevToolsBindings> devtools_bindings_;
322   std::unique_ptr<DevToolsProtocolTestBindings>
323       devtools_protocol_test_bindings_;
324 
325   // What phase of running an individual test we are currently in.
326   TestPhase test_phase_;
327 
328   // Per test config.
329   std::string expected_pixel_hash_;
330   GURL test_url_;
331   bool protocol_mode_;
332 
333   // Stores the default test-adapted WebPreferences which is then used to fully
334   // reset the main window's preferences if and when it is reused.
335   blink::web_pref::WebPreferences default_prefs_;
336 
337   // True if the WebPreferences of newly created RenderViewHost should be
338   // overridden with prefs_.
339   bool should_override_prefs_;
340   blink::web_pref::WebPreferences prefs_;
341 
342   bool crash_when_leak_found_;
343   std::unique_ptr<LeakDetector> leak_detector_;
344 
345   std::unique_ptr<WebTestBluetoothChooserFactory> bluetooth_chooser_factory_;
346 
347   // Observe windows opened by tests.
348   base::flat_map<WebContents*, std::unique_ptr<WebTestWindowObserver>>
349       test_opened_window_observers_;
350 
351   // Renderer processes are observed to detect crashes.
352   ScopedObserver<RenderProcessHost, RenderProcessHostObserver>
353       render_process_host_observer_{this};
354   std::set<RenderProcessHost*> all_observed_render_process_hosts_;
355   std::set<RenderProcessHost*> main_window_render_process_hosts_;
356   std::set<RenderViewHost*> main_window_render_view_hosts_;
357 
358   // Changes reported by WebTestRuntimeFlagsChanged() that have accumulated
359   // since PrepareForWebTest (i.e. changes that need to be sent to a fresh
360   // renderer created while test is in progress).
361   base::DictionaryValue accumulated_web_test_runtime_flags_changes_;
362 
363   // Work items to be processed in the TestRunner on the renderer process
364   // that hosts the main window's main frame.
365   base::circular_deque<mojom::WorkItemPtr> work_queue_;
366 
367   // Properties of the work queue.
368   base::DictionaryValue work_queue_states_;
369 
370   mojom::WebTestRendererDumpResultPtr renderer_dump_result_;
371   std::string navigation_history_dump_;
372   base::Optional<SkBitmap> pixel_dump_;
373   base::Optional<std::string> layout_dump_;
374   std::string actual_pixel_hash_;
375   // By default a test that opens other windows will have them closed at the end
376   // of the test before checking for leaks. It may specify that it has closed
377   // any windows it opened, and thus look for leaks from them with this flag.
378   bool check_for_leaked_windows_ = false;
379   bool waiting_for_pixel_results_ = false;
380   int waiting_for_layout_dumps_ = 0;
381 
382   // Map from frame_tree_node_id into frame-specific dumps while collecting
383   // text dumps from all frames, before stitching them together.
384   std::map<int, std::string> frame_to_layout_dump_map_;
385 
386   std::vector<std::unique_ptr<Node>> composite_all_frames_node_storage_;
387   std::queue<Node*> composite_all_frames_node_queue_;
388 
389   // Map from one frame to one mojo pipe.
390   std::map<GlobalFrameRoutingId,
391            mojo::AssociatedRemote<mojom::WebTestRenderFrame>>
392       web_test_render_frame_map_;
393 
394   std::map<RenderProcessHost*,
395            mojo::AssociatedRemote<mojom::WebTestRenderThread>>
396       web_test_render_thread_map_;
397 
398   // The set of bindings that receive messages on the mojom::WebTestControlHost
399   // interface from renderer processes. There should be one per renderer
400   // process, and we store it with the |render_process_id| attached to it
401   // so that we can tell where a given message came from if needed.
402   mojo::AssociatedReceiverSet<mojom::WebTestControlHost,
403                               int /*render_process_id*/>
404       receiver_bindings_;
405 
406   base::ScopedTempDir writable_directory_for_tests_;
407 
408   enum class NextPointerLockAction {
409     kWillSucceed,
410     kTestWillRespond,
411     kWillFail,
412   };
413   NextPointerLockAction next_pointer_lock_action_ =
414       NextPointerLockAction::kWillSucceed;
415 
416   SEQUENCE_CHECKER(sequence_checker_);
417 
418   base::WeakPtrFactory<WebTestControlHost> weak_factory_{this};
419 };
420 
421 }  // namespace content
422 
423 #endif  // CONTENT_WEB_TEST_BROWSER_WEB_TEST_CONTROL_HOST_H_
424