atom_api_debugger.cc 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. #include "atom/browser/api/atom_api_debugger.h"
  5. #include <string>
  6. #include "atom/browser/atom_browser_main_parts.h"
  7. #include "atom/common/native_mate_converters/callback.h"
  8. #include "atom/common/native_mate_converters/value_converter.h"
  9. #include "base/json/json_reader.h"
  10. #include "base/json/json_writer.h"
  11. #include "base/memory/ptr_util.h"
  12. #include "content/public/browser/devtools_agent_host.h"
  13. #include "content/public/browser/web_contents.h"
  14. #include "native_mate/dictionary.h"
  15. #include "native_mate/object_template_builder.h"
  16. #include "atom/common/node_includes.h"
  17. using content::DevToolsAgentHost;
  18. namespace atom {
  19. namespace api {
  20. Debugger::Debugger(v8::Isolate* isolate, content::WebContents* web_contents)
  21. : web_contents_(web_contents) {
  22. Init(isolate);
  23. }
  24. Debugger::~Debugger() {}
  25. void Debugger::AgentHostClosed(DevToolsAgentHost* agent_host,
  26. bool replaced_with_another_client) {
  27. std::string detach_reason = "target closed";
  28. if (replaced_with_another_client)
  29. detach_reason = "replaced with devtools";
  30. Emit("detach", detach_reason);
  31. }
  32. void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
  33. const std::string& message) {
  34. DCHECK(agent_host == agent_host_.get());
  35. v8::Locker locker(isolate());
  36. v8::HandleScope handle_scope(isolate());
  37. std::unique_ptr<base::Value> parsed_message = base::JSONReader::Read(message);
  38. if (!parsed_message || !parsed_message->is_dict())
  39. return;
  40. base::DictionaryValue* dict =
  41. static_cast<base::DictionaryValue*>(parsed_message.get());
  42. int id;
  43. if (!dict->GetInteger("id", &id)) {
  44. std::string method;
  45. if (!dict->GetString("method", &method))
  46. return;
  47. base::DictionaryValue* params_value = nullptr;
  48. base::DictionaryValue params;
  49. if (dict->GetDictionary("params", &params_value))
  50. params.Swap(params_value);
  51. Emit("message", method, params);
  52. } else {
  53. auto send_command_callback = pending_requests_[id];
  54. pending_requests_.erase(id);
  55. if (send_command_callback.is_null())
  56. return;
  57. base::DictionaryValue* error_body = nullptr;
  58. base::DictionaryValue error;
  59. if (dict->GetDictionary("error", &error_body))
  60. error.Swap(error_body);
  61. base::DictionaryValue* result_body = nullptr;
  62. base::DictionaryValue result;
  63. if (dict->GetDictionary("result", &result_body))
  64. result.Swap(result_body);
  65. send_command_callback.Run(error, result);
  66. }
  67. }
  68. void Debugger::Attach(mate::Arguments* args) {
  69. std::string protocol_version;
  70. args->GetNext(&protocol_version);
  71. if (!protocol_version.empty() &&
  72. !DevToolsAgentHost::IsSupportedProtocolVersion(protocol_version)) {
  73. args->ThrowError("Requested protocol version is not supported");
  74. return;
  75. }
  76. agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents_);
  77. if (!agent_host_.get()) {
  78. args->ThrowError("No target available");
  79. return;
  80. }
  81. if (agent_host_->IsAttached()) {
  82. args->ThrowError("Another debugger is already attached to this target");
  83. return;
  84. }
  85. agent_host_->AttachClient(this);
  86. }
  87. bool Debugger::IsAttached() {
  88. return agent_host_.get() ? agent_host_->IsAttached() : false;
  89. }
  90. void Debugger::Detach() {
  91. if (!agent_host_.get())
  92. return;
  93. agent_host_->DetachClient(this);
  94. AgentHostClosed(agent_host_.get(), false);
  95. agent_host_ = nullptr;
  96. }
  97. void Debugger::SendCommand(mate::Arguments* args) {
  98. if (!agent_host_.get())
  99. return;
  100. std::string method;
  101. if (!args->GetNext(&method)) {
  102. args->ThrowError();
  103. return;
  104. }
  105. base::DictionaryValue command_params;
  106. args->GetNext(&command_params);
  107. SendCommandCallback callback;
  108. args->GetNext(&callback);
  109. base::DictionaryValue request;
  110. int request_id = ++previous_request_id_;
  111. pending_requests_[request_id] = callback;
  112. request.SetInteger("id", request_id);
  113. request.SetString("method", method);
  114. if (!command_params.empty())
  115. request.Set("params", base::WrapUnique(command_params.DeepCopy()));
  116. std::string json_args;
  117. base::JSONWriter::Write(request, &json_args);
  118. agent_host_->DispatchProtocolMessage(this, json_args);
  119. }
  120. // static
  121. mate::Handle<Debugger> Debugger::Create(v8::Isolate* isolate,
  122. content::WebContents* web_contents) {
  123. return mate::CreateHandle(isolate, new Debugger(isolate, web_contents));
  124. }
  125. // static
  126. void Debugger::BuildPrototype(v8::Isolate* isolate,
  127. v8::Local<v8::FunctionTemplate> prototype) {
  128. prototype->SetClassName(mate::StringToV8(isolate, "Debugger"));
  129. mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
  130. .SetMethod("attach", &Debugger::Attach)
  131. .SetMethod("isAttached", &Debugger::IsAttached)
  132. .SetMethod("detach", &Debugger::Detach)
  133. .SetMethod("sendCommand", &Debugger::SendCommand);
  134. }
  135. } // namespace api
  136. } // namespace atom
  137. namespace {
  138. using atom::api::Debugger;
  139. void Initialize(v8::Local<v8::Object> exports,
  140. v8::Local<v8::Value> unused,
  141. v8::Local<v8::Context> context,
  142. void* priv) {
  143. v8::Isolate* isolate = context->GetIsolate();
  144. mate::Dictionary(isolate, exports)
  145. .Set("Debugger", Debugger::GetConstructor(isolate)->GetFunction());
  146. }
  147. } // namespace
  148. NODE_BUILTIN_MODULE_CONTEXT_AWARE(atom_browser_debugger, Initialize);