Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4f3f21b

Browse filesBrowse files
islandryuaduh95
authored andcommitted
inspector: auto collect webstorage data
PR-URL: #62145 Backport-PR-URL: #63176 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 36cc041 commit 4f3f21b
Copy full SHA for 4f3f21b

8 files changed

+340-17Lines changed: 340 additions & 17 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file
+107Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
'use strict';
2+
3+
const { Storage } = internalBinding('webstorage');
4+
const { DOMStorage } = require('inspector');
5+
const path = require('path');
6+
const { getOptionValue } = require('internal/options');
7+
8+
class InspectorLocalStorage extends Storage {
9+
setItem(key, value) {
10+
const oldValue = this.getItem(key);
11+
super.setItem(key, value);
12+
if (oldValue == null) {
13+
itemAdded(key, value, true);
14+
} else {
15+
itemUpdated(key, oldValue, value, true);
16+
}
17+
}
18+
19+
removeItem(key) {
20+
super.removeItem(key);
21+
itemRemoved(key, true);
22+
}
23+
24+
clear() {
25+
super.clear();
26+
itemsCleared(true);
27+
}
28+
}
29+
30+
const InspectorSessionStorage = class extends Storage {
31+
setItem(key, value) {
32+
const oldValue = this.getItem(key);
33+
super.setItem(key, value);
34+
if (oldValue == null) {
35+
itemAdded(key, value, false);
36+
} else {
37+
itemUpdated(key, oldValue, value, false);
38+
}
39+
}
40+
41+
removeItem(key) {
42+
super.removeItem(key);
43+
itemRemoved(key, false);
44+
}
45+
46+
clear() {
47+
super.clear();
48+
itemsCleared(false);
49+
}
50+
};
51+
52+
function itemAdded(key, value, isLocalStorage) {
53+
DOMStorage.domStorageItemAdded({
54+
key,
55+
newValue: value,
56+
storageId: {
57+
securityOrigin: '',
58+
isLocalStorage,
59+
storageKey: getStorageKey(),
60+
},
61+
});
62+
}
63+
64+
function itemUpdated(key, oldValue, newValue, isLocalStorage) {
65+
DOMStorage.domStorageItemUpdated({
66+
key,
67+
oldValue,
68+
newValue,
69+
storageId: {
70+
securityOrigin: '',
71+
isLocalStorage,
72+
storageKey: getStorageKey(),
73+
},
74+
});
75+
}
76+
77+
function itemRemoved(key, isLocalStorage) {
78+
DOMStorage.domStorageItemRemoved({
79+
key,
80+
storageId: {
81+
securityOrigin: '',
82+
isLocalStorage,
83+
storageKey: getStorageKey(),
84+
},
85+
});
86+
}
87+
88+
function itemsCleared(isLocalStorage) {
89+
DOMStorage.domStorageItemsCleared({
90+
storageId: {
91+
securityOrigin: '',
92+
isLocalStorage,
93+
storageKey: getStorageKey(),
94+
},
95+
});
96+
}
97+
98+
function getStorageKey() {
99+
const localStorageFile = getOptionValue('--localstorage-file');
100+
const resolvedAbsolutePath = path.resolve(localStorageFile);
101+
return 'file://' + resolvedAbsolutePath;
102+
}
103+
104+
module.exports = {
105+
InspectorLocalStorage,
106+
InspectorSessionStorage,
107+
};
Collapse file

‎lib/internal/webstorage.js‎

Copy file name to clipboardExpand all lines: lib/internal/webstorage.js
+24-2Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const {
33
ObjectDefineProperties,
44
} = primordials;
55
const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes;
6+
const { hasInspector } = internalBinding('config');
67
const { getOptionValue } = require('internal/options');
78
const { emitExperimentalWarning } = require('internal/util');
89
const { kConstructorKey, Storage } = internalBinding('webstorage');
@@ -15,6 +16,17 @@ module.exports = { Storage };
1516

1617
let lazyLocalStorage;
1718
let lazySessionStorage;
19+
let lazyInspectorStorage;
20+
21+
const experimentalStorageInspection =
22+
hasInspector && getOptionValue('--experimental-storage-inspection');
23+
24+
function getInspectorStorage() {
25+
if (lazyInspectorStorage === undefined) {
26+
lazyInspectorStorage = require('internal/inspector/webstorage');
27+
}
28+
return lazyInspectorStorage;
29+
}
1830

1931
ObjectDefineProperties(module.exports, {
2032
__proto__: null,
@@ -32,7 +44,12 @@ ObjectDefineProperties(module.exports, {
3244
'is an invalid localStorage location');
3345
}
3446

35-
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
47+
if (experimentalStorageInspection) {
48+
const { InspectorLocalStorage } = getInspectorStorage();
49+
lazyLocalStorage = new InspectorLocalStorage(kConstructorKey, getValidatedPath(location), true);
50+
} else {
51+
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
52+
}
3653
}
3754

3855
return lazyLocalStorage;
@@ -44,7 +61,12 @@ ObjectDefineProperties(module.exports, {
4461
enumerable: true,
4562
get() {
4663
if (lazySessionStorage === undefined) {
47-
lazySessionStorage = new Storage(kConstructorKey, kInMemoryPath);
64+
if (experimentalStorageInspection) {
65+
const { InspectorSessionStorage } = getInspectorStorage();
66+
lazySessionStorage = new InspectorSessionStorage(kConstructorKey, kInMemoryPath, false);
67+
} else {
68+
lazySessionStorage = new Storage(kConstructorKey, kInMemoryPath);
69+
}
4870
}
4971

5072
return lazySessionStorage;
Collapse file

‎src/inspector/dom_storage_agent.cc‎

Copy file name to clipboardExpand all lines: src/inspector/dom_storage_agent.cc
+48-9Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#include "dom_storage_agent.h"
2+
#include <optional>
23
#include "env-inl.h"
34
#include "inspector/inspector_object_utils.h"
5+
#include "util.h"
6+
#include "v8-exception.h"
47
#include "v8-isolate.h"
58

69
namespace node {
@@ -85,14 +88,27 @@ protocol::DispatchResponse DOMStorageAgent::getDOMStorageItems(
8588
"DOMStorage domain is not enabled");
8689
}
8790
bool is_local_storage = storageId->getIsLocalStorage();
88-
const std::unordered_map<std::string, std::string>& storage_map =
89-
is_local_storage ? local_storage_map_ : session_storage_map_;
91+
const StorageMap* storage_map =
92+
is_local_storage ? &local_storage_map_ : &session_storage_map_;
93+
std::optional<StorageMap> storage_map_fallback;
94+
if (storage_map->empty()) {
95+
auto web_storage_obj = getWebStorage(is_local_storage);
96+
if (web_storage_obj) {
97+
storage_map_fallback = web_storage_obj.value()->GetAll();
98+
storage_map = &storage_map_fallback.value();
99+
}
100+
}
101+
90102
auto result =
91103
std::make_unique<protocol::Array<protocol::Array<protocol::String>>>();
92-
for (const auto& pair : storage_map) {
104+
for (const auto& pair : *storage_map) {
93105
auto item = std::make_unique<protocol::Array<protocol::String>>();
94-
item->push_back(pair.first);
95-
item->push_back(pair.second);
106+
item->push_back(protocol::StringUtil::fromUTF16(
107+
reinterpret_cast<const uint16_t*>(pair.first.data()),
108+
pair.first.size()));
109+
item->push_back(protocol::StringUtil::fromUTF16(
110+
reinterpret_cast<const uint16_t*>(pair.second.data()),
111+
pair.second.size()));
96112
result->push_back(std::move(item));
97113
}
98114
*items = std::move(result);
@@ -219,7 +235,7 @@ void DOMStorageAgent::registerStorage(Local<Context> context,
219235
.ToLocal(&storage_map_obj)) {
220236
return;
221237
}
222-
std::unordered_map<std::string, std::string>& storage_map =
238+
StorageMap& storage_map =
223239
is_local_storage ? local_storage_map_ : session_storage_map_;
224240
Local<Array> property_names;
225241
if (!storage_map_obj->GetOwnPropertyNames(context).ToLocal(&property_names)) {
@@ -235,9 +251,32 @@ void DOMStorageAgent::registerStorage(Local<Context> context,
235251
if (!storage_map_obj->Get(context, key_value).ToLocal(&value_value)) {
236252
return;
237253
}
238-
node::Utf8Value key_utf8(isolate, key_value);
239-
node::Utf8Value value_utf8(isolate, value_value);
240-
storage_map[*key_utf8] = *value_utf8;
254+
node::TwoByteValue key_utf16(isolate, key_value);
255+
node::TwoByteValue value_utf16(isolate, value_value);
256+
storage_map[key_utf16.ToU16String()] = value_utf16.ToU16String();
257+
}
258+
}
259+
260+
std::optional<node::webstorage::Storage*> DOMStorageAgent::getWebStorage(
261+
bool is_local_storage) {
262+
v8::Isolate* isolate = env_->isolate();
263+
v8::HandleScope handle_scope(isolate);
264+
v8::Local<v8::Object> global = env_->context()->Global();
265+
v8::Local<v8::Value> web_storage_val;
266+
v8::TryCatch try_catch(isolate);
267+
if (!global
268+
->Get(env_->context(),
269+
is_local_storage
270+
? FIXED_ONE_BYTE_STRING(isolate, "localStorage")
271+
: FIXED_ONE_BYTE_STRING(isolate, "sessionStorage"))
272+
.ToLocal(&web_storage_val) ||
273+
!web_storage_val->IsObject() || try_catch.HasCaught()) {
274+
return std::nullopt;
275+
} else {
276+
node::webstorage::Storage* storage;
277+
ASSIGN_OR_RETURN_UNWRAP(
278+
&storage, web_storage_val.As<v8::Object>(), std::nullopt);
279+
return storage;
241280
}
242281
}
243282

Collapse file

‎src/inspector/dom_storage_agent.h‎

Copy file name to clipboardExpand all lines: src/inspector/dom_storage_agent.h
+7-2Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#ifndef SRC_INSPECTOR_DOM_STORAGE_AGENT_H_
22
#define SRC_INSPECTOR_DOM_STORAGE_AGENT_H_
33

4+
#include <optional>
45
#include <string>
56
#include "env.h"
67
#include "node/inspector/protocol/DOMStorage.h"
8+
#include "node_webstorage.h"
79
#include "notification_emitter.h"
810
#include "v8.h"
911

@@ -50,9 +52,12 @@ class DOMStorageAgent : public protocol::DOMStorage::Backend,
5052
DOMStorageAgent& operator=(const DOMStorageAgent&) = delete;
5153

5254
private:
55+
typedef std::unordered_map<std::u16string, std::u16string> StorageMap;
56+
std::optional<node::webstorage::Storage*> getWebStorage(
57+
bool is_local_storage);
5358
std::unique_ptr<protocol::DOMStorage::Frontend> frontend_;
54-
std::unordered_map<std::string, std::string> local_storage_map_ = {};
55-
std::unordered_map<std::string, std::string> session_storage_map_ = {};
59+
StorageMap local_storage_map_ = {};
60+
StorageMap session_storage_map_ = {};
5661
bool enabled_ = false;
5762
Environment* env_;
5863
};
Collapse file

‎src/node_builtins.cc‎

Copy file name to clipboardExpand all lines: src/node_builtins.cc
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ BuiltinLoader::BuiltinCategories BuiltinLoader::GetBuiltinCategories() const {
122122
"internal/inspector/network", "internal/inspector/network_http",
123123
"internal/inspector/network_http2", "internal/inspector/network_undici",
124124
"internal/inspector_async_hook", "internal/inspector_network_tracking",
125+
"internal/inspector/webstorage",
125126
#endif // !HAVE_INSPECTOR
126127

127128
#if !NODE_USE_V8_PLATFORM || !defined(NODE_HAVE_I18N_SUPPORT)
@@ -145,6 +146,7 @@ BuiltinLoader::BuiltinCategories BuiltinLoader::GetBuiltinCategories() const {
145146
"wasi", // Experimental.
146147
#if !HAVE_SQLITE
147148
"internal/webstorage", // Experimental.
149+
"internal/inspector/webstorage",
148150
#endif
149151
"internal/test/binding", "internal/v8_prof_polyfill",
150152
};
Collapse file

‎src/node_webstorage.cc‎

Copy file name to clipboardExpand all lines: src/node_webstorage.cc
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "node_webstorage.h"
2+
#include <string>
3+
#include <unordered_map>
24
#include "base_object-inl.h"
35
#include "debug_utils-inl.h"
46
#include "env-inl.h"
@@ -7,6 +9,7 @@
79
#include "node_errors.h"
810
#include "node_mem-inl.h"
911
#include "path.h"
12+
#include "simdutf.h"
1013
#include "sqlite3.h"
1114
#include "util-inl.h"
1215

@@ -278,6 +281,35 @@ MaybeLocal<Array> Storage::Enumerate() {
278281
return Array::New(env()->isolate(), values.data(), values.size());
279282
}
280283

284+
std::unordered_map<std::u16string, std::u16string> Storage::GetAll() {
285+
if (!Open().IsJust()) {
286+
return {};
287+
}
288+
289+
static constexpr std::string_view sql =
290+
"SELECT key, value FROM nodejs_webstorage";
291+
sqlite3_stmt* s = nullptr;
292+
int r = sqlite3_prepare_v2(db_.get(), sql.data(), sql.size(), &s, nullptr);
293+
auto stmt = stmt_unique_ptr(s);
294+
std::unordered_map<std::u16string, std::u16string> result;
295+
while ((r = sqlite3_step(stmt.get())) == SQLITE_ROW) {
296+
CHECK(sqlite3_column_type(stmt.get(), 0) == SQLITE_BLOB);
297+
CHECK(sqlite3_column_type(stmt.get(), 1) == SQLITE_BLOB);
298+
auto key_size = sqlite3_column_bytes(stmt.get(), 0) / sizeof(uint16_t);
299+
auto value_size = sqlite3_column_bytes(stmt.get(), 1) / sizeof(uint16_t);
300+
auto key_uint16(
301+
reinterpret_cast<const char16_t*>(sqlite3_column_blob(stmt.get(), 0)));
302+
auto value_uint16(
303+
reinterpret_cast<const char16_t*>(sqlite3_column_blob(stmt.get(), 1)));
304+
305+
std::u16string key(key_uint16, key_size);
306+
std::u16string value(value_uint16, value_size);
307+
308+
result.emplace(std::move(key), std::move(value));
309+
}
310+
return result;
311+
}
312+
281313
MaybeLocal<Value> Storage::Length() {
282314
if (!Open().IsJust()) {
283315
return {};
Collapse file

‎src/node_webstorage.h‎

Copy file name to clipboardExpand all lines: src/node_webstorage.h
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6+
#include <unordered_map>
67
#include "base_object.h"
78
#include "node_mem.h"
89
#include "sqlite3.h"
@@ -40,6 +41,7 @@ class Storage : public BaseObject {
4041
v8::MaybeLocal<v8::Value> LoadKey(const int index);
4142
v8::Maybe<void> Remove(v8::Local<v8::Name> key);
4243
v8::Maybe<void> Store(v8::Local<v8::Name> key, v8::Local<v8::Value> value);
44+
std::unordered_map<std::u16string, std::u16string> GetAll();
4345

4446
SET_MEMORY_INFO_NAME(Storage)
4547
SET_SELF_SIZE(Storage)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.