// Copyright 2004-present Facebook. All Rights Reserved. #include "Inspector.h" #include "InspectorController.h" #include #include #include #include #include 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 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 controller) : name(name) , controller(std::move(controller)) {} Inspector::PageHolder::~PageHolder() = default; Inspector& Inspector::instance() { static Inspector inspector; return inspector; } std::vector Inspector::getPages() const { std::lock_guard lock(registrationMutex_); std::vector 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 lock(registrationMutex_); auto controller = folly::make_unique(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 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::connect(int pageId, std::unique_ptr remote) { std::lock_guard lock(registrationMutex_); return folly::make_unique(pages_.at(pageId).connect(std::move(remote))); } void Inspector::disconnect(int pageId) { std::lock_guard lock(registrationMutex_); pages_.at(pageId).controller->onDisconnect(); } std::shared_ptr Inspector::PageHolder::connect(std::unique_ptr remote) { if (connection_) { throw std::runtime_error("Already connected"); } connection_ = std::make_shared(*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) : 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 lock(remoteMutex_); if (!remoteConnection_) { return; } remoteConnection_->onMessage(std::move(message)); } void Inspector::DuplexConnection::sendToLocal(std::string message) { std::lock_guard lock(localMutex_); if (!remoteConnection_) { return; } page_.controller->onMessage(std::move(message)); } void Inspector::DuplexConnection::terminate(bool local) { std::lock_guard lockLocal(localMutex_); { // Temp lock here so we can still send going away message std::lock_guard lockRemote(remoteMutex_); if (!remoteConnection_) { // Already disconnected return; } } if (local) { page_.controller->onGoingAway(); } std::lock_guard lockRemote(remoteMutex_); auto remoteConnection = std::move(remoteConnection_); if (local) { remoteConnection->onDisconnect(); } page_.controller->onDisconnect(); // This kills us page_.connection_.reset(); } } }