Skip to content

Commit 611b71e

Browse files
committed
inspector: initial support storage inspection
1 parent b5c9469 commit 611b71e

22 files changed

+896
-88
lines changed

doc/api/cli.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,17 @@ added:
11221122
11231123
Enable experimental support for inspector network resources.
11241124

1125+
### `--experimental-inspector-storage-key=key`
1126+
1127+
<!-- YAML
1128+
added:
1129+
- REPLACEME
1130+
-->
1131+
1132+
> Stability: 1.1 - Active Development
1133+
1134+
Specify a inspector storage key used by the Node.js inspector when connecting to Chrome DevTools.
1135+
11251136
### `--experimental-loader=module`
11261137

11271138
<!-- YAML

doc/api/inspector.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,106 @@ setNetworkResources().then(() => {
682682

683683
For more details, see the official CDP documentation: [Network.loadNetworkResource](https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-loadNetworkResource)
684684

685+
### `inspector.DOMStorage.domStorageItemAdded`
686+
687+
<!-- YAML
688+
added:
689+
- REPLACEME
690+
-->
691+
692+
* `params` {Object}
693+
* `storageId` {Object}
694+
* `securityOrigin` {string}
695+
* `storageKey` {string}
696+
* `isLocalStorage` {boolean}
697+
* `key` {string}
698+
* `newValue` {string}
699+
700+
This feature is only available with the
701+
`--experimental-inspector-storage-key=key` flag enabled.
702+
703+
Broadcasts the `DOMStorage.domStorageItemAdded` event to connected frontends.
704+
This event indicates that a new item has been added to the storage.
705+
706+
### `inspector.DOMStorage.domStorageItemRemoved`
707+
708+
<!-- YAML
709+
added:
710+
- REPLACEME
711+
-->
712+
713+
* `params` {Object}
714+
* `storageId` {Object}
715+
* `securityOrigin` {string}
716+
* `storageKey` {string}
717+
* `isLocalStorage` {boolean}
718+
* `key` {string}
719+
720+
This feature is only available with the
721+
`--experimental-inspector-storage-key=key` flag enabled.
722+
723+
Broadcasts the `DOMStorage.domStorageItemRemoved` event to connected frontends.
724+
This event indicates that an item has been removed from the storage.
725+
726+
### `inspector.DOMStorage.domStorageItemUpdated`
727+
728+
<!-- YAML
729+
added:
730+
- REPLACEME
731+
-->
732+
733+
* `params` {Object}
734+
* `storageId` {Object}
735+
* `securityOrigin` {string}
736+
* `storageKey` {string}
737+
* `isLocalStorage` {boolean}
738+
* `key` {string}
739+
* `oldValue` {string}
740+
* `newValue` {string}
741+
742+
This feature is only available with the
743+
`--experimental-inspector-storage-key=key` flag enabled.
744+
745+
Broadcasts the `DOMStorage.domStorageItemUpdated` event to connected frontends.
746+
This event indicates that a storage item has been updated.
747+
748+
### `inspector.DOMStorage.domStorageItemsCleared`
749+
750+
<!-- YAML
751+
added:
752+
- REPLACEME
753+
-->
754+
755+
* `params` {Object}
756+
* `storageId` {Object}
757+
* `securityOrigin` {string}
758+
* `storageKey` {string}
759+
* `isLocalStorage` {boolean}
760+
761+
This feature is only available with the
762+
`--experimental-inspector-storage-key=key` flag enabled.
763+
764+
Broadcasts the `DOMStorage.domStorageItemsCleared` event to connected
765+
frontends. This event indicates that all items have been cleared from the
766+
storage.
767+
768+
### `inspector.DOMStorage.registerStorage`
769+
770+
<!-- YAML
771+
added:
772+
- REPLACEME
773+
-->
774+
775+
* `params` {Object}
776+
* `isLocalStorage` {boolean}
777+
* `storageMap` {Object}
778+
779+
This feature is only available with the
780+
`--experimental-inspector-storage-key=key` flag enabled.
781+
782+
Registers a storage map with the inspector. The registered storage entries are
783+
reported when getDOMStorageItems is called.
784+
685785
## Support of breakpoints
686786

687787
The Chrome DevTools Protocol [`Debugger` domain][] allows an

doc/node.1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ Enable the experimental QUIC support.
231231
.
232232
.It Fl -experimental-inspector-network-resource
233233
Enable experimental support for inspector network resources.
234+
235+
.It Fl -experimental-inspector-storage-key
236+
Specify a inspector storage key used by the Node.js inspector when connecting to Chrome DevTools.
237+
234238
.
235239
.It Fl -force-context-aware
236240
Disable loading native addons that are not context-aware.

lib/inspector.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,16 @@ const NetworkResources = {
229229
put,
230230
};
231231

232+
const DOMStorage = {
233+
domStorageItemAdded: (params) => broadcastToFrontend('DOMStorage.domStorageItemAdded', params),
234+
domStorageItemRemoved: (params) => broadcastToFrontend('DOMStorage.domStorageItemRemoved', params),
235+
domStorageItemUpdated: (params) => broadcastToFrontend('DOMStorage.domStorageItemUpdated', params),
236+
domStorageItemsCleared: (params) => broadcastToFrontend('DOMStorage.domStorageItemsCleared', params),
237+
// Pseudo-event: not part of the CDP DOMStorage domain.
238+
// Call DOMStorageAgent::registerStorage in inspector/dom_storage_agent.cc.
239+
registerStorage: (params) => broadcastToFrontend('DOMStorage.registerStorage', params),
240+
};
241+
232242
module.exports = {
233243
open: inspectorOpen,
234244
close: _debugEnd,
@@ -238,4 +248,5 @@ module.exports = {
238248
Session,
239249
Network,
240250
NetworkResources,
251+
DOMStorage,
241252
};

src/inspector/dom_storage_agent.cc

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#include "dom_storage_agent.h"
2+
3+
#include "crdtp/dispatch.h"
4+
#include "env-inl.h"
5+
#include "inspector/inspector_object_utils.h"
6+
#include "v8-isolate.h"
7+
8+
namespace node {
9+
namespace inspector {
10+
11+
using v8::Array;
12+
using v8::HandleScope;
13+
using v8::Local;
14+
using v8::Object;
15+
using v8::Value;
16+
17+
std::unique_ptr<protocol::DOMStorage::StorageId> createStorageIdFromObject(
18+
v8::Local<v8::Context> context, v8::Local<v8::Object> storage_id_obj) {
19+
protocol::String security_origin;
20+
if (!ObjectGetProtocolString(context, storage_id_obj, "securityOrigin")
21+
.To(&security_origin)) {
22+
return {};
23+
}
24+
bool is_local_storage =
25+
ObjectGetBool(context, storage_id_obj, "isLocalStorage").FromMaybe(false);
26+
protocol::String storageKey;
27+
if (!ObjectGetProtocolString(context, storage_id_obj, "storageKey")
28+
.To(&storageKey)) {
29+
return {};
30+
}
31+
32+
return protocol::DOMStorage::StorageId::create()
33+
.setSecurityOrigin(security_origin)
34+
.setIsLocalStorage(is_local_storage)
35+
.setStorageKey(storageKey)
36+
.build();
37+
}
38+
39+
DOMStorageAgent::DOMStorageAgent(Environment* env) : env_(env) {}
40+
41+
DOMStorageAgent::~DOMStorageAgent() {}
42+
43+
void DOMStorageAgent::Wire(protocol::UberDispatcher* dispatcher) {
44+
frontend_ =
45+
std::make_unique<protocol::DOMStorage::Frontend>(dispatcher->channel());
46+
protocol::DOMStorage::Dispatcher::wire(dispatcher, this);
47+
event_notifier_map_["domStorageItemAdded"] =
48+
(EventNotifier)(&DOMStorageAgent::domStorageItemAdded);
49+
event_notifier_map_["domStorageItemRemoved"] =
50+
(EventNotifier)(&DOMStorageAgent::domStorageItemRemoved);
51+
event_notifier_map_["domStorageItemUpdated"] =
52+
(EventNotifier)(&DOMStorageAgent::domStorageItemUpdated);
53+
event_notifier_map_["domStorageItemsCleared"] =
54+
(EventNotifier)(&DOMStorageAgent::domStorageItemsCleared);
55+
event_notifier_map_["registerStorage"] =
56+
(EventNotifier)(&DOMStorageAgent::registerStorage);
57+
}
58+
59+
protocol::DispatchResponse DOMStorageAgent::enable() {
60+
this->enabled_ = true;
61+
return protocol::DispatchResponse::Success();
62+
}
63+
64+
protocol::DispatchResponse DOMStorageAgent::disable() {
65+
this->enabled_ = false;
66+
return protocol::DispatchResponse::Success();
67+
}
68+
69+
protocol::DispatchResponse DOMStorageAgent::getDOMStorageItems(
70+
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
71+
std::unique_ptr<protocol::Array<protocol::Array<protocol::String>>>*
72+
items) {
73+
if (!enabled_) {
74+
return protocol::DispatchResponse::ServerError(
75+
"DOMStorage domain is not enabled");
76+
}
77+
bool is_local_storage = storageId->getIsLocalStorage();
78+
const std::unordered_map<std::string, std::string>& storage_map =
79+
is_local_storage ? local_storage_map_ : session_storage_map_;
80+
auto result =
81+
std::make_unique<protocol::Array<protocol::Array<protocol::String>>>();
82+
for (const auto& pair : storage_map) {
83+
auto item = std::make_unique<protocol::Array<protocol::String>>();
84+
item->push_back(pair.first);
85+
item->push_back(pair.second);
86+
result->push_back(std::move(item));
87+
}
88+
*items = std::move(result);
89+
return protocol::DispatchResponse::Success();
90+
}
91+
92+
protocol::DispatchResponse DOMStorageAgent::setDOMStorageItem(
93+
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
94+
const std::string& key,
95+
const std::string& value) {
96+
return protocol::DispatchResponse::ServerError("Not implemented");
97+
}
98+
99+
protocol::DispatchResponse DOMStorageAgent::removeDOMStorageItem(
100+
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
101+
const std::string& key) {
102+
return protocol::DispatchResponse::ServerError("Not implemented");
103+
}
104+
105+
protocol::DispatchResponse DOMStorageAgent::clear(
106+
std::unique_ptr<protocol::DOMStorage::StorageId> storageId) {
107+
return protocol::DispatchResponse::ServerError("Not implemented");
108+
}
109+
110+
void DOMStorageAgent::domStorageItemAdded(v8::Local<v8::Context> context,
111+
v8::Local<v8::Object> params) {
112+
Local<Object> storage_id_obj;
113+
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
114+
return;
115+
}
116+
117+
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
118+
createStorageIdFromObject(context, storage_id_obj);
119+
if (!storage_id) {
120+
return;
121+
}
122+
123+
protocol::String key;
124+
if (!ObjectGetProtocolString(context, params, "key").To(&key)) {
125+
return;
126+
}
127+
protocol::String new_value;
128+
if (!ObjectGetProtocolString(context, params, "newValue").To(&new_value)) {
129+
return;
130+
}
131+
frontend_->domStorageItemAdded(std::move(storage_id), key, new_value);
132+
}
133+
134+
void DOMStorageAgent::domStorageItemRemoved(v8::Local<v8::Context> context,
135+
v8::Local<v8::Object> params) {
136+
Local<Object> storage_id_obj;
137+
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
138+
return;
139+
}
140+
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
141+
createStorageIdFromObject(context, storage_id_obj);
142+
143+
if (!storage_id) {
144+
return;
145+
}
146+
147+
protocol::String key;
148+
if (!ObjectGetProtocolString(context, params, "key").To(&key)) {
149+
return;
150+
}
151+
frontend_->domStorageItemRemoved(std::move(storage_id), key);
152+
}
153+
154+
void DOMStorageAgent::domStorageItemUpdated(v8::Local<v8::Context> context,
155+
v8::Local<v8::Object> params) {
156+
Local<Object> storage_id_obj;
157+
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
158+
return;
159+
}
160+
161+
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
162+
createStorageIdFromObject(context, storage_id_obj);
163+
164+
if (!storage_id) {
165+
return;
166+
}
167+
168+
protocol::String key;
169+
if (!ObjectGetProtocolString(context, params, "key").To(&key)) {
170+
return;
171+
}
172+
protocol::String old_value;
173+
if (!ObjectGetProtocolString(context, params, "oldValue").To(&old_value)) {
174+
return;
175+
}
176+
protocol::String new_value;
177+
if (!ObjectGetProtocolString(context, params, "newValue").To(&new_value)) {
178+
return;
179+
}
180+
frontend_->domStorageItemUpdated(
181+
std::move(storage_id), key, old_value, new_value);
182+
}
183+
184+
void DOMStorageAgent::domStorageItemsCleared(v8::Local<v8::Context> context,
185+
v8::Local<v8::Object> params) {
186+
Local<Object> storage_id_obj;
187+
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
188+
return;
189+
}
190+
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
191+
createStorageIdFromObject(context, storage_id_obj);
192+
193+
if (!storage_id) {
194+
return;
195+
}
196+
frontend_->domStorageItemsCleared(std::move(storage_id));
197+
}
198+
199+
void DOMStorageAgent::registerStorage(v8::Local<v8::Context> context,
200+
v8::Local<v8::Object> params) {
201+
v8::Isolate* isolate = env_->isolate();
202+
HandleScope handle_scope(isolate);
203+
bool is_local_storage;
204+
if (!ObjectGetBool(context, params, "isLocalStorage").To(&is_local_storage)) {
205+
return;
206+
}
207+
Local<Object> storage_map_obj;
208+
if (!ObjectGetObject(context, params, "storageMap")
209+
.ToLocal(&storage_map_obj)) {
210+
return;
211+
}
212+
std::unordered_map<std::string, std::string>& storage_map =
213+
is_local_storage ? local_storage_map_ : session_storage_map_;
214+
Local<Array> property_names;
215+
if (!storage_map_obj->GetOwnPropertyNames(context).ToLocal(&property_names)) {
216+
return;
217+
}
218+
uint32_t length = property_names->Length();
219+
for (uint32_t i = 0; i < length; ++i) {
220+
Local<Value> key_value;
221+
if (!property_names->Get(context, i).ToLocal(&key_value)) {
222+
return;
223+
}
224+
Local<Value> value_value;
225+
if (!storage_map_obj->Get(context, key_value).ToLocal(&value_value)) {
226+
return;
227+
}
228+
node::Utf8Value key_utf8(isolate, key_value);
229+
node::Utf8Value value_utf8(isolate, value_value);
230+
storage_map[*key_utf8] = *value_utf8;
231+
}
232+
}
233+
234+
bool DOMStorageAgent::canEmit(const std::string& domain) {
235+
return domain == "DOMStorage";
236+
}
237+
} // namespace inspector
238+
} // namespace node

0 commit comments

Comments
 (0)