Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/react-native/Libraries/Utilities/HMRClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ Error: ${e.message}`;
const changeId = body?.changeId;
if (changeId != null && changeId !== lastMarkerChangeId) {
lastMarkerChangeId = changeId;

// Add marker entry in performance timeline
performance.mark('Fast Refresh - Update done', {
detail: {
devtools: {
Expand All @@ -246,6 +248,17 @@ Error: ${e.message}`;
},
},
});

// Notify CDP clients via lifecycle event
if (
// $FlowFixMe[prop-missing] - Injected by HostRuntimeBinding
typeof globalThis.__react_native_application_cdp_binding ===
'function'
) {
globalThis.__react_native_application_cdp_binding(
'fastRefreshComplete',
);
}
}
}
});
Expand Down
31 changes: 31 additions & 0 deletions packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,29 @@ class HostAgent::Impl final {
"Expected to find at least one session eligible to receive a background trace after ReactNativeApplication.enable");
(void)emitted;

static constexpr auto kBindingName =
"__react_native_application_cdp_binding";
sessionState_.privateBindings.insert(std::string(kBindingName));
sessionState_.onPrivateBindingCalled =
[this](const std::string& name, const std::string& payload) {
if (name == kBindingName && payload == "fastRefreshComplete") {
emitFastRefreshComplete();
}
};
if (instanceAgent_) {
instanceAgent_->installPrivateBindingHandler(kBindingName);
}

return {
.isFinishedHandlingRequest = true,
.shouldSendOKResponse = true,
};
}
if (req.method == "ReactNativeApplication.disable") {
sessionState_.isReactNativeApplicationDomainEnabled = false;
sessionState_.privateBindings.erase(
"__react_native_application_cdp_binding");
sessionState_.onPrivateBindingCalled = nullptr;

return {
.isFinishedHandlingRequest = true,
Expand Down Expand Up @@ -392,6 +408,20 @@ class HostAgent::Impl final {
frontendChannel_(cdp::jsonNotification("Network.disable"));
}

void emitFastRefreshComplete() {
if (!sessionState_.isReactNativeApplicationDomainEnabled) {
return;
}
folly::dynamic params = folly::dynamic::object(
"timestamp",
duration_cast<milliseconds>(system_clock::now().time_since_epoch())
.count());
frontendChannel_(
cdp::jsonNotification(
"ReactNativeApplication.unstable_fastRefreshComplete",
std::move(params)));
}

private:
enum class FuseboxClientType { Unknown, Fusebox, NonFusebox };

Expand Down Expand Up @@ -497,6 +527,7 @@ class HostAgent::Impl final {
return false;
}
void emitSystemStateChanged(bool isSingleHost) {}
void emitFastRefreshComplete() {}
};

#endif // REACT_NATIVE_DEBUGGER_ENABLED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ void InstanceAgent::setCurrentRuntime(RuntimeTarget* runtimeTarget) {
maybeSendPendingConsoleMessages();
}

void InstanceAgent::installPrivateBindingHandler(const std::string& name) {
if (runtimeAgent_) {
runtimeAgent_->installPrivateBindingHandler(name);
}
}

void InstanceAgent::maybeSendExecutionContextCreatedNotification() {
if (runtimeAgent_ != nullptr) {
auto& newContext = runtimeAgent_->getExecutionContextDescription();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ class InstanceAgent final {
*/
void sendConsoleMessage(SimpleConsoleMessage message);

/**
* Install a private binding handler on the current runtime, if one exists.
*/
void installPrivateBindingHandler(const std::string &name);

private:
void maybeSendExecutionContextCreatedNotification();
void sendConsoleMessageImmediately(SimpleConsoleMessage message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ RuntimeAgent::RuntimeAgent(
}
}

for (auto& name : sessionState_.privateBindings) {
targetController_.installBindingHandler(name);
}

if (sessionState_.isRuntimeDomainEnabled) {
targetController_.notifyDomainStateChanged(
RuntimeTargetController::Domain::Runtime, true, *this);
Expand Down Expand Up @@ -97,6 +101,13 @@ bool RuntimeAgent::handleRequest(const cdp::PreparsedRequest& req) {
void RuntimeAgent::notifyBindingCalled(
const std::string& bindingName,
const std::string& payload) {
// Notify private binding subscribers (managed separately from client-
// initiated subscribedBindings).
if (sessionState_.privateBindings.count(bindingName) != 0u &&
sessionState_.onPrivateBindingCalled) {
sessionState_.onPrivateBindingCalled(bindingName, payload);
}

// NOTE: When dispatching @cdp Runtime.bindingCalled notifications, we don't
// re-check whether the session is expecting notifications from the current
// context - only that it's subscribed to that binding name.
Expand All @@ -119,6 +130,11 @@ void RuntimeAgent::notifyBindingCalled(
"name", bindingName)("payload", payload)));
}

void RuntimeAgent::installPrivateBindingHandler(
const std::string& bindingName) {
targetController_.installBindingHandler(bindingName);
}

RuntimeAgent::ExportedState RuntimeAgent::getExportedState() {
return {
.delegateState = delegate_ ? delegate_->getExportedState() : nullptr,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ class RuntimeAgent final {

void notifyBindingCalled(const std::string &bindingName, const std::string &payload);

/**
* Install a private binding handler on the runtime. Private bindings are
* tracked separately from client-initiated subscribedBindings and do not
* emit @cdp Runtime.bindingCalled events.
*/
void installPrivateBindingHandler(const std::string &bindingName);

struct ExportedState {
std::unique_ptr<RuntimeAgentDelegate::ExportedState> delegateState;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ExecutionContext.h"
#include "RuntimeAgent.h"

#include <functional>
#include <string>
#include <string_view>
#include <unordered_map>
Expand Down Expand Up @@ -59,6 +60,20 @@ struct SessionState {
*/
RuntimeAgent::ExportedState lastRuntimeAgentExportedState;

/**
* Binding names installed privately by the backend (e.g. for
* ReactNativeApplication domain events). These are separate from
* subscribedBindings (which tracks client-initiated @cdp Runtime.addBinding
* requests) and do not emit @cdp Runtime.bindingCalled to the frontend.
*/
std::unordered_set<std::string> privateBindings;

/**
* Callback invoked by RuntimeAgent when a private binding is called from
* JS. Parameters are the binding name and payload.
*/
std::function<void(const std::string &, const std::string &)> onPrivateBindingCalled;

// Here, we will eventually allow RuntimeAgents to store their own arbitrary
// state (e.g. some sort of K/V storage of folly::dynamic?)

Expand Down
Loading