1 // Copyright 2014 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_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
6 #define CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
7 
8 // A collection of functions designed for use with content_shell based browser
9 // tests internal to the content/ module.
10 // Note: If a function here also works with browser_tests, it should be in
11 // the content public API.
12 
13 #include <memory>
14 #include <string>
15 #include <vector>
16 
17 #include "base/compiler_specific.h"
18 #include "base/files/file_path.h"
19 #include "base/macros.h"
20 #include "base/memory/weak_ptr.h"
21 #include "base/optional.h"
22 #include "base/run_loop.h"
23 #include "build/build_config.h"
24 #include "content/browser/bad_message.h"
25 #include "content/common/frame_messages.h"
26 #include "content/public/browser/devtools_agent_host.h"
27 #include "content/public/browser/javascript_dialog_manager.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/test/browser_test_utils.h"
30 #include "content/public/test/test_utils.h"
31 #include "mojo/public/cpp/bindings/pending_remote.h"
32 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom-forward.h"
33 #include "third_party/blink/public/mojom/choosers/popup_menu.mojom.h"
34 #include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
35 #include "url/gurl.h"
36 
37 namespace content {
38 
39 class FrameTreeNode;
40 class RenderFrameHost;
41 class RenderFrameHostImpl;
42 class RenderWidgetHostImpl;
43 class Shell;
44 class SiteInstance;
45 class ToRenderFrameHost;
46 
47 // Navigates the frame represented by |node| to |url|, blocking until the
48 // navigation finishes. Returns true if the navigation succeedd and the final
49 // URL matches |url|.
50 bool NavigateFrameToURL(FrameTreeNode* node, const GURL& url);
51 
52 // Sets the DialogManager to proceed by default or not when showing a
53 // BeforeUnload dialog, and if it proceeds, what value to return.
54 void SetShouldProceedOnBeforeUnload(Shell* shell, bool proceed, bool success);
55 
56 // Extends the ToRenderFrameHost mechanism to FrameTreeNodes.
57 RenderFrameHost* ConvertToRenderFrameHost(FrameTreeNode* frame_tree_node);
58 
59 // Helper function to navigate a window to a |url|, using a browser-initiated
60 // navigation that will stay in the same BrowsingInstance.  Most
61 // browser-initiated navigations swap BrowsingInstances, but some tests need a
62 // navigation to swap processes for cross-site URLs (even outside of
63 // --site-per-process) while staying in the same BrowsingInstance.
64 WARN_UNUSED_RESULT bool NavigateToURLInSameBrowsingInstance(Shell* window,
65                                                             const GURL& url);
66 
67 // Creates compact textual representations of the state of the frame tree that
68 // is appropriate for use in assertions.
69 //
70 // The diagrams show frame tree structure, the SiteInstance of current frames,
71 // presence of pending frames, and the SiteInstances of any and all proxies.
72 // They look like this:
73 //
74 //        Site A (D pending) -- proxies for B C
75 //          |--Site B --------- proxies for A C
76 //          +--Site C --------- proxies for B A
77 //               |--Site A ---- proxies for B
78 //               +--Site A ---- proxies for B
79 //                    +--Site A -- proxies for B
80 //       Where A = http://127.0.0.1/
81 //             B = http://foo.com/ (no process)
82 //             C = http://bar.com/
83 //             D = http://next.com/
84 //
85 // SiteInstances are assigned single-letter names (A, B, C) which are remembered
86 // across invocations of the pretty-printer.
87 class FrameTreeVisualizer {
88  public:
89   FrameTreeVisualizer();
90   ~FrameTreeVisualizer();
91 
92   // Formats and returns a diagram for the provided FrameTreeNode.
93   std::string DepictFrameTree(FrameTreeNode* root);
94 
95  private:
96   // Assign or retrive the abbreviated short name (A, B, C) for a site instance.
97   std::string GetName(SiteInstance* site_instance);
98 
99   // Elements are site instance ids. The index of the SiteInstance in the vector
100   // determines the abbreviated name (0->A, 1->B) for that SiteInstance.
101   std::vector<int> seen_site_instance_ids_;
102 
103   DISALLOW_COPY_AND_ASSIGN(FrameTreeVisualizer);
104 };
105 
106 // Uses window.open to open a popup from the frame |opener| with the specified
107 // |url|, |name| and window |features|. |expect_return_from_window_open| is used
108 // to indicate if the caller expects window.open() to return a non-null value.
109 // Waits for the navigation to |url| to finish and then returns the new popup's
110 // Shell.  Note that since this navigation to |url| is renderer-initiated, it
111 // won't cause a process swap unless used in --site-per-process mode.
112 Shell* OpenPopup(const ToRenderFrameHost& opener,
113                  const GURL& url,
114                  const std::string& name,
115                  const std::string& features,
116                  bool expect_return_from_window_open);
117 
118 // Same as above, but with an empty |features| and
119 // |expect_return_from_window_open| assumed to be true..
120 Shell* OpenPopup(const ToRenderFrameHost& opener,
121                  const GURL& url,
122                  const std::string& name);
123 
124 // Helper for mocking choosing a file via a file dialog.
125 class FileChooserDelegate : public WebContentsDelegate {
126  public:
127   // Constructs a WebContentsDelegate that mocks a file dialog.
128   // The mocked file dialog will always reply that the user selected |file|.
129   // |callback| is invoked when RunFileChooser() is called.
130   FileChooserDelegate(const base::FilePath& file, base::OnceClosure callback);
131   ~FileChooserDelegate() override;
132 
133   // Implementation of WebContentsDelegate::RunFileChooser.
134   void RunFileChooser(RenderFrameHost* render_frame_host,
135                       scoped_refptr<content::FileSelectListener> listener,
136                       const blink::mojom::FileChooserParams& params) override;
137 
138   // The params passed to RunFileChooser.
params()139   const blink::mojom::FileChooserParams& params() const { return *params_; }
140 
141  private:
142   base::FilePath file_;
143   base::OnceClosure callback_;
144   blink::mojom::FileChooserParamsPtr params_;
145 };
146 
147 // This class is a TestNavigationManager that only monitors notifications within
148 // the given frame tree node.
149 class FrameTestNavigationManager : public TestNavigationManager {
150  public:
151   FrameTestNavigationManager(int frame_tree_node_id,
152                              WebContents* web_contents,
153                              const GURL& url);
154 
155  private:
156   // TestNavigationManager:
157   bool ShouldMonitorNavigation(NavigationHandle* handle) override;
158 
159   // Notifications are filtered so only this frame is monitored.
160   int filtering_frame_tree_node_id_;
161 
162   DISALLOW_COPY_AND_ASSIGN(FrameTestNavigationManager);
163 };
164 
165 // An observer that can wait for a specific URL to be committed in a specific
166 // frame.
167 // Note: it does not track the start of a navigation, unlike other observers.
168 class UrlCommitObserver : WebContentsObserver {
169  public:
170   explicit UrlCommitObserver(FrameTreeNode* frame_tree_node, const GURL& url);
171   ~UrlCommitObserver() override;
172 
173   void Wait();
174 
175  private:
176   void DidFinishNavigation(NavigationHandle* navigation_handle) override;
177 
178   // The id of the FrameTreeNode in which navigations are peformed.
179   int frame_tree_node_id_;
180 
181   // The URL this observer is expecting to be committed.
182   GURL url_;
183 
184   // The RunLoop used to spin the message loop.
185   base::RunLoop run_loop_;
186 
187   DISALLOW_COPY_AND_ASSIGN(UrlCommitObserver);
188 };
189 
190 // Waits for a kill of the given RenderProcessHost and returns the
191 // BadMessageReason that caused a //content-triggerred kill.
192 //
193 // Example usage:
194 //   RenderProcessHostBadIpcMessageWaiter kill_waiter(render_process_host);
195 //   ... test code that triggers a renderer kill ...
196 //   EXPECT_EQ(bad_message::RFH_INVALID_ORIGIN_ON_COMMIT, kill_waiter.Wait());
197 //
198 // Tests that don't expect kills (e.g. tests where a renderer process exits
199 // normally, like RenderFrameHostManagerTest.ProcessExitWithSwappedOutViews)
200 // should use RenderProcessHostWatcher instead of
201 // RenderProcessHostBadIpcMessageWaiter.
202 class RenderProcessHostBadIpcMessageWaiter {
203  public:
204   explicit RenderProcessHostBadIpcMessageWaiter(
205       RenderProcessHost* render_process_host);
206 
207   // Waits until the renderer process exits.  Returns the bad message that made
208   // //content kill the renderer.  |base::nullopt| is returned if the renderer
209   // was killed outside of //content or exited normally.
210   base::Optional<bad_message::BadMessageReason> Wait() WARN_UNUSED_RESULT;
211 
212  private:
213   RenderProcessHostKillWaiter internal_waiter_;
214 
215   DISALLOW_COPY_AND_ASSIGN(RenderProcessHostBadIpcMessageWaiter);
216 };
217 
218 class ShowPopupWidgetWaiter
219     : public WebContentsObserver,
220       public blink::mojom::PopupWidgetHostInterceptorForTesting {
221  public:
222   ShowPopupWidgetWaiter(WebContents* web_contents,
223                         RenderFrameHostImpl* frame_host);
224   ~ShowPopupWidgetWaiter() override;
225 
last_initial_rect()226   gfx::Rect last_initial_rect() const { return initial_rect_; }
227 
last_routing_id()228   int last_routing_id() const { return routing_id_; }
229 
230   // Waits until a popup request is received.
231   void Wait();
232 
233   // Stops observing new messages.
234   void Stop();
235 
236  private:
237 
238   // WebContentsObserver:
239 #if defined(OS_MAC) || defined(OS_ANDROID)
240   bool ShowPopupMenu(
241       RenderFrameHost* render_frame_host,
242       mojo::PendingRemote<blink::mojom::PopupMenuClient>* popup_client,
243       const gfx::Rect& bounds,
244       int32_t item_height,
245       double font_size,
246       int32_t selected_item,
247       std::vector<blink::mojom::MenuItemPtr>* menu_items,
248       bool right_aligned,
249       bool allow_multiple_selection) override;
250 #endif
251 
252   // Callback bound for creating a popup widget.
253   void DidCreatePopupWidget(RenderWidgetHostImpl* render_widget_host);
254 
255   // blink::mojom::PopupWidgetHostInterceptorForTesting:
256   blink::mojom::PopupWidgetHost* GetForwardingInterface() override;
257   void ShowPopup(const gfx::Rect& initial_rect,
258                  ShowPopupCallback callback) override;
259 
260   base::RunLoop run_loop_;
261   gfx::Rect initial_rect_;
262   int32_t routing_id_ = MSG_ROUTING_NONE;
263   int32_t process_id_ = 0;
264   RenderFrameHostImpl* frame_host_;
265 
266   DISALLOW_COPY_AND_ASSIGN(ShowPopupWidgetWaiter);
267 };
268 
269 // A BrowserMessageFilter that drops a blacklisted message.
270 class DropMessageFilter : public BrowserMessageFilter {
271  public:
272   DropMessageFilter(uint32_t message_class, uint32_t drop_message_id);
273 
274  protected:
275   ~DropMessageFilter() override;
276 
277  private:
278   // BrowserMessageFilter:
279   bool OnMessageReceived(const IPC::Message& message) override;
280 
281   const uint32_t drop_message_id_;
282 
283   DISALLOW_COPY_AND_ASSIGN(DropMessageFilter);
284 };
285 
286 // A BrowserMessageFilter that observes a message without handling it, and
287 // reports when it was seen.
288 class ObserveMessageFilter : public BrowserMessageFilter {
289  public:
290   ObserveMessageFilter(uint32_t message_class, uint32_t watch_message_id);
291 
has_received_message()292   bool has_received_message() { return received_; }
293 
294   // Spins a RunLoop until the message is observed.
295   void Wait();
296 
297  protected:
298   ~ObserveMessageFilter() override;
299 
300   // BrowserMessageFilter:
301   bool OnMessageReceived(const IPC::Message& message) override;
302 
303  private:
304   void QuitWait();
305 
306   const uint32_t watch_message_id_;
307   bool received_ = false;
308   base::OnceClosure quit_closure_;
309 
310   DISALLOW_COPY_AND_ASSIGN(ObserveMessageFilter);
311 };
312 
313 // This observer waits until WebContentsObserver::OnRendererUnresponsive
314 // notification.
315 class UnresponsiveRendererObserver : public WebContentsObserver {
316  public:
317   explicit UnresponsiveRendererObserver(WebContents* web_contents);
318   ~UnresponsiveRendererObserver() override;
319 
320   RenderProcessHost* Wait(base::TimeDelta timeout = base::TimeDelta::Max());
321 
322  private:
323   // WebContentsObserver:
324   void OnRendererUnresponsive(RenderProcessHost* render_process_host) override;
325 
326   RenderProcessHost* captured_render_process_host_ = nullptr;
327   base::RunLoop run_loop_;
328 
329   DISALLOW_COPY_AND_ASSIGN(UnresponsiveRendererObserver);
330 };
331 
332 // Helper class that overrides the JavaScriptDialogManager of a WebContents
333 // to endlessly block on beforeunload.
334 class BeforeUnloadBlockingDelegate : public JavaScriptDialogManager,
335                                      public WebContentsDelegate {
336  public:
337   explicit BeforeUnloadBlockingDelegate(WebContentsImpl* web_contents);
338   ~BeforeUnloadBlockingDelegate() override;
339   void Wait();
340 
341   // WebContentsDelegate
342 
343   JavaScriptDialogManager* GetJavaScriptDialogManager(
344       WebContents* source) override;
345 
346   // JavaScriptDialogManager
347 
348   void RunJavaScriptDialog(WebContents* web_contents,
349                            RenderFrameHost* render_frame_host,
350                            JavaScriptDialogType dialog_type,
351                            const base::string16& message_text,
352                            const base::string16& default_prompt_text,
353                            DialogClosedCallback callback,
354                            bool* did_suppress_message) override;
355 
356   void RunBeforeUnloadDialog(WebContents* web_contents,
357                              RenderFrameHost* render_frame_host,
358                              bool is_reload,
359                              DialogClosedCallback callback) override;
360 
361   bool HandleJavaScriptDialog(WebContents* web_contents,
362                               bool accept,
363                               const base::string16* prompt_override) override;
364 
CancelDialogs(WebContents * web_contents,bool reset_state)365   void CancelDialogs(WebContents* web_contents, bool reset_state) override {}
366 
367  private:
368   WebContentsImpl* web_contents_;
369 
370   DialogClosedCallback callback_;
371 
372   std::unique_ptr<base::RunLoop> run_loop_ = std::make_unique<base::RunLoop>();
373 
374   DISALLOW_COPY_AND_ASSIGN(BeforeUnloadBlockingDelegate);
375 };
376 
377 // A helper class to get DevTools inspector log messages (e.g. network errors).
378 class DevToolsInspectorLogWatcher : public DevToolsAgentHostClient {
379  public:
380   explicit DevToolsInspectorLogWatcher(WebContents* web_contents);
381   ~DevToolsInspectorLogWatcher() override;
382 
383   void FlushAndStopWatching();
last_message()384   std::string last_message() { return last_message_; }
385 
386   // DevToolsAgentHostClient:
387   void DispatchProtocolMessage(DevToolsAgentHost* host,
388                                base::span<const uint8_t> message) override;
389   void AgentHostClosed(DevToolsAgentHost* host) override;
390 
391  private:
392   scoped_refptr<DevToolsAgentHost> host_;
393   base::RunLoop run_loop_enable_log_;
394   base::RunLoop run_loop_disable_log_;
395   std::string last_message_;
396 };
397 
398 }  // namespace content
399 
400 #endif  // CONTENT_TEST_CONTENT_BROWSER_TEST_UTILS_INTERNAL_H_
401