Skip to content

Commit e7b0a9c

Browse files
feat: add API for receiving logs from service workers (electron#20624)
* feat: add API for receiving logs from service workers * feat: add new serviceWorkerContext APIs * chore: add missing #include's * refactor: rename serviceWorkerContext to serviceWorkers * chore: clean up based on review * chore: remove native_mate * chore: add tests for the service worker module * Update spec-main/api-service-workers-spec.ts Co-Authored-By: Jeremy Apthorp <jeremya@chromium.org> * chore: fix linting * chore: handle renames Co-authored-by: Jeremy Apthorp <nornagon@nornagon.net>
1 parent 2e6fff8 commit e7b0a9c

19 files changed

Lines changed: 456 additions & 324 deletions

docs/api/service-workers.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
## Class: ServiceWorkers
2+
3+
> Query and receive events from a sessions active service workers.
4+
5+
Process: [Main](../glossary.md#main-process)
6+
7+
Instances of the `ServiceWorkers` class are accessed by using `serviceWorkers` property of
8+
a `Session`.
9+
10+
For example:
11+
12+
```javascript
13+
const { session } = require('electron')
14+
15+
// Get all service workers.
16+
console.log(session.defaultSession.serviceWorkers.getAllRunning())
17+
18+
// Handle logs and get service worker info
19+
session.defaultSession.serviceWorkers.on('console-message', (event, messageDetails) => {
20+
console.log(
21+
'Got service worker message',
22+
messageDetails,
23+
'from',
24+
session.defaultSession.serviceWorkers.getFromVersionID(messageDetails.versionId)
25+
)
26+
})
27+
```
28+
29+
### Instance Events
30+
31+
The following events are available on instances of `ServiceWorkers`:
32+
33+
#### Event: 'console-message'
34+
35+
Returns:
36+
37+
* `event` Event
38+
* `messageDetails` Object - Information about the console message
39+
* `message` String - The actual console message
40+
* `versionId` Number - The version ID of the service worker that sent the log message
41+
* `source` String - The type of source for this message. Can be `javascript`, `xml`, `network`, `console-api`, `storage`, `app-cache`, `rendering`, `security`, `deprecation`, `worker`, `violation`, `intervention`, `recommendation` or `other`.
42+
* `level` Number - The log level, from 0 to 3. In order it matches `verbose`, `info`, `warning` and `error`.
43+
* `sourceUrl` String - The URL the message came from
44+
* `lineNumber` Number - The line number of the source that triggered this console message
45+
46+
Emitted when a service worker logs something to the console.
47+
48+
### Instance Methods
49+
50+
The following methods are available on instances of `ServiceWorkers`:
51+
52+
#### `serviceWorkers.getAllRunning()`
53+
54+
Returns `Record<Number, ServiceWorkerInfo>` - A [ServiceWorkerInfo](structures/service-worker-info.md) object where the keys are the service worker version ID and the values are the information about that service worker.
55+
56+
#### `serviceWorkers.getFromVersionID(versionId)`
57+
58+
* `versionId` Number
59+
60+
Returns [`ServiceWorkerInfo`](structures/service-worker-info.md) - Information about this service worker
61+
62+
If the service worker does not exist or is not running this method will throw an exception.

docs/api/session.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,10 @@ code to the `setSpellCheckerLanaguages` API that isn't in this array will result
585585

586586
A [`Cookies`](cookies.md) object for this session.
587587

588+
#### `ses.serviceWorkers` _Readonly_
589+
590+
A [`ServiceWorkers`](service-workers.md) object for this session.
591+
588592
#### `ses.webRequest` _Readonly_
589593

590594
A [`WebRequest`](web-request.md) object for this session.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# ServiceWorkerInfo Object
2+
3+
* `scriptUrl` String - The full URL to the script that this service worker runs
4+
* `scope` String - The base URL that this service worker is active for.
5+
* `renderProcessId` Number - The virtual ID of the process that this service worker is running in. This is not an OS level PID. This aligns with the ID set used for `webContents.getProcessId()`.

filenames.auto.gni

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ auto_filenames = {
4646
"docs/api/remote.md",
4747
"docs/api/sandbox-option.md",
4848
"docs/api/screen.md",
49+
"docs/api/service-workers.md",
4950
"docs/api/session.md",
5051
"docs/api/shell.md",
5152
"docs/api/structures",
@@ -111,6 +112,7 @@ auto_filenames = {
111112
"docs/api/structures/remove-password.md",
112113
"docs/api/structures/scrubber-item.md",
113114
"docs/api/structures/segmented-control-segment.md",
115+
"docs/api/structures/service-worker-info.md",
114116
"docs/api/structures/shared-worker-info.md",
115117
"docs/api/structures/shortcut-details.md",
116118
"docs/api/structures/size.md",

filenames.gni

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ filenames = {
8888
"shell/browser/api/electron_api_protocol.h",
8989
"shell/browser/api/electron_api_screen.cc",
9090
"shell/browser/api/electron_api_screen.h",
91+
"shell/browser/api/electron_api_service_worker_context.cc",
92+
"shell/browser/api/electron_api_service_worker_context.h",
9193
"shell/browser/api/electron_api_session.cc",
9294
"shell/browser/api/electron_api_session.h",
9395
"shell/browser/api/electron_api_system_preferences.cc",

lib/browser/api/session.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const { EventEmitter } = require('events')
44
const { app, deprecate } = require('electron')
5-
const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session')
5+
const { fromPartition, Session, Cookies, NetLog, Protocol, ServiceWorkerContext } = process.electronBinding('session')
66

77
// Public API.
88
Object.defineProperties(exports, {
@@ -16,8 +16,9 @@ Object.defineProperties(exports, {
1616
}
1717
})
1818

19-
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
2019
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
20+
Object.setPrototypeOf(ServiceWorkerContext.prototype, EventEmitter.prototype)
21+
Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
2122

2223
Session.prototype._init = function () {
2324
app.emit('session-created', this)

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@types/semver": "^6.0.1",
2020
"@types/send": "^0.14.5",
2121
"@types/split": "^1.0.0",
22+
"@types/uuid": "^3.4.6",
2223
"@types/webpack": "^4.4.32",
2324
"@types/webpack-env": "^1.13.9",
2425
"@typescript-eslint/eslint-plugin": "^2.6.0",
@@ -141,4 +142,4 @@
141142
"@types/multiparty": "^0.0.32",
142143
"@types/temp": "^0.8.34"
143144
}
144-
}
145+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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+
5+
#include "shell/browser/api/electron_api_service_worker_context.h"
6+
7+
#include <string>
8+
#include <utility>
9+
10+
#include "chrome/browser/browser_process.h"
11+
#include "content/public/browser/console_message.h"
12+
#include "content/public/browser/storage_partition.h"
13+
#include "gin/data_object_builder.h"
14+
#include "gin/handle.h"
15+
#include "shell/browser/electron_browser_context.h"
16+
#include "shell/common/gin_converters/value_converter.h"
17+
#include "shell/common/gin_helper/dictionary.h"
18+
#include "shell/common/gin_helper/object_template_builder.h"
19+
#include "shell/common/node_includes.h"
20+
21+
namespace electron {
22+
23+
namespace api {
24+
25+
namespace {
26+
27+
std::string MessageSourceToString(
28+
const blink::mojom::ConsoleMessageSource source) {
29+
if (source == blink::mojom::ConsoleMessageSource::kXml)
30+
return "xml";
31+
if (source == blink::mojom::ConsoleMessageSource::kJavaScript)
32+
return "javascript";
33+
if (source == blink::mojom::ConsoleMessageSource::kNetwork)
34+
return "network";
35+
if (source == blink::mojom::ConsoleMessageSource::kConsoleApi)
36+
return "console-api";
37+
if (source == blink::mojom::ConsoleMessageSource::kStorage)
38+
return "storage";
39+
if (source == blink::mojom::ConsoleMessageSource::kAppCache)
40+
return "app-cache";
41+
if (source == blink::mojom::ConsoleMessageSource::kRendering)
42+
return "rendering";
43+
if (source == blink::mojom::ConsoleMessageSource::kSecurity)
44+
return "security";
45+
if (source == blink::mojom::ConsoleMessageSource::kDeprecation)
46+
return "deprecation";
47+
if (source == blink::mojom::ConsoleMessageSource::kWorker)
48+
return "worker";
49+
if (source == blink::mojom::ConsoleMessageSource::kViolation)
50+
return "violation";
51+
if (source == blink::mojom::ConsoleMessageSource::kIntervention)
52+
return "intervention";
53+
if (source == blink::mojom::ConsoleMessageSource::kRecommendation)
54+
return "recommendation";
55+
return "other";
56+
}
57+
58+
v8::Local<v8::Value> ServiceWorkerRunningInfoToDict(
59+
v8::Isolate* isolate,
60+
const content::ServiceWorkerRunningInfo& info) {
61+
return gin::DataObjectBuilder(isolate)
62+
.Set("scriptUrl", info.script_url.spec())
63+
.Set("scope", info.scope.spec())
64+
.Set("renderProcessId", info.render_process_id)
65+
.Build();
66+
}
67+
68+
} // namespace
69+
70+
ServiceWorkerContext::ServiceWorkerContext(
71+
v8::Isolate* isolate,
72+
ElectronBrowserContext* browser_context)
73+
: browser_context_(browser_context), weak_ptr_factory_(this) {
74+
Init(isolate);
75+
service_worker_context_ =
76+
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
77+
->GetServiceWorkerContext();
78+
service_worker_context_->AddObserver(this);
79+
}
80+
81+
ServiceWorkerContext::~ServiceWorkerContext() {
82+
service_worker_context_->RemoveObserver(this);
83+
}
84+
85+
void ServiceWorkerContext::OnReportConsoleMessage(
86+
int64_t version_id,
87+
const content::ConsoleMessage& message) {
88+
Emit("console-message",
89+
gin::DataObjectBuilder(v8::Isolate::GetCurrent())
90+
.Set("versionId", version_id)
91+
.Set("source", MessageSourceToString(message.source))
92+
.Set("level", static_cast<int32_t>(message.message_level))
93+
.Set("message", message.message)
94+
.Set("lineNumber", message.line_number)
95+
.Set("sourceUrl", message.source_url.spec())
96+
.Build());
97+
}
98+
99+
void ServiceWorkerContext::OnDestruct(content::ServiceWorkerContext* context) {
100+
if (context == service_worker_context_) {
101+
delete this;
102+
}
103+
}
104+
105+
v8::Local<v8::Value> ServiceWorkerContext::GetAllRunningWorkerInfo(
106+
v8::Isolate* isolate) {
107+
gin::DataObjectBuilder builder(isolate);
108+
const base::flat_map<int64_t, content::ServiceWorkerRunningInfo>& info_map =
109+
service_worker_context_->GetRunningServiceWorkerInfos();
110+
for (auto iter = info_map.begin(); iter != info_map.end(); ++iter) {
111+
builder.Set(
112+
std::to_string(iter->first),
113+
ServiceWorkerRunningInfoToDict(isolate, std::move(iter->second)));
114+
}
115+
return builder.Build();
116+
}
117+
118+
v8::Local<v8::Value> ServiceWorkerContext::GetWorkerInfoFromID(
119+
gin_helper::ErrorThrower thrower,
120+
int64_t version_id) {
121+
const base::flat_map<int64_t, content::ServiceWorkerRunningInfo>& info_map =
122+
service_worker_context_->GetRunningServiceWorkerInfos();
123+
auto iter = info_map.find(version_id);
124+
if (iter == info_map.end()) {
125+
thrower.ThrowError("Could not find service worker with that version_id");
126+
return v8::Local<v8::Value>();
127+
}
128+
return ServiceWorkerRunningInfoToDict(thrower.isolate(),
129+
std::move(iter->second));
130+
}
131+
132+
// static
133+
gin::Handle<ServiceWorkerContext> ServiceWorkerContext::Create(
134+
v8::Isolate* isolate,
135+
ElectronBrowserContext* browser_context) {
136+
return gin::CreateHandle(isolate,
137+
new ServiceWorkerContext(isolate, browser_context));
138+
}
139+
140+
// static
141+
void ServiceWorkerContext::BuildPrototype(
142+
v8::Isolate* isolate,
143+
v8::Local<v8::FunctionTemplate> prototype) {
144+
prototype->SetClassName(gin::StringToV8(isolate, "ServiceWorkerContext"));
145+
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
146+
.SetMethod("getAllRunning",
147+
&ServiceWorkerContext::GetAllRunningWorkerInfo)
148+
.SetMethod("getFromVersionID",
149+
&ServiceWorkerContext::GetWorkerInfoFromID);
150+
}
151+
152+
} // namespace api
153+
154+
} // namespace electron
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
5+
#ifndef SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_CONTEXT_H_
6+
#define SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_CONTEXT_H_
7+
8+
#include "content/public/browser/service_worker_context.h"
9+
#include "content/public/browser/service_worker_context_observer.h"
10+
#include "gin/handle.h"
11+
#include "shell/common/gin_helper/trackable_object.h"
12+
13+
namespace electron {
14+
15+
class ElectronBrowserContext;
16+
17+
namespace api {
18+
19+
class ServiceWorkerContext
20+
: public gin_helper::TrackableObject<ServiceWorkerContext>,
21+
public content::ServiceWorkerContextObserver {
22+
public:
23+
static gin::Handle<ServiceWorkerContext> Create(
24+
v8::Isolate* isolate,
25+
ElectronBrowserContext* browser_context);
26+
27+
static void BuildPrototype(v8::Isolate* isolate,
28+
v8::Local<v8::FunctionTemplate> prototype);
29+
30+
v8::Local<v8::Value> GetAllRunningWorkerInfo(v8::Isolate* isolate);
31+
v8::Local<v8::Value> GetWorkerInfoFromID(gin_helper::ErrorThrower thrower,
32+
int64_t version_id);
33+
34+
// content::ServiceWorkerContextObserver
35+
void OnReportConsoleMessage(int64_t version_id,
36+
const content::ConsoleMessage& message) override;
37+
void OnDestruct(content::ServiceWorkerContext* context) override;
38+
39+
protected:
40+
explicit ServiceWorkerContext(v8::Isolate* isolate,
41+
ElectronBrowserContext* browser_context);
42+
~ServiceWorkerContext() override;
43+
44+
private:
45+
ElectronBrowserContext* browser_context_;
46+
47+
content::ServiceWorkerContext* service_worker_context_;
48+
49+
base::WeakPtrFactory<ServiceWorkerContext> weak_ptr_factory_;
50+
51+
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContext);
52+
};
53+
54+
} // namespace api
55+
56+
} // namespace electron
57+
58+
#endif // SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_CONTEXT_H_

0 commit comments

Comments
 (0)