Skip to content

Commit cd28c41

Browse files
authored
feat(inspector): attach Chrome DevTools to Web Worker isolates (#386)
1 parent 4dadac8 commit cd28c41

8 files changed

Lines changed: 873 additions & 80 deletions

File tree

NativeScript/inspector/JsV8InspectorClient.h

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <functional>
77
#include <map>
8+
#include <mutex>
89
#include <string>
910
#include <vector>
1011

@@ -15,6 +16,8 @@
1516

1617
namespace v8_inspector {
1718

19+
class WorkerInspectorClient;
20+
1821
class JsV8InspectorClient : V8InspectorClient, V8Inspector::Channel {
1922
public:
2023
JsV8InspectorClient(tns::Runtime* runtime);
@@ -23,6 +26,20 @@ class JsV8InspectorClient : V8InspectorClient, V8Inspector::Channel {
2326
void disconnect();
2427
void dispatchMessage(const std::string& message);
2528

29+
// The single instance debugging the main isolate (created when IsDebug);
30+
// also acts as the router for worker sessions. Null in release builds.
31+
static JsV8InspectorClient* GetInstance();
32+
33+
// Thread-safe write to the connected frontend (no-op when disconnected).
34+
void SendToFrontend(const std::string& message);
35+
36+
// Worker targets (Chrome DevTools Target domain, flat-session mode).
37+
// Register/Unregister are called on the worker's own thread.
38+
void RegisterWorkerTarget(int workerId, WorkerInspectorClient* client);
39+
void UnregisterWorkerTarget(int workerId);
40+
// Called on a worker thread by the Debugger.pause interrupt.
41+
void SchedulePauseInWorker(int workerId);
42+
2643
// Overrides of V8Inspector::Channel
2744
void sendResponse(int callId, std::unique_ptr<StringBuffer> message) override;
2845
void sendNotification(std::unique_ptr<StringBuffer> message) override;
@@ -63,13 +80,39 @@ class JsV8InspectorClient : V8InspectorClient, V8Inspector::Channel {
6380

6481
// Streams backing Network.loadNetworkResource responses, read by the
6582
// frontend through IO.read/IO.close (how Chrome DevTools fetches source
66-
// maps from the target). Only touched from dispatchMessage (main thread).
83+
// maps from the target). Served on the socket thread for any session;
84+
// guarded by resourceStreamsMutex_.
6785
struct ResourceStream {
6886
std::string data;
6987
size_t offset = 0;
7088
};
7189
std::map<std::string, ResourceStream> resourceStreams_;
7290
int lastStreamId_ = 0;
91+
std::mutex resourceStreamsMutex_;
92+
93+
// Worker targets announced to the frontend via Target.attachedToTarget,
94+
// keyed by their flat-protocol sessionId. Guarded by workerTargetsMutex_;
95+
// a registered client pointer stays valid until UnregisterWorkerTarget
96+
// (which runs on the worker's own thread, before the client is deleted).
97+
struct WorkerTarget {
98+
int workerId;
99+
WorkerInspectorClient* client;
100+
bool announced = false;
101+
};
102+
std::map<std::string, WorkerTarget> workerTargets_;
103+
std::mutex workerTargetsMutex_;
104+
bool autoAttach_ = false; // guarded by workerTargetsMutex_
105+
106+
static JsV8InspectorClient* instance_;
107+
108+
std::mutex senderMutex_;
109+
110+
// Routes a frontend message carrying a sessionId to its worker session
111+
// (socket thread). msgId is -1 when the message has no id.
112+
void RouteToWorker(const std::string& sessionId, const std::string& method,
113+
long long msgId, const std::string& message);
114+
// Announces all not-yet-announced workers (after Target.setAutoAttach).
115+
void AnnounceWorkerTargets();
73116

74117
// Override of V8InspectorClient
75118
v8::Local<v8::Context> ensureDefaultContextInGroup(
@@ -90,10 +133,15 @@ class JsV8InspectorClient : V8InspectorClient, V8Inspector::Channel {
90133
const v8::FunctionCallbackInfo<v8::Value>& args);
91134

92135
// Source map delivery to Chrome DevTools (Network.loadNetworkResource + IO
93-
// domain). V8's inspector doesn't implement these embedder domains.
94-
void HandleLoadNetworkResource(int msgId, const std::string& url);
95-
void HandleIORead(int msgId, const std::string& handle, int size);
96-
void HandleIOClose(int msgId, const std::string& handle);
136+
// domain). V8's inspector doesn't implement these embedder domains. The
137+
// handlers are filesystem-only and thread-safe; they serve any session
138+
// (sessionId is echoed in the reply when non-empty).
139+
void HandleLoadNetworkResource(int msgId, const std::string& url,
140+
const std::string& sessionId);
141+
void HandleIORead(int msgId, const std::string& handle, int size,
142+
const std::string& sessionId);
143+
void HandleIOClose(int msgId, const std::string& handle,
144+
const std::string& sessionId);
97145

98146
// {N} specific helpers
99147
bool CallDomainHandlerFunction(v8::Local<v8::Context> context,

0 commit comments

Comments
 (0)