Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / frame_host / render_frame_host_manager.cc
index 5738861..c871e04 100644 (file)
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/render_view_devtools_agent_host.h"
 #include "content/browser/frame_host/cross_process_frame_connector.h"
+#include "content/browser/frame_host/debug_urls.h"
 #include "content/browser/frame_host/interstitial_page_impl.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/render_frame_host_factory.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/cross_site_transferring_request.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 
 namespace content {
 
-RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams()
-    : is_transfer(false), frame_id(-1), should_replace_current_entry(false) {
-}
-
 RenderFrameHostManager::PendingNavigationParams::PendingNavigationParams(
     const GlobalRequestID& global_request_id,
-    bool is_transfer,
+    scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
     const std::vector<GURL>& transfer_url_chain,
     Referrer referrer,
     PageTransition page_transition,
     int64 frame_id,
     bool should_replace_current_entry)
     : global_request_id(global_request_id),
-      is_transfer(is_transfer),
+      cross_site_transferring_request(cross_site_transferring_request.Pass()),
       transfer_url_chain(transfer_url_chain),
       referrer(referrer),
       page_transition(page_transition),
@@ -74,7 +72,8 @@ RenderFrameHostManager::RenderFrameHostManager(
       render_frame_host_(NULL),
       pending_render_frame_host_(NULL),
       interstitial_page_(NULL),
-      cross_process_frame_connector_(NULL) {}
+      cross_process_frame_connector_(NULL),
+      weak_factory_(this) {}
 
 RenderFrameHostManager::~RenderFrameHostManager() {
   if (pending_render_frame_host_)
@@ -205,6 +204,15 @@ RenderFrameHostImpl* RenderFrameHostManager::Navigate(
     }
   }
 
+  // If entry includes the request ID of a request that is being transferred,
+  // the destination render frame will take ownership, so release ownership of
+  // the request.
+  if (pending_nav_params_ &&
+      pending_nav_params_->global_request_id ==
+          entry.transferred_global_request_id()) {
+    pending_nav_params_->cross_site_transferring_request->ReleaseRequest();
+  }
+
   return dest_render_frame_host;
 }
 
@@ -236,7 +244,7 @@ bool RenderFrameHostManager::ShouldCloseTabOnUnresponsiveRenderer() {
   // If the tab becomes unresponsive during {before}unload while doing a
   // cross-site navigation, proceed with the navigation.  (This assumes that
   // the pending RenderFrameHost is still responsive.)
-  if (render_frame_host_->render_view_host()->is_waiting_for_unload_ack()) {
+  if (render_frame_host_->render_view_host()->IsWaitingForUnloadACK()) {
     // The request has been started and paused while we're waiting for the
     // unload handler to finish.  We'll pretend that it did.  The pending
     // renderer will then be swapped in as part of the usual DidNavigate logic.
@@ -277,7 +285,7 @@ void RenderFrameHostManager::SwappedOut(RenderViewHost* render_view_host) {
   // TODO(creis): The blank swapped out page is visible during this time, but
   // we can shorten this by delivering the response directly, rather than
   // forcing an identical request to be made.
-  if (pending_nav_params_->is_transfer) {
+  if (pending_nav_params_->cross_site_transferring_request) {
     // Treat the last URL in the chain as the destination and the remainder as
     // the redirect chain.
     CHECK(pending_nav_params_->transfer_url_chain.size());
@@ -326,7 +334,7 @@ void RenderFrameHostManager::SwappedOutFrame(
   // TODO(creis): The blank swapped out page is visible during this time, but
   // we can shorten this by delivering the response directly, rather than
   // forcing an identical request to be made.
-  if (pending_nav_params_->is_transfer) {
+  if (pending_nav_params_->cross_site_transferring_request) {
     // Treat the last URL in the chain as the destination and the remainder as
     // the redirect chain.
     CHECK(pending_nav_params_->transfer_url_chain.size());
@@ -478,7 +486,7 @@ void RenderFrameHostManager::ShouldClosePage(
 void RenderFrameHostManager::OnCrossSiteResponse(
     RenderViewHost* pending_render_view_host,
     const GlobalRequestID& global_request_id,
-    bool is_transfer,
+    scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request,
     const std::vector<GURL>& transfer_url_chain,
     const Referrer& referrer,
     PageTransition page_transition,
@@ -494,10 +502,12 @@ void RenderFrameHostManager::OnCrossSiteResponse(
   // here, but currently we pass information for a transfer if
   // ShouldSwapProcessesForRedirect returned true in the network stack.
   // In that case, we should set up a transfer after the unload handler runs.
-  // If is_transfer is false, we will just run the unload handler and resume.
+  // If |cross_site_transferring_request| is NULL, we will just run the unload
+  // handler and resume.
   pending_nav_params_.reset(new PendingNavigationParams(
-      global_request_id, is_transfer, transfer_url_chain, referrer,
-      page_transition, frame_id, should_replace_current_entry));
+      global_request_id, cross_site_transferring_request.Pass(),
+      transfer_url_chain, referrer, page_transition, frame_id,
+      should_replace_current_entry));
 
   // Run the unload handler of the current page.
   SwapOutOldPage();
@@ -539,6 +549,15 @@ void RenderFrameHostManager::SwapOutOldPage() {
   }
 }
 
+void RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance(
+    int32 site_instance_id,
+    RenderFrameHostImpl* rfh) {
+  RFHPendingDeleteMap::iterator iter =
+      pending_delete_hosts_.find(site_instance_id);
+  if (iter != pending_delete_hosts_.end() && iter->second.get() == rfh)
+    pending_delete_hosts_.erase(site_instance_id);
+}
+
 void RenderFrameHostManager::Observe(
     int type,
     const NotificationSource& source,
@@ -560,8 +579,30 @@ bool RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance(
     FrameTreeNode* node) {
   RenderFrameHostMap::iterator iter =
       node->render_manager()->swapped_out_hosts_.find(site_instance_id);
-  if (iter != node->render_manager()->swapped_out_hosts_.end())
-    delete iter->second;
+  if (iter != node->render_manager()->swapped_out_hosts_.end()) {
+    RenderFrameHostImpl* swapped_out_rfh = iter->second;
+    // If the RVH is pending swap out, it needs to switch state to
+    // pending shutdown. Otherwise it is deleted.
+    if (swapped_out_rfh->render_view_host()->rvh_state() ==
+        RenderViewHostImpl::STATE_PENDING_SWAP_OUT) {
+      swapped_out_rfh->SetPendingShutdown(base::Bind(
+          &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance,
+          node->render_manager()->weak_factory_.GetWeakPtr(),
+          site_instance_id,
+          swapped_out_rfh));
+      RFHPendingDeleteMap::iterator pending_delete_iter =
+          node->render_manager()->pending_delete_hosts_.find(site_instance_id);
+      if (pending_delete_iter ==
+              node->render_manager()->pending_delete_hosts_.end() ||
+          pending_delete_iter->second.get() != iter->second) {
+        node->render_manager()->pending_delete_hosts_[site_instance_id] =
+            linked_ptr<RenderFrameHostImpl>(swapped_out_rfh);
+      }
+    } else {
+      delete swapped_out_rfh;
+    }
+    node->render_manager()->swapped_out_hosts_.erase(site_instance_id);
+  }
 
   return true;
 }
@@ -602,6 +643,11 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
   const GURL& new_url = SiteInstanceImpl::GetEffectiveURL(browser_context,
                                                           new_entry->GetURL());
 
+  // Don't force a new BrowsingInstance for debug URLs that are handled in the
+  // renderer process, like javascript: or chrome://crash.
+  if (IsRendererDebugURL(new_url))
+    return false;
+
   // For security, we should transition between processes when one is a Web UI
   // page and one isn't.
   if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL(
@@ -781,10 +827,13 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry(
       current_instance->GetSiteURL();
 
   // View-source URLs must use a new SiteInstance and BrowsingInstance.
+  // We don't need a swap when going from view-source to a debug URL like
+  // chrome://crash, however.
   // TODO(creis): Refactor this method so this duplicated code isn't needed.
   // See http://crbug.com/123007.
   if (current_entry &&
-      current_entry->IsViewSourceMode() != entry.IsViewSourceMode()) {
+      current_entry->IsViewSourceMode() != entry.IsViewSourceMode() &&
+      !IsRendererDebugURL(dest_url)) {
     return SiteInstance::CreateForURL(browser_context, dest_url);
   }
 
@@ -944,7 +993,7 @@ bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host,
     // process unless it's swapped out.
     RenderViewHostImpl* rvh_impl =
         static_cast<RenderViewHostImpl*>(render_view_host);
-    if (!rvh_impl->is_swapped_out()) {
+    if (!rvh_impl->IsSwappedOut()) {
       CHECK(!ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
                 render_view_host->GetProcess()->GetID()));
     }
@@ -972,10 +1021,14 @@ void RenderFrameHostManager::CommitPending() {
   // |pending_web_ui_|, or clear |web_ui_| if there is no pending WebUI, or
   // leave |web_ui_| as is if reusing it.
   DCHECK(!(pending_web_ui_.get() && pending_and_current_web_ui_.get()));
-  if (pending_web_ui_)
+  if (pending_web_ui_) {
     web_ui_.reset(pending_web_ui_.release());
-  else if (!pending_and_current_web_ui_.get())
+  } else if (!pending_and_current_web_ui_.get()) {
     web_ui_.reset();
+  } else {
+    DCHECK_EQ(pending_and_current_web_ui_.get(), web_ui_.get());
+    pending_and_current_web_ui_.reset();
+  }
 
   // It's possible for the pending_render_frame_host_ to be NULL when we aren't
   // crossing process boundaries. If so, we just needed to handle the Web UI
@@ -1019,10 +1072,16 @@ void RenderFrameHostManager::CommitPending() {
 
   // If the old view is live and top-level, hide it now that the new one is
   // visible.
+  int32 old_site_instance_id =
+      old_render_frame_host->render_view_host()->GetSiteInstance()->GetId();
   if (old_render_frame_host->render_view_host()->GetView()) {
     if (is_main_frame) {
       old_render_frame_host->render_view_host()->GetView()->Hide();
-      old_render_frame_host->render_view_host()->WasSwappedOut();
+      old_render_frame_host->render_view_host()->WasSwappedOut(base::Bind(
+          &RenderFrameHostManager::ClearPendingShutdownRFHForSiteInstance,
+          weak_factory_.GetWeakPtr(),
+          old_site_instance_id,
+          old_render_frame_host));
     } else {
       // TODO(creis): We'll need to set this back to false if we navigate back.
       old_render_frame_host->set_swapped_out(true);
@@ -1056,18 +1115,17 @@ void RenderFrameHostManager::CommitPending() {
 
   if (old_render_frame_host->render_view_host()->IsRenderViewLive()) {
     // If the old RFH is live, we are swapping it out and should keep track of
-    // it in case we navigate back to it.
+    // it in case we navigate back to it, or it is waiting for the unload event
+    // to execute in the background.
     // TODO(creis): Swap out the subframe in --site-per-process.
     if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess))
       DCHECK(old_render_frame_host->is_swapped_out() ||
-             old_render_frame_host->render_view_host()->is_swapped_out());
-
+             !RenderViewHostImpl::IsRVHStateActive(
+                  old_render_frame_host->render_view_host()->rvh_state()));
     // Temp fix for http://crbug.com/90867 until we do a better cleanup to make
     // sure we don't get different rvh instances for the same site instance
     // in the same rvhmgr.
     // TODO(creis): Clean this up.
-    int32 old_site_instance_id =
-        old_render_frame_host->render_view_host()->GetSiteInstance()->GetId();
     RenderFrameHostMap::iterator iter =
         swapped_out_hosts_.find(old_site_instance_id);
     if (iter != swapped_out_hosts_.end() &&
@@ -1075,7 +1133,23 @@ void RenderFrameHostManager::CommitPending() {
       // Delete the RFH that will be replaced in the map to avoid a leak.
       delete iter->second;
     }
-    swapped_out_hosts_[old_site_instance_id] = old_render_frame_host;
+    // If the RenderViewHost backing the RenderFrameHost is pending shutdown,
+    // the RenderFrameHost should be put in the map of RenderFrameHosts pending
+    // shutdown. Otherwise, it is stored in the map of swapped out
+    // RenderFrameHosts.
+    if (old_render_frame_host->render_view_host()->rvh_state() ==
+            RenderViewHostImpl::STATE_PENDING_SHUTDOWN) {
+      swapped_out_hosts_.erase(old_site_instance_id);
+      RFHPendingDeleteMap::iterator pending_delete_iter =
+          pending_delete_hosts_.find(old_site_instance_id);
+      if (pending_delete_iter == pending_delete_hosts_.end() ||
+          pending_delete_iter->second.get() != old_render_frame_host) {
+        pending_delete_hosts_[old_site_instance_id] =
+            linked_ptr<RenderFrameHostImpl>(old_render_frame_host);
+      }
+    } else {
+      swapped_out_hosts_[old_site_instance_id] = old_render_frame_host;
+    }
 
     // If there are no active views in this SiteInstance, it means that
     // this RFH was the last active one in the SiteInstance. Now that we
@@ -1118,7 +1192,6 @@ void RenderFrameHostManager::ShutdownRenderFrameHostsInSiteInstance(
       tree->ForEach(base::Bind(
           &RenderFrameHostManager::ClearSwappedOutRFHsInSiteInstance,
           site_instance_id));
-      // rvh is now deleted.
     }
   }
 }