// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/devtools/remote_debugging_server.h" #include "base/lazy_instance.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/history/top_sites_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/common/chrome_content_client.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_version_info.h" #include "components/devtools_http_handler/devtools_http_handler.h" #include "components/devtools_http_handler/devtools_http_handler_delegate.h" #include "components/history/core/browser/top_sites.h" #include "content/public/browser/devtools_frontend_host.h" #include "grit/browser_resources.h" #include "net/base/net_errors.h" #include "net/socket/tcp_server_socket.h" #include "ui/base/resource/resource_bundle.h" namespace { base::LazyInstance::Leaky g_tethering_enabled = LAZY_INSTANCE_INITIALIZER; const uint16 kMinTetheringPort = 9333; const uint16 kMaxTetheringPort = 9444; const int kBackLog = 10; class TCPServerSocketFactory : public devtools_http_handler::DevToolsHttpHandler::ServerSocketFactory { public: TCPServerSocketFactory(const std::string& address, uint16 port) : address_(address), port_(port), last_tethering_port_(kMinTetheringPort) { } private: // devtools_http_handler::DevToolsHttpHandler::ServerSocketFactory. scoped_ptr CreateForHttpServer() override { scoped_ptr socket( new net::TCPServerSocket(nullptr, net::NetLog::Source())); if (socket->ListenWithAddressAndPort(address_, port_, kBackLog) != net::OK) return scoped_ptr(); return socket; } scoped_ptr CreateForTethering(std::string* name) override { if (!g_tethering_enabled.Get()) return scoped_ptr(); if (last_tethering_port_ == kMaxTetheringPort) last_tethering_port_ = kMinTetheringPort; uint16 port = ++last_tethering_port_; *name = base::IntToString(port); scoped_ptr socket( new net::TCPServerSocket(nullptr, net::NetLog::Source())); if (socket->ListenWithAddressAndPort("127.0.0.1", port, kBackLog) != net::OK) { return scoped_ptr(); } return socket.Pass(); } std::string address_; uint16 port_; uint16 last_tethering_port_; DISALLOW_COPY_AND_ASSIGN(TCPServerSocketFactory); }; class ChromeDevToolsHttpHandlerDelegate : public devtools_http_handler::DevToolsHttpHandlerDelegate { public: ChromeDevToolsHttpHandlerDelegate(); ~ChromeDevToolsHttpHandlerDelegate() override; // devtools_http_handler::DevToolsHttpHandlerDelegate implementation. std::string GetDiscoveryPageHTML() override; std::string GetFrontendResource(const std::string& path) override; std::string GetPageThumbnailData(const GURL& url) override; private: DISALLOW_COPY_AND_ASSIGN(ChromeDevToolsHttpHandlerDelegate); }; ChromeDevToolsHttpHandlerDelegate::ChromeDevToolsHttpHandlerDelegate() { } ChromeDevToolsHttpHandlerDelegate::~ChromeDevToolsHttpHandlerDelegate() { } std::string ChromeDevToolsHttpHandlerDelegate::GetDiscoveryPageHTML() { std::set profiles; for (chrome::BrowserIterator it; !it.done(); it.Next()) profiles.insert((*it)->profile()); for (std::set::iterator it = profiles.begin(); it != profiles.end(); ++it) { scoped_refptr ts = TopSitesFactory::GetForProfile(*it); if (ts) { // TopSites updates itself after a delay. Ask TopSites to update itself // when we're about to show the remote debugging landing page. ts->SyncWithHistory(); } } return ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_DEVTOOLS_DISCOVERY_PAGE_HTML).as_string(); } std::string ChromeDevToolsHttpHandlerDelegate::GetFrontendResource( const std::string& path) { return content::DevToolsFrontendHost::GetFrontendResource(path).as_string(); } std::string ChromeDevToolsHttpHandlerDelegate::GetPageThumbnailData( const GURL& url) { for (chrome::BrowserIterator it; !it.done(); it.Next()) { Profile* profile = (*it)->profile(); scoped_refptr top_sites = TopSitesFactory::GetForProfile(profile); if (!top_sites) continue; scoped_refptr data; if (top_sites->GetPageThumbnail(url, false, &data)) return std::string(data->front_as(), data->size()); } return std::string(); } } // namespace // static void RemoteDebuggingServer::EnableTetheringForDebug() { g_tethering_enabled.Get() = true; } RemoteDebuggingServer::RemoteDebuggingServer( chrome::HostDesktopType host_desktop_type, const std::string& ip, uint16 port) { base::FilePath output_dir; if (!port) { // The client requested an ephemeral port. Must write the selected // port to a well-known location in the profile directory to // bootstrap the connection process. bool result = PathService::Get(chrome::DIR_USER_DATA, &output_dir); DCHECK(result); } base::FilePath debug_frontend_dir; #if defined(DEBUG_DEVTOOLS) PathService::Get(chrome::DIR_INSPECTOR, &debug_frontend_dir); #endif chrome::VersionInfo version_info; devtools_http_handler_.reset(new devtools_http_handler::DevToolsHttpHandler( make_scoped_ptr(new TCPServerSocketFactory(ip, port)), std::string(), new ChromeDevToolsHttpHandlerDelegate(), output_dir, debug_frontend_dir, version_info.ProductNameAndVersionForUserAgent(), ::GetUserAgent())); } RemoteDebuggingServer::~RemoteDebuggingServer() { // Ensure Profile is alive, because the whole DevTools subsystem // accesses it during shutdown. DCHECK(g_browser_process->profile_manager()); }