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.
5 #include "chrome/browser/ui/webui/devtools_ui.h"
9 #include "base/memory/ref_counted_memory.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/net/chrome_url_request_context.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/url_constants.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/devtools_http_handler.h"
18 #include "content/public/browser/url_data_source.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_ui.h"
21 #include "net/url_request/url_fetcher.h"
22 #include "net/url_request/url_fetcher_delegate.h"
23 #include "net/url_request/url_request_context_getter.h"
24 #include "ui/base/resource/resource_bundle.h"
26 using content::BrowserThread
;
27 using content::WebContents
;
31 std::string
PathWithoutParams(const std::string
& path
) {
32 return GURL(std::string("chrome-devtools://devtools/") + path
)
36 const char kRemoteFrontendDomain
[] = "chrome-devtools-frontend.appspot.com";
37 const char kRemoteFrontendBase
[] =
38 "https://chrome-devtools-frontend.appspot.com/";
39 const char kHttpNotFound
[] = "HTTP/1.1 404 Not Found\n\n";
41 #if defined(DEBUG_DEVTOOLS)
42 // Local frontend url provided by InspectUI.
43 const char kFallbackFrontendURL
[] =
44 "chrome-devtools://devtools/bundled/devtools.html";
46 // URL causing the DevTools window to display a plain text warning.
47 const char kFallbackFrontendURL
[] =
48 "data:text/plain,Cannot load DevTools frontend from an untrusted origin";
49 #endif // defined(DEBUG_DEVTOOLS)
52 class FetchRequest
: public net::URLFetcherDelegate
{
54 FetchRequest(net::URLRequestContextGetter
* request_context
,
56 const content::URLDataSource::GotDataCallback
& callback
)
57 : callback_(callback
) {
58 if (!url
.is_valid()) {
59 OnURLFetchComplete(NULL
);
63 fetcher_
.reset(net::URLFetcher::Create(url
, net::URLFetcher::GET
, this));
64 fetcher_
->SetRequestContext(request_context
);
69 virtual ~FetchRequest() {}
71 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
{
74 source
->GetResponseAsString(&response
);
76 response
= kHttpNotFound
;
78 callback_
.Run(base::RefCountedString::TakeString(&response
));
82 scoped_ptr
<net::URLFetcher
> fetcher_
;
83 content::URLDataSource::GotDataCallback callback_
;
86 std::string
GetMimeTypeForPath(const std::string
& path
) {
87 std::string filename
= PathWithoutParams(path
);
88 if (EndsWith(filename
, ".html", false)) {
90 } else if (EndsWith(filename
, ".css", false)) {
92 } else if (EndsWith(filename
, ".js", false)) {
93 return "application/javascript";
94 } else if (EndsWith(filename
, ".png", false)) {
96 } else if (EndsWith(filename
, ".gif", false)) {
98 } else if (EndsWith(filename
, ".manifest", false)) {
99 return "text/cache-manifest";
105 // An URLDataSource implementation that handles chrome-devtools://devtools/
106 // requests. Three types of requests could be handled based on the URL path:
107 // 1. /bundled/: bundled DevTools frontend is served.
108 // 2. /remote/: Remote DevTools frontend is served from App Engine.
109 // 3. /localhost/: Remote frontend is served from localhost:9222. This is a
110 // debug only feature hidden beihnd a compile time flag DEBUG_DEVTOOLS.
111 class DevToolsDataSource
: public content::URLDataSource
{
113 explicit DevToolsDataSource(net::URLRequestContextGetter
*
114 request_context
) : request_context_(request_context
) {
117 // content::URLDataSource implementation.
118 virtual std::string
GetSource() const OVERRIDE
{
119 return chrome::kChromeUIDevToolsHost
;
122 virtual void StartDataRequest(
123 const std::string
& path
,
124 int render_process_id
,
126 const content::URLDataSource::GotDataCallback
& callback
) OVERRIDE
{
127 std::string
bundled_path_prefix(chrome::kChromeUIDevToolsBundledPath
);
128 bundled_path_prefix
+= "/";
129 if (StartsWithASCII(path
, bundled_path_prefix
, false)) {
130 StartBundledDataRequest(path
.substr(bundled_path_prefix
.length()),
136 std::string
remote_path_prefix(chrome::kChromeUIDevToolsRemotePath
);
137 remote_path_prefix
+= "/";
138 if (StartsWithASCII(path
, remote_path_prefix
, false)) {
139 StartRemoteDataRequest(path
.substr(remote_path_prefix
.length()),
147 // Serves bundled DevTools frontend from ResourceBundle.
148 void StartBundledDataRequest(
149 const std::string
& path
,
150 int render_process_id
,
152 const content::URLDataSource::GotDataCallback
& callback
) {
153 std::string filename
= PathWithoutParams(path
);
156 content::DevToolsHttpHandler::GetFrontendResourceId(filename
);
158 DLOG_IF(WARNING
, -1 == resource_id
) << "Unable to find dev tool resource: "
159 << filename
<< ". If you compiled with debug_devtools=1, try running"
160 " with --debug-devtools.";
161 const ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
162 scoped_refptr
<base::RefCountedStaticMemory
> bytes(rb
.LoadDataResourceBytes(
164 callback
.Run(bytes
.get());
167 // Serves remote DevTools frontend from hard-coded App Engine domain.
168 void StartRemoteDataRequest(
169 const std::string
& path
,
170 int render_process_id
,
172 const content::URLDataSource::GotDataCallback
& callback
) {
173 GURL url
= GURL(kRemoteFrontendBase
+ path
);
174 CHECK_EQ(url
.host(), kRemoteFrontendDomain
);
175 new FetchRequest(request_context_
.get(), url
, callback
);
178 virtual std::string
GetMimeType(const std::string
& path
) const OVERRIDE
{
179 return GetMimeTypeForPath(path
);
182 virtual bool ShouldAddContentSecurityPolicy() const OVERRIDE
{
186 virtual bool ShouldServeMimeTypeAsContentTypeHeader() const OVERRIDE
{
191 virtual ~DevToolsDataSource() {}
192 scoped_refptr
<net::URLRequestContextGetter
> request_context_
;
194 DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource
);
200 GURL
DevToolsUI::GetProxyURL(const std::string
& frontend_url
) {
201 GURL
url(frontend_url
);
202 if (!url
.is_valid() || url
.host() != kRemoteFrontendDomain
) {
203 return GURL(kFallbackFrontendURL
);
205 return GURL(base::StringPrintf("%s://%s/%s/%s",
206 chrome::kChromeDevToolsScheme
,
207 chrome::kChromeUIDevToolsHost
,
208 chrome::kChromeUIDevToolsRemotePath
,
209 url
.path().substr(1).c_str()));
212 DevToolsUI::DevToolsUI(content::WebUI
* web_ui
) : WebUIController(web_ui
) {
213 web_ui
->SetBindings(0);
214 Profile
* profile
= Profile::FromWebUI(web_ui
);
215 content::URLDataSource::Add(
217 new DevToolsDataSource(profile
->GetRequestContext()));