1 // Copyright 2013 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 "content/shell/browser/shell_devtools_delegate.h" 6 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/command_line.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "content/public/browser/devtools_agent_host.h" 15 #include "content/public/browser/devtools_http_handler.h" 16 #include "content/public/browser/devtools_target.h" 17 #include "content/public/browser/favicon_status.h" 18 #include "content/public/browser/navigation_entry.h" 19 #include "content/public/browser/render_view_host.h" 20 #include "content/public/browser/web_contents.h" 21 #include "content/public/common/content_switches.h" 22 #include "content/public/common/url_constants.h" 23 #include "content/shell/browser/shell.h" 24 #include "grit/shell_resources.h" 25 #include "net/socket/tcp_listen_socket.h" 26 #include "ui/base/resource/resource_bundle.h" 27 #include "webkit/common/user_agent/user_agent_util.h" 28 29 #if defined(OS_ANDROID) 30 #include "content/public/browser/android/devtools_auth.h" 31 #include "net/socket/unix_domain_socket_posix.h" 32 #endif 33 34 using content::DevToolsAgentHost; 35 using content::RenderViewHost; 36 using content::WebContents; 37 38 namespace { 39 40 #if defined(OS_ANDROID) 41 const char kFrontEndURL[] = 42 "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html"; 43 #endif 44 const char kTargetTypePage[] = "page"; 45 46 net::StreamListenSocketFactory* CreateSocketFactory() { 47 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 48 #if defined(OS_ANDROID) 49 std::string socket_name = "content_shell_devtools_remote"; 50 if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) { 51 socket_name = command_line.GetSwitchValueASCII( 52 switches::kRemoteDebuggingSocketName); 53 } 54 return new net::UnixDomainSocketWithAbstractNamespaceFactory( 55 socket_name, "", base::Bind(&content::CanUserConnectToDevTools)); 56 #else 57 // See if the user specified a port on the command line (useful for 58 // automation). If not, use an ephemeral port by specifying 0. 59 int port = 0; 60 if (command_line.HasSwitch(switches::kRemoteDebuggingPort)) { 61 int temp_port; 62 std::string port_str = 63 command_line.GetSwitchValueASCII(switches::kRemoteDebuggingPort); 64 if (base::StringToInt(port_str, &temp_port) && 65 temp_port > 0 && temp_port < 65535) { 66 port = temp_port; 67 } else { 68 DLOG(WARNING) << "Invalid http debugger port number " << temp_port; 69 } 70 } 71 return new net::TCPListenSocketFactory("127.0.0.1", port); 72 #endif 73 } 74 75 class Target : public content::DevToolsTarget { 76 public: 77 explicit Target(WebContents* web_contents); 78 79 virtual std::string GetId() const OVERRIDE { return id_; } 80 virtual std::string GetType() const OVERRIDE { return kTargetTypePage; } 81 virtual std::string GetTitle() const OVERRIDE { return title_; } 82 virtual std::string GetDescription() const OVERRIDE { return std::string(); } 83 virtual GURL GetUrl() const OVERRIDE { return url_; } 84 virtual GURL GetFaviconUrl() const OVERRIDE { return favicon_url_; } 85 virtual base::TimeTicks GetLastActivityTime() const OVERRIDE { 86 return last_activity_time_; 87 } 88 virtual bool IsAttached() const OVERRIDE { 89 return agent_host_->IsAttached(); 90 } 91 virtual scoped_refptr<DevToolsAgentHost> GetAgentHost() const OVERRIDE { 92 return agent_host_; 93 } 94 virtual bool Activate() const OVERRIDE; 95 virtual bool Close() const OVERRIDE; 96 97 private: 98 scoped_refptr<DevToolsAgentHost> agent_host_; 99 std::string id_; 100 std::string title_; 101 GURL url_; 102 GURL favicon_url_; 103 base::TimeTicks last_activity_time_; 104 }; 105 106 Target::Target(WebContents* web_contents) { 107 agent_host_ = 108 DevToolsAgentHost::GetOrCreateFor(web_contents->GetRenderViewHost()); 109 id_ = agent_host_->GetId(); 110 title_ = UTF16ToUTF8(web_contents->GetTitle()); 111 url_ = web_contents->GetURL(); 112 content::NavigationController& controller = web_contents->GetController(); 113 content::NavigationEntry* entry = controller.GetActiveEntry(); 114 if (entry != NULL && entry->GetURL().is_valid()) 115 favicon_url_ = entry->GetFavicon().url; 116 last_activity_time_ = web_contents->GetLastSelectedTime(); 117 } 118 119 bool Target::Activate() const { 120 RenderViewHost* rvh = agent_host_->GetRenderViewHost(); 121 if (!rvh) 122 return false; 123 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 124 if (!web_contents) 125 return false; 126 web_contents->GetDelegate()->ActivateContents(web_contents); 127 return true; 128 } 129 130 bool Target::Close() const { 131 RenderViewHost* rvh = agent_host_->GetRenderViewHost(); 132 if (!rvh) 133 return false; 134 rvh->ClosePage(); 135 return true; 136 } 137 138 } // namespace 139 140 namespace content { 141 142 ShellDevToolsDelegate::ShellDevToolsDelegate(BrowserContext* browser_context) 143 : browser_context_(browser_context) { 144 std::string frontend_url; 145 #if defined(OS_ANDROID) 146 frontend_url = base::StringPrintf(kFrontEndURL, 147 webkit_glue::GetWebKitRevision().c_str()); 148 #endif 149 devtools_http_handler_ = 150 DevToolsHttpHandler::Start(CreateSocketFactory(), frontend_url, this); 151 } 152 153 ShellDevToolsDelegate::~ShellDevToolsDelegate() { 154 } 155 156 void ShellDevToolsDelegate::Stop() { 157 // The call below destroys this. 158 devtools_http_handler_->Stop(); 159 } 160 161 std::string ShellDevToolsDelegate::GetDiscoveryPageHTML() { 162 #if defined(OS_ANDROID) 163 return std::string(); 164 #else 165 return ResourceBundle::GetSharedInstance().GetRawDataResource( 166 IDR_CONTENT_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string(); 167 #endif 168 } 169 170 bool ShellDevToolsDelegate::BundlesFrontendResources() { 171 #if defined(OS_ANDROID) 172 return false; 173 #else 174 return true; 175 #endif 176 } 177 178 base::FilePath ShellDevToolsDelegate::GetDebugFrontendDir() { 179 return base::FilePath(); 180 } 181 182 std::string ShellDevToolsDelegate::GetPageThumbnailData(const GURL& url) { 183 return std::string(); 184 } 185 186 scoped_ptr<DevToolsTarget> 187 ShellDevToolsDelegate::CreateNewTarget(const GURL& url) { 188 Shell* shell = Shell::CreateNewWindow(browser_context_, 189 url, 190 NULL, 191 MSG_ROUTING_NONE, 192 gfx::Size()); 193 return scoped_ptr<DevToolsTarget>(new Target(shell->web_contents())); 194 } 195 196 void ShellDevToolsDelegate::EnumerateTargets(TargetCallback callback) { 197 TargetList targets; 198 std::vector<RenderViewHost*> rvh_list = 199 content::DevToolsAgentHost::GetValidRenderViewHosts(); 200 for (std::vector<RenderViewHost*>::iterator it = rvh_list.begin(); 201 it != rvh_list.end(); ++it) { 202 WebContents* web_contents = WebContents::FromRenderViewHost(*it); 203 if (web_contents) 204 targets.push_back(new Target(web_contents)); 205 } 206 callback.Run(targets); 207 } 208 209 scoped_ptr<net::StreamListenSocket> 210 ShellDevToolsDelegate::CreateSocketForTethering( 211 net::StreamListenSocket::Delegate* delegate, 212 std::string* name) { 213 return scoped_ptr<net::StreamListenSocket>(); 214 } 215 216 } // namespace content 217