mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
inspector: initial support storage inspection
PR-URL: https://github.com/nodejs/node/pull/61139 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Kohei Ueno <kohei.ueno119@gmail.com>
This commit is contained in:
1 parent
5d39030b2e
commit
ec564135f4
22 files changed
+912
-88
No files matched your search
@@ -1270,6 +1270,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
|
||||
|
||||
@@ -682,6 +682,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
|
||||
|
||||
@@ -237,6 +237,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.
|
||||
|
||||
@@ -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,
|
||||
@@ -238,4 +248,5 @@ module.exports = {
|
||||
Session,
|
||||
Network,
|
||||
NetworkResources,
|
||||
DOMStorage,
|
||||
};
|
||||
@@ -0,0 +1,248 @@
|
||||
#include "dom_storage_agent.h"
|
||||
#include "env-inl.h"
|
||||
#include "inspector/inspector_object_utils.h"
|
||||
#include "v8-isolate.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
using v8::Array;
|
||||
using v8::Context;
|
||||
using v8::HandleScope;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Object;
|
||||
using v8::Value;
|
||||
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> createStorageIdFromObject(
|
||||
Local<Context> context, Local<Object> storage_id_obj) {
|
||||
protocol::String security_origin;
|
||||
if (!ObjectGetProtocolString(context, storage_id_obj, "securityOrigin")
|
||||
.To(&security_origin)) {
|
||||
return {};
|
||||
}
|
||||
bool is_local_storage =
|
||||
ObjectGetBool(context, storage_id_obj, "isLocalStorage").FromMaybe(false);
|
||||
protocol::String storageKey;
|
||||
if (!ObjectGetProtocolString(context, storage_id_obj, "storageKey")
|
||||
.To(&storageKey)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return protocol::DOMStorage::StorageId::create()
|
||||
.setSecurityOrigin(security_origin)
|
||||
.setIsLocalStorage(is_local_storage)
|
||||
.setStorageKey(storageKey)
|
||||
.build();
|
||||
}
|
||||
|
||||
DOMStorageAgent::DOMStorageAgent(Environment* env) : env_(env) {}
|
||||
|
||||
DOMStorageAgent::~DOMStorageAgent() {}
|
||||
|
||||
void DOMStorageAgent::Wire(protocol::UberDispatcher* dispatcher) {
|
||||
frontend_ =
|
||||
std::make_unique<protocol::DOMStorage::Frontend>(dispatcher->channel());
|
||||
protocol::DOMStorage::Dispatcher::wire(dispatcher, this);
|
||||
addEventNotifier("domStorageItemAdded",
|
||||
[this](v8::Local<v8::Context> ctx, v8::Local<v8::Object> p) {
|
||||
this->domStorageItemAdded(ctx, p);
|
||||
});
|
||||
addEventNotifier("domStorageItemRemoved",
|
||||
[this](v8::Local<v8::Context> ctx, v8::Local<v8::Object> p) {
|
||||
this->domStorageItemRemoved(ctx, p);
|
||||
});
|
||||
addEventNotifier("domStorageItemUpdated",
|
||||
[this](v8::Local<v8::Context> ctx, v8::Local<v8::Object> p) {
|
||||
this->domStorageItemUpdated(ctx, p);
|
||||
});
|
||||
addEventNotifier("domStorageItemsCleared",
|
||||
[this](v8::Local<v8::Context> ctx, v8::Local<v8::Object> p) {
|
||||
this->domStorageItemsCleared(ctx, p);
|
||||
});
|
||||
addEventNotifier("registerStorage",
|
||||
[this](v8::Local<v8::Context> ctx, v8::Local<v8::Object> p) {
|
||||
this->registerStorage(ctx, p);
|
||||
});
|
||||
}
|
||||
|
||||
protocol::DispatchResponse DOMStorageAgent::enable() {
|
||||
this->enabled_ = true;
|
||||
return protocol::DispatchResponse::Success();
|
||||
}
|
||||
|
||||
protocol::DispatchResponse DOMStorageAgent::disable() {
|
||||
this->enabled_ = false;
|
||||
return protocol::DispatchResponse::Success();
|
||||
}
|
||||
|
||||
protocol::DispatchResponse DOMStorageAgent::getDOMStorageItems(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
|
||||
std::unique_ptr<protocol::Array<protocol::Array<protocol::String>>>*
|
||||
items) {
|
||||
if (!enabled_) {
|
||||
return protocol::DispatchResponse::ServerError(
|
||||
"DOMStorage domain is not enabled");
|
||||
}
|
||||
bool is_local_storage = storageId->getIsLocalStorage();
|
||||
const std::unordered_map<std::string, std::string>& storage_map =
|
||||
is_local_storage ? local_storage_map_ : session_storage_map_;
|
||||
auto result =
|
||||
std::make_unique<protocol::Array<protocol::Array<protocol::String>>>();
|
||||
for (const auto& pair : storage_map) {
|
||||
auto item = std::make_unique<protocol::Array<protocol::String>>();
|
||||
item->push_back(pair.first);
|
||||
item->push_back(pair.second);
|
||||
result->push_back(std::move(item));
|
||||
}
|
||||
*items = std::move(result);
|
||||
return protocol::DispatchResponse::Success();
|
||||
}
|
||||
|
||||
protocol::DispatchResponse DOMStorageAgent::setDOMStorageItem(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
|
||||
const std::string& key,
|
||||
const std::string& value) {
|
||||
return protocol::DispatchResponse::ServerError("Not implemented");
|
||||
}
|
||||
|
||||
protocol::DispatchResponse DOMStorageAgent::removeDOMStorageItem(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
|
||||
const std::string& key) {
|
||||
return protocol::DispatchResponse::ServerError("Not implemented");
|
||||
}
|
||||
|
||||
protocol::DispatchResponse DOMStorageAgent::clear(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId) {
|
||||
return protocol::DispatchResponse::ServerError("Not implemented");
|
||||
}
|
||||
|
||||
void DOMStorageAgent::domStorageItemAdded(Local<Context> context,
|
||||
Local<Object> params) {
|
||||
Local<Object> storage_id_obj;
|
||||
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
|
||||
createStorageIdFromObject(context, storage_id_obj);
|
||||
if (!storage_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
protocol::String key;
|
||||
if (!ObjectGetProtocolString(context, params, "key").To(&key)) {
|
||||
return;
|
||||
}
|
||||
protocol::String new_value;
|
||||
if (!ObjectGetProtocolString(context, params, "newValue").To(&new_value)) {
|
||||
return;
|
||||
}
|
||||
frontend_->domStorageItemAdded(std::move(storage_id), key, new_value);
|
||||
}
|
||||
|
||||
void DOMStorageAgent::domStorageItemRemoved(Local<Context> context,
|
||||
Local<Object> params) {
|
||||
Local<Object> storage_id_obj;
|
||||
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
|
||||
createStorageIdFromObject(context, storage_id_obj);
|
||||
|
||||
if (!storage_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
protocol::String key;
|
||||
if (!ObjectGetProtocolString(context, params, "key").To(&key)) {
|
||||
return;
|
||||
}
|
||||
frontend_->domStorageItemRemoved(std::move(storage_id), key);
|
||||
}
|
||||
|
||||
void DOMStorageAgent::domStorageItemUpdated(Local<Context> context,
|
||||
Local<Object> params) {
|
||||
Local<Object> storage_id_obj;
|
||||
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
|
||||
createStorageIdFromObject(context, storage_id_obj);
|
||||
|
||||
if (!storage_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
protocol::String key;
|
||||
if (!ObjectGetProtocolString(context, params, "key").To(&key)) {
|
||||
return;
|
||||
}
|
||||
protocol::String old_value;
|
||||
if (!ObjectGetProtocolString(context, params, "oldValue").To(&old_value)) {
|
||||
return;
|
||||
}
|
||||
protocol::String new_value;
|
||||
if (!ObjectGetProtocolString(context, params, "newValue").To(&new_value)) {
|
||||
return;
|
||||
}
|
||||
frontend_->domStorageItemUpdated(
|
||||
std::move(storage_id), key, old_value, new_value);
|
||||
}
|
||||
|
||||
void DOMStorageAgent::domStorageItemsCleared(Local<Context> context,
|
||||
Local<Object> params) {
|
||||
Local<Object> storage_id_obj;
|
||||
if (!ObjectGetObject(context, params, "storageId").ToLocal(&storage_id_obj)) {
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storage_id =
|
||||
createStorageIdFromObject(context, storage_id_obj);
|
||||
|
||||
if (!storage_id) {
|
||||
return;
|
||||
}
|
||||
frontend_->domStorageItemsCleared(std::move(storage_id));
|
||||
}
|
||||
|
||||
void DOMStorageAgent::registerStorage(Local<Context> context,
|
||||
Local<Object> params) {
|
||||
Isolate* isolate = env_->isolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
bool is_local_storage;
|
||||
if (!ObjectGetBool(context, params, "isLocalStorage").To(&is_local_storage)) {
|
||||
return;
|
||||
}
|
||||
Local<Object> storage_map_obj;
|
||||
if (!ObjectGetObject(context, params, "storageMap")
|
||||
.ToLocal(&storage_map_obj)) {
|
||||
return;
|
||||
}
|
||||
std::unordered_map<std::string, std::string>& storage_map =
|
||||
is_local_storage ? local_storage_map_ : session_storage_map_;
|
||||
Local<Array> property_names;
|
||||
if (!storage_map_obj->GetOwnPropertyNames(context).ToLocal(&property_names)) {
|
||||
return;
|
||||
}
|
||||
uint32_t length = property_names->Length();
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
Local<Value> key_value;
|
||||
if (!property_names->Get(context, i).ToLocal(&key_value)) {
|
||||
return;
|
||||
}
|
||||
Local<Value> value_value;
|
||||
if (!storage_map_obj->Get(context, key_value).ToLocal(&value_value)) {
|
||||
return;
|
||||
}
|
||||
node::Utf8Value key_utf8(isolate, key_value);
|
||||
node::Utf8Value value_utf8(isolate, value_value);
|
||||
storage_map[*key_utf8] = *value_utf8;
|
||||
}
|
||||
}
|
||||
|
||||
bool DOMStorageAgent::canEmit(const std::string& domain) {
|
||||
return domain == "DOMStorage";
|
||||
}
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef SRC_INSPECTOR_DOM_STORAGE_AGENT_H_
|
||||
#define SRC_INSPECTOR_DOM_STORAGE_AGENT_H_
|
||||
|
||||
#include <string>
|
||||
#include "env.h"
|
||||
#include "node/inspector/protocol/DOMStorage.h"
|
||||
#include "notification_emitter.h"
|
||||
#include "v8.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
class DOMStorageAgent : public protocol::DOMStorage::Backend,
|
||||
public NotificationEmitter {
|
||||
public:
|
||||
explicit DOMStorageAgent(Environment* env);
|
||||
~DOMStorageAgent() override;
|
||||
|
||||
void Wire(protocol::UberDispatcher* dispatcher);
|
||||
|
||||
protocol::DispatchResponse enable() override;
|
||||
protocol::DispatchResponse disable() override;
|
||||
protocol::DispatchResponse getDOMStorageItems(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
|
||||
std::unique_ptr<protocol::Array<protocol::Array<protocol::String>>>*
|
||||
items) override;
|
||||
protocol::DispatchResponse setDOMStorageItem(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
|
||||
const std::string& key,
|
||||
const std::string& value) override;
|
||||
protocol::DispatchResponse removeDOMStorageItem(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId,
|
||||
const std::string& key) override;
|
||||
protocol::DispatchResponse clear(
|
||||
std::unique_ptr<protocol::DOMStorage::StorageId> storageId) override;
|
||||
|
||||
void domStorageItemAdded(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> params);
|
||||
void domStorageItemRemoved(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> params);
|
||||
void domStorageItemUpdated(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> params);
|
||||
void domStorageItemsCleared(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> params);
|
||||
void registerStorage(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> params);
|
||||
bool canEmit(const std::string& domain) override;
|
||||
|
||||
DOMStorageAgent(const DOMStorageAgent&) = delete;
|
||||
DOMStorageAgent& operator=(const DOMStorageAgent&) = delete;
|
||||
|
||||
private:
|
||||
std::unique_ptr<protocol::DOMStorage::Frontend> frontend_;
|
||||
std::unordered_map<std::string, std::string> local_storage_map_ = {};
|
||||
std::unordered_map<std::string, std::string> session_storage_map_ = {};
|
||||
bool enabled_ = false;
|
||||
Environment* env_;
|
||||
};
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // SRC_INSPECTOR_DOM_STORAGE_AGENT_H_
|
||||
@@ -0,0 +1,67 @@
|
||||
experimental domain DOMStorage
|
||||
|
||||
type SerializedStorageKey extends string
|
||||
|
||||
# DOM Storage identifier.
|
||||
type StorageId extends object
|
||||
properties
|
||||
# Security origin for the storage.
|
||||
optional string securityOrigin
|
||||
# Represents a key by which DOM Storage keys its CachedStorageAreas
|
||||
optional SerializedStorageKey storageKey
|
||||
# Whether the storage is local storage (not session storage).
|
||||
boolean isLocalStorage
|
||||
|
||||
# DOM Storage item.
|
||||
type Item extends array of string
|
||||
|
||||
command clear
|
||||
parameters
|
||||
StorageId storageId
|
||||
|
||||
# Disables storage tracking, prevents storage events from being sent to the client.
|
||||
command disable
|
||||
|
||||
# Enables storage tracking, storage events will now be delivered to the client.
|
||||
command enable
|
||||
|
||||
command getDOMStorageItems
|
||||
parameters
|
||||
StorageId storageId
|
||||
returns
|
||||
array of Item entries
|
||||
|
||||
command removeDOMStorageItem
|
||||
parameters
|
||||
StorageId storageId
|
||||
string key
|
||||
|
||||
command setDOMStorageItem
|
||||
parameters
|
||||
StorageId storageId
|
||||
string key
|
||||
string value
|
||||
|
||||
event domStorageItemAdded
|
||||
parameters
|
||||
StorageId storageId
|
||||
string key
|
||||
string newValue
|
||||
|
||||
event domStorageItemRemoved
|
||||
parameters
|
||||
StorageId storageId
|
||||
string key
|
||||
|
||||
event domStorageItemUpdated
|
||||
parameters
|
||||
StorageId storageId
|
||||
string key
|
||||
string oldValue
|
||||
string newValue
|
||||
|
||||
event domStorageItemsCleared
|
||||
parameters
|
||||
StorageId storageId
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
experimental domain Storage
|
||||
type SerializedStorageKey extends string
|
||||
experimental command getStorageKey
|
||||
parameters
|
||||
optional string frameId
|
||||
returns
|
||||
SerializedStorageKey storageKey
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "inspector_object_utils.h"
|
||||
#include "inspector/protocol_helper.h"
|
||||
#include "util-inl.h"
|
||||
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::HandleScope;
|
||||
using v8::Int32;
|
||||
using v8::Isolate;
|
||||
using v8::Just;
|
||||
using v8::Local;
|
||||
using v8::Maybe;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Nothing;
|
||||
using v8::Number;
|
||||
using v8::Object;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
// Get a protocol string property from the object.
|
||||
Maybe<protocol::String> ObjectGetProtocolString(Local<Context> context,
|
||||
Local<Object> object,
|
||||
Local<String> property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, property).ToLocal(&value) || !value->IsString()) {
|
||||
return Nothing<protocol::String>();
|
||||
}
|
||||
Local<String> str = value.As<String>();
|
||||
return Just(ToProtocolString(Isolate::GetCurrent(), str));
|
||||
}
|
||||
|
||||
// Get a protocol string property from the object.
|
||||
Maybe<protocol::String> ObjectGetProtocolString(Local<Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
return ObjectGetProtocolString(
|
||||
context, object, OneByteString(Isolate::GetCurrent(), property));
|
||||
}
|
||||
|
||||
// Get a protocol double property from the object.
|
||||
Maybe<double> ObjectGetDouble(Local<Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsNumber()) {
|
||||
return Nothing<double>();
|
||||
}
|
||||
return Just(value.As<Number>()->Value());
|
||||
}
|
||||
|
||||
// Get a protocol int property from the object.
|
||||
Maybe<int> ObjectGetInt(Local<Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsInt32()) {
|
||||
return Nothing<int>();
|
||||
}
|
||||
return Just(value.As<Int32>()->Value());
|
||||
}
|
||||
|
||||
// Get a protocol bool property from the object.
|
||||
Maybe<bool> ObjectGetBool(Local<Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsBoolean()) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
return Just(value.As<Boolean>()->Value());
|
||||
}
|
||||
|
||||
// Get an object property from the object.
|
||||
MaybeLocal<Object> ObjectGetObject(Local<Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
EscapableHandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsObject()) {
|
||||
return {};
|
||||
}
|
||||
return handle_scope.Escape(value.As<Object>());
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef SRC_INSPECTOR_INSPECTOR_OBJECT_UTILS_H_
|
||||
#define SRC_INSPECTOR_INSPECTOR_OBJECT_UTILS_H_
|
||||
|
||||
#include <v8.h>
|
||||
#include "node/inspector/protocol/Protocol.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
v8::Maybe<protocol::String> ObjectGetProtocolString(
|
||||
v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> object,
|
||||
v8::Local<v8::String> property);
|
||||
|
||||
v8::Maybe<protocol::String> ObjectGetProtocolString(
|
||||
v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> object,
|
||||
const char* property);
|
||||
|
||||
v8::Maybe<double> ObjectGetDouble(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> object,
|
||||
const char* property);
|
||||
|
||||
v8::Maybe<int> ObjectGetInt(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> object,
|
||||
const char* property);
|
||||
|
||||
v8::Maybe<bool> ObjectGetBool(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> object,
|
||||
const char* property);
|
||||
|
||||
v8::MaybeLocal<v8::Object> ObjectGetObject(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Object> object,
|
||||
const char* property);
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
#endif // SRC_INSPECTOR_INSPECTOR_OBJECT_UTILS_H_
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <string>
|
||||
#include "debug_utils-inl.h"
|
||||
#include "env-inl.h"
|
||||
#include "inspector/inspector_object_utils.h"
|
||||
#include "inspector/network_resource_manager.h"
|
||||
#include "inspector/protocol_helper.h"
|
||||
#include "network_inspector.h"
|
||||
@@ -14,98 +15,15 @@
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::HandleScope;
|
||||
using v8::Isolate;
|
||||
using v8::Just;
|
||||
using v8::Local;
|
||||
using v8::Maybe;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Nothing;
|
||||
using v8::Object;
|
||||
using v8::Uint8Array;
|
||||
using v8::Value;
|
||||
|
||||
constexpr size_t kDefaultMaxTotalBufferSize = 100 * 1024 * 1024; // 100MB
|
||||
|
||||
// Get a protocol string property from the object.
|
||||
Maybe<protocol::String> ObjectGetProtocolString(v8::Local<v8::Context> context,
|
||||
Local<Object> object,
|
||||
Local<v8::String> property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, property).ToLocal(&value) || !value->IsString()) {
|
||||
return Nothing<protocol::String>();
|
||||
}
|
||||
Local<v8::String> str = value.As<v8::String>();
|
||||
return Just(ToProtocolString(Isolate::GetCurrent(), str));
|
||||
}
|
||||
|
||||
// Get a protocol string property from the object.
|
||||
Maybe<protocol::String> ObjectGetProtocolString(v8::Local<v8::Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
return ObjectGetProtocolString(
|
||||
context, object, OneByteString(Isolate::GetCurrent(), property));
|
||||
}
|
||||
|
||||
// Get a protocol double property from the object.
|
||||
Maybe<double> ObjectGetDouble(v8::Local<v8::Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsNumber()) {
|
||||
return Nothing<double>();
|
||||
}
|
||||
return Just(value.As<v8::Number>()->Value());
|
||||
}
|
||||
|
||||
// Get a protocol int property from the object.
|
||||
Maybe<int> ObjectGetInt(v8::Local<v8::Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsInt32()) {
|
||||
return Nothing<int>();
|
||||
}
|
||||
return Just(value.As<v8::Int32>()->Value());
|
||||
}
|
||||
|
||||
// Get a protocol bool property from the object.
|
||||
Maybe<bool> ObjectGetBool(v8::Local<v8::Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
HandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsBoolean()) {
|
||||
return Nothing<bool>();
|
||||
}
|
||||
return Just(value.As<v8::Boolean>()->Value());
|
||||
}
|
||||
|
||||
// Get an object property from the object.
|
||||
MaybeLocal<v8::Object> ObjectGetObject(v8::Local<v8::Context> context,
|
||||
Local<Object> object,
|
||||
const char* property) {
|
||||
EscapableHandleScope handle_scope(Isolate::GetCurrent());
|
||||
Local<Value> value;
|
||||
if (!object->Get(context, OneByteString(Isolate::GetCurrent(), property))
|
||||
.ToLocal(&value) ||
|
||||
!value->IsObject()) {
|
||||
return {};
|
||||
}
|
||||
return handle_scope.Escape(value.As<v8::Object>());
|
||||
}
|
||||
|
||||
// Create a protocol::Network::Headers from the v8 object.
|
||||
std::unique_ptr<protocol::Network::Headers> createHeadersFromObject(
|
||||
v8::Local<v8::Context> context, Local<Object> headers_obj) {
|
||||
|
||||
@@ -40,6 +40,14 @@
|
||||
'src/inspector/io_agent.h',
|
||||
'src/inspector/network_resource_manager.cc',
|
||||
'src/inspector/network_resource_manager.h',
|
||||
'src/inspector/dom_storage_agent.cc',
|
||||
'src/inspector/dom_storage_agent.h',
|
||||
'src/inspector/inspector_object_utils.cc',
|
||||
'src/inspector/inspector_object_utils.h',
|
||||
'src/inspector/storage_agent.h',
|
||||
'src/inspector/storage_agent.cc',
|
||||
'src/inspector/notification_emitter.h',
|
||||
'src/inspector/notification_emitter.cc',
|
||||
],
|
||||
'node_inspector_generated_sources': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Forward.h',
|
||||
@@ -57,6 +65,10 @@
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Target.h',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/IO.h',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/IO.cpp',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/DOMStorage.h',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/DOMStorage.cpp',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Storage.cpp',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Storage.h',
|
||||
],
|
||||
'node_protocol_files': [
|
||||
'<(protocol_tool_path)/lib/Forward_h.template',
|
||||
@@ -80,6 +92,8 @@
|
||||
'domain_node_tracing.pdl',
|
||||
'domain_node_worker.pdl',
|
||||
'domain_target.pdl',
|
||||
'domain_dom_storage.pdl',
|
||||
'domain_storage.pdl',
|
||||
],
|
||||
},
|
||||
'defines': [
|
||||
|
||||
@@ -9,3 +9,5 @@ include domain_node_runtime.pdl
|
||||
include domain_node_tracing.pdl
|
||||
include domain_node_worker.pdl
|
||||
include domain_target.pdl
|
||||
include domain_dom_storage.pdl
|
||||
include domain_storage.pdl
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "notification_emitter.h"
|
||||
#include "v8-inspector.h"
|
||||
#include "v8.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
NotificationEmitter::NotificationEmitter() {}
|
||||
|
||||
void NotificationEmitter::emitNotification(v8::Local<v8::Context> context,
|
||||
const EventKey& event,
|
||||
v8::Local<v8::Object> params) {
|
||||
auto it = event_notifier_map_.find(event);
|
||||
if (it != event_notifier_map_.end() && it->second) {
|
||||
it->second(context, params);
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationEmitter::addEventNotifier(const EventKey& event,
|
||||
EventNotifier notifier) {
|
||||
event_notifier_map_[event] = notifier;
|
||||
}
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef SRC_INSPECTOR_NOTIFICATION_EMITTER_H_
|
||||
#define SRC_INSPECTOR_NOTIFICATION_EMITTER_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "v8.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
class NotificationEmitter {
|
||||
public:
|
||||
using EventKey = std::string;
|
||||
using EventNotifier =
|
||||
std::function<void(v8::Local<v8::Context>, v8::Local<v8::Object>)>;
|
||||
|
||||
NotificationEmitter();
|
||||
virtual ~NotificationEmitter() = default;
|
||||
|
||||
void emitNotification(v8::Local<v8::Context> context,
|
||||
const EventKey& event,
|
||||
v8::Local<v8::Object> params);
|
||||
virtual bool canEmit(const std::string& domain) = 0;
|
||||
|
||||
NotificationEmitter(const NotificationEmitter&) = delete;
|
||||
NotificationEmitter& operator=(const NotificationEmitter&) = delete;
|
||||
|
||||
protected:
|
||||
void addEventNotifier(const EventKey& event, EventNotifier notifier);
|
||||
|
||||
private:
|
||||
std::unordered_map<EventKey, EventNotifier> event_notifier_map_ = {};
|
||||
};
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // SRC_INSPECTOR_NOTIFICATION_EMITTER_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "inspector/storage_agent.h"
|
||||
#include <string>
|
||||
#include "env-inl.h"
|
||||
#include "node_url.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
namespace protocol {
|
||||
StorageAgent::StorageAgent(Environment* env) : env_(env) {}
|
||||
StorageAgent::~StorageAgent() {}
|
||||
|
||||
void StorageAgent::Wire(protocol::UberDispatcher* dispatcher) {
|
||||
frontend_ =
|
||||
std::make_unique<protocol::Storage::Frontend>(dispatcher->channel());
|
||||
protocol::Storage::Dispatcher::wire(dispatcher, this);
|
||||
}
|
||||
DispatchResponse StorageAgent::getStorageKey(
|
||||
std::optional<protocol::String> frameId, protocol::String* storageKey) {
|
||||
auto local_storage_file = env_->options()->localstorage_file;
|
||||
*storageKey = node::url::FromFilePath(to_absolute_path(local_storage_file));
|
||||
return protocol::DispatchResponse::Success();
|
||||
}
|
||||
|
||||
std::string StorageAgent::to_absolute_path(const std::filesystem::path& input) {
|
||||
std::filesystem::path abs =
|
||||
std::filesystem::weakly_canonical(std::filesystem::absolute(input));
|
||||
return abs.generic_string();
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef SRC_INSPECTOR_STORAGE_AGENT_H_
|
||||
#define SRC_INSPECTOR_STORAGE_AGENT_H_
|
||||
|
||||
#include "env.h"
|
||||
#include "node/inspector/protocol/Storage.h"
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
namespace protocol {
|
||||
|
||||
class StorageAgent : public protocol::Storage::Backend {
|
||||
public:
|
||||
explicit StorageAgent(Environment* env);
|
||||
~StorageAgent() override;
|
||||
|
||||
void Wire(protocol::UberDispatcher* dispatcher);
|
||||
|
||||
DispatchResponse getStorageKey(std::optional<protocol::String> frameId,
|
||||
protocol::String* storageKey) override;
|
||||
|
||||
StorageAgent(const StorageAgent&) = delete;
|
||||
StorageAgent& operator=(const StorageAgent&) = delete;
|
||||
|
||||
private:
|
||||
std::string to_absolute_path(const std::filesystem::path& input);
|
||||
std::unique_ptr<protocol::Storage::Frontend> frontend_;
|
||||
Environment* env_;
|
||||
};
|
||||
} // namespace protocol
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // SRC_INSPECTOR_STORAGE_AGENT_H_
|
||||
+28
-4
@@ -1,13 +1,18 @@
|
||||
#include "inspector_agent.h"
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "crdtp/json.h"
|
||||
#include "env-inl.h"
|
||||
#include "inspector/dom_storage_agent.h"
|
||||
#include "inspector/io_agent.h"
|
||||
#include "inspector/main_thread_interface.h"
|
||||
#include "inspector/network_inspector.h"
|
||||
#include "inspector/node_json.h"
|
||||
#include "inspector/node_string.h"
|
||||
#include "inspector/protocol_helper.h"
|
||||
#include "inspector/runtime_agent.h"
|
||||
#include "inspector/storage_agent.h"
|
||||
#include "inspector/target_agent.h"
|
||||
#include "inspector/tracing_agent.h"
|
||||
#include "inspector/worker_agent.h"
|
||||
@@ -225,6 +230,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
|
||||
bool prevent_shutdown)
|
||||
: delegate_(std::move(delegate)),
|
||||
main_thread_(main_thread),
|
||||
env_(env),
|
||||
prevent_shutdown_(prevent_shutdown),
|
||||
retaining_context_(false) {
|
||||
session_ = inspector->connect(CONTEXT_GROUP_ID,
|
||||
@@ -259,6 +265,12 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
|
||||
target_agent_->Wire(node_dispatcher_.get());
|
||||
target_agent_->listenWorker(worker_manager);
|
||||
}
|
||||
if (env->options()->experimental_storage_inspection) {
|
||||
dom_storage_agent_ = std::make_unique<DOMStorageAgent>(env);
|
||||
dom_storage_agent_->Wire(node_dispatcher_.get());
|
||||
storage_agent_ = std::make_unique<protocol::StorageAgent>(env_);
|
||||
storage_agent_->Wire(node_dispatcher_.get());
|
||||
}
|
||||
}
|
||||
|
||||
~ChannelImpl() override {
|
||||
@@ -275,6 +287,14 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
|
||||
if (target_agent_) {
|
||||
target_agent_->reset();
|
||||
}
|
||||
if (storage_agent_) {
|
||||
storage_agent_->disable();
|
||||
storage_agent_.reset();
|
||||
}
|
||||
if (dom_storage_agent_) {
|
||||
dom_storage_agent_->disable();
|
||||
dom_storage_agent_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void emitNotificationFromBackend(v8::Local<v8::Context> context,
|
||||
@@ -283,11 +303,13 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
|
||||
std::string raw_event = protocol::StringUtil::StringViewToUtf8(event);
|
||||
std::string domain_name = raw_event.substr(0, raw_event.find('.'));
|
||||
std::string event_name = raw_event.substr(raw_event.find('.') + 1);
|
||||
if (network_inspector_->canEmit(domain_name)) {
|
||||
if (network_inspector_->canEmit(domain_name) &&
|
||||
env_->options()->experimental_network_inspection) {
|
||||
network_inspector_->emitNotification(
|
||||
context, domain_name, event_name, params);
|
||||
} else {
|
||||
UNREACHABLE("Unknown domain for emitNotificationFromBackend");
|
||||
} else if (dom_storage_agent_ && dom_storage_agent_->canEmit(domain_name) &&
|
||||
env_->options()->experimental_storage_inspection) {
|
||||
dom_storage_agent_->emitNotification(context, event_name, params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,11 +440,14 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
|
||||
std::unique_ptr<protocol::WorkerAgent> worker_agent_;
|
||||
std::shared_ptr<protocol::TargetAgent> target_agent_;
|
||||
std::unique_ptr<NetworkInspector> network_inspector_;
|
||||
std::unique_ptr<DOMStorageAgent> dom_storage_agent_;
|
||||
std::unique_ptr<protocol::StorageAgent> storage_agent_;
|
||||
std::shared_ptr<protocol::IoAgent> io_agent_;
|
||||
std::unique_ptr<InspectorSessionDelegate> delegate_;
|
||||
std::unique_ptr<v8_inspector::V8InspectorSession> session_;
|
||||
std::unique_ptr<UberDispatcher> node_dispatcher_;
|
||||
std::shared_ptr<MainThreadHandle> main_thread_;
|
||||
Environment* env_;
|
||||
bool prevent_shutdown_;
|
||||
bool retaining_context_;
|
||||
};
|
||||
@@ -942,7 +967,6 @@ std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
|
||||
void Agent::EmitProtocolEvent(v8::Local<v8::Context> context,
|
||||
const StringView& event,
|
||||
Local<Object> params) {
|
||||
if (!env()->options()->experimental_network_inspection) return;
|
||||
client_->emitNotification(context, event, params);
|
||||
}
|
||||
|
||||
|
||||
@@ -799,6 +799,9 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
||||
AddOption("--experimental-network-inspection",
|
||||
"experimental network inspection support",
|
||||
&EnvironmentOptions::experimental_network_inspection);
|
||||
AddOption("--experimental-storage-inspection",
|
||||
"experimental storage inspection support",
|
||||
&EnvironmentOptions::experimental_storage_inspection);
|
||||
AddOption("--experimental-worker-inspection",
|
||||
"experimental worker inspection support",
|
||||
&EnvironmentOptions::experimental_worker_inspection);
|
||||
|
||||
@@ -176,6 +176,7 @@ class EnvironmentOptions : public Options {
|
||||
bool cpu_prof = false;
|
||||
bool experimental_network_inspection = false;
|
||||
bool experimental_worker_inspection = false;
|
||||
bool experimental_storage_inspection = false;
|
||||
bool experimental_inspector_network_resource = false;
|
||||
std::string heap_prof_dir;
|
||||
std::string heap_prof_name;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Flags: --inspect=0 --experimental-storage-inspection --localstorage-file=./localstorage.db
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
common.skipIfInspectorDisabled();
|
||||
const { DOMStorage, Session } = require('node:inspector/promises');
|
||||
const { pathToFileURL } = require('node:url');
|
||||
const path = require('node:path');
|
||||
|
||||
|
||||
async function test() {
|
||||
const session = new Session();
|
||||
await session.connect();
|
||||
|
||||
await session.post('DOMStorage.enable');
|
||||
|
||||
const localStorageFileUrl =
|
||||
pathToFileURL(path.join(process.cwd(), 'localstorage.db')).href;
|
||||
|
||||
const { storageKey } = await session.post('Storage.getStorageKey');
|
||||
assert.strictEqual(
|
||||
storageKey.toLowerCase(),
|
||||
localStorageFileUrl.toLowerCase()
|
||||
);
|
||||
|
||||
await checkStorage(true);
|
||||
await checkStorage(false);
|
||||
|
||||
async function checkStorage(isLocalStorage) {
|
||||
DOMStorage.registerStorage({
|
||||
isLocalStorage,
|
||||
storageMap: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
[1]: 2,
|
||||
[true]: 'booleanKey',
|
||||
},
|
||||
});
|
||||
const result = await session.post('DOMStorage.getDOMStorageItems', {
|
||||
storageId: {
|
||||
isLocalStorage,
|
||||
securityOrigin: 'node-inspector://default-dom-storage',
|
||||
},
|
||||
});
|
||||
const sortedEntries = result.entries.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
assert.deepStrictEqual(sortedEntries, [
|
||||
['1', '2'],
|
||||
['key1', 'value1'],
|
||||
['key2', 'value2'],
|
||||
['true', 'booleanKey'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
test().then(common.mustCall());
|
||||
@@ -1,4 +1,4 @@
|
||||
// Flags: --inspect=0 --experimental-network-inspection
|
||||
// Flags: --inspect=0 --experimental-network-inspection --experimental-storage-inspection
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
|
||||
@@ -116,6 +116,36 @@ const EXPECTED_EVENTS = {
|
||||
|
||||
}
|
||||
},
|
||||
],
|
||||
DOMStorage: [
|
||||
{
|
||||
name: 'domStorageItemAdded',
|
||||
params: {
|
||||
storageId: {
|
||||
securityOrigin: '',
|
||||
isLocalStorage: true,
|
||||
storageKey: 'node-inspector://default-dom-storage',
|
||||
},
|
||||
key: 'testKey',
|
||||
newValue: 'testValue',
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'domStorageItemRemoved',
|
||||
skip: true
|
||||
},
|
||||
{
|
||||
name: 'domStorageItemUpdated',
|
||||
skip: true
|
||||
},
|
||||
{
|
||||
name: 'domStorageItemsCleared',
|
||||
skip: true
|
||||
},
|
||||
{
|
||||
name: 'registerStorage',
|
||||
skip: true
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
@@ -146,6 +176,7 @@ for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) {
|
||||
|
||||
// Check that all events emit the expected parameters.
|
||||
await session.post('Network.enable');
|
||||
await session.post('DOMStorage.enable');
|
||||
for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) {
|
||||
for (const event of events) {
|
||||
if (event.skip) {
|
||||
|
||||
Reference in new issue
Block a user