Stop trying to reuse processes for web iframes on extension pages.

There's evidence that some web iframes embedded on extension
background pages may be doing a lot of work with the assumption that
since this work isn't on a foreground tab, it doesn't need to be
optimized for responsiveness.  However, OOPIFs currently use an
aggressive process reuse policy, which also affects this use case,
making it possible for a web iframe on an extension to get into
another tab's process and adversely affect its performance.  This CL
avoids using that reuse policy for web iframes on extensions.

Caveats:

- When over the process limit, web iframes on extensions will still
  attempt to reuse an existing process.

- OOPIFs from normal tabs may still join processes for web iframes on
  extensions.  This and the previous point imply that there won't be a
  guarantee of perfect performance isolation for web iframes on
  extensions; the goal is to be more resilient to problems in the
  common case.

- We will stop potentially useful process reuse between same-site
  iframes on multiple instances of extensions, even for instances of
  the same extension.  We expect this to be rare in practice though.

Bug: 899418, 899838
Change-Id: I35f6ecc1945292f9fab1c21f65d1ac4b7970dbe3
Reviewed-on: https://chromium-review.googlesource.com/c/1306410
Reviewed-by: Charlie Reis <[email protected]>
Reviewed-by: Devlin <[email protected]>
Commit-Queue: Alex Moshchuk <[email protected]>
Cr-Commit-Position: refs/heads/master@{#605028}
diff --git a/chrome/browser/extensions/process_manager_browsertest.cc b/chrome/browser/extensions/process_manager_browsertest.cc
index ded03802..a0612f8d 100644
--- a/chrome/browser/extensions/process_manager_browsertest.cc
+++ b/chrome/browser/extensions/process_manager_browsertest.cc
@@ -1647,6 +1647,61 @@
   }
 }
 
+// Verify that web iframes on extension frames do not attempt to aggressively
+// reuse existing processes for the same site.  This helps prevent a
+// misbehaving web iframe on an extension from slowing down other processes.
+// See https://crbug.com/899418.
+IN_PROC_BROWSER_TEST_F(ProcessManagerBrowserTest,
+                       WebSubframeOnExtensionDoesNotReuseExistingProcess) {
+  // This test matters only *with* --site-per-process.  It depends on process
+  // reuse logic that subframes use to look for existing processes, but that
+  // logic is only turned on for sites that require a dedicated process.
+  if (!content::AreAllSitesIsolatedForTesting())
+    return;
+
+  // Create a simple extension with a background page that has an empty iframe.
+  const Extension* extension = CreateExtension("Extension", true);
+  embedded_test_server()->ServeFilesFromDirectory(extension->path());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Navigate main tab to a web page on foo.com.
+  GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
+  NavigateToURL(foo_url);
+  content::WebContents* tab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  EXPECT_EQ(foo_url, tab->GetLastCommittedURL());
+
+  // So far, there should be two extension frames: one for the background page,
+  // one for the empty subframe on it.
+  ProcessManager* pm = ProcessManager::Get(profile());
+  EXPECT_EQ(2u, pm->GetAllFrames().size());
+  EXPECT_EQ(2u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
+
+  // Navigate the subframe on the extension background page to foo.com, and
+  // wait for the old subframe to go away.
+  ExtensionHost* background_host =
+      pm->GetBackgroundHostForExtension(extension->id());
+  content::RenderFrameHost* background_rfh =
+      background_host->host_contents()->GetMainFrame();
+  content::RenderFrameHost* extension_subframe =
+      ChildFrameAt(background_rfh, 0);
+  content::RenderFrameDeletedObserver deleted_observer(extension_subframe);
+  EXPECT_TRUE(
+      content::ExecJs(extension_subframe,
+                      content::JsReplace("window.location = $1;", foo_url)));
+  deleted_observer.WaitUntilDeleted();
+
+  // There should now only be one extension frame for the background page.  The
+  // subframe should've swapped processes and should now be a web frame.
+  EXPECT_EQ(1u, pm->GetAllFrames().size());
+  EXPECT_EQ(1u, pm->GetRenderFrameHostsForExtension(extension->id()).size());
+  content::RenderFrameHost* subframe = ChildFrameAt(background_rfh, 0);
+  EXPECT_EQ(foo_url, subframe->GetLastCommittedURL());
+
+  // Verify that the subframe did *not* reuse the existing foo.com process.
+  EXPECT_NE(tab->GetMainFrame()->GetProcess(), subframe->GetProcess());
+}
+
 // Test to verify that loading a resource other than an icon file is
 // disallowed for hosted apps, while icons are allowed.
 // See https://crbug.com/717626.