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

Side by Side Diff: chrome/browser/debugger/extension_ports_remote_service.cc

Issue 8635005: DevTools: remove support for legacy remote debugger (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed tests compilation Created 9 years 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 // Implementation of the ExtensionPortsRemoteService.
6
7 // Inspired significantly from debugger_remote_service
8 // and ../automation/extension_port_container.
9
10 #include "chrome/browser/debugger/extension_ports_remote_service.h"
11
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/message_loop.h"
15 #include "base/string_number_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/debugger/devtools_protocol_handler.h"
19 #include "chrome/browser/debugger/devtools_remote_message.h"
20 #include "chrome/browser/debugger/inspectable_tab_proxy.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
23 #include "chrome/common/extensions/extension_messages.h"
24 #include "content/browser/debugger/devtools_manager.h"
25 #include "content/browser/tab_contents/tab_contents.h"
26 #include "content/common/devtools_messages.h"
27
28 namespace {
29
30 // Protocol is as follows:
31 //
32 // From external client:
33 // {"command": "connect",
34 // "data": {
35 // "extensionId": "<extension_id string>",
36 // "channelName": "<port name string>", (optional)
37 // "tabId": <numerical tab ID> (optional)
38 // }
39 // }
40 // To connect to a background page or tool strip, the tabId should be omitted.
41 // Tab IDs can be enumerated with the list_tabs DevToolsService command.
42 //
43 // Response:
44 // {"command": "connect",
45 // "result": 0, (assuming success)
46 // "data": {
47 // "portId": <numerical port ID>
48 // }
49 // }
50 //
51 // Posting a message from external client:
52 // Put the target message port ID in the devtools destination field.
53 // {"command": "postMessage",
54 // "data": <message body - arbitrary JSON>
55 // }
56 // Response:
57 // {"command": "postMessage",
58 // "result": 0 (Assuming success)
59 // }
60 // Note this is a confirmation from the devtools protocol layer, not
61 // a response from the extension.
62 //
63 // Message from an extension to the external client:
64 // The message port ID is in the devtools destination field.
65 // {"command": "onMessage",
66 // "result": 0, (Always 0)
67 // "data": <message body - arbitrary JSON>
68 // }
69 //
70 // The "disconnect" command from the external client, and
71 // "onDisconnect" notification from the ExtensionMessageService, are
72 // similar: with the message port ID in the destination field, but no
73 // "data" field in this case.
74
75 // Commands:
76 const char kConnect[] = "connect";
77 const char kDisconnect[] = "disconnect";
78 const char kPostMessage[] = "postMessage";
79 // Events:
80 const char kOnMessage[] = "onMessage";
81 const char kOnDisconnect[] = "onDisconnect";
82
83 // Constants for the JSON message fields.
84 // The type is wstring because the constant is used to get a
85 // DictionaryValue field (which requires a wide string).
86
87 // Mandatory.
88 const char kCommandKey[] = "command";
89
90 // Always present in messages sent to the external client.
91 const char kResultKey[] = "result";
92
93 // Field for command-specific parameters. Not strictly necessary, but
94 // makes it more similar to the remote debugger protocol, which should
95 // allow easier reuse of client code.
96 const char kDataKey[] = "data";
97
98 // Fields within the "data" dictionary:
99
100 // Required for "connect":
101 const char kExtensionIdKey[] = "extensionId";
102 // Optional in "connect":
103 const char kChannelNameKey[] = "channelName";
104 const char kTabIdKey[] = "tabId";
105
106 // Present under "data" in replies to a successful "connect" .
107 const char kPortIdKey[] = "portId";
108
109 } // namespace
110
111 const char ExtensionPortsRemoteService::kToolName[] = "ExtensionPorts";
112
113 ExtensionPortsRemoteService::ExtensionPortsRemoteService(
114 DevToolsProtocolHandler* delegate)
115 : delegate_(delegate), service_(NULL) {
116 // We need an ExtensionMessageService instance. It hangs off of
117 // |profile|. But we do not have a particular tab or RenderViewHost
118 // as context. I'll just use the first active profile not in
119 // incognito mode. But this is probably not the right way.
120 ProfileManager* profile_manager = g_browser_process->profile_manager();
121 if (!profile_manager) {
122 LOG(WARNING) << "No profile manager for ExtensionPortsRemoteService";
123 return;
124 }
125
126 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
127 for (size_t i = 0; i < profiles.size(); ++i) {
128 if (!profiles[i]->IsOffTheRecord()) {
129 service_ = profiles[i]->GetExtensionMessageService();
130 break;
131 }
132 }
133 if (!service_)
134 LOG(WARNING) << "No usable profile for ExtensionPortsRemoteService";
135 }
136
137 ExtensionPortsRemoteService::~ExtensionPortsRemoteService() {
138 }
139
140 void ExtensionPortsRemoteService::HandleMessage(
141 const DevToolsRemoteMessage& message) {
142 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
143 const std::string destinationString = message.destination();
144 scoped_ptr<Value> request(base::JSONReader::Read(message.content(), true));
145 if (request.get() == NULL) {
146 // Bad JSON
147 NOTREACHED();
148 return;
149 }
150 DictionaryValue* content;
151 if (!request->IsType(Value::TYPE_DICTIONARY)) {
152 NOTREACHED(); // Broken protocol :(
153 return;
154 }
155 content = static_cast<DictionaryValue*>(request.get());
156 if (!content->HasKey(kCommandKey)) {
157 NOTREACHED(); // Broken protocol :(
158 return;
159 }
160 std::string command;
161 DictionaryValue response;
162
163 content->GetString(kCommandKey, &command);
164 response.SetString(kCommandKey, command);
165
166 if (!service_) {
167 // This happens if we failed to obtain an ExtensionMessageService
168 // during initialization.
169 NOTREACHED();
170 response.SetInteger(kResultKey, RESULT_NO_SERVICE);
171 SendResponse(response, message.tool(), message.destination());
172 return;
173 }
174
175 int destination = -1;
176 if (!destinationString.empty())
177 base::StringToInt(destinationString, &destination);
178
179 if (command == kConnect) {
180 if (destination != -1) // destination should be empty for this command.
181 response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND);
182 else
183 ConnectCommand(content, &response);
184 } else if (command == kDisconnect) {
185 if (destination == -1) // Destination required for this command.
186 response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND);
187 else
188 DisconnectCommand(destination, &response);
189 } else if (command == kPostMessage) {
190 if (destination == -1) // Destination required for this command.
191 response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND);
192 else
193 PostMessageCommand(destination, content, &response);
194 } else {
195 // Unknown command
196 NOTREACHED();
197 response.SetInteger(kResultKey, RESULT_UNKNOWN_COMMAND);
198 }
199 SendResponse(response, message.tool(), message.destination());
200 }
201
202 void ExtensionPortsRemoteService::OnConnectionLost() {
203 VLOG(1) << "OnConnectionLost";
204 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
205 DCHECK(service_);
206 for (PortIdSet::iterator it = openPortIds_.begin();
207 it != openPortIds_.end();
208 ++it)
209 service_->CloseChannel(*it);
210 openPortIds_.clear();
211 }
212
213 void ExtensionPortsRemoteService::SendResponse(
214 const Value& response, const std::string& tool,
215 const std::string& destination) {
216 std::string response_content;
217 base::JSONWriter::Write(&response, false, &response_content);
218 scoped_ptr<DevToolsRemoteMessage> response_message(
219 DevToolsRemoteMessageBuilder::instance().Create(
220 tool, destination, response_content));
221 delegate_->Send(*response_message.get());
222 }
223
224 bool ExtensionPortsRemoteService::Send(IPC::Message *message) {
225 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
226
227 IPC_BEGIN_MESSAGE_MAP(ExtensionPortsRemoteService, *message)
228 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke)
229 IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnDeliverMessage)
230 IPC_MESSAGE_UNHANDLED_ERROR()
231 IPC_END_MESSAGE_MAP()
232
233 delete message;
234 return true;
235 }
236
237 void ExtensionPortsRemoteService::OnExtensionMessageInvoke(
238 const std::string& extension_id,
239 const std::string& function_name,
240 const ListValue& args,
241 const GURL& event_url) {
242 if (function_name == ExtensionMessageService::kDispatchOnDisconnect) {
243 DCHECK_EQ(args.GetSize(), 1u);
244 int port_id;
245 if (args.GetInteger(0, &port_id))
246 OnExtensionPortDisconnected(port_id);
247 } else if (function_name == ExtensionMessageService::kDispatchOnConnect) {
248 // There is no way for this service to be addressed and receive
249 // connections.
250 NOTREACHED() << function_name << " shouldn't be called.";
251 } else {
252 NOTREACHED() << function_name << " shouldn't be called.";
253 }
254 }
255
256 void ExtensionPortsRemoteService::OnDeliverMessage(
257 int port_id, const std::string& message) {
258 VLOG(1) << "Message event: from port " << port_id << ", < " << message << ">";
259 // Transpose the information into a JSON message for the external client.
260 DictionaryValue content;
261 content.SetString(kCommandKey, kOnMessage);
262 content.SetInteger(kResultKey, RESULT_OK);
263 // Turn the stringified message body back into JSON.
264 Value* data = base::JSONReader::Read(message, false);
265 if (!data) {
266 NOTREACHED();
267 return;
268 }
269 content.Set(kDataKey, data);
270 SendResponse(content, kToolName, base::IntToString(port_id));
271 }
272
273 void ExtensionPortsRemoteService::OnExtensionPortDisconnected(int port_id) {
274 VLOG(1) << "Disconnect event for port " << port_id;
275 openPortIds_.erase(port_id);
276 DictionaryValue content;
277 content.SetString(kCommandKey, kOnDisconnect);
278 content.SetInteger(kResultKey, RESULT_OK);
279 SendResponse(content, kToolName, base::IntToString(port_id));
280 }
281
282 void ExtensionPortsRemoteService::ConnectCommand(
283 DictionaryValue* content, DictionaryValue* response) {
284 // Parse out the parameters.
285 DictionaryValue* data;
286 if (!content->GetDictionary(kDataKey, &data)) {
287 response->SetInteger(kResultKey, RESULT_PARAMETER_ERROR);
288 return;
289 }
290 std::string extension_id;
291 if (!data->GetString(kExtensionIdKey, &extension_id)) {
292 response->SetInteger(kResultKey, RESULT_PARAMETER_ERROR);
293 return;
294 }
295 std::string channel_name = "";
296 data->GetString(kChannelNameKey, &channel_name); // optional.
297 int tab_id = -1;
298 data->GetInteger(kTabIdKey, &tab_id); // optional.
299 int port_id;
300 if (tab_id != -1) { // Resolve the tab ID.
301 const InspectableTabProxy::TabMap& tab_map =
302 delegate_->inspectable_tab_proxy()->tab_map();
303 InspectableTabProxy::TabMap::const_iterator it = tab_map.find(tab_id);
304 TabContents* tab_contents = NULL;
305 if (it != tab_map.end())
306 tab_contents = it->second->tab_contents();
307 if (!tab_contents) {
308 VLOG(1) << "tab not found: " << tab_id;
309 response->SetInteger(kResultKey, RESULT_TAB_NOT_FOUND);
310 return;
311 }
312 // Ask the ExtensionMessageService to open the channel.
313 VLOG(1) << "Connect: extension_id <" << extension_id
314 << ">, channel_name <" << channel_name
315 << ">, tab " << tab_id;
316 DCHECK(service_);
317 port_id = service_->OpenSpecialChannelToTab(
318 extension_id, channel_name, tab_contents, this);
319 } else { // no tab: channel to an extension page.
320 // Ask the ExtensionMessageService to open the channel.
321 VLOG(1) << "Connect: extension_id <" << extension_id
322 << ">, channel_name <" << channel_name << ">";
323 DCHECK(service_);
324 port_id = service_->OpenSpecialChannelToExtension(
325 extension_id, channel_name, "null", this);
326 }
327 if (port_id == -1) {
328 // Failure: probably the extension ID doesn't exist.
329 VLOG(1) << "Connect failed";
330 response->SetInteger(kResultKey, RESULT_CONNECT_FAILED);
331 return;
332 }
333 VLOG(1) << "Connected: port " << port_id;
334 openPortIds_.insert(port_id);
335 // Reply to external client with the port ID assigned to the new channel.
336 DictionaryValue* reply_data = new DictionaryValue();
337 reply_data->SetInteger(kPortIdKey, port_id);
338 response->Set(kDataKey, reply_data);
339 response->SetInteger(kResultKey, RESULT_OK);
340 }
341
342 void ExtensionPortsRemoteService::DisconnectCommand(
343 int port_id, DictionaryValue* response) {
344 VLOG(1) << "Disconnect port " << port_id;
345 PortIdSet::iterator portEntry = openPortIds_.find(port_id);
346 if (portEntry == openPortIds_.end()) { // unknown port ID.
347 VLOG(1) << "unknown port: " << port_id;
348 response->SetInteger(kResultKey, RESULT_UNKNOWN_PORT);
349 return;
350 }
351 DCHECK(service_);
352 service_->CloseChannel(port_id);
353 openPortIds_.erase(portEntry);
354 response->SetInteger(kResultKey, RESULT_OK);
355 }
356
357 void ExtensionPortsRemoteService::PostMessageCommand(
358 int port_id, DictionaryValue* content, DictionaryValue* response) {
359 Value* data;
360 if (!content->Get(kDataKey, &data)) {
361 response->SetInteger(kResultKey, RESULT_PARAMETER_ERROR);
362 return;
363 }
364 std::string message;
365 // Stringified the JSON message body.
366 base::JSONWriter::Write(data, false, &message);
367 VLOG(1) << "postMessage: port " << port_id
368 << ", message: <" << message << ">";
369 PortIdSet::iterator portEntry = openPortIds_.find(port_id);
370 if (portEntry == openPortIds_.end()) { // Unknown port ID.
371 VLOG(1) << "unknown port: " << port_id;
372 response->SetInteger(kResultKey, RESULT_UNKNOWN_PORT);
373 return;
374 }
375 // Post the message through the ExtensionMessageService.
376 DCHECK(service_);
377 service_->PostMessageFromRenderer(port_id, message);
378 // Confirm to the external client that we sent its message.
379 response->SetInteger(kResultKey, RESULT_OK);
380 }
OLDNEW
« no previous file with comments | « chrome/browser/debugger/extension_ports_remote_service.h ('k') | chrome/browser/debugger/inspectable_tab_proxy.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698