1 // Copyright (c) 2016 GitHub, Inc.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
5 #include "atom/browser/api/atom_api_debugger.h"
9 #include "atom/browser/atom_browser_main_parts.h"
10 #include "atom/common/native_mate_converters/callback.h"
11 #include "atom/common/native_mate_converters/value_converter.h"
12 #include "atom/common/node_includes.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "content/public/browser/devtools_agent_host.h"
16 #include "content/public/browser/web_contents.h"
17 #include "native_mate/dictionary.h"
18 #include "native_mate/object_template_builder.h"
20 using content::DevToolsAgentHost;
28 // The wrapDebugger funtion which is implemented in JavaScript.
29 using WrapDebuggerCallback = base::Callback<void(v8::Local<v8::Value>)>;
30 WrapDebuggerCallback g_wrap_debugger;
34 Debugger::Debugger(v8::Isolate* isolate, content::WebContents* web_contents)
35 : web_contents_(web_contents),
36 previous_request_id_(0) {
40 Debugger::~Debugger() {
43 void Debugger::AgentHostClosed(DevToolsAgentHost* agent_host,
44 bool replaced_with_another_client) {
45 std::string detach_reason = "target closed";
46 if (replaced_with_another_client)
47 detach_reason = "replaced with devtools";
48 Emit("detach", detach_reason);
51 void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
52 const std::string& message) {
53 DCHECK(agent_host == agent_host_.get());
55 std::unique_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
56 if (!parsed_message->IsType(base::Value::TYPE_DICTIONARY))
59 base::DictionaryValue* dict =
60 static_cast<base::DictionaryValue*>(parsed_message.get());
62 if (!dict->GetInteger("id", &id)) {
64 if (!dict->GetString("method", &method))
66 base::DictionaryValue* params_value = nullptr;
67 base::DictionaryValue params;
68 if (dict->GetDictionary("params", ¶ms_value))
69 params.Swap(params_value);
70 Emit("message", method, params);
72 auto send_command_callback = pending_requests_[id];
73 pending_requests_.erase(id);
74 if (send_command_callback.is_null())
76 base::DictionaryValue* error_body = nullptr;
77 base::DictionaryValue error;
78 if (dict->GetDictionary("error", &error_body))
79 error.Swap(error_body);
81 base::DictionaryValue* result_body = nullptr;
82 base::DictionaryValue result;
83 if (dict->GetDictionary("result", &result_body))
84 result.Swap(result_body);
85 send_command_callback.Run(error, result);
89 void Debugger::Attach(mate::Arguments* args) {
90 std::string protocol_version;
91 args->GetNext(&protocol_version);
93 if (!protocol_version.empty() &&
94 !DevToolsAgentHost::IsSupportedProtocolVersion(protocol_version)) {
95 args->ThrowError("Requested protocol version is not supported");
98 agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents_);
99 if (!agent_host_.get()) {
100 args->ThrowError("No target available");
103 if (agent_host_->IsAttached()) {
104 args->ThrowError("Another debugger is already attached to this target");
108 agent_host_->AttachClient(this);
111 bool Debugger::IsAttached() {
112 return agent_host_.get() ? agent_host_->IsAttached() : false;
115 void Debugger::Detach() {
116 if (!agent_host_.get())
118 agent_host_->DetachClient();
119 AgentHostClosed(agent_host_.get(), false);
120 agent_host_ = nullptr;
123 void Debugger::SendCommand(mate::Arguments* args) {
124 if (!agent_host_.get())
128 if (!args->GetNext(&method)) {
132 base::DictionaryValue command_params;
133 args->GetNext(&command_params);
134 SendCommandCallback callback;
135 args->GetNext(&callback);
137 base::DictionaryValue request;
138 int request_id = ++previous_request_id_;
139 pending_requests_[request_id] = callback;
140 request.SetInteger("id", request_id);
141 request.SetString("method", method);
142 if (!command_params.empty())
143 request.Set("params", command_params.DeepCopy());
145 std::string json_args;
146 base::JSONWriter::Write(request, &json_args);
147 agent_host_->DispatchProtocolMessage(json_args);
151 mate::Handle<Debugger> Debugger::Create(
152 v8::Isolate* isolate,
153 content::WebContents* web_contents) {
154 auto handle = mate::CreateHandle(
155 isolate, new Debugger(isolate, web_contents));
156 g_wrap_debugger.Run(handle.ToV8());
161 void Debugger::BuildPrototype(v8::Isolate* isolate,
162 v8::Local<v8::ObjectTemplate> prototype) {
163 mate::ObjectTemplateBuilder(isolate, prototype)
164 .SetMethod("attach", &Debugger::Attach)
165 .SetMethod("isAttached", &Debugger::IsAttached)
166 .SetMethod("detach", &Debugger::Detach)
167 .SetMethod("sendCommand", &Debugger::SendCommand);
170 void SetWrapDebugger(const WrapDebuggerCallback& callback) {
171 g_wrap_debugger = callback;
180 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
181 v8::Local<v8::Context> context, void* priv) {
182 v8::Isolate* isolate = context->GetIsolate();
183 mate::Dictionary dict(isolate, exports);
184 dict.SetMethod("_setWrapDebugger", &atom::api::SetWrapDebugger);
189 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_debugger, Initialize);