190 lines
5.1 KiB
C++
190 lines
5.1 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include "InspectorInterfaces.h"
|
|
|
|
#include <cassert>
|
|
#include <list>
|
|
#include <mutex>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
|
|
namespace facebook::react::jsinspector_modern {
|
|
|
|
// pure destructors in C++ are odd. You would think they don't want an
|
|
// implementation, but in fact the linker requires one. Define them to be
|
|
// empty so that people don't count on them for any particular behaviour.
|
|
IDestructible::~IDestructible() {}
|
|
ILocalConnection::~ILocalConnection() {}
|
|
IRemoteConnection::~IRemoteConnection() {}
|
|
IInspector::~IInspector() {}
|
|
IPageStatusListener::~IPageStatusListener() {}
|
|
|
|
const folly::dynamic targetCapabilitiesToDynamic(
|
|
const InspectorTargetCapabilities& capabilities) {
|
|
return folly::dynamic::object(
|
|
"nativePageReloads", capabilities.nativePageReloads)(
|
|
"nativeSourceCodeFetching", capabilities.nativeSourceCodeFetching)(
|
|
"prefersFuseboxFrontend", capabilities.prefersFuseboxFrontend);
|
|
}
|
|
|
|
namespace {
|
|
|
|
class InspectorImpl : public IInspector {
|
|
public:
|
|
int addPage(
|
|
const std::string& description,
|
|
const std::string& vm,
|
|
ConnectFunc connectFunc,
|
|
InspectorTargetCapabilities capabilities) override;
|
|
void removePage(int pageId) override;
|
|
|
|
std::vector<InspectorPageDescription> getPages() const override;
|
|
std::unique_ptr<ILocalConnection> connect(
|
|
int pageId,
|
|
std::unique_ptr<IRemoteConnection> remote) override;
|
|
|
|
void registerPageStatusListener(
|
|
std::weak_ptr<IPageStatusListener> listener) override;
|
|
|
|
private:
|
|
class Page {
|
|
public:
|
|
Page(
|
|
int id,
|
|
const std::string& title,
|
|
const std::string& vm,
|
|
ConnectFunc connectFunc,
|
|
InspectorTargetCapabilities capabilities);
|
|
operator InspectorPageDescription() const;
|
|
|
|
ConnectFunc getConnectFunc() const;
|
|
|
|
private:
|
|
int id_;
|
|
std::string description_;
|
|
std::string vm_;
|
|
ConnectFunc connectFunc_;
|
|
InspectorTargetCapabilities capabilities_;
|
|
};
|
|
mutable std::mutex mutex_;
|
|
int nextPageId_{1};
|
|
std::map<int, Page> pages_;
|
|
std::list<std::weak_ptr<IPageStatusListener>> listeners_;
|
|
};
|
|
|
|
InspectorImpl::Page::Page(
|
|
int id,
|
|
const std::string& description,
|
|
const std::string& vm,
|
|
ConnectFunc connectFunc,
|
|
InspectorTargetCapabilities capabilities)
|
|
: id_(id),
|
|
description_(description),
|
|
vm_(vm),
|
|
connectFunc_(std::move(connectFunc)),
|
|
capabilities_(std::move(capabilities)) {}
|
|
|
|
InspectorImpl::Page::operator InspectorPageDescription() const {
|
|
return InspectorPageDescription{
|
|
.id = id_,
|
|
.description = description_,
|
|
.vm = vm_,
|
|
.capabilities = capabilities_,
|
|
};
|
|
}
|
|
|
|
InspectorImpl::ConnectFunc InspectorImpl::Page::getConnectFunc() const {
|
|
return connectFunc_;
|
|
}
|
|
|
|
int InspectorImpl::addPage(
|
|
const std::string& description,
|
|
const std::string& vm,
|
|
ConnectFunc connectFunc,
|
|
InspectorTargetCapabilities capabilities) {
|
|
std::scoped_lock lock(mutex_);
|
|
|
|
// Note: getPages guarantees insertion/addition order. As an implementation
|
|
// detail, incrementing page IDs takes advantage of std::map's key ordering.
|
|
int pageId = nextPageId_++;
|
|
assert(pages_.count(pageId) == 0 && "Unexpected duplicate page ID");
|
|
pages_.emplace(
|
|
pageId,
|
|
Page{pageId, description, vm, std::move(connectFunc), capabilities});
|
|
|
|
return pageId;
|
|
}
|
|
|
|
void InspectorImpl::removePage(int pageId) {
|
|
std::scoped_lock lock(mutex_);
|
|
|
|
if (pages_.erase(pageId) != 0) {
|
|
for (auto listenerWeak : listeners_) {
|
|
if (auto listener = listenerWeak.lock()) {
|
|
listener->onPageRemoved(pageId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<InspectorPageDescription> InspectorImpl::getPages() const {
|
|
std::scoped_lock lock(mutex_);
|
|
|
|
std::vector<InspectorPageDescription> inspectorPages;
|
|
// pages_ is a std::map keyed on an incremental id, so this is insertion
|
|
// ordered.
|
|
for (auto& it : pages_) {
|
|
inspectorPages.push_back(InspectorPageDescription(it.second));
|
|
}
|
|
|
|
return inspectorPages;
|
|
}
|
|
|
|
std::unique_ptr<ILocalConnection> InspectorImpl::connect(
|
|
int pageId,
|
|
std::unique_ptr<IRemoteConnection> remote) {
|
|
IInspector::ConnectFunc connectFunc;
|
|
|
|
{
|
|
std::scoped_lock lock(mutex_);
|
|
|
|
auto it = pages_.find(pageId);
|
|
if (it != pages_.end()) {
|
|
connectFunc = it->second.getConnectFunc();
|
|
}
|
|
}
|
|
|
|
return connectFunc ? connectFunc(std::move(remote)) : nullptr;
|
|
}
|
|
|
|
void InspectorImpl::registerPageStatusListener(
|
|
std::weak_ptr<IPageStatusListener> listener) {
|
|
std::scoped_lock lock(mutex_);
|
|
// Remove expired listeners
|
|
for (auto it = listeners_.begin(); it != listeners_.end();) {
|
|
if (it->expired()) {
|
|
it = listeners_.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
listeners_.push_back(listener);
|
|
}
|
|
} // namespace
|
|
|
|
IInspector& getInspectorInstance() {
|
|
static InspectorImpl instance;
|
|
return instance;
|
|
}
|
|
|
|
std::unique_ptr<IInspector> makeTestInspectorInstance() {
|
|
return std::make_unique<InspectorImpl>();
|
|
}
|
|
|
|
} // namespace facebook::react::jsinspector_modern
|