Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: content/shell/browser/shell_devtools_frontend.cc

Issue 2756623002: DevTools: extract bindings from ShellDevToolsFrontend (Closed)
Patch Set: fixup Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/shell/browser/shell_devtools_frontend.h" 5 #include "content/shell/browser/shell_devtools_frontend.h"
6 6
7 #include <stddef.h>
8
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/json/string_escape.h"
12 #include "base/macros.h"
13 #include "base/strings/string_number_conversions.h" 7 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h" 8 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h" 9 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/storage_partition.h"
21 #include "content/public/browser/web_contents.h" 10 #include "content/public/browser/web_contents.h"
22 #include "content/public/common/content_client.h"
23 #include "content/shell/browser/shell.h" 11 #include "content/shell/browser/shell.h"
24 #include "content/shell/browser/shell_browser_context.h" 12 #include "content/shell/browser/shell_browser_context.h"
25 #include "content/shell/browser/shell_browser_main_parts.h" 13 #include "content/shell/browser/shell_devtools_bindings.h"
26 #include "content/shell/browser/shell_content_browser_client.h"
27 #include "content/shell/browser/shell_devtools_manager_delegate.h" 14 #include "content/shell/browser/shell_devtools_manager_delegate.h"
28 #include "net/base/io_buffer.h"
29 #include "net/base/net_errors.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/url_request/url_fetcher.h"
32 #include "net/url_request/url_fetcher_response_writer.h"
33 15
34 namespace content { 16 namespace content {
35 17
36 namespace { 18 namespace {
37
38
39 // ResponseWriter -------------------------------------------------------------
40
41 class ResponseWriter : public net::URLFetcherResponseWriter {
42 public:
43 ResponseWriter(base::WeakPtr<ShellDevToolsFrontend> shell_devtools_,
44 int stream_id);
45 ~ResponseWriter() override;
46
47 // URLFetcherResponseWriter overrides:
48 int Initialize(const net::CompletionCallback& callback) override;
49 int Write(net::IOBuffer* buffer,
50 int num_bytes,
51 const net::CompletionCallback& callback) override;
52 int Finish(int net_error, const net::CompletionCallback& callback) override;
53
54 private:
55 base::WeakPtr<ShellDevToolsFrontend> shell_devtools_;
56 int stream_id_;
57
58 DISALLOW_COPY_AND_ASSIGN(ResponseWriter);
59 };
60
61 ResponseWriter::ResponseWriter(
62 base::WeakPtr<ShellDevToolsFrontend> shell_devtools,
63 int stream_id)
64 : shell_devtools_(shell_devtools),
65 stream_id_(stream_id) {
66 }
67
68 ResponseWriter::~ResponseWriter() {
69 }
70
71 int ResponseWriter::Initialize(const net::CompletionCallback& callback) {
72 return net::OK;
73 }
74
75 int ResponseWriter::Write(net::IOBuffer* buffer,
76 int num_bytes,
77 const net::CompletionCallback& callback) {
78 std::string chunk = std::string(buffer->data(), num_bytes);
79 if (!base::IsStringUTF8(chunk))
80 return num_bytes;
81
82 base::Value* id = new base::Value(stream_id_);
83 base::Value* chunkValue = new base::Value(chunk);
84
85 content::BrowserThread::PostTask(
86 content::BrowserThread::UI, FROM_HERE,
87 base::Bind(&ShellDevToolsFrontend::CallClientFunction,
88 shell_devtools_, "DevToolsAPI.streamWrite",
89 base::Owned(id), base::Owned(chunkValue), nullptr));
90 return num_bytes;
91 }
92
93 int ResponseWriter::Finish(int net_error,
94 const net::CompletionCallback& callback) {
95 return net::OK;
96 }
97
98 static GURL GetFrontendURL() { 19 static GURL GetFrontendURL() {
99 int port = ShellDevToolsManagerDelegate::GetHttpHandlerPort(); 20 int port = ShellDevToolsManagerDelegate::GetHttpHandlerPort();
100 return GURL( 21 return GURL(
101 base::StringPrintf("http://127.0.0.1:%d/devtools/inspector.html", port)); 22 base::StringPrintf("http://127.0.0.1:%d/devtools/inspector.html", port));
102 } 23 }
103
104 } // namespace 24 } // namespace
105 25
106 // This constant should be in sync with
107 // the constant at devtools_ui_bindings.cc.
108 const size_t kMaxMessageChunkSize = IPC::Channel::kMaximumMessageSize / 4;
109
110 // static 26 // static
111 ShellDevToolsFrontend* ShellDevToolsFrontend::Show( 27 ShellDevToolsFrontend* ShellDevToolsFrontend::Show(
112 WebContents* inspected_contents) { 28 WebContents* inspected_contents) {
113 Shell* shell = Shell::CreateNewWindow(inspected_contents->GetBrowserContext(), 29 Shell* shell = Shell::CreateNewWindow(inspected_contents->GetBrowserContext(),
114 GURL(), 30 GURL(),
115 NULL, 31 NULL,
116 gfx::Size()); 32 gfx::Size());
117 ShellDevToolsFrontend* devtools_frontend = new ShellDevToolsFrontend( 33 ShellDevToolsFrontend* devtools_frontend = new ShellDevToolsFrontend(
118 shell, 34 shell,
119 inspected_contents); 35 inspected_contents);
120 shell->LoadURL(GetFrontendURL()); 36 shell->LoadURL(GetFrontendURL());
121 return devtools_frontend; 37 return devtools_frontend;
122 } 38 }
123 39
124 void ShellDevToolsFrontend::Activate() { 40 void ShellDevToolsFrontend::Activate() {
125 frontend_shell_->ActivateContents(web_contents()); 41 devtools_bindings_->Activate();
dgozman 2017/03/16 21:38:07 Let's not delegate this to bindings.
chenwilliam 2017/03/17 22:08:26 Done.
126 } 42 }
127 43
128 void ShellDevToolsFrontend::Focus() { 44 void ShellDevToolsFrontend::Focus() {
129 web_contents()->Focus(); 45 devtools_bindings_->Focus();
dgozman 2017/03/16 21:38:07 ditto
chenwilliam 2017/03/17 22:08:27 Done.
130 } 46 }
131 47
132 void ShellDevToolsFrontend::InspectElementAt(int x, int y) { 48 void ShellDevToolsFrontend::InspectElementAt(int x, int y) {
133 if (agent_host_) { 49 devtools_bindings_->InspectElementAt(x, y);
134 agent_host_->InspectElement(this, x, y);
135 } else {
136 inspect_element_at_x_ = x;
137 inspect_element_at_y_ = y;
138 }
139 } 50 }
140 51
141 void ShellDevToolsFrontend::Close() { 52 void ShellDevToolsFrontend::Close() {
142 frontend_shell_->Close(); 53 frontend_shell_->Close();
143 } 54 }
144 55
145 void ShellDevToolsFrontend::DisconnectFromTarget() { 56 void ShellDevToolsFrontend::DisconnectFromTarget() {
dgozman 2017/03/16 21:38:07 Is this used by anyone now?
chenwilliam 2017/03/17 22:08:26 Nope, removed.
146 if (!agent_host_) 57 devtools_bindings_->DisconnectFromTarget();
147 return;
148 agent_host_->DetachClient(this);
149 agent_host_ = NULL;
150 } 58 }
151 59
152 ShellDevToolsFrontend::ShellDevToolsFrontend(Shell* frontend_shell, 60 ShellDevToolsFrontend::ShellDevToolsFrontend(Shell* frontend_shell,
153 WebContents* inspected_contents) 61 WebContents* inspected_contents)
154 : WebContentsObserver(frontend_shell->web_contents()), 62 : frontend_shell_(frontend_shell),
155 frontend_shell_(frontend_shell), 63 devtools_bindings_(
156 inspected_contents_(inspected_contents), 64 new ShellDevToolsBindings(frontend_shell->web_contents(),
157 inspect_element_at_x_(-1), 65 inspected_contents)) {
158 inspect_element_at_y_(-1), 66 frontend_shell->web_contents()->SetDelegate(frontend_shell);
dgozman 2017/03/16 21:38:07 Why this call?
chenwilliam 2017/03/17 22:08:26 No longer needed since I'm using ShellDevToolsDele
159 weak_factory_(this) {
160 } 67 }
161 68
162 ShellDevToolsFrontend::~ShellDevToolsFrontend() { 69 ShellDevToolsFrontend::~ShellDevToolsFrontend() {}
163 for (const auto& pair : pending_requests_)
164 delete pair.first;
165 }
166
167 void ShellDevToolsFrontend::RenderViewCreated(
168 RenderViewHost* render_view_host) {
169 if (!frontend_host_) {
170 frontend_host_.reset(DevToolsFrontendHost::Create(
171 web_contents()->GetMainFrame(),
172 base::Bind(&ShellDevToolsFrontend::HandleMessageFromDevToolsFrontend,
173 base::Unretained(this))));
174 }
175 }
176
177 void ShellDevToolsFrontend::DocumentAvailableInMainFrame() {
178 agent_host_ = DevToolsAgentHost::GetOrCreateFor(inspected_contents_);
179 agent_host_->AttachClient(this);
180 if (inspect_element_at_x_ != -1) {
181 agent_host_->InspectElement(
182 this, inspect_element_at_x_, inspect_element_at_y_);
183 inspect_element_at_x_ = -1;
184 inspect_element_at_y_ = -1;
185 }
186 }
187
188 void ShellDevToolsFrontend::WebContentsDestroyed() {
189 if (agent_host_)
190 agent_host_->DetachClient(this);
191 delete this;
192 }
193
194 void ShellDevToolsFrontend::SetPreferences(const std::string& json) {
195 preferences_.Clear();
196 if (json.empty())
197 return;
198 base::DictionaryValue* dict = nullptr;
199 std::unique_ptr<base::Value> parsed = base::JSONReader::Read(json);
200 if (!parsed || !parsed->GetAsDictionary(&dict))
201 return;
202 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
203 if (!it.value().IsType(base::Value::Type::STRING))
204 continue;
205 preferences_.SetWithoutPathExpansion(it.key(), it.value().CreateDeepCopy());
206 }
207 }
208
209 void ShellDevToolsFrontend::HandleMessageFromDevToolsFrontend(
210 const std::string& message) {
211 if (!agent_host_)
212 return;
213 std::string method;
214 base::ListValue* params = NULL;
215 base::DictionaryValue* dict = NULL;
216 std::unique_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
217 if (!parsed_message ||
218 !parsed_message->GetAsDictionary(&dict) ||
219 !dict->GetString("method", &method)) {
220 return;
221 }
222 int request_id = 0;
223 dict->GetInteger("id", &request_id);
224 dict->GetList("params", &params);
225
226 if (method == "dispatchProtocolMessage" && params && params->GetSize() == 1) {
227 if (!agent_host_ || !agent_host_->IsAttached())
228 return;
229 std::string protocol_message;
230 if (!params->GetString(0, &protocol_message))
231 return;
232 agent_host_->DispatchProtocolMessage(this, protocol_message);
233 } else if (method == "loadCompleted") {
234 web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
235 base::ASCIIToUTF16("DevToolsAPI.setUseSoftMenu(true);"));
236 } else if (method == "loadNetworkResource" && params->GetSize() == 3) {
237 // TODO(pfeldman): handle some of the embedder messages in content.
238 std::string url;
239 std::string headers;
240 int stream_id;
241 if (!params->GetString(0, &url) ||
242 !params->GetString(1, &headers) ||
243 !params->GetInteger(2, &stream_id)) {
244 return;
245 }
246
247 GURL gurl(url);
248 if (!gurl.is_valid()) {
249 base::DictionaryValue response;
250 response.SetInteger("statusCode", 404);
251 SendMessageAck(request_id, &response);
252 return;
253 }
254
255 net::URLFetcher* fetcher =
256 net::URLFetcher::Create(gurl, net::URLFetcher::GET, this).release();
257 pending_requests_[fetcher] = request_id;
258 fetcher->SetRequestContext(
259 BrowserContext::GetDefaultStoragePartition(
260 web_contents()->GetBrowserContext())->
261 GetURLRequestContext());
262 fetcher->SetExtraRequestHeaders(headers);
263 fetcher->SaveResponseWithWriter(
264 std::unique_ptr<net::URLFetcherResponseWriter>(
265 new ResponseWriter(weak_factory_.GetWeakPtr(), stream_id)));
266 fetcher->Start();
267 return;
268 } else if (method == "getPreferences") {
269 SendMessageAck(request_id, &preferences_);
270 return;
271 } else if (method == "setPreference") {
272 std::string name;
273 std::string value;
274 if (!params->GetString(0, &name) ||
275 !params->GetString(1, &value)) {
276 return;
277 }
278 preferences_.SetStringWithoutPathExpansion(name, value);
279 } else if (method == "removePreference") {
280 std::string name;
281 if (!params->GetString(0, &name))
282 return;
283 preferences_.RemoveWithoutPathExpansion(name, nullptr);
284 } else if (method == "requestFileSystems") {
285 web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
286 base::ASCIIToUTF16("DevToolsAPI.fileSystemsLoaded([]);"));
287 } else if (method == "reattach") {
288 agent_host_->DetachClient(this);
289 agent_host_->AttachClient(this);
290 } else {
291 return;
292 }
293
294 if (request_id)
295 SendMessageAck(request_id, nullptr);
296 }
297
298 void ShellDevToolsFrontend::DispatchProtocolMessage(
299 DevToolsAgentHost* agent_host, const std::string& message) {
300
301 if (message.length() < kMaxMessageChunkSize) {
302 std::string param;
303 base::EscapeJSONString(message, true, &param);
304 std::string code = "DevToolsAPI.dispatchMessage(" + param + ");";
305 base::string16 javascript = base::UTF8ToUTF16(code);
306 web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(javascript);
307 return;
308 }
309
310 size_t total_size = message.length();
311 for (size_t pos = 0; pos < message.length(); pos += kMaxMessageChunkSize) {
312 std::string param;
313 base::EscapeJSONString(message.substr(pos, kMaxMessageChunkSize), true,
314 &param);
315 std::string code = "DevToolsAPI.dispatchMessageChunk(" + param + "," +
316 std::to_string(pos ? 0 : total_size) + ");";
317 base::string16 javascript = base::UTF8ToUTF16(code);
318 web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(javascript);
319 }
320 }
321
322 void ShellDevToolsFrontend::OnURLFetchComplete(const net::URLFetcher* source) {
323 // TODO(pfeldman): this is a copy of chrome's devtools_ui_bindings.cc.
324 // We should handle some of the commands including this one in content.
325 DCHECK(source);
326 PendingRequestsMap::iterator it = pending_requests_.find(source);
327 DCHECK(it != pending_requests_.end());
328
329 base::DictionaryValue response;
330 base::DictionaryValue* headers = new base::DictionaryValue();
331 net::HttpResponseHeaders* rh = source->GetResponseHeaders();
332 response.SetInteger("statusCode", rh ? rh->response_code() : 200);
333 response.Set("headers", headers);
334
335 size_t iterator = 0;
336 std::string name;
337 std::string value;
338 while (rh && rh->EnumerateHeaderLines(&iterator, &name, &value))
339 headers->SetString(name, value);
340
341 SendMessageAck(it->second, &response);
342 pending_requests_.erase(it);
343 delete source;
344 }
345
346 void ShellDevToolsFrontend::CallClientFunction(
347 const std::string& function_name,
348 const base::Value* arg1,
349 const base::Value* arg2,
350 const base::Value* arg3) {
351 std::string javascript = function_name + "(";
352 if (arg1) {
353 std::string json;
354 base::JSONWriter::Write(*arg1, &json);
355 javascript.append(json);
356 if (arg2) {
357 base::JSONWriter::Write(*arg2, &json);
358 javascript.append(", ").append(json);
359 if (arg3) {
360 base::JSONWriter::Write(*arg3, &json);
361 javascript.append(", ").append(json);
362 }
363 }
364 }
365 javascript.append(");");
366 web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
367 base::UTF8ToUTF16(javascript));
368 }
369
370 void ShellDevToolsFrontend::SendMessageAck(int request_id,
371 const base::Value* arg) {
372 base::Value id_value(request_id);
373 CallClientFunction("DevToolsAPI.embedderMessageAck",
374 &id_value, arg, nullptr);
375 }
376
377 void ShellDevToolsFrontend::AgentHostClosed(
378 DevToolsAgentHost* agent_host, bool replaced) {
379 agent_host_ = nullptr;
380 frontend_shell_->Close();
381 }
382 70
383 } // namespace content 71 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698