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.
4
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_impl.h"
9#include "content/browser/devtools/render_view_devtools_agent_host.h"
10#include "content/browser/renderer_host/test_render_view_host.h"
11#include "content/common/view_messages.h"
12#include "content/public/browser/content_browser_client.h"
13#include "content/public/browser/devtools_agent_host.h"
14#include "content/public/browser/devtools_client_host.h"
15#include "content/public/browser/devtools_external_agent_proxy.h"
16#include "content/public/browser/devtools_external_agent_proxy_delegate.h"
17#include "content/public/browser/web_contents_delegate.h"
18#include "content/test/test_content_browser_client.h"
19#include "content/test/test_web_contents.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22using base::TimeDelta;
23
24namespace content {
25namespace {
26
27class TestDevToolsClientHost : public DevToolsClientHost {
28 public:
29  TestDevToolsClientHost()
30      : last_sent_message(NULL),
31        closed_(false) {
32  }
33
34  virtual ~TestDevToolsClientHost() {
35    EXPECT_TRUE(closed_);
36  }
37
38  virtual void Close(DevToolsManager* manager) {
39    EXPECT_FALSE(closed_);
40    close_counter++;
41    manager->ClientHostClosing(this);
42    closed_ = true;
43  }
44  virtual void InspectedContentsClosing() OVERRIDE {
45    FAIL();
46  }
47
48  virtual void DispatchOnInspectorFrontend(
49      const std::string& message) OVERRIDE {
50    last_sent_message = &message;
51  }
52
53  virtual void ReplacedWithAnotherClient() OVERRIDE {
54  }
55
56  static void ResetCounters() {
57    close_counter = 0;
58  }
59
60  static int close_counter;
61
62  const std::string* last_sent_message;
63
64 private:
65  bool closed_;
66
67  DISALLOW_COPY_AND_ASSIGN(TestDevToolsClientHost);
68};
69
70int TestDevToolsClientHost::close_counter = 0;
71
72
73class TestWebContentsDelegate : public WebContentsDelegate {
74 public:
75  TestWebContentsDelegate() : renderer_unresponsive_received_(false) {}
76
77  // Notification that the contents is hung.
78  virtual void RendererUnresponsive(WebContents* source) OVERRIDE {
79    renderer_unresponsive_received_ = true;
80  }
81
82  bool renderer_unresponsive_received() const {
83    return renderer_unresponsive_received_;
84  }
85
86 private:
87  bool renderer_unresponsive_received_;
88};
89
90class DevToolsManagerTestBrowserClient : public TestContentBrowserClient {
91 public:
92  DevToolsManagerTestBrowserClient() {
93  }
94
95  virtual bool ShouldSwapProcessesForNavigation(
96      SiteInstance* site_instance,
97      const GURL& current_url,
98      const GURL& new_url) OVERRIDE {
99    return true;
100  }
101
102 private:
103  DISALLOW_COPY_AND_ASSIGN(DevToolsManagerTestBrowserClient);
104};
105
106}  // namespace
107
108class DevToolsManagerTest : public RenderViewHostImplTestHarness {
109 protected:
110  virtual void SetUp() OVERRIDE {
111    original_browser_client_ = SetBrowserClientForTesting(&browser_client_);
112
113    RenderViewHostImplTestHarness::SetUp();
114    TestDevToolsClientHost::ResetCounters();
115  }
116
117  virtual void TearDown() OVERRIDE {
118    RenderViewHostImplTestHarness::TearDown();
119    SetBrowserClientForTesting(original_browser_client_);
120  }
121
122 private:
123  ContentBrowserClient* original_browser_client_;
124  DevToolsManagerTestBrowserClient browser_client_;
125};
126
127TEST_F(DevToolsManagerTest, OpenAndManuallyCloseDevToolsClientHost) {
128  DevToolsManager* manager = DevToolsManager::GetInstance();
129
130  scoped_refptr<DevToolsAgentHost> agent(
131      DevToolsAgentHost::GetOrCreateFor(rvh()));
132  EXPECT_FALSE(agent->IsAttached());
133
134  TestDevToolsClientHost client_host;
135  manager->RegisterDevToolsClientHostFor(agent.get(), &client_host);
136  // Test that the connection is established.
137  EXPECT_TRUE(agent->IsAttached());
138  EXPECT_EQ(agent, manager->GetDevToolsAgentHostFor(&client_host));
139  EXPECT_EQ(0, TestDevToolsClientHost::close_counter);
140
141  client_host.Close(manager);
142  EXPECT_EQ(1, TestDevToolsClientHost::close_counter);
143  EXPECT_FALSE(agent->IsAttached());
144}
145
146TEST_F(DevToolsManagerTest, ForwardMessageToClient) {
147  DevToolsManagerImpl* manager = DevToolsManagerImpl::GetInstance();
148
149  TestDevToolsClientHost client_host;
150  scoped_refptr<DevToolsAgentHost> agent_host(
151      DevToolsAgentHost::GetOrCreateFor(rvh()));
152  manager->RegisterDevToolsClientHostFor(agent_host.get(), &client_host);
153  EXPECT_EQ(0, TestDevToolsClientHost::close_counter);
154
155  std::string m = "test message";
156  agent_host = DevToolsAgentHost::GetOrCreateFor(rvh());
157  manager->DispatchOnInspectorFrontend(agent_host.get(), m);
158  EXPECT_TRUE(&m == client_host.last_sent_message);
159
160  client_host.Close(manager);
161  EXPECT_EQ(1, TestDevToolsClientHost::close_counter);
162}
163
164TEST_F(DevToolsManagerTest, NoUnresponsiveDialogInInspectedContents) {
165  TestRenderViewHost* inspected_rvh = test_rvh();
166  inspected_rvh->set_render_view_created(true);
167  EXPECT_FALSE(contents()->GetDelegate());
168  TestWebContentsDelegate delegate;
169  contents()->SetDelegate(&delegate);
170
171  TestDevToolsClientHost client_host;
172  scoped_refptr<DevToolsAgentHost> agent_host(
173      DevToolsAgentHost::GetOrCreateFor(inspected_rvh));
174  DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
175      agent_host.get(), &client_host);
176
177  // Start with a short timeout.
178  inspected_rvh->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
179  // Wait long enough for first timeout and see if it fired.
180  base::MessageLoop::current()->PostDelayedTask(
181      FROM_HERE,
182      base::MessageLoop::QuitClosure(),
183      TimeDelta::FromMilliseconds(10));
184  base::MessageLoop::current()->Run();
185  EXPECT_FALSE(delegate.renderer_unresponsive_received());
186
187  // Now close devtools and check that the notification is delivered.
188  client_host.Close(DevToolsManager::GetInstance());
189  // Start with a short timeout.
190  inspected_rvh->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
191  // Wait long enough for first timeout and see if it fired.
192  base::MessageLoop::current()->PostDelayedTask(
193      FROM_HERE,
194      base::MessageLoop::QuitClosure(),
195      TimeDelta::FromMilliseconds(10));
196  base::MessageLoop::current()->Run();
197  EXPECT_TRUE(delegate.renderer_unresponsive_received());
198
199  contents()->SetDelegate(NULL);
200}
201
202TEST_F(DevToolsManagerTest, ReattachOnCancelPendingNavigation) {
203  contents()->transition_cross_site = true;
204  // Navigate to URL.  First URL should use first RenderViewHost.
205  const GURL url("http://www.google.com");
206  controller().LoadURL(
207      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
208  contents()->TestDidNavigate(rvh(), 1, url, PAGE_TRANSITION_TYPED);
209  EXPECT_FALSE(contents()->cross_navigation_pending());
210
211  TestDevToolsClientHost client_host;
212  DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
213  devtools_manager->RegisterDevToolsClientHostFor(
214      DevToolsAgentHost::GetOrCreateFor(rvh()).get(), &client_host);
215
216  // Navigate to new site which should get a new RenderViewHost.
217  const GURL url2("http://www.yahoo.com");
218  controller().LoadURL(
219      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
220  EXPECT_TRUE(contents()->cross_navigation_pending());
221  EXPECT_EQ(devtools_manager->GetDevToolsAgentHostFor(&client_host),
222      DevToolsAgentHost::GetOrCreateFor(pending_rvh()));
223
224  // Interrupt pending navigation and navigate back to the original site.
225  controller().LoadURL(
226      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
227  contents()->TestDidNavigate(rvh(), 1, url, PAGE_TRANSITION_TYPED);
228  EXPECT_FALSE(contents()->cross_navigation_pending());
229  EXPECT_EQ(devtools_manager->GetDevToolsAgentHostFor(&client_host),
230      DevToolsAgentHost::GetOrCreateFor(rvh()));
231  client_host.Close(DevToolsManager::GetInstance());
232}
233
234class TestExternalAgentDelegate: public DevToolsExternalAgentProxyDelegate {
235  std::map<std::string,int> event_counter_;
236
237  void recordEvent(const std::string& name) {
238    if (event_counter_.find(name) == event_counter_.end())
239      event_counter_[name] = 0;
240    event_counter_[name] = event_counter_[name] + 1;
241  }
242
243  void expectEvent(int count, const std::string& name) {
244    EXPECT_EQ(count, event_counter_[name]);
245  }
246
247  virtual void Attach() OVERRIDE {
248    recordEvent("Attach");
249  };
250
251  virtual void Detach() OVERRIDE {
252    recordEvent("Detach");
253  };
254
255  virtual void SendMessageToBackend(const std::string& message) OVERRIDE {
256    recordEvent(std::string("SendMessageToBackend.") + message);
257  };
258
259 public :
260  virtual ~TestExternalAgentDelegate() {
261    expectEvent(1, "Attach");
262    expectEvent(1, "Detach");
263    expectEvent(0, "SendMessageToBackend.message0");
264    expectEvent(1, "SendMessageToBackend.message1");
265    expectEvent(2, "SendMessageToBackend.message2");
266  }
267};
268
269TEST_F(DevToolsManagerTest, TestExternalProxy) {
270  TestExternalAgentDelegate delegate;
271
272  scoped_ptr<DevToolsExternalAgentProxy> proxy(
273      DevToolsExternalAgentProxy::Create(&delegate));
274
275  scoped_refptr<DevToolsAgentHost> agent_host = proxy->GetAgentHost();
276  EXPECT_EQ(agent_host, DevToolsAgentHost::GetForId(agent_host->GetId()));
277
278  DevToolsManager* manager = DevToolsManager::GetInstance();
279
280  TestDevToolsClientHost client_host;
281  manager->RegisterDevToolsClientHostFor(agent_host.get(), &client_host);
282
283  manager->DispatchOnInspectorBackend(&client_host, "message1");
284  manager->DispatchOnInspectorBackend(&client_host, "message2");
285  manager->DispatchOnInspectorBackend(&client_host, "message2");
286
287  client_host.Close(manager);
288}
289
290}  // namespace content
291