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
;
32 class TestDevToolsClientHost
: public DevToolsAgentHostClient
{
34 TestDevToolsClientHost()
35 : last_sent_message(NULL
),
39 ~TestDevToolsClientHost() override
{ EXPECT_TRUE(closed_
); }
42 EXPECT_FALSE(closed_
);
44 agent_host_
->DetachClient();
48 void AgentHostClosed(DevToolsAgentHost
* agent_host
, bool replaced
) override
{
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() {
68 static int close_counter
;
70 const std::string
* last_sent_message
;
74 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
76 DISALLOW_COPY_AND_ASSIGN(TestDevToolsClientHost
);
79 int TestDevToolsClientHost::close_counter
= 0;
82 class TestWebContentsDelegate
: public WebContentsDelegate
{
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_
;
96 bool renderer_unresponsive_received_
;
99 class TestTarget
: public DevToolsTarget
{
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
{
119 bool Activate() const override
{ return agent_host_
->Activate(); }
120 bool Close() const override
{ return agent_host_
->Close(); }
123 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
126 class TestDevToolsManagerDelegate
: public DevToolsManagerDelegate
{
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
{
142 scoped_ptr
<DevToolsTarget
> CreateNewTarget(const GURL
& url
) override
{
143 return scoped_ptr
<DevToolsTarget
>();
146 void EnumerateTargets(TargetCallback callback
) override
{
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
));
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
{
166 ~ContentBrowserClientWithDevTools() override
{}
167 content::DevToolsManagerDelegate
* GetDevToolsManagerDelegate() override
{
168 return new TestDevToolsManagerDelegate();
172 class TestDevToolsManagerObserver
: public DevToolsManager::Observer
{
174 TestDevToolsManagerObserver()
175 : updates_count_(0) {}
176 ~TestDevToolsManagerObserver() override
{}
178 int updates_count() { return updates_count_
; }
179 const std::vector
<scoped_refptr
<DevToolsAgentHost
>> hosts() {
183 void TargetListChanged(const TargetList
& targets
) override
{
186 for (TargetList::const_iterator it
= targets
.begin();
187 it
!= targets
.end(); ++it
) {
188 hosts_
.push_back((*it
)->GetAgentHost());
194 std::vector
<scoped_refptr
<DevToolsAgentHost
>> hosts_
;
199 class DevToolsManagerTest
: public RenderViewHostImplTestHarness
{
201 DevToolsManagerTest()
202 : old_browser_client_(NULL
) {}
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
);
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(
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.
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(
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());
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
);
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");
354 class TestDevToolsManagerScheduler
{
356 DevToolsManager::Scheduler
callback() {
357 return base::Bind(&TestDevToolsManagerScheduler::Schedule
,
358 base::Unretained(this));
362 ASSERT_FALSE(closure_
.is_null());
363 base::Closure copy
= closure_
;
369 return closure_
.is_null();
373 void Schedule(base::Closure closure
) {
374 EXPECT_TRUE(closure_
.is_null());
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
);
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.
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(
427 blink::WebContentSecurityPolicyTypeReport
,
428 browser_context()->GetResourceContext(),
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);
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.
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