Create swapped-out opener RVHs after a process swap.
This is required to support cross-process JavaScript calls, like postMessage.

BUG=99202
TEST=window.opener present after a cross-process navigation.

Review URL: https://chromiumcodereview.appspot.com/10171018

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134981 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 08d28b3..05dafcd 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -239,6 +239,7 @@
       site_instance,
       routing_id,
       static_cast<const WebContentsImpl*>(base_web_contents),
+      NULL,
       static_cast<SessionStorageNamespaceImpl*>(session_storage_namespace));
 }
 }
@@ -250,10 +251,12 @@
     SiteInstance* site_instance,
     int routing_id,
     const WebContentsImpl* base_web_contents,
+    WebContentsImpl* opener,
     SessionStorageNamespaceImpl* session_storage_namespace)
     : delegate_(NULL),
       ALLOW_THIS_IN_INITIALIZER_LIST(controller_(
           this, browser_context, session_storage_namespace)),
+      opener_(opener),
       ALLOW_THIS_IN_INITIALIZER_LIST(render_manager_(this, this)),
       is_loading_(false),
       crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING),
@@ -280,7 +283,6 @@
       temporary_zoom_settings_(false),
       content_restrictions_(0),
       view_type_(content::VIEW_TYPE_WEB_CONTENTS),
-      has_opener_(false),
       color_chooser_(NULL) {
   render_manager_.Init(browser_context, site_instance, routing_id);
 
@@ -310,6 +312,12 @@
   view_->CreateView(base_web_contents ?
       base_web_contents->GetView()->GetContainerSize() : gfx::Size());
 
+  // Listen for whether our opener gets destroyed.
+  if (opener_) {
+    registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+                   content::Source<WebContents>(opener_));
+  }
+
 #if defined(ENABLE_JAVA_BRIDGE)
   java_bridge_dispatcher_host_manager_.reset(
       new JavaBridgeDispatcherHostManager(this));
@@ -920,13 +928,12 @@
 }
 
 WebContents* WebContentsImpl::Clone() {
-  // We create a new SiteInstance so that the new tab won't share processes
-  // with the old one. This can be changed in the future if we need it to share
-  // processes for some reason.
+  // We use our current SiteInstance since the cloned entry will use it anyway.
+  // We pass |this| for the |base_web_contents| to size the view correctly, and
+  // our own opener so that the cloned page can access it if it was before.
   WebContentsImpl* tc = new WebContentsImpl(
-      GetBrowserContext(),
-      SiteInstance::Create(GetBrowserContext()),
-      MSG_ROUTING_NONE, this, NULL);
+      GetBrowserContext(), GetSiteInstance(),
+      MSG_ROUTING_NONE, this, opener_, NULL);
   tc->GetControllerImpl().CopyStateFrom(controller_);
   return tc;
 }
@@ -958,6 +965,28 @@
   view_->Focus();
 }
 
+void WebContentsImpl::Observe(int type,
+                              const content::NotificationSource& source,
+                              const content::NotificationDetails& details) {
+  switch (type) {
+    case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
+      OnWebContentsDestroyed(
+          content::Source<content::WebContents>(source).ptr());
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+void WebContentsImpl::OnWebContentsDestroyed(WebContents* web_contents) {
+  // Clear the opener if it has been closed.
+  if (web_contents == opener_) {
+    registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
+                      content::Source<WebContents>(opener_));
+    opener_ = NULL;
+  }
+}
+
 void WebContentsImpl::AddObserver(WebContentsObserver* observer) {
   observers_.AddObserver(observer);
 }
@@ -1408,7 +1437,7 @@
 }
 
 bool WebContentsImpl::HasOpener() const {
-  return has_opener_;
+  return opener_ != NULL;
 }
 
 void WebContentsImpl::DidChooseColorInColorChooser(int color_chooser_id,
@@ -2000,6 +2029,12 @@
 }
 
 void WebContentsImpl::RenderViewCreated(RenderViewHost* render_view_host) {
+  // Don't send notifications if we are just creating a swapped-out RVH for
+  // the opener chain.  These won't be used for view-source or WebUI, so it's
+  // ok to return early.
+  if (static_cast<RenderViewHostImpl*>(render_view_host)->is_swapped_out())
+    return;
+
   content::NotificationService::current()->Notify(
       content::NOTIFICATION_RENDER_VIEW_HOST_CREATED_FOR_TAB,
       content::Source<WebContents>(this),
@@ -2547,6 +2582,28 @@
   NotifySwapped();
 }
 
+int WebContentsImpl::CreateOpenerRenderViewsForRenderManager(
+    SiteInstance* instance) {
+  if (!opener_)
+    return MSG_ROUTING_NONE;
+
+  // Recursively create RenderViews for anything else in the opener chain.
+  return opener_->CreateOpenerRenderViews(instance);
+}
+
+int WebContentsImpl::CreateOpenerRenderViews(SiteInstance* instance) {
+  int opener_route_id = MSG_ROUTING_NONE;
+
+  // If this tab has an opener, ensure it has a RenderView in the given
+  // SiteInstance as well.
+  if (opener_)
+    opener_route_id = opener_->CreateOpenerRenderViews(instance);
+
+  // Create a swapped out RenderView in the given SiteInstance if none exists,
+  // setting its opener to the given route_id.  Return the new view's route_id.
+  return render_manager_.CreateRenderView(instance, opener_route_id, true);
+}
+
 NavigationControllerImpl& WebContentsImpl::GetControllerForRenderManager() {
   return GetControllerImpl();
 }
@@ -2561,7 +2618,7 @@
 }
 
 bool WebContentsImpl::CreateRenderViewForRenderManager(
-    RenderViewHost* render_view_host) {
+    RenderViewHost* render_view_host, int opener_route_id) {
   // Can be NULL during tests.
   RenderWidgetHostView* rwh_view = view_->CreateViewForWidget(render_view_host);
 
@@ -2575,8 +2632,10 @@
       GetMaxPageIDForSiteInstance(render_view_host->GetSiteInstance());
 
   if (!static_cast<RenderViewHostImpl*>(
-          render_view_host)->CreateRenderView(string16(), max_page_id))
+          render_view_host)->CreateRenderView(string16(), opener_route_id,
+                                              max_page_id)) {
     return false;
+  }
 
 #if defined(OS_LINUX) || defined(OS_OPENBSD)
   // Force a ViewMsg_Resize to be sent, needed to make plugins show up on