electron_api_service_worker_context.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright (c) 2019 Slack Technologies, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/api/electron_api_service_worker_context.h"
  5. #include <string_view>
  6. #include <utility>
  7. #include "chrome/browser/browser_process.h"
  8. #include "content/public/browser/console_message.h"
  9. #include "content/public/browser/storage_partition.h"
  10. #include "gin/data_object_builder.h"
  11. #include "gin/handle.h"
  12. #include "gin/object_template_builder.h"
  13. #include "shell/browser/api/electron_api_service_worker_main.h"
  14. #include "shell/browser/electron_browser_context.h"
  15. #include "shell/browser/javascript_environment.h"
  16. #include "shell/common/gin_converters/gurl_converter.h"
  17. #include "shell/common/gin_converters/service_worker_converter.h"
  18. #include "shell/common/gin_converters/value_converter.h"
  19. #include "shell/common/gin_helper/dictionary.h"
  20. #include "shell/common/gin_helper/promise.h"
  21. #include "shell/common/node_util.h"
  22. using ServiceWorkerStatus =
  23. content::ServiceWorkerRunningInfo::ServiceWorkerVersionStatus;
  24. namespace electron::api {
  25. namespace {
  26. constexpr std::string_view MessageSourceToString(
  27. const blink::mojom::ConsoleMessageSource source) {
  28. switch (source) {
  29. case blink::mojom::ConsoleMessageSource::kXml:
  30. return "xml";
  31. case blink::mojom::ConsoleMessageSource::kJavaScript:
  32. return "javascript";
  33. case blink::mojom::ConsoleMessageSource::kNetwork:
  34. return "network";
  35. case blink::mojom::ConsoleMessageSource::kConsoleApi:
  36. return "console-api";
  37. case blink::mojom::ConsoleMessageSource::kStorage:
  38. return "storage";
  39. case blink::mojom::ConsoleMessageSource::kRendering:
  40. return "rendering";
  41. case blink::mojom::ConsoleMessageSource::kSecurity:
  42. return "security";
  43. case blink::mojom::ConsoleMessageSource::kDeprecation:
  44. return "deprecation";
  45. case blink::mojom::ConsoleMessageSource::kWorker:
  46. return "worker";
  47. case blink::mojom::ConsoleMessageSource::kViolation:
  48. return "violation";
  49. case blink::mojom::ConsoleMessageSource::kIntervention:
  50. return "intervention";
  51. case blink::mojom::ConsoleMessageSource::kRecommendation:
  52. return "recommendation";
  53. default:
  54. return "other";
  55. }
  56. }
  57. v8::Local<v8::Value> ServiceWorkerRunningInfoToDict(
  58. v8::Isolate* isolate,
  59. const content::ServiceWorkerRunningInfo& info) {
  60. return gin::DataObjectBuilder(isolate)
  61. .Set("scriptUrl", info.script_url.spec())
  62. .Set("scope", info.scope.spec())
  63. .Set("renderProcessId", info.render_process_id)
  64. .Build();
  65. }
  66. } // namespace
  67. gin::WrapperInfo ServiceWorkerContext::kWrapperInfo = {gin::kEmbedderNativeGin};
  68. ServiceWorkerContext::ServiceWorkerContext(
  69. v8::Isolate* isolate,
  70. ElectronBrowserContext* browser_context) {
  71. storage_partition_ = browser_context->GetDefaultStoragePartition();
  72. service_worker_context_ = storage_partition_->GetServiceWorkerContext();
  73. service_worker_context_->AddObserver(this);
  74. }
  75. ServiceWorkerContext::~ServiceWorkerContext() {
  76. service_worker_context_->RemoveObserver(this);
  77. }
  78. void ServiceWorkerContext::OnRunningStatusChanged(
  79. int64_t version_id,
  80. blink::EmbeddedWorkerStatus running_status) {
  81. ServiceWorkerMain* worker =
  82. ServiceWorkerMain::FromVersionID(version_id, storage_partition_);
  83. if (worker)
  84. worker->OnRunningStatusChanged(running_status);
  85. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  86. v8::HandleScope scope(isolate);
  87. EmitWithoutEvent("running-status-changed",
  88. gin::DataObjectBuilder(isolate)
  89. .Set("versionId", version_id)
  90. .Set("runningStatus", running_status)
  91. .Build());
  92. }
  93. void ServiceWorkerContext::OnReportConsoleMessage(
  94. int64_t version_id,
  95. const GURL& scope,
  96. const content::ConsoleMessage& message) {
  97. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  98. v8::HandleScope handle_scope(isolate);
  99. Emit("console-message",
  100. gin::DataObjectBuilder(isolate)
  101. .Set("versionId", version_id)
  102. .Set("source", MessageSourceToString(message.source))
  103. .Set("level", static_cast<int32_t>(message.message_level))
  104. .Set("message", message.message)
  105. .Set("lineNumber", message.line_number)
  106. .Set("sourceUrl", message.source_url.spec())
  107. .Build());
  108. }
  109. void ServiceWorkerContext::OnRegistrationCompleted(const GURL& scope) {
  110. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  111. v8::HandleScope handle_scope(isolate);
  112. Emit("registration-completed",
  113. gin::DataObjectBuilder(isolate).Set("scope", scope).Build());
  114. }
  115. void ServiceWorkerContext::OnVersionRedundant(int64_t version_id,
  116. const GURL& scope) {
  117. ServiceWorkerMain* worker =
  118. ServiceWorkerMain::FromVersionID(version_id, storage_partition_);
  119. if (worker)
  120. worker->OnVersionRedundant();
  121. }
  122. void ServiceWorkerContext::OnVersionStartingRunning(int64_t version_id) {
  123. OnRunningStatusChanged(version_id, blink::EmbeddedWorkerStatus::kStarting);
  124. }
  125. void ServiceWorkerContext::OnVersionStartedRunning(
  126. int64_t version_id,
  127. const content::ServiceWorkerRunningInfo& running_info) {
  128. OnRunningStatusChanged(version_id, blink::EmbeddedWorkerStatus::kRunning);
  129. }
  130. void ServiceWorkerContext::OnVersionStoppingRunning(int64_t version_id) {
  131. OnRunningStatusChanged(version_id, blink::EmbeddedWorkerStatus::kStopping);
  132. }
  133. void ServiceWorkerContext::OnVersionStoppedRunning(int64_t version_id) {
  134. OnRunningStatusChanged(version_id, blink::EmbeddedWorkerStatus::kStopped);
  135. }
  136. void ServiceWorkerContext::OnDestruct(content::ServiceWorkerContext* context) {
  137. if (context == service_worker_context_) {
  138. delete this;
  139. }
  140. }
  141. v8::Local<v8::Value> ServiceWorkerContext::GetAllRunningWorkerInfo(
  142. v8::Isolate* isolate) {
  143. gin::DataObjectBuilder builder(isolate);
  144. const base::flat_map<int64_t, content::ServiceWorkerRunningInfo>& info_map =
  145. service_worker_context_->GetRunningServiceWorkerInfos();
  146. for (const auto& iter : info_map) {
  147. builder.Set(
  148. base::NumberToString(iter.first),
  149. ServiceWorkerRunningInfoToDict(isolate, std::move(iter.second)));
  150. }
  151. return builder.Build();
  152. }
  153. v8::Local<v8::Value> ServiceWorkerContext::GetInfoFromVersionID(
  154. gin_helper::ErrorThrower thrower,
  155. int64_t version_id) {
  156. const base::flat_map<int64_t, content::ServiceWorkerRunningInfo>& info_map =
  157. service_worker_context_->GetRunningServiceWorkerInfos();
  158. auto iter = info_map.find(version_id);
  159. if (iter == info_map.end()) {
  160. thrower.ThrowError("Could not find service worker with that version_id");
  161. return {};
  162. }
  163. return ServiceWorkerRunningInfoToDict(thrower.isolate(),
  164. std::move(iter->second));
  165. }
  166. v8::Local<v8::Value> ServiceWorkerContext::GetFromVersionID(
  167. gin_helper::ErrorThrower thrower,
  168. int64_t version_id) {
  169. util::EmitWarning(thrower.isolate(),
  170. "The session.serviceWorkers.getFromVersionID API is "
  171. "deprecated, use "
  172. "session.serviceWorkers.getInfoFromVersionID instead.",
  173. "ServiceWorkersDeprecateGetFromVersionID");
  174. return GetInfoFromVersionID(thrower, version_id);
  175. }
  176. v8::Local<v8::Value> ServiceWorkerContext::GetWorkerFromVersionID(
  177. v8::Isolate* isolate,
  178. int64_t version_id) {
  179. return ServiceWorkerMain::From(isolate, service_worker_context_,
  180. storage_partition_, version_id)
  181. .ToV8();
  182. }
  183. gin::Handle<ServiceWorkerMain>
  184. ServiceWorkerContext::GetWorkerFromVersionIDIfExists(v8::Isolate* isolate,
  185. int64_t version_id) {
  186. ServiceWorkerMain* worker =
  187. ServiceWorkerMain::FromVersionID(version_id, storage_partition_);
  188. if (!worker)
  189. return gin::Handle<ServiceWorkerMain>();
  190. return gin::CreateHandle(isolate, worker);
  191. }
  192. v8::Local<v8::Promise> ServiceWorkerContext::StartWorkerForScope(
  193. v8::Isolate* isolate,
  194. GURL scope) {
  195. auto shared_promise =
  196. std::make_shared<gin_helper::Promise<v8::Local<v8::Value>>>(isolate);
  197. v8::Local<v8::Promise> handle = shared_promise->GetHandle();
  198. blink::StorageKey storage_key =
  199. blink::StorageKey::CreateFirstParty(url::Origin::Create(scope));
  200. service_worker_context_->StartWorkerForScope(
  201. scope, storage_key,
  202. base::BindOnce(&ServiceWorkerContext::DidStartWorkerForScope,
  203. weak_ptr_factory_.GetWeakPtr(), shared_promise),
  204. base::BindOnce(&ServiceWorkerContext::DidFailToStartWorkerForScope,
  205. weak_ptr_factory_.GetWeakPtr(), shared_promise));
  206. return handle;
  207. }
  208. void ServiceWorkerContext::DidStartWorkerForScope(
  209. std::shared_ptr<gin_helper::Promise<v8::Local<v8::Value>>> shared_promise,
  210. int64_t version_id,
  211. int process_id,
  212. int thread_id) {
  213. v8::Isolate* isolate = shared_promise->isolate();
  214. v8::HandleScope handle_scope(isolate);
  215. v8::Local<v8::Value> service_worker_main =
  216. GetWorkerFromVersionID(isolate, version_id);
  217. shared_promise->Resolve(service_worker_main);
  218. shared_promise.reset();
  219. }
  220. void ServiceWorkerContext::DidFailToStartWorkerForScope(
  221. std::shared_ptr<gin_helper::Promise<v8::Local<v8::Value>>> shared_promise,
  222. content::StatusCodeResponse status) {
  223. shared_promise->RejectWithErrorMessage("Failed to start service worker.");
  224. shared_promise.reset();
  225. }
  226. v8::Local<v8::Promise> ServiceWorkerContext::StopAllWorkers(
  227. v8::Isolate* isolate) {
  228. auto promise = gin_helper::Promise<void>(isolate);
  229. v8::Local<v8::Promise> handle = promise.GetHandle();
  230. service_worker_context_->StopAllServiceWorkers(base::BindOnce(
  231. [](gin_helper::Promise<void> promise) { promise.Resolve(); },
  232. std::move(promise)));
  233. return handle;
  234. }
  235. // static
  236. gin::Handle<ServiceWorkerContext> ServiceWorkerContext::Create(
  237. v8::Isolate* isolate,
  238. ElectronBrowserContext* browser_context) {
  239. return gin::CreateHandle(isolate,
  240. new ServiceWorkerContext(isolate, browser_context));
  241. }
  242. // static
  243. gin::ObjectTemplateBuilder ServiceWorkerContext::GetObjectTemplateBuilder(
  244. v8::Isolate* isolate) {
  245. return gin_helper::EventEmitterMixin<
  246. ServiceWorkerContext>::GetObjectTemplateBuilder(isolate)
  247. .SetMethod("getAllRunning",
  248. &ServiceWorkerContext::GetAllRunningWorkerInfo)
  249. .SetMethod("getFromVersionID", &ServiceWorkerContext::GetFromVersionID)
  250. .SetMethod("getInfoFromVersionID",
  251. &ServiceWorkerContext::GetInfoFromVersionID)
  252. .SetMethod("getWorkerFromVersionID",
  253. &ServiceWorkerContext::GetWorkerFromVersionID)
  254. .SetMethod("_getWorkerFromVersionIDIfExists",
  255. &ServiceWorkerContext::GetWorkerFromVersionIDIfExists)
  256. .SetMethod("startWorkerForScope",
  257. &ServiceWorkerContext::StartWorkerForScope)
  258. .SetMethod("_stopAllWorkers", &ServiceWorkerContext::StopAllWorkers);
  259. }
  260. const char* ServiceWorkerContext::GetTypeName() {
  261. return "ServiceWorkerContext";
  262. }
  263. } // namespace electron::api