- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / devtools_ui.cc
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 "chrome/browser/ui/webui/devtools_ui.h"
6
7 #include <string>
8
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"
25
26 using content::BrowserThread;
27 using content::WebContents;
28
29 namespace {
30
31 std::string PathWithoutParams(const std::string& path) {
32   return GURL(std::string("chrome-devtools://devtools/") + path)
33       .path().substr(1);
34 }
35
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";
40
41 #if defined(DEBUG_DEVTOOLS)
42 // Local frontend url provided by InspectUI.
43 const char kFallbackFrontendURL[] =
44     "chrome-devtools://devtools/bundled/devtools.html";
45 #else
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)
50
51
52 class FetchRequest : public net::URLFetcherDelegate {
53  public:
54   FetchRequest(net::URLRequestContextGetter* request_context,
55                const GURL& url,
56                const content::URLDataSource::GotDataCallback& callback)
57       : callback_(callback) {
58     if (!url.is_valid()) {
59       OnURLFetchComplete(NULL);
60       return;
61     }
62
63     fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET, this));
64     fetcher_->SetRequestContext(request_context);
65     fetcher_->Start();
66   }
67
68  private:
69   virtual ~FetchRequest() {}
70
71   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
72     std::string response;
73     if (source)
74       source->GetResponseAsString(&response);
75     else
76       response = kHttpNotFound;
77
78     callback_.Run(base::RefCountedString::TakeString(&response));
79     delete this;
80   }
81
82   scoped_ptr<net::URLFetcher> fetcher_;
83   content::URLDataSource::GotDataCallback callback_;
84 };
85
86 std::string GetMimeTypeForPath(const std::string& path) {
87   std::string filename = PathWithoutParams(path);
88   if (EndsWith(filename, ".html", false)) {
89     return "text/html";
90   } else if (EndsWith(filename, ".css", false)) {
91     return "text/css";
92   } else if (EndsWith(filename, ".js", false)) {
93     return "application/javascript";
94   } else if (EndsWith(filename, ".png", false)) {
95     return "image/png";
96   } else if (EndsWith(filename, ".gif", false)) {
97     return "image/gif";
98   } else if (EndsWith(filename, ".manifest", false)) {
99     return "text/cache-manifest";
100   }
101   NOTREACHED();
102   return "text/plain";
103 }
104
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 {
112  public:
113   explicit DevToolsDataSource(net::URLRequestContextGetter*
114     request_context) : request_context_(request_context) {
115   }
116
117   // content::URLDataSource implementation.
118   virtual std::string GetSource() const OVERRIDE {
119     return chrome::kChromeUIDevToolsHost;
120   }
121
122   virtual void StartDataRequest(
123       const std::string& path,
124       int render_process_id,
125       int render_view_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()),
131                               render_process_id,
132                               render_view_id,
133                               callback);
134       return;
135     }
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()),
140                               render_process_id,
141                               render_view_id,
142                               callback);
143       return;
144     }
145   }
146
147   // Serves bundled DevTools frontend from ResourceBundle.
148   void StartBundledDataRequest(
149       const std::string& path,
150       int render_process_id,
151       int render_view_id,
152       const content::URLDataSource::GotDataCallback& callback) {
153     std::string filename = PathWithoutParams(path);
154
155     int resource_id =
156         content::DevToolsHttpHandler::GetFrontendResourceId(filename);
157
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(
163         resource_id));
164     callback.Run(bytes.get());
165   }
166
167   // Serves remote DevTools frontend from hard-coded App Engine domain.
168   void StartRemoteDataRequest(
169       const std::string& path,
170       int render_process_id,
171       int render_view_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);
176   }
177
178   virtual std::string GetMimeType(const std::string& path) const OVERRIDE {
179     return GetMimeTypeForPath(path);
180   }
181
182   virtual bool ShouldAddContentSecurityPolicy() const OVERRIDE {
183     return false;
184   }
185
186  private:
187   virtual ~DevToolsDataSource() {}
188   scoped_refptr<net::URLRequestContextGetter> request_context_;
189
190   DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
191 };
192
193 }  // namespace
194
195 // static
196 GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) {
197   GURL url(frontend_url);
198   if (!url.is_valid() || url.host() != kRemoteFrontendDomain) {
199     return GURL(kFallbackFrontendURL);
200   }
201   return GURL(base::StringPrintf("%s://%s/%s/%s",
202               chrome::kChromeDevToolsScheme,
203               chrome::kChromeUIDevToolsHost,
204               chrome::kChromeUIDevToolsRemotePath,
205               url.path().substr(1).c_str()));
206 }
207
208 DevToolsUI::DevToolsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
209   web_ui->SetBindings(0);
210   Profile* profile = Profile::FromWebUI(web_ui);
211   content::URLDataSource::Add(
212       profile,
213       new DevToolsDataSource(profile->GetRequestContext()));
214 }