Use "= delete" for DISALLOW_COPY and DISALLOW_ASSIGN.
[chromium-blink-merge.git] / content / shell / browser / shell_devtools_frontend.cc
blob2ad0c2ed445d9d8a0cd259dbe6aef21329a3073e
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.
5 #include "content/shell/browser/shell_devtools_frontend.h"
7 #include "base/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "components/devtools_http_handler/devtools_http_handler.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/content_client.h"
20 #include "content/shell/browser/blink_test_controller.h"
21 #include "content/shell/browser/shell.h"
22 #include "content/shell/browser/shell_browser_context.h"
23 #include "content/shell/browser/shell_browser_main_parts.h"
24 #include "content/shell/browser/shell_content_browser_client.h"
25 #include "content/shell/browser/shell_devtools_manager_delegate.h"
26 #include "content/shell/common/shell_switches.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/net_errors.h"
29 #include "net/http/http_response_headers.h"
30 #include "net/url_request/url_fetcher.h"
31 #include "net/url_request/url_fetcher_response_writer.h"
33 namespace content {
35 namespace {
38 // ResponseWriter -------------------------------------------------------------
40 class ResponseWriter : public net::URLFetcherResponseWriter {
41 public:
42 ResponseWriter(base::WeakPtr<ShellDevToolsFrontend> shell_devtools_,
43 int stream_id);
44 ~ResponseWriter() override;
46 // URLFetcherResponseWriter overrides:
47 int Initialize(const net::CompletionCallback& callback) override;
48 int Write(net::IOBuffer* buffer,
49 int num_bytes,
50 const net::CompletionCallback& callback) override;
51 int Finish(const net::CompletionCallback& callback) override;
53 private:
54 base::WeakPtr<ShellDevToolsFrontend> shell_devtools_;
55 int stream_id_;
57 DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
60 ResponseWriter::ResponseWriter(
61 base::WeakPtr<ShellDevToolsFrontend> shell_devtools,
62 int stream_id)
63 : shell_devtools_(shell_devtools),
64 stream_id_(stream_id) {
67 ResponseWriter::~ResponseWriter() {
70 int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
71 return net::OK;
74 int ResponseWriter::Write(net::IOBuffer* buffer,
75 int num_bytes,
76 const net::CompletionCallback& callback) {
77 base::FundamentalValue* id = new base::FundamentalValue(stream_id_);
78 base::StringValue* chunk =
79 new base::StringValue(std::string(buffer->data(), num_bytes));
81 content::BrowserThread::PostTask(
82 content::BrowserThread::UI, FROM_HERE,
83 base::Bind(&ShellDevToolsFrontend::CallClientFunction,
84 shell_devtools_, "DevToolsAPI.streamWrite",
85 base::Owned(id), base::Owned(chunk), nullptr));
86 return num_bytes;
89 int ResponseWriter::Finish(const net::CompletionCallback& callback) {
90 return net::OK;
93 } // namespace
95 // This constant should be in sync with
96 // the constant at devtools_ui_bindings.cc.
97 const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
99 // static
100 ShellDevToolsFrontend* ShellDevToolsFrontend::Show(
101 WebContents* inspected_contents) {
102 Shell* shell = Shell::CreateNewWindow(inspected_contents->GetBrowserContext(),
103 GURL(),
104 NULL,
105 gfx::Size());
106 ShellDevToolsFrontend* devtools_frontend = new ShellDevToolsFrontend(
107 shell,
108 inspected_contents);
110 devtools_http_handler::DevToolsHttpHandler* http_handler =
111 ShellContentBrowserClient::Get()
112 ->shell_browser_main_parts()
113 ->devtools_http_handler();
114 shell->LoadURL(http_handler->GetFrontendURL("/devtools/devtools.html"));
116 return devtools_frontend;
119 void ShellDevToolsFrontend::Activate() {
120 frontend_shell_->ActivateContents(web_contents());
123 void ShellDevToolsFrontend::Focus() {
124 web_contents()->Focus();
127 void ShellDevToolsFrontend::InspectElementAt(int x, int y) {
128 if (agent_host_)
129 agent_host_->InspectElement(x, y);
132 void ShellDevToolsFrontend::Close() {
133 frontend_shell_->Close();
136 void ShellDevToolsFrontend::DisconnectFromTarget() {
137 if (!agent_host_)
138 return;
139 agent_host_->DetachClient();
140 agent_host_ = NULL;
143 ShellDevToolsFrontend::ShellDevToolsFrontend(Shell* frontend_shell,
144 WebContents* inspected_contents)
145 : WebContentsObserver(frontend_shell->web_contents()),
146 frontend_shell_(frontend_shell),
147 inspected_contents_(inspected_contents),
148 weak_factory_(this) {
151 ShellDevToolsFrontend::~ShellDevToolsFrontend() {
152 for (const auto& pair : pending_requests_)
153 delete pair.first;
156 void ShellDevToolsFrontend::RenderViewCreated(
157 RenderViewHost* render_view_host) {
158 if (!frontend_host_) {
159 frontend_host_.reset(
160 DevToolsFrontendHost::Create(web_contents()->GetMainFrame(), this));
164 void ShellDevToolsFrontend::DocumentAvailableInMainFrame() {
165 agent_host_ = DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
166 agent_host_->AttachClient(this);
169 void ShellDevToolsFrontend::WebContentsDestroyed() {
170 if (agent_host_)
171 agent_host_->DetachClient();
172 delete this;
175 void ShellDevToolsFrontend::HandleMessageFromDevToolsFrontend(
176 const std::string& message) {
177 if (!agent_host_)
178 return;
179 std::string method;
180 base::ListValue* params = NULL;
181 base::DictionaryValue* dict = NULL;
182 scoped_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
183 if (!parsed_message ||
184 !parsed_message->GetAsDictionary(&dict) ||
185 !dict->GetString("method", &method)) {
186 return;
188 int request_id = 0;
189 dict->GetInteger("id", &request_id);
190 dict->GetList("params", &params);
192 std::string browser_message;
193 if (method == "sendMessageToBrowser" && params &&
194 params->GetSize() == 1 && params->GetString(0, &browser_message)) {
195 agent_host_->DispatchProtocolMessage(browser_message);
196 } else if (method == "loadCompleted") {
197 web_contents()->GetMainFrame()->ExecuteJavaScript(
198 base::ASCIIToUTF16("DevToolsAPI.setUseSoftMenu(true);"));
199 } else if (method == "loadNetworkResource" && params->GetSize() == 3) {
200 // TODO(pfeldman): handle some of the embedder messages in content.
201 std::string url;
202 std::string headers;
203 int stream_id;
204 if (!params->GetString(0, &url) ||
205 !params->GetString(1, &headers) ||
206 !params->GetInteger(2, &stream_id)) {
207 return;
210 GURL gurl(url);
211 if (!gurl.is_valid()) {
212 base::DictionaryValue response;
213 response.SetInteger("statusCode", 404);
214 SendMessageAck(request_id, &response);
215 return;
218 net::URLFetcher* fetcher =
219 net::URLFetcher::Create(gurl, net::URLFetcher::GET, this).release();
220 pending_requests_[fetcher] = request_id;
221 fetcher->SetRequestContext(web_contents()->GetBrowserContext()->
222 GetRequestContext());
223 fetcher->SetExtraRequestHeaders(headers);
224 fetcher->SaveResponseWithWriter(scoped_ptr<net::URLFetcherResponseWriter>(
225 new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
226 fetcher->Start();
227 return;
228 } else if (method == "getPreferences") {
229 SendMessageAck(request_id, &preferences_);
230 return;
231 } else if (method == "setPreference") {
232 std::string name;
233 std::string value;
234 if (!params->GetString(0, &name) ||
235 !params->GetString(1, &value)) {
236 return;
238 preferences_.SetStringWithoutPathExpansion(name, value);
239 } else if (method == "removePreference") {
240 std::string name;
241 if (!params->GetString(0, &name))
242 return;
243 preferences_.RemoveWithoutPathExpansion(name, nullptr);
244 } else {
245 return;
248 if (request_id)
249 SendMessageAck(request_id, nullptr);
252 void ShellDevToolsFrontend::HandleMessageFromDevToolsFrontendToBackend(
253 const std::string& message) {
254 if (agent_host_)
255 agent_host_->DispatchProtocolMessage(message);
258 void ShellDevToolsFrontend::DispatchProtocolMessage(
259 DevToolsAgentHost* agent_host, const std::string& message) {
261 if (message.length() < kMaxMessageChunkSize) {
262 base::string16 javascript = base::UTF8ToUTF16(
263 "DevToolsAPI.dispatchMessage(" + message + ");");
264 web_contents()->GetMainFrame()->ExecuteJavaScript(javascript);
265 return;
268 base::FundamentalValue total_size(static_cast<int>(message.length()));
269 for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
270 std::string param;
271 base::JSONWriter::Write(
272 base::StringValue(message.substr(pos, kMaxMessageChunkSize)), &param);
273 std::string code = "DevToolsAPI.dispatchMessageChunk(" + param + ");";
274 base::string16 javascript = base::UTF8ToUTF16(code);
275 web_contents()->GetMainFrame()->ExecuteJavaScript(javascript);
279 void ShellDevToolsFrontend::OnURLFetchComplete(const net::URLFetcher* source) {
280 // TODO(pfeldman): this is a copy of chrome's devtools_ui_bindings.cc.
281 // We should handle some of the commands including this one in content.
282 DCHECK(source);
283 PendingRequestsMap::iterator it = pending_requests_.find(source);
284 DCHECK(it != pending_requests_.end());
286 base::DictionaryValue response;
287 base::DictionaryValue* headers = new base::DictionaryValue();
288 net::HttpResponseHeaders* rh = source->GetResponseHeaders();
289 response.SetInteger("statusCode", rh ? rh->response_code() : 200);
290 response.Set("headers", headers);
292 void* iterator = NULL;
293 std::string name;
294 std::string value;
295 while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
296 headers->SetString(name, value);
298 SendMessageAck(it->second, &response);
299 pending_requests_.erase(it);
300 delete source;
303 void ShellDevToolsFrontend::CallClientFunction(
304 const std::string& function_name,
305 const base::Value* arg1,
306 const base::Value* arg2,
307 const base::Value* arg3) {
308 std::string javascript = function_name + "(";
309 if (arg1) {
310 std::string json;
311 base::JSONWriter::Write(*arg1, &json);
312 javascript.append(json);
313 if (arg2) {
314 base::JSONWriter::Write(*arg2, &json);
315 javascript.append(", ").append(json);
316 if (arg3) {
317 base::JSONWriter::Write(*arg3, &json);
318 javascript.append(", ").append(json);
322 javascript.append(");");
323 web_contents()->GetMainFrame()->ExecuteJavaScript(
324 base::UTF8ToUTF16(javascript));
327 void ShellDevToolsFrontend::SendMessageAck(int request_id,
328 const base::Value* arg) {
329 base::FundamentalValue id_value(request_id);
330 CallClientFunction("DevToolsAPI.embedderMessageAck",
331 &id_value, arg, nullptr);
334 void ShellDevToolsFrontend::AgentHostClosed(
335 DevToolsAgentHost* agent_host, bool replaced) {
336 frontend_shell_->Close();
339 } // namespace content