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 22 using base::TimeDelta; 23 24 namespace content { 25 namespace { 26 27 class 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 70 int TestDevToolsClientHost::close_counter = 0; 71 72 73 class 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 90 class 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 108 class 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 127 TEST_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 146 TEST_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 164 TEST_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 202 TEST_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 234 class 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 269 TEST_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