blob: 0c7fabe48dcc4f3f44013e69a726bface16613ad [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_SESSION_STATE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_SESSION_STATE_H_
#include <memory>
#include <type_traits>
#include <vector>
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class InspectorAgentState;
// An abstraction for the inspector session state in the renderer.
// Makes the |reattach_state| sent from the browser available to the
// InspectorAgentState::Field instances, and queues the updates
// that are made to these fields.
class CORE_EXPORT InspectorSessionState {
public:
InspectorSessionState(mojom::blink::DevToolsSessionStatePtr reattach_state);
// Make |reattach_state| available to InspectorAgentState::Field instances.
const mojom::blink::DevToolsSessionState* ReattachState() const;
// Registers a field update in the stored session state and in the updates
// that are sent back to the browser.
// A null string for |value| indicates a deletion.
// TODO(johannes): Lower cost of repeated updates.
void EnqueueUpdate(const WTF::String& key, const WTF::String& value);
// Yields and consumes the field updates that have thus far accumulated.
// These updates are sent back to DevToolsSession on the browser side.
mojom::blink::DevToolsSessionStatePtr TakeUpdates();
private:
const mojom::blink::DevToolsSessionStatePtr reattach_state_;
mojom::blink::DevToolsSessionStatePtr updates_;
};
// InspectorAgentState connects the fields of inspector agents
// with the renderer-side InspectorSessionState.
class CORE_EXPORT InspectorAgentState {
private:
// Trivial Helpers for converting between the value types used for the agent
// state fields and JSON strings used for the wire protocol. The point of
// these is to be able to call overloaded methods from the template
// implementations below; they just delegate to protocol::Value parsing
// and serialization.
static void EncodeToJSON(bool v, WTF::String* out);
static bool DecodeFromJSON(const WTF::String& in, bool* v);
static void EncodeToJSON(int32_t v, WTF::String* out);
static bool DecodeFromJSON(const WTF::String& in, int32_t* v);
static void EncodeToJSON(double v, WTF::String* out);
static bool DecodeFromJSON(const WTF::String& in, double* v);
static void EncodeToJSON(const WTF::String& v, WTF::String* out);
static bool DecodeFromJSON(const WTF::String& in, WTF::String* v);
public:
// A field is connected to the |agent_state|, which initializes the field
// via ::InitFrom / ::Decode when the agent is (re)attached.
class Field {
public:
Field(InspectorAgentState* agent_state)
: prefix_key_(agent_state->RegisterField(this)) {}
virtual ~Field() = default;
void InitFrom(InspectorSessionState* session_state) {
session_state_ = session_state;
Decode();
}
// Clears the field to its default or to the empty map if it's a map field.
virtual void Clear() = 0;
protected:
virtual void Decode() = 0;
// The field instance is allowed to use/allocate any entry in
// the session state starting with this prefix. SimpleField instances
// just use prefix_key_ directly, MapField instances append a suffix.
const WTF::String prefix_key_;
InspectorSessionState* session_state_;
};
// A simple field with a default value, providing Get, Set, and Clear
// operations. E.g. an instantiation with WTF::String yields:
// - const WTF::String& Get();
// - Set(const WTF::String&);
// - void Clear();
template <class ValueType>
class SimpleField : public Field {
// Means in practice: const WTF::String& for WTF::String, otherwise same
// as ValueType.
using ConstRefType =
typename std::conditional<std::is_fundamental<ValueType>::value,
ValueType,
const ValueType&>::type;
public:
// Constructs a new field registered with |agent_state| which
// will use |default_value| when not set.
SimpleField(InspectorAgentState* agent_state, ConstRefType default_value)
: Field(agent_state),
default_value_(default_value),
value_(default_value) {}
// Returns the value of the field, or the default if not set.
ConstRefType Get() const { return value_; }
// Sets the field to the value, or clears if |value| is the default.
void Set(ConstRefType value) {
if (value == value_)
return;
if (value == default_value_) {
Clear();
return;
}
value_ = value;
WTF::String encoded_value;
EncodeToJSON(value, &encoded_value);
session_state_->EnqueueUpdate(prefix_key_, encoded_value);
}
// Clears the field to its default.
void Clear() override {
if (default_value_ == value_)
return;
value_ = default_value_;
session_state_->EnqueueUpdate(prefix_key_, WTF::String());
}
private:
// Decodes the key from the encoded session state. This is used
// during initialization when an agent is reattached.
void Decode() override {
const mojom::blink::DevToolsSessionState* reattach_state =
session_state_->ReattachState();
if (!reattach_state)
return;
auto it = reattach_state->entries.find(prefix_key_);
if (it != reattach_state->entries.end())
DecodeFromJSON(it->value, &value_);
}
const ValueType default_value_;
ValueType value_;
};
// A map field provides a map from WTF::String to its value type,
// and Keys, Get, Set, Clear operations.
template <class ValueType>
class MapField : public Field {
// Means in practice: const WTF::String& for WTF::String, otherwise same
// as ValueType.
using ConstRefType =
typename std::conditional<std::is_fundamental<ValueType>::value,
ValueType,
const ValueType&>::type;
public:
// Constructs a new field registered with |agent_state| which
// will use |default_value| for keys that are not set.
MapField(InspectorAgentState* agent_state, ConstRefType default_value)
: Field(agent_state), default_value_(default_value) {}
// Enumerates the keys for which values are stored in this field.
// The order of the keys is undefined.
std::vector<WTF::String> Keys() const {
// TODO(johannes): It'd be nice to avoid copying; unfortunately
// it didn't seem easy to return map_.Keys().
std::vector<WTF::String> keys;
for (const WTF::String& s : map_.Keys())
keys.push_back(s);
return keys;
}
// O(1) shortcut for Keys().empty().
bool IsEmpty() const { return map_.IsEmpty(); }
// Returns the value for a given |key|, or the default value if
// the key wasn't set.
ConstRefType Get(const WTF::String& key) const {
auto it = map_.find(key);
return it == map_.end() ? default_value_ : it->value;
}
// Sets the |value| for |key| as provided, except if |value| is the
// default value in which case |key| is cleared.
void Set(const WTF::String& key, ConstRefType value) {
if (value == default_value_) {
Clear(key);
return;
}
auto it = map_.find(key);
if (it != map_.end() && it->value == value)
return;
map_.Set(key, value);
WTF::String encoded_value;
EncodeToJSON(value, &encoded_value);
session_state_->EnqueueUpdate(prefix_key_ + key, encoded_value);
}
// Clears the entry for |key|.
void Clear(const WTF::String& key) {
auto it = map_.find(key);
if (it == map_.end())
return;
map_.erase(it);
session_state_->EnqueueUpdate(prefix_key_ + key, WTF::String());
}
// Clears the entire field.
void Clear() override {
// TODO(johannes): Handle this in a single update.
for (const WTF::String& key : map_.Keys())
session_state_->EnqueueUpdate(prefix_key_ + key, WTF::String());
map_.clear();
}
private:
// Decodes the key from the encoded session state. This is used
// during initialization when an agent is reattached.
void Decode() override {
const mojom::blink::DevToolsSessionState* reattach_state =
session_state_->ReattachState();
if (!reattach_state)
return;
// TODO(johannes): Avoid scanning all keys, let session_state_ provide
// the keys that match a prefix.
for (const auto& entry : reattach_state->entries) {
if (!entry.key.StartsWith(prefix_key_))
continue;
WTF::String suffix_key = entry.key.Substring(prefix_key_.length());
ValueType v;
if (DecodeFromJSON(entry.value, &v))
map_.Set(suffix_key, v);
}
}
const ValueType default_value_;
WTF::HashMap<WTF::String, ValueType> map_;
};
using Boolean = SimpleField<bool>;
using Integer = SimpleField<int32_t>;
using Double = SimpleField<double>;
using String = SimpleField<WTF::String>;
using BooleanMap = MapField<bool>;
using IntegerMap = MapField<int32_t>;
using DoubleMap = MapField<double>;
using StringMap = MapField<WTF::String>;
InspectorAgentState(const WTF::String& domain_name);
// Registers |field| and returns the prefix key for it.
// The prefix key is domain_name + "." + index in fields_ + "/",
// e.g. "network.0/".
WTF::String RegisterField(Field* field);
// Init must be called *after* all fields are registered with the
// InspectorAgentState. Usually, the fact that fields are registered in
// the constructors / initializers of agents takes care of it.
void InitFrom(InspectorSessionState* session_state);
// Clears all fields registered with this InspectorAgentState instance.
void ClearAllFields();
private:
const WTF::String domain_name_;
std::vector<Field*> fields_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_SESSION_STATE_H_