Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,17 @@ added:

Use this flag to enable [ShadowRealm][] support.

### `--experimental-storage-inspection`

<!-- YAML
added:
- REPLACEME
-->

> Stability: 1.1 - Active Development

Enable experimental support for storage inspection

### `--experimental-test-coverage`

<!-- YAML
Expand Down
97 changes: 97 additions & 0 deletions doc/api/inspector.md
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,103 @@ setNetworkResources().then(() => {

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

### `inspector.DOMStorage.domStorageItemAdded`

<!-- YAML
added:
- REPLACEME
-->

* `params` {Object}
* `storageId` {Object}
* `securityOrigin` {string}
* `storageKey` {string}
* `isLocalStorage` {boolean}
* `key` {string}
* `newValue` {string}

This feature is only available with the
`--experimental-storage-inspection` flag enabled.

Broadcasts the `DOMStorage.domStorageItemAdded` event to connected frontends.
This event indicates that a new item has been added to the storage.

### `inspector.DOMStorage.domStorageItemRemoved`

<!-- YAML
added:
- REPLACEME
-->

* `params` {Object}
* `storageId` {Object}
* `securityOrigin` {string}
* `storageKey` {string}
* `isLocalStorage` {boolean}
* `key` {string}

This feature is only available with the
`--experimental-storage-inspection` flag enabled.

Broadcasts the `DOMStorage.domStorageItemRemoved` event to connected frontends.
This event indicates that an item has been removed from the storage.

### `inspector.DOMStorage.domStorageItemUpdated`

<!-- YAML
added:
- REPLACEME
-->

* `params` {Object}
* `storageId` {Object}
* `securityOrigin` {string}
* `storageKey` {string}
* `isLocalStorage` {boolean}
* `key` {string}
* `oldValue` {string}
* `newValue` {string}

This feature is only available with the
`--experimental-storage-inspection` flag enabled.

Broadcasts the `DOMStorage.domStorageItemUpdated` event to connected frontends.
This event indicates that a storage item has been updated.

### `inspector.DOMStorage.domStorageItemsCleared`

<!-- YAML
added:
- REPLACEME
-->

* `params` {Object}
* `storageId` {Object}
* `securityOrigin` {string}
* `storageKey` {string}
* `isLocalStorage` {boolean}

This feature is only available with the
`--experimental-storage-inspection` flag enabled.

Broadcasts the `DOMStorage.domStorageItemsCleared` event to connected
frontends. This event indicates that all items have been cleared from the
storage.

### `inspector.DOMStorage.registerStorage`

<!-- YAML
added:
- REPLACEME
-->

* `params` {Object}
* `isLocalStorage` {boolean}
* `storageMap` {Object}

This feature is only available with the
`--experimental-storage-inspection` flag enabled.

## Support of breakpoints

The Chrome DevTools Protocol [`Debugger` domain][] allows an
Expand Down
4 changes: 4 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ Enable the experimental QUIC support.
.
.It Fl -experimental-inspector-network-resource
Enable experimental support for inspector network resources.

.It Fl -experimental-storage-inspection
Enable experimental support for storage inspection.

.
.It Fl -force-context-aware
Disable loading native addons that are not context-aware.
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default [
// Filtering tsc output files (i.e. if there a foo.ts, we ignore foo.js):
(f, _, files) => f.endsWith('js') && files.includes(f.replace(/(\.[cm]?)js$/, '$1ts')),
),
'!test/fixtures/test-inspector-dom-storage.mjs',
'!test/fixtures/test-runner',
'test/fixtures/test-runner/*',
'!test/fixtures/test-runner/output',
Expand Down
11 changes: 11 additions & 0 deletions lib/inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@ const NetworkResources = {
put,
};

const DOMStorage = {
domStorageItemAdded: (params) => broadcastToFrontend('DOMStorage.domStorageItemAdded', params),
domStorageItemRemoved: (params) => broadcastToFrontend('DOMStorage.domStorageItemRemoved', params),
domStorageItemUpdated: (params) => broadcastToFrontend('DOMStorage.domStorageItemUpdated', params),
domStorageItemsCleared: (params) => broadcastToFrontend('DOMStorage.domStorageItemsCleared', params),
// Pseudo-event: not part of the CDP DOMStorage domain.
// Call DOMStorageAgent::registerStorage in inspector/dom_storage_agent.cc.
registerStorage: (params) => broadcastToFrontend('DOMStorage.registerStorage', params),
};

module.exports = {
open: inspectorOpen,
close: _debugEnd,
Expand All @@ -238,4 +248,5 @@ module.exports = {
Session,
Network,
NetworkResources,
DOMStorage,
};
113 changes: 113 additions & 0 deletions lib/internal/inspector/webstorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
'use strict';

const { Storage } = internalBinding('webstorage');
const { DOMStorage } = require('inspector');
const path = require('path');
const { getOptionValue } = require('internal/options');

class InspectorLocalStorage extends Storage {
setItem(key, value) {
key = `${key}`;
value = `${value}`;
const oldValue = this.getItem(key);
super.setItem(key, value);
if (oldValue == null) {
itemAdded(key, value, true);
} else {
itemUpdated(key, oldValue, value, true);
}
}

removeItem(key) {
key = `${key}`;
super.removeItem(key);
itemRemoved(key, true);
}

clear() {
super.clear();
itemsCleared(true);
}
}

const InspectorSessionStorage = class extends Storage {
setItem(key, value) {
key = `${key}`;
value = `${value}`;
const oldValue = this.getItem(key);
super.setItem(key, value);
if (oldValue == null) {
itemAdded(key, value, false);
} else {
itemUpdated(key, oldValue, value, false);
}
}

removeItem(key) {
key = `${key}`;
super.removeItem(key);
itemRemoved(key, false);
}

clear() {
super.clear();
itemsCleared(false);
}
};

function itemAdded(key, value, isLocalStorage) {
DOMStorage.domStorageItemAdded({
key,
newValue: value,
storageId: {
securityOrigin: '',
isLocalStorage,
storageKey: getStorageKey(),
},
});
}

function itemUpdated(key, oldValue, newValue, isLocalStorage) {
DOMStorage.domStorageItemUpdated({
key,
oldValue,
newValue,
storageId: {
securityOrigin: '',
isLocalStorage,
storageKey: getStorageKey(),
},
});
}

function itemRemoved(key, isLocalStorage) {
DOMStorage.domStorageItemRemoved({
key,
storageId: {
securityOrigin: '',
isLocalStorage,
storageKey: getStorageKey(),
},
});
}

function itemsCleared(isLocalStorage) {
DOMStorage.domStorageItemsCleared({
storageId: {
securityOrigin: '',
isLocalStorage,
storageKey: getStorageKey(),
},
});
}

function getStorageKey() {
const localStorageFile = getOptionValue('--localstorage-file');
const resolvedAbsolutePath = path.resolve(localStorageFile);
return 'file://' + resolvedAbsolutePath;
}

module.exports = {
InspectorLocalStorage,
InspectorSessionStorage,
};
26 changes: 24 additions & 2 deletions lib/internal/webstorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {
ObjectDefineProperties,
} = primordials;
const { ERR_INVALID_ARG_VALUE } = require('internal/errors').codes;
const { hasInspector } = internalBinding('config');
const { getOptionValue } = require('internal/options');
const { emitExperimentalWarning } = require('internal/util');
const { kConstructorKey, Storage } = internalBinding('webstorage');
Expand All @@ -15,6 +16,17 @@ module.exports = { Storage };

let lazyLocalStorage;
let lazySessionStorage;
let lazyInspectorStorage;

const experimentalStorageInspection =
hasInspector && getOptionValue('--experimental-storage-inspection');

function getInspectorStorage() {
if (lazyInspectorStorage === undefined) {
lazyInspectorStorage = require('internal/inspector/webstorage');
}
return lazyInspectorStorage;
}

ObjectDefineProperties(module.exports, {
__proto__: null,
Expand All @@ -32,7 +44,12 @@ ObjectDefineProperties(module.exports, {
'is an invalid localStorage location');
}

lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
if (experimentalStorageInspection) {
const { InspectorLocalStorage } = getInspectorStorage();
lazyLocalStorage = new InspectorLocalStorage(kConstructorKey, getValidatedPath(location), true);
} else {
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
}
}

return lazyLocalStorage;
Expand All @@ -44,7 +61,12 @@ ObjectDefineProperties(module.exports, {
enumerable: true,
get() {
if (lazySessionStorage === undefined) {
lazySessionStorage = new Storage(kConstructorKey, kInMemoryPath);
if (experimentalStorageInspection) {
const { InspectorSessionStorage } = getInspectorStorage();
lazySessionStorage = new InspectorSessionStorage(kConstructorKey, kInMemoryPath, false);
} else {
lazySessionStorage = new Storage(kConstructorKey, kInMemoryPath);
}
}

return lazySessionStorage;
Expand Down
Loading
Loading