Commit fa3aada5 authored by Johannes Henkel's avatar Johannes Henkel Committed by Commit Bot

[DevTools] Roll inspector_protocol (V8)

Upstream PR:
"Introduce a crdtp/dispatch.{h,cc} library."
https://chromium-review.googlesource.com/c/deps/inspector_protocol/+/1974680
"For the shallow parse of a DevTools message, allow "params": null."
https://chromium-review.googlesource.com/c/deps/inspector_protocol/+/2109466

New Revision: c69cdc36200992d21a17bf4e5c2f3a95b8860ddf

Change-Id: Icc447ff9ce408b24f5245c643dd2f1843da9255f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2076215
Commit-Queue: Johannes Henkel <johannes@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66813}
parent 1e40c682
......@@ -116,7 +116,7 @@ bool substituteObjectTags(int sessionId, const String16& groupName,
protocol::Response response =
injectedScript->wrapObject(originValue, groupName, WrapMode::kNoPreview,
configValue, maxDepth - 1, &wrapper);
if (!response.isSuccess() || !wrapper) {
if (!response.IsSuccess() || !wrapper) {
reportError(context, tryCatch, "cannot wrap value");
return false;
}
......
......@@ -179,7 +179,7 @@ class InjectedScript::ProtocolPromiseHandler {
if (!session) return;
InjectedScript::ContextScope scope(session, m_executionContextId);
Response response = scope.initialize();
if (!response.isSuccess()) return;
if (!response.IsSuccess()) return;
std::unique_ptr<EvaluateCallback> callback =
scope.injectedScript()->takeEvaluateCallback(m_callback);
......@@ -210,7 +210,7 @@ class InjectedScript::ProtocolPromiseHandler {
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
response = scope.injectedScript()->wrapObject(result, m_objectGroup,
m_wrapMode, &wrappedValue);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -224,14 +224,14 @@ class InjectedScript::ProtocolPromiseHandler {
if (!session) return;
InjectedScript::ContextScope scope(session, m_executionContextId);
Response response = scope.initialize();
if (!response.isSuccess()) return;
if (!response.IsSuccess()) return;
std::unique_ptr<EvaluateCallback> callback =
scope.injectedScript()->takeEvaluateCallback(m_callback);
if (!callback) return;
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
response = scope.injectedScript()->wrapObject(result, m_objectGroup,
m_wrapMode, &wrappedValue);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -251,7 +251,7 @@ class InjectedScript::ProtocolPromiseHandler {
exceptionDetails;
response = scope.injectedScript()->createExceptionDetails(
message, exception, m_objectGroup, &exceptionDetails);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -297,7 +297,7 @@ class InjectedScript::ProtocolPromiseHandler {
.build();
response = scope.injectedScript()->addExceptionToDetails(
result, exceptionDetails.get(), m_objectGroup);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -315,11 +315,11 @@ class InjectedScript::ProtocolPromiseHandler {
if (!session) return;
InjectedScript::ContextScope scope(session, m_executionContextId);
Response response = scope.initialize();
if (!response.isSuccess()) return;
if (!response.IsSuccess()) return;
std::unique_ptr<EvaluateCallback> callback =
scope.injectedScript()->takeEvaluateCallback(m_callback);
if (!callback) return;
callback->sendFailure(Response::Error("Promise was collected"));
callback->sendFailure(Response::ServerError("Promise was collected"));
}
V8InspectorImpl* m_inspector;
......@@ -380,60 +380,59 @@ Response InjectedScript::getProperties(
.setEnumerable(mirror.enumerable)
.setIsOwn(mirror.isOwn)
.build();
Response response;
std::unique_ptr<RemoteObject> remoteObject;
if (mirror.value) {
response = wrapObjectMirror(*mirror.value, groupName, wrapMode,
v8::MaybeLocal<v8::Value>(),
kMaxCustomPreviewDepth, &remoteObject);
if (!response.isSuccess()) return response;
Response response = wrapObjectMirror(
*mirror.value, groupName, wrapMode, v8::MaybeLocal<v8::Value>(),
kMaxCustomPreviewDepth, &remoteObject);
if (!response.IsSuccess()) return response;
descriptor->setValue(std::move(remoteObject));
descriptor->setWritable(mirror.writable);
}
if (mirror.getter) {
response =
Response response =
mirror.getter->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.getter->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setGet(std::move(remoteObject));
}
if (mirror.setter) {
response =
Response response =
mirror.setter->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.setter->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setSet(std::move(remoteObject));
}
if (mirror.symbol) {
response =
Response response =
mirror.symbol->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.symbol->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setSymbol(std::move(remoteObject));
}
if (mirror.exception) {
response =
Response response =
mirror.exception->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response = bindRemoteObjectIfNeeded(sessionId, context,
mirror.exception->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setValue(std::move(remoteObject));
descriptor->setWasThrown(true);
}
(*properties)->emplace_back(std::move(descriptor));
}
return Response::OK();
return Response::Success();
}
Response InjectedScript::getInternalAndPrivateProperties(
......@@ -445,7 +444,7 @@ Response InjectedScript::getInternalAndPrivateProperties(
*internalProperties = std::make_unique<Array<InternalPropertyDescriptor>>();
*privateProperties = std::make_unique<Array<PrivatePropertyDescriptor>>();
if (!value->IsObject()) return Response::OK();
if (!value->IsObject()) return Response::Success();
v8::Local<v8::Object> value_obj = value.As<v8::Object>();
......@@ -458,11 +457,11 @@ Response InjectedScript::getInternalAndPrivateProperties(
std::unique_ptr<RemoteObject> remoteObject;
Response response = internalProperty.value->buildRemoteObject(
m_context->context(), WrapMode::kNoPreview, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response = bindRemoteObjectIfNeeded(sessionId, context,
internalProperty.value->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
(*internalProperties)
->emplace_back(InternalPropertyDescriptor::create()
.setName(internalProperty.name)
......@@ -479,45 +478,44 @@ Response InjectedScript::getInternalAndPrivateProperties(
.build();
std::unique_ptr<RemoteObject> remoteObject;
Response response;
DCHECK((privateProperty.getter || privateProperty.setter) ^
(!!privateProperty.value));
if (privateProperty.value) {
response = privateProperty.value->buildRemoteObject(
Response response = privateProperty.value->buildRemoteObject(
context, WrapMode::kNoPreview, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response = bindRemoteObjectIfNeeded(sessionId, context,
privateProperty.value->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setValue(std::move(remoteObject));
}
if (privateProperty.getter) {
response = privateProperty.getter->buildRemoteObject(
Response response = privateProperty.getter->buildRemoteObject(
context, WrapMode::kNoPreview, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response = bindRemoteObjectIfNeeded(sessionId, context,
privateProperty.getter->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setGet(std::move(remoteObject));
}
if (privateProperty.setter) {
response = privateProperty.setter->buildRemoteObject(
Response response = privateProperty.setter->buildRemoteObject(
context, WrapMode::kNoPreview, &remoteObject);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response = bindRemoteObjectIfNeeded(sessionId, context,
privateProperty.setter->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
descriptor->setSet(std::move(remoteObject));
}
(*privateProperties)->emplace_back(std::move(descriptor));
}
return Response::OK();
return Response::Success();
}
void InjectedScript::releaseObject(const String16& objectId) {
......@@ -564,11 +562,11 @@ Response InjectedScript::wrapObjectMirror(
v8::Local<v8::Context> context = m_context->context();
v8::Context::Scope contextScope(context);
Response response = mirror.buildRemoteObject(context, wrapMode, result);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
v8::Local<v8::Value> value = mirror.v8Value();
response = bindRemoteObjectIfNeeded(sessionId, context, value, groupName,
result->get());
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (customPreviewEnabled && value->IsObject()) {
std::unique_ptr<protocol::Runtime::CustomPreview> customPreview;
generateCustomPreview(sessionId, groupName, value.As<v8::Object>(),
......@@ -576,7 +574,7 @@ Response InjectedScript::wrapObjectMirror(
&customPreview);
if (customPreview) (*result)->setCustomPreview(std::move(customPreview));
}
return Response::OK();
return Response::Success();
}
std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
......@@ -593,7 +591,7 @@ std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
std::unique_ptr<RemoteObject> remoteObject;
Response response =
wrapObject(table, "console", WrapMode::kNoPreview, &remoteObject);
if (!remoteObject || !response.isSuccess()) return nullptr;
if (!remoteObject || !response.IsSuccess()) return nullptr;
auto mirror = ValueMirror::create(context, table);
std::unique_ptr<ObjectPreview> preview;
......@@ -662,7 +660,8 @@ void InjectedScript::addPromiseCallback(
void InjectedScript::discardEvaluateCallbacks() {
for (auto& callback : m_evaluateCallbacks) {
callback->sendFailure(Response::Error("Execution context was destroyed."));
callback->sendFailure(
Response::ServerError("Execution context was destroyed."));
delete callback;
}
m_evaluateCallbacks.clear();
......@@ -681,9 +680,9 @@ Response InjectedScript::findObject(const RemoteObjectId& objectId,
v8::Local<v8::Value>* outObject) const {
auto it = m_idToWrappedObject.find(objectId.id());
if (it == m_idToWrappedObject.end())
return Response::Error("Could not find object with given id");
return Response::ServerError("Could not find object with given id");
*outObject = it->second.Get(m_context->isolate());
return Response::OK();
return Response::Success();
}
String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const {
......@@ -723,9 +722,9 @@ Response InjectedScript::resolveCallArgument(
std::unique_ptr<RemoteObjectId> remoteObjectId;
Response response =
RemoteObjectId::parse(callArgument->getObjectId(""), &remoteObjectId);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (remoteObjectId->contextId() != m_context->contextId())
return Response::Error(
return Response::ServerError(
"Argument should belong to the same JavaScript world as target "
"object");
return findObject(*remoteObjectId, result);
......@@ -753,28 +752,29 @@ Response InjectedScript::resolveCallArgument(
->compileAndRunInternalScript(
m_context->context(), toV8String(m_context->isolate(), value))
.ToLocal(result)) {
return Response::Error("Couldn't parse value object in call argument");
return Response::ServerError(
"Couldn't parse value object in call argument");
}
return Response::OK();
return Response::Success();
}
*result = v8::Undefined(m_context->isolate());
return Response::OK();
return Response::Success();
}
Response InjectedScript::addExceptionToDetails(
v8::Local<v8::Value> exception,
protocol::Runtime::ExceptionDetails* exceptionDetails,
const String16& objectGroup) {
if (exception.IsEmpty()) return Response::OK();
if (exception.IsEmpty()) return Response::Success();
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped;
Response response =
wrapObject(exception, objectGroup,
exception->IsNativeError() ? WrapMode::kNoPreview
: WrapMode::kWithPreview,
&wrapped);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
exceptionDetails->setException(std::move(wrapped));
return Response::OK();
return Response::Success();
}
Response InjectedScript::createExceptionDetails(
......@@ -821,9 +821,9 @@ Response InjectedScript::createExceptionDetails(
}
Response response =
addExceptionToDetails(exception, exceptionDetails.get(), objectGroup);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
*result = std::move(exceptionDetails);
return Response::OK();
return Response::Success();
}
Response InjectedScript::wrapEvaluateResult(
......@@ -836,14 +836,14 @@ Response InjectedScript::wrapEvaluateResult(
if (!maybeResultValue.ToLocal(&resultValue))
return Response::InternalError();
Response response = wrapObject(resultValue, objectGroup, wrapMode, result);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (objectGroup == "console") {
m_lastEvaluationResult.Reset(m_context->isolate(), resultValue);
m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel);
}
} else {
if (tryCatch.HasTerminated() || !tryCatch.CanContinue()) {
return Response::Error("Execution was terminated");
return Response::ServerError("Execution was terminated");
}
v8::Local<v8::Value> exception = tryCatch.Exception();
Response response =
......@@ -851,13 +851,13 @@ Response InjectedScript::wrapEvaluateResult(
exception->IsNativeError() ? WrapMode::kNoPreview
: WrapMode::kWithPreview,
result);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
// We send exception in result for compatibility reasons, even though it's
// accessible through exceptionDetails.exception.
response = createExceptionDetails(tryCatch, objectGroup, exceptionDetails);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
}
return Response::OK();
return Response::Success();
}
v8::Local<v8::Object> InjectedScript::commandLineAPI() {
......@@ -889,11 +889,11 @@ Response InjectedScript::Scope::initialize() {
m_inspector->sessionById(m_contextGroupId, m_sessionId);
if (!session) return Response::InternalError();
Response response = findInjectedScript(session);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
m_context = m_injectedScript->context()->context();
m_context->Enter();
if (m_allowEval) m_context->AllowCodeGenerationFromStrings(true);
return Response::OK();
return Response::Success();
}
void InjectedScript::Scope::installCommandLineAPI() {
......@@ -976,15 +976,15 @@ Response InjectedScript::ObjectScope::findInjectedScript(
V8InspectorSessionImpl* session) {
std::unique_ptr<RemoteObjectId> remoteId;
Response response = RemoteObjectId::parse(m_remoteObjectId, &remoteId);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
InjectedScript* injectedScript = nullptr;
response = session->findInjectedScript(remoteId.get(), injectedScript);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
m_objectGroupName = injectedScript->objectGroupName(*remoteId);
response = injectedScript->findObject(*remoteId, &m_object);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
m_injectedScript = injectedScript;
return Response::OK();
return Response::Success();
}
InjectedScript::CallFrameScope::CallFrameScope(V8InspectorSessionImpl* session,
......@@ -997,7 +997,7 @@ Response InjectedScript::CallFrameScope::findInjectedScript(
V8InspectorSessionImpl* session) {
std::unique_ptr<RemoteCallFrameId> remoteId;
Response response = RemoteCallFrameId::parse(m_remoteCallFrameId, &remoteId);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal());
return session->findInjectedScript(remoteId.get(), m_injectedScript);
}
......@@ -1022,9 +1022,9 @@ String16 InjectedScript::bindObject(v8::Local<v8::Value> value,
Response InjectedScript::bindRemoteObjectIfNeeded(
int sessionId, v8::Local<v8::Context> context, v8::Local<v8::Value> value,
const String16& groupName, protocol::Runtime::RemoteObject* remoteObject) {
if (!remoteObject) return Response::OK();
if (remoteObject->hasValue()) return Response::OK();
if (remoteObject->hasUnserializableValue()) return Response::OK();
if (!remoteObject) return Response::Success();
if (remoteObject->hasValue()) return Response::Success();
if (remoteObject->hasUnserializableValue()) return Response::Success();
if (remoteObject->getType() != RemoteObject::TypeEnum::Undefined) {
v8::Isolate* isolate = context->GetIsolate();
V8InspectorImpl* inspector =
......@@ -1035,11 +1035,11 @@ Response InjectedScript::bindRemoteObjectIfNeeded(
inspectedContext ? inspectedContext->getInjectedScript(sessionId)
: nullptr;
if (!injectedScript) {
return Response::Error("Cannot find context with specified id");
return Response::ServerError("Cannot find context with specified id");
}
remoteObject->setObjectId(injectedScript->bindObject(value, groupName));
}
return Response::OK();
return Response::Success();
}
void InjectedScript::unbindObject(int id) {
......
......@@ -38,12 +38,12 @@ Response RemoteObjectId::parse(const String16& objectId,
std::unique_ptr<RemoteObjectId> remoteObjectId(new RemoteObjectId());
std::unique_ptr<protocol::DictionaryValue> parsedObjectId =
remoteObjectId->parseInjectedScriptId(objectId);
if (!parsedObjectId) return Response::Error("Invalid remote object id");
if (!parsedObjectId) return Response::ServerError("Invalid remote object id");
bool success = parsedObjectId->getInteger("id", &remoteObjectId->m_id);
if (!success) return Response::Error("Invalid remote object id");
if (!success) return Response::ServerError("Invalid remote object id");
*result = std::move(remoteObjectId);
return Response::OK();
return Response::Success();
}
RemoteCallFrameId::RemoteCallFrameId()
......@@ -54,13 +54,13 @@ Response RemoteCallFrameId::parse(const String16& objectId,
std::unique_ptr<RemoteCallFrameId> remoteCallFrameId(new RemoteCallFrameId());
std::unique_ptr<protocol::DictionaryValue> parsedObjectId =
remoteCallFrameId->parseInjectedScriptId(objectId);
if (!parsedObjectId) return Response::Error("Invalid call frame id");
if (!parsedObjectId) return Response::ServerError("Invalid call frame id");
bool success =
parsedObjectId->getInteger("ordinal", &remoteCallFrameId->m_frameOrdinal);
if (!success) return Response::Error("Invalid call frame id");
if (!success) return Response::ServerError("Invalid call frame id");
*result = std::move(remoteCallFrameId);
return Response::OK();
return Response::Success();
}
String16 RemoteCallFrameId::serialize(int injectedScriptId, int frameOrdinal) {
......
......@@ -161,3 +161,10 @@ String16 stackTraceIdToString(uintptr_t id) {
}
} // namespace v8_inspector
namespace v8_crdtp {
void SerializerTraits<v8_inspector::protocol::Binary>::Serialize(
const v8_inspector::protocol::Binary& binary, std::vector<uint8_t>* out) {
cbor::EncodeBinary(span<uint8_t>(binary.data(), binary.size()), out);
}
} // namespace v8_crdtp
......@@ -24,17 +24,6 @@ using String = v8_inspector::String16;
class StringUtil {
public:
static String substring(const String& s, size_t pos, size_t len) {
return s.substring(pos, len);
}
static size_t find(const String& s, const char* needle) {
return s.find(needle);
}
static size_t find(const String& s, const String& needle) {
return s.find(needle);
}
static const size_t kNotFound = String::kNotFound;
static String fromUTF8(const uint8_t* data, size_t length) {
return String16::fromUTF8(reinterpret_cast<const char*>(data), length);
}
......@@ -100,4 +89,13 @@ String16 stackTraceIdToString(uintptr_t id);
} // namespace v8_inspector
// See third_party/inspector_protocol/crdtp/serializer_traits.h.
namespace v8_crdtp {
template <>
struct SerializerTraits<v8_inspector::protocol::Binary> {
static void Serialize(const v8_inspector::protocol::Binary& binary,
std::vector<uint8_t>* out);
};
} // namespace v8_crdtp
#endif // V8_INSPECTOR_STRING_UTIL_H_
......@@ -27,23 +27,23 @@ V8ConsoleAgentImpl::V8ConsoleAgentImpl(
V8ConsoleAgentImpl::~V8ConsoleAgentImpl() = default;
Response V8ConsoleAgentImpl::enable() {
if (m_enabled) return Response::OK();
if (m_enabled) return Response::Success();
m_state->setBoolean(ConsoleAgentState::consoleEnabled, true);
m_enabled = true;
m_session->inspector()->enableStackCapturingIfNeeded();
reportAllMessages();
return Response::OK();
return Response::Success();
}
Response V8ConsoleAgentImpl::disable() {
if (!m_enabled) return Response::OK();
if (!m_enabled) return Response::Success();
m_session->inspector()->disableStackCapturingIfNeeded();
m_state->setBoolean(ConsoleAgentState::consoleEnabled, false);
m_enabled = false;
return Response::OK();
return Response::Success();
}
Response V8ConsoleAgentImpl::clearMessages() { return Response::OK(); }
Response V8ConsoleAgentImpl::clearMessages() { return Response::Success(); }
void V8ConsoleAgentImpl::restore() {
if (!m_state->booleanProperty(ConsoleAgentState::consoleEnabled, false))
......
......@@ -595,7 +595,7 @@ static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject;
protocol::Response response = injectedScript->wrapObject(
value, "", WrapMode::kNoPreview, &wrappedObject);
if (!response.isSuccess()) return;
if (!response.IsSuccess()) return;
std::unique_ptr<protocol::DictionaryValue> hints =
protocol::DictionaryValue::create();
......
......@@ -252,8 +252,8 @@ Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
InjectedScript* injectedScript,
std::unique_ptr<Array<Scope>>* scopes) {
*scopes = std::make_unique<Array<Scope>>();
if (!injectedScript) return Response::OK();
if (iterator->Done()) return Response::OK();
if (!injectedScript) return Response::Success();
if (iterator->Done()) return Response::Success();
String16 scriptId = String16::fromInteger(iterator->GetScriptId());
......@@ -262,7 +262,7 @@ Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
Response result =
injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
WrapMode::kNoPreview, &object);
if (!result.isSuccess()) return result;
if (!result.IsSuccess()) return result;
auto scope = Scope::create()
.setType(scopeType(iterator->GetType()))
......@@ -290,7 +290,7 @@ Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
}
(*scopes)->emplace_back(std::move(scope));
}
return Response::OK();
return Response::Success();
}
protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
......@@ -344,17 +344,17 @@ Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
*outDebuggerId =
m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
if (enabled()) return Response::OK();
if (enabled()) return Response::Success();
if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
return Response::Error("Script execution is prohibited");
return Response::ServerError("Script execution is prohibited");
enableImpl();
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::disable() {
if (!enabled()) return Response::OK();
if (!enabled()) return Response::Success();
m_state->remove(DebuggerAgentState::breakpointsByRegex);
m_state->remove(DebuggerAgentState::breakpointsByUrl);
......@@ -389,7 +389,7 @@ Response V8DebuggerAgentImpl::disable() {
m_enabled = false;
m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
m_debugger->disable();
return Response::OK();
return Response::Success();
}
void V8DebuggerAgentImpl::restore() {
......@@ -421,21 +421,21 @@ void V8DebuggerAgentImpl::restore() {
}
Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (m_breakpointsActive == active) return Response::OK();
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
if (m_breakpointsActive == active) return Response::Success();
m_breakpointsActive = active;
m_debugger->setBreakpointsActive(active);
if (!active && !m_breakReason.empty()) {
clearBreakDetails();
m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
}
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
m_skipAllPauses = skip;
return Response::OK();
return Response::Success();
}
static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
......@@ -469,13 +469,14 @@ Response V8DebuggerAgentImpl::setBreakpointByUrl(
(optionalURLRegex.isJust() ? 1 : 0) +
(optionalScriptHash.isJust() ? 1 : 0);
if (specified != 1) {
return Response::Error(
return Response::ServerError(
"Either url or urlRegex or scriptHash must be specified.");
}
int columnNumber = 0;
if (optionalColumnNumber.isJust()) {
columnNumber = optionalColumnNumber.fromJust();
if (columnNumber < 0) return Response::Error("Incorrect column number");
if (columnNumber < 0)
return Response::ServerError("Incorrect column number");
}
BreakpointType type = BreakpointType::kByUrl;
......@@ -515,7 +516,8 @@ Response V8DebuggerAgentImpl::setBreakpointByUrl(
UNREACHABLE();
}
if (breakpoints->get(breakpointId)) {
return Response::Error("Breakpoint at specified location already exists.");
return Response::ServerError(
"Breakpoint at specified location already exists.");
}
String16 hint;
......@@ -539,7 +541,7 @@ Response V8DebuggerAgentImpl::setBreakpointByUrl(
breakpointHints->setString(breakpointId, hint);
}
*outBreakpointId = breakpointId;
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setBreakpoint(
......@@ -551,15 +553,17 @@ Response V8DebuggerAgentImpl::setBreakpoint(
location->getLineNumber(), location->getColumnNumber(0));
if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
m_breakpointIdToDebuggerBreakpointIds.end()) {
return Response::Error("Breakpoint at specified location already exists.");
return Response::ServerError(
"Breakpoint at specified location already exists.");
}
*actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
optionalCondition.fromMaybe(String16()),
location->getLineNumber(),
location->getColumnNumber(0));
if (!*actualLocation) return Response::Error("Could not resolve breakpoint");
if (!*actualLocation)
return Response::ServerError("Could not resolve breakpoint");
*outBreakpointId = breakpointId;
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
......@@ -567,9 +571,9 @@ Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
String16* outBreakpointId) {
InjectedScript::ObjectScope scope(m_session, functionObjectId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (!scope.object()->IsFunction()) {
return Response::Error("Could not find function with given id");
return Response::ServerError("Could not find function with given id");
}
v8::Local<v8::Function> function =
v8::Local<v8::Function>::Cast(scope.object());
......@@ -577,35 +581,37 @@ Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
m_breakpointIdToDebuggerBreakpointIds.end()) {
return Response::Error("Breakpoint at specified location already exists.");
return Response::ServerError(
"Breakpoint at specified location already exists.");
}
v8::Local<v8::String> condition =
toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
setBreakpointImpl(breakpointId, function, condition);
*outBreakpointId = breakpointId;
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setInstrumentationBreakpoint(
const String16& instrumentation, String16* outBreakpointId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
protocol::DictionaryValue* breakpoints = getOrCreateObject(
m_state, DebuggerAgentState::instrumentationBreakpoints);
if (breakpoints->get(breakpointId)) {
return Response::Error("Instrumentation breakpoint is already enabled.");
return Response::ServerError(
"Instrumentation breakpoint is already enabled.");
}
breakpoints->setBoolean(breakpointId, true);
*outBreakpointId = breakpointId;
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
BreakpointType type;
String16 selector;
if (!parseBreakpointId(breakpointId, &type, &selector)) {
return Response::OK();
return Response::Success();
}
protocol::DictionaryValue* breakpoints = nullptr;
switch (type) {
......@@ -649,7 +655,7 @@ Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
}
removeBreakpointImpl(breakpointId, scripts);
return Response::OK();
return Response::Success();
}
void V8DebuggerAgentImpl::removeBreakpointImpl(
......@@ -681,7 +687,7 @@ Response V8DebuggerAgentImpl::getPossibleBreakpoints(
String16 scriptId = start->getScriptId();
if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
return Response::Error(
return Response::ServerError(
"start.lineNumber and start.columnNumber should be >= 0");
v8::debug::Location v8Start(start->getLineNumber(),
......@@ -689,23 +695,24 @@ Response V8DebuggerAgentImpl::getPossibleBreakpoints(
v8::debug::Location v8End;
if (end.isJust()) {
if (end.fromJust()->getScriptId() != scriptId)
return Response::Error("Locations should contain the same scriptId");
return Response::ServerError(
"Locations should contain the same scriptId");
int line = end.fromJust()->getLineNumber();
int column = end.fromJust()->getColumnNumber(0);
if (line < 0 || column < 0)
return Response::Error(
return Response::ServerError(
"end.lineNumber and end.columnNumber should be >= 0");
v8End = v8::debug::Location(line, column);
}
auto it = m_scripts.find(scriptId);
if (it == m_scripts.end()) return Response::Error("Script not found");
if (it == m_scripts.end()) return Response::ServerError("Script not found");
std::vector<v8::debug::BreakLocation> v8Locations;
{
v8::HandleScope handleScope(m_isolate);
int contextId = it->second->executionContextId();
InspectedContext* inspected = m_inspector->getContext(contextId);
if (!inspected) {
return Response::Error("Cannot retrive script context");
return Response::ServerError("Cannot retrive script context");
}
v8::Context::Scope contextScope(inspected->context());
v8::MicrotasksScope microtasks(m_isolate,
......@@ -729,23 +736,23 @@ Response V8DebuggerAgentImpl::getPossibleBreakpoints(
}
(*locations)->emplace_back(std::move(breakLocation));
}
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::continueToLocation(
std::unique_ptr<protocol::Debugger::Location> location,
Maybe<String16> targetCallFrames) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
if (it == m_scripts.end()) {
return Response::Error("Cannot continue to specified location");
return Response::ServerError("Cannot continue to specified location");
}
V8DebuggerScript* script = it->second.get();
int contextId = script->executionContextId();
InspectedContext* inspected = m_inspector->getContext(contextId);
if (!inspected)
return Response::Error("Cannot continue to specified location");
return Response::ServerError("Cannot continue to specified location");
v8::HandleScope handleScope(m_isolate);
v8::Context::Scope contextScope(inspected->context());
return m_debugger->continueToLocation(
......@@ -759,7 +766,7 @@ Response V8DebuggerAgentImpl::getStackTrace(
std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
bool isOk = false;
int64_t id = inStackTraceId->getId().toInteger64(&isOk);
if (!isOk) return Response::Error("Invalid stack trace id");
if (!isOk) return Response::ServerError("Invalid stack trace id");
V8DebuggerId debuggerId;
if (inStackTraceId->hasDebuggerId()) {
......@@ -767,19 +774,20 @@ Response V8DebuggerAgentImpl::getStackTrace(
} else {
debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
}
if (!debuggerId.isValid()) return Response::Error("Invalid stack trace id");
if (!debuggerId.isValid())
return Response::ServerError("Invalid stack trace id");
V8StackTraceId v8StackTraceId(id, debuggerId.pair());
if (v8StackTraceId.IsInvalid())
return Response::Error("Invalid stack trace id");
return Response::ServerError("Invalid stack trace id");
auto stack =
m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
if (!stack) {
return Response::Error("Stack trace with given id is not found");
return Response::ServerError("Stack trace with given id is not found");
}
*outStackTrace = stack->buildInspectorObject(
m_debugger, m_debugger->maxAsyncCallChainDepth());
return Response::OK();
return Response::Success();
}
bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
......@@ -879,13 +887,13 @@ Response V8DebuggerAgentImpl::searchInContent(
v8::HandleScope handles(m_isolate);
ScriptsMap::iterator it = m_scripts.find(scriptId);
if (it == m_scripts.end())
return Response::Error("No script for id: " + scriptId);
return Response::ServerError("No script for id: " + scriptId.utf8());
*results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>(
searchInTextByLinesImpl(m_session, it->second->source(0), query,
optionalCaseSensitive.fromMaybe(false),
optionalIsRegex.fromMaybe(false)));
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setScriptSource(
......@@ -895,11 +903,11 @@ Response V8DebuggerAgentImpl::setScriptSource(
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
ScriptsMap::iterator it = m_scripts.find(scriptId);
if (it == m_scripts.end()) {
return Response::Error("No script with given id found");
return Response::ServerError("No script with given id found");
}
int contextId = it->second->executionContextId();
InspectedContext* inspected = m_inspector->getContext(contextId);
......@@ -922,17 +930,17 @@ Response V8DebuggerAgentImpl::setScriptSource(
.setColumnNumber(result.column_number != -1 ? result.column_number
: 0)
.build();
return Response::OK();
return Response::Success();
} else {
*stackChanged = result.stack_changed;
}
std::unique_ptr<Array<CallFrame>> callFrames;
Response response = currentCallFrames(&callFrames);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
*newCallFrames = std::move(callFrames);
*asyncStackTrace = currentAsyncStackTrace();
*asyncStackTraceId = currentExternalStackTrace();
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::restartFrame(
......@@ -940,52 +948,52 @@ Response V8DebuggerAgentImpl::restartFrame(
std::unique_ptr<Array<CallFrame>>* newCallFrames,
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_session, callFrameId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::Error("Could not find call frame with given id");
return Response::ServerError("Could not find call frame with given id");
}
if (!it->Restart()) {
return Response::InternalError();
}
response = currentCallFrames(newCallFrames);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
*asyncStackTrace = currentAsyncStackTrace();
*asyncStackTraceId = currentExternalStackTrace();
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::getScriptSource(
const String16& scriptId, String16* scriptSource,
Maybe<protocol::Binary>* bytecode) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
ScriptsMap::iterator it = m_scripts.find(scriptId);
if (it == m_scripts.end())
return Response::Error("No script for id: " + scriptId);
return Response::ServerError("No script for id: " + scriptId.utf8());
*scriptSource = it->second->source(0);
v8::MemorySpan<const uint8_t> span;
if (it->second->wasmBytecode().To(&span)) {
*bytecode = protocol::Binary::fromSpan(span.data(), span.size());
}
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId,
protocol::Binary* bytecode) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
ScriptsMap::iterator it = m_scripts.find(scriptId);
if (it == m_scripts.end())
return Response::Error("No script for id: " + scriptId);
return Response::ServerError("No script for id: " + scriptId.utf8());
v8::MemorySpan<const uint8_t> span;
if (!it->second->wasmBytecode().To(&span))
return Response::Error("Script with id " + scriptId +
" is not WebAssembly");
return Response::ServerError("Script with id " + scriptId.utf8() +
" is not WebAssembly");
*bytecode = protocol::Binary::fromSpan(span.data(), span.size());
return Response::OK();
return Response::Success();
}
void V8DebuggerAgentImpl::pushBreakDetails(
......@@ -1023,8 +1031,8 @@ void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
}
Response V8DebuggerAgentImpl::pause() {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (isPaused()) return Response::OK();
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
if (isPaused()) return Response::Success();
if (m_debugger->canBreakProgram()) {
m_debugger->interruptAndBreak(m_session->contextGroupId());
} else {
......@@ -1033,48 +1041,48 @@ Response V8DebuggerAgentImpl::pause() {
}
pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
}
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::resume(Maybe<bool> terminateOnResume) {
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
m_session->releaseObjectGroup(kBacktraceObjectGroup);
m_debugger->continueProgram(m_session->contextGroupId(),
terminateOnResume.fromMaybe(false));
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::stepOver() {
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
m_session->releaseObjectGroup(kBacktraceObjectGroup);
m_debugger->stepOverStatement(m_session->contextGroupId());
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::stepInto(Maybe<bool> inBreakOnAsyncCall) {
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
m_session->releaseObjectGroup(kBacktraceObjectGroup);
m_debugger->stepIntoStatement(m_session->contextGroupId(),
inBreakOnAsyncCall.fromMaybe(false));
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::stepOut() {
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
m_session->releaseObjectGroup(kBacktraceObjectGroup);
m_debugger->stepOutOfFunction(m_session->contextGroupId());
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::pauseOnAsyncCall(
std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
// Deprecated, just return OK.
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setPauseOnExceptions(
const String16& stringPauseState) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
v8::debug::ExceptionBreakState pauseState;
if (stringPauseState == "none") {
pauseState = v8::debug::NoBreakOnException;
......@@ -1083,11 +1091,11 @@ Response V8DebuggerAgentImpl::setPauseOnExceptions(
} else if (stringPauseState == "uncaught") {
pauseState = v8::debug::BreakOnUncaughtException;
} else {
return Response::Error("Unknown pause on exceptions mode: " +
stringPauseState);
return Response::ServerError("Unknown pause on exceptions mode: " +
stringPauseState.utf8());
}
setPauseOnExceptionsImpl(pauseState);
return Response::OK();
return Response::Success();
}
void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
......@@ -1105,17 +1113,17 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
std::unique_ptr<RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_session, callFrameId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::Error("Could not find call frame with given id");
return Response::ServerError("Could not find call frame with given id");
}
v8::MaybeLocal<v8::Value> maybeResultValue;
......@@ -1123,7 +1131,7 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
V8InspectorImpl::EvaluateScope evaluateScope(scope);
if (timeout.isJust()) {
response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
}
maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
throwOnSideEffect.fromMaybe(false));
......@@ -1131,7 +1139,7 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
// Re-initialize after running client's code, as it could have destroyed
// context or session.
response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview;
if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
......@@ -1144,20 +1152,20 @@ Response V8DebuggerAgentImpl::setVariableValue(
int scopeNumber, const String16& variableName,
std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
const String16& callFrameId) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_session, callFrameId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
v8::Local<v8::Value> newValue;
response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
&newValue);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
int frameOrdinal = static_cast<int>(scope.frameOrdinal());
auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
if (it->Done()) {
return Response::Error("Could not find call frame with given id");
return Response::ServerError("Could not find call frame with given id");
}
auto scopeIterator = it->GetScopeIterator();
while (!scopeIterator->Done() && scopeNumber > 0) {
......@@ -1165,7 +1173,7 @@ Response V8DebuggerAgentImpl::setVariableValue(
scopeIterator->Advance();
}
if (scopeNumber != 0) {
return Response::Error("Could not find scope with given number");
return Response::ServerError("Could not find scope with given number");
}
if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
......@@ -1173,40 +1181,40 @@ Response V8DebuggerAgentImpl::setVariableValue(
scope.tryCatch().HasCaught()) {
return Response::InternalError();
}
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setReturnValue(
std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
v8::HandleScope handleScope(m_isolate);
auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
if (iterator->Done()) {
return Response::Error("Could not find top call frame");
return Response::ServerError("Could not find top call frame");
}
if (iterator->GetReturnValue().IsEmpty()) {
return Response::Error(
return Response::ServerError(
"Could not update return value at non-return position");
}
InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
v8::Local<v8::Value> newValue;
response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
&newValue);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
v8::debug::SetReturnValue(m_isolate, newValue);
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
if (!enabled() && !m_session->runtimeAgent()->enabled()) {
return Response::Error(kDebuggerNotEnabled);
return Response::ServerError(kDebuggerNotEnabled);
}
m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
m_debugger->setAsyncCallStackDepth(this, depth);
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setBlackboxPatterns(
......@@ -1215,7 +1223,7 @@ Response V8DebuggerAgentImpl::setBlackboxPatterns(
m_blackboxPattern = nullptr;
resetBlackboxedStateCache();
m_state->remove(DebuggerAgentState::blackboxPattern);
return Response::OK();
return Response::Success();
}
String16Builder patternBuilder;
......@@ -1228,19 +1236,20 @@ Response V8DebuggerAgentImpl::setBlackboxPatterns(
patternBuilder.append(')');
String16 pattern = patternBuilder.toString();
Response response = setBlackboxPattern(pattern);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
resetBlackboxedStateCache();
m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
std::unique_ptr<V8Regex> regex(new V8Regex(
m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
if (!regex->isValid())
return Response::Error("Pattern parser error: " + regex->errorMessage());
return Response::ServerError("Pattern parser error: " +
regex->errorMessage().utf8());
m_blackboxPattern = std::move(regex);
return Response::OK();
return Response::Success();
}
void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
......@@ -1255,12 +1264,12 @@ Response V8DebuggerAgentImpl::setBlackboxedRanges(
inPositions) {
auto it = m_scripts.find(scriptId);
if (it == m_scripts.end())
return Response::Error("No script with passed id.");
return Response::ServerError("No script with passed id.");
if (inPositions->empty()) {
m_blackboxedPositions.erase(scriptId);
it->second->resetBlackboxedStateCache();
return Response::OK();
return Response::Success();
}
std::vector<std::pair<int, int>> positions;
......@@ -1268,9 +1277,10 @@ Response V8DebuggerAgentImpl::setBlackboxedRanges(
for (const std::unique_ptr<protocol::Debugger::ScriptPosition>& position :
*inPositions) {
if (position->getLineNumber() < 0)
return Response::Error("Position missing 'line' or 'line' < 0.");
return Response::ServerError("Position missing 'line' or 'line' < 0.");
if (position->getColumnNumber() < 0)
return Response::Error("Position missing 'column' or 'column' < 0.");
return Response::ServerError(
"Position missing 'column' or 'column' < 0.");
positions.push_back(
std::make_pair(position->getLineNumber(), position->getColumnNumber()));
}
......@@ -1280,20 +1290,20 @@ Response V8DebuggerAgentImpl::setBlackboxedRanges(
if (positions[i - 1].first == positions[i].first &&
positions[i - 1].second < positions[i].second)
continue;
return Response::Error(
return Response::ServerError(
"Input positions array is not sorted or contains duplicate values.");
}
m_blackboxedPositions[scriptId] = positions;
it->second->resetBlackboxedStateCache();
return Response::OK();
return Response::Success();
}
Response V8DebuggerAgentImpl::currentCallFrames(
std::unique_ptr<Array<CallFrame>>* result) {
if (!isPaused()) {
*result = std::make_unique<Array<CallFrame>>();
return Response::OK();
return Response::Success();
}
v8::HandleScope handles(m_isolate);
*result = std::make_unique<Array<CallFrame>>();
......@@ -1312,7 +1322,7 @@ Response V8DebuggerAgentImpl::currentCallFrames(
auto scopeIterator = iterator->GetScopeIterator();
Response res =
buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
if (!res.isSuccess()) return res;
if (!res.IsSuccess()) return res;
std::unique_ptr<RemoteObject> protocolReceiver;
if (injectedScript) {
......@@ -1321,7 +1331,7 @@ Response V8DebuggerAgentImpl::currentCallFrames(
res =
injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
WrapMode::kNoPreview, &protocolReceiver);
if (!res.isSuccess()) return res;
if (!res.IsSuccess()) return res;
}
}
if (!protocolReceiver) {
......@@ -1371,12 +1381,12 @@ Response V8DebuggerAgentImpl::currentCallFrames(
std::unique_ptr<RemoteObject> value;
res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
WrapMode::kNoPreview, &value);
if (!res.isSuccess()) return res;
if (!res.IsSuccess()) return res;
frame->setReturnValue(std::move(value));
}
(*result)->emplace_back(std::move(frame));
}
return Response::OK();
return Response::Success();
}
std::unique_ptr<protocol::Runtime::StackTrace>
......@@ -1678,7 +1688,7 @@ void V8DebuggerAgentImpl::didPause(
std::unique_ptr<Array<CallFrame>> protocolCallFrames;
Response response = currentCallFrames(&protocolCallFrames);
if (!response.isSuccess())
if (!response.IsSuccess())
protocolCallFrames = std::make_unique<Array<CallFrame>>();
m_frontend.paused(std::move(protocolCallFrames), breakReason,
......
......@@ -339,8 +339,8 @@ void V8Debugger::terminateExecution(
std::unique_ptr<TerminateExecutionCallback> callback) {
if (m_terminateExecutionCallback) {
if (callback) {
callback->sendFailure(
Response::Error("There is current termination request in progress"));
callback->sendFailure(Response::ServerError(
"There is current termination request in progress"));
}
return;
}
......@@ -394,9 +394,9 @@ Response V8Debugger::continueToLocation(
}
continueProgram(targetContextGroupId);
// TODO(kozyatinskiy): Return actual line and column number.
return Response::OK();
return Response::Success();
} else {
return Response::Error("Cannot continue to specified location");
return Response::ServerError("Cannot continue to specified location");
}
}
......
......@@ -173,7 +173,7 @@ void V8HeapProfilerAgentImpl::restore() {
Response V8HeapProfilerAgentImpl::collectGarbage() {
m_isolate->LowMemoryNotification();
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
......@@ -183,7 +183,7 @@ Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled,
allocationTrackingEnabled);
startTrackingHeapObjectsInternal(allocationTrackingEnabled);
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
......@@ -192,12 +192,12 @@ Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
takeHeapSnapshot(std::move(reportProgress),
std::move(treatGlobalObjectsAsRoots));
stopTrackingHeapObjectsInternal();
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::enable() {
m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::disable() {
......@@ -209,13 +209,13 @@ Response V8HeapProfilerAgentImpl::disable() {
}
m_isolate->GetHeapProfiler()->ClearObjectIds();
m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) {
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) return Response::Error("Cannot access v8 heap profiler");
if (!profiler) return Response::ServerError("Cannot access v8 heap profiler");
std::unique_ptr<HeapSnapshotProgress> progress;
if (reportProgress.fromMaybe(false))
progress.reset(new HeapSnapshotProgress(&m_frontend));
......@@ -223,11 +223,11 @@ Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
GlobalObjectNameResolver resolver(m_session);
const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(
progress.get(), &resolver, treatGlobalObjectsAsRoots.fromMaybe(true));
if (!snapshot) return Response::Error("Failed to take heap snapshot");
if (!snapshot) return Response::ServerError("Failed to take heap snapshot");
HeapSnapshotOutputStream stream(&m_frontend);
snapshot->Serialize(&stream);
const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::getObjectByHeapObjectId(
......@@ -235,36 +235,38 @@ Response V8HeapProfilerAgentImpl::getObjectByHeapObjectId(
std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
bool ok;
int id = heapSnapshotObjectId.toInteger(&ok);
if (!ok) return Response::Error("Invalid heap snapshot object id");
if (!ok) return Response::ServerError("Invalid heap snapshot object id");
v8::HandleScope handles(m_isolate);
v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
if (heapObject.IsEmpty()) return Response::Error("Object is not available");
if (heapObject.IsEmpty())
return Response::ServerError("Object is not available");
if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
return Response::Error("Object is not available");
return Response::ServerError("Object is not available");
*result = m_session->wrapObject(heapObject->CreationContext(), heapObject,
objectGroup.fromMaybe(""), false);
if (!*result) return Response::Error("Object is not available");
return Response::OK();
if (!*result) return Response::ServerError("Object is not available");
return Response::Success();
}
Response V8HeapProfilerAgentImpl::addInspectedHeapObject(
const String16& inspectedHeapObjectId) {
bool ok;
int id = inspectedHeapObjectId.toInteger(&ok);
if (!ok) return Response::Error("Invalid heap snapshot object id");
if (!ok) return Response::ServerError("Invalid heap snapshot object id");
v8::HandleScope handles(m_isolate);
v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
if (heapObject.IsEmpty()) return Response::Error("Object is not available");
if (heapObject.IsEmpty())
return Response::ServerError("Object is not available");
if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
return Response::Error("Object is not available");
return Response::ServerError("Object is not available");
m_session->addInspectedObject(
std::unique_ptr<InspectableHeapObject>(new InspectableHeapObject(id)));
return Response::OK();
return Response::Success();
}
Response V8HeapProfilerAgentImpl::getHeapObjectId(
......@@ -274,12 +276,12 @@ Response V8HeapProfilerAgentImpl::getHeapObjectId(
v8::Local<v8::Context> context;
Response response =
m_session->unwrapObject(objectId, &value, &context, nullptr);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (value->IsUndefined()) return Response::InternalError();
v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value);
*heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id));
return Response::OK();
return Response::Success();
}
void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() {
......@@ -320,7 +322,7 @@ void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() {
Response V8HeapProfilerAgentImpl::startSampling(
Maybe<double> samplingInterval) {
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
if (!profiler) return Response::Error("Cannot access v8 heap profiler");
if (!profiler) return Response::ServerError("Cannot access v8 heap profiler");
const unsigned defaultSamplingInterval = 1 << 15;
double samplingIntervalValue =
samplingInterval.fromMaybe(defaultSamplingInterval);
......@@ -331,7 +333,7 @@ Response V8HeapProfilerAgentImpl::startSampling(
profiler->StartSamplingHeapProfiler(
static_cast<uint64_t>(samplingIntervalValue), 128,
v8::HeapProfiler::kSamplingForceGC);
return Response::OK();
return Response::Success();
}
namespace {
......@@ -367,7 +369,7 @@ buildSampingHeapProfileNode(v8::Isolate* isolate,
Response V8HeapProfilerAgentImpl::stopSampling(
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
Response result = getSamplingProfile(profile);
if (result.isSuccess()) {
if (result.IsSuccess()) {
m_isolate->GetHeapProfiler()->StopSamplingHeapProfiler();
m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
false);
......@@ -383,7 +385,7 @@ Response V8HeapProfilerAgentImpl::getSamplingProfile(
std::unique_ptr<v8::AllocationProfile> v8Profile(
profiler->GetAllocationProfile());
if (!v8Profile)
return Response::Error("V8 sampling heap profiler was not started.");
return Response::ServerError("V8 sampling heap profiler was not started.");
v8::AllocationProfile::Node* root = v8Profile->GetRootNode();
auto samples = std::make_unique<
protocol::Array<protocol::HeapProfiler::SamplingHeapProfileSample>>();
......@@ -399,7 +401,7 @@ Response V8HeapProfilerAgentImpl::getSamplingProfile(
.setHead(buildSampingHeapProfileNode(m_isolate, root))
.setSamples(std::move(samples))
.build();
return Response::OK();
return Response::Success();
}
} // namespace v8_inspector
......@@ -467,12 +467,12 @@ class V8InspectorImpl::EvaluateScope::TerminateTask : public v8::Task {
protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {
if (m_isolate->IsExecutionTerminating()) {
return protocol::Response::Error("Execution was terminated");
return protocol::Response::ServerError("Execution was terminated");
}
m_cancelToken.reset(new CancelToken());
v8::debug::GetCurrentPlatform()->CallDelayedOnWorkerThread(
std::make_unique<TerminateTask>(m_isolate, m_cancelToken), timeout);
return protocol::Response::OK();
return protocol::Response::Success();
}
} // namespace v8_inspector
......@@ -5,6 +5,7 @@
#include "src/inspector/v8-inspector-session-impl.h"
#include "../../third_party/inspector_protocol/crdtp/cbor.h"
#include "../../third_party/inspector_protocol/crdtp/dispatch.h"
#include "../../third_party/inspector_protocol/crdtp/json.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
......@@ -184,23 +185,24 @@ std::unique_ptr<StringBuffer> V8InspectorSessionImpl::serializeForFrontend(
return StringBufferFrom(std::move(string16));
}
void V8InspectorSessionImpl::sendProtocolResponse(
void V8InspectorSessionImpl::SendProtocolResponse(
int callId, std::unique_ptr<protocol::Serializable> message) {
m_channel->sendResponse(callId, serializeForFrontend(std::move(message)));
}
void V8InspectorSessionImpl::sendProtocolNotification(
void V8InspectorSessionImpl::SendProtocolNotification(
std::unique_ptr<protocol::Serializable> message) {
m_channel->sendNotification(serializeForFrontend(std::move(message)));
}
void V8InspectorSessionImpl::fallThrough(int callId, const String16& method,
void V8InspectorSessionImpl::FallThrough(int callId,
const v8_crdtp::span<uint8_t> method,
v8_crdtp::span<uint8_t> message) {
// There's no other layer to handle the command.
UNREACHABLE();
}
void V8InspectorSessionImpl::flushProtocolNotifications() {
void V8InspectorSessionImpl::FlushProtocolNotifications() {
m_channel->flushProtocolNotifications();
}
......@@ -224,14 +226,15 @@ Response V8InspectorSessionImpl::findInjectedScript(
injectedScript = nullptr;
InspectedContext* context =
m_inspector->getContext(m_contextGroupId, contextId);
if (!context) return Response::Error("Cannot find context with specified id");
if (!context)
return Response::ServerError("Cannot find context with specified id");
injectedScript = context->getInjectedScript(m_sessionId);
if (!injectedScript) {
injectedScript = context->createInjectedScript(m_sessionId);
if (m_customObjectFormatterEnabled)
injectedScript->setCustomObjectFormatterEnabled(true);
}
return Response::OK();
return Response::Success();
}
Response V8InspectorSessionImpl::findInjectedScript(
......@@ -259,8 +262,11 @@ bool V8InspectorSessionImpl::unwrapObject(
String16 objectGroupString;
Response response = unwrapObject(toString16(objectId), object, context,
objectGroup ? &objectGroupString : nullptr);
if (!response.isSuccess()) {
if (error) *error = StringBufferFrom(response.errorMessage());
if (response.IsError()) {
if (error) {
const std::string& msg = response.Message();
*error = StringBufferFrom(String16::fromUTF8(msg.data(), msg.size()));
}
return false;
}
if (objectGroup)
......@@ -274,15 +280,15 @@ Response V8InspectorSessionImpl::unwrapObject(const String16& objectId,
String16* objectGroup) {
std::unique_ptr<RemoteObjectId> remoteId;
Response response = RemoteObjectId::parse(objectId, &remoteId);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
InjectedScript* injectedScript = nullptr;
response = findInjectedScript(remoteId.get(), injectedScript);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
response = injectedScript->findObject(*remoteId, object);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
*context = injectedScript->context()->context();
if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId);
return Response::OK();
return Response::Success();
}
std::unique_ptr<protocol::Runtime::API::RemoteObject>
......@@ -349,19 +355,29 @@ void V8InspectorSessionImpl::dispatchProtocolMessage(
} else {
// We're ignoring the return value of the conversion function
// intentionally. It means the |parsed_message| below will be nullptr.
ConvertToCBOR(message, &converted_cbor);
auto status = ConvertToCBOR(message, &converted_cbor);
if (!status.ok()) {
m_channel->sendNotification(
serializeForFrontend(v8_crdtp::CreateErrorNotification(
v8_crdtp::DispatchResponse::ParseError(status.ToASCIIString()))));
return;
}
cbor = SpanFrom(converted_cbor);
}
int callId;
std::unique_ptr<protocol::Value> parsed_message =
protocol::Value::parseBinary(cbor.data(), cbor.size());
String16 method;
if (m_dispatcher.parseCommand(parsed_message.get(), &callId, &method)) {
// Pass empty string instead of the actual message to save on a conversion.
// We're allowed to do so because fall-through is not implemented.
m_dispatcher.dispatch(callId, method, std::move(parsed_message),
v8_crdtp::span<uint8_t>());
v8_crdtp::Dispatchable dispatchable(cbor);
if (!dispatchable.ok()) {
if (dispatchable.HasCallId()) {
m_channel->sendNotification(serializeForFrontend(
v8_crdtp::CreateErrorNotification(dispatchable.DispatchError())));
} else {
m_channel->sendResponse(
dispatchable.CallId(),
serializeForFrontend(v8_crdtp::CreateErrorResponse(
dispatchable.CallId(), dispatchable.DispatchError())));
}
return;
}
m_dispatcher.Dispatch(dispatchable).Run();
}
std::vector<uint8_t> V8InspectorSessionImpl::state() {
......
......@@ -100,13 +100,13 @@ class V8InspectorSessionImpl : public V8InspectorSession,
protocol::DictionaryValue* agentState(const String16& name);
// protocol::FrontendChannel implementation.
void sendProtocolResponse(
void SendProtocolResponse(
int callId, std::unique_ptr<protocol::Serializable> message) override;
void sendProtocolNotification(
void SendProtocolNotification(
std::unique_ptr<protocol::Serializable> message) override;
void fallThrough(int callId, const String16& method,
void FallThrough(int callId, v8_crdtp::span<uint8_t> method,
v8_crdtp::span<uint8_t> message) override;
void flushProtocolNotifications() override;
void FlushProtocolNotifications() override;
std::unique_ptr<StringBuffer> serializeForFrontend(
std::unique_ptr<protocol::Serializable> message);
......
......@@ -220,14 +220,14 @@ void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
}
Response V8ProfilerAgentImpl::enable() {
if (m_enabled) return Response::OK();
if (m_enabled) return Response::Success();
m_enabled = true;
m_state->setBoolean(ProfilerAgentState::profilerEnabled, true);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::disable() {
if (!m_enabled) return Response::OK();
if (!m_enabled) return Response::Success();
for (size_t i = m_startedProfiles.size(); i > 0; --i)
stopProfiling(m_startedProfiles[i - 1].m_id, false);
m_startedProfiles.clear();
......@@ -236,15 +236,16 @@ Response V8ProfilerAgentImpl::disable() {
DCHECK(!m_profiler);
m_enabled = false;
m_state->setBoolean(ProfilerAgentState::profilerEnabled, false);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::setSamplingInterval(int interval) {
if (m_profiler) {
return Response::Error("Cannot change sampling interval when profiling.");
return Response::ServerError(
"Cannot change sampling interval when profiling.");
}
m_state->setInteger(ProfilerAgentState::samplingInterval, interval);
return Response::OK();
return Response::Success();
}
void V8ProfilerAgentImpl::restore() {
......@@ -272,36 +273,36 @@ void V8ProfilerAgentImpl::restore() {
}
Response V8ProfilerAgentImpl::start() {
if (m_recordingCPUProfile) return Response::OK();
if (!m_enabled) return Response::Error("Profiler is not enabled");
if (m_recordingCPUProfile) return Response::Success();
if (!m_enabled) return Response::ServerError("Profiler is not enabled");
m_recordingCPUProfile = true;
m_frontendInitiatedProfileId = nextProfileId();
startProfiling(m_frontendInitiatedProfileId);
m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::stop(
std::unique_ptr<protocol::Profiler::Profile>* profile) {
if (!m_recordingCPUProfile) {
return Response::Error("No recording profiles found");
return Response::ServerError("No recording profiles found");
}
m_recordingCPUProfile = false;
std::unique_ptr<protocol::Profiler::Profile> cpuProfile =
stopProfiling(m_frontendInitiatedProfileId, !!profile);
if (profile) {
*profile = std::move(cpuProfile);
if (!profile->get()) return Response::Error("Profile is not found");
if (!profile->get()) return Response::ServerError("Profile is not found");
}
m_frontendInitiatedProfileId = String16();
m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::startPreciseCoverage(
Maybe<bool> callCount, Maybe<bool> detailed,
Maybe<bool> allowTriggeredUpdates, double* out_timestamp) {
if (!m_enabled) return Response::Error("Profiler is not enabled");
if (!m_enabled) return Response::ServerError("Profiler is not enabled");
*out_timestamp =
v8::base::TimeTicks::HighResolutionNow().since_origin().InSecondsF();
bool callCountValue = callCount.fromMaybe(false);
......@@ -324,17 +325,17 @@ Response V8ProfilerAgentImpl::startPreciseCoverage(
? (detailedValue ? Mode::kBlockCount : Mode::kPreciseCount)
: (detailedValue ? Mode::kBlockBinary : Mode::kPreciseBinary);
C::SelectMode(m_isolate, mode);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::stopPreciseCoverage() {
if (!m_enabled) return Response::Error("Profiler is not enabled");
if (!m_enabled) return Response::ServerError("Profiler is not enabled");
m_state->setBoolean(ProfilerAgentState::preciseCoverageStarted, false);
m_state->setBoolean(ProfilerAgentState::preciseCoverageCallCount, false);
m_state->setBoolean(ProfilerAgentState::preciseCoverageDetailed, false);
v8::debug::Coverage::SelectMode(m_isolate,
v8::debug::CoverageMode::kBestEffort);
return Response::OK();
return Response::Success();
}
namespace {
......@@ -402,7 +403,7 @@ Response coverageToProtocol(
.build());
}
*out_result = std::move(result);
return Response::OK();
return Response::Success();
}
} // anonymous namespace
......@@ -412,7 +413,7 @@ Response V8ProfilerAgentImpl::takePreciseCoverage(
double* out_timestamp) {
if (!m_state->booleanProperty(ProfilerAgentState::preciseCoverageStarted,
false)) {
return Response::Error("Precise coverage has not been started.");
return Response::ServerError("Precise coverage has not been started.");
}
v8::HandleScope handle_scope(m_isolate);
v8::debug::Coverage coverage = v8::debug::Coverage::CollectPrecise(m_isolate);
......@@ -500,14 +501,14 @@ Response V8ProfilerAgentImpl::startTypeProfile() {
m_state->setBoolean(ProfilerAgentState::typeProfileStarted, true);
v8::debug::TypeProfile::SelectMode(m_isolate,
v8::debug::TypeProfileMode::kCollect);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::stopTypeProfile() {
m_state->setBoolean(ProfilerAgentState::typeProfileStarted, false);
v8::debug::TypeProfile::SelectMode(m_isolate,
v8::debug::TypeProfileMode::kNone);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::takeTypeProfile(
......@@ -515,37 +516,38 @@ Response V8ProfilerAgentImpl::takeTypeProfile(
out_result) {
if (!m_state->booleanProperty(ProfilerAgentState::typeProfileStarted,
false)) {
return Response::Error("Type profile has not been started.");
return Response::ServerError("Type profile has not been started.");
}
v8::HandleScope handle_scope(m_isolate);
v8::debug::TypeProfile type_profile =
v8::debug::TypeProfile::Collect(m_isolate);
*out_result = typeProfileToProtocol(m_session->inspector(), type_profile);
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::enableRuntimeCallStats() {
if (m_counters)
return Response::Error("RuntimeCallStats collection already enabled.");
return Response::ServerError(
"RuntimeCallStats collection already enabled.");
if (V8Inspector* inspector = v8::debug::GetInspector(m_isolate))
m_counters = inspector->enableCounters();
else
return Response::Error("No inspector found.");
return Response::ServerError("No inspector found.");
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::disableRuntimeCallStats() {
if (m_counters) m_counters.reset();
return Response::OK();
return Response::Success();
}
Response V8ProfilerAgentImpl::getRuntimeCallStats(
std::unique_ptr<protocol::Array<protocol::Profiler::CounterInfo>>*
out_result) {
if (!m_counters)
return Response::Error("RuntimeCallStats collection is not enabled.");
return Response::ServerError("RuntimeCallStats collection is not enabled.");
*out_result =
std::make_unique<protocol::Array<protocol::Profiler::CounterInfo>>();
......@@ -559,7 +561,7 @@ Response V8ProfilerAgentImpl::getRuntimeCallStats(
.build());
}
return Response::OK();
return Response::Success();
}
String16 V8ProfilerAgentImpl::nextProfileId() {
......
......@@ -99,7 +99,7 @@ bool wrapEvaluateResultAsync(InjectedScript* injectedScript,
Response response = injectedScript->wrapEvaluateResult(
maybeResultValue, tryCatch, objectGroup, wrapMode, &result,
&exceptionDetails);
if (response.isSuccess()) {
if (response.IsSuccess()) {
callback->sendSuccess(std::move(result), std::move(exceptionDetails));
return true;
}
......@@ -128,7 +128,7 @@ void innerCallFunctionOn(
v8::Local<v8::Value> argumentValue;
Response response = scope.injectedScript()->resolveCallArgument(
(*arguments)[i].get(), &argumentValue);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -154,7 +154,7 @@ void innerCallFunctionOn(
// Re-initialize after running client's code, as it could have destroyed
// context or session.
Response response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -169,8 +169,8 @@ void innerCallFunctionOn(
v8::Local<v8::Value> functionValue;
if (!maybeFunctionValue.ToLocal(&functionValue) ||
!functionValue->IsFunction()) {
callback->sendFailure(
Response::Error("Given expression does not evaluate to a function"));
callback->sendFailure(Response::ServerError(
"Given expression does not evaluate to a function"));
return;
}
......@@ -184,7 +184,7 @@ void innerCallFunctionOn(
// Re-initialize after running client's code, as it could have destroyed
// context or session.
response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -211,10 +211,10 @@ Response ensureContext(V8InspectorImpl* inspector, int contextGroupId,
v8::Local<v8::Context> defaultContext =
inspector->client()->ensureDefaultContextInGroup(contextGroupId);
if (defaultContext.IsEmpty())
return Response::Error("Cannot find default execution context");
return Response::ServerError("Cannot find default execution context");
*contextId = InspectedContext::contextId(defaultContext);
}
return Response::OK();
return Response::Success();
}
} // namespace
......@@ -243,14 +243,14 @@ void V8RuntimeAgentImpl::evaluate(
int contextId = 0;
Response response = ensureContext(m_inspector, m_session->contextGroupId(),
std::move(executionContextId), &contextId);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
InjectedScript::ContextScope scope(m_session, contextId);
response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -269,7 +269,7 @@ void V8RuntimeAgentImpl::evaluate(
V8InspectorImpl::EvaluateScope evaluateScope(scope);
if (timeout.isJust()) {
response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -292,7 +292,7 @@ void V8RuntimeAgentImpl::evaluate(
// Re-initialize after running client's code, as it could have destroyed
// context or session.
response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -320,13 +320,13 @@ void V8RuntimeAgentImpl::awaitPromise(
std::unique_ptr<AwaitPromiseCallback> callback) {
InjectedScript::ObjectScope scope(m_session, promiseObjectId);
Response response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
if (!scope.object()->IsPromise()) {
callback->sendFailure(
Response::Error("Could not find promise with given id"));
Response::ServerError("Could not find promise with given id"));
return;
}
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
......@@ -346,12 +346,12 @@ void V8RuntimeAgentImpl::callFunctionOn(
Maybe<int> executionContextId, Maybe<String16> objectGroup,
std::unique_ptr<CallFunctionOnCallback> callback) {
if (objectId.isJust() && executionContextId.isJust()) {
callback->sendFailure(Response::Error(
callback->sendFailure(Response::ServerError(
"ObjectId must not be specified together with executionContextId"));
return;
}
if (!objectId.isJust() && !executionContextId.isJust()) {
callback->sendFailure(Response::Error(
callback->sendFailure(Response::ServerError(
"Either ObjectId or executionContextId must be specified"));
return;
}
......@@ -361,7 +361,7 @@ void V8RuntimeAgentImpl::callFunctionOn(
if (objectId.isJust()) {
InjectedScript::ObjectScope scope(m_session, objectId.fromJust());
Response response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -377,13 +377,13 @@ void V8RuntimeAgentImpl::callFunctionOn(
Response response =
ensureContext(m_inspector, m_session->contextGroupId(),
std::move(executionContextId.fromJust()), &contextId);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
InjectedScript::ContextScope scope(m_session, contextId);
response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -410,13 +410,13 @@ Response V8RuntimeAgentImpl::getProperties(
InjectedScript::ObjectScope scope(m_session, objectId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
scope.ignoreExceptionsAndMuteConsole();
v8::MicrotasksScope microtasks_scope(m_inspector->isolate(),
v8::MicrotasksScope::kRunMicrotasks);
if (!scope.object()->IsObject())
return Response::Error("Value with given id is not an object");
return Response::ServerError("Value with given id is not an object");
v8::Local<v8::Object> object = scope.object().As<v8::Object>();
response = scope.injectedScript()->getProperties(
......@@ -425,9 +425,9 @@ Response V8RuntimeAgentImpl::getProperties(
generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview,
result, exceptionDetails);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (exceptionDetails->isJust() || accessorPropertiesOnly.fromMaybe(false))
return Response::OK();
return Response::Success();
std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>
internalPropertiesProtocolArray;
std::unique_ptr<protocol::Array<PrivatePropertyDescriptor>>
......@@ -435,68 +435,69 @@ Response V8RuntimeAgentImpl::getProperties(
response = scope.injectedScript()->getInternalAndPrivateProperties(
object, scope.objectGroupName(), &internalPropertiesProtocolArray,
&privatePropertiesProtocolArray);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (!internalPropertiesProtocolArray->empty())
*internalProperties = std::move(internalPropertiesProtocolArray);
if (!privatePropertiesProtocolArray->empty())
*privateProperties = std::move(privatePropertiesProtocolArray);
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::releaseObject(const String16& objectId) {
InjectedScript::ObjectScope scope(m_session, objectId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
scope.injectedScript()->releaseObject(objectId);
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::releaseObjectGroup(const String16& objectGroup) {
m_session->releaseObjectGroup(objectGroup);
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::runIfWaitingForDebugger() {
m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId());
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(bool enabled) {
m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled,
enabled);
if (!m_enabled) return Response::Error("Runtime agent is not enabled");
if (!m_enabled) return Response::ServerError("Runtime agent is not enabled");
m_session->setCustomObjectFormatterEnabled(enabled);
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::setMaxCallStackSizeToCapture(int size) {
if (size < 0) {
return Response::Error("maxCallStackSizeToCapture should be non-negative");
return Response::ServerError(
"maxCallStackSizeToCapture should be non-negative");
}
V8StackTraceImpl::maxCallStackSizeToCapture = size;
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::discardConsoleEntries() {
V8ConsoleMessageStorage* storage =
m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
storage->clear();
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::compileScript(
const String16& expression, const String16& sourceURL, bool persistScript,
Maybe<int> executionContextId, Maybe<String16>* scriptId,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
if (!m_enabled) return Response::Error("Runtime agent is not enabled");
if (!m_enabled) return Response::ServerError("Runtime agent is not enabled");
int contextId = 0;
Response response = ensureContext(m_inspector, m_session->contextGroupId(),
std::move(executionContextId), &contextId);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
InjectedScript::ContextScope scope(m_session, contextId);
response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents();
v8::Local<v8::Script> script;
......@@ -507,14 +508,14 @@ Response V8RuntimeAgentImpl::compileScript(
if (scope.tryCatch().HasCaught()) {
response = scope.injectedScript()->createExceptionDetails(
scope.tryCatch(), String16(), exceptionDetails);
if (!response.isSuccess()) return response;
return Response::OK();
if (!response.IsSuccess()) return response;
return Response::Success();
} else {
return Response::Error("Script compilation failed");
return Response::ServerError("Script compilation failed");
}
}
if (!persistScript) return Response::OK();
if (!persistScript) return Response::Success();
String16 scriptValueId =
String16::fromInteger(script->GetUnboundScript()->GetId());
......@@ -522,7 +523,7 @@ Response V8RuntimeAgentImpl::compileScript(
new v8::Global<v8::Script>(m_inspector->isolate(), script));
m_compiledScripts[scriptValueId] = std::move(global);
*scriptId = scriptValueId;
return Response::OK();
return Response::Success();
}
void V8RuntimeAgentImpl::runScript(
......@@ -532,27 +533,28 @@ void V8RuntimeAgentImpl::runScript(
Maybe<bool> generatePreview, Maybe<bool> awaitPromise,
std::unique_ptr<RunScriptCallback> callback) {
if (!m_enabled) {
callback->sendFailure(Response::Error("Runtime agent is not enabled"));
callback->sendFailure(
Response::ServerError("Runtime agent is not enabled"));
return;
}
auto it = m_compiledScripts.find(scriptId);
if (it == m_compiledScripts.end()) {
callback->sendFailure(Response::Error("No script with given id"));
callback->sendFailure(Response::ServerError("No script with given id"));
return;
}
int contextId = 0;
Response response = ensureContext(m_inspector, m_session->contextGroupId(),
std::move(executionContextId), &contextId);
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
InjectedScript::ContextScope scope(m_session, contextId);
response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -563,7 +565,7 @@ void V8RuntimeAgentImpl::runScript(
m_compiledScripts.erase(it);
v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate());
if (script.IsEmpty()) {
callback->sendFailure(Response::Error("Script execution failed"));
callback->sendFailure(Response::ServerError("Script execution failed"));
return;
}
......@@ -579,7 +581,7 @@ void V8RuntimeAgentImpl::runScript(
// Re-initialize after running client's code, as it could have destroyed
// context or session.
response = scope.initialize();
if (!response.isSuccess()) {
if (!response.IsSuccess()) {
callback->sendFailure(response);
return;
}
......@@ -604,9 +606,9 @@ Response V8RuntimeAgentImpl::queryObjects(
std::unique_ptr<protocol::Runtime::RemoteObject>* objects) {
InjectedScript::ObjectScope scope(m_session, prototypeObjectId);
Response response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
if (!scope.object()->IsObject()) {
return Response::Error("Prototype should be instance of Object");
return Response::ServerError("Prototype should be instance of Object");
}
v8::Local<v8::Array> resultArray = m_inspector->debugger()->queryObjects(
scope.context(), v8::Local<v8::Object>::Cast(scope.object()));
......@@ -621,11 +623,11 @@ Response V8RuntimeAgentImpl::globalLexicalScopeNames(
int contextId = 0;
Response response = ensureContext(m_inspector, m_session->contextGroupId(),
std::move(executionContextId), &contextId);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
InjectedScript::ContextScope scope(m_session, contextId);
response = scope.initialize();
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
v8::PersistentValueVector<v8::String> names(m_inspector->isolate());
v8::debug::GlobalLexicalScopeNames(scope.context(), &names);
......@@ -634,14 +636,14 @@ Response V8RuntimeAgentImpl::globalLexicalScopeNames(
(*outNames)->emplace_back(
toProtocolString(m_inspector->isolate(), names.Get(i)));
}
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::getIsolateId(String16* outIsolateId) {
char buf[40];
std::snprintf(buf, sizeof(buf), "%" PRIx64, m_inspector->isolateId());
*outIsolateId = buf;
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::getHeapUsage(double* out_usedSize,
......@@ -650,7 +652,7 @@ Response V8RuntimeAgentImpl::getHeapUsage(double* out_usedSize,
m_inspector->isolate()->GetHeapStatistics(&stats);
*out_usedSize = stats.used_heap_size();
*out_totalSize = stats.total_heap_size();
return Response::OK();
return Response::Success();
}
void V8RuntimeAgentImpl::terminateExecution(
......@@ -666,25 +668,25 @@ Response V8RuntimeAgentImpl::addBinding(const String16& name,
}
protocol::DictionaryValue* bindings =
m_state->getObject(V8RuntimeAgentImplState::bindings);
if (bindings->booleanProperty(name, false)) return Response::OK();
if (bindings->booleanProperty(name, false)) return Response::Success();
if (executionContextId.isJust()) {
int contextId = executionContextId.fromJust();
InspectedContext* context =
m_inspector->getContext(m_session->contextGroupId(), contextId);
if (!context) {
return Response::Error(
return Response::ServerError(
"Cannot find execution context with given executionContextId");
}
addBinding(context, name);
// false means that we should not add this binding later.
bindings->setBoolean(name, false);
return Response::OK();
return Response::Success();
}
bindings->setBoolean(name, true);
m_inspector->forEachContext(
m_session->contextGroupId(),
[&name, this](InspectedContext* context) { addBinding(context, name); });
return Response::OK();
return Response::Success();
}
void V8RuntimeAgentImpl::bindingCallback(
......@@ -731,9 +733,9 @@ void V8RuntimeAgentImpl::addBinding(InspectedContext* context,
Response V8RuntimeAgentImpl::removeBinding(const String16& name) {
protocol::DictionaryValue* bindings =
m_state->getObject(V8RuntimeAgentImplState::bindings);
if (!bindings) return Response::OK();
if (!bindings) return Response::Success();
bindings->remove(name);
return Response::OK();
return Response::Success();
}
void V8RuntimeAgentImpl::bindingCalled(const String16& name,
......@@ -771,7 +773,7 @@ void V8RuntimeAgentImpl::restore() {
}
Response V8RuntimeAgentImpl::enable() {
if (m_enabled) return Response::OK();
if (m_enabled) return Response::Success();
m_inspector->client()->beginEnsureAllContextsInGroup(
m_session->contextGroupId());
m_enabled = true;
......@@ -783,11 +785,11 @@ Response V8RuntimeAgentImpl::enable() {
for (const auto& message : storage->messages()) {
if (!reportMessage(message.get(), false)) break;
}
return Response::OK();
return Response::Success();
}
Response V8RuntimeAgentImpl::disable() {
if (!m_enabled) return Response::OK();
if (!m_enabled) return Response::Success();
m_enabled = false;
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false);
m_state->remove(V8RuntimeAgentImplState::bindings);
......@@ -799,7 +801,7 @@ Response V8RuntimeAgentImpl::disable() {
if (m_session->debuggerAgent() && !m_session->debuggerAgent()->enabled()) {
m_session->debuggerAgent()->setAsyncCallStackDepth(0);
}
return Response::OK();
return Response::Success();
}
void V8RuntimeAgentImpl::reset() {
......
......@@ -21,7 +21,7 @@ Response V8SchemaAgentImpl::getDomains(
*result =
std::make_unique<std::vector<std::unique_ptr<protocol::Schema::Domain>>>(
m_session->supportedDomainsImpl());
return Response::OK();
return Response::Success();
}
} // namespace v8_inspector
......@@ -42,17 +42,18 @@ V8InternalValueType v8InternalValueTypeFrom(v8::Local<v8::Context> context,
Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value, int maxDepth,
std::unique_ptr<protocol::Value>* result) {
if (!maxDepth) return Response::Error("Object reference chain is too long");
if (!maxDepth)
return Response::ServerError("Object reference chain is too long");
maxDepth--;
if (value->IsNull() || value->IsUndefined()) {
*result = protocol::Value::null();
return Response::OK();
return Response::Success();
}
if (value->IsBoolean()) {
*result =
protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
return Response::OK();
return Response::Success();
}
if (value->IsNumber()) {
double doubleValue = value.As<v8::Number>()->Value();
......@@ -62,16 +63,16 @@ Response toProtocolValue(v8::Local<v8::Context> context,
int intValue = static_cast<int>(doubleValue);
if (intValue == doubleValue) {
*result = protocol::FundamentalValue::create(intValue);
return Response::OK();
return Response::Success();
}
}
*result = protocol::FundamentalValue::create(doubleValue);
return Response::OK();
return Response::Success();
}
if (value->IsString()) {
*result = protocol::StringValue::create(
toProtocolString(context->GetIsolate(), value.As<v8::String>()));
return Response::OK();
return Response::Success();
}
if (value->IsArray()) {
v8::Local<v8::Array> array = value.As<v8::Array>();
......@@ -84,11 +85,11 @@ Response toProtocolValue(v8::Local<v8::Context> context,
return Response::InternalError();
std::unique_ptr<protocol::Value> element;
Response response = toProtocolValue(context, value, maxDepth, &element);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
inspectorArray->pushValue(std::move(element));
}
*result = std::move(inspectorArray);
return Response::OK();
return Response::Success();
}
if (value->IsObject()) {
std::unique_ptr<protocol::DictionaryValue> jsonObject =
......@@ -119,21 +120,21 @@ Response toProtocolValue(v8::Local<v8::Context> context,
std::unique_ptr<protocol::Value> propertyValue;
Response response =
toProtocolValue(context, property, maxDepth, &propertyValue);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
jsonObject->setValue(
toProtocolString(context->GetIsolate(), propertyName),
std::move(propertyValue));
}
*result = std::move(jsonObject);
return Response::OK();
return Response::Success();
}
return Response::Error("Object couldn't be returned by value");
return Response::ServerError("Object couldn't be returned by value");
}
Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
std::unique_ptr<protocol::Value>* result) {
if (value->IsUndefined()) return Response::OK();
if (value->IsUndefined()) return Response::Success();
return toProtocolValue(context, value, 1000, result);
}
......@@ -361,7 +362,7 @@ class PrimitiveValueMirror final : public ValueMirror {
.build();
if (m_value->IsNull())
(*result)->setSubtype(RemoteObject::SubtypeEnum::Null);
return Response::OK();
return Response::Success();
}
void buildEntryPreview(
......@@ -416,7 +417,7 @@ class NumberMirror final : public ValueMirror {
} else {
(*result)->setValue(protocol::FundamentalValue::create(m_value->Value()));
}
return Response::OK();
return Response::Success();
}
void buildPropertyPreview(
v8::Local<v8::Context> context, const String16& name,
......@@ -470,7 +471,7 @@ class BigIntMirror final : public ValueMirror {
.setUnserializableValue(description)
.setDescription(description)
.build();
return Response::OK();
return Response::Success();
}
void buildPropertyPreview(v8::Local<v8::Context> context,
......@@ -513,13 +514,13 @@ class SymbolMirror final : public ValueMirror {
v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) const override {
if (mode == WrapMode::kForceValue) {
return Response::Error("Object couldn't be returned by value");
return Response::ServerError("Object couldn't be returned by value");
}
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Symbol)
.setDescription(descriptionForSymbol(context, m_symbol))
.build();
return Response::OK();
return Response::Success();
}
void buildPropertyPreview(v8::Local<v8::Context> context,
......@@ -576,7 +577,7 @@ class LocationMirror final : public ValueMirror {
.setDescription("Object")
.setValue(std::move(location))
.build();
return Response::OK();
return Response::Success();
}
v8::Local<v8::Value> v8Value() const override { return m_value; }
......@@ -620,7 +621,7 @@ class FunctionMirror final : public ValueMirror {
if (mode == WrapMode::kForceValue) {
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(context, m_value, &protocolValue);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Function)
.setValue(std::move(protocolValue))
......@@ -633,7 +634,7 @@ class FunctionMirror final : public ValueMirror {
.setDescription(descriptionForFunction(context, m_value))
.build();
}
return Response::OK();
return Response::Success();
}
void buildPropertyPreview(
......@@ -881,7 +882,7 @@ class ObjectMirror final : public ValueMirror {
if (mode == WrapMode::kForceValue) {
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(context, m_value, &protocolValue);
if (!response.isSuccess()) return response;
if (!response.IsSuccess()) return response;
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Object)
.setValue(std::move(protocolValue))
......@@ -904,7 +905,7 @@ class ObjectMirror final : public ValueMirror {
(*result)->setPreview(std::move(previewValue));
}
}
return Response::OK();
return Response::Success();
}
void buildObjectPreview(
......
......@@ -14,9 +14,12 @@ v8_source_set("crdtp") {
sources = [
"crdtp/cbor.cc",
"crdtp/cbor.h",
"crdtp/dispatch.cc",
"crdtp/dispatch.h",
"crdtp/error_support.cc",
"crdtp/error_support.h",
"crdtp/export.h",
"crdtp/find_by_first.h",
"crdtp/glue.h",
"crdtp/json.cc",
"crdtp/json.h",
......@@ -24,6 +27,7 @@ v8_source_set("crdtp") {
"crdtp/serializable.cc",
"crdtp/serializable.h",
"crdtp/serializer_traits.h",
"crdtp/span.cc",
"crdtp/span.h",
"crdtp/status.cc",
"crdtp/status.h",
......@@ -47,7 +51,9 @@ v8_source_set("crdtp_platform") {
v8_source_set("crdtp_test") {
sources = [
"crdtp/cbor_test.cc",
"crdtp/dispatch_test.cc",
"crdtp/error_support_test.cc",
"crdtp/find_by_first_test.cc",
"crdtp/glue_test.cc",
"crdtp/json_test.cc",
"crdtp/serializable_test.cc",
......
......@@ -2,7 +2,7 @@ Name: inspector protocol
Short Name: inspector_protocol
URL: https://chromium.googlesource.com/deps/inspector_protocol/
Version: 0
Revision: 81ef742ba3587767fc08652d299df9e9b7051407
Revision: c69cdc36200992d21a17bf4e5c2f3a95b8860ddf
License: BSD
License File: LICENSE
Security Critical: no
......
......@@ -658,19 +658,16 @@ def main():
"Values_h.template",
"Object_h.template",
"ValueConversions_h.template",
"DispatcherBase_h.template",
]
protocol_cpp_templates = [
"Protocol_cpp.template",
"Values_cpp.template",
"Object_cpp.template",
"DispatcherBase_cpp.template",
]
forward_h_templates = [
"Forward_h.template",
"FrontendChannel_h.template",
]
base_string_adapter_h_templates = [
......
// Copyright 2020 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.
#include "dispatch.h"
#include <cassert>
#include "cbor.h"
#include "error_support.h"
#include "find_by_first.h"
#include "frontend_channel.h"
namespace v8_crdtp {
// =============================================================================
// DispatchResponse - Error status and chaining / fall through
// =============================================================================
// static
DispatchResponse DispatchResponse::Success() {
DispatchResponse result;
result.code_ = DispatchCode::SUCCESS;
return result;
}
// static
DispatchResponse DispatchResponse::FallThrough() {
DispatchResponse result;
result.code_ = DispatchCode::FALL_THROUGH;
return result;
}
// static
DispatchResponse DispatchResponse::ParseError(std::string message) {
DispatchResponse result;
result.code_ = DispatchCode::PARSE_ERROR;
result.message_ = std::move(message);
return result;
}
// static
DispatchResponse DispatchResponse::InvalidRequest(std::string message) {
DispatchResponse result;
result.code_ = DispatchCode::INVALID_REQUEST;
result.message_ = std::move(message);
return result;
}
// static
DispatchResponse DispatchResponse::MethodNotFound(std::string message) {
DispatchResponse result;
result.code_ = DispatchCode::METHOD_NOT_FOUND;
result.message_ = std::move(message);
return result;
}
// static
DispatchResponse DispatchResponse::InvalidParams(std::string message) {
DispatchResponse result;
result.code_ = DispatchCode::INVALID_PARAMS;
result.message_ = std::move(message);
return result;
}
// static
DispatchResponse DispatchResponse::InternalError() {
DispatchResponse result;
result.code_ = DispatchCode::INTERNAL_ERROR;
result.message_ = "Internal error";
return result;
}
// static
DispatchResponse DispatchResponse::ServerError(std::string message) {
DispatchResponse result;
result.code_ = DispatchCode::SERVER_ERROR;
result.message_ = std::move(message);
return result;
}
// =============================================================================
// Dispatchable - a shallow parser for CBOR encoded DevTools messages
// =============================================================================
namespace {
constexpr size_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t);
} // namespace
Dispatchable::Dispatchable(span<uint8_t> serialized) : serialized_(serialized) {
Status s = cbor::CheckCBORMessage(serialized);
if (!s.ok()) {
status_ = {Error::MESSAGE_MUST_BE_AN_OBJECT, s.pos};
return;
}
cbor::CBORTokenizer tokenizer(serialized);
if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) {
status_ = tokenizer.Status();
return;
}
// We checked for the envelope start byte above, so the tokenizer
// must agree here, since it's not an error.
assert(tokenizer.TokenTag() == cbor::CBORTokenTag::ENVELOPE);
// Before we enter the envelope, we save the position that we
// expect to see after we're done parsing the envelope contents.
// This way we can compare and produce an error if the contents
// didn't fit exactly into the envelope length.
const size_t pos_past_envelope = tokenizer.Status().pos +
kEncodedEnvelopeHeaderSize +
tokenizer.GetEnvelopeContents().size();
tokenizer.EnterEnvelope();
if (tokenizer.TokenTag() == cbor::CBORTokenTag::ERROR_VALUE) {
status_ = tokenizer.Status();
return;
}
if (tokenizer.TokenTag() != cbor::CBORTokenTag::MAP_START) {
status_ = {Error::MESSAGE_MUST_BE_AN_OBJECT, tokenizer.Status().pos};
return;
}
assert(tokenizer.TokenTag() == cbor::CBORTokenTag::MAP_START);
tokenizer.Next(); // Now we should be pointed at the map key.
while (tokenizer.TokenTag() != cbor::CBORTokenTag::STOP) {
switch (tokenizer.TokenTag()) {
case cbor::CBORTokenTag::DONE:
status_ =
Status{Error::CBOR_UNEXPECTED_EOF_IN_MAP, tokenizer.Status().pos};
return;
case cbor::CBORTokenTag::ERROR_VALUE:
status_ = tokenizer.Status();
return;
case cbor::CBORTokenTag::STRING8:
if (!MaybeParseProperty(&tokenizer))
return;
break;
default:
// We require the top-level keys to be UTF8 (US-ASCII in practice).
status_ = Status{Error::CBOR_INVALID_MAP_KEY, tokenizer.Status().pos};
return;
}
}
tokenizer.Next();
if (!has_call_id_) {
status_ = Status{Error::MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY,
tokenizer.Status().pos};
return;
}
if (method_.empty()) {
status_ = Status{Error::MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY,
tokenizer.Status().pos};
return;
}
// The contents of the envelope parsed OK, now check that we're at
// the expected position.
if (pos_past_envelope != tokenizer.Status().pos) {
status_ = Status{Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH,
tokenizer.Status().pos};
return;
}
if (tokenizer.TokenTag() != cbor::CBORTokenTag::DONE) {
status_ = Status{Error::CBOR_TRAILING_JUNK, tokenizer.Status().pos};
return;
}
}
bool Dispatchable::ok() const {
return status_.ok();
}
DispatchResponse Dispatchable::DispatchError() const {
// TODO(johannes): Replace with DCHECK / similar?
if (status_.ok())
return DispatchResponse::Success();
if (status_.IsMessageError())
return DispatchResponse::InvalidRequest(status_.Message());
return DispatchResponse::ParseError(status_.ToASCIIString());
}
bool Dispatchable::MaybeParseProperty(cbor::CBORTokenizer* tokenizer) {
span<uint8_t> property_name = tokenizer->GetString8();
if (SpanEquals(SpanFrom("id"), property_name))
return MaybeParseCallId(tokenizer);
if (SpanEquals(SpanFrom("method"), property_name))
return MaybeParseMethod(tokenizer);
if (SpanEquals(SpanFrom("params"), property_name))
return MaybeParseParams(tokenizer);
if (SpanEquals(SpanFrom("sessionId"), property_name))
return MaybeParseSessionId(tokenizer);
status_ =
Status{Error::MESSAGE_HAS_UNKNOWN_PROPERTY, tokenizer->Status().pos};
return false;
}
bool Dispatchable::MaybeParseCallId(cbor::CBORTokenizer* tokenizer) {
if (has_call_id_) {
status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
return false;
}
tokenizer->Next();
if (tokenizer->TokenTag() != cbor::CBORTokenTag::INT32) {
status_ = Status{Error::MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY,
tokenizer->Status().pos};
return false;
}
call_id_ = tokenizer->GetInt32();
has_call_id_ = true;
tokenizer->Next();
return true;
}
bool Dispatchable::MaybeParseMethod(cbor::CBORTokenizer* tokenizer) {
if (!method_.empty()) {
status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
return false;
}
tokenizer->Next();
if (tokenizer->TokenTag() != cbor::CBORTokenTag::STRING8) {
status_ = Status{Error::MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY,
tokenizer->Status().pos};
return false;
}
method_ = tokenizer->GetString8();
tokenizer->Next();
return true;
}
bool Dispatchable::MaybeParseParams(cbor::CBORTokenizer* tokenizer) {
if (params_seen_) {
status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
return false;
}
params_seen_ = true;
tokenizer->Next();
if (tokenizer->TokenTag() == cbor::CBORTokenTag::NULL_VALUE) {
tokenizer->Next();
return true;
}
if (tokenizer->TokenTag() != cbor::CBORTokenTag::ENVELOPE) {
status_ = Status{Error::MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY,
tokenizer->Status().pos};
return false;
}
params_ = tokenizer->GetEnvelope();
tokenizer->Next();
return true;
}
bool Dispatchable::MaybeParseSessionId(cbor::CBORTokenizer* tokenizer) {
if (!session_id_.empty()) {
status_ = Status{Error::CBOR_DUPLICATE_MAP_KEY, tokenizer->Status().pos};
return false;
}
tokenizer->Next();
if (tokenizer->TokenTag() != cbor::CBORTokenTag::STRING8) {
status_ = Status{Error::MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY,
tokenizer->Status().pos};
return false;
}
session_id_ = tokenizer->GetString8();
tokenizer->Next();
return true;
}
namespace {
class ProtocolError : public Serializable {
public:
explicit ProtocolError(DispatchResponse dispatch_response)
: dispatch_response_(std::move(dispatch_response)) {}
void AppendSerialized(std::vector<uint8_t>* out) const override {
Status status;
std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(out, &status);
encoder->HandleMapBegin();
if (has_call_id_) {
encoder->HandleString8(SpanFrom("id"));
encoder->HandleInt32(call_id_);
}
encoder->HandleString8(SpanFrom("error"));
encoder->HandleMapBegin();
encoder->HandleString8(SpanFrom("code"));
encoder->HandleInt32(static_cast<int32_t>(dispatch_response_.Code()));
encoder->HandleString8(SpanFrom("message"));
encoder->HandleString8(SpanFrom(dispatch_response_.Message()));
if (!data_.empty()) {
encoder->HandleString8(SpanFrom("data"));
encoder->HandleString8(SpanFrom(data_));
}
encoder->HandleMapEnd();
encoder->HandleMapEnd();
assert(status.ok());
}
void SetCallId(int call_id) {
has_call_id_ = true;
call_id_ = call_id;
}
void SetData(std::string data) { data_ = std::move(data); }
private:
const DispatchResponse dispatch_response_;
std::string data_;
int call_id_ = 0;
bool has_call_id_ = false;
};
} // namespace
// =============================================================================
// Helpers for creating protocol cresponses and notifications.
// =============================================================================
std::unique_ptr<Serializable> CreateErrorResponse(
int call_id,
DispatchResponse dispatch_response,
const ErrorSupport* errors) {
auto protocol_error =
std::make_unique<ProtocolError>(std::move(dispatch_response));
protocol_error->SetCallId(call_id);
if (errors && !errors->Errors().empty()) {
protocol_error->SetData(
std::string(errors->Errors().begin(), errors->Errors().end()));
}
return protocol_error;
}
std::unique_ptr<Serializable> CreateErrorNotification(
DispatchResponse dispatch_response) {
return std::make_unique<ProtocolError>(std::move(dispatch_response));
}
namespace {
class Response : public Serializable {
public:
Response(int call_id, std::unique_ptr<Serializable> params)
: call_id_(call_id), params_(std::move(params)) {}
void AppendSerialized(std::vector<uint8_t>* out) const override {
Status status;
std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(out, &status);
encoder->HandleMapBegin();
encoder->HandleString8(SpanFrom("id"));
encoder->HandleInt32(call_id_);
encoder->HandleString8(SpanFrom("result"));
if (params_) {
params_->AppendSerialized(out);
} else {
encoder->HandleMapBegin();
encoder->HandleMapEnd();
}
encoder->HandleMapEnd();
assert(status.ok());
}
private:
const int call_id_;
std::unique_ptr<Serializable> params_;
};
class Notification : public Serializable {
public:
Notification(const char* method, std::unique_ptr<Serializable> params)
: method_(method), params_(std::move(params)) {}
void AppendSerialized(std::vector<uint8_t>* out) const override {
Status status;
std::unique_ptr<ParserHandler> encoder = cbor::NewCBOREncoder(out, &status);
encoder->HandleMapBegin();
encoder->HandleString8(SpanFrom("method"));
encoder->HandleString8(SpanFrom(method_));
encoder->HandleString8(SpanFrom("params"));
if (params_) {
params_->AppendSerialized(out);
} else {
encoder->HandleMapBegin();
encoder->HandleMapEnd();
}
encoder->HandleMapEnd();
assert(status.ok());
}
private:
const char* method_;
std::unique_ptr<Serializable> params_;
};
} // namespace
std::unique_ptr<Serializable> CreateResponse(
int call_id,
std::unique_ptr<Serializable> params) {
return std::make_unique<Response>(call_id, std::move(params));
}
std::unique_ptr<Serializable> CreateNotification(
const char* method,
std::unique_ptr<Serializable> params) {
return std::make_unique<Notification>(method, std::move(params));
}
// =============================================================================
// DomainDispatcher - Dispatching betwen protocol methods within a domain.
// =============================================================================
DomainDispatcher::WeakPtr::WeakPtr(DomainDispatcher* dispatcher)
: dispatcher_(dispatcher) {}
DomainDispatcher::WeakPtr::~WeakPtr() {
if (dispatcher_)
dispatcher_->weak_ptrs_.erase(this);
}
DomainDispatcher::Callback::~Callback() = default;
void DomainDispatcher::Callback::dispose() {
backend_impl_ = nullptr;
}
DomainDispatcher::Callback::Callback(
std::unique_ptr<DomainDispatcher::WeakPtr> backend_impl,
int call_id,
span<uint8_t> method,
span<uint8_t> message)
: backend_impl_(std::move(backend_impl)),
call_id_(call_id),
method_(method),
message_(message.begin(), message.end()) {}
void DomainDispatcher::Callback::sendIfActive(
std::unique_ptr<Serializable> partialMessage,
const DispatchResponse& response) {
if (!backend_impl_ || !backend_impl_->get())
return;
backend_impl_->get()->sendResponse(call_id_, response,
std::move(partialMessage));
backend_impl_ = nullptr;
}
void DomainDispatcher::Callback::fallThroughIfActive() {
if (!backend_impl_ || !backend_impl_->get())
return;
backend_impl_->get()->channel()->FallThrough(call_id_, method_,
SpanFrom(message_));
backend_impl_ = nullptr;
}
DomainDispatcher::DomainDispatcher(FrontendChannel* frontendChannel)
: frontend_channel_(frontendChannel) {}
DomainDispatcher::~DomainDispatcher() {
clearFrontend();
}
void DomainDispatcher::sendResponse(int call_id,
const DispatchResponse& response,
std::unique_ptr<Serializable> result) {
if (!frontend_channel_)
return;
std::unique_ptr<Serializable> serializable;
if (response.IsError()) {
serializable = CreateErrorResponse(call_id, response);
} else {
serializable = CreateResponse(call_id, std::move(result));
}
frontend_channel_->SendProtocolResponse(call_id, std::move(serializable));
}
bool DomainDispatcher::MaybeReportInvalidParams(
const Dispatchable& dispatchable,
const ErrorSupport& errors) {
if (errors.Errors().empty())
return false;
if (frontend_channel_) {
frontend_channel_->SendProtocolResponse(
dispatchable.CallId(),
CreateErrorResponse(
dispatchable.CallId(),
DispatchResponse::InvalidParams("Invalid parameters"), &errors));
}
return true;
}
void DomainDispatcher::clearFrontend() {
frontend_channel_ = nullptr;
for (auto& weak : weak_ptrs_)
weak->dispose();
weak_ptrs_.clear();
}
std::unique_ptr<DomainDispatcher::WeakPtr> DomainDispatcher::weakPtr() {
auto weak = std::make_unique<DomainDispatcher::WeakPtr>(this);
weak_ptrs_.insert(weak.get());
return weak;
}
// =============================================================================
// UberDispatcher - dispatches between domains (backends).
// =============================================================================
UberDispatcher::DispatchResult::DispatchResult(bool method_found,
std::function<void()> runnable)
: method_found_(method_found), runnable_(runnable) {}
void UberDispatcher::DispatchResult::Run() {
if (!runnable_)
return;
runnable_();
runnable_ = nullptr;
}
UberDispatcher::UberDispatcher(FrontendChannel* frontend_channel)
: frontend_channel_(frontend_channel) {
assert(frontend_channel);
}
UberDispatcher::~UberDispatcher() = default;
constexpr size_t kNotFound = std::numeric_limits<size_t>::max();
namespace {
size_t DotIdx(span<uint8_t> method) {
const void* p = memchr(method.data(), '.', method.size());
return p ? reinterpret_cast<const uint8_t*>(p) - method.data() : kNotFound;
}
} // namespace
UberDispatcher::DispatchResult UberDispatcher::Dispatch(
const Dispatchable& dispatchable) const {
span<uint8_t> method = FindByFirst(redirects_, dispatchable.Method(),
/*default_value=*/dispatchable.Method());
size_t dot_idx = DotIdx(method);
if (dot_idx != kNotFound) {
span<uint8_t> domain = method.subspan(0, dot_idx);
span<uint8_t> command = method.subspan(dot_idx + 1);
DomainDispatcher* dispatcher = FindByFirst(dispatchers_, domain);
if (dispatcher) {
std::function<void(const Dispatchable&)> dispatched =
dispatcher->Dispatch(command);
if (dispatched) {
return DispatchResult(
true, [dispatchable, dispatched = std::move(dispatched)]() {
dispatched(dispatchable);
});
}
}
}
return DispatchResult(false, [this, dispatchable]() {
frontend_channel_->SendProtocolResponse(
dispatchable.CallId(),
CreateErrorResponse(dispatchable.CallId(),
DispatchResponse::MethodNotFound(
"'" +
std::string(dispatchable.Method().begin(),
dispatchable.Method().end()) +
"' wasn't found")));
});
}
template <typename T>
struct FirstLessThan {
bool operator()(const std::pair<span<uint8_t>, T>& left,
const std::pair<span<uint8_t>, T>& right) {
return SpanLessThan(left.first, right.first);
}
};
void UberDispatcher::WireBackend(
span<uint8_t> domain,
const std::vector<std::pair<span<uint8_t>, span<uint8_t>>>&
sorted_redirects,
std::unique_ptr<DomainDispatcher> dispatcher) {
auto it = redirects_.insert(redirects_.end(), sorted_redirects.begin(),
sorted_redirects.end());
std::inplace_merge(redirects_.begin(), it, redirects_.end(),
FirstLessThan<span<uint8_t>>());
auto jt = dispatchers_.insert(dispatchers_.end(),
std::make_pair(domain, std::move(dispatcher)));
std::inplace_merge(dispatchers_.begin(), jt, dispatchers_.end(),
FirstLessThan<std::unique_ptr<DomainDispatcher>>());
}
} // namespace v8_crdtp
// Copyright 2020 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 V8_CRDTP_DISPATCH_H_
#define V8_CRDTP_DISPATCH_H_
#include <cassert>
#include <cstdint>
#include <functional>
#include <string>
#include <unordered_set>
#include "export.h"
#include "serializable.h"
#include "span.h"
#include "status.h"
namespace v8_crdtp {
class FrontendChannel;
class ErrorSupport;
namespace cbor {
class CBORTokenizer;
} // namespace cbor
// =============================================================================
// DispatchResponse - Error status and chaining / fall through
// =============================================================================
enum class DispatchCode {
SUCCESS = 1,
FALL_THROUGH = 2,
// For historical reasons, these error codes correspond to commonly used
// XMLRPC codes (e.g. see METHOD_NOT_FOUND in
// https://github.com/python/cpython/blob/master/Lib/xmlrpc/client.py).
PARSE_ERROR = -32700,
INVALID_REQUEST = -32600,
METHOD_NOT_FOUND = -32601,
INVALID_PARAMS = -32602,
INTERNAL_ERROR = -32603,
SERVER_ERROR = -32000,
};
// Information returned by command handlers. Usually returned after command
// execution attempts.
class DispatchResponse {
public:
const std::string& Message() const { return message_; }
DispatchCode Code() const { return code_; }
bool IsSuccess() const { return code_ == DispatchCode::SUCCESS; }
bool IsFallThrough() const { return code_ == DispatchCode::FALL_THROUGH; }
bool IsError() const { return code_ < DispatchCode::SUCCESS; }
static DispatchResponse Success();
static DispatchResponse FallThrough();
// Indicates that a message could not be parsed. E.g., malformed JSON.
static DispatchResponse ParseError(std::string message);
// Indicates that a request is lacking required top-level properties
// ('id', 'method'), has top-level properties of the wrong type, or has
// unknown top-level properties.
static DispatchResponse InvalidRequest(std::string message);
// Indicates that a protocol method such as "Page.bringToFront" could not be
// dispatched because it's not known to the (domain) dispatcher.
static DispatchResponse MethodNotFound(std::string message);
// Indicates that the params sent to a domain handler are invalid.
static DispatchResponse InvalidParams(std::string message);
// Used for application level errors, e.g. within protocol agents.
static DispatchResponse InternalError();
// Used for application level errors, e.g. within protocol agents.
static DispatchResponse ServerError(std::string message);
private:
DispatchResponse() = default;
DispatchCode code_;
std::string message_;
};
// =============================================================================
// Dispatchable - a shallow parser for CBOR encoded DevTools messages
// =============================================================================
// This parser extracts only the known top-level fields from a CBOR encoded map;
// method, id, sessionId, and params.
class Dispatchable {
public:
// This constructor parses the |serialized| message. If successful,
// |ok()| will yield |true|, and |Method()|, |SessionId()|, |CallId()|,
// |Params()| can be used to access, the extracted contents. Otherwise,
// |ok()| will yield |false|, and |DispatchError()| can be
// used to send a response or notification to the client.
explicit Dispatchable(span<uint8_t> serialized);
// The serialized message that we just parsed.
span<uint8_t> Serialized() const { return serialized_; }
// Yields true if parsing was successful. This is cheaper than calling
// ::DispatchError().
bool ok() const;
// If !ok(), returns a DispatchResponse with appropriate code and error
// which can be sent to the client as a response or notification.
DispatchResponse DispatchError() const;
// Top level field: the command to be executed, fully qualified by
// domain. E.g. "Page.createIsolatedWorld".
span<uint8_t> Method() const { return method_; }
// Used to identify protocol connections attached to a specific
// target. See Target.attachToTarget, Target.setAutoAttach.
span<uint8_t> SessionId() const { return session_id_; }
// The call id, a sequence number that's used in responses to indicate
// the request to which the response belongs.
int32_t CallId() const { return call_id_; }
bool HasCallId() const { return has_call_id_; }
// The payload of the request in CBOR format. The |Dispatchable| parser does
// not parse into this; it only provides access to its raw contents here.
span<uint8_t> Params() const { return params_; }
private:
bool MaybeParseProperty(cbor::CBORTokenizer* tokenizer);
bool MaybeParseCallId(cbor::CBORTokenizer* tokenizer);
bool MaybeParseMethod(cbor::CBORTokenizer* tokenizer);
bool MaybeParseParams(cbor::CBORTokenizer* tokenizer);
bool MaybeParseSessionId(cbor::CBORTokenizer* tokenizer);
span<uint8_t> serialized_;
Status status_;
bool has_call_id_ = false;
int32_t call_id_;
span<uint8_t> method_;
bool params_seen_ = false;
span<uint8_t> params_;
span<uint8_t> session_id_;
};
// =============================================================================
// Helpers for creating protocol cresponses and notifications.
// =============================================================================
// The resulting notifications can be sent to a protocol client,
// usually via a FrontendChannel (see frontend_channel.h).
std::unique_ptr<Serializable> CreateErrorResponse(
int callId,
DispatchResponse dispatch_response,
const ErrorSupport* errors = nullptr);
std::unique_ptr<Serializable> CreateErrorNotification(
DispatchResponse dispatch_response);
std::unique_ptr<Serializable> CreateResponse(
int callId,
std::unique_ptr<Serializable> params);
std::unique_ptr<Serializable> CreateNotification(
const char* method,
std::unique_ptr<Serializable> params = nullptr);
// =============================================================================
// DomainDispatcher - Dispatching betwen protocol methods within a domain.
// =============================================================================
// This class is subclassed by |DomainDispatcherImpl|, which we generate per
// DevTools domain. It contains routines called from the generated code,
// e.g. ::MaybeReportInvalidParams, which are optimized for small code size.
// The most important method is ::Dispatch, which implements method dispatch
// by command name lookup.
class DomainDispatcher {
public:
class WeakPtr {
public:
explicit WeakPtr(DomainDispatcher*);
~WeakPtr();
DomainDispatcher* get() { return dispatcher_; }
void dispose() { dispatcher_ = nullptr; }
private:
DomainDispatcher* dispatcher_;
};
class Callback {
public:
virtual ~Callback();
void dispose();
protected:
// |method| must point at static storage (a C++ string literal in practice).
Callback(std::unique_ptr<WeakPtr> backend_impl,
int call_id,
span<uint8_t> method,
span<uint8_t> message);
void sendIfActive(std::unique_ptr<Serializable> partialMessage,
const DispatchResponse& response);
void fallThroughIfActive();
private:
std::unique_ptr<WeakPtr> backend_impl_;
int call_id_;
// Subclasses of this class are instantiated from generated code which
// passes a string literal for the method name to the constructor. So the
// storage for |method| is the binary of the running process.
span<uint8_t> method_;
std::vector<uint8_t> message_;
};
explicit DomainDispatcher(FrontendChannel*);
virtual ~DomainDispatcher();
// Given a |command_name| without domain qualification, looks up the
// corresponding method. If the method is not found, returns nullptr.
// Otherwise, Returns a closure that will parse the provided
// Dispatchable.params() to a protocol object and execute the
// apprpropriate method. If the parsing fails it will issue an
// error response on the frontend channel, otherwise it will execute the
// command.
virtual std::function<void(const Dispatchable&)> Dispatch(
span<uint8_t> command_name) = 0;
// Sends a response to the client via the channel.
void sendResponse(int call_id,
const DispatchResponse&,
std::unique_ptr<Serializable> result = nullptr);
// Returns true if |errors| contains errors *and* reports these errors
// as a response on the frontend channel. Called from generated code,
// optimized for code size of the callee.
bool MaybeReportInvalidParams(const Dispatchable& dispatchable,
const ErrorSupport& errors);
FrontendChannel* channel() { return frontend_channel_; }
void clearFrontend();
std::unique_ptr<WeakPtr> weakPtr();
private:
FrontendChannel* frontend_channel_;
std::unordered_set<WeakPtr*> weak_ptrs_;
};
// =============================================================================
// UberDispatcher - dispatches between domains (backends).
// =============================================================================
class UberDispatcher {
public:
// Return type for ::Dispatch.
class DispatchResult {
public:
DispatchResult(bool method_found, std::function<void()> runnable);
// Indicates whether the method was found, that is, it could be dispatched
// to a backend registered with this dispatcher.
bool MethodFound() const { return method_found_; }
// Runs the dispatched result. This will send the appropriate error
// responses if the method wasn't found or if something went wrong during
// parameter parsing.
void Run();
private:
bool method_found_;
std::function<void()> runnable_;
};
// |frontend_hannel| can't be nullptr.
explicit UberDispatcher(FrontendChannel* frontend_channel);
virtual ~UberDispatcher();
// Dispatches the provided |dispatchable| considering all redirects and domain
// handlers registered with this uber dispatcher. Also see |DispatchResult|.
// |dispatchable.ok()| must hold - callers must check this separately and
// deal with errors.
DispatchResult Dispatch(const Dispatchable& dispatchable) const;
// Invoked from generated code for wiring domain backends; that is,
// connecting domain handlers to an uber dispatcher.
// See <domain-namespace>::Dispatcher::Wire(UberDispatcher*,Backend*).
FrontendChannel* channel() const {
assert(frontend_channel_);
return frontend_channel_;
}
// Invoked from generated code for wiring domain backends; that is,
// connecting domain handlers to an uber dispatcher.
// See <domain-namespace>::Dispatcher::Wire(UberDispatcher*,Backend*).
void WireBackend(span<uint8_t> domain,
const std::vector<std::pair<span<uint8_t>, span<uint8_t>>>&,
std::unique_ptr<DomainDispatcher> dispatcher);
private:
DomainDispatcher* findDispatcher(span<uint8_t> method);
FrontendChannel* const frontend_channel_;
// Pairs of ascii strings of the form ("Domain1.method1","Domain2.method2")
// indicating that the first element of each pair redirects to the second.
// Sorted by first element.
std::vector<std::pair<span<uint8_t>, span<uint8_t>>> redirects_;
// Domain dispatcher instances, sorted by their domain name.
std::vector<std::pair<span<uint8_t>, std::unique_ptr<DomainDispatcher>>>
dispatchers_;
};
} // namespace v8_crdtp
#endif // V8_CRDTP_DISPATCH_H_
// Copyright 2020 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.
#include <vector>
#include "cbor.h"
#include "dispatch.h"
#include "error_support.h"
#include "frontend_channel.h"
#include "json.h"
#include "test_platform.h"
namespace v8_crdtp {
// =============================================================================
// DispatchResponse - Error status and chaining / fall through
// =============================================================================
TEST(DispatchResponseTest, OK) {
EXPECT_EQ(DispatchCode::SUCCESS, DispatchResponse::Success().Code());
EXPECT_TRUE(DispatchResponse::Success().IsSuccess());
}
TEST(DispatchResponseTest, ServerError) {
DispatchResponse error = DispatchResponse::ServerError("Oops!");
EXPECT_FALSE(error.IsSuccess());
EXPECT_EQ(DispatchCode::SERVER_ERROR, error.Code());
EXPECT_EQ("Oops!", error.Message());
}
TEST(DispatchResponseTest, InternalError) {
DispatchResponse error = DispatchResponse::InternalError();
EXPECT_FALSE(error.IsSuccess());
EXPECT_EQ(DispatchCode::INTERNAL_ERROR, error.Code());
EXPECT_EQ("Internal error", error.Message());
}
TEST(DispatchResponseTest, InvalidParams) {
DispatchResponse error = DispatchResponse::InvalidParams("too cool");
EXPECT_FALSE(error.IsSuccess());
EXPECT_EQ(DispatchCode::INVALID_PARAMS, error.Code());
EXPECT_EQ("too cool", error.Message());
}
TEST(DispatchResponseTest, FallThrough) {
DispatchResponse error = DispatchResponse::FallThrough();
EXPECT_FALSE(error.IsSuccess());
EXPECT_TRUE(error.IsFallThrough());
EXPECT_EQ(DispatchCode::FALL_THROUGH, error.Code());
}
// =============================================================================
// Dispatchable - a shallow parser for CBOR encoded DevTools messages
// =============================================================================
TEST(DispatchableTest, MessageMustBeAnObject) {
// Provide no input whatsoever.
span<uint8_t> empty_span;
Dispatchable empty(empty_span);
EXPECT_FALSE(empty.ok());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, empty.DispatchError().Code());
EXPECT_EQ("Message must be an object", empty.DispatchError().Message());
}
TEST(DispatchableTest, MessageMustHaveIntegerIdProperty) {
// Construct an empty map inside of an envelope.
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom("{}"), &cbor).ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_FALSE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ("Message must have integer 'id' property",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, MessageMustHaveIntegerIdProperty_IncorrectType) {
// This time we set the id property, but fail to make it an int32.
std::vector<uint8_t> cbor;
ASSERT_TRUE(
json::ConvertJSONToCBOR(SpanFrom("{\"id\":\"foo\"}"), &cbor).ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_FALSE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ("Message must have integer 'id' property",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, MessageMustHaveStringMethodProperty) {
// This time we set the id property, but not the method property.
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom("{\"id\":42}"), &cbor).ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ("Message must have string 'method' property",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, MessageMustHaveStringMethodProperty_IncorrectType) {
// This time we set the method property, but fail to make it a string.
std::vector<uint8_t> cbor;
ASSERT_TRUE(
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":42}"), &cbor)
.ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ("Message must have string 'method' property",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, MessageMayHaveStringSessionIdProperty) {
// This time, the session id is an int but it should be a string. Method and
// call id are present.
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(
SpanFrom("{\"id\":42,\"method\":\"Foo.executeBar\","
"\"sessionId\":42" // int32 is wrong type
"}"),
&cbor)
.ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ("Message may have string 'sessionId' property",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, MessageMayHaveObjectParamsProperty) {
// This time, we fail to use the correct type for the params property.
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(
SpanFrom("{\"id\":42,\"method\":\"Foo.executeBar\","
"\"params\":42" // int32 is wrong type
"}"),
&cbor)
.ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ("Message may have object 'params' property",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, MessageWithUnknownProperty) {
// This time we set the 'unknown' property, so we are told what's allowed.
std::vector<uint8_t> cbor;
ASSERT_TRUE(
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"unknown\":42}"), &cbor)
.ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(DispatchCode::INVALID_REQUEST, dispatchable.DispatchError().Code());
EXPECT_EQ(
"Message has property other than 'id', 'method', 'sessionId', 'params'",
dispatchable.DispatchError().Message());
}
TEST(DispatchableTest, DuplicateMapKey) {
for (const std::string& json :
{"{\"id\":42,\"id\":42}", "{\"params\":null,\"params\":null}",
"{\"method\":\"foo\",\"method\":\"foo\"}",
"{\"sessionId\":\"42\",\"sessionId\":\"42\"}"}) {
SCOPED_TRACE("json = " + json);
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom(json), &cbor).ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_EQ(DispatchCode::PARSE_ERROR, dispatchable.DispatchError().Code());
EXPECT_THAT(dispatchable.DispatchError().Message(),
testing::StartsWith("CBOR: duplicate map key at position "));
}
}
TEST(DispatchableTest, ValidMessageParsesOK_NoParams) {
for (const std::string& json :
{"{\"id\":42,\"method\":\"Foo.executeBar\",\"sessionId\":"
"\"f421ssvaz4\"}",
"{\"id\":42,\"method\":\"Foo.executeBar\",\"sessionId\":\"f421ssvaz4\","
"\"params\":null}"}) {
SCOPED_TRACE("json = " + json);
std::vector<uint8_t> cbor;
ASSERT_TRUE(json::ConvertJSONToCBOR(SpanFrom(json), &cbor).ok());
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_TRUE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(42, dispatchable.CallId());
EXPECT_EQ("Foo.executeBar", std::string(dispatchable.Method().begin(),
dispatchable.Method().end()));
EXPECT_EQ("f421ssvaz4", std::string(dispatchable.SessionId().begin(),
dispatchable.SessionId().end()));
EXPECT_TRUE(dispatchable.Params().empty());
}
}
TEST(DispatchableTest, ValidMessageParsesOK_WithParams) {
std::vector<uint8_t> cbor;
cbor::EnvelopeEncoder envelope;
envelope.EncodeStart(&cbor);
cbor.push_back(cbor::EncodeIndefiniteLengthMapStart());
cbor::EncodeString8(SpanFrom("id"), &cbor);
cbor::EncodeInt32(42, &cbor);
cbor::EncodeString8(SpanFrom("method"), &cbor);
cbor::EncodeString8(SpanFrom("Foo.executeBar"), &cbor);
cbor::EncodeString8(SpanFrom("params"), &cbor);
cbor::EnvelopeEncoder params_envelope;
params_envelope.EncodeStart(&cbor);
// The |Dispatchable| class does not parse into the "params" envelope,
// so we can stick anything into there for the purpose of this test.
// For convenience, we use a String8.
cbor::EncodeString8(SpanFrom("params payload"), &cbor);
params_envelope.EncodeStop(&cbor);
cbor::EncodeString8(SpanFrom("sessionId"), &cbor);
cbor::EncodeString8(SpanFrom("f421ssvaz4"), &cbor);
cbor.push_back(cbor::EncodeStop());
envelope.EncodeStop(&cbor);
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_TRUE(dispatchable.ok());
EXPECT_TRUE(dispatchable.HasCallId());
EXPECT_EQ(42, dispatchable.CallId());
EXPECT_EQ("Foo.executeBar", std::string(dispatchable.Method().begin(),
dispatchable.Method().end()));
EXPECT_EQ("f421ssvaz4", std::string(dispatchable.SessionId().begin(),
dispatchable.SessionId().end()));
cbor::CBORTokenizer params_tokenizer(dispatchable.Params());
ASSERT_EQ(cbor::CBORTokenTag::ENVELOPE, params_tokenizer.TokenTag());
params_tokenizer.EnterEnvelope();
ASSERT_EQ(cbor::CBORTokenTag::STRING8, params_tokenizer.TokenTag());
EXPECT_EQ("params payload", std::string(params_tokenizer.GetString8().begin(),
params_tokenizer.GetString8().end()));
}
TEST(DispatchableTest, FaultyCBORTrailingJunk) {
// In addition to the higher level parsing errors, we also catch CBOR
// structural corruption. E.g., in this case, the message would be
// OK but has some extra trailing bytes.
std::vector<uint8_t> cbor;
cbor::EnvelopeEncoder envelope;
envelope.EncodeStart(&cbor);
cbor.push_back(cbor::EncodeIndefiniteLengthMapStart());
cbor::EncodeString8(SpanFrom("id"), &cbor);
cbor::EncodeInt32(42, &cbor);
cbor::EncodeString8(SpanFrom("method"), &cbor);
cbor::EncodeString8(SpanFrom("Foo.executeBar"), &cbor);
cbor::EncodeString8(SpanFrom("sessionId"), &cbor);
cbor::EncodeString8(SpanFrom("f421ssvaz4"), &cbor);
cbor.push_back(cbor::EncodeStop());
envelope.EncodeStop(&cbor);
size_t trailing_junk_pos = cbor.size();
cbor.push_back('t');
cbor.push_back('r');
cbor.push_back('a');
cbor.push_back('i');
cbor.push_back('l');
Dispatchable dispatchable(SpanFrom(cbor));
EXPECT_FALSE(dispatchable.ok());
EXPECT_EQ(DispatchCode::PARSE_ERROR, dispatchable.DispatchError().Code());
EXPECT_EQ(56u, trailing_junk_pos);
EXPECT_EQ("CBOR: trailing junk at position 56",
dispatchable.DispatchError().Message());
}
// =============================================================================
// Helpers for creating protocol cresponses and notifications.
// =============================================================================
TEST(CreateErrorResponseTest, SmokeTest) {
ErrorSupport errors;
errors.Push();
errors.SetName("foo");
errors.Push();
errors.SetName("bar");
errors.AddError("expected a string");
errors.SetName("baz");
errors.AddError("expected a surprise");
auto serializable = CreateErrorResponse(
42, DispatchResponse::InvalidParams("invalid params message"), &errors);
std::string json;
auto status =
json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
ASSERT_TRUE(status.ok());
EXPECT_EQ(
"{\"id\":42,\"error\":"
"{\"code\":-32602,"
"\"message\":\"invalid params message\","
"\"data\":\"foo.bar: expected a string; "
"foo.baz: expected a surprise\"}}",
json);
}
TEST(CreateErrorNotificationTest, SmokeTest) {
auto serializable =
CreateErrorNotification(DispatchResponse::InvalidRequest("oops!"));
std::string json;
auto status =
json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
ASSERT_TRUE(status.ok());
EXPECT_EQ("{\"error\":{\"code\":-32600,\"message\":\"oops!\"}}", json);
}
TEST(CreateResponseTest, SmokeTest) {
auto serializable = CreateResponse(42, nullptr);
std::string json;
auto status =
json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
ASSERT_TRUE(status.ok());
EXPECT_EQ("{\"id\":42,\"result\":{}}", json);
}
TEST(CreateNotificationTest, SmokeTest) {
auto serializable = CreateNotification("Foo.bar");
std::string json;
auto status =
json::ConvertCBORToJSON(SpanFrom(serializable->Serialize()), &json);
ASSERT_TRUE(status.ok());
EXPECT_EQ("{\"method\":\"Foo.bar\",\"params\":{}}", json);
}
// =============================================================================
// UberDispatcher - dispatches between domains (backends).
// =============================================================================
class TestChannel : public FrontendChannel {
public:
std::string JSON() const {
std::string json;
json::ConvertCBORToJSON(SpanFrom(cbor_), &json);
return json;
}
private:
void SendProtocolResponse(int call_id,
std::unique_ptr<Serializable> message) override {
cbor_ = message->Serialize();
}
void SendProtocolNotification(
std::unique_ptr<Serializable> message) override {
cbor_ = message->Serialize();
}
void FallThrough(int call_id,
span<uint8_t> method,
span<uint8_t> message) override {}
void FlushProtocolNotifications() override {}
std::vector<uint8_t> cbor_;
};
TEST(UberDispatcherTest, MethodNotFound) {
// No domain dispatchers are registered, so unsuprisingly, we'll get a method
// not found error and can see that DispatchResult::MethodFound() yields
// false.
TestChannel channel;
UberDispatcher dispatcher(&channel);
std::vector<uint8_t> message;
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":\"Foo.bar\"}"),
&message);
Dispatchable dispatchable(SpanFrom(message));
ASSERT_TRUE(dispatchable.ok());
UberDispatcher::DispatchResult dispatched = dispatcher.Dispatch(dispatchable);
EXPECT_FALSE(dispatched.MethodFound());
dispatched.Run();
EXPECT_EQ(
"{\"id\":42,\"error\":"
"{\"code\":-32601,\"message\":\"'Foo.bar' wasn't found\"}}",
channel.JSON());
}
// A domain dispatcher which captured dispatched and executed commands in fields
// for testing.
class TestDomain : public DomainDispatcher {
public:
explicit TestDomain(FrontendChannel* channel) : DomainDispatcher(channel) {}
std::function<void(const Dispatchable&)> Dispatch(
span<uint8_t> command_name) override {
dispatched_commands_.push_back(
std::string(command_name.begin(), command_name.end()));
return [this](const Dispatchable& dispatchable) {
executed_commands_.push_back(dispatchable.CallId());
};
}
// Command names of the dispatched commands.
std::vector<std::string> DispatchedCommands() const {
return dispatched_commands_;
}
// Call ids of the executed commands.
std::vector<int32_t> ExecutedCommands() const { return executed_commands_; }
private:
std::vector<std::string> dispatched_commands_;
std::vector<int32_t> executed_commands_;
};
TEST(UberDispatcherTest, DispatchingToDomainWithRedirects) {
// This time, we register two domain dispatchers (Foo and Bar) and issue one
// command 'Foo.execute' which executes on Foo and one command 'Foo.redirect'
// which executes as 'Bar.redirected'.
TestChannel channel;
UberDispatcher dispatcher(&channel);
auto foo_dispatcher = std::make_unique<TestDomain>(&channel);
TestDomain* foo = foo_dispatcher.get();
auto bar_dispatcher = std::make_unique<TestDomain>(&channel);
TestDomain* bar = bar_dispatcher.get();
dispatcher.WireBackend(
SpanFrom("Foo"), {{SpanFrom("Foo.redirect"), SpanFrom("Bar.redirected")}},
std::move(foo_dispatcher));
dispatcher.WireBackend(SpanFrom("Bar"), {}, std::move(bar_dispatcher));
{
std::vector<uint8_t> message;
json::ConvertJSONToCBOR(SpanFrom("{\"id\":42,\"method\":\"Foo.execute\"}"),
&message);
Dispatchable dispatchable(SpanFrom(message));
ASSERT_TRUE(dispatchable.ok());
UberDispatcher::DispatchResult dispatched =
dispatcher.Dispatch(dispatchable);
EXPECT_TRUE(dispatched.MethodFound());
dispatched.Run();
}
{
std::vector<uint8_t> message;
json::ConvertJSONToCBOR(SpanFrom("{\"id\":43,\"method\":\"Foo.redirect\"}"),
&message);
Dispatchable dispatchable(SpanFrom(message));
ASSERT_TRUE(dispatchable.ok());
UberDispatcher::DispatchResult dispatched =
dispatcher.Dispatch(dispatchable);
EXPECT_TRUE(dispatched.MethodFound());
dispatched.Run();
}
EXPECT_THAT(foo->DispatchedCommands(), testing::ElementsAre("execute"));
EXPECT_THAT(foo->ExecutedCommands(), testing::ElementsAre(42));
EXPECT_THAT(bar->DispatchedCommands(), testing::ElementsAre("redirected"));
EXPECT_THAT(bar->ExecutedCommands(), testing::ElementsAre(43));
}
} // namespace v8_crdtp
......@@ -7,6 +7,7 @@
#include <cstdint>
#include <string>
#include <vector>
#include "export.h"
#include "span.h"
......
// Copyright 2020 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 V8_CRDTP_FIND_BY_FIRST_H_
#define V8_CRDTP_FIND_BY_FIRST_H_
#include <algorithm>
#include <cstdint>
#include <memory>
#include <vector>
#include "export.h"
#include "span.h"
namespace v8_crdtp {
// =============================================================================
// FindByFirst - Retrieval from a sorted vector that's keyed by span<uint8_t>.
// =============================================================================
// Given a vector of pairs sorted by the first element of each pair, find
// the corresponding value given a key to be compared to the first element.
// Together with std::inplace_merge and pre-sorting or std::sort, this can
// be used to implement a minimalistic equivalent of Chromium's flat_map.
// In this variant, the template parameter |T| is a value type and a
// |default_value| is provided.
template <typename T>
T FindByFirst(const std::vector<std::pair<span<uint8_t>, T>>& sorted_by_first,
span<uint8_t> key,
T default_value) {
auto it = std::lower_bound(
sorted_by_first.begin(), sorted_by_first.end(), key,
[](const std::pair<span<uint8_t>, T>& left, span<uint8_t> right) {
return SpanLessThan(left.first, right);
});
return (it != sorted_by_first.end() && SpanEquals(it->first, key))
? it->second
: default_value;
}
// In this variant, the template parameter |T| is a class or struct that's
// instantiated in std::unique_ptr, and we return either a T* or a nullptr.
template <typename T>
T* FindByFirst(const std::vector<std::pair<span<uint8_t>, std::unique_ptr<T>>>&
sorted_by_first,
span<uint8_t> key) {
auto it = std::lower_bound(
sorted_by_first.begin(), sorted_by_first.end(), key,
[](const std::pair<span<uint8_t>, std::unique_ptr<T>>& left,
span<uint8_t> right) { return SpanLessThan(left.first, right); });
return (it != sorted_by_first.end() && SpanEquals(it->first, key))
? it->second.get()
: nullptr;
}
} // namespace v8_crdtp
#endif // V8_CRDTP_FIND_BY_FIRST_H_
// Copyright 2020 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.
#include <string>
#include "find_by_first.h"
#include "test_platform.h"
namespace v8_crdtp {
// =============================================================================
// FindByFirst - Efficient retrieval from a sorted vector.
// =============================================================================
TEST(FindByFirst, SpanBySpan) {
std::vector<std::pair<span<uint8_t>, span<uint8_t>>> sorted_span_by_span = {
{SpanFrom("foo1"), SpanFrom("bar1")},
{SpanFrom("foo2"), SpanFrom("bar2")},
{SpanFrom("foo3"), SpanFrom("bar3")},
};
{
auto result = FindByFirst(sorted_span_by_span, SpanFrom("foo1"),
SpanFrom("not_found"));
EXPECT_EQ("bar1", std::string(result.begin(), result.end()));
}
{
auto result = FindByFirst(sorted_span_by_span, SpanFrom("foo3"),
SpanFrom("not_found"));
EXPECT_EQ("bar3", std::string(result.begin(), result.end()));
}
{
auto result = FindByFirst(sorted_span_by_span, SpanFrom("baz"),
SpanFrom("not_found"));
EXPECT_EQ("not_found", std::string(result.begin(), result.end()));
}
}
namespace {
class TestObject {
public:
explicit TestObject(const std::string& message) : message_(message) {}
const std::string& message() const { return message_; }
private:
std::string message_;
};
} // namespace
TEST(FindByFirst, ObjectBySpan) {
std::vector<std::pair<span<uint8_t>, std::unique_ptr<TestObject>>>
sorted_object_by_span;
sorted_object_by_span.push_back(
std::make_pair(SpanFrom("foo1"), std::make_unique<TestObject>("bar1")));
sorted_object_by_span.push_back(
std::make_pair(SpanFrom("foo2"), std::make_unique<TestObject>("bar2")));
sorted_object_by_span.push_back(
std::make_pair(SpanFrom("foo3"), std::make_unique<TestObject>("bar3")));
{
TestObject* result =
FindByFirst<TestObject>(sorted_object_by_span, SpanFrom("foo1"));
ASSERT_TRUE(result);
ASSERT_EQ("bar1", result->message());
}
{
TestObject* result =
FindByFirst<TestObject>(sorted_object_by_span, SpanFrom("foo3"));
ASSERT_TRUE(result);
ASSERT_EQ("bar3", result->message());
}
{
TestObject* result =
FindByFirst<TestObject>(sorted_object_by_span, SpanFrom("baz"));
ASSERT_FALSE(result);
}
}
} // namespace v8_crdtp
// Copyright 2020 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 V8_CRDTP_FRONTEND_CHANNEL_H_
#define V8_CRDTP_FRONTEND_CHANNEL_H_
#include <cstdint>
#include <memory>
#include "export.h"
#include "serializable.h"
#include "span.h"
namespace v8_crdtp {
// =============================================================================
// FrontendChannel - For sending notifications and responses to protocol clients
// =============================================================================
class FrontendChannel {
public:
virtual ~FrontendChannel() = default;
// Sends protocol responses and notifications. The |call_id| parameter is
// seemingly redundant because it's also included in the message, but
// responses may be sent from an untrusted source to a trusted process (e.g.
// from Chromium's renderer (blink) to the browser process), which needs
// to be able to match the response to an earlier request without parsing the
// messsage.
virtual void SendProtocolResponse(int call_id,
std::unique_ptr<Serializable> message) = 0;
virtual void SendProtocolNotification(
std::unique_ptr<Serializable> message) = 0;
// FallThrough indicates that |message| should be handled in another layer.
// Usually this means the layer responding to the message didn't handle it,
// but in some cases messages are handled by multiple layers (e.g. both
// the embedder and the content layer in Chromium).
virtual void FallThrough(int call_id,
span<uint8_t> method,
span<uint8_t> message) = 0;
// Session implementations may queue notifications for performance or
// other considerations; this is a hook for domain handlers to manually flush.
virtual void FlushProtocolNotifications() = 0;
};
} // namespace v8_crdtp
#endif // V8_CRDTP_FRONTEND_CHANNEL_H_
......@@ -6,6 +6,7 @@
#define V8_CRDTP_JSON_H_
#include <memory>
#include <vector>
#include "export.h"
#include "parser_handler.h"
......
......@@ -14,4 +14,23 @@ std::vector<uint8_t> Serializable::Serialize() const {
AppendSerialized(&out);
return out;
}
namespace {
class PreSerialized : public Serializable {
public:
explicit PreSerialized(std::vector<uint8_t> bytes) : bytes_(bytes) {}
void AppendSerialized(std::vector<uint8_t>* out) const override {
out->insert(out->end(), bytes_.begin(), bytes_.end());
}
private:
std::vector<uint8_t> bytes_;
};
} // namespace
// static
std::unique_ptr<Serializable> Serializable::From(std::vector<uint8_t> bytes) {
return std::make_unique<PreSerialized>(std::move(bytes));
}
} // namespace v8_crdtp
......@@ -6,6 +6,7 @@
#define V8_CRDTP_SERIALIZABLE_H_
#include <cstdint>
#include <memory>
#include <vector>
#include "export.h"
......@@ -13,7 +14,6 @@ namespace v8_crdtp {
// =============================================================================
// Serializable - An object to be emitted as a sequence of bytes.
// =============================================================================
class Serializable {
public:
// Convenience: Invokes |AppendSerialized| on an empty vector.
......@@ -22,6 +22,10 @@ class Serializable {
virtual void AppendSerialized(std::vector<uint8_t>* out) const = 0;
virtual ~Serializable() = default;
// Wraps a vector of |bytes| into a Serializable for situations in which we
// eagerly serialize a structure.
static std::unique_ptr<Serializable> From(std::vector<uint8_t> bytes);
};
} // namespace v8_crdtp
......
// Copyright 2020 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.
#include "span.h"
#include <algorithm>
namespace v8_crdtp {
bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept {
auto min_size = std::min(x.size(), y.size());
const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
return (r < 0) || (r == 0 && x.size() < y.size());
}
bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept {
auto len = x.size();
if (len != y.size())
return false;
return x.data() == y.data() || len == 0 ||
std::memcmp(x.data(), y.data(), len) == 0;
}
} // namespace v8_crdtp
......@@ -5,11 +5,11 @@
#ifndef V8_CRDTP_SPAN_H_
#define V8_CRDTP_SPAN_H_
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "export.h"
namespace v8_crdtp {
// =============================================================================
......@@ -76,19 +76,15 @@ inline span<typename C::value_type> SpanFrom(const C& v) {
// Less than / equality comparison functions for sorting / searching for byte
// spans. These are similar to absl::string_view's < and == operators.
constexpr inline bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept {
auto min_size = std::min(x.size(), y.size());
const int r = min_size == 0 ? 0 : memcmp(x.data(), y.data(), min_size);
return (r < 0) || (r == 0 && x.size() < y.size());
}
bool SpanLessThan(span<uint8_t> x, span<uint8_t> y) noexcept;
constexpr inline bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept {
auto len = x.size();
if (len != y.size())
return false;
return x.data() == y.data() || len == 0 ||
std::memcmp(x.data(), y.data(), len) == 0;
}
bool SpanEquals(span<uint8_t> x, span<uint8_t> y) noexcept;
struct SpanLt {
bool operator()(span<uint8_t> l, span<uint8_t> r) const {
return SpanLessThan(l, r);
}
};
} // namespace v8_crdtp
#endif // V8_CRDTP_SPAN_H_
......@@ -4,7 +4,6 @@
#include <cstdlib>
#include <string>
#include <unordered_map>
#include "span.h"
#include "test_platform.h"
......@@ -13,7 +12,6 @@ namespace v8_crdtp {
// =============================================================================
// span - sequence of bytes
// =============================================================================
template <typename T>
class SpanTest : public ::testing::Test {};
......@@ -108,41 +106,4 @@ TEST(SpanComparisons, ByteWiseLexicographicalOrder) {
EXPECT_FALSE(SpanLessThan(SpanFrom(msg), SpanFrom(lesser_msg)));
EXPECT_FALSE(SpanEquals(SpanFrom(msg), SpanFrom(lesser_msg)));
}
// TODO(johannes): The following shows how the span can be used in an
// std::unordered_map as a key. Once we have a production usage, we'll move
// SpanHash, SpanEq, SpanHasher into the header.
// A simple hash code, inspired by http://stackoverflow.com/q/1646807.
constexpr inline size_t SpanHash(span<uint8_t> s) noexcept {
size_t hash = 17;
for (uint8_t c : s)
hash = 31 * hash + c;
return hash;
}
// Structs for making std::unordered_map with std::span<uint8_t> keys.
struct SpanEq {
constexpr inline bool operator()(span<uint8_t> l, span<uint8_t> r) const {
return SpanEquals(l, r);
}
};
struct SpanHasher {
constexpr inline size_t operator()(span<uint8_t> s) const {
return SpanHash(s);
}
};
TEST(SpanHasherAndSpanEq, SpanAsKeyInUnorderedMap) {
// A very simple smoke test for unordered_map, storing three key/value pairs.
std::unordered_map<span<uint8_t>, int32_t, SpanHasher, SpanEq> a_map;
a_map[SpanFrom("foo")] = 1;
a_map[SpanFrom("bar")] = 2;
a_map[SpanFrom("baz")] = 3;
EXPECT_EQ(3u, a_map.size());
EXPECT_EQ(1, a_map[SpanFrom("foo")]);
EXPECT_EQ(2, a_map[SpanFrom("bar")]);
EXPECT_EQ(3, a_map[SpanFrom("baz")]);
}
} // namespace v8_crdtp
......@@ -9,100 +9,118 @@ namespace v8_crdtp {
// Status and Error codes
// =============================================================================
std::string Status::ToASCIIString() const {
std::string Status::Message() const {
switch (error) {
case Error::OK:
return "OK";
case Error::JSON_PARSER_UNPROCESSED_INPUT_REMAINS:
return ToASCIIString("JSON: unprocessed input remains");
return "JSON: unprocessed input remains";
case Error::JSON_PARSER_STACK_LIMIT_EXCEEDED:
return ToASCIIString("JSON: stack limit exceeded");
return "JSON: stack limit exceeded";
case Error::JSON_PARSER_NO_INPUT:
return ToASCIIString("JSON: no input");
return "JSON: no input";
case Error::JSON_PARSER_INVALID_TOKEN:
return ToASCIIString("JSON: invalid token");
return "JSON: invalid token";
case Error::JSON_PARSER_INVALID_NUMBER:
return ToASCIIString("JSON: invalid number");
return "JSON: invalid number";
case Error::JSON_PARSER_INVALID_STRING:
return ToASCIIString("JSON: invalid string");
return "JSON: invalid string";
case Error::JSON_PARSER_UNEXPECTED_ARRAY_END:
return ToASCIIString("JSON: unexpected array end");
return "JSON: unexpected array end";
case Error::JSON_PARSER_COMMA_OR_ARRAY_END_EXPECTED:
return ToASCIIString("JSON: comma or array end expected");
return "JSON: comma or array end expected";
case Error::JSON_PARSER_STRING_LITERAL_EXPECTED:
return ToASCIIString("JSON: string literal expected");
return "JSON: string literal expected";
case Error::JSON_PARSER_COLON_EXPECTED:
return ToASCIIString("JSON: colon expected");
return "JSON: colon expected";
case Error::JSON_PARSER_UNEXPECTED_MAP_END:
return ToASCIIString("JSON: unexpected map end");
return "JSON: unexpected map end";
case Error::JSON_PARSER_COMMA_OR_MAP_END_EXPECTED:
return ToASCIIString("JSON: comma or map end expected");
return "JSON: comma or map end expected";
case Error::JSON_PARSER_VALUE_EXPECTED:
return ToASCIIString("JSON: value expected");
return "JSON: value expected";
case Error::CBOR_INVALID_INT32:
return ToASCIIString("CBOR: invalid int32");
return "CBOR: invalid int32";
case Error::CBOR_INVALID_DOUBLE:
return ToASCIIString("CBOR: invalid double");
return "CBOR: invalid double";
case Error::CBOR_INVALID_ENVELOPE:
return ToASCIIString("CBOR: invalid envelope");
return "CBOR: invalid envelope";
case Error::CBOR_ENVELOPE_CONTENTS_LENGTH_MISMATCH:
return ToASCIIString("CBOR: envelope contents length mismatch");
return "CBOR: envelope contents length mismatch";
case Error::CBOR_MAP_OR_ARRAY_EXPECTED_IN_ENVELOPE:
return ToASCIIString("CBOR: map or array expected in envelope");
return "CBOR: map or array expected in envelope";
case Error::CBOR_INVALID_STRING8:
return ToASCIIString("CBOR: invalid string8");
return "CBOR: invalid string8";
case Error::CBOR_INVALID_STRING16:
return ToASCIIString("CBOR: invalid string16");
return "CBOR: invalid string16";
case Error::CBOR_INVALID_BINARY:
return ToASCIIString("CBOR: invalid binary");
return "CBOR: invalid binary";
case Error::CBOR_UNSUPPORTED_VALUE:
return ToASCIIString("CBOR: unsupported value");
return "CBOR: unsupported value";
case Error::CBOR_NO_INPUT:
return ToASCIIString("CBOR: no input");
return "CBOR: no input";
case Error::CBOR_INVALID_START_BYTE:
return ToASCIIString("CBOR: invalid start byte");
return "CBOR: invalid start byte";
case Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE:
return ToASCIIString("CBOR: unexpected eof expected value");
return "CBOR: unexpected eof expected value";
case Error::CBOR_UNEXPECTED_EOF_IN_ARRAY:
return ToASCIIString("CBOR: unexpected eof in array");
return "CBOR: unexpected eof in array";
case Error::CBOR_UNEXPECTED_EOF_IN_MAP:
return ToASCIIString("CBOR: unexpected eof in map");
return "CBOR: unexpected eof in map";
case Error::CBOR_INVALID_MAP_KEY:
return ToASCIIString("CBOR: invalid map key");
return "CBOR: invalid map key";
case Error::CBOR_DUPLICATE_MAP_KEY:
return "CBOR: duplicate map key";
case Error::CBOR_STACK_LIMIT_EXCEEDED:
return ToASCIIString("CBOR: stack limit exceeded");
return "CBOR: stack limit exceeded";
case Error::CBOR_TRAILING_JUNK:
return ToASCIIString("CBOR: trailing junk");
return "CBOR: trailing junk";
case Error::CBOR_MAP_START_EXPECTED:
return ToASCIIString("CBOR: map start expected");
return "CBOR: map start expected";
case Error::CBOR_MAP_STOP_EXPECTED:
return ToASCIIString("CBOR: map stop expected");
return "CBOR: map stop expected";
case Error::CBOR_ARRAY_START_EXPECTED:
return ToASCIIString("CBOR: array start expected");
return "CBOR: array start expected";
case Error::CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED:
return ToASCIIString("CBOR: envelope size limit exceeded");
return "CBOR: envelope size limit exceeded";
case Error::MESSAGE_MUST_BE_AN_OBJECT:
return "Message must be an object";
case Error::MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY:
return "Message must have integer 'id' property";
case Error::MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY:
return "Message must have string 'method' property";
case Error::MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY:
return "Message may have string 'sessionId' property";
case Error::MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY:
return "Message may have object 'params' property";
case Error::MESSAGE_HAS_UNKNOWN_PROPERTY:
return "Message has property other than "
"'id', 'method', 'sessionId', 'params'";
case Error::BINDINGS_MANDATORY_FIELD_MISSING:
return ToASCIIString("BINDINGS: mandatory field missing");
return "BINDINGS: mandatory field missing";
case Error::BINDINGS_BOOL_VALUE_EXPECTED:
return ToASCIIString("BINDINGS: bool value expected");
return "BINDINGS: bool value expected";
case Error::BINDINGS_INT32_VALUE_EXPECTED:
return ToASCIIString("BINDINGS: int32 value expected");
return "BINDINGS: int32 value expected";
case Error::BINDINGS_DOUBLE_VALUE_EXPECTED:
return ToASCIIString("BINDINGS: double value expected");
return "BINDINGS: double value expected";
case Error::BINDINGS_STRING_VALUE_EXPECTED:
return ToASCIIString("BINDINGS: string value expected");
return "BINDINGS: string value expected";
case Error::BINDINGS_STRING8_VALUE_EXPECTED:
return ToASCIIString("BINDINGS: string8 value expected");
return "BINDINGS: string8 value expected";
case Error::BINDINGS_BINARY_VALUE_EXPECTED:
return ToASCIIString("BINDINGS: binary value expected");
return "BINDINGS: binary value expected";
}
// Some compilers can't figure out that we can't get here.
return "INVALID ERROR CODE";
}
std::string Status::ToASCIIString(const char* msg) const {
return std::string(msg) + " at position " + std::to_string(pos);
std::string Status::ToASCIIString() const {
if (ok())
return "OK";
return Message() + " at position " + std::to_string(pos);
}
} // namespace v8_crdtp
......@@ -18,7 +18,9 @@ namespace v8_crdtp {
enum class Error {
OK = 0,
// JSON parsing errors - json_parser.{h,cc}.
// JSON parsing errors; checked when parsing / converting from JSON.
// See json.{h,cc}.
JSON_PARSER_UNPROCESSED_INPUT_REMAINS = 0x01,
JSON_PARSER_STACK_LIMIT_EXCEEDED = 0x02,
JSON_PARSER_NO_INPUT = 0x03,
......@@ -33,6 +35,7 @@ enum class Error {
JSON_PARSER_COMMA_OR_MAP_END_EXPECTED = 0x0c,
JSON_PARSER_VALUE_EXPECTED = 0x0d,
// CBOR parsing errors; checked when parsing / converting from CBOR.
CBOR_INVALID_INT32 = 0x0e,
CBOR_INVALID_DOUBLE = 0x0f,
CBOR_INVALID_ENVELOPE = 0x10,
......@@ -48,20 +51,31 @@ enum class Error {
CBOR_UNEXPECTED_EOF_IN_ARRAY = 0x1a,
CBOR_UNEXPECTED_EOF_IN_MAP = 0x1b,
CBOR_INVALID_MAP_KEY = 0x1c,
CBOR_STACK_LIMIT_EXCEEDED = 0x1d,
CBOR_TRAILING_JUNK = 0x1e,
CBOR_MAP_START_EXPECTED = 0x1f,
CBOR_MAP_STOP_EXPECTED = 0x20,
CBOR_ARRAY_START_EXPECTED = 0x21,
CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x22,
BINDINGS_MANDATORY_FIELD_MISSING = 0x23,
BINDINGS_BOOL_VALUE_EXPECTED = 0x24,
BINDINGS_INT32_VALUE_EXPECTED = 0x25,
BINDINGS_DOUBLE_VALUE_EXPECTED = 0x26,
BINDINGS_STRING_VALUE_EXPECTED = 0x27,
BINDINGS_STRING8_VALUE_EXPECTED = 0x28,
BINDINGS_BINARY_VALUE_EXPECTED = 0x29,
CBOR_DUPLICATE_MAP_KEY = 0x1d,
CBOR_STACK_LIMIT_EXCEEDED = 0x1e,
CBOR_TRAILING_JUNK = 0x1f,
CBOR_MAP_START_EXPECTED = 0x20,
CBOR_MAP_STOP_EXPECTED = 0x21,
CBOR_ARRAY_START_EXPECTED = 0x22,
CBOR_ENVELOPE_SIZE_LIMIT_EXCEEDED = 0x23,
// Message errors are constraints we place on protocol messages coming
// from a protocol client; these are checked in crdtp::Dispatchable
// (see dispatch.h) as it performs a shallow parse.
MESSAGE_MUST_BE_AN_OBJECT = 0x24,
MESSAGE_MUST_HAVE_INTEGER_ID_PROPERTY = 0x25,
MESSAGE_MUST_HAVE_STRING_METHOD_PROPERTY = 0x26,
MESSAGE_MAY_HAVE_STRING_SESSION_ID_PROPERTY = 0x27,
MESSAGE_MAY_HAVE_OBJECT_PARAMS_PROPERTY = 0x28,
MESSAGE_HAS_UNKNOWN_PROPERTY = 0x29,
BINDINGS_MANDATORY_FIELD_MISSING = 0x30,
BINDINGS_BOOL_VALUE_EXPECTED = 0x31,
BINDINGS_INT32_VALUE_EXPECTED = 0x32,
BINDINGS_DOUBLE_VALUE_EXPECTED = 0x33,
BINDINGS_STRING_VALUE_EXPECTED = 0x34,
BINDINGS_STRING8_VALUE_EXPECTED = 0x35,
BINDINGS_BINARY_VALUE_EXPECTED = 0x36,
};
// A status value with position that can be copied. The default status
......@@ -76,12 +90,18 @@ struct Status {
Status(Error error, size_t pos) : error(error), pos(pos) {}
Status() = default;
// Returns a 7 bit US-ASCII string, either "OK" or an error message
// that includes the position.
std::string ToASCIIString() const;
bool IsMessageError() const {
return error >= Error::MESSAGE_MUST_BE_AN_OBJECT &&
error <= Error::MESSAGE_HAS_UNKNOWN_PROPERTY;
}
// Returns 7 bit US-ASCII string, either "OK" or an error message without
// position.
std::string Message() const;
private:
std::string ToASCIIString(const char* msg) const;
// Returns a 7 bit US-ASCII string, either "OK" or an error message that
// includes the position.
std::string ToASCIIString() const;
};
} // namespace v8_crdtp
......
......@@ -33,10 +33,7 @@ template("inspector_protocol_generate") {
invoker.config_file,
"$inspector_protocol_dir/lib/base_string_adapter_cc.template",
"$inspector_protocol_dir/lib/base_string_adapter_h.template",
"$inspector_protocol_dir/lib/DispatcherBase_cpp.template",
"$inspector_protocol_dir/lib/DispatcherBase_h.template",
"$inspector_protocol_dir/lib/Forward_h.template",
"$inspector_protocol_dir/lib/FrontendChannel_h.template",
"$inspector_protocol_dir/lib/Object_cpp.template",
"$inspector_protocol_dir/lib/Object_h.template",
"$inspector_protocol_dir/lib/Protocol_cpp.template",
......@@ -56,6 +53,7 @@ template("inspector_protocol_generate") {
"--jinja_dir",
rebase_path("//third_party/", root_build_dir), # jinja is in chromium's
# third_party
"--output_base",
rebase_path(invoker.out_dir, root_build_dir),
"--config",
......
// This file is generated by DispatcherBase_cpp.template.
// Copyright 2016 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.
//#include "DispatcherBase.h"
//#include "Parser.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
// static
DispatchResponse DispatchResponse::OK()
{
DispatchResponse result;
result.m_status = kSuccess;
result.m_errorCode = kParseError;
return result;
}
// static
DispatchResponse DispatchResponse::Error(const String& error)
{
DispatchResponse result;
result.m_status = kError;
result.m_errorCode = kServerError;
result.m_errorMessage = error;
return result;
}
// static
DispatchResponse DispatchResponse::InternalError()
{
DispatchResponse result;
result.m_status = kError;
result.m_errorCode = kInternalError;
result.m_errorMessage = "Internal error";
return result;
}
// static
DispatchResponse DispatchResponse::InvalidParams(const String& error)
{
DispatchResponse result;
result.m_status = kError;
result.m_errorCode = kInvalidParams;
result.m_errorMessage = error;
return result;
}
// static
DispatchResponse DispatchResponse::FallThrough()
{
DispatchResponse result;
result.m_status = kFallThrough;
result.m_errorCode = kParseError;
return result;
}
// static
const char DispatcherBase::kInvalidParamsString[] = "Invalid parameters";
DispatcherBase::WeakPtr::WeakPtr(DispatcherBase* dispatcher) : m_dispatcher(dispatcher) { }
DispatcherBase::WeakPtr::~WeakPtr()
{
if (m_dispatcher)
m_dispatcher->m_weakPtrs.erase(this);
}
DispatcherBase::Callback::Callback(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message)
: m_backendImpl(std::move(backendImpl))
, m_callId(callId)
, m_method(method)
, m_message(message.begin(), message.end()) { }
DispatcherBase::Callback::~Callback() = default;
void DispatcherBase::Callback::dispose()
{
m_backendImpl = nullptr;
}
void DispatcherBase::Callback::sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const DispatchResponse& response)
{
if (!m_backendImpl || !m_backendImpl->get())
return;
m_backendImpl->get()->sendResponse(m_callId, response, std::move(partialMessage));
m_backendImpl = nullptr;
}
void DispatcherBase::Callback::fallThroughIfActive()
{
if (!m_backendImpl || !m_backendImpl->get())
return;
m_backendImpl->get()->channel()->fallThrough(m_callId, m_method, {{config.crdtp.namespace}}::SpanFrom(m_message));
m_backendImpl = nullptr;
}
DispatcherBase::DispatcherBase(FrontendChannel* frontendChannel)
: m_frontendChannel(frontendChannel) { }
DispatcherBase::~DispatcherBase()
{
clearFrontend();
}
void DispatcherBase::sendResponse(int callId, const DispatchResponse& response, std::unique_ptr<protocol::DictionaryValue> result)
{
if (!m_frontendChannel)
return;
if (response.status() == DispatchResponse::kError) {
reportProtocolError(callId, response.errorCode(), response.errorMessage(), nullptr);
return;
}
m_frontendChannel->sendProtocolResponse(callId, InternalResponse::createResponse(callId, std::move(result)));
}
void DispatcherBase::sendResponse(int callId, const DispatchResponse& response)
{
sendResponse(callId, response, DictionaryValue::create());
}
namespace {
class ProtocolError : public Serializable {
public:
static std::unique_ptr<ProtocolError> createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors)
{
std::unique_ptr<ProtocolError> protocolError(new ProtocolError(code, errorMessage));
protocolError->m_callId = callId;
protocolError->m_hasCallId = true;
if (errors && !errors->Errors().empty()) {
protocolError->m_data =
StringUtil::fromUTF8(errors->Errors().data(), errors->Errors().size());
}
return protocolError;
}
static std::unique_ptr<ProtocolError> createErrorNotification(DispatchResponse::ErrorCode code, const String& errorMessage)
{
return std::unique_ptr<ProtocolError>(new ProtocolError(code, errorMessage));
}
void AppendSerialized(std::vector<uint8_t>* out) const override
{
toDictionary()->AppendSerialized(out);
}
~ProtocolError() override {}
private:
ProtocolError(DispatchResponse::ErrorCode code, const String& errorMessage)
: m_code(code)
, m_errorMessage(errorMessage)
{
}
std::unique_ptr<DictionaryValue> toDictionary() const {
std::unique_ptr<protocol::DictionaryValue> error = DictionaryValue::create();
error->setInteger("code", m_code);
error->setString("message", m_errorMessage);
if (m_data.length())
error->setString("data", m_data);
std::unique_ptr<protocol::DictionaryValue> message = DictionaryValue::create();
message->setObject("error", std::move(error));
if (m_hasCallId)
message->setInteger("id", m_callId);
return message;
}
DispatchResponse::ErrorCode m_code;
String m_errorMessage;
String m_data;
int m_callId = 0;
bool m_hasCallId = false;
};
} // namespace
static void reportProtocolErrorTo(FrontendChannel* frontendChannel, int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors)
{
if (frontendChannel)
frontendChannel->sendProtocolResponse(callId, ProtocolError::createErrorResponse(callId, code, errorMessage, errors));
}
static void reportProtocolErrorTo(FrontendChannel* frontendChannel, DispatchResponse::ErrorCode code, const String& errorMessage)
{
if (frontendChannel)
frontendChannel->sendProtocolNotification(ProtocolError::createErrorNotification(code, errorMessage));
}
void DispatcherBase::reportProtocolError(int callId, DispatchResponse::ErrorCode code, const String& errorMessage, ErrorSupport* errors)
{
reportProtocolErrorTo(m_frontendChannel, callId, code, errorMessage, errors);
}
void DispatcherBase::clearFrontend()
{
m_frontendChannel = nullptr;
for (auto& weak : m_weakPtrs)
weak->dispose();
m_weakPtrs.clear();
}
std::unique_ptr<DispatcherBase::WeakPtr> DispatcherBase::weakPtr()
{
std::unique_ptr<DispatcherBase::WeakPtr> weak(new DispatcherBase::WeakPtr(this));
m_weakPtrs.insert(weak.get());
return weak;
}
UberDispatcher::UberDispatcher(FrontendChannel* frontendChannel)
: m_frontendChannel(frontendChannel) { }
void UberDispatcher::registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase> dispatcher)
{
m_dispatchers[name] = std::move(dispatcher);
}
void UberDispatcher::setupRedirects(const std::unordered_map<String, String>& redirects)
{
for (const auto& pair : redirects)
m_redirects[pair.first] = pair.second;
}
bool UberDispatcher::parseCommand(Value* parsedMessage, int* outCallId, String* outMethod) {
if (!parsedMessage) {
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kParseError, "Message must be a valid JSON");
return false;
}
protocol::DictionaryValue* messageObject = DictionaryValue::cast(parsedMessage);
if (!messageObject) {
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must be an object");
return false;
}
int callId = 0;
protocol::Value* callIdValue = messageObject->get("id");
bool success = callIdValue && callIdValue->asInteger(&callId);
if (!success) {
reportProtocolErrorTo(m_frontendChannel, DispatchResponse::kInvalidRequest, "Message must have integer 'id' property");
return false;
}
if (outCallId)
*outCallId = callId;
protocol::Value* methodValue = messageObject->get("method");
String method;
success = methodValue && methodValue->asString(&method);
if (!success) {
reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kInvalidRequest, "Message must have string 'method' property", nullptr);
return false;
}
if (outMethod)
*outMethod = method;
return true;
}
protocol::DispatcherBase* UberDispatcher::findDispatcher(const String& method) {
size_t dotIndex = StringUtil::find(method, ".");
if (dotIndex == StringUtil::kNotFound)
return nullptr;
String domain = StringUtil::substring(method, 0, dotIndex);
auto it = m_dispatchers.find(domain);
if (it == m_dispatchers.end())
return nullptr;
if (!it->second->canDispatch(method))
return nullptr;
return it->second.get();
}
bool UberDispatcher::canDispatch(const String& in_method)
{
String method = in_method;
auto redirectIt = m_redirects.find(method);
if (redirectIt != m_redirects.end())
method = redirectIt->second;
return !!findDispatcher(method);
}
void UberDispatcher::dispatch(int callId, const String& in_method, std::unique_ptr<Value> parsedMessage, {{config.crdtp.namespace}}::span<uint8_t> rawMessage)
{
String method = in_method;
auto redirectIt = m_redirects.find(method);
if (redirectIt != m_redirects.end())
method = redirectIt->second;
protocol::DispatcherBase* dispatcher = findDispatcher(method);
if (!dispatcher) {
reportProtocolErrorTo(m_frontendChannel, callId, DispatchResponse::kMethodNotFound, "'" + method + "' wasn't found", nullptr);
return;
}
std::unique_ptr<protocol::DictionaryValue> messageObject = DictionaryValue::cast(std::move(parsedMessage));
dispatcher->dispatch(callId, method, rawMessage, std::move(messageObject));
}
UberDispatcher::~UberDispatcher() = default;
// static
std::unique_ptr<Serializable> InternalResponse::createResponse(int callId, std::unique_ptr<Serializable> params)
{
return std::unique_ptr<Serializable>(new InternalResponse(callId, nullptr, std::move(params)));
}
// static
std::unique_ptr<Serializable> InternalResponse::createNotification(const char* method, std::unique_ptr<Serializable> params)
{
return std::unique_ptr<Serializable>(new InternalResponse(0, method, std::move(params)));
}
// static
std::unique_ptr<Serializable> InternalResponse::createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& message)
{
return ProtocolError::createErrorResponse(callId, code, message, nullptr);
}
void InternalResponse::AppendSerialized(std::vector<uint8_t>* out) const
{
using {{config.crdtp.namespace}}::cbor::NewCBOREncoder;
using {{config.crdtp.namespace}}::ParserHandler;
using {{config.crdtp.namespace}}::Status;
using {{config.crdtp.namespace}}::SpanFrom;
Status status;
std::unique_ptr<ParserHandler> encoder = NewCBOREncoder(out, &status);
encoder->HandleMapBegin();
if (m_method) {
encoder->HandleString8(SpanFrom("method"));
encoder->HandleString8(SpanFrom(m_method));
encoder->HandleString8(SpanFrom("params"));
} else {
encoder->HandleString8(SpanFrom("id"));
encoder->HandleInt32(m_callId);
encoder->HandleString8(SpanFrom("result"));
}
if (m_params) {
m_params->AppendSerialized(out);
} else {
encoder->HandleMapBegin();
encoder->HandleMapEnd();
}
encoder->HandleMapEnd();
DCHECK(status.ok());
}
InternalResponse::InternalResponse(int callId, const char* method, std::unique_ptr<Serializable> params)
: m_callId(callId)
, m_method(method)
, m_params(params ? std::move(params) : nullptr)
{
}
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
// This file is generated by DispatcherBase_h.template.
// Copyright 2016 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 {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
#define {{"_".join(config.protocol.namespace)}}_DispatcherBase_h
//#include "Forward.h"
//#include "ErrorSupport.h"
//#include "Values.h"
#include "{{config.crdtp.dir}}/span.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
class WeakPtr;
class {{config.lib.export_macro}} DispatchResponse {
public:
enum Status {
kSuccess = 0,
kError = 1,
kFallThrough = 2,
};
// For historical reasons, these error codes correspond to commonly used
// XMLRPC codes (e.g. see METHOD_NOT_FOUND in
// https://github.com/python/cpython/blob/master/Lib/xmlrpc/client.py).
enum ErrorCode {
kParseError = -32700,
kInvalidRequest = -32600,
kMethodNotFound = -32601,
kInvalidParams = -32602,
kInternalError = -32603,
kServerError = -32000,
};
Status status() const { return m_status; }
const String& errorMessage() const { return m_errorMessage; }
ErrorCode errorCode() const { return m_errorCode; }
bool isSuccess() const { return m_status == kSuccess; }
static DispatchResponse OK();
static DispatchResponse Error(const String&);
static DispatchResponse InternalError();
static DispatchResponse InvalidParams(const String&);
static DispatchResponse FallThrough();
private:
Status m_status;
String m_errorMessage;
ErrorCode m_errorCode;
};
class {{config.lib.export_macro}} DispatcherBase {
PROTOCOL_DISALLOW_COPY(DispatcherBase);
public:
static const char kInvalidParamsString[];
class {{config.lib.export_macro}} WeakPtr {
public:
explicit WeakPtr(DispatcherBase*);
~WeakPtr();
DispatcherBase* get() { return m_dispatcher; }
void dispose() { m_dispatcher = nullptr; }
private:
DispatcherBase* m_dispatcher;
};
class {{config.lib.export_macro}} Callback {
public:
Callback(std::unique_ptr<WeakPtr> backendImpl, int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message);
virtual ~Callback();
void dispose();
protected:
void sendIfActive(std::unique_ptr<protocol::DictionaryValue> partialMessage, const DispatchResponse& response);
void fallThroughIfActive();
private:
std::unique_ptr<WeakPtr> m_backendImpl;
int m_callId;
String m_method;
std::vector<uint8_t> m_message;
};
explicit DispatcherBase(FrontendChannel*);
virtual ~DispatcherBase();
virtual bool canDispatch(const String& method) = 0;
virtual void dispatch(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> rawMessage, std::unique_ptr<protocol::DictionaryValue> messageObject) = 0;
FrontendChannel* channel() { return m_frontendChannel; }
void sendResponse(int callId, const DispatchResponse&, std::unique_ptr<protocol::DictionaryValue> result);
void sendResponse(int callId, const DispatchResponse&);
void reportProtocolError(int callId, DispatchResponse::ErrorCode, const String& errorMessage, ErrorSupport* errors);
void clearFrontend();
std::unique_ptr<WeakPtr> weakPtr();
private:
FrontendChannel* m_frontendChannel;
std::unordered_set<WeakPtr*> m_weakPtrs;
};
class {{config.lib.export_macro}} UberDispatcher {
PROTOCOL_DISALLOW_COPY(UberDispatcher);
public:
explicit UberDispatcher(FrontendChannel*);
void registerBackend(const String& name, std::unique_ptr<protocol::DispatcherBase>);
void setupRedirects(const std::unordered_map<String, String>&);
bool parseCommand(Value* message, int* callId, String* method);
bool canDispatch(const String& method);
void dispatch(int callId, const String& method, std::unique_ptr<Value> message, {{config.crdtp.namespace}}::span<uint8_t> rawMessage);
FrontendChannel* channel() { return m_frontendChannel; }
virtual ~UberDispatcher();
private:
protocol::DispatcherBase* findDispatcher(const String& method);
FrontendChannel* m_frontendChannel;
std::unordered_map<String, String> m_redirects;
std::unordered_map<String, std::unique_ptr<protocol::DispatcherBase>> m_dispatchers;
};
class InternalResponse : public Serializable {
PROTOCOL_DISALLOW_COPY(InternalResponse);
public:
static std::unique_ptr<Serializable> createResponse(int callId, std::unique_ptr<Serializable> params);
static std::unique_ptr<Serializable> createNotification(const char* method, std::unique_ptr<Serializable> params = nullptr);
static std::unique_ptr<Serializable> createErrorResponse(int callId, DispatchResponse::ErrorCode code, const String& message);
void AppendSerialized(std::vector<uint8_t>* out) const override;
~InternalResponse() override {}
private:
InternalResponse(int callId, const char* method, std::unique_ptr<Serializable> params);
int m_callId;
const char* m_method = nullptr;
std::unique_ptr<Serializable> m_params;
};
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
#endif // !defined({{"_".join(config.protocol.namespace)}}_DispatcherBase_h)
......@@ -19,6 +19,8 @@
#include <unordered_set>
#include "{{config.crdtp.dir}}/error_support.h"
#include "{{config.crdtp.dir}}/dispatch.h"
#include "{{config.crdtp.dir}}/frontend_channel.h"
#include "{{config.crdtp.dir}}/glue.h"
{% for namespace in config.protocol.namespace %}
......@@ -26,15 +28,18 @@ namespace {{namespace}} {
{% endfor %}
class DictionaryValue;
class DispatchResponse;
using DispatchResponse = {{config.crdtp.namespace}}::DispatchResponse;
using ErrorSupport = {{config.crdtp.namespace}}::ErrorSupport;
using Serializable = {{config.crdtp.namespace}}::Serializable;
using FrontendChannel = {{config.crdtp.namespace}}::FrontendChannel;
using DomainDispatcher = {{config.crdtp.namespace}}::DomainDispatcher;
using UberDispatcher = {{config.crdtp.namespace}}::UberDispatcher;
class FundamentalValue;
class ListValue;
class Object;
using Response = DispatchResponse;
class SerializedValue;
class StringValue;
class UberDispatcher;
class Value;
namespace detail {
......
// This file is generated by FrontendChannel_h.template.
// Copyright 2016 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 {{"_".join(config.protocol.namespace)}}_FrontendChannel_h
#define {{"_".join(config.protocol.namespace)}}_FrontendChannel_h
#include "{{config.crdtp.dir}}/serializable.h"
#include "{{config.crdtp.dir}}/span.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
{% endfor %}
using {{config.crdtp.namespace}}::Serializable;
class {{config.lib.export_macro}} FrontendChannel {
public:
virtual ~FrontendChannel() { }
virtual void sendProtocolResponse(int callId, std::unique_ptr<Serializable> message) = 0;
virtual void sendProtocolNotification(std::unique_ptr<Serializable> message) = 0;
virtual void fallThrough(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message) = 0;
virtual void flushProtocolNotifications() = 0;
};
{% for namespace in config.protocol.namespace %}
} // namespace {{namespace}}
{% endfor %}
#endif // !defined({{"_".join(config.protocol.namespace)}}_FrontendChannel_h)
......@@ -34,17 +34,6 @@ using String = std::string;
class {{config.lib.export_macro}} StringUtil {
public:
static String substring(const String& s, unsigned pos, unsigned len) {
return s.substr(pos, len);
}
static size_t find(const String& s, const char* needle) {
return s.find(needle);
}
static size_t find(const String& s, const String& needle) {
return s.find(needle);
}
static const size_t kNotFound = static_cast<size_t>(-1);
static String fromUTF8(const uint8_t* data, size_t length) {
return std::string(reinterpret_cast<const char*>(data), length);
}
......
......@@ -21,9 +21,16 @@ FILES_TO_SYNC = [
'crdtp/cbor.cc',
'crdtp/cbor.h',
'crdtp/cbor_test.cc',
'crdtp/dispatch.h',
'crdtp/dispatch.cc',
'crdtp/dispatch_test.cc',
'crdtp/error_support.cc',
'crdtp/error_support.h',
'crdtp/error_support_test.cc',
'crdtp/export_template.h',
'crdtp/find_by_first.h',
'crdtp/find_by_first_test.cc',
'crdtp/frontend_channel.h',
'crdtp/glue.h',
'crdtp/glue_test.cc',
'crdtp/json.cc',
......@@ -36,6 +43,7 @@ FILES_TO_SYNC = [
'crdtp/serializable_test.cc',
'crdtp/serializer_traits.h',
'crdtp/serializer_traits_test.cc',
'crdtp/span.cc',
'crdtp/span.h',
'crdtp/span_test.cc',
'crdtp/status.cc',
......
......@@ -9,7 +9,9 @@
#include {{format_include(config.protocol.package, "Protocol")}}
#include "{{config.crdtp.dir}}/cbor.h"
#include "{{config.crdtp.dir}}/find_by_first.h"
#include "{{config.crdtp.dir}}/serializer_traits.h"
#include "{{config.crdtp.dir}}/span.h"
{% for namespace in config.protocol.namespace %}
namespace {{namespace}} {
......@@ -169,7 +171,7 @@ void Frontend::{{event.name | to_method_case}}(
{%- endif %} {{parameter.name}}{%- if not loop.last -%}, {% endif -%}
{% endfor -%})
{
if (!m_frontendChannel)
if (!frontend_channel_)
return;
{% if event.parameters %}
std::unique_ptr<{{event.name | to_title_case}}Notification> messageData = {{event.name | to_title_case}}Notification::{{"create" | to_method_case}}()
......@@ -185,69 +187,79 @@ void Frontend::{{event.name | to_method_case}}(
messageData->{{"set" | to_method_case}}{{parameter.name | to_title_case}}(std::move({{parameter.name}}).takeJust());
{% endif %}
{% endfor %}
m_frontendChannel->sendProtocolNotification(InternalResponse::createNotification("{{domain.domain}}.{{event.name}}", std::move(messageData)));
frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}", std::move(messageData)));
{% else %}
m_frontendChannel->sendProtocolNotification(InternalResponse::createNotification("{{domain.domain}}.{{event.name}}"));
frontend_channel_->SendProtocolNotification({{config.crdtp.namespace}}::CreateNotification("{{domain.domain}}.{{event.name}}"));
{% endif %}
}
{% endfor %}
void Frontend::flush()
{
m_frontendChannel->flushProtocolNotifications();
frontend_channel_->FlushProtocolNotifications();
}
void Frontend::sendRawNotification(std::unique_ptr<Serializable> notification)
{
m_frontendChannel->sendProtocolNotification(std::move(notification));
frontend_channel_->SendProtocolNotification(std::move(notification));
}
// --------------------- Dispatcher.
class DispatcherImpl : public protocol::DispatcherBase {
class DomainDispatcherImpl : public protocol::DomainDispatcher {
public:
DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend)
: DispatcherBase(frontendChannel)
, m_backend(backend) {
{% for command in domain.commands %}
{% if "redirect" in command %}
m_redirects["{{domain.domain}}.{{command.name}}"] = "{{command.redirect}}.{{command.name}}";
{% continue %}
{% endif %}
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
m_dispatchMap["{{domain.domain}}.{{command.name}}"] = &DispatcherImpl::{{command.name}};
{% endfor %}
}
~DispatcherImpl() override { }
bool canDispatch(const String& method) override;
void dispatch(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message, std::unique_ptr<protocol::DictionaryValue> messageObject) override;
std::unordered_map<String, String>& redirects() { return m_redirects; }
DomainDispatcherImpl(FrontendChannel* frontendChannel, Backend* backend)
: DomainDispatcher(frontendChannel)
, m_backend(backend) {}
~DomainDispatcherImpl() override { }
protected:
using CallHandler = void (DispatcherImpl::*)(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message, std::unique_ptr<DictionaryValue> messageObject, ErrorSupport* errors);
using DispatchMap = std::unordered_map<String, CallHandler>;
DispatchMap m_dispatchMap;
std::unordered_map<String, String> m_redirects;
using CallHandler = void (DomainDispatcherImpl::*)(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors);
std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) override;
{% for command in domain.commands %}
{% if "redirect" in command %}{% continue %}{% endif %}
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
void {{command.name}}(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport*);
void {{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors);
{% endfor %}
protected:
Backend* m_backend;
};
bool DispatcherImpl::canDispatch(const String& method) {
return m_dispatchMap.find(method) != m_dispatchMap.end();
namespace {
// This helper method with a static map of command methods (instance methods
// of DomainDispatcherImpl declared just above) by their name is used immediately below,
// in the DomainDispatcherImpl::Dispatch method.
DomainDispatcherImpl::CallHandler CommandByName({{config.crdtp.namespace}}::span<uint8_t> command_name) {
static auto* commands = [](){
auto* commands = new std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>,
DomainDispatcherImpl::CallHandler>>{
{% for command in domain.commands|sort(attribute="name",case_sensitive=True) %}
{% if "redirect" in command %}{% continue %}{% endif %}
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
{
{{config.crdtp.namespace}}::SpanFrom("{{command.name}}"),
&DomainDispatcherImpl::{{command.name}}
},
{% endfor %}
};
return commands;
}();
return {{config.crdtp.namespace}}::FindByFirst<DomainDispatcherImpl::CallHandler>(*commands, command_name, nullptr);
}
void DispatcherImpl::dispatch(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message, std::unique_ptr<protocol::DictionaryValue> messageObject)
{
std::unordered_map<String, CallHandler>::iterator it = m_dispatchMap.find(method);
DCHECK(it != m_dispatchMap.end());
protocol::ErrorSupport errors;
(this->*(it->second))(callId, method, message, std::move(messageObject), &errors);
} // namespace
std::function<void(const {{config.crdtp.namespace}}::Dispatchable&)> DomainDispatcherImpl::Dispatch({{config.crdtp.namespace}}::span<uint8_t> command_name) {
CallHandler handler = CommandByName(command_name);
if (!handler) return nullptr;
return [this, handler](const {{config.crdtp.namespace}}::Dispatchable& dispatchable){
std::unique_ptr<DictionaryValue> params =
DictionaryValue::cast(protocol::Value::parseBinary(dispatchable.Params().data(),
dispatchable.Params().size()));
ErrorSupport errors;
errors.Push();
(this->*handler)(dispatchable, params.get(), &errors);
};
}
{% for command in domain.commands %}
......@@ -256,10 +268,11 @@ void DispatcherImpl::dispatch(int callId, const String& method, {{config.crdtp.n
{% if not protocol.generate_command(domain.domain, command.name) %}{% continue %}{% endif %}
{% if protocol.is_async_command(domain.domain, command.name) %}
class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DispatcherBase::Callback {
class {{command_name_title}}CallbackImpl : public Backend::{{command_name_title}}Callback, public DomainDispatcher::Callback {
public:
{{command_name_title}}CallbackImpl(std::unique_ptr<DispatcherBase::WeakPtr> backendImpl, int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message)
: DispatcherBase::Callback(std::move(backendImpl), callId, method, message) { }
{{command_name_title}}CallbackImpl(std::unique_ptr<DomainDispatcher::WeakPtr> backendImpl, int callId, {{config.crdtp.namespace}}::span<uint8_t> message)
: DomainDispatcher::Callback(std::move(backendImpl), callId,
{{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), message) { }
void sendSuccess(
{%- for parameter in command.returns -%}
......@@ -271,16 +284,16 @@ public:
{%- if not loop.last -%}, {% endif -%}
{%- endfor -%}) override
{
std::unique_ptr<protocol::DictionaryValue> resultObject = DictionaryValue::create();
{% for parameter in command.returns %}
{% if "optional" in parameter %}
if ({{parameter.name}}.isJust())
resultObject->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue({{parameter.name}}.fromJust()));
{% else %}
resultObject->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue({{protocol.resolve_type(parameter).to_raw_type % parameter.name}}));
{% endif %}
{% endfor %}
sendIfActive(std::move(resultObject), DispatchResponse::OK());
std::vector<uint8_t> result_buffer;
{{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
envelope_encoder.EncodeStart(&result_buffer);
result_buffer.push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
{% for parameter in command.returns %}
{{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{parameter.name}}"), {{parameter.name}}, &result_buffer);
{% endfor %}
result_buffer.push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
envelope_encoder.EncodeStop(&result_buffer);
sendIfActive({{config.crdtp.namespace}}::Serializable::From(std::move(result_buffer)), DispatchResponse::Success());
}
void fallThrough() override
......@@ -290,21 +303,19 @@ public:
void sendFailure(const DispatchResponse& response) override
{
DCHECK(response.status() == DispatchResponse::kError);
DCHECK(response.IsError());
sendIfActive(nullptr, response);
}
};
{% endif %}
void DispatcherImpl::{{command.name}}(int callId, const String& method, {{config.crdtp.namespace}}::span<uint8_t> message, std::unique_ptr<DictionaryValue> requestMessageObject, ErrorSupport* errors)
void DomainDispatcherImpl::{{command.name}}(const {{config.crdtp.namespace}}::Dispatchable& dispatchable, DictionaryValue* params, ErrorSupport* errors)
{
{% if "parameters" in command %}
// Prepare input parameters.
protocol::DictionaryValue* object = DictionaryValue::cast(requestMessageObject->get("params"));
errors->Push();
{% for parameter in command.parameters %}
{% set parameter_type = protocol.resolve_type(parameter) %}
protocol::Value* {{parameter.name}}Value = object ? object->get("{{parameter.name}}") : nullptr;
protocol::Value* {{parameter.name}}Value = params ? params->get("{{parameter.name}}") : nullptr;
{% if parameter.optional %}
Maybe<{{parameter_type.raw_type}}> in_{{parameter.name}};
if ({{parameter.name}}Value) {
......@@ -316,11 +327,7 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, {{config
{{parameter_type.type}} in_{{parameter.name}} = ValueConversions<{{parameter_type.raw_type}}>::fromValue({{parameter.name}}Value, errors);
{% endif %}
{% endfor %}
errors->Pop();
if (!errors->Errors().empty()) {
reportProtocolError(callId, DispatchResponse::kInvalidParams, kInvalidParamsString, errors);
return;
}
if (MaybeReportInvalidParams(dispatchable, *errors)) return;
{% endif %}
{% if "returns" in command and not protocol.is_async_command(domain.domain, command.name) %}
// Declare output parameters.
......@@ -334,7 +341,7 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, {{config
{% endif %}
{% if not protocol.is_async_command(domain.domain, command.name) %}
std::unique_ptr<DispatcherBase::WeakPtr> weak = weakPtr();
std::unique_ptr<DomainDispatcher::WeakPtr> weak = weakPtr();
DispatchResponse response = m_backend->{{command.name | to_method_case}}(
{%- for parameter in command.parameters -%}
{%- if not loop.first -%}, {% endif -%}
......@@ -350,31 +357,31 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, {{config
&out_{{parameter.name}}
{%- endfor %}
{% endif %});
if (response.status() == DispatchResponse::kFallThrough) {
channel()->fallThrough(callId, method, message);
if (response.IsFallThrough()) {
channel()->FallThrough(dispatchable.CallId(), {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), dispatchable.Serialized());
return;
}
{% if "returns" in command %}
std::unique_ptr<protocol::DictionaryValue> result = DictionaryValue::create();
if (response.status() == DispatchResponse::kSuccess) {
{% for parameter in command.returns %}
{% if "optional" in parameter %}
if (out_{{parameter.name}}.isJust())
result->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue(out_{{parameter.name}}.fromJust()));
{% else %}
result->setValue("{{parameter.name}}", ValueConversions<{{protocol.resolve_type(parameter).raw_type}}>::toValue({{protocol.resolve_type(parameter).to_raw_type % ("out_" + parameter.name)}}));
{% endif %}
{% endfor %}
}
if (weak->get())
weak->get()->sendResponse(callId, response, std::move(result));
if (weak->get()) {
std::vector<uint8_t> result;
if (response.IsSuccess()) {
{{config.crdtp.namespace}}::cbor::EnvelopeEncoder envelope_encoder;
envelope_encoder.EncodeStart(&result);
result.push_back({{config.crdtp.namespace}}::cbor::EncodeIndefiniteLengthMapStart());
{% for parameter in command.returns %}
{{config.crdtp.namespace}}::SerializeField({{config.crdtp.namespace}}::SpanFrom("{{parameter.name}}"), out_{{parameter.name}}, &result);
{% endfor %}
result.push_back({{config.crdtp.namespace}}::cbor::EncodeStop());
envelope_encoder.EncodeStop(&result);
}
weak->get()->sendResponse(dispatchable.CallId(), response, {{config.crdtp.namespace}}::Serializable::From(std::move(result)));
}
{% else %}
if (weak->get())
weak->get()->sendResponse(callId, response);
weak->get()->sendResponse(dispatchable.CallId(), response);
{% endif %}
return;
{% else %}
std::unique_ptr<{{command_name_title}}CallbackImpl> callback(new {{command.name | to_title_case}}CallbackImpl(weakPtr(), callId, method, message));
m_backend->{{command.name | to_method_case}}(
{%- for property in command.parameters -%}
{%- if not loop.first -%}, {% endif -%}
......@@ -385,18 +392,34 @@ void DispatcherImpl::{{command.name}}(int callId, const String& method, {{config
{%- endif -%}
{%- endfor -%}
{%- if command.parameters -%}, {% endif -%}
std::move(callback));
return;
std::make_unique<{{command_name_title}}CallbackImpl>(weakPtr(), dispatchable.CallId(), dispatchable.Serialized()));
{% endif %}
}
{% endfor %}
namespace {
// This helper method (with a static map of redirects) is used from Dispatcher::wire
// immediately below.
const std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>, {{config.crdtp.namespace}}::span<uint8_t>>>& SortedRedirects() {
static auto* redirects = [](){
auto* redirects = new std::vector<std::pair<{{config.crdtp.namespace}}::span<uint8_t>, {{config.crdtp.namespace}}::span<uint8_t>>>{
{% for command in domain.commands|sort(attribute="name",case_sensitive=True) %}
{% if "redirect" in command %}
{ {{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}.{{command.name}}"), {{config.crdtp.namespace}}::SpanFrom("{{command.redirect}}.{{command.name}}") },
{% endif %}
{% endfor %}
};
return redirects;
}();
return *redirects;
}
} // namespace
// static
void Dispatcher::wire(UberDispatcher* uber, Backend* backend)
{
std::unique_ptr<DispatcherImpl> dispatcher(new DispatcherImpl(uber->channel(), backend));
uber->setupRedirects(dispatcher->redirects());
uber->registerBackend("{{domain.domain}}", std::move(dispatcher));
auto dispatcher = std::make_unique<DomainDispatcherImpl>(uber->channel(), backend);
uber->WireBackend({{config.crdtp.namespace}}::SpanFrom("{{domain.domain}}"), SortedRedirects(), std::move(dispatcher));
}
} // {{domain.domain}}
......
......@@ -239,7 +239,7 @@ public:
{% if protocol.generate_disable(domain) %}
virtual DispatchResponse {{"disable" | to_method_case}}()
{
return DispatchResponse::OK();
return DispatchResponse::Success();
}
{% endif %}
};
......@@ -248,7 +248,7 @@ public:
class {{config.protocol.export_macro}} Frontend {
public:
explicit Frontend(FrontendChannel* frontendChannel) : m_frontendChannel(frontendChannel) { }
explicit Frontend(FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {}
{% for event in domain.events %}
{% if not protocol.generate_event(domain.domain, event.name) %}{% continue %}{% endif %}
void {{event.name | to_method_case}}(
......@@ -262,10 +262,10 @@ public:
);
{% endfor %}
void flush();
void sendRawNotification(std::unique_ptr<Serializable>);
private:
FrontendChannel* m_frontendChannel;
void flush();
void sendRawNotification(std::unique_ptr<Serializable>);
private:
FrontendChannel* frontend_channel_;
};
// ------------- Dispatcher.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment