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