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 #include "base/base_paths.h"
6 #include "base/bind.h"
7 #include "base/files/file_util.h"
8 #include "base/location.h"
9 #include "base/macros.h"
10 #include "base/path_service.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "build/build_config.h"
18 #include "chrome/browser/extensions/extension_apitest.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "components/guest_view/browser/test_guest_view_manager.h"
22 #include "components/javascript_dialogs/app_modal_dialog_controller.h"
23 #include "components/javascript_dialogs/app_modal_dialog_view.h"
24 #include "components/printing/common/print_messages.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/navigation_handle.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/common/content_features.h"
29 #include "content/public/common/content_switches.h"
30 #include "content/public/test/browser_test_utils.h"
31 #include "content/public/test/test_renderer_host.h"
32 #include "extensions/browser/api/extensions_api_client.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/guest_view/extensions_guest_view_manager_delegate.h"
35 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
36 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
37 #include "extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h"
38 #include "extensions/browser/process_manager.h"
39 #include "extensions/common/guest_view/extensions_guest_view_messages.h"
40 #include "extensions/common/mojom/guest_view.mojom.h"
41 #include "extensions/test/result_catcher.h"
42 #include "mojo/public/cpp/bindings/associated_remote.h"
43 #include "net/dns/mock_host_resolver.h"
44 #include "net/test/embedded_test_server/embedded_test_server.h"
45 #include "net/test/embedded_test_server/http_request.h"
46 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
47 #include "ui/base/ui_base_features.h"
48 #include "url/url_constants.h"
49 
50 using extensions::ExtensionsAPIClient;
51 using extensions::MimeHandlerStreamManager;
52 using extensions::MimeHandlerViewGuest;
53 using extensions::TestMimeHandlerViewGuest;
54 using guest_view::GuestViewManager;
55 using guest_view::GuestViewManagerDelegate;
56 using guest_view::TestGuestViewManager;
57 using guest_view::TestGuestViewManagerFactory;
58 
59 // The test extension id is set by the key value in the manifest.
60 const char kExtensionId[] = "oickdpebdnfbgkcaoklfcdhjniefkcji";
61 
62 class MimeHandlerViewTest : public extensions::ExtensionApiTest {
63  public:
MimeHandlerViewTest()64   MimeHandlerViewTest() {
65     GuestViewManager::set_factory_for_testing(&factory_);
66   }
67 
~MimeHandlerViewTest()68   ~MimeHandlerViewTest() override {}
69 
SetUpOnMainThread()70   void SetUpOnMainThread() override {
71     extensions::ExtensionApiTest::SetUpOnMainThread();
72 
73     embedded_test_server()->ServeFilesFromDirectory(
74         test_data_dir_.AppendASCII("mime_handler_view"));
75     embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
76         &MimeHandlerViewTest::MonitorRequest, base::Unretained(this)));
77     host_resolver()->AddRule("*", "127.0.0.1");
78     ASSERT_TRUE(StartEmbeddedTestServer());
79   }
80 
81   // TODO(paulmeyer): This function is implemented over and over by the
82   // different GuestView test classes. It really needs to be refactored out to
83   // some kind of GuestViewTest base class.
GetGuestViewManager() const84   TestGuestViewManager* GetGuestViewManager() const {
85     TestGuestViewManager* manager = static_cast<TestGuestViewManager*>(
86         TestGuestViewManager::FromBrowserContext(browser()->profile()));
87     // TestGuestViewManager::WaitForSingleGuestCreated can and will get called
88     // before a guest is created. Since GuestViewManager is usually not created
89     // until the first guest is created, this means that |manager| will be
90     // nullptr if trying to use the manager to wait for the first guest. Because
91     // of this, the manager must be created here if it does not already exist.
92     if (!manager) {
93       manager = static_cast<TestGuestViewManager*>(
94           GuestViewManager::CreateWithDelegate(
95               browser()->profile(),
96               ExtensionsAPIClient::Get()->CreateGuestViewManagerDelegate(
97                   browser()->profile())));
98     }
99     return manager;
100   }
101 
GetEmbedderWebContents()102   content::WebContents* GetEmbedderWebContents() {
103     return browser()->tab_strip_model()->GetWebContentsAt(0);
104   }
105 
GetLastGuestView() const106   MimeHandlerViewGuest* GetLastGuestView() const {
107     return MimeHandlerViewGuest::FromWebContents(
108                GetGuestViewManager()->GetLastGuestCreated())
109         ->As<MimeHandlerViewGuest>();
110   }
111 
LoadTestExtension()112   const extensions::Extension* LoadTestExtension() {
113     const extensions::Extension* extension =
114         LoadExtension(test_data_dir_.AppendASCII("mime_handler_view"));
115     if (!extension)
116       return nullptr;
117 
118     CHECK_EQ(std::string(kExtensionId), extension->id());
119 
120     return extension;
121   }
122 
RunTestWithUrl(const GURL & url)123   void RunTestWithUrl(const GURL& url) {
124     // Use the testing subclass of MimeHandlerViewGuest.
125     GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
126         base::Bind(&TestMimeHandlerViewGuest::Create));
127 
128     const extensions::Extension* extension = LoadTestExtension();
129     ASSERT_TRUE(extension);
130 
131     extensions::ResultCatcher catcher;
132 
133     ui_test_utils::NavigateToURL(browser(), url);
134 
135     if (!catcher.GetNextResult())
136       FAIL() << catcher.message();
137   }
138 
RunTest(const std::string & path)139   void RunTest(const std::string& path) {
140     RunTestWithUrl(embedded_test_server()->GetURL("/" + path));
141   }
142 
basic_count() const143   int basic_count() const { return basic_count_; }
144 
145  private:
MonitorRequest(const net::test_server::HttpRequest & request)146   void MonitorRequest(const net::test_server::HttpRequest& request) {
147     if (request.relative_url == "/testBasic.csv")
148       basic_count_++;
149   }
150 
151   TestGuestViewManagerFactory factory_;
152   base::test::ScopedFeatureList scoped_feature_list_;
153   int basic_count_ = 0;
154 };
155 
156 class UserActivationUpdateWaiter {
157  public:
UserActivationUpdateWaiter(content::WebContents * web_contents)158   explicit UserActivationUpdateWaiter(content::WebContents* web_contents) {
159     user_activation_interceptor_.Init(web_contents->GetMainFrame());
160   }
161   ~UserActivationUpdateWaiter() = default;
162 
Wait()163   void Wait() {
164     if (user_activation_interceptor_.update_user_activation_state())
165       return;
166     base::RunLoop run_loop;
167     user_activation_interceptor_.set_quit_handler(run_loop.QuitClosure());
168     run_loop.Run();
169   }
170 
171  private:
172   content::UpdateUserActivationStateInterceptor user_activation_interceptor_;
173 };
174 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,Embedded)175 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Embedded) {
176   RunTest("test_embedded.html");
177   // Sanity check. Navigate the page and verify the guest goes away.
178   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
179   auto* gv_manager = GetGuestViewManager();
180   gv_manager->WaitForAllGuestsDeleted();
181   EXPECT_EQ(1U, gv_manager->num_guests_created());
182 }
183 
184 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
185 class PrintPreviewWaiter : public content::BrowserMessageFilter {
186  public:
PrintPreviewWaiter()187   PrintPreviewWaiter() : BrowserMessageFilter(PrintMsgStart) {}
188 
OnMessageReceived(const IPC::Message & message)189   bool OnMessageReceived(const IPC::Message& message) override {
190     IPC_BEGIN_MESSAGE_MAP(PrintPreviewWaiter, message)
191       IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview)
192     IPC_END_MESSAGE_MAP()
193     return false;
194   }
195 
OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params & params,const PrintHostMsg_PreviewIds & ids)196   void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
197                          const PrintHostMsg_PreviewIds& ids) {
198     // Expect that there is at least one page.
199     did_load_ = true;
200     run_loop_.Quit();
201 
202     EXPECT_TRUE(params.page_count >= 1);
203   }
204 
Wait()205   void Wait() {
206     if (!did_load_)
207       run_loop_.Run();
208   }
209 
210  private:
211   ~PrintPreviewWaiter() override = default;
212 
213   bool did_load_ = false;
214   base::RunLoop run_loop_;
215 };
216 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbeddedThenPrint)217 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedThenPrint) {
218   RunTest("test_embedded.html");
219   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
220   auto* gv_manager = GetGuestViewManager();
221   gv_manager->WaitForAllGuestsDeleted();
222   EXPECT_EQ(1U, gv_manager->num_guests_created());
223 
224   // Verify that print dialog comes up.
225   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
226   auto* main_frame = web_contents->GetMainFrame();
227   auto print_preview_waiter = base::MakeRefCounted<PrintPreviewWaiter>();
228   web_contents->GetMainFrame()->GetProcess()->AddFilter(
229       print_preview_waiter.get());
230   // Use setTimeout() to prevent ExecuteScript() from blocking on the print
231   // dialog.
232   ASSERT_TRUE(content::ExecuteScript(
233       main_frame, "setTimeout(function() { window.print(); }, 0)"));
234   print_preview_waiter->Wait();
235 }
236 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
237 
238 // This test start with an <object> that has a content frame. Then the content
239 // frame (plugin frame) is navigated to a cross-origin target page. After the
240 // navigation is completed, the <object> is set to render MimeHandlerView by
241 // setting its |data| and |type| attributes accordingly.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbedWithInitialCrossOriginFrame)242 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbedWithInitialCrossOriginFrame) {
243   const std::string kTestName = "test_cross_origin_frame";
244   std::string cross_origin_url =
245       embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
246   auto test_url = embedded_test_server()->GetURL(
247       "a.com",
248       base::StringPrintf("/test_object_with_frame.html?test_data=%s,%s,%s",
249                          kTestName.c_str(), cross_origin_url.c_str(),
250                          "testEmbedded.csv"));
251   RunTestWithUrl(test_url);
252 }
253 
254 // This test verifies that navigations on the plugin frame before setting it to
255 // load MimeHandlerView do not race with the creation of the guest. The test
256 // loads a page with an <object> which is first navigated to some cross-origin
257 // domain and then immediately after load, the page triggers a navigation of its
258 // own to another cross-origin domain. Meanwhile the embedder sets the <object>
259 // to load a MimeHandlerView. The test passes if MHV loads. This is to catch the
260 // potential race between the cross-origin renderer initiated navigation and
261 // the navigation to "about:blank" started from the browser.
262 //
263 // Disabled due to flakiness: https://crbug.com/1002788.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,DISABLED_NavigationRaceFromEmbedder)264 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
265                        DISABLED_NavigationRaceFromEmbedder) {
266   const std::string kTestName = "test_navigation_race_embedder";
267   auto cross_origin_url =
268       embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
269   auto test_url = embedded_test_server()->GetURL(
270       "a.com",
271       base::StringPrintf("/test_object_with_frame.html?test_data=%s,%s,%s",
272                          kTestName.c_str(), cross_origin_url.c_str(),
273                          "testEmbedded.csv"));
274   RunTestWithUrl(test_url);
275 }
276 
277 // TODO(ekaramad): Without proper handling of navigation to 'about:blank', this
278 // test would be flaky. Use TestNavigationManager class and possibly break the
279 // test into more sub-tests for various scenarios (https://crbug.com/659750).
280 // This test verifies that (almost) concurrent navigations in a cross-process
281 // frame inside an <embed> which is transitioning to a MimeHandlerView will
282 // not block creation of MimeHandlerView. The test will load some cross-origin
283 // content in <object> which right after loading will navigate it self to some
284 // other cross-origin content. On the embedder side, when the first page loads,
285 // the <object> loads some text/csv content to create a MimeHandlerViewGuest.
286 // The test passes if MHV loads.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,NavigationRaceFromCrossProcessRenderer)287 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
288                        NavigationRaceFromCrossProcessRenderer) {
289   const std::string kTestName = "test_navigation_race_cross_origin";
290   auto cross_origin_url =
291       embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
292   auto other_cross_origin_url =
293       embedded_test_server()->GetURL("c.com", "/test_page.html").spec();
294   auto test_url = embedded_test_server()->GetURL(
295       "a.com",
296       base::StringPrintf("/test_object_with_frame.html?test_data=%s,%s,%s,%s",
297                          kTestName.c_str(), cross_origin_url.c_str(),
298                          other_cross_origin_url.c_str(), "testEmbedded.csv"));
299   RunTestWithUrl(test_url);
300 }
301 
302 // This test verifies that removing embedder RenderFrame will not crash the
303 // renderer (for context see https://crbug.com/930803).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbedderFrameRemovedNoCrash)304 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbedderFrameRemovedNoCrash) {
305   RunTest("test_iframe_basic.html");
306   auto* guest_view = GuestViewBase::FromWebContents(
307       GetGuestViewManager()->WaitForSingleGuestCreated());
308   ASSERT_TRUE(guest_view);
309   int32_t element_instance_id = guest_view->element_instance_id();
310   auto* embedder_web_contents = GetEmbedderWebContents();
311   auto* child_frame =
312       content::ChildFrameAt(embedder_web_contents->GetMainFrame(), 0);
313   content::RenderFrameDeletedObserver render_frame_observer(child_frame);
314   ASSERT_TRUE(
315       content::ExecJs(embedder_web_contents,
316                       "document.querySelector('iframe').outerHTML = ''"));
317   render_frame_observer.WaitUntilDeleted();
318   // Send the IPC. During destruction MHVFC would cause a UaF since it was not
319   // removed from the global map.
320   mojo::AssociatedRemote<extensions::mojom::MimeHandlerViewContainerManager>
321       container_manager;
322   embedder_web_contents->GetMainFrame()
323       ->GetRemoteAssociatedInterfaces()
324       ->GetInterface(&container_manager);
325   container_manager->DestroyFrameContainer(element_instance_id);
326   // Running the following JS code fails if the renderer has crashed.
327   ASSERT_TRUE(content::ExecJs(embedder_web_contents, "window.name = 'foo'"));
328 }
329 
330 // TODO(ekaramad): Somehow canceling a first dialog in a setup similar to the
331 // test below pops up another dialog. This is likely due to the navigation to
332 // about:blank from both the browser side and the embedder side in the method
333 // HTMLPlugInElement::RequestObjectInternal. Find out the issue and add another
334 // test here where the dialog is dismissed and the guest not created.
335 // (https://crbug.com/659750).
336 // This test verifies that transitioning a plugin element from text/html to
337 // application/pdf respects 'beforeunload'. The test specifically checks that
338 // 'beforeunload' dialog is shown to the user and if the user decides to
339 // proceed with the transition, MimeHandlerViewGuest is created.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbedWithInitialFrameAcceptBeforeUnloadDialog)340 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
341                        EmbedWithInitialFrameAcceptBeforeUnloadDialog) {
342   // Use the testing subclass of MimeHandlerViewGuest.
343   GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
344       base::BindRepeating(&TestMimeHandlerViewGuest::Create));
345   const extensions::Extension* extension = LoadTestExtension();
346   ASSERT_TRUE(extension);
347   ui_test_utils::NavigateToURL(
348       browser(),
349       embedded_test_server()->GetURL("a.com", "/test_object_with_frame.html"));
350   auto* main_frame =
351       browser()->tab_strip_model()->GetWebContentsAt(0)->GetMainFrame();
352   auto url_with_beforeunload =
353       embedded_test_server()->GetURL("b.com", "/test_page.html?beforeunload");
354   bool result = false;
355   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
356       main_frame,
357       base::StringPrintf(
358           "object.data = '%s';"
359           " object.onload = () => window.domAutomationController.send(true);",
360           url_with_beforeunload.spec().c_str()),
361       &result));
362   ASSERT_TRUE(result);
363   // Give user gesture to the frame, set the <object> to text/csv resource and
364   // handle the "beforeunload" dialog.
365   content::PrepContentsForBeforeUnloadTest(
366       browser()->tab_strip_model()->GetWebContentsAt(0));
367   ASSERT_TRUE(content::ExecuteScript(main_frame,
368                                      "object.data = './testEmbedded.csv';"
369                                      "object.type = 'text/csv';"));
370   javascript_dialogs::AppModalDialogController* alert =
371       ui_test_utils::WaitForAppModalDialog();
372   ASSERT_TRUE(alert->is_before_unload_dialog());
373   alert->view()->AcceptAppModalDialog();
374 
375   EXPECT_TRUE(GetGuestViewManager()->WaitForSingleGuestCreated());
376 }
377 // The following tests will eventually converted into a parametric version which
378 // will run on both BrowserPlugin-based and cross-process-frame-based
379 // MimeHandlerView (https://crbug.com/659750).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,PostMessage)380 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, PostMessage) {
381   RunTest("test_postmessage.html");
382 }
383 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,Basic)384 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Basic) {
385   RunTest("testBasic.csv");
386   // Verify that for a navigation to a MimeHandlerView MIME type, exactly one
387   // stream is intercepted. This means :
388   // a- For BrowserPlugin-based MHV the PluginDocument passes the |view_id| to
389   //    MimeHandlerViewContainer (so a new request is not sent).
390   // b- For frame-based MimeHandlerView we do not create a PluginDocument. If a
391   //    PluginDocument was created here, the |view_id| associated with the
392   //    stream intercepted from navigation response would be lost (
393   //    PluginDocument does not talk to a MimeHandlerViewFrameContainer). Then,
394   //    the newly added <embed> by the PluginDocument would send its own request
395   //    leading to a total of 2 intercepted streams. The first one (from
396   //    navigation) would never be released.
397   EXPECT_EQ(0U, MimeHandlerStreamManager::Get(
398                     GetEmbedderWebContents()->GetBrowserContext())
399                     ->streams_.size());
400 }
401 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,Iframe)402 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Iframe) {
403   RunTest("test_iframe.html");
404 }
405 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,NonAsciiHeaders)406 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, NonAsciiHeaders) {
407   RunTest("testNonAsciiHeaders.csv");
408 }
409 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,DataUrl)410 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, DataUrl) {
411   const char* kDataUrlCsv = "data:text/csv;base64,Y29udGVudCB0byByZWFkCg==";
412   RunTestWithUrl(GURL(kDataUrlCsv));
413 }
414 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbeddedDataUrlObject)415 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlObject) {
416   RunTest("test_embedded_data_url_object.html");
417 }
418 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbeddedDataUrlEmbed)419 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlEmbed) {
420   RunTest("test_embedded_data_url_embed.html");
421 }
422 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,EmbeddedDataUrlLong)423 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlLong) {
424   RunTest("test_embedded_data_url_long.html");
425 }
426 
427 // Regression test for crbug.com/587709.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,SingleRequest)428 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, SingleRequest) {
429   GURL url(embedded_test_server()->GetURL("/testBasic.csv"));
430   RunTest("testBasic.csv");
431   EXPECT_EQ(1, basic_count());
432 }
433 
434 // Test that a mime handler view can keep a background page alive.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,BackgroundPage)435 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BackgroundPage) {
436   extensions::ProcessManager::SetEventPageIdleTimeForTesting(1);
437   extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1);
438   RunTest("testBackgroundPage.csv");
439 }
440 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,TargetBlankAnchor)441 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, TargetBlankAnchor) {
442   RunTest("testTargetBlankAnchor.csv");
443   ASSERT_EQ(2, browser()->tab_strip_model()->count());
444   content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(1));
445   EXPECT_EQ(
446       GURL(url::kAboutBlankURL),
447       browser()->tab_strip_model()->GetWebContentsAt(1)->GetLastCommittedURL());
448 }
449 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,BeforeUnload_NoDialog)450 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BeforeUnload_NoDialog) {
451   ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadNoDialog.csv"));
452   auto* web_contents = GetEmbedderWebContents();
453   content::PrepContentsForBeforeUnloadTest(web_contents);
454 
455   // Wait for a round trip to the outer renderer to ensure any beforeunload
456   // toggle IPC has had time to reach the browser.
457   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "");
458 
459   // Try to navigate away from the page. If the beforeunload listener is
460   // triggered and a dialog is shown, this navigation will never complete,
461   // causing the test to timeout and fail.
462   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
463 }
464 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,BeforeUnload_ShowDialog)465 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BeforeUnload_ShowDialog) {
466   ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadShowDialog.csv"));
467   auto* web_contents = GetEmbedderWebContents();
468   content::PrepContentsForBeforeUnloadTest(web_contents);
469 
470   // Wait for a round trip to the outer renderer to ensure the beforeunload
471   // toggle IPC has had time to reach the browser.
472   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "");
473 
474   web_contents->GetController().LoadURL(GURL(url::kAboutBlankURL), {},
475                                         ui::PAGE_TRANSITION_TYPED, "");
476 
477   javascript_dialogs::AppModalDialogController* before_unload_dialog =
478       ui_test_utils::WaitForAppModalDialog();
479   EXPECT_TRUE(before_unload_dialog->is_before_unload_dialog());
480   EXPECT_FALSE(before_unload_dialog->is_reload());
481   before_unload_dialog->OnAccept(base::string16(), false);
482 }
483 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,BeforeUnloadEnabled_WithoutUserActivation)484 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
485                        BeforeUnloadEnabled_WithoutUserActivation) {
486   ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadWithUserActivation.csv"));
487   auto* web_contents = GetEmbedderWebContents();
488   // Prepare frames but don't trigger user activation.
489   content::PrepContentsForBeforeUnloadTest(web_contents, false);
490 
491   // Even though this test's JS setup enables BeforeUnload dialogs, the dialog
492   // is still suppressed here because of lack of user activation.  As a result,
493   // the following navigation away from the page works fine.  If a beforeunload
494   // dialog were shown, this navigation would fail, causing the test to timeout.
495   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
496 }
497 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,BeforeUnloadEnabled_WithUserActivation)498 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
499                        BeforeUnloadEnabled_WithUserActivation) {
500   ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadWithUserActivation.csv"));
501   auto* web_contents = GetEmbedderWebContents();
502   // Prepare frames but don't trigger user activation across all frames.
503   content::PrepContentsForBeforeUnloadTest(web_contents, false);
504 
505   // Make sure we have a guestviewmanager.
506   auto* guest_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
507   UserActivationUpdateWaiter activation_waiter(guest_contents);
508 
509   // Activate |guest_contents| through a click, then wait until the activation
510   // IPC reaches the browser process.
511   SimulateMouseClick(guest_contents, 0, blink::WebMouseEvent::Button::kLeft);
512   activation_waiter.Wait();
513 
514   // Wait for a round trip to the outer renderer to ensure any beforeunload
515   // toggle IPC has had time to reach the browser.
516   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "");
517 
518   // Try to navigate away, this should invoke a beforeunload dialog.
519   web_contents->GetController().LoadURL(GURL(url::kAboutBlankURL), {},
520                                         ui::PAGE_TRANSITION_TYPED, "");
521 
522   javascript_dialogs::AppModalDialogController* before_unload_dialog =
523       ui_test_utils::WaitForAppModalDialog();
524   EXPECT_TRUE(before_unload_dialog->is_before_unload_dialog());
525   EXPECT_FALSE(before_unload_dialog->is_reload());
526   before_unload_dialog->OnAccept(base::string16(), false);
527 }
528 
529 // Helper class to wait for document load event in the main frame.
530 class DocumentLoadComplete : public content::WebContentsObserver {
531  public:
DocumentLoadComplete(content::WebContents * web_contents)532   explicit DocumentLoadComplete(content::WebContents* web_contents)
533       : content::WebContentsObserver(web_contents) {}
~DocumentLoadComplete()534   ~DocumentLoadComplete() override {}
535 
DocumentOnLoadCompletedInMainFrame()536   void DocumentOnLoadCompletedInMainFrame() override {
537     did_load_ = true;
538     run_loop_.Quit();
539   }
540 
Wait()541   void Wait() {
542     if (!did_load_)
543       run_loop_.Run();
544   }
545 
546  private:
547   bool did_load_ = false;
548   base::RunLoop run_loop_;
549 };
550 
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,ActivatePostMessageSupportOnce)551 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, ActivatePostMessageSupportOnce) {
552   RunTest("test_embedded.html");
553   // Attach a second <embed>.
554   ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(),
555                               "const e = document.createElement('embed');"
556                               "e.src = './testEmbedded.csv'; e.type='text/csv';"
557                               "document.body.appendChild(e);"));
558   DocumentLoadComplete(GetGuestViewManager()->WaitForNextGuestCreated()).Wait();
559   // After load, an IPC has been sent to the renderer to update routing IDs for
560   // the guest frame and the content frame (and activate the
561   // PostMessageSupport). Run some JS to Ensure no DCHECKs have fired in the
562   // embedder process.
563   ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(), "foo = 0;"));
564 }
565 
566 // This is a minimized repro for a clusterfuzz crasher and is not really related
567 // to MimeHandlerView. The test verifies that when
568 // HTMLPlugInElement::PluginWrapper is called for a plugin with no node document
569 // frame, the renderer does not crash (see https://966371).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,AdoptNodeInOnLoadDoesNotCrash)570 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, AdoptNodeInOnLoadDoesNotCrash) {
571   ui_test_utils::NavigateToURL(
572       browser(),
573       embedded_test_server()->GetURL("/adopt_node_in_onload_no_crash.html"));
574   // Run some JavaScript in embedder and make sure it is not crashed.
575   ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(), "true"));
576 }
577 
578 // Verifies that sandboxed frames do not create GuestViews (plugins are
579 // blocked in sandboxed frames).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,DoNotLoadInSandboxedFrame)580 IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, DoNotLoadInSandboxedFrame) {
581   // Use the testing subclass of MimeHandlerViewGuest.
582   GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
583       base::Bind(&TestMimeHandlerViewGuest::Create));
584 
585   const extensions::Extension* extension = LoadTestExtension();
586   ASSERT_TRUE(extension);
587 
588   ui_test_utils::NavigateToURL(
589       browser(), embedded_test_server()->GetURL("/test_sandboxed_frame.html"));
590 
591   auto* guest_view_manager = GetGuestViewManager();
592   // The page contains three <iframes> where two are sandboxed. The expectation
593   // is that the sandboxed frames do not end up creating a MimeHandlerView.
594   // Therefore, it suffices to wait for one GuestView to be created, then remove
595   // the non-sandboxed frame, and ensue there are no GuestViews left.
596   if (guest_view_manager->num_guests_created() == 0)
597     ASSERT_TRUE(guest_view_manager->WaitForNextGuestCreated());
598   ASSERT_EQ(1U, guest_view_manager->num_guests_created());
599   // Remove the non-sandboxed frame.
600   ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(),
601                               "remove_frame('notsandboxed');"));
602   // The page is expected to embed only '1' GuestView. If there is GuestViews
603   // embedded inside other frames we should be timing out here.
604   guest_view_manager->WaitForAllGuestsDeleted();
605   // Sanity check: Ensure that the documents in a sandbox frame is empty.
606   auto sandbox1_document_has_contents =
607       content::EvalJs(GetEmbedderWebContents(),
608                       "!!(sandbox1.contentDocument.body && "
609                       "sandbox1.contentDocument.body.firstChild)")
610           .ExtractBool();
611   EXPECT_FALSE(sandbox1_document_has_contents);
612   // The document inside 'sandbox2' contains an <object> with fallback content.
613   // The expectation is that the <object> fails to load the MimeHandlerView and
614   // should show the fallback content instead, which means the width of the
615   // layout object is non-zero.
616   auto fallback_width =
617       content::EvalJs(GetEmbedderWebContents(),
618                       "sandbox2.contentDocument.getElementById('fallback')."
619                       "getBoundingClientRect().width")
620           .ExtractInt();
621   EXPECT_NE(0, fallback_width);
622 }
623