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

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

Issue 174226: Extension ports devtools remote service.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 4 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 | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 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_reader.h"
13 #include "base/json_writer.h"
14 #include "base/message_loop.h"
15 #include "base/string_util.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/debugger/devtools_manager.h"
19 #include "chrome/browser/debugger/devtools_protocol_handler.h"
20 #include "chrome/browser/debugger/devtools_remote_message.h"
21 #include "chrome/browser/debugger/inspectable_tab_proxy.h"
22 #include "chrome/browser/profile.h"
23 #include "chrome/browser/profile_manager.h"
24 #include "chrome/browser/tab_contents/tab_contents.h"
25 #include "chrome/common/devtools_messages.h"
26 #include "chrome/common/render_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,
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
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 // "data": <message body - arbitrary JSON>
67 // }
68 //
69 // The "disconnect" command from the external client, and
70 // "onDisconnect" notification from the ExtensionMessageService, are
71 // similar: with the message port ID in the destination field, but no
72 // "data" field in this case.
73
74 // Commands:
75 static const std::string kConnect = "connect";
76 static const std::string kDisconnect = "disconnect";
77 static const std::string kPostMessage = "postMessage";
78 // Events:
79 static const std::string kOnMessage = "onMessage";
80 static const std::string kOnDisconnect = "onDisconnect";
81
82 // Constants for the JSON message fields.
83 // The type is wstring because the constant is used to get a
84 // DictionaryValue field (which requires a wide string).
85
86 // Mandatory.
87 static const std::wstring kCommandWide = L"command";
88
89 // Always present in messages sent to the external client.
90 static const std::wstring kResultWide = L"result";
91
92 // Field for command-specific parameters. Not strictly necessary, but
93 // makes it more similar to the remote debugger protocol, which should
94 // allow easier reuse of client code.
95 static const std::wstring kDataWide = L"data";
96
97 // Fields within the "data" dictionary:
98
99 // Required for "connect":
100 static const std::wstring kExtensionIdWide = L"extensionId";
101 // Optional in "connect":
102 static const std::wstring kChannelNameWide = L"channelName";
103 static const std::wstring kTabIdWide = L"tabId";
104
105 // Present under "data" in replies to a successful "connect" .
106 static const std::wstring kPortIdWide = L"portId";
107
108 } // namespace
109
110 const std::string ExtensionPortsRemoteService::kToolName = "ExtensionPorts";
111
112 ExtensionPortsRemoteService::ExtensionPortsRemoteService(
113 DevToolsProtocolHandler* delegate)
114 : delegate_(delegate), service_(NULL) {
115 // We need an ExtensionMessageService instance. It hangs off of
116 // |profile|. But we do not have a particular tab or RenderViewHost
117 // as context. I'll just use the first active profile not in
118 // incognito mode. But this is probably not the right way.
119 ProfileManager* profile_manager = g_browser_process->profile_manager();
120 if (!profile_manager) {
121 LOG(WARNING) << "No profile manager for ExtensionPortsRemoteService";
122 return;
123 }
124 for (ProfileManager::ProfileVector::const_iterator it
125 = profile_manager->begin();
126 it != profile_manager->end();
127 ++it) {
128 if (!(*it)->IsOffTheRecord()) {
129 service_ = (*it)->GetExtensionMessageService();
130 break;
131 }
132 }
133 if (!service_)
134 LOG(WARNING) << "No usable profile for ExtensionPortsRemoteService";
135 }
136
137 void ExtensionPortsRemoteService::HandleMessage(
138 const DevToolsRemoteMessage& message) {
139 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
140 const std::string destinationString = message.destination();
141 scoped_ptr<Value> request(JSONReader::Read(message.content(), true));
142 if (request.get() == NULL) {
143 // Bad JSON
144 NOTREACHED();
145 return;
146 }
147 DictionaryValue* content;
148 if (!request->IsType(Value::TYPE_DICTIONARY)) {
149 NOTREACHED(); // Broken protocol :(
150 return;
151 }
152 content = static_cast<DictionaryValue*>(request.get());
153 if (!content->HasKey(kCommandWide)) {
154 NOTREACHED(); // Broken protocol :(
155 return;
156 }
157 std::string command;
158 DictionaryValue response;
159
160 content->GetString(kCommandWide, &command);
161 response.SetString(kCommandWide, command);
162
163 if (!service_) {
164 // This happens if we failed to obtain an ExtensionMessageService
165 // during initialization.
166 NOTREACHED();
167 response.SetInteger(kResultWide, RESULT_NO_SERVICE);
168 SendResponse(response, message.tool(), message.destination());
169 return;
170 }
171
172 int destination = -1;
173 if (destinationString.size() != 0)
174 StringToInt(destinationString, &destination);
175
176 if (command == kConnect) {
177 if (destination != -1) // destination should be empty for this command.
178 response.SetInteger(kResultWide, RESULT_UNKNOWN_COMMAND);
179 else
180 ConnectCommand(content, &response);
181 } else if (command == kDisconnect) {
182 if (destination == -1) // Destination required for this command.
183 response.SetInteger(kResultWide, RESULT_UNKNOWN_COMMAND);
184 else
185 DisconnectCommand(destination, &response);
186 } else if (command == kPostMessage) {
187 if (destination == -1) // Destination required for this command.
188 response.SetInteger(kResultWide, RESULT_UNKNOWN_COMMAND);
189 else
190 PostMessageCommand(destination, content, &response);
191 } else {
192 // Unknown command
193 NOTREACHED();
194 response.SetInteger(kResultWide, RESULT_UNKNOWN_COMMAND);
195 }
196 SendResponse(response, message.tool(), message.destination());
197 }
198
199 void ExtensionPortsRemoteService::OnConnectionLost() {
200 LOG(INFO) << "OnConnectionLost";
201 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
202 DCHECK(service_);
203 for (PortIdSet::iterator it = openPortIds_.begin();
204 it != openPortIds_.end();
205 ++it)
206 service_->CloseChannel(*it);
207 openPortIds_.clear();
208 }
209
210 void ExtensionPortsRemoteService::SendResponse(
211 const Value& response, const std::string& tool,
212 const std::string& destination) {
213 std::string response_content;
214 JSONWriter::Write(&response, false, &response_content);
215 scoped_ptr<DevToolsRemoteMessage> response_message(
216 DevToolsRemoteMessageBuilder::instance().Create(
217 tool, destination, response_content));
218 delegate_->Send(*response_message.get());
219 }
220
221 bool ExtensionPortsRemoteService::Send(IPC::Message *message) {
222 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
223
224 IPC_BEGIN_MESSAGE_MAP(ExtensionPortsRemoteService, *message)
225 IPC_MESSAGE_HANDLER(ViewMsg_ExtensionMessageInvoke,
226 OnExtensionMessageInvoke)
227 IPC_MESSAGE_UNHANDLED_ERROR()
228 IPC_END_MESSAGE_MAP()
229
230 delete message;
231 return true;
232 }
233
234 void ExtensionPortsRemoteService::OnExtensionMessageInvoke(
235 const std::string& function_name, const ListValue& args) {
236 if (function_name == ExtensionMessageService::kDispatchOnMessage) {
237 DCHECK_EQ(args.GetSize(), 2u);
238 std::string message;
239 int port_id;
240 if (args.GetString(0, &message) && args.GetInteger(1, &port_id))
241 OnExtensionMessage(message, port_id);
242 } else 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::OnExtensionMessage(
257 const std::string& message, int port_id) {
258 LOG(INFO) << "Message event: from port " << port_id
259 << ", < " << message << ">";
260 // Transpose the information into a JSON message for the external client.
261 DictionaryValue content;
262 content.SetString(kCommandWide, kOnMessage);
263 content.SetInteger(kResultWide, RESULT_OK);
264 // Turn the stringified message body back into JSON.
265 Value* data = JSONReader::Read(message, false);
266 if (!data) {
267 NOTREACHED();
268 return;
269 }
270 content.Set(kDataWide, data);
271 SendResponse(content, kToolName, IntToString(port_id));
272 }
273
274 void ExtensionPortsRemoteService::OnExtensionPortDisconnected(int port_id) {
275 LOG(INFO) << "Disconnect event for port " << port_id;
276 openPortIds_.erase(port_id);
277 DictionaryValue content;
278 content.SetString(kCommandWide, kOnDisconnect);
279 content.SetInteger(kResultWide, RESULT_OK);
280 SendResponse(content, kToolName, IntToString(port_id));
281 }
282
283 void ExtensionPortsRemoteService::ConnectCommand(
284 DictionaryValue* content, DictionaryValue* response) {
285 // Parse out the parameters.
286 DictionaryValue* data;
287 if (!content->GetDictionary(kDataWide, &data)) {
288 response->SetInteger(kResultWide, RESULT_PARAMETER_ERROR);
289 return;
290 }
291 std::string extension_id;
292 if (!data->GetString(kExtensionIdWide, &extension_id)) {
293 response->SetInteger(kResultWide, RESULT_PARAMETER_ERROR);
294 return;
295 }
296 std::string channel_name = "";
297 data->GetString(kChannelNameWide, &channel_name); // optional.
298 int tab_id = -1;
299 data->GetInteger(kTabIdWide, &tab_id); // optional.
300 int port_id;
301 if (tab_id != -1) { // Resolve the tab ID.
302 const InspectableTabProxy::ControllersMap& navcon_map =
303 delegate_->inspectable_tab_proxy()->controllers_map();
304 InspectableTabProxy::ControllersMap::const_iterator it =
305 navcon_map.find(tab_id);
306 TabContents* tab_contents = NULL;
307 if (it != navcon_map.end())
308 tab_contents = it->second->tab_contents();
309 if (!tab_contents) {
310 LOG(INFO) << "tab not found: " << tab_id;
311 response->SetInteger(kResultWide, RESULT_TAB_NOT_FOUND);
312 return;
313 }
314 // Ask the ExtensionMessageService to open the channel.
315 LOG(INFO) << "Connect: extension_id <" << extension_id
316 << ">, channel_name <" << channel_name << ">"
317 << ", tab " << tab_id;
318 DCHECK(service_);
319 port_id = service_->OpenSpecialChannelToTab(
320 extension_id, channel_name, tab_contents, this);
321 } else { // no tab: channel to an extension' background page / toolstrip.
322 // Ask the ExtensionMessageService to open the channel.
323 LOG(INFO) << "Connect: extension_id <" << extension_id
324 << ">, channel_name <" << channel_name << ">";
325 DCHECK(service_);
326 port_id = service_->OpenSpecialChannelToExtension(
327 extension_id, channel_name, this);
328 }
329 if (port_id == -1) {
330 // Failure: probably the extension ID doesn't exist.
331 LOG(INFO) << "Connect failed";
332 response->SetInteger(kResultWide, RESULT_CONNECT_FAILED);
333 return;
334 }
335 LOG(INFO) << "Connected: port " << port_id;
336 openPortIds_.insert(port_id);
337 // Reply to external client with the port ID assigned to the new channel.
338 DictionaryValue* reply_data = new DictionaryValue();
339 reply_data->SetInteger(kPortIdWide, port_id);
340 response->Set(kDataWide, reply_data);
341 response->SetInteger(kResultWide, RESULT_OK);
342 }
343
344 void ExtensionPortsRemoteService::DisconnectCommand(
345 int port_id, DictionaryValue* response) {
346 LOG(INFO) << "Disconnect port " << port_id;
347 PortIdSet::iterator portEntry = openPortIds_.find(port_id);
348 if (portEntry == openPortIds_.end()) { // unknown port ID.
349 LOG(INFO) << "unknown port: " << port_id;
350 response->SetInteger(kResultWide, RESULT_UNKNOWN_PORT);
351 return;
352 }
353 DCHECK(service_);
354 service_->CloseChannel(port_id);
355 openPortIds_.erase(portEntry);
356 response->SetInteger(kResultWide, RESULT_OK);
357 }
358
359 void ExtensionPortsRemoteService::PostMessageCommand(
360 int port_id, DictionaryValue* content, DictionaryValue* response) {
361 Value* data;
362 if (!content->Get(kDataWide, &data)) {
363 response->SetInteger(kResultWide, RESULT_PARAMETER_ERROR);
364 return;
365 }
366 std::string message;
367 // Stringified the JSON message body.
368 JSONWriter::Write(data, false, &message);
369 LOG(INFO) << "postMessage: port " << port_id
370 << ", message: <" << message << ">";
371 PortIdSet::iterator portEntry = openPortIds_.find(port_id);
372 if (portEntry == openPortIds_.end()) { // Unknown port ID.
373 LOG(INFO) << "unknown port: " << port_id;
374 response->SetInteger(kResultWide, RESULT_UNKNOWN_PORT);
375 return;
376 }
377 // Post the message through the ExtensionMessageService.
378 DCHECK(service_);
379 service_->PostMessageFromRenderer(port_id, message);
380 // Confirm to the external client that we sent its message.
381 response->SetInteger(kResultWide, RESULT_OK);
382 }
OLDNEW
« no previous file with comments | « chrome/browser/debugger/extension_ports_remote_service.h ('k') | chrome/browser/extensions/extension_message_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698