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/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index 38e068a..b7fb2af 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -474,6 +474,15 @@
   ExtensionProcessBindings::SetFunctionNames(names);
 }
 
+void RenderThread::OnExtensionExtentsUpdated(
+    const ViewMsg_ExtensionExtentsUpdated_Params& params) {
+  extension_extents_.resize(params.extension_apps.size());
+  for (size_t i = 0; i < params.extension_apps.size(); ++i) {
+    extension_extents_[i].extension_id = params.extension_apps[i].first;
+    extension_extents_[i].web_extent = params.extension_apps[i].second;
+  }
+}
+
 void RenderThread::OnPageActionsUpdated(
     const std::string& extension_id,
     const std::vector<std::string>& page_actions) {
@@ -548,6 +557,8 @@
                         OnExtensionMessageInvoke)
     IPC_MESSAGE_HANDLER(ViewMsg_Extension_SetFunctionNames,
                         OnSetExtensionFunctionNames)
+    IPC_MESSAGE_HANDLER(ViewMsg_ExtensionExtentsUpdated,
+                        OnExtensionExtentsUpdated)
     IPC_MESSAGE_HANDLER(ViewMsg_PurgeMemory, OnPurgeMemory)
     IPC_MESSAGE_HANDLER(ViewMsg_PurgePluginListCache,
                         OnPurgePluginListCache)
@@ -1012,3 +1023,15 @@
     gpu_channel_ = NULL;
   }
 }
+
+std::string RenderThread::GetExtensionIdForURL(const GURL& url) {
+  if (url.SchemeIs(chrome::kExtensionScheme))
+    return url.host();
+
+  for (size_t i = 0; i < extension_extents_.size(); ++i) {
+    if (extension_extents_[i].web_extent.ContainsURL(url))
+      return extension_extents_[i].extension_id;
+  }
+
+  return std::string();
+}
diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h
index e8396dd..ba5c849 100644
--- a/chrome/renderer/render_thread.h
+++ b/chrome/renderer/render_thread.h
@@ -18,6 +18,8 @@
 #include "chrome/common/child_thread.h"
 #include "chrome/common/css_colors.h"
 #include "chrome/common/dom_storage_common.h"
+#include "chrome/common/extensions/extension_extent.h"
+#include "chrome/common/render_messages.h"
 #include "chrome/renderer/gpu_channel_host.h"
 #include "chrome/renderer/renderer_histogram_snapshots.h"
 #include "chrome/renderer/visitedlink_slave.h"
@@ -185,7 +187,21 @@
   // has been lost.
   GpuChannelHost* GetGpuChannel();
 
+  // Returns the extension ID that the given URL is a part of, or empty if
+  // none. This includes web URLs that are part of an extension's web extent.
+  // TODO(mpcomplete): this doesn't feel like it belongs here. Find a better
+  // place.
+  std::string GetExtensionIdForURL(const GURL& url);
+
  private:
+  // Contains extension-related data that the renderer needs to know about.
+  // TODO(mpcomplete): this doesn't feel like it belongs here. Find a better
+  // place.
+  struct ExtensionInfo {
+    std::string extension_id;
+    ExtensionExtent web_extent;
+  };
+
   virtual void OnControlMessageReceived(const IPC::Message& msg);
 
   void Init();
@@ -198,6 +214,8 @@
       const GURL& url, const ContentSettings& content_settings);
   void OnUpdateUserScripts(base::SharedMemoryHandle table);
   void OnSetExtensionFunctionNames(const std::vector<std::string>& names);
+  void OnExtensionExtentsUpdated(
+      const ViewMsg_ExtensionExtentsUpdated_Params& params);
   void OnPageActionsUpdated(const std::string& extension_id,
       const std::vector<std::string>& page_actions);
   void OnDOMStorageEvent(const ViewMsg_DOMStorageEvent_Params& params);
@@ -314,6 +332,10 @@
   // The channel from the renderer process to the GPU process.
   scoped_refptr<GpuChannelHost> gpu_channel_;
 
+  // A list of extension web extents, which tells us which URLs belong to an
+  // installed app.
+  std::vector<ExtensionInfo> extension_extents_;
+
   DISALLOW_COPY_AND_ASSIGN(RenderThread);
 };
 
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 1aaf0a4..3364d16 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -320,6 +320,25 @@
   return false;
 }
 
+// Returns true if the frame is navigating to an URL either into or out of an
+// extension app's extent.
+static bool CrossesExtensionExtents(WebFrame* frame, const GURL& new_url) {
+  if (!RenderThread::current())
+    return false;
+
+  // If the URL is still empty, this is a window.open navigation. Check the
+  // opener's URL.
+  GURL old_url(frame->url());
+  if (old_url.is_empty() && frame->opener())
+    old_url = frame->opener()->url();
+
+  std::string old_extension =
+      RenderThread::current()->GetExtensionIdForURL(old_url);
+  std::string new_extension =
+      RenderThread::current()->GetExtensionIdForURL(new_url);
+  return (old_extension != new_extension);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 int32 RenderView::next_page_id_ = 1;
@@ -2210,7 +2229,7 @@
   const GURL& url = request.url();
 
   // If the browser is interested, then give it a chance to look at top level
-  // navigations
+  // navigations.
   if (ShouldRouteNavigationToBrowser(url, frame, type)) {
     last_top_level_navigation_page_id_ = page_id_;
     GURL referrer(request.httpHeaderField(WebString::fromUTF8("Referer")));
@@ -2233,7 +2252,8 @@
       !url.SchemeIs(chrome::kAboutScheme)) {
     // When we received such unsolicited navigations, we sometimes want to
     // punt them up to the browser to handle.
-    if (BindingsPolicy::is_dom_ui_enabled(enabled_bindings_) ||
+    if (CrossesExtensionExtents(frame, url) ||
+        BindingsPolicy::is_dom_ui_enabled(enabled_bindings_) ||
         BindingsPolicy::is_extension_enabled(enabled_bindings_) ||
         frame->isViewSourceModeEnabled() ||
         url.SchemeIs(chrome::kViewSourceScheme) ||