Index: content/browser/frame_host/render_frame_host_manager_unittest.cc |
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc |
index 798ab6a4ecf9b918db0c00b4b1da49195e966022..a5100dfbe4b2dc071a52f2e80d9c8271e3b260c9 100644 |
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc |
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc |
@@ -124,8 +124,7 @@ class RenderViewHostDeletedObserver : public WebContentsObserver { |
DISALLOW_COPY_AND_ASSIGN(RenderViewHostDeletedObserver); |
}; |
- |
-// This observer keeps track of the last deleted RenderViewHost to avoid |
+// This observer keeps track of the last deleted RenderFrameHost to avoid |
// accessing it and causing use-after-free condition. |
class RenderFrameHostDeletedObserver : public WebContentsObserver { |
public: |
@@ -193,6 +192,60 @@ class PluginFaviconMessageObserver : public WebContentsObserver { |
DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver); |
}; |
+// Ensures that RenderFrameDeleted and RenderFrameCreated are called in a |
+// consistent manner. |
+class FrameLifetimeConsistencyChecker : public WebContentsObserver { |
+ public: |
+ explicit FrameLifetimeConsistencyChecker(WebContentsImpl* web_contents) |
+ : WebContentsObserver(web_contents) { |
+ RenderViewCreated(web_contents->GetRenderViewHost()); |
+ RenderFrameCreated(web_contents->GetMainFrame()); |
+ } |
+ |
+ virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE { |
+ std::pair<int, int> routing_pair = |
+ std::make_pair(render_frame_host->GetProcess()->GetID(), |
+ render_frame_host->GetRoutingID()); |
+ bool was_live_already = !live_routes_.insert(routing_pair).second; |
+ bool was_used_before = deleted_routes_.count(routing_pair) != 0; |
+ |
+ if (was_live_already) { |
+ FAIL() << "RenderFrameCreated called more than once for routing pair: " |
+ << Format(render_frame_host); |
+ } else if (was_used_before) { |
+ FAIL() << "RenderFrameCreated called for routing pair " |
+ << Format(render_frame_host) << " that was previously deleted."; |
+ } |
+ } |
+ |
+ virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE { |
+ std::pair<int, int> routing_pair = |
+ std::make_pair(render_frame_host->GetProcess()->GetID(), |
+ render_frame_host->GetRoutingID()); |
+ bool was_live = live_routes_.erase(routing_pair); |
+ bool was_dead_already = !deleted_routes_.insert(routing_pair).second; |
+ |
+ if (was_dead_already) { |
+ FAIL() << "RenderFrameDeleted called more than once for routing pair " |
+ << Format(render_frame_host); |
+ } else if (!was_live) { |
+ FAIL() << "RenderFrameDeleted called for routing pair " |
+ << Format(render_frame_host) |
+ << " for which RenderFrameCreated was never called"; |
+ } |
+ } |
+ |
+ private: |
+ std::string Format(RenderFrameHost* render_frame_host) { |
+ return base::StringPrintf( |
+ "(%d, %d -> %s )", |
+ render_frame_host->GetProcess()->GetID(), |
+ render_frame_host->GetRoutingID(), |
+ render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str()); |
+ } |
+ std::set<std::pair<int, int> > live_routes_; |
+ std::set<std::pair<int, int> > deleted_routes_; |
+}; |
} // namespace |
@@ -202,9 +255,11 @@ class RenderFrameHostManagerTest |
virtual void SetUp() OVERRIDE { |
RenderViewHostImplTestHarness::SetUp(); |
WebUIControllerFactory::RegisterFactory(&factory_); |
+ lifetime_checker_.reset(new FrameLifetimeConsistencyChecker(contents())); |
} |
virtual void TearDown() OVERRIDE { |
+ lifetime_checker_.reset(); |
RenderViewHostImplTestHarness::TearDown(); |
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); |
} |
@@ -310,6 +365,7 @@ class RenderFrameHostManagerTest |
private: |
RenderFrameHostManagerTestWebUIControllerFactory factory_; |
+ scoped_ptr<FrameLifetimeConsistencyChecker> lifetime_checker_; |
}; |
// Tests that when you navigate from a chrome:// url to another page, and |