Implement app process model isolation.

The process grouping logic is unfortunately duplicated in SiteInstance and
RenderView. URLs that are part of extension X's web extent get converted into
a pseudo URL of the form chrome-extension://X/path. This groups pages from an
extension app and its offline resources into the same process.

The rest is mostly plumbing and passing data around.

BUG=41273

Review URL: http://codereview.chromium.org/1735004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45384 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/app_process_apitest.cc b/chrome/browser/extensions/app_process_apitest.cc
new file mode 100644
index 0000000..ecf0c63
--- /dev/null
+++ b/chrome/browser/extensions/app_process_apitest.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/ui_test_utils.h"
+#include "net/base/mock_host_resolver.h"
+
+class AppApiTest : public ExtensionApiTest {
+ public:
+  void SetUpCommandLine(CommandLine* command_line) {
+    ExtensionApiTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kEnableExtensionApps);
+  }
+};
+
+// Simulates a page calling window.open on an URL, and waits for the navigation.
+static void WindowOpenHelper(Browser* browser,
+                             RenderViewHost* opener_host, const GURL& url) {
+  bool result = false;
+  ui_test_utils::ExecuteJavaScriptAndExtractBool(
+      opener_host, L"",
+      L"window.open('" + UTF8ToWide(url.spec()) + L"');"
+      L"window.domAutomationController.send(true);",
+      &result);
+  ASSERT_TRUE(result);
+
+  // Now the current tab should be the new tab.
+  TabContents* newtab = browser->GetSelectedTabContents();
+  if (!newtab->controller().GetLastCommittedEntry() ||
+      newtab->controller().GetLastCommittedEntry()->url() != url)
+    ui_test_utils::WaitForNavigation(&newtab->controller());
+  EXPECT_EQ(url, newtab->controller().GetLastCommittedEntry()->url());
+}
+
+// Simulates a page navigating itself to an URL, and waits for the navigation.
+static void NavigateTabHelper(TabContents* contents, const GURL& url) {
+  bool result = false;
+  ui_test_utils::ExecuteJavaScriptAndExtractBool(
+      contents->render_view_host(), L"",
+      L"window.location = '" + UTF8ToWide(url.spec()) + L"';"
+      L"window.domAutomationController.send(true);",
+      &result);
+  ASSERT_TRUE(result);
+
+  if (contents->controller().GetLastCommittedEntry() ||
+      contents->controller().GetLastCommittedEntry()->url() != url)
+    ui_test_utils::WaitForNavigation(&contents->controller());
+  EXPECT_EQ(url, contents->controller().GetLastCommittedEntry()->url());
+}
+
+IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
+  host_resolver()->AddRule("*", "127.0.0.1");
+  StartHTTPServer();
+
+  ASSERT_TRUE(RunExtensionTest("app_process")) << message_;
+
+  Extension* extension = GetSingleLoadedExtension();
+  ExtensionHost* host = browser()->profile()->GetExtensionProcessManager()->
+      GetBackgroundHostForExtension(extension);
+  ASSERT_TRUE(host);
+
+  // The extension should have opened 3 new tabs. Including the original blank
+  // tab, we now have 4 tabs. Two should be part of the extension app, and
+  // grouped in the extension process.
+  ASSERT_EQ(4, browser()->tab_count());
+  EXPECT_EQ(host->render_process_host(),
+            browser()->GetTabContentsAt(1)->render_view_host()->process());
+  EXPECT_EQ(host->render_process_host(),
+            browser()->GetTabContentsAt(2)->render_view_host()->process());
+  EXPECT_NE(host->render_process_host(),
+            browser()->GetTabContentsAt(3)->render_view_host()->process());
+
+  // Now let's do the same using window.open. The same should happen.
+  WindowOpenHelper(browser(), host->render_view_host(),
+                   browser()->GetTabContentsAt(1)->GetURL());
+  WindowOpenHelper(browser(), host->render_view_host(),
+                   browser()->GetTabContentsAt(2)->GetURL());
+  WindowOpenHelper(browser(), host->render_view_host(),
+                   browser()->GetTabContentsAt(3)->GetURL());
+
+  ASSERT_EQ(7, browser()->tab_count());
+  EXPECT_EQ(host->render_process_host(),
+            browser()->GetTabContentsAt(4)->render_view_host()->process());
+  EXPECT_EQ(host->render_process_host(),
+            browser()->GetTabContentsAt(5)->render_view_host()->process());
+  EXPECT_NE(host->render_process_host(),
+            browser()->GetTabContentsAt(6)->render_view_host()->process());
+
+  // Now let's have these pages navigate, into or out of the extension web
+  // extent. They should switch processes.
+  const GURL& app_url(browser()->GetTabContentsAt(1)->GetURL());
+  const GURL& non_app_url(browser()->GetTabContentsAt(3)->GetURL());
+  NavigateTabHelper(browser()->GetTabContentsAt(1), non_app_url);
+  NavigateTabHelper(browser()->GetTabContentsAt(3), app_url);
+  EXPECT_NE(host->render_process_host(),
+            browser()->GetTabContentsAt(1)->render_view_host()->process());
+  EXPECT_EQ(host->render_process_host(),
+            browser()->GetTabContentsAt(3)->render_view_host()->process());
+}