atom_api_protocol.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #ifndef ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
  5. #define ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
  6. #include <map>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include "atom/browser/api/trackable_object.h"
  12. #include "atom/browser/atom_browser_context.h"
  13. #include "atom/browser/net/atom_url_request_job_factory.h"
  14. #include "atom/common/promise_util.h"
  15. #include "base/callback.h"
  16. #include "base/memory/weak_ptr.h"
  17. #include "base/task/post_task.h"
  18. #include "content/public/browser/browser_task_traits.h"
  19. #include "content/public/browser/browser_thread.h"
  20. #include "native_mate/arguments.h"
  21. #include "native_mate/dictionary.h"
  22. #include "native_mate/handle.h"
  23. #include "net/url_request/url_request_context.h"
  24. namespace base {
  25. class DictionaryValue;
  26. }
  27. namespace atom {
  28. namespace api {
  29. std::vector<std::string> GetStandardSchemes();
  30. void RegisterSchemesAsPrivileged(v8::Local<v8::Value> val,
  31. mate::Arguments* args);
  32. class Protocol : public mate::TrackableObject<Protocol> {
  33. public:
  34. using Handler =
  35. base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
  36. using CompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
  37. static mate::Handle<Protocol> Create(v8::Isolate* isolate,
  38. AtomBrowserContext* browser_context);
  39. static void BuildPrototype(v8::Isolate* isolate,
  40. v8::Local<v8::FunctionTemplate> prototype);
  41. protected:
  42. Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context);
  43. ~Protocol() override;
  44. private:
  45. // Possible errors.
  46. enum ProtocolError {
  47. PROTOCOL_OK, // no error
  48. PROTOCOL_FAIL, // operation failed, should never occur
  49. PROTOCOL_REGISTERED,
  50. PROTOCOL_NOT_REGISTERED,
  51. PROTOCOL_INTERCEPTED,
  52. PROTOCOL_NOT_INTERCEPTED,
  53. };
  54. // The protocol handler that will create a protocol handler for certain
  55. // request job.
  56. template <typename RequestJob>
  57. class CustomProtocolHandler
  58. : public net::URLRequestJobFactory::ProtocolHandler {
  59. public:
  60. CustomProtocolHandler(v8::Isolate* isolate,
  61. net::URLRequestContextGetter* request_context,
  62. const Handler& handler)
  63. : isolate_(isolate),
  64. request_context_(request_context),
  65. handler_(handler) {}
  66. ~CustomProtocolHandler() override {}
  67. net::URLRequestJob* MaybeCreateJob(
  68. net::URLRequest* request,
  69. net::NetworkDelegate* network_delegate) const override {
  70. RequestJob* request_job = new RequestJob(request, network_delegate);
  71. request_job->SetHandlerInfo(isolate_, request_context_, handler_);
  72. return request_job;
  73. }
  74. private:
  75. v8::Isolate* isolate_;
  76. net::URLRequestContextGetter* request_context_;
  77. Protocol::Handler handler_;
  78. DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
  79. };
  80. // Register the protocol with certain request job.
  81. template <typename RequestJob>
  82. void RegisterProtocol(const std::string& scheme,
  83. const Handler& handler,
  84. mate::Arguments* args) {
  85. CompletionCallback callback;
  86. args->GetNext(&callback);
  87. auto* getter = static_cast<URLRequestContextGetter*>(
  88. browser_context_->GetRequestContext());
  89. base::PostTaskWithTraitsAndReplyWithResult(
  90. FROM_HERE, {content::BrowserThread::IO},
  91. base::BindOnce(&Protocol::RegisterProtocolInIO<RequestJob>,
  92. base::RetainedRef(getter), isolate(), scheme, handler),
  93. base::BindOnce(&Protocol::OnIOCompleted, GetWeakPtr(), callback));
  94. }
  95. template <typename RequestJob>
  96. static ProtocolError RegisterProtocolInIO(
  97. scoped_refptr<URLRequestContextGetter> request_context_getter,
  98. v8::Isolate* isolate,
  99. const std::string& scheme,
  100. const Handler& handler) {
  101. auto* job_factory = request_context_getter->job_factory();
  102. if (job_factory->IsHandledProtocol(scheme))
  103. return PROTOCOL_REGISTERED;
  104. auto protocol_handler = std::make_unique<CustomProtocolHandler<RequestJob>>(
  105. isolate, request_context_getter.get(), handler);
  106. if (job_factory->SetProtocolHandler(scheme, std::move(protocol_handler)))
  107. return PROTOCOL_OK;
  108. else
  109. return PROTOCOL_FAIL;
  110. }
  111. // Unregister the protocol handler that handles |scheme|.
  112. void UnregisterProtocol(const std::string& scheme, mate::Arguments* args);
  113. static ProtocolError UnregisterProtocolInIO(
  114. scoped_refptr<URLRequestContextGetter> request_context_getter,
  115. const std::string& scheme);
  116. // Whether the protocol has handler registered.
  117. v8::Local<v8::Promise> IsProtocolHandled(const std::string& scheme);
  118. // Replace the protocol handler with a new one.
  119. template <typename RequestJob>
  120. void InterceptProtocol(const std::string& scheme,
  121. const Handler& handler,
  122. mate::Arguments* args) {
  123. CompletionCallback callback;
  124. args->GetNext(&callback);
  125. auto* getter = static_cast<URLRequestContextGetter*>(
  126. browser_context_->GetRequestContext());
  127. base::PostTaskWithTraitsAndReplyWithResult(
  128. FROM_HERE, {content::BrowserThread::IO},
  129. base::BindOnce(&Protocol::InterceptProtocolInIO<RequestJob>,
  130. base::RetainedRef(getter), isolate(), scheme, handler),
  131. base::BindOnce(&Protocol::OnIOCompleted, GetWeakPtr(), callback));
  132. }
  133. template <typename RequestJob>
  134. static ProtocolError InterceptProtocolInIO(
  135. scoped_refptr<URLRequestContextGetter> request_context_getter,
  136. v8::Isolate* isolate,
  137. const std::string& scheme,
  138. const Handler& handler) {
  139. auto* job_factory = request_context_getter->job_factory();
  140. if (!job_factory->IsHandledProtocol(scheme))
  141. return PROTOCOL_NOT_REGISTERED;
  142. // It is possible a protocol is handled but can not be intercepted.
  143. if (!job_factory->HasProtocolHandler(scheme))
  144. return PROTOCOL_FAIL;
  145. auto protocol_handler = std::make_unique<CustomProtocolHandler<RequestJob>>(
  146. isolate, request_context_getter.get(), handler);
  147. if (!job_factory->InterceptProtocol(scheme, std::move(protocol_handler)))
  148. return PROTOCOL_INTERCEPTED;
  149. return PROTOCOL_OK;
  150. }
  151. // Restore the |scheme| to its original protocol handler.
  152. void UninterceptProtocol(const std::string& scheme, mate::Arguments* args);
  153. static ProtocolError UninterceptProtocolInIO(
  154. scoped_refptr<URLRequestContextGetter> request_context_getter,
  155. const std::string& scheme);
  156. // Convert error code to JS exception and call the callback.
  157. void OnIOCompleted(const CompletionCallback& callback, ProtocolError error);
  158. // Convert error code to string.
  159. std::string ErrorCodeToString(ProtocolError error);
  160. base::WeakPtr<Protocol> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
  161. scoped_refptr<AtomBrowserContext> browser_context_;
  162. base::WeakPtrFactory<Protocol> weak_factory_;
  163. DISALLOW_COPY_AND_ASSIGN(Protocol);
  164. };
  165. } // namespace api
  166. } // namespace atom
  167. #endif // ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_