Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / webui / devtools_ui.cc
blob68ccc018f21807f075806ec639e57a0740a228eb
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"
7 #include "base/memory/ref_counted_memory.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/common/url_constants.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/devtools_frontend_host.h"
14 #include "content/public/browser/url_data_source.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/browser/web_ui.h"
17 #include "content/public/common/user_agent.h"
18 #include "net/url_request/url_fetcher.h"
19 #include "net/url_request/url_fetcher_delegate.h"
20 #include "net/url_request/url_request_context_getter.h"
22 using content::BrowserThread;
23 using content::WebContents;
25 namespace {
27 std::string PathWithoutParams(const std::string& path) {
28 return GURL(std::string("chrome-devtools://devtools/") + path)
29 .path().substr(1);
32 const char kRemoteFrontendDomain[] = "chrome-devtools-frontend.appspot.com";
33 const char kRemoteFrontendBase[] =
34 "https://chrome-devtools-frontend.appspot.com/";
35 const char kRemoteFrontendPath[] = "serve_file";
36 const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n";
38 #if defined(DEBUG_DEVTOOLS)
39 // Local frontend url provided by InspectUI.
40 const char kFallbackFrontendURL[] =
41 "chrome-devtools://devtools/bundled/inspector.html";
42 #else
43 // URL causing the DevTools window to display a plain text warning.
44 const char kFallbackFrontendURL[] =
45 "data:text/plain,Cannot load DevTools frontend from an untrusted origin";
46 #endif // defined(DEBUG_DEVTOOLS)
48 // DevToolsDataSource ---------------------------------------------------------
50 std::string GetMimeTypeForPath(const std::string& path) {
51 std::string filename = PathWithoutParams(path);
52 if (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) {
53 return "text/html";
54 } else if (base::EndsWith(filename, ".css",
55 base::CompareCase::INSENSITIVE_ASCII)) {
56 return "text/css";
57 } else if (base::EndsWith(filename, ".js",
58 base::CompareCase::INSENSITIVE_ASCII)) {
59 return "application/javascript";
60 } else if (base::EndsWith(filename, ".png",
61 base::CompareCase::INSENSITIVE_ASCII)) {
62 return "image/png";
63 } else if (base::EndsWith(filename, ".gif",
64 base::CompareCase::INSENSITIVE_ASCII)) {
65 return "image/gif";
66 } else if (base::EndsWith(filename, ".svg",
67 base::CompareCase::INSENSITIVE_ASCII)) {
68 return "image/svg+xml";
69 } else if (base::EndsWith(filename, ".manifest",
70 base::CompareCase::INSENSITIVE_ASCII)) {
71 return "text/cache-manifest";
73 return "text/html";
76 // An URLDataSource implementation that handles chrome-devtools://devtools/
77 // requests. Three types of requests could be handled based on the URL path:
78 // 1. /bundled/: bundled DevTools frontend is served.
79 // 2. /remote/: remote DevTools frontend is served from App Engine.
80 class DevToolsDataSource : public content::URLDataSource,
81 public net::URLFetcherDelegate {
82 public:
83 using GotDataCallback = content::URLDataSource::GotDataCallback;
85 explicit DevToolsDataSource(net::URLRequestContextGetter* request_context);
87 // content::URLDataSource implementation.
88 std::string GetSource() const override;
90 void StartDataRequest(const std::string& path,
91 int render_process_id,
92 int render_frame_id,
93 const GotDataCallback& callback) override;
95 private:
96 // content::URLDataSource overrides.
97 std::string GetMimeType(const std::string& path) const override;
98 bool ShouldAddContentSecurityPolicy() const override;
99 bool ShouldDenyXFrameOptions() const override;
100 bool ShouldServeMimeTypeAsContentTypeHeader() const override;
102 // net::URLFetcherDelegate overrides.
103 void OnURLFetchComplete(const net::URLFetcher* source) override;
105 // Serves bundled DevTools frontend from ResourceBundle.
106 void StartBundledDataRequest(const std::string& path,
107 int render_process_id,
108 int render_frame_id,
109 const GotDataCallback& callback);
111 // Serves remote DevTools frontend from hard-coded App Engine domain.
112 void StartRemoteDataRequest(const std::string& path,
113 int render_process_id,
114 int render_frame_id,
115 const GotDataCallback& callback);
117 ~DevToolsDataSource() override;
119 scoped_refptr<net::URLRequestContextGetter> request_context_;
121 using PendingRequestsMap = std::map<const net::URLFetcher*, GotDataCallback>;
122 PendingRequestsMap pending_;
124 DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
127 DevToolsDataSource::DevToolsDataSource(
128 net::URLRequestContextGetter* request_context)
129 : request_context_(request_context) {
132 DevToolsDataSource::~DevToolsDataSource() {
133 for (const auto& pair : pending_) {
134 delete pair.first;
135 pair.second.Run(
136 new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
140 std::string DevToolsDataSource::GetSource() const {
141 return chrome::kChromeUIDevToolsHost;
144 void DevToolsDataSource::StartDataRequest(
145 const std::string& path,
146 int render_process_id,
147 int render_frame_id,
148 const content::URLDataSource::GotDataCallback& callback) {
149 // Serve request from local bundle.
150 std::string bundled_path_prefix(chrome::kChromeUIDevToolsBundledPath);
151 bundled_path_prefix += "/";
152 if (base::StartsWith(path, bundled_path_prefix,
153 base::CompareCase::INSENSITIVE_ASCII)) {
154 StartBundledDataRequest(path.substr(bundled_path_prefix.length()),
155 render_process_id, render_frame_id, callback);
156 return;
159 // Serve request from remote location.
160 std::string remote_path_prefix(chrome::kChromeUIDevToolsRemotePath);
161 remote_path_prefix += "/";
162 if (base::StartsWith(path, remote_path_prefix,
163 base::CompareCase::INSENSITIVE_ASCII)) {
164 StartRemoteDataRequest(path.substr(remote_path_prefix.length()),
165 render_process_id, render_frame_id, callback);
166 return;
169 callback.Run(NULL);
172 std::string DevToolsDataSource::GetMimeType(const std::string& path) const {
173 return GetMimeTypeForPath(path);
176 bool DevToolsDataSource::ShouldAddContentSecurityPolicy() const {
177 return false;
180 bool DevToolsDataSource::ShouldDenyXFrameOptions() const {
181 return false;
184 bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
185 return true;
188 void DevToolsDataSource::StartBundledDataRequest(
189 const std::string& path,
190 int render_process_id,
191 int render_frame_id,
192 const content::URLDataSource::GotDataCallback& callback) {
193 std::string filename = PathWithoutParams(path);
194 base::StringPiece resource =
195 content::DevToolsFrontendHost::GetFrontendResource(filename);
197 DLOG_IF(WARNING, resource.empty())
198 << "Unable to find dev tool resource: " << filename
199 << ". If you compiled with debug_devtools=1, try running with "
200 "--debug-devtools.";
201 scoped_refptr<base::RefCountedStaticMemory> bytes(
202 new base::RefCountedStaticMemory(resource.data(), resource.length()));
203 callback.Run(bytes.get());
206 void DevToolsDataSource::StartRemoteDataRequest(
207 const std::string& path,
208 int render_process_id,
209 int render_frame_id,
210 const content::URLDataSource::GotDataCallback& callback) {
211 GURL url = GURL(kRemoteFrontendBase + path);
212 CHECK_EQ(url.host(), kRemoteFrontendDomain);
213 if (!url.is_valid()) {
214 callback.Run(
215 new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
216 return;
218 net::URLFetcher* fetcher =
219 net::URLFetcher::Create(url, net::URLFetcher::GET, this).release();
220 pending_[fetcher] = callback;
221 fetcher->SetRequestContext(request_context_.get());
222 fetcher->Start();
225 void DevToolsDataSource::OnURLFetchComplete(const net::URLFetcher* source) {
226 DCHECK(source);
227 PendingRequestsMap::iterator it = pending_.find(source);
228 DCHECK(it != pending_.end());
229 std::string response;
230 source->GetResponseAsString(&response);
231 delete source;
232 it->second.Run(base::RefCountedString::TakeString(&response));
233 pending_.erase(it);
236 } // namespace
238 // DevToolsUI -----------------------------------------------------------------
240 // static
241 GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) {
242 GURL url(frontend_url);
243 if (!url.is_valid() || url.host() != kRemoteFrontendDomain)
244 return GURL(kFallbackFrontendURL);
245 return GURL(base::StringPrintf("%s://%s/%s/%s",
246 content::kChromeDevToolsScheme,
247 chrome::kChromeUIDevToolsHost,
248 chrome::kChromeUIDevToolsRemotePath,
249 url.path().substr(1).c_str()));
252 // static
253 GURL DevToolsUI::GetRemoteBaseURL() {
254 return GURL(base::StringPrintf(
255 "%s%s/%s/",
256 kRemoteFrontendBase,
257 kRemoteFrontendPath,
258 content::GetWebKitRevision().c_str()));
261 DevToolsUI::DevToolsUI(content::WebUI* web_ui)
262 : WebUIController(web_ui),
263 bindings_(web_ui->GetWebContents()) {
264 web_ui->SetBindings(0);
265 Profile* profile = Profile::FromWebUI(web_ui);
266 content::URLDataSource::Add(
267 profile,
268 new DevToolsDataSource(profile->GetRequestContext()));
271 DevToolsUI::~DevToolsUI() {