Merge pull request #5610 from bigtimebuddy/master
[platform/framework/web/crosswalk-tizen.git] / atom / browser / api / atom_api_debugger.cc
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.
4
5 #include "atom/browser/api/atom_api_debugger.h"
6
7 #include <string>
8
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"
19
20 using content::DevToolsAgentHost;
21
22 namespace atom {
23
24 namespace api {
25
26 namespace {
27
28 // The wrapDebugger funtion which is implemented in JavaScript.
29 using WrapDebuggerCallback = base::Callback<void(v8::Local<v8::Value>)>;
30 WrapDebuggerCallback g_wrap_debugger;
31
32 }  // namespace
33
34 Debugger::Debugger(v8::Isolate* isolate, content::WebContents* web_contents)
35     : web_contents_(web_contents),
36       previous_request_id_(0) {
37   Init(isolate);
38 }
39
40 Debugger::~Debugger() {
41 }
42
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);
49 }
50
51 void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
52                                        const std::string& message) {
53   DCHECK(agent_host == agent_host_.get());
54
55   std::unique_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
56   if (!parsed_message->IsType(base::Value::TYPE_DICTIONARY))
57     return;
58
59   base::DictionaryValue* dict =
60       static_cast<base::DictionaryValue*>(parsed_message.get());
61   int id;
62   if (!dict->GetInteger("id", &id)) {
63     std::string method;
64     if (!dict->GetString("method", &method))
65       return;
66     base::DictionaryValue* params_value = nullptr;
67     base::DictionaryValue params;
68     if (dict->GetDictionary("params", &params_value))
69       params.Swap(params_value);
70     Emit("message", method, params);
71   } else {
72     auto send_command_callback = pending_requests_[id];
73     pending_requests_.erase(id);
74     if (send_command_callback.is_null())
75       return;
76     base::DictionaryValue* error_body = nullptr;
77     base::DictionaryValue error;
78     if (dict->GetDictionary("error", &error_body))
79       error.Swap(error_body);
80
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);
86   }
87 }
88
89 void Debugger::Attach(mate::Arguments* args) {
90   std::string protocol_version;
91   args->GetNext(&protocol_version);
92
93   if (!protocol_version.empty() &&
94       !DevToolsAgentHost::IsSupportedProtocolVersion(protocol_version)) {
95     args->ThrowError("Requested protocol version is not supported");
96     return;
97   }
98   agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents_);
99   if (!agent_host_.get()) {
100     args->ThrowError("No target available");
101     return;
102   }
103   if (agent_host_->IsAttached()) {
104     args->ThrowError("Another debugger is already attached to this target");
105     return;
106   }
107
108   agent_host_->AttachClient(this);
109 }
110
111 bool Debugger::IsAttached() {
112   return agent_host_.get() ? agent_host_->IsAttached() : false;
113 }
114
115 void Debugger::Detach() {
116   if (!agent_host_.get())
117     return;
118   agent_host_->DetachClient();
119   AgentHostClosed(agent_host_.get(), false);
120   agent_host_ = nullptr;
121 }
122
123 void Debugger::SendCommand(mate::Arguments* args) {
124   if (!agent_host_.get())
125     return;
126
127   std::string method;
128   if (!args->GetNext(&method)) {
129     args->ThrowError();
130     return;
131   }
132   base::DictionaryValue command_params;
133   args->GetNext(&command_params);
134   SendCommandCallback callback;
135   args->GetNext(&callback);
136
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());
144
145   std::string json_args;
146   base::JSONWriter::Write(request, &json_args);
147   agent_host_->DispatchProtocolMessage(json_args);
148 }
149
150 // static
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());
157   return handle;
158 }
159
160 // static
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);
168 }
169
170 void SetWrapDebugger(const WrapDebuggerCallback& callback) {
171   g_wrap_debugger = callback;
172 }
173
174 }  // namespace api
175
176 }  // namespace atom
177
178 namespace {
179
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);
185 }
186
187 }  // namespace
188
189 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_debugger, Initialize);