mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-02 09:21:44 +08:00
Remove Inspector Logic (cannot work on iOS / System JSC)
Reviewed By: javache Differential Revision: D4620530 fbshipit-source-id: 52abc6178b1ad1b52ba1b1825702c9c254a04520
This commit is contained in:
committed by
Facebook Github Bot
parent
4ac585b34b
commit
3e528b1014
@@ -51,6 +51,5 @@ cxx_library(
|
||||
"//xplat/fbsystrace:fbsystrace",
|
||||
react_native_xplat_target("cxxreact:bridge"),
|
||||
react_native_xplat_target("cxxreact:module"),
|
||||
react_native_xplat_target("inspector:inspector"),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "JInspector.h"
|
||||
|
||||
#ifdef WITH_INSPECTOR
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
@@ -88,3 +90,5 @@ void JInspector::registerNatives() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_INSPECTOR
|
||||
|
||||
#include <inspector/Inspector.h>
|
||||
|
||||
#include <fb/fbjni.h>
|
||||
@@ -59,3 +61,5 @@ private:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,8 +27,7 @@ if THIS_IS_FBANDROID:
|
||||
'-Wno-pessimizing-move',
|
||||
],
|
||||
deps = [
|
||||
'//xplat/folly:molly',
|
||||
react_native_xplat_target('inspector:inspector'),
|
||||
'//xplat/folly:molly'
|
||||
])
|
||||
|
||||
cxx_library(
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
#include "Error.h"
|
||||
#include "Protocol.h"
|
||||
|
||||
#include <folly/json.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void Agent::onConnect(std::shared_ptr<Channel> channel) {
|
||||
channel_ = std::move(channel);
|
||||
|
||||
channel_->registerDomain(getDomain(), [this](std::string, int callId, const std::string& method, folly::dynamic args) {
|
||||
auto result = handle(method, std::move(args));
|
||||
if (result.isNull()) {
|
||||
result = folly::dynamic::object;
|
||||
}
|
||||
auto message = folly::dynamic::object("id", callId)("result", std::move(result));
|
||||
channel_->sendMessage(folly::toJson(std::move(message)));
|
||||
});
|
||||
}
|
||||
|
||||
void Agent::onDisconnect() {
|
||||
channel_.reset();
|
||||
}
|
||||
|
||||
folly::dynamic Agent::handle(const std::string& method, folly::dynamic args) {
|
||||
try {
|
||||
return methods_.at(method)(std::move(args));
|
||||
} catch (const std::out_of_range& e) {
|
||||
throw InspectorException(ErrorCode::MethodNotFound, "Unknown method: '" + method + "'");
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::registerMethod(std::string name, Method method) {
|
||||
methods_.emplace(std::move(name), std::move(method));
|
||||
}
|
||||
|
||||
void Agent::sendEvent(std::string name, folly::dynamic params) {
|
||||
if (!channel_) {
|
||||
return;
|
||||
}
|
||||
channel_->sendMessage(Event(getDomain(), std::move(name), std::move(params)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Dispatcher.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* An dispatcher that makes it simple to implement an agent that serves a single domain.
|
||||
*/
|
||||
class Agent : public Dispatcher {
|
||||
public:
|
||||
void onConnect(std::shared_ptr<Channel> channel) override;
|
||||
void onDisconnect() override;
|
||||
protected:
|
||||
using Method = std::function<folly::dynamic(folly::dynamic)>;
|
||||
void registerMethod(std::string name, Method method);
|
||||
void sendEvent(std::string name, folly::dynamic params = nullptr);
|
||||
|
||||
virtual std::string getDomain() = 0;
|
||||
private:
|
||||
folly::dynamic handle(const std::string& method, folly::dynamic args);
|
||||
|
||||
std::shared_ptr<Channel> channel_;
|
||||
std::unordered_map<std::string, Method> methods_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := inspector
|
||||
|
||||
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
|
||||
|
||||
LOCAL_CFLAGS := \
|
||||
-DLOG_TAG=\"ReactNative\"
|
||||
|
||||
LOCAL_CFLAGS += -Wall -Werror -fexceptions -frtti
|
||||
CXX11_FLAGS := -std=c++11
|
||||
LOCAL_CFLAGS += $(CXX11_FLAGS)
|
||||
LOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := jschelpers
|
||||
LOCAL_SHARED_LIBRARIES := libfolly_json libjsc libglog
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
$(call import-module,folly)
|
||||
$(call import-module,jsc)
|
||||
$(call import-module,glog)
|
||||
$(call import-module,jschelpers)
|
||||
@@ -1,60 +0,0 @@
|
||||
EXPORTED_HEADERS = [
|
||||
"Inspector.h",
|
||||
]
|
||||
|
||||
def library(**kwargs):
|
||||
if THIS_IS_FBANDROID:
|
||||
include_defs('//ReactAndroid/DEFS')
|
||||
|
||||
cxx_library(
|
||||
force_static = True,
|
||||
# We depend on JSC, support the same platforms
|
||||
supported_platforms_regex = '^android-(armv7|x86)$',
|
||||
deps = [
|
||||
'//xplat/folly:molly',
|
||||
react_native_xplat_target('jschelpers:jschelpers'),
|
||||
],
|
||||
**kwargs
|
||||
)
|
||||
elif THIS_IS_FBOBJC:
|
||||
ios_library(
|
||||
inherited_buck_flags = STATIC_LIBRARY_IOS_FLAGS,
|
||||
frameworks = [
|
||||
'$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework',
|
||||
],
|
||||
deps = [
|
||||
'//xplat/folly:molly',
|
||||
react_native_xplat_target('jschelpers:jschelpers'),
|
||||
],
|
||||
**kwargs
|
||||
)
|
||||
else:
|
||||
raise Error('Unknown repo')
|
||||
|
||||
library(
|
||||
name = "inspector",
|
||||
srcs = glob(["*.cpp"]),
|
||||
compiler_flags = [
|
||||
"-Wall",
|
||||
"-Wno-shadow",
|
||||
"-Wno-inconsistent-missing-override",
|
||||
"-Wno-unused-local-typedef",
|
||||
"-Wno-unused-private-field",
|
||||
"-Wno-undefined-bool-conversion",
|
||||
"-fexceptions",
|
||||
"-fvisibility=hidden",
|
||||
"-std=gnu++1y",
|
||||
],
|
||||
exported_headers = EXPORTED_HEADERS,
|
||||
header_namespace = "inspector",
|
||||
headers = subdir_glob(
|
||||
[("inspector", "*.h")],
|
||||
excludes = EXPORTED_HEADERS,
|
||||
),
|
||||
preprocessor_flags = [
|
||||
"-DENABLE_INSPECTOR=1",
|
||||
],
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
)
|
||||
@@ -1,152 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "ConsoleAgent.h"
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/APICast.h>
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
#include <JavaScriptCore/JSObjectRef.h>
|
||||
#include <JavaScriptCore/JSValueRef.h>
|
||||
#include <JavaScriptCore/JSStringRef.h>
|
||||
|
||||
#include <JavaScriptCore/JSLock.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/JSArray.h>
|
||||
#include <JavaScriptCore/InjectedScriptManager.h>
|
||||
#include <JavaScriptCore/ScriptArguments.h>
|
||||
#include <JavaScriptCore/ScriptCallStack.h>
|
||||
#include <JavaScriptCore/ScriptCallStackFactory.h>
|
||||
|
||||
#include <folly/json.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
static JSValueRef inspectorLog(
|
||||
ConsoleAgent* agent,
|
||||
JSContextRef ctx,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]) {
|
||||
CHECK(argumentCount == 4) << "__inspectorLog takes 4 args";
|
||||
auto execState = toJS(ctx);
|
||||
JSC::JSLockHolder lock(execState);
|
||||
auto params = toJS(execState, arguments[2]);
|
||||
agent->log(
|
||||
execState,
|
||||
Value(ctx, arguments[0]).toString().str(),
|
||||
Value(ctx, arguments[1]).toString().str(),
|
||||
JSC::asArray(params),
|
||||
Value(ctx, arguments[3]).asUnsignedInteger());
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
size_t skipNativeCode(const Inspector::ScriptCallStack& callStack, size_t offset) {
|
||||
for (; offset < callStack.size(); offset++) {
|
||||
auto& frame = callStack.at(offset);
|
||||
if (frame.sourceURL() != "[native code]") {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
return callStack.size();
|
||||
}
|
||||
|
||||
const Inspector::ScriptCallFrame* firstUserFrame(const Inspector::ScriptCallStack& callStack, size_t framesToSkip) {
|
||||
// Skip out of native code
|
||||
size_t offset = skipNativeCode(callStack, 0);
|
||||
|
||||
// Skip frames of console polyfill
|
||||
offset = skipNativeCode(callStack, offset + framesToSkip);
|
||||
if (offset >= callStack.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (callStack.at(offset).functionName() == "infoLog") {
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (offset >= callStack.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &callStack.at(offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ConsoleAgent::ConsoleAgent(JSC::JSGlobalObject& globalObject, Inspector::InjectedScriptManager* injectedScriptManager)
|
||||
: globalObject_(globalObject)
|
||||
, injectedScriptManager_(injectedScriptManager) {
|
||||
registerMethod("enable", [this](folly::dynamic) -> folly::dynamic {
|
||||
using namespace std::placeholders;
|
||||
enabled_ = true;
|
||||
JSGlobalContextRef context = toGlobalRef(globalObject_.globalExec());
|
||||
installGlobalFunction(context, "__inspectorLog", std::bind(&inspectorLog, this, _1, _2, _3, _4));
|
||||
return nullptr;
|
||||
});
|
||||
registerMethod("disable", [this](folly::dynamic) -> folly::dynamic {
|
||||
JSGlobalContextRef context = toGlobalRef(globalObject_.globalExec());
|
||||
removeGlobal(context, "__inspectorLog");
|
||||
enabled_ = false;
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void ConsoleAgent::log(JSC::ExecState* execState, std::string message) {
|
||||
log(execState, "log", std::move(message), nullptr, 0);
|
||||
}
|
||||
|
||||
void ConsoleAgent::log(JSC::ExecState* execState, std::string level, std::string message, JSC::JSArray* params, size_t framesToSkip) {
|
||||
if (!enabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto callStack = Inspector::createScriptCallStack(execState, Inspector::ScriptCallStack::maxCallStackSizeToCapture);
|
||||
|
||||
auto logEntry = folly::dynamic::object
|
||||
("source", "console-api")
|
||||
("level", level)
|
||||
("text", std::move(message))
|
||||
("timestamp", Timestamp::now())
|
||||
("stackTrace", folly::parseJson(toStdString(callStack->buildInspectorArray()->toJSONString())));
|
||||
|
||||
if (params) {
|
||||
logEntry("parameters", convertParams(execState, params));
|
||||
}
|
||||
|
||||
if (auto frame = firstUserFrame(*callStack, framesToSkip)) {
|
||||
logEntry
|
||||
("url", toStdString(frame->sourceURL()))
|
||||
("line", frame->lineNumber())
|
||||
("column", frame->columnNumber());
|
||||
}
|
||||
|
||||
sendEvent("messageAdded", folly::dynamic::object("message", std::move(logEntry)));
|
||||
}
|
||||
|
||||
folly::dynamic ConsoleAgent::convertParams(JSC::ExecState* execState, JSC::JSArray* params) {
|
||||
auto injectedScript = injectedScriptManager_->injectedScriptFor(execState->lexicalGlobalObject()->globalExec());
|
||||
if (injectedScript.hasNoValue()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
folly::dynamic remoteParams = folly::dynamic::array;
|
||||
for (size_t i = 0, size = params->length(); i < size; i++) {
|
||||
auto scriptValue = Deprecated::ScriptValue(execState->vm(), params->getIndex(execState, i));
|
||||
auto remoteValue = injectedScript.wrapObject(std::move(scriptValue), "console", true);
|
||||
remoteParams.push_back(folly::parseJson(toStdString(remoteValue->toJSONString())));
|
||||
}
|
||||
|
||||
return remoteParams;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
class ExecState;
|
||||
class JSArray;
|
||||
}
|
||||
|
||||
namespace Inspector {
|
||||
class InjectedScriptManager;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/**
|
||||
* Implements the Console agent. Relies on Javascript to call the globally exposed method __inspectorLog
|
||||
* to send logging events.
|
||||
*/
|
||||
class ConsoleAgent : public Agent {
|
||||
public:
|
||||
ConsoleAgent(JSC::JSGlobalObject& globalObject, Inspector::InjectedScriptManager* injectedScriptManager);
|
||||
|
||||
void log(JSC::ExecState* execState, std::string message);
|
||||
void log(JSC::ExecState* execState, std::string level, std::string message, JSC::JSArray* params, size_t framesToSkip);
|
||||
private:
|
||||
bool enabled_{false};
|
||||
JSC::JSGlobalObject& globalObject_;
|
||||
Inspector::InjectedScriptManager* injectedScriptManager_;
|
||||
|
||||
folly::dynamic convertParams(JSC::ExecState* execState, JSC::JSArray* params);
|
||||
|
||||
std::string getDomain() override {
|
||||
return "Console";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "Dispatcher.h"
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
// Both double-conversions and WTF define the ASSERT macro
|
||||
#undef ASSERT
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* A bidrectional channel that allows both sending events to the remote inspector and registering
|
||||
* to receive events for a specific domain.
|
||||
*/
|
||||
class Channel {
|
||||
public:
|
||||
using MessageHandler = std::function<void(std::string message, int callId, const std::string& methodName, folly::dynamic args)>;
|
||||
|
||||
virtual ~Channel() = default;
|
||||
|
||||
virtual void sendMessage(std::string message) = 0;
|
||||
virtual void registerDomain(std::string domain, MessageHandler handler) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
* A dispatcher is responsible for one or multiple domains and registering them with the Channel
|
||||
* when it is connected.
|
||||
*/
|
||||
class Dispatcher {
|
||||
public:
|
||||
virtual ~Dispatcher() {}
|
||||
|
||||
virtual void onConnect(std::shared_ptr<Channel> channel) = 0;
|
||||
virtual void onDisconnect() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "Error.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
InspectorException::InspectorException(int callId, ErrorCode code, std::string message)
|
||||
: error_(callId, code, std::move(message)) {}
|
||||
|
||||
InspectorException::InspectorException(ErrorCode code, std::string message)
|
||||
: error_(code, std::move(message)) {}
|
||||
|
||||
InspectorException InspectorException::withCallId(int callId) const {
|
||||
return InspectorException(callId, error_.code(), error_.message());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Protocol.h"
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include <folly/Optional.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class InspectorException : public std::exception {
|
||||
public:
|
||||
InspectorException(int callId, ErrorCode code, std::string message);
|
||||
explicit InspectorException(ErrorCode code, std::string message);
|
||||
|
||||
const char* what() const throw() override {
|
||||
return error_.message().c_str();
|
||||
}
|
||||
|
||||
const Error& error() const {
|
||||
return error_;
|
||||
}
|
||||
|
||||
InspectorException withCallId(int callId) const;
|
||||
private:
|
||||
Error error_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "Inspector.h"
|
||||
|
||||
#include "InspectorController.h"
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
|
||||
#include <JavaScriptCore/APICast.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/JSLock.h>
|
||||
|
||||
#include <folly/Memory.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
JSC::JSGlobalObject& getGlobalObject(JSContextRef ctx) {
|
||||
JSC::ExecState* exec = toJS(ctx);
|
||||
JSC::JSLockHolder locker(exec);
|
||||
|
||||
JSC::JSGlobalObject* globalObject = exec->vmEntryGlobalObject();
|
||||
return *globalObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Inspector::LocalConnection::LocalConnection(std::shared_ptr<Inspector::DuplexConnection> duplexConnection)
|
||||
: duplexConnection_(std::move(duplexConnection)) {}
|
||||
|
||||
void Inspector::LocalConnection::sendMessage(std::string message) {
|
||||
duplexConnection_->sendToLocal(std::move(message));
|
||||
}
|
||||
|
||||
void Inspector::LocalConnection::disconnect() {
|
||||
duplexConnection_->terminate(false);
|
||||
}
|
||||
|
||||
Inspector::PageHolder::PageHolder(std::string name, std::unique_ptr<InspectorController> controller)
|
||||
: name(name)
|
||||
, controller(std::move(controller)) {}
|
||||
|
||||
Inspector::PageHolder::~PageHolder() = default;
|
||||
|
||||
Inspector& Inspector::instance() {
|
||||
static Inspector inspector;
|
||||
return inspector;
|
||||
}
|
||||
|
||||
std::vector<Inspector::Page> Inspector::getPages() const {
|
||||
std::lock_guard<std::mutex> lock(registrationMutex_);
|
||||
std::vector<Page> pages;
|
||||
pages.reserve(pages_.size());
|
||||
for (auto& entry : pages_) {
|
||||
pages.emplace_back(Page{entry.first, entry.second.name});
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
void Inspector::registerGlobalContext(std::string title, JSGlobalContextRef ctx) {
|
||||
std::lock_guard<std::mutex> lock(registrationMutex_);
|
||||
auto controller = folly::make_unique<InspectorController>(getGlobalObject(ctx));
|
||||
auto pageId = numPages_++;
|
||||
pages_.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(pageId),
|
||||
std::forward_as_tuple(std::move(title), std::move(controller)));
|
||||
}
|
||||
|
||||
void Inspector::unregisterGlobalContext(JSGlobalContextRef ctx) {
|
||||
std::lock_guard<std::mutex> lock(registrationMutex_);
|
||||
auto& globalObject = getGlobalObject(ctx);
|
||||
for (auto it = pages_.begin(); it != pages_.end(); it++) {
|
||||
auto& page = it->second;
|
||||
if (page.controller->getGlobalObject().globalExec() == globalObject.globalExec()) {
|
||||
if (page.connection_) {
|
||||
page.connection_->terminate(true);
|
||||
}
|
||||
pages_.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Inspector::LocalConnection> Inspector::connect(int pageId, std::unique_ptr<RemoteConnection> remote) {
|
||||
std::lock_guard<std::mutex> lock(registrationMutex_);
|
||||
return folly::make_unique<LocalConnection>(pages_.at(pageId).connect(std::move(remote)));
|
||||
}
|
||||
|
||||
void Inspector::disconnect(int pageId) {
|
||||
std::lock_guard<std::mutex> lock(registrationMutex_);
|
||||
pages_.at(pageId).controller->onDisconnect();
|
||||
}
|
||||
|
||||
std::shared_ptr<Inspector::DuplexConnection> Inspector::PageHolder::connect(std::unique_ptr<RemoteConnection> remote) {
|
||||
if (connection_) {
|
||||
throw std::runtime_error("Already connected");
|
||||
}
|
||||
connection_ = std::make_shared<DuplexConnection>(*this, std::move(remote));
|
||||
controller->onConnect([connection = connection_](std::string message) {
|
||||
connection->sendToRemote(std::move(message));
|
||||
});
|
||||
return connection_;
|
||||
}
|
||||
|
||||
Inspector::DuplexConnection::DuplexConnection(PageHolder& page, std::unique_ptr<RemoteConnection> remoteConnection)
|
||||
: page_(page)
|
||||
, remoteConnection_(std::move(remoteConnection)) {}
|
||||
|
||||
Inspector::DuplexConnection::~DuplexConnection() {
|
||||
if (remoteConnection_) {
|
||||
LOG(FATAL) << "DuplexConnection wasn't terminated before destruction";
|
||||
}
|
||||
}
|
||||
|
||||
void Inspector::DuplexConnection::sendToRemote(std::string message) {
|
||||
std::lock_guard<std::mutex> lock(remoteMutex_);
|
||||
if (!remoteConnection_) {
|
||||
return;
|
||||
}
|
||||
|
||||
remoteConnection_->onMessage(std::move(message));
|
||||
}
|
||||
|
||||
void Inspector::DuplexConnection::sendToLocal(std::string message) {
|
||||
std::lock_guard<std::mutex> lock(localMutex_);
|
||||
if (!remoteConnection_) {
|
||||
return;
|
||||
}
|
||||
|
||||
page_.controller->onMessage(std::move(message));
|
||||
}
|
||||
|
||||
void Inspector::DuplexConnection::terminate(bool local) {
|
||||
std::lock_guard<std::mutex> lockLocal(localMutex_);
|
||||
{
|
||||
// Temp lock here so we can still send going away message
|
||||
std::lock_guard<std::mutex> lockRemote(remoteMutex_);
|
||||
if (!remoteConnection_) {
|
||||
// Already disconnected
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (local) {
|
||||
page_.controller->onGoingAway();
|
||||
}
|
||||
std::lock_guard<std::mutex> lockRemote(remoteMutex_);
|
||||
|
||||
auto remoteConnection = std::move(remoteConnection_);
|
||||
if (local) {
|
||||
remoteConnection->onDisconnect();
|
||||
}
|
||||
page_.controller->onDisconnect();
|
||||
// This kills us
|
||||
page_.connection_.reset();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
#include <JavaScriptCore/JSBase.h>
|
||||
#undef WTF_EXPORT_PRIVATE
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class InspectorController;
|
||||
class Sender;
|
||||
/**
|
||||
* The inspector exposes method to query for available 'pages' and connect to a specific one.
|
||||
* Available Javascript contextes needs to be registered when they are created and removed when
|
||||
* they are torn down.
|
||||
*/
|
||||
class Inspector {
|
||||
private:
|
||||
class DuplexConnection;
|
||||
public:
|
||||
struct Page {
|
||||
const int id;
|
||||
const std::string title;
|
||||
};
|
||||
|
||||
struct RemoteConnection {
|
||||
virtual ~RemoteConnection() = default;
|
||||
virtual void onMessage(std::string message) = 0;
|
||||
virtual void onDisconnect() = 0;
|
||||
};
|
||||
|
||||
class LocalConnection {
|
||||
public:
|
||||
void sendMessage(std::string message);
|
||||
void disconnect();
|
||||
|
||||
LocalConnection(std::shared_ptr<DuplexConnection> duplexConnection);
|
||||
private:
|
||||
std::shared_ptr<DuplexConnection> duplexConnection_;
|
||||
};
|
||||
|
||||
static Inspector& instance();
|
||||
|
||||
void registerGlobalContext(std::string title, JSGlobalContextRef ctx);
|
||||
void unregisterGlobalContext(JSGlobalContextRef ctx);
|
||||
|
||||
std::vector<Page> getPages() const;
|
||||
std::unique_ptr<LocalConnection> connect(int pageId, std::unique_ptr<RemoteConnection> remote);
|
||||
private:
|
||||
struct PageHolder;
|
||||
|
||||
class DuplexConnection {
|
||||
public:
|
||||
DuplexConnection(PageHolder& page, std::unique_ptr<RemoteConnection> remoteConnection);
|
||||
~DuplexConnection();
|
||||
|
||||
void sendToRemote(std::string message);
|
||||
void sendToLocal(std::string message);
|
||||
void terminate(bool local);
|
||||
private:
|
||||
PageHolder& page_;
|
||||
std::unique_ptr<RemoteConnection> remoteConnection_;
|
||||
std::mutex localMutex_;
|
||||
std::mutex remoteMutex_;
|
||||
};
|
||||
|
||||
struct PageHolder {
|
||||
PageHolder(std::string name, std::unique_ptr<InspectorController> controller);
|
||||
~PageHolder();
|
||||
|
||||
std::shared_ptr<DuplexConnection> connect(std::unique_ptr<RemoteConnection> remote);
|
||||
|
||||
const std::string name;
|
||||
std::unique_ptr<InspectorController> controller;
|
||||
std::shared_ptr<DuplexConnection> connection_;
|
||||
};
|
||||
|
||||
Inspector() {};
|
||||
void disconnect(int pageId);
|
||||
|
||||
int numPages_ = 0;
|
||||
std::unordered_map<int, PageHolder> pages_;
|
||||
mutable std::mutex registrationMutex_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "InspectorAgent.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
InspectorAgent::InspectorAgent() {
|
||||
auto emptyMethod = [](folly::dynamic) -> folly::dynamic {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
registerMethod("enable", emptyMethod);
|
||||
registerMethod("disable", emptyMethod);
|
||||
}
|
||||
|
||||
void InspectorAgent::detach() {
|
||||
sendEvent("detached", folly::dynamic::object("reason", "target_closed"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class InspectorAgent : public Agent {
|
||||
public:
|
||||
InspectorAgent();
|
||||
|
||||
void detach();
|
||||
private:
|
||||
std::string getDomain() override {
|
||||
return "Inspector";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "InspectorController.h"
|
||||
|
||||
#include "Error.h"
|
||||
#include "Agent.h"
|
||||
#include "LegacyInspectorEnvironment.h"
|
||||
#include "InspectorAgent.h"
|
||||
#include "PageAgent.h"
|
||||
#include "ConsoleAgent.h"
|
||||
#include "JSDispatcher.h"
|
||||
#include "LegacyAgents.h"
|
||||
|
||||
#include <folly/Memory.h>
|
||||
#include <folly/Conv.h>
|
||||
#include <folly/json.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class ConcreteChannel : public Channel {
|
||||
public:
|
||||
ConcreteChannel(Receiver receiver)
|
||||
: receiver_(std::move(receiver)) {}
|
||||
|
||||
void sendMessage(std::string message) override {
|
||||
receiver_(std::move(message));
|
||||
}
|
||||
|
||||
void registerDomain(std::string domain, MessageHandler handler) override {
|
||||
domains_.emplace(std::move(domain), std::move(handler));
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, MessageHandler>& getDomains() {
|
||||
return domains_;
|
||||
}
|
||||
private:
|
||||
std::unordered_map<std::string, MessageHandler> domains_;
|
||||
Receiver receiver_;
|
||||
};
|
||||
|
||||
class MessageRouter {
|
||||
public:
|
||||
MessageRouter(ConcreteChannel* channel)
|
||||
: channel_(channel) {
|
||||
CHECK(channel_) << "Channel is null";
|
||||
}
|
||||
|
||||
/*
|
||||
* Messages are in JSON, formatted like:
|
||||
* {
|
||||
* "id": 1,
|
||||
* "method": "Debugger.removeBreakpoint",
|
||||
* "params": { "removeBreakpoint": "xyz" }
|
||||
* }
|
||||
*/
|
||||
void route(std::string message) {
|
||||
try {
|
||||
auto json = parseJson(message);
|
||||
auto callId = getCallId(json);
|
||||
receive(callId, std::move(message), std::move(json));
|
||||
} catch (const InspectorException& e) {
|
||||
channel_->sendMessage(e.error());
|
||||
}
|
||||
}
|
||||
private:
|
||||
void receive(int callId, std::string message, folly::dynamic json) {
|
||||
try {
|
||||
auto method = Method::parse(json["method"].asString());
|
||||
auto& handler = getHandler(method.domain());
|
||||
handler(std::move(message), callId, method.name(), std::move(json["params"]));
|
||||
} catch (const InspectorException& e) {
|
||||
throw e.withCallId(callId);
|
||||
} catch (const std::exception& e) {
|
||||
LOG(ERROR) << "Dispatcher failed: " << e.what();
|
||||
throw InspectorException(callId, ErrorCode::ServerError, "Internal error");
|
||||
} catch (...) {
|
||||
throw InspectorException(callId, ErrorCode::ServerError, "Internal error");
|
||||
}
|
||||
}
|
||||
|
||||
folly::dynamic parseJson(const std::string& message) {
|
||||
try {
|
||||
return folly::parseJson(message);
|
||||
} catch (const std::runtime_error& e) {
|
||||
throw InspectorException(ErrorCode::ParseError, "Message must be in JSON format");
|
||||
}
|
||||
}
|
||||
|
||||
int getCallId(folly::dynamic& json) {
|
||||
auto& id = json["id"];
|
||||
if (!id.isInt()) {
|
||||
throw InspectorException(ErrorCode::InvalidRequest, "The type of 'id' property must be number");
|
||||
} else {
|
||||
return id.asInt();
|
||||
}
|
||||
}
|
||||
|
||||
Channel::MessageHandler& getHandler(const std::string& domain) {
|
||||
try {
|
||||
auto& domains = channel_->getDomains();
|
||||
return domains.at(domain);
|
||||
} catch (const std::out_of_range& e) {
|
||||
throw InspectorException(ErrorCode::MethodNotFound, folly::to<std::string>("Unknown domain: '", domain, "'"));
|
||||
}
|
||||
}
|
||||
|
||||
ConcreteChannel* channel_;
|
||||
};
|
||||
|
||||
class SchemaAgent : public Agent {
|
||||
public:
|
||||
SchemaAgent() {
|
||||
registerMethod("getDomains", [this](folly::dynamic) -> folly::dynamic {
|
||||
CHECK(channel_) << "Channel is null";
|
||||
folly::dynamic names = folly::dynamic::array;
|
||||
auto& domains = channel_->getDomains();
|
||||
for (auto& entry : domains) {
|
||||
// TODO(blom): Actually get version?
|
||||
names.push_back(folly::dynamic::object("name", entry.first)("version", "1.0"));
|
||||
}
|
||||
return names;
|
||||
});
|
||||
}
|
||||
|
||||
void onConnect(std::shared_ptr<Channel> channel) override {
|
||||
Agent::onConnect(channel);
|
||||
channel_ = std::static_pointer_cast<ConcreteChannel>(channel);
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<ConcreteChannel> channel_;
|
||||
|
||||
std::string getDomain() override {
|
||||
return "Schema";
|
||||
}
|
||||
};
|
||||
|
||||
InspectorController::InspectorController(JSC::JSGlobalObject& globalObject)
|
||||
: globalObject_(globalObject) {
|
||||
auto environment = folly::make_unique<LegacyInspectorEnvironment>();
|
||||
auto inspectorAgent = folly::make_unique<InspectorAgent>();
|
||||
inspectorAgent_ = inspectorAgent.get();
|
||||
dispatchers_.push_back(std::move(inspectorAgent));
|
||||
dispatchers_.push_back(folly::make_unique<SchemaAgent>());
|
||||
dispatchers_.push_back(folly::make_unique<PageAgent>());
|
||||
dispatchers_.push_back(folly::make_unique<JSDispatcher>(globalObject));
|
||||
|
||||
auto consoleAgent = folly::make_unique<ConsoleAgent>(globalObject, environment->injectedScriptManager());
|
||||
auto legacyAgents = folly::make_unique<LegacyAgents>(globalObject, std::move(environment), consoleAgent.get());
|
||||
|
||||
dispatchers_.push_back(std::move(consoleAgent));
|
||||
dispatchers_.push_back(std::move(legacyAgents));
|
||||
}
|
||||
|
||||
InspectorController::~InspectorController() {
|
||||
CHECK(!channel_) << "Wasn't disconnected";
|
||||
}
|
||||
|
||||
void InspectorController::onConnect(Receiver receiver) {
|
||||
CHECK(!channel_) << "Already connected";
|
||||
|
||||
channel_ = std::make_shared<ConcreteChannel>(std::move(receiver));
|
||||
|
||||
for (auto& dispatcher : dispatchers_) {
|
||||
dispatcher->onConnect(channel_);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorController::onMessage(std::string message) {
|
||||
CHECK(channel_) << "Not connected";
|
||||
|
||||
MessageRouter(channel_.get()).route(message);
|
||||
}
|
||||
|
||||
void InspectorController::onGoingAway() {
|
||||
CHECK(channel_) << "Not connected";
|
||||
|
||||
inspectorAgent_->detach();
|
||||
}
|
||||
|
||||
void InspectorController::onDisconnect() {
|
||||
CHECK(channel_) << "Not connected";
|
||||
|
||||
for (auto& dispatcher : dispatchers_) {
|
||||
dispatcher->onDisconnect();
|
||||
}
|
||||
|
||||
channel_.reset();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Dispatcher.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class ConcreteChannel;
|
||||
class InspectorAgent;
|
||||
|
||||
using Receiver = std::function<void(std::string)>;
|
||||
|
||||
class InspectorController {
|
||||
public:
|
||||
InspectorController(JSC::JSGlobalObject& globalObject);
|
||||
~InspectorController();
|
||||
|
||||
JSC::JSGlobalObject& getGlobalObject() const { return globalObject_; }
|
||||
|
||||
void onConnect(Receiver receiver);
|
||||
void onMessage(std::string message);
|
||||
void onGoingAway();
|
||||
void onDisconnect();
|
||||
private:
|
||||
JSC::JSGlobalObject& globalObject_;
|
||||
std::shared_ptr<ConcreteChannel> channel_;
|
||||
std::vector<std::unique_ptr<Dispatcher>> dispatchers_;
|
||||
InspectorAgent* inspectorAgent_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "JSDispatcher.h"
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <jschelpers/JSCHelpers.h>
|
||||
|
||||
#include <JavaScriptCore/APICast.h>
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
#include <JavaScriptCore/JSObjectRef.h>
|
||||
#include <JavaScriptCore/JSStringRef.h>
|
||||
|
||||
#include <folly/json.h>
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
static JSValueRef nativeRegisterAgent(
|
||||
JSDispatcher* agent,
|
||||
JSContextRef ctx,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]) {
|
||||
CHECK(argumentCount == 1) << "__registerInspectorAgent takes 1 arg";
|
||||
auto execState = toJS(ctx);
|
||||
JSC::JSLockHolder lock(execState);
|
||||
auto globalContext = JSContextGetGlobalContext(ctx);
|
||||
agent->addAgent(
|
||||
execState,
|
||||
Value(globalContext, arguments[0]).asObject());
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeSendEvent(
|
||||
JSDispatcher* agent,
|
||||
const std::string& domain,
|
||||
JSContextRef ctx,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[]) {
|
||||
CHECK(argumentCount == 2) << "sendEvent takes 2 args";
|
||||
auto execState = toJS(ctx);
|
||||
JSC::JSLockHolder lock(execState);
|
||||
auto globalContext = JSContextGetGlobalContext(ctx);
|
||||
auto params = folly::parseJson(Value(globalContext, arguments[1]).toJSONString());
|
||||
agent->sendEvent(
|
||||
domain,
|
||||
Value(globalContext, arguments[0]).toString().str(),
|
||||
std::move(params));
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeInspectorTimestamp(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception) {
|
||||
return JSValueMakeNumber(ctx, Timestamp::now());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
JSDispatcher::JSDispatcher(JSC::JSGlobalObject& globalObject) {
|
||||
using namespace std::placeholders;
|
||||
JSGlobalContextRef context = toGlobalRef(globalObject.globalExec());
|
||||
installGlobalFunction(context, "__registerInspectorAgent", std::bind(&nativeRegisterAgent, this, _1, _2, _3, _4));
|
||||
installGlobalFunction(context, "__inspectorTimestamp", &nativeInspectorTimestamp);
|
||||
}
|
||||
|
||||
void JSDispatcher::onConnect(std::shared_ptr<Channel> channel) {
|
||||
channel_ = std::move(channel);
|
||||
|
||||
for (auto& pair : agents_) {
|
||||
registerAgent(pair.first);
|
||||
}
|
||||
}
|
||||
|
||||
void JSDispatcher::onDisconnect() {
|
||||
channel_.reset();
|
||||
}
|
||||
|
||||
void JSDispatcher::addAgent(JSC::ExecState* execState, Object agentType) {
|
||||
auto context = agentType.context();
|
||||
auto domainObject = agentType.getProperty("DOMAIN");
|
||||
if (domainObject.isUndefined()) {
|
||||
throw std::invalid_argument("DOMAIN should be string");
|
||||
}
|
||||
auto domain = domainObject.toString().str();
|
||||
// Bind the domain to the send event function
|
||||
using namespace std::placeholders;
|
||||
Value sendEventFunction = Object(
|
||||
context,
|
||||
makeFunction(context, "sendEvent", std::bind(&nativeSendEvent, this, domain, _1, _2, _3, _4)));
|
||||
auto agent = agentType.callAsConstructor({ sendEventFunction });
|
||||
agent.makeProtected();
|
||||
|
||||
if (channel_) {
|
||||
registerAgent(domain);
|
||||
}
|
||||
agents_.emplace(std::move(domain), std::move(agent));
|
||||
}
|
||||
|
||||
void JSDispatcher::registerAgent(const std::string& name) {
|
||||
channel_->registerDomain(
|
||||
name,
|
||||
[this, name](std::string, int callId, const std::string& methodName, folly::dynamic args) {
|
||||
Object& agent = agents_.at(name);
|
||||
auto context = agent.context();
|
||||
JSC::JSLockHolder lock(toJS(context));
|
||||
// TODO(blom): Check undefined before asObject
|
||||
auto method = agent.getProperty(methodName.c_str()).asObject();
|
||||
if (args.isNull()) {
|
||||
args = folly::dynamic::object;
|
||||
}
|
||||
auto response = method.callAsFunction(agent, { Value::fromDynamic(context, args) });
|
||||
auto result = (response.isUndefined() || response.isNull()) ? folly::dynamic::object() : folly::parseJson(response.toJSONString());
|
||||
auto message = folly::dynamic::object("id", callId)("result", std::move(result));
|
||||
channel_->sendMessage(folly::toJson(std::move(message)));
|
||||
});
|
||||
}
|
||||
|
||||
void JSDispatcher::sendEvent(std::string domain, std::string name, folly::dynamic params) {
|
||||
channel_->sendMessage(Event(std::move(domain), std::move(name), std::move(params)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Dispatcher.h"
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/JSValueRef.h>
|
||||
|
||||
#include <jschelpers/Value.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
class JSObject;
|
||||
class ExecState;
|
||||
class JSArray;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* A dispatcher that allows agents to be implemented in Javascript. Provides the global method
|
||||
* __registerInspectorAgent to register a JS agent.
|
||||
*/
|
||||
class JSDispatcher : public Dispatcher {
|
||||
public:
|
||||
JSDispatcher(JSC::JSGlobalObject& globalObject);
|
||||
|
||||
void onConnect(std::shared_ptr<Channel> channel) override;
|
||||
void onDisconnect() override;
|
||||
|
||||
void addAgent(JSC::ExecState* execState, Object agentType);
|
||||
void registerAgent(const std::string& name);
|
||||
|
||||
void sendEvent(std::string domain, std::string name, folly::dynamic params);
|
||||
private:
|
||||
std::shared_ptr<Channel> channel_;
|
||||
|
||||
std::unordered_map<std::string, Object> agents_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "LegacyAgents.h"
|
||||
|
||||
#include "LegacyInspectorEnvironment.h"
|
||||
#include "LegacyRuntimeAgent.h"
|
||||
#include "LegacyDebuggerAgent.h"
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <folly/Memory.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
LegacyAgents::LegacyAgents(
|
||||
JSC::JSGlobalObject& globalObject,
|
||||
std::unique_ptr<LegacyInspectorEnvironment> environment,
|
||||
ConsoleAgent* consoleAgent)
|
||||
: LegacyDispatcher(globalObject)
|
||||
, environment_(std::move(environment)) {
|
||||
auto injectedScriptManager = environment_->injectedScriptManager();
|
||||
auto runtimeAgent = folly::make_unique<LegacyRuntimeAgent>(injectedScriptManager, globalObject);
|
||||
auto debuggerAgent = folly::make_unique<LegacyDebuggerAgent>(injectedScriptManager, globalObject, consoleAgent);
|
||||
|
||||
runtimeAgent->setScriptDebugServer(&debuggerAgent->scriptDebugServer());
|
||||
|
||||
addAgent("Runtime", std::move(runtimeAgent));
|
||||
addAgent("Debugger", std::move(debuggerAgent));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LegacyDispatcher.h"
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class LegacyInspectorEnvironment;
|
||||
class ConsoleAgent;
|
||||
|
||||
/*
|
||||
* An dispatcher that provides the existing agents in JavaScriptCore.
|
||||
*/
|
||||
class LegacyAgents : public LegacyDispatcher {
|
||||
public:
|
||||
LegacyAgents(
|
||||
JSC::JSGlobalObject& globalObject,
|
||||
std::unique_ptr<LegacyInspectorEnvironment> environment,
|
||||
ConsoleAgent* consoleAgent);
|
||||
private:
|
||||
std::unique_ptr<LegacyInspectorEnvironment> environment_;
|
||||
ConsoleAgent* consoleAgent_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "LegacyDebuggerAgent.h"
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
#include <JavaScriptCore/InjectedScript.h>
|
||||
#include <JavaScriptCore/InjectedScriptManager.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
LegacyDebuggerAgent::LegacyDebuggerAgent(InjectedScriptManager* injectedScriptManager, JSC::JSGlobalObject& globalObject, ConsoleAgent* consoleAgent)
|
||||
: InspectorDebuggerAgent(injectedScriptManager)
|
||||
, scriptDebugServer_(globalObject)
|
||||
, consoleAgent_(consoleAgent) {}
|
||||
|
||||
void LegacyDebuggerAgent::startListeningScriptDebugServer() {
|
||||
scriptDebugServer().addListener(this);
|
||||
}
|
||||
|
||||
void LegacyDebuggerAgent::stopListeningScriptDebugServer(bool isBeingDestroyed) {
|
||||
scriptDebugServer().removeListener(this, isBeingDestroyed);
|
||||
}
|
||||
|
||||
InjectedScript LegacyDebuggerAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) {
|
||||
if (executionContextId) {
|
||||
*error = ASCIILiteral("Execution context id is not supported for JSContext inspection as there is only one execution context.");
|
||||
return InjectedScript();
|
||||
}
|
||||
|
||||
JSC::ExecState* exec = scriptDebugServer_.globalObject().globalExec();
|
||||
return injectedScriptManager()->injectedScriptFor(exec);
|
||||
}
|
||||
|
||||
void LegacyDebuggerAgent::breakpointActionLog(JSC::ExecState* exec, const String& message) {
|
||||
consoleAgent_->log(exec, toStdString(message));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ConsoleAgent.h"
|
||||
#include "LegacyScriptDebugServer.h"
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/InspectorDebuggerAgent.h>
|
||||
#include <JavaScriptCore/InspectorConsoleAgent.h>
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class LegacyDebuggerAgent : public Inspector::InspectorDebuggerAgent {
|
||||
public:
|
||||
LegacyDebuggerAgent(Inspector::InjectedScriptManager*, JSC::JSGlobalObject&, ConsoleAgent*);
|
||||
|
||||
virtual LegacyScriptDebugServer& scriptDebugServer() override { return scriptDebugServer_; }
|
||||
|
||||
virtual void startListeningScriptDebugServer() override;
|
||||
virtual void stopListeningScriptDebugServer(bool isBeingDestroyed) override;
|
||||
virtual Inspector::InjectedScript injectedScriptForEval(Inspector::ErrorString*, const int* executionContextId) override;
|
||||
|
||||
virtual void breakpointActionLog(JSC::ExecState*, const String&) override;
|
||||
|
||||
virtual void muteConsole() override { }
|
||||
virtual void unmuteConsole() override { }
|
||||
|
||||
private:
|
||||
LegacyScriptDebugServer scriptDebugServer_;
|
||||
ConsoleAgent* consoleAgent_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "LegacyDispatcher.h"
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
#include <wtf/text/CString.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/JSLock.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
LegacyDispatcher::FrontendChannel::FrontendChannel(std::shared_ptr<Channel> channel)
|
||||
: channel_(channel) {}
|
||||
|
||||
bool LegacyDispatcher::FrontendChannel::sendMessageToFrontend(const WTF::String& message) {
|
||||
channel_->sendMessage(toStdString(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
LegacyDispatcher::LegacyDispatcher(JSC::JSGlobalObject& globalObject)
|
||||
: globalObject_(globalObject) {}
|
||||
|
||||
void LegacyDispatcher::addAgent(std::string domain, std::unique_ptr<InspectorAgentBase> agent) {
|
||||
domains_.emplace_back(std::move(domain));
|
||||
agents_.append(std::move(agent));
|
||||
}
|
||||
|
||||
void LegacyDispatcher::onConnect(std::shared_ptr<Channel> channel) {
|
||||
// TODO: Should perhaps only create this once and then connect each time instead
|
||||
frontendChannel_ = std::make_unique<FrontendChannel>(channel);
|
||||
dispatcher_.reset(InspectorBackendDispatcher::create(frontendChannel_.get()).leakRef());
|
||||
|
||||
auto messageHandler = [this](std::string message, int, const std::string&, folly::dynamic) {
|
||||
JSC::JSLockHolder lock(globalObject_.globalExec());
|
||||
dispatcher_->dispatch(message.c_str());
|
||||
};
|
||||
for (auto& domain : domains_) {
|
||||
channel->registerDomain(domain, messageHandler);
|
||||
}
|
||||
|
||||
agents_.didCreateFrontendAndBackend(frontendChannel_.get(), dispatcher_.get());
|
||||
}
|
||||
|
||||
void LegacyDispatcher::onDisconnect() {
|
||||
// TODO: Perhaps support InspectedTargetDestroyed
|
||||
agents_.willDestroyFrontendAndBackend(InspectorDisconnectReason::InspectorDestroyed);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Dispatcher.h"
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/InspectorAgentRegistry.h>
|
||||
#include <JavaScriptCore/InspectorAgentBase.h>
|
||||
#include <JavaScriptCore/InspectorFrontendChannel.h>
|
||||
#include <JavaScriptCore/InspectorBackendDispatcher.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
/*
|
||||
* An dispatcher that is able to register JavaScriptCore agents that extend the InspectorAgentBase
|
||||
* base class.
|
||||
*/
|
||||
class LegacyDispatcher : public Dispatcher {
|
||||
public:
|
||||
LegacyDispatcher(JSC::JSGlobalObject& globalObject);
|
||||
void addAgent(std::string domain, std::unique_ptr<Inspector::InspectorAgentBase> agent);
|
||||
|
||||
void onConnect(std::shared_ptr<Channel> channel) override;
|
||||
void onDisconnect() override;
|
||||
private:
|
||||
class FrontendChannel : public Inspector::InspectorFrontendChannel {
|
||||
public:
|
||||
FrontendChannel(std::shared_ptr<Channel> channel);
|
||||
bool sendMessageToFrontend(const WTF::String& message) override;
|
||||
private:
|
||||
std::shared_ptr<Channel> channel_;
|
||||
};
|
||||
|
||||
JSC::JSGlobalObject& globalObject_;
|
||||
std::vector<std::string> domains_;
|
||||
Inspector::InspectorAgentRegistry agents_;
|
||||
|
||||
std::unique_ptr<FrontendChannel> frontendChannel_;
|
||||
std::unique_ptr<Inspector::InspectorBackendDispatcher> dispatcher_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "LegacyInspectorEnvironment.h"
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/Completion.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
#include <JavaScriptCore/InjectedScriptManager.h>
|
||||
#include <JavaScriptCore/InjectedScriptHost.h>
|
||||
|
||||
#include <folly/Memory.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
LegacyInspectorEnvironment::LegacyInspectorEnvironment()
|
||||
: injectedScriptManager_(folly::make_unique<InjectedScriptManager>(*this, InjectedScriptHost::create())) {}
|
||||
|
||||
LegacyInspectorEnvironment::~LegacyInspectorEnvironment() {
|
||||
injectedScriptManager_->disconnect();
|
||||
}
|
||||
|
||||
InspectorFunctionCallHandler LegacyInspectorEnvironment::functionCallHandler() const {
|
||||
return JSC::call;
|
||||
}
|
||||
|
||||
InspectorEvaluateHandler LegacyInspectorEnvironment::evaluateHandler() const {
|
||||
return JSC::evaluate;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/InspectorEnvironment.h>
|
||||
|
||||
namespace Inspector {
|
||||
class InjectedScriptManager;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class LegacyInspectorEnvironment : public Inspector::InspectorEnvironment {
|
||||
public:
|
||||
LegacyInspectorEnvironment();
|
||||
~LegacyInspectorEnvironment();
|
||||
|
||||
Inspector::InjectedScriptManager* injectedScriptManager() const {
|
||||
return injectedScriptManager_.get();
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<Inspector::InjectedScriptManager> injectedScriptManager_;
|
||||
|
||||
bool developerExtrasEnabled() const override { return true; }
|
||||
bool canAccessInspectedScriptState(JSC::ExecState*) const override { return true; }
|
||||
Inspector::InspectorFunctionCallHandler functionCallHandler() const override;
|
||||
Inspector::InspectorEvaluateHandler evaluateHandler() const override;
|
||||
void willCallInjectedScriptFunction(JSC::ExecState*, const WTF::String& scriptName, int scriptLine) override {};
|
||||
void didCallInjectedScriptFunction(JSC::ExecState*) override {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "LegacyRuntimeAgent.h"
|
||||
|
||||
#include <JavaScriptCore/InjectedScript.h>
|
||||
#include <JavaScriptCore/InjectedScriptManager.h>
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
LegacyRuntimeAgent::LegacyRuntimeAgent(InjectedScriptManager* injectedScriptManager, JSC::JSGlobalObject& globalObject)
|
||||
: InspectorRuntimeAgent(injectedScriptManager)
|
||||
, m_globalObject(globalObject) {}
|
||||
|
||||
void LegacyRuntimeAgent::didCreateFrontendAndBackend(InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) {
|
||||
// m_frontendDispatcher = folly::make_unique<InspectorRuntimeFrontendDispatcher>(frontendChannel);
|
||||
frontendChannel_ = frontendChannel;
|
||||
m_backendDispatcher.reset(InspectorRuntimeBackendDispatcher::create(backendDispatcher, this).leakRef());
|
||||
}
|
||||
|
||||
void LegacyRuntimeAgent::enable(ErrorString* error) {
|
||||
InspectorRuntimeAgent::enable(error);
|
||||
|
||||
auto contextObject = InspectorObject::create();
|
||||
contextObject->setNumber(ASCIILiteral("id"), 1);
|
||||
contextObject->setBoolean(ASCIILiteral("isDefault"), true);
|
||||
contextObject->setBoolean(ASCIILiteral("isPageContext"), true);
|
||||
contextObject->setString(ASCIILiteral("origin"), ASCIILiteral(""));
|
||||
contextObject->setString(ASCIILiteral("name"), ASCIILiteral("React Native"));
|
||||
|
||||
auto jsonMessage = InspectorObject::create();
|
||||
jsonMessage->setString(ASCIILiteral("method"), ASCIILiteral("Runtime.executionContextCreated"));
|
||||
auto paramsObject = InspectorObject::create();
|
||||
paramsObject->setValue(ASCIILiteral("context"), contextObject);
|
||||
jsonMessage->setObject(ASCIILiteral("params"), paramsObject);
|
||||
|
||||
frontendChannel_->sendMessageToFrontend(jsonMessage->toJSONString());
|
||||
}
|
||||
|
||||
void LegacyRuntimeAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) {
|
||||
frontendChannel_ = nullptr;
|
||||
m_backendDispatcher = nullptr;
|
||||
}
|
||||
|
||||
JSC::VM& LegacyRuntimeAgent::globalVM() {
|
||||
return m_globalObject.vm();
|
||||
}
|
||||
|
||||
InjectedScript LegacyRuntimeAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId) {
|
||||
JSC::ExecState* scriptState = m_globalObject.globalExec();
|
||||
InjectedScript injectedScript = injectedScriptManager()->injectedScriptFor(scriptState);
|
||||
if (injectedScript.hasNoValue()) {
|
||||
*error = ASCIILiteral("Internal error: main world execution context not found.");
|
||||
}
|
||||
|
||||
return injectedScript;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/InspectorRuntimeAgent.h>
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class LegacyRuntimeAgent : public Inspector::InspectorRuntimeAgent {
|
||||
public:
|
||||
LegacyRuntimeAgent(Inspector::InjectedScriptManager*, JSC::JSGlobalObject&);
|
||||
|
||||
void enable(Inspector::ErrorString* error) override;
|
||||
|
||||
void didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel*, Inspector::InspectorBackendDispatcher*) override;
|
||||
void willDestroyFrontendAndBackend(Inspector::InspectorDisconnectReason) override;
|
||||
|
||||
JSC::VM& globalVM() override;
|
||||
Inspector::InjectedScript injectedScriptForEval(Inspector::ErrorString* error, const int* executionContextId) override;
|
||||
|
||||
void muteConsole() override { }
|
||||
void unmuteConsole() override { }
|
||||
private:
|
||||
Inspector::InspectorFrontendChannel* frontendChannel_;
|
||||
// std::unique_ptr<Inspector::InspectorRuntimeFrontendDispatcher> m_frontendDispatcher;
|
||||
std::unique_ptr<Inspector::InspectorRuntimeBackendDispatcher> m_backendDispatcher;
|
||||
JSC::JSGlobalObject& m_globalObject;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "LegacyScriptDebugServer.h"
|
||||
|
||||
#include <JavaScriptCore/JSGlobalObject.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
using namespace Inspector;
|
||||
|
||||
LegacyScriptDebugServer::LegacyScriptDebugServer(JSC::JSGlobalObject& globalObject)
|
||||
: Inspector::ScriptDebugServer(false)
|
||||
, globalObject_(globalObject) {}
|
||||
|
||||
void LegacyScriptDebugServer::addListener(ScriptDebugListener* listener)
|
||||
{
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasEmpty = listeners_.isEmpty();
|
||||
listeners_.add(listener);
|
||||
|
||||
// First listener. Attach the debugger to the JSGlobalObject.
|
||||
if (wasEmpty) {
|
||||
attach(&globalObject_);
|
||||
recompileAllJSFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
void LegacyScriptDebugServer::removeListener(ScriptDebugListener* listener, bool isBeingDestroyed) {
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
listeners_.remove(listener);
|
||||
|
||||
// Last listener. Detach the debugger from the JSGlobalObject.
|
||||
if (listeners_.isEmpty()) {
|
||||
detach(&globalObject_, isBeingDestroyed ? Debugger::GlobalObjectIsDestructing : Debugger::TerminatingDebuggingSession);
|
||||
if (!isBeingDestroyed) {
|
||||
recompileAllJSFunctions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LegacyScriptDebugServer::recompileAllJSFunctions() {
|
||||
JSC::Debugger::recompileAllJSFunctions(&globalObject_.vm());
|
||||
}
|
||||
|
||||
void LegacyScriptDebugServer::runEventLoopWhilePaused() {
|
||||
// Drop all locks so another thread can work in the VM while we are nested.
|
||||
JSC::JSLock::DropAllLocks dropAllLocks(&globalObject_.vm());
|
||||
|
||||
// Spinning here is our best option, we could override the method
|
||||
// notifyDoneProcessingDebuggerEvents but it's marked as final :(
|
||||
while (!m_doneProcessingDebuggerEvents) {
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <JavaScriptCore/ScriptDebugServer.h>
|
||||
|
||||
namespace JSC {
|
||||
class JSGlobalObject;
|
||||
}
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class LegacyScriptDebugServer : public Inspector::ScriptDebugServer {
|
||||
public:
|
||||
LegacyScriptDebugServer(JSC::JSGlobalObject& object);
|
||||
|
||||
void addListener(Inspector::ScriptDebugListener* listener);
|
||||
void removeListener(Inspector::ScriptDebugListener* listener, bool isBeingDestroyed);
|
||||
|
||||
JSC::JSGlobalObject& globalObject() const { return globalObject_; }
|
||||
|
||||
void recompileAllJSFunctions() override;
|
||||
|
||||
private:
|
||||
ListenerSet* getListenersForGlobalObject(JSC::JSGlobalObject*) override { return &listeners_; }
|
||||
void didPause(JSC::JSGlobalObject*) override { }
|
||||
void didContinue(JSC::JSGlobalObject*) override { }
|
||||
void runEventLoopWhilePaused() override;
|
||||
bool isContentScript(JSC::ExecState*) const override { return false; }
|
||||
|
||||
// NOTE: Currently all exceptions are reported at the API boundary through reportAPIException.
|
||||
// Until a time comes where an exception can be caused outside of the API (e.g. setTimeout
|
||||
// or some other async operation in a pure JSContext) we can ignore exceptions reported here.
|
||||
// TODO: Should we actually ignore them?
|
||||
void reportException(JSC::ExecState*, JSC::JSValue) const override { }
|
||||
|
||||
ListenerSet listeners_;
|
||||
JSC::JSGlobalObject& globalObject_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "PageAgent.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
PageAgent::PageAgent() {
|
||||
auto emptyMethod = [](folly::dynamic args) -> folly::dynamic {
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
registerMethod("enable", emptyMethod);
|
||||
registerMethod("disable", emptyMethod);
|
||||
registerMethod("getResourceTree", [](folly::dynamic args) -> folly::dynamic {
|
||||
return folly::dynamic::object
|
||||
("frameTree", folly::dynamic::object
|
||||
("childFrames", folly::dynamic::array)
|
||||
("resources", folly::dynamic::array)
|
||||
("frame", folly::dynamic::object
|
||||
("id", "1")
|
||||
("loaderId", "1")
|
||||
("name", "main")
|
||||
("url", "")
|
||||
("securityOrigin", "")
|
||||
("mimeType", "application/octet-stream")));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Agent.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class PageAgent : public Agent {
|
||||
public:
|
||||
PageAgent();
|
||||
private:
|
||||
std::string getDomain() override {
|
||||
return "Page";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "Protocol.h"
|
||||
|
||||
#include "Error.h"
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
#include <folly/json.h>
|
||||
#include <folly/Conv.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
namespace {
|
||||
|
||||
folly::dynamic getCallId(const folly::Optional<int>& callId) {
|
||||
if (callId.hasValue()) {
|
||||
return callId.value();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Method Method::parse(const std::string& formatted) {
|
||||
auto splitPos = formatted.find_first_of('.');
|
||||
if (splitPos == std::string::npos) {
|
||||
throw InspectorException(ErrorCode::InvalidRequest, "Invalid method format");
|
||||
}
|
||||
return Method(formatted.substr(0, splitPos), formatted.substr(splitPos + 1));
|
||||
}
|
||||
|
||||
Method::Method(std::string domain, std::string name)
|
||||
: domain_(std::move(domain))
|
||||
, name_(std::move(name)) {}
|
||||
|
||||
std::string Method::formatted() const {
|
||||
return folly::to<std::string>(domain_, '.', name_);
|
||||
}
|
||||
|
||||
Event::Event(std::string domain, std::string method, folly::dynamic params)
|
||||
: method_(std::move(domain), std::move(method))
|
||||
, params_(std::move(params)) {}
|
||||
|
||||
Event::operator std::string() const {
|
||||
auto event = folly::dynamic::object("method", method_.formatted());
|
||||
if (!params_.isNull()) {
|
||||
event("params", params_);
|
||||
}
|
||||
|
||||
return folly::toJson(std::move(event));
|
||||
}
|
||||
|
||||
namespace Timestamp {
|
||||
double now() {
|
||||
using duration = std::chrono::duration<double, std::ratio<1>>;
|
||||
auto epoch = std::chrono::duration_cast<duration>(
|
||||
std::chrono::system_clock::now().time_since_epoch()
|
||||
);
|
||||
return epoch.count();
|
||||
}
|
||||
}
|
||||
|
||||
Error::Error(int callId, ErrorCode code, std::string message)
|
||||
: callId_(callId)
|
||||
, code_(code)
|
||||
, message_(std::move(message)) {}
|
||||
|
||||
Error::Error(ErrorCode code, std::string message)
|
||||
: code_(code)
|
||||
, message_(std::move(message)) {}
|
||||
|
||||
Error::operator std::string() const {
|
||||
auto errorCode = static_cast<int>(code_);
|
||||
return folly::toJson(
|
||||
folly::dynamic::object("id", getCallId(callId_))
|
||||
("error", folly::dynamic::object("code", errorCode)("message", message_))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
#include <folly/Optional.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class Method {
|
||||
public:
|
||||
static Method parse(const std::string& formatted);
|
||||
|
||||
Method(std::string domain, std::string name);
|
||||
|
||||
const std::string& domain() const {
|
||||
return domain_;
|
||||
}
|
||||
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
std::string formatted() const;
|
||||
private:
|
||||
std::string domain_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
class Event {
|
||||
public:
|
||||
explicit Event(std::string domain, std::string method, folly::dynamic params);
|
||||
|
||||
operator std::string() const;
|
||||
private:
|
||||
Method method_;
|
||||
folly::dynamic params_;
|
||||
};
|
||||
|
||||
namespace Timestamp {
|
||||
double now();
|
||||
}
|
||||
|
||||
enum class ErrorCode {
|
||||
ParseError = -32700,
|
||||
InvalidRequest = -32600,
|
||||
MethodNotFound = -32601,
|
||||
InvalidParams = -32602,
|
||||
InternalError = -32603,
|
||||
ServerError = -32000,
|
||||
};
|
||||
|
||||
class Error {
|
||||
public:
|
||||
Error(int callId, ErrorCode code, std::string message);
|
||||
Error(ErrorCode code, std::string message);
|
||||
|
||||
const std::string& message() const {
|
||||
return message_;
|
||||
}
|
||||
|
||||
ErrorCode code() const {
|
||||
return code_;
|
||||
}
|
||||
|
||||
operator std::string() const;
|
||||
private:
|
||||
folly::Optional<int> callId_;
|
||||
ErrorCode code_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# Inspector
|
||||
|
||||
This directory implements an the Chrome debugging protocol [1]. The version used is roughly 1.1 of
|
||||
the protocol. The code here doesn't specify a transport and doesn't implement an actual server. This
|
||||
is left up to higher parts of the stack.
|
||||
|
||||
The implementation uses multiple "dispatchers" to route messages for a specific domain. It reuses
|
||||
existing code in JavaScriptCore to handle the domains for Debugger and Runtime. For Console, Page
|
||||
and Inspector there are new implementations.
|
||||
|
||||
## Open source
|
||||
|
||||
The inspector currently doesn't compile in open source. This is due to how the build on Android
|
||||
where we download the JSC sources and build an artifact separately, later download the headers we
|
||||
need. The number of headers download would have to be expanded and verify that it builds correctly.
|
||||
|
||||
[1]: https://developer.chrome.com/devtools/docs/debugger-protocol
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JavaScriptCore/config.h>
|
||||
#include <wtf/text/WTFString.h>
|
||||
#include <wtf/text/CString.h>
|
||||
#include <JavaScriptCore/JSContextRef.h>
|
||||
#include <JavaScriptCore/JSObjectRef.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
inline std::string toStdString(const WTF::String& str) {
|
||||
return std::string(str.utf8().data());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class AgentMap {
|
||||
public:
|
||||
void add(JSGlobalContextRef ctx, T* agent) {
|
||||
map_[ctx] = agent;
|
||||
}
|
||||
|
||||
void remove(T* agent) {
|
||||
auto it = std::find_if(
|
||||
map_.begin(),
|
||||
map_.end(),
|
||||
[agent](const typename MapType::value_type& entry) { return entry.second == agent; });
|
||||
map_.erase(it);
|
||||
}
|
||||
|
||||
T* get(JSGlobalContextRef ctx) {
|
||||
return map_.at(ctx);
|
||||
}
|
||||
private:
|
||||
using MapType = std::unordered_map<JSGlobalContextRef, T*>;
|
||||
MapType map_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user