OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/public/test/web_contents_observer_sanity_checker.h" | 5 #include "content/public/test/web_contents_observer_sanity_checker.h" |
6 | 6 |
| 7 #include "base/debug/stack_trace.h" |
7 #include "base/strings/stringprintf.h" | 8 #include "base/strings/stringprintf.h" |
8 #include "content/public/browser/render_frame_host.h" | 9 #include "content/public/browser/render_frame_host.h" |
9 #include "content/public/browser/render_process_host.h" | 10 #include "content/public/browser/render_process_host.h" |
10 #include "content/public/browser/site_instance.h" | 11 #include "content/public/browser/site_instance.h" |
11 #include "content/public/browser/web_contents.h" | 12 #include "content/public/browser/web_contents.h" |
12 #include "content/public/browser/web_contents_observer.h" | 13 #include "content/public/browser/web_contents_observer.h" |
13 | 14 |
14 namespace content { | 15 namespace content { |
15 | 16 |
16 namespace { | 17 namespace { |
17 | 18 |
18 const char kWebContentsObserverSanityCheckerKey[] = | 19 const char kWebContentsObserverSanityCheckerKey[] = |
19 "WebContentsObserverSanityChecker"; | 20 "WebContentsObserverSanityChecker"; |
20 | 21 |
| 22 // Set this variable to true to collect stack traces on each RenderFrameCreated |
| 23 // and RenderFrameDeleted call, so duplicate calls can be easily debugged. |
| 24 static bool g_collect_stack_traces = true; |
| 25 |
21 } // namespace | 26 } // namespace |
22 | 27 |
23 // static | 28 // static |
24 void WebContentsObserverSanityChecker::Enable(WebContents* web_contents) { | 29 void WebContentsObserverSanityChecker::Enable(WebContents* web_contents) { |
25 if (web_contents->GetUserData(&kWebContentsObserverSanityCheckerKey)) | 30 if (web_contents->GetUserData(&kWebContentsObserverSanityCheckerKey)) |
26 return; | 31 return; |
27 web_contents->SetUserData(&kWebContentsObserverSanityCheckerKey, | 32 web_contents->SetUserData(&kWebContentsObserverSanityCheckerKey, |
28 new WebContentsObserverSanityChecker(web_contents)); | 33 new WebContentsObserverSanityChecker(web_contents)); |
29 } | 34 } |
30 | 35 |
31 void WebContentsObserverSanityChecker::RenderFrameCreated( | 36 void WebContentsObserverSanityChecker::RenderFrameCreated( |
32 RenderFrameHost* render_frame_host) { | 37 RenderFrameHost* render_frame_host) { |
| 38 LOG(ERROR) << "Checker[" << this << "]::RenderFrameCreated: " << Format(render
_frame_host); |
| 39 |
33 CHECK(!web_contents_destroyed_); | 40 CHECK(!web_contents_destroyed_); |
34 std::pair<int, int> routing_pair = | 41 std::pair<int, int> routing_pair = |
35 std::make_pair(render_frame_host->GetProcess()->GetID(), | 42 std::make_pair(render_frame_host->GetProcess()->GetID(), |
36 render_frame_host->GetRoutingID()); | 43 render_frame_host->GetRoutingID()); |
37 bool frame_exists = !live_routes_.insert(routing_pair).second; | 44 bool frame_exists = !live_routes_.insert(routing_pair).second; |
38 bool dead_already = deleted_routes_.count(routing_pair) != 0; | 45 deleted_routes_.erase(routing_pair); |
39 | 46 |
40 if (frame_exists) { | 47 if (frame_exists) { |
41 // TODO(nick): Disabled because of http://crbug.com/425397 | 48 std::string trace; |
42 #if 0 | 49 if (g_collect_stack_traces) { |
| 50 trace = "\nPrevious creation stack trace:\n" + |
| 51 render_frame_created_stacks_[routing_pair]; |
| 52 } |
43 CHECK(false) << "RenderFrameCreated called more than once for routing pair:" | 53 CHECK(false) << "RenderFrameCreated called more than once for routing pair:" |
44 << Format(render_frame_host); | 54 << Format(render_frame_host) << trace; |
45 #endif | 55 } |
46 } else if (dead_already) { | 56 |
47 CHECK(false) << "RenderFrameCreated called for routing pair that was " | 57 if (g_collect_stack_traces) { |
48 << "previously deleted: " << Format(render_frame_host); | 58 base::debug::StackTrace stack; |
| 59 render_frame_created_stacks_.insert( |
| 60 std::make_pair(routing_pair, stack.ToString())); |
| 61 render_frame_deleted_stacks_.erase(routing_pair); |
49 } | 62 } |
50 } | 63 } |
51 | 64 |
52 void WebContentsObserverSanityChecker::RenderFrameDeleted( | 65 void WebContentsObserverSanityChecker::RenderFrameDeleted( |
53 RenderFrameHost* render_frame_host) { | 66 RenderFrameHost* render_frame_host) { |
| 67 LOG(ERROR) << "Checker[" << this << "]::RenderFrameDeleted: " << Format(render
_frame_host); |
| 68 |
54 CHECK(!web_contents_destroyed_); | 69 CHECK(!web_contents_destroyed_); |
55 std::pair<int, int> routing_pair = | 70 std::pair<int, int> routing_pair = |
56 std::make_pair(render_frame_host->GetProcess()->GetID(), | 71 std::make_pair(render_frame_host->GetProcess()->GetID(), |
57 render_frame_host->GetRoutingID()); | 72 render_frame_host->GetRoutingID()); |
58 bool was_live = !!live_routes_.erase(routing_pair); | 73 bool was_live = !!live_routes_.erase(routing_pair); |
59 bool was_dead_already = !deleted_routes_.insert(routing_pair).second; | 74 bool was_dead_already = !deleted_routes_.insert(routing_pair).second; |
60 | 75 |
61 if (was_dead_already) { | 76 if (was_dead_already) { |
| 77 std::string trace; |
| 78 if (g_collect_stack_traces) { |
| 79 trace = "\nPrevious deletion stack trace:\n" + |
| 80 render_frame_deleted_stacks_[routing_pair]; |
| 81 } |
62 CHECK(false) << "RenderFrameDeleted called more than once for routing pair " | 82 CHECK(false) << "RenderFrameDeleted called more than once for routing pair " |
63 << Format(render_frame_host); | 83 << Format(render_frame_host) << trace; |
64 } else if (!was_live) { | 84 } else if (!was_live) { |
65 // TODO(nick): Clients can easily ignore an unrecognized object, but it | 85 // TODO(nick): Clients can easily ignore an unrecognized object, but it |
66 // would be useful from a finding-bugs perspective if we could enable this | 86 // would be useful from a finding-bugs perspective if we could enable this |
67 // check. | 87 // check. |
68 #if 0 | 88 #if 0 |
69 CHECK(false) << "RenderFrameDeleted called for routing pair " | 89 CHECK(false) << "RenderFrameDeleted called for routing pair " |
70 << Format(render_frame_host) | 90 << Format(render_frame_host) |
71 << " for which RenderFrameCreated was never called"; | 91 << " for which RenderFrameCreated was never called"; |
72 #endif | 92 #endif |
73 } | 93 } |
| 94 |
| 95 if (g_collect_stack_traces) { |
| 96 base::debug::StackTrace stack; |
| 97 render_frame_deleted_stacks_.insert( |
| 98 std::make_pair(routing_pair, stack.ToString())); |
| 99 render_frame_created_stacks_.erase(routing_pair); |
| 100 } |
74 } | 101 } |
75 | 102 |
76 void WebContentsObserverSanityChecker::RenderFrameForInterstitialPageCreated( | 103 void WebContentsObserverSanityChecker::RenderFrameForInterstitialPageCreated( |
77 RenderFrameHost* render_frame_host) { | 104 RenderFrameHost* render_frame_host) { |
78 // TODO(nick): Record this. | 105 // TODO(nick): Record this. |
79 } | 106 } |
80 | 107 |
81 void WebContentsObserverSanityChecker::RenderFrameHostChanged( | 108 void WebContentsObserverSanityChecker::RenderFrameHostChanged( |
82 RenderFrameHost* old_host, | 109 RenderFrameHost* old_host, |
83 RenderFrameHost* new_host) { | 110 RenderFrameHost* new_host) { |
84 CHECK(new_host); | 111 CHECK(new_host); |
85 if (old_host) | 112 CHECK_NE(new_host, old_host); |
86 AssertFrameExists(old_host); | 113 |
87 AssertFrameExists(new_host); | 114 if (old_host) { |
| 115 LOG(ERROR) << "Checker[" << this << "]::RenderFrameHostChanged: old:" << For
mat(old_host); |
| 116 |
| 117 AssertRenderFrameHostExists(old_host); |
| 118 std::pair<int, int> routing_pair = |
| 119 std::make_pair(old_host->GetProcess()->GetID(), |
| 120 old_host->GetRoutingID()); |
| 121 bool old_did_exist = !!current_hosts_.erase(routing_pair); |
| 122 if (!old_did_exist) { |
| 123 CHECK(false) |
| 124 << "RenderFrameHostChanged called with old host that did not exist:" |
| 125 << Format(old_host); |
| 126 } |
| 127 } else { |
| 128 LOG(ERROR) << "Checker[" << this << "]::RenderFrameHostChanged: old: null"; |
| 129 } |
| 130 |
| 131 LOG(ERROR) << "Checker[" << this << "]::RenderFrameHostChanged: new:" << Forma
t(new_host); |
| 132 |
| 133 std::pair<int, int> routing_pair = |
| 134 std::make_pair(new_host->GetProcess()->GetID(), |
| 135 new_host->GetRoutingID()); |
| 136 bool host_exists = !current_hosts_.insert(routing_pair).second; |
| 137 if (host_exists) { |
| 138 CHECK(false) |
| 139 << "RenderFrameHostChanged called more than once for routing pair:" |
| 140 << Format(new_host); |
| 141 } |
88 } | 142 } |
89 | 143 |
90 void WebContentsObserverSanityChecker::FrameDeleted( | 144 void WebContentsObserverSanityChecker::FrameDeleted( |
91 RenderFrameHost* render_frame_host) { | 145 RenderFrameHost* render_frame_host) { |
92 AssertFrameExists(render_frame_host); | 146 LOG(ERROR) << "Checker[" << this << "]::FrameDeleted: " << Format(render_frame
_host); |
| 147 AssertRenderFrameExists(render_frame_host); |
93 } | 148 } |
94 | 149 |
95 void WebContentsObserverSanityChecker::DidStartProvisionalLoadForFrame( | 150 void WebContentsObserverSanityChecker::DidStartProvisionalLoadForFrame( |
96 RenderFrameHost* render_frame_host, | 151 RenderFrameHost* render_frame_host, |
97 const GURL& validated_url, | 152 const GURL& validated_url, |
98 bool is_error_page, | 153 bool is_error_page, |
99 bool is_iframe_srcdoc) { | 154 bool is_iframe_srcdoc) { |
100 AssertFrameExists(render_frame_host); | 155 AssertRenderFrameExists(render_frame_host); |
101 } | 156 } |
102 | 157 |
103 void WebContentsObserverSanityChecker::DidCommitProvisionalLoadForFrame( | 158 void WebContentsObserverSanityChecker::DidCommitProvisionalLoadForFrame( |
104 RenderFrameHost* render_frame_host, | 159 RenderFrameHost* render_frame_host, |
105 const GURL& url, | 160 const GURL& url, |
106 ui::PageTransition transition_type) { | 161 ui::PageTransition transition_type) { |
107 AssertFrameExists(render_frame_host); | 162 AssertRenderFrameExists(render_frame_host); |
108 } | 163 } |
109 | 164 |
110 void WebContentsObserverSanityChecker::DidFailProvisionalLoad( | 165 void WebContentsObserverSanityChecker::DidFailProvisionalLoad( |
111 RenderFrameHost* render_frame_host, | 166 RenderFrameHost* render_frame_host, |
112 const GURL& validated_url, | 167 const GURL& validated_url, |
113 int error_code, | 168 int error_code, |
114 const base::string16& error_description) { | 169 const base::string16& error_description) { |
115 AssertFrameExists(render_frame_host); | 170 AssertRenderFrameExists(render_frame_host); |
116 } | 171 } |
117 | 172 |
118 void WebContentsObserverSanityChecker::DidNavigateMainFrame( | 173 void WebContentsObserverSanityChecker::DidNavigateMainFrame( |
119 const LoadCommittedDetails& details, | 174 const LoadCommittedDetails& details, |
120 const FrameNavigateParams& params) { | 175 const FrameNavigateParams& params) { |
121 AssertMainFrameExists(); | 176 AssertMainFrameExists(); |
122 } | 177 } |
123 | 178 |
124 void WebContentsObserverSanityChecker::DidNavigateAnyFrame( | 179 void WebContentsObserverSanityChecker::DidNavigateAnyFrame( |
125 RenderFrameHost* render_frame_host, | 180 RenderFrameHost* render_frame_host, |
126 const LoadCommittedDetails& details, | 181 const LoadCommittedDetails& details, |
127 const FrameNavigateParams& params) { | 182 const FrameNavigateParams& params) { |
128 AssertFrameExists(render_frame_host); | 183 AssertRenderFrameExists(render_frame_host); |
129 } | 184 } |
130 | 185 |
131 void WebContentsObserverSanityChecker::DocumentAvailableInMainFrame() { | 186 void WebContentsObserverSanityChecker::DocumentAvailableInMainFrame() { |
132 AssertMainFrameExists(); | 187 AssertMainFrameExists(); |
133 } | 188 } |
134 | 189 |
135 void WebContentsObserverSanityChecker::DocumentOnLoadCompletedInMainFrame() { | 190 void WebContentsObserverSanityChecker::DocumentOnLoadCompletedInMainFrame() { |
136 AssertMainFrameExists(); | 191 AssertMainFrameExists(); |
137 } | 192 } |
138 | 193 |
139 void WebContentsObserverSanityChecker::DocumentLoadedInFrame( | 194 void WebContentsObserverSanityChecker::DocumentLoadedInFrame( |
140 RenderFrameHost* render_frame_host) { | 195 RenderFrameHost* render_frame_host) { |
141 AssertFrameExists(render_frame_host); | 196 AssertRenderFrameExists(render_frame_host); |
142 } | 197 } |
143 | 198 |
144 void WebContentsObserverSanityChecker::DidFinishLoad( | 199 void WebContentsObserverSanityChecker::DidFinishLoad( |
145 RenderFrameHost* render_frame_host, | 200 RenderFrameHost* render_frame_host, |
146 const GURL& validated_url) { | 201 const GURL& validated_url) { |
147 AssertFrameExists(render_frame_host); | 202 AssertRenderFrameExists(render_frame_host); |
148 } | 203 } |
149 | 204 |
150 void WebContentsObserverSanityChecker::DidFailLoad( | 205 void WebContentsObserverSanityChecker::DidFailLoad( |
151 RenderFrameHost* render_frame_host, | 206 RenderFrameHost* render_frame_host, |
152 const GURL& validated_url, | 207 const GURL& validated_url, |
153 int error_code, | 208 int error_code, |
154 const base::string16& error_description) { | 209 const base::string16& error_description) { |
155 AssertFrameExists(render_frame_host); | 210 AssertRenderFrameExists(render_frame_host); |
156 } | 211 } |
157 | 212 |
158 void WebContentsObserverSanityChecker::DidGetRedirectForResourceRequest( | 213 void WebContentsObserverSanityChecker::DidGetRedirectForResourceRequest( |
159 RenderFrameHost* render_frame_host, | 214 RenderFrameHost* render_frame_host, |
160 const ResourceRedirectDetails& details) { | 215 const ResourceRedirectDetails& details) { |
161 AssertFrameExists(render_frame_host); | 216 AssertRenderFrameExists(render_frame_host); |
162 } | 217 } |
163 | 218 |
164 void WebContentsObserverSanityChecker::DidOpenRequestedURL( | 219 void WebContentsObserverSanityChecker::DidOpenRequestedURL( |
165 WebContents* new_contents, | 220 WebContents* new_contents, |
166 RenderFrameHost* source_render_frame_host, | 221 RenderFrameHost* source_render_frame_host, |
167 const GURL& url, | 222 const GURL& url, |
168 const Referrer& referrer, | 223 const Referrer& referrer, |
169 WindowOpenDisposition disposition, | 224 WindowOpenDisposition disposition, |
170 ui::PageTransition transition) { | 225 ui::PageTransition transition) { |
171 AssertFrameExists(source_render_frame_host); | 226 AssertRenderFrameExists(source_render_frame_host); |
172 } | 227 } |
173 | 228 |
174 bool WebContentsObserverSanityChecker::OnMessageReceived( | 229 bool WebContentsObserverSanityChecker::OnMessageReceived( |
175 const IPC::Message& message, | 230 const IPC::Message& message, |
176 RenderFrameHost* render_frame_host) { | 231 RenderFrameHost* render_frame_host) { |
177 #if !defined(OS_MACOSX) | 232 #if !defined(OS_MACOSX) |
178 // TODO(avi): Disabled because of http://crbug.com/445054 | 233 // TODO(avi): Disabled because of http://crbug.com/445054 |
179 AssertFrameExists(render_frame_host); | 234 AssertRenderFrameExists(render_frame_host); |
180 #endif | 235 #endif |
181 return false; | 236 return false; |
182 } | 237 } |
183 | 238 |
184 void WebContentsObserverSanityChecker::WebContentsDestroyed() { | 239 void WebContentsObserverSanityChecker::WebContentsDestroyed() { |
185 CHECK(!web_contents_destroyed_); | 240 CHECK(!web_contents_destroyed_); |
186 web_contents_destroyed_ = true; | 241 web_contents_destroyed_ = true; |
187 } | 242 } |
188 | 243 |
189 WebContentsObserverSanityChecker::WebContentsObserverSanityChecker( | 244 WebContentsObserverSanityChecker::WebContentsObserverSanityChecker( |
190 WebContents* web_contents) | 245 WebContents* web_contents) |
191 : WebContentsObserver(web_contents), web_contents_destroyed_(false) { | 246 : WebContentsObserver(web_contents), web_contents_destroyed_(false) { |
| 247 LOG(ERROR) << "SanityChecker[" << this << "]::ctor"; |
| 248 |
192 // Prime the pump with the initial objects. | 249 // Prime the pump with the initial objects. |
193 RenderViewCreated(web_contents->GetRenderViewHost()); | 250 RenderViewCreated(web_contents->GetRenderViewHost()); |
194 RenderFrameCreated(web_contents->GetMainFrame()); | |
195 } | 251 } |
196 | 252 |
197 WebContentsObserverSanityChecker::~WebContentsObserverSanityChecker() { | 253 WebContentsObserverSanityChecker::~WebContentsObserverSanityChecker() { |
| 254 LOG(ERROR) << "SanityChecker[" << this << "]::dtor"; |
198 CHECK(web_contents_destroyed_); | 255 CHECK(web_contents_destroyed_); |
199 } | 256 } |
200 | 257 |
201 void WebContentsObserverSanityChecker::AssertFrameExists( | 258 void WebContentsObserverSanityChecker::AssertRenderFrameHostExists( |
202 RenderFrameHost* render_frame_host) { | 259 RenderFrameHost* render_frame_host) { |
203 CHECK(!web_contents_destroyed_); | 260 CHECK(!web_contents_destroyed_); |
204 std::pair<int, int> routing_pair = | 261 std::pair<int, int> routing_pair = |
205 std::make_pair(render_frame_host->GetProcess()->GetID(), | 262 std::make_pair(render_frame_host->GetProcess()->GetID(), |
206 render_frame_host->GetRoutingID()); | 263 render_frame_host->GetRoutingID()); |
| 264 |
| 265 bool host_changed_happened = current_hosts_.count(routing_pair) != 0; |
| 266 bool host_deleted_happened = deleted_hosts_.count(routing_pair) != 0; |
| 267 |
| 268 CHECK(host_changed_happened) |
| 269 << "A RenderFrameHost pointer was passed to a WebContentsObserver " |
| 270 << "method, but WebContentsObserver::RenderFrameHostChanged was never " |
| 271 << "called for that RenderFrameHost: " << Format(render_frame_host); |
| 272 CHECK(!host_deleted_happened) |
| 273 << "A RenderFrameHost pointer was passed to a WebContentsObserver " |
| 274 << "method, but WebContentsObserver::FrameDeleted was never " |
| 275 << "called for that RenderFrameHost: " << Format(render_frame_host); |
| 276 } |
| 277 |
| 278 void WebContentsObserverSanityChecker::AssertRenderFrameExists( |
| 279 RenderFrameHost* render_frame_host) { |
| 280 CHECK(!web_contents_destroyed_); |
| 281 std::pair<int, int> routing_pair = |
| 282 std::make_pair(render_frame_host->GetProcess()->GetID(), |
| 283 render_frame_host->GetRoutingID()); |
| 284 |
207 bool render_frame_created_happened = live_routes_.count(routing_pair) != 0; | 285 bool render_frame_created_happened = live_routes_.count(routing_pair) != 0; |
208 bool render_frame_deleted_happened = deleted_routes_.count(routing_pair) != 0; | 286 bool render_frame_deleted_happened = deleted_routes_.count(routing_pair) != 0; |
209 | 287 |
210 CHECK(render_frame_created_happened) | 288 CHECK(render_frame_created_happened) |
211 << "A RenderFrameHost pointer was passed to a WebContentsObserver " | 289 << "A RenderFrameHost pointer was passed to a WebContentsObserver " |
212 << "method, but WebContentsObserver::RenderFrameCreated was never called " | 290 << "method, but WebContentsObserver::RenderFrameCreated was never called " |
213 << "for that RenderFrameHost: " << Format(render_frame_host); | 291 << "for that RenderFrameHost: " << Format(render_frame_host); |
214 CHECK(!render_frame_deleted_happened) | 292 CHECK(!render_frame_deleted_happened) |
215 << "A RenderFrameHost pointer was passed to a WebContentsObserver " | 293 << "A RenderFrameHost pointer was passed to a WebContentsObserver " |
216 << "method, but WebContentsObserver::RenderFrameDeleted had already been " | 294 << "method, but WebContentsObserver::RenderFrameDeleted had already been " |
217 << "called on that frame:" << Format(render_frame_host); | 295 << "called on that frame:" << Format(render_frame_host); |
218 } | 296 } |
219 | 297 |
220 void WebContentsObserverSanityChecker::AssertMainFrameExists() { | 298 void WebContentsObserverSanityChecker::AssertMainFrameExists() { |
221 AssertFrameExists(web_contents()->GetMainFrame()); | 299 AssertRenderFrameExists(web_contents()->GetMainFrame()); |
222 } | 300 } |
223 | 301 |
224 std::string WebContentsObserverSanityChecker::Format( | 302 std::string WebContentsObserverSanityChecker::Format( |
225 RenderFrameHost* render_frame_host) { | 303 RenderFrameHost* render_frame_host) { |
226 return base::StringPrintf( | 304 return base::StringPrintf( |
227 "(%d, %d -> %s )", render_frame_host->GetProcess()->GetID(), | 305 "(%d, %d -> %s)", render_frame_host->GetProcess()->GetID(), |
228 render_frame_host->GetRoutingID(), | 306 render_frame_host->GetRoutingID(), |
229 render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str()); | 307 render_frame_host->GetSiteInstance()->GetSiteURL().spec().c_str()); |
230 } | 308 } |
231 | 309 |
232 } // namespace content | 310 } // namespace content |
OLD | NEW |