cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / browser / devtools / devtools_manager_unittest.cc
blob4c52321a8de082f614c3718a0c576a955545b859
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/time/time.h"
8 #include "content/browser/devtools/devtools_manager.h"
9 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
10 #include "content/browser/devtools/render_view_devtools_agent_host.h"
11 #include "content/browser/shared_worker/shared_worker_instance.h"
12 #include "content/browser/shared_worker/worker_storage_partition.h"
13 #include "content/common/view_messages.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/browser/devtools_agent_host.h"
17 #include "content/public/browser/devtools_external_agent_proxy.h"
18 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
19 #include "content/public/browser/devtools_target.h"
20 #include "content/public/browser/web_contents_delegate.h"
21 #include "content/public/test/test_utils.h"
22 #include "content/test/test_content_browser_client.h"
23 #include "content/test/test_render_view_host.h"
24 #include "content/test/test_web_contents.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 using base::TimeDelta;
29 namespace content {
30 namespace {
32 class TestDevToolsClientHost : public DevToolsAgentHostClient {
33 public:
34 TestDevToolsClientHost()
35 : last_sent_message(NULL),
36 closed_(false) {
39 ~TestDevToolsClientHost() override { EXPECT_TRUE(closed_); }
41 void Close() {
42 EXPECT_FALSE(closed_);
43 close_counter++;
44 agent_host_->DetachClient();
45 closed_ = true;
48 void AgentHostClosed(DevToolsAgentHost* agent_host, bool replaced) override {
49 FAIL();
52 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
53 const std::string& message) override {
54 last_sent_message = &message;
57 void InspectAgentHost(DevToolsAgentHost* agent_host) {
58 agent_host_ = agent_host;
59 agent_host_->AttachClient(this);
62 DevToolsAgentHost* agent_host() { return agent_host_.get(); }
64 static void ResetCounters() {
65 close_counter = 0;
68 static int close_counter;
70 const std::string* last_sent_message;
72 private:
73 bool closed_;
74 scoped_refptr<DevToolsAgentHost> agent_host_;
76 DISALLOW_COPY_AND_ASSIGN(TestDevToolsClientHost);
79 int TestDevToolsClientHost::close_counter = 0;
82 class TestWebContentsDelegate : public WebContentsDelegate {
83 public:
84 TestWebContentsDelegate() : renderer_unresponsive_received_(false) {}
86 // Notification that the contents is hung.
87 void RendererUnresponsive(WebContents* source) override {
88 renderer_unresponsive_received_ = true;
91 bool renderer_unresponsive_received() const {
92 return renderer_unresponsive_received_;
95 private:
96 bool renderer_unresponsive_received_;
99 class TestTarget : public DevToolsTarget {
100 public:
101 explicit TestTarget(scoped_refptr<DevToolsAgentHost> agent_host)
102 : agent_host_(agent_host) {}
103 ~TestTarget() override {}
105 std::string GetId() const override { return agent_host_->GetId(); }
106 std::string GetParentId() const override { return std::string(); }
107 std::string GetType() const override { return std::string(); }
108 std::string GetTitle() const override { return agent_host_->GetTitle(); }
109 std::string GetDescription() const override { return std::string(); }
110 GURL GetURL() const override { return agent_host_->GetURL(); }
111 GURL GetFaviconURL() const override { return GURL(); }
112 base::TimeTicks GetLastActivityTime() const override {
113 return base::TimeTicks();
115 bool IsAttached() const override { return agent_host_->IsAttached(); }
116 scoped_refptr<DevToolsAgentHost> GetAgentHost() const override {
117 return agent_host_;
119 bool Activate() const override { return agent_host_->Activate(); }
120 bool Close() const override { return agent_host_->Close(); }
122 private:
123 scoped_refptr<DevToolsAgentHost> agent_host_;
126 class TestDevToolsManagerDelegate : public DevToolsManagerDelegate {
127 public:
128 ~TestDevToolsManagerDelegate() override {}
130 void Inspect(BrowserContext* browser_context,
131 DevToolsAgentHost* agent_host) override {}
133 void DevToolsAgentStateChanged(DevToolsAgentHost* agent_host,
134 bool attached) override {}
136 base::DictionaryValue* HandleCommand(
137 DevToolsAgentHost* agent_host,
138 base::DictionaryValue* command) override {
139 return NULL;
142 scoped_ptr<DevToolsTarget> CreateNewTarget(const GURL& url) override {
143 return scoped_ptr<DevToolsTarget>();
146 void EnumerateTargets(TargetCallback callback) override {
147 TargetList result;
148 DevToolsAgentHost::List agents = DevToolsAgentHost::GetOrCreateAll();
149 for (DevToolsAgentHost::List::iterator it = agents.begin();
150 it != agents.end(); ++it) {
151 if ((*it)->GetType() == DevToolsAgentHost::TYPE_WEB_CONTENTS)
152 result.insert(result.begin(), new TestTarget(*it));
153 else
154 result.push_back(new TestTarget(*it));
156 callback.Run(result);
159 std::string GetPageThumbnailData(const GURL& url) override {
160 return std::string();
164 class ContentBrowserClientWithDevTools : public TestContentBrowserClient {
165 public:
166 ~ContentBrowserClientWithDevTools() override {}
167 content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override {
168 return new TestDevToolsManagerDelegate();
172 class TestDevToolsManagerObserver : public DevToolsManager::Observer {
173 public:
174 TestDevToolsManagerObserver()
175 : updates_count_(0) {}
176 ~TestDevToolsManagerObserver() override {}
178 int updates_count() { return updates_count_; }
179 const std::vector<scoped_refptr<DevToolsAgentHost>> hosts() {
180 return hosts_;
183 void TargetListChanged(const TargetList& targets) override {
184 updates_count_++;
185 hosts_.clear();
186 for (TargetList::const_iterator it = targets.begin();
187 it != targets.end(); ++it) {
188 hosts_.push_back((*it)->GetAgentHost());
192 private:
193 int updates_count_;
194 std::vector<scoped_refptr<DevToolsAgentHost>> hosts_;
197 } // namespace
199 class DevToolsManagerTest : public RenderViewHostImplTestHarness {
200 public:
201 DevToolsManagerTest()
202 : old_browser_client_(NULL) {}
204 protected:
205 virtual void SetUp() override {
206 RenderViewHostImplTestHarness::SetUp();
207 TestDevToolsClientHost::ResetCounters();
208 old_browser_client_ = SetBrowserClientForTesting(&browser_client_);
211 virtual void TearDown() override {
212 SetBrowserClientForTesting(old_browser_client_);
213 RenderViewHostImplTestHarness::TearDown();
216 ContentBrowserClientWithDevTools browser_client_;
217 ContentBrowserClient* old_browser_client_;
220 TEST_F(DevToolsManagerTest, OpenAndManuallyCloseDevToolsClientHost) {
221 scoped_refptr<DevToolsAgentHost> agent(
222 DevToolsAgentHost::GetOrCreateFor(web_contents()));
223 EXPECT_FALSE(agent->IsAttached());
225 TestDevToolsClientHost client_host;
226 client_host.InspectAgentHost(agent.get());
227 // Test that the connection is established.
228 EXPECT_TRUE(agent->IsAttached());
229 EXPECT_EQ(0, TestDevToolsClientHost::close_counter);
231 client_host.Close();
232 EXPECT_EQ(1, TestDevToolsClientHost::close_counter);
233 EXPECT_FALSE(agent->IsAttached());
236 TEST_F(DevToolsManagerTest, NoUnresponsiveDialogInInspectedContents) {
237 TestRenderViewHost* inspected_rvh = test_rvh();
238 inspected_rvh->set_render_view_created(true);
239 EXPECT_FALSE(contents()->GetDelegate());
240 TestWebContentsDelegate delegate;
241 contents()->SetDelegate(&delegate);
243 TestDevToolsClientHost client_host;
244 scoped_refptr<DevToolsAgentHost> agent_host(DevToolsAgentHost::GetOrCreateFor(
245 WebContents::FromRenderViewHost(inspected_rvh)));
246 client_host.InspectAgentHost(agent_host.get());
248 // Start with a short timeout.
249 inspected_rvh->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
250 // Wait long enough for first timeout and see if it fired.
251 base::MessageLoop::current()->PostDelayedTask(
252 FROM_HERE,
253 base::MessageLoop::QuitClosure(),
254 TimeDelta::FromMilliseconds(10));
255 base::MessageLoop::current()->Run();
256 EXPECT_FALSE(delegate.renderer_unresponsive_received());
258 // Now close devtools and check that the notification is delivered.
259 client_host.Close();
260 // Start with a short timeout.
261 inspected_rvh->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
262 // Wait long enough for first timeout and see if it fired.
263 base::MessageLoop::current()->PostDelayedTask(
264 FROM_HERE,
265 base::MessageLoop::QuitClosure(),
266 TimeDelta::FromMilliseconds(10));
267 base::MessageLoop::current()->Run();
268 EXPECT_TRUE(delegate.renderer_unresponsive_received());
270 contents()->SetDelegate(NULL);
273 TEST_F(DevToolsManagerTest, ReattachOnCancelPendingNavigation) {
274 // Navigate to URL. First URL should use first RenderViewHost.
275 const GURL url("http://www.google.com");
276 controller().LoadURL(
277 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
278 contents()->TestDidNavigate(
279 contents()->GetMainFrame(), 1, url, ui::PAGE_TRANSITION_TYPED);
280 EXPECT_FALSE(contents()->cross_navigation_pending());
282 TestDevToolsClientHost client_host;
283 client_host.InspectAgentHost(
284 DevToolsAgentHost::GetOrCreateFor(web_contents()).get());
286 // Navigate to new site which should get a new RenderViewHost.
287 const GURL url2("http://www.yahoo.com");
288 controller().LoadURL(
289 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
290 EXPECT_TRUE(contents()->cross_navigation_pending());
291 EXPECT_EQ(client_host.agent_host(),
292 DevToolsAgentHost::GetOrCreateFor(web_contents()).get());
294 // Interrupt pending navigation and navigate back to the original site.
295 controller().LoadURL(
296 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
297 contents()->TestDidNavigate(
298 contents()->GetMainFrame(), 1, url, ui::PAGE_TRANSITION_TYPED);
299 EXPECT_FALSE(contents()->cross_navigation_pending());
300 EXPECT_EQ(client_host.agent_host(),
301 DevToolsAgentHost::GetOrCreateFor(web_contents()).get());
302 client_host.Close();
305 class TestExternalAgentDelegate: public DevToolsExternalAgentProxyDelegate {
306 std::map<std::string,int> event_counter_;
308 void recordEvent(const std::string& name) {
309 if (event_counter_.find(name) == event_counter_.end())
310 event_counter_[name] = 0;
311 event_counter_[name] = event_counter_[name] + 1;
314 void expectEvent(int count, const std::string& name) {
315 EXPECT_EQ(count, event_counter_[name]);
318 void Attach(DevToolsExternalAgentProxy* proxy) override {
319 recordEvent("Attach");
322 void Detach() override { recordEvent("Detach"); };
324 void SendMessageToBackend(const std::string& message) override {
325 recordEvent(std::string("SendMessageToBackend.") + message);
328 public :
329 ~TestExternalAgentDelegate() override {
330 expectEvent(1, "Attach");
331 expectEvent(1, "Detach");
332 expectEvent(0, "SendMessageToBackend.message0");
333 expectEvent(1, "SendMessageToBackend.message1");
334 expectEvent(2, "SendMessageToBackend.message2");
338 TEST_F(DevToolsManagerTest, TestExternalProxy) {
339 TestExternalAgentDelegate* delegate = new TestExternalAgentDelegate();
341 scoped_refptr<DevToolsAgentHost> agent_host =
342 DevToolsAgentHost::Create(delegate);
343 EXPECT_EQ(agent_host, DevToolsAgentHost::GetForId(agent_host->GetId()));
345 TestDevToolsClientHost client_host;
346 client_host.InspectAgentHost(agent_host.get());
347 agent_host->DispatchProtocolMessage("message1");
348 agent_host->DispatchProtocolMessage("message2");
349 agent_host->DispatchProtocolMessage("message2");
351 client_host.Close();
354 class TestDevToolsManagerScheduler {
355 public:
356 DevToolsManager::Scheduler callback() {
357 return base::Bind(&TestDevToolsManagerScheduler::Schedule,
358 base::Unretained(this));
361 void Run() {
362 ASSERT_FALSE(closure_.is_null());
363 base::Closure copy = closure_;
364 closure_.Reset();
365 copy.Run();
368 bool IsEmpty() {
369 return closure_.is_null();
372 private:
373 void Schedule(base::Closure closure) {
374 EXPECT_TRUE(closure_.is_null());
375 closure_ = closure;
378 base::Closure closure_;
381 TEST_F(DevToolsManagerTest, TestObserver) {
382 GURL url1("data:text/html,<body>Body1</body>");
383 GURL url2("data:text/html,<body>Body2</body>");
384 GURL url3("data:text/html,<body>Body3</body>");
386 TestDevToolsManagerScheduler scheduler;
387 DevToolsManager* manager = DevToolsManager::GetInstance();
388 manager->SetSchedulerForTest(scheduler.callback());
390 contents()->NavigateAndCommit(url1);
391 RunAllPendingInMessageLoop();
393 scoped_ptr<TestDevToolsManagerObserver> observer(
394 new TestDevToolsManagerObserver());
395 manager->AddObserver(observer.get());
396 // Added observer should get an update.
397 EXPECT_EQ(1, observer->updates_count());
398 ASSERT_EQ(1u, observer->hosts().size());
399 EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents());
400 EXPECT_EQ(url1.spec(), observer->hosts()[0]->GetURL().spec());
402 contents()->NavigateAndCommit(url2);
403 RunAllPendingInMessageLoop();
404 contents()->NavigateAndCommit(url3);
405 scheduler.Run();
406 // Updates should be coalesced.
407 EXPECT_EQ(2, observer->updates_count());
408 ASSERT_EQ(1u, observer->hosts().size());
409 EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents());
410 EXPECT_EQ(url3.spec(), observer->hosts()[0]->GetURL().spec());
412 // Check there were no extra updates.
413 scheduler.Run();
414 EXPECT_TRUE(scheduler.IsEmpty());
415 EXPECT_EQ(2, observer->updates_count());
417 scoped_ptr<WorkerStoragePartition> partition(new WorkerStoragePartition(
418 browser_context()->GetRequestContext(),
419 NULL, NULL, NULL, NULL, NULL, NULL, NULL));
420 WorkerStoragePartitionId partition_id(*partition.get());
422 GURL shared_worker_url("http://example.com/shared_worker.js");
423 SharedWorkerInstance shared_worker(
424 shared_worker_url,
425 base::string16(),
426 base::string16(),
427 blink::WebContentSecurityPolicyTypeReport,
428 browser_context()->GetResourceContext(),
429 partition_id);
430 EmbeddedWorkerDevToolsManager::GetInstance()->SharedWorkerCreated(
431 1, 1, shared_worker);
432 contents()->NavigateAndCommit(url2);
434 EXPECT_EQ(3, observer->updates_count());
435 ASSERT_EQ(2u, observer->hosts().size());
436 EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents());
437 EXPECT_EQ(url2.spec(), observer->hosts()[0]->GetURL().spec());
438 EXPECT_EQ(DevToolsAgentHost::TYPE_SHARED_WORKER,
439 observer->hosts()[1]->GetType());
440 EXPECT_EQ(shared_worker_url.spec(), observer->hosts()[1]->GetURL().spec());
442 EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(1, 1);
443 scheduler.Run();
444 EXPECT_EQ(4, observer->updates_count());
445 ASSERT_EQ(1u, observer->hosts().size());
446 EXPECT_EQ(contents(), observer->hosts()[0]->GetWebContents());
447 EXPECT_EQ(url2.spec(), observer->hosts()[0]->GetURL().spec());
449 // Check there were no extra updates.
450 scheduler.Run();
451 EXPECT_TRUE(scheduler.IsEmpty());
452 EXPECT_EQ(4, observer->updates_count());
454 manager->RemoveObserver(observer.get());
456 EXPECT_TRUE(scheduler.IsEmpty());
457 manager->SetSchedulerForTest(DevToolsManager::Scheduler());
460 } // namespace content