1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
package Torello.Browser;

import static Torello.Java.C.BCYAN;
import static Torello.Java.C.BGREEN_BKGND;
import static Torello.Java.C.RESET;

import Torello.Java.StorageWriter;
import Torello.Java.StrIndent;
import Torello.Java.StrPrint;

import Torello.Java.Additional.AppendableSafe;
import Torello.Browser.WebSocketSender;
import Torello.Java.EXCC;

import java.lang.reflect.Constructor;
import java.util.function.Consumer;
import javax.json.JsonObject;


// ************************************************************************************************
// ************************************************************************************************
// Chat-GPT wrote the summary below: July 20th, 2025 -  VERY ACCURATE!
// ************************************************************************************************
// ************************************************************************************************
// 
// Package-private class used internally by WebSocketSender to process incoming CDP event messages.
// These messages are not responses to sent commands, but spontaneous notifications sent by the
// browser (e.g., "Page.loadEventFired", "Runtime.consoleAPICalled").
//
// This class extracts the method name, prepares a simplified payload, and forwards the event to
// the active WSAdapter instance. It plays a critical role in dispatching asynchronous events that
// originate from the browser without being tied to a request ID.
//
// When a browser-generated event is received over the WebSocket connection, this class is
// responsible for handling and routing that event to the appropriate typed POJO that represents
// it. Every event generated by the Chrome DevTools Protocol (e.g., "Debugger.paused",
// "Network.requestWillBeSent") is associated with a dedicated Java class — typically implemented
// as a nested type within its corresponding domain class (like `Debugger.paused` inside `Debugger`).
//
// To perform this lookup, the incoming CDP event name is mapped to its associated partially-qualified
// class name (e.g., "Debugger.paused") using a static TreeMap located in class 'EVENT_DATA_FILE'.
// This map is loaded at runtime from a precompiled data file. The TreeMap provides a way to
// translate the simple event string received from Chrome into a class that can be dynamically
// loaded and instantiated via reflection.
//
// Note that the TreeMap’s value is only "partially-qualified": it includes the containing class
// name and nested type (e.g., `Debugger.paused`), but omits the `Torello.Browser.` prefix.
// This separation allows the Java class to be fully resolved at runtime using the known package
// path in combination with the lookup result.
// 
// 
// ************************************************************************************************
// ************************************************************************************************
// Chat-GPT Generated Explanation, Version 2 
// ************************************************************************************************
// ************************************************************************************************
// 
// 
// (This is a ** GREAT SUMMARY **, by the way - I REALLY GET WRITER'S BLOCK VERY VERY EASILY!)
// 
// When Chrome fires asynchronous browser events over the DevTools Protocol, they are delivered as
// JSON-formatted WebSocket messages received by the underlying Neovisionaries WebSocket API. These
// datagrams typically contain two key fields: an "event" property (representing the event name such
// as "paused" or "loadEventFired"), and a "params" object containing structured event-specific data.
// The raw event name received is not namespaced — Chrome does not include the domain or fully
// qualified class name, only the event's local identifier.
//
// To determine which Java class should be used to handle each incoming event, a
// TreeMap<String, String> is loaded at runtime. This map connects the unqualified Chrome event name
// (e.g., "paused") to a fully scoped class path segment like "Debugger$paused", where the domain and
// event are combined into a nested type using Java’s '$' separator. This allows the system to
// resolve the appropriate event-type class reflectively by constructing a fully-qualified name and
// invoking Class.forName("Torello.Browser." + className).
//
// Each event class is a POJO generated by a code generator, and every such class includes a
// constructor that accepts a single javax.json.JsonObject — this constructor is used to deserialize
// the incoming "params" payload into a strongly typed Java object. Once instantiated, the resulting
// object is cast to the BrowserEvent interface and dispatched to the Consumer<BrowserEvent> handler
// originally provided by the user when creating the WebSocketSender instance. This flow allows raw
// Chrome events to be automatically transformed into domain-typed Java objects and seamlessly
// delivered to user-defined callback logic.
// 
// 
// ************************************************************************************************
// ************************************************************************************************
// TEST CLASS:
// ************************************************************************************************
// ************************************************************************************************
// 
// 
// import Torello.Java.FileRW;
// import java.util.Map;
// import java.util.TreeMap;
// 
// class df 
// {
//     public static void main(String[] argv) throws Exception
//     {
//         final String dataFileName = "Torello/Browser/data-files/EventNames.tmdat";
// 
//         @SuppressWarnings("unchecked")
//         final TreeMap<String, String> tm = (TreeMap<String, String>)
//             FileRW.readObjectFromFile(dataFileName, TreeMap.class, true);
// 
//         for (final Map.Entry<String, String> e : tm.entrySet())
//             System.out.println(e.toString());
//     }
// }
// 
// 
// ************************************************************************************************
// ************************************************************************************************
// OUTPUTS:
// ************************************************************************************************
// ************************************************************************************************
// 
// 
// narrative@cloudshell:~$ java df
// accepted=Tethering$accepted
// addDatabase=Database$addDatabase
// addHeapSnapshotChunk=HeapProfiler$addHeapSnapshotChunk
// animationCanceled=Animation$animationCanceled
// animationCreated=Animation$animationCreated
// animationStarted=Animation$animationStarted
// attachedToTarget=Target$attachedToTarget
// attributeModified=DOM$attributeModified
// attributeRemoved=DOM$attributeRemoved
// ...
// windowOpen=Page$windowOpen
// workerErrorReported=ServiceWorker$workerErrorReported
// workerRegistrationUpdated=ServiceWorker$workerRegistrationUpdated
// workerVersionUpdated=ServiceWorker$workerVersionUpdated
// narrative@cloudshell:~$ 


class HandleEvent 
{
    // A Browser Generated Event has fired.  Report this even.
    static void handle(
            final   JsonObject      jo,
                    String          eventName,
            final   WebSocketSender THIS
        )
    {
        final Consumer<RDPError>        rdpErrorHandler = THIS.rdpErrorHandler;
        final Consumer<BrowserEvent>    eventHandler    = THIS.eventHandler;

        final String eventClassName = eventName.replace(".", "$");

        // Convert the class-name from a string to a fully-qualified class name
        final String className = EVENT_DATA_FILE.getEventClassName(eventClassName);


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // ERROR-CASE: "Method" Json-Property doesn't map to a type listed in the Data-File
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (className == null)
        {
            final String errMsg = "Un-Typed Browser Event Received";
            PrintUtil.ERR(THIS, errMsg, jo);
            rdpErrorHandler.accept(new RDPError(errMsg, jo));
            return;
        }
            

        final Class<?> c;

        try 
            { c = Class.forName("Torello.Browser." + className); }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // ERROR-CASE: The Class Loader thew an exception while attempting to load the Class
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        catch (Exception e)
        {
            final String errMsg = 
                "Event Class-Loader Attempt has Thrown Exception:\n" +
                "Guessed Event-Class Name [" + className + ']';

            PrintUtil.ERR(THIS, errMsg, jo, e);
            rdpErrorHandler.accept(new RDPError(errMsg, jo, e));
            return;
        }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // ERROR-CASE: The Class Loader seems to have returned 'null' from the Class.forName call
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (c == null)
        {
            final String errMsg = 
                "BrowserEvent Class-Loader Attempt has Returned a Null Class<?>:\n" +
                "Guessed Event-Class Name [" + className + ']';

            PrintUtil.ERR(THIS, errMsg, jo);
            rdpErrorHandler.accept(new RDPError(errMsg, jo));
            return;
        }


        final Object event;

        try 
        {
            final Constructor<?>    ctr     = c.getConstructor(JsonObject.class);
            final JsonObject        params  = jo.getJsonObject("params");
            event = ctr.newInstance(params);
        }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // ERROR-CASE: Retrieving the Class Constructor, or invoking it has caused an exception
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        catch (Exception e)
        {
            final String errMsg =
                "Failed to build BrowserEvent Class using Constructor: " + 
                '[' + BCYAN + "Torello.Browser." + className + RESET;

            PrintUtil.ERR(THIS, errMsg, jo, e);
            rdpErrorHandler.accept(new RDPError(errMsg, jo, e));
            return;
        }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // ERROR-CASE: (Likely Impossible) Constructor Invocation seems to have returned null
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (event == null)
        {
            final String errMsg =
                "Failed to build event-class using constructor: " + 
                '[' + BCYAN + className + RESET + ']';

            PrintUtil.ERR(THIS, errMsg, jo);
            rdpErrorHandler.accept(new RDPError(errMsg, jo));
            return;
        }

        final BrowserEvent browserEvent;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // ERROR-CASE: (Likely Impossible) Constructed Object is not an instance of BrowserEvent
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (! (event instanceof BrowserEvent))
        {
            final String errMsg =
                "Event Class not an Instance of BrowserEvent: " + 
                '[' + BCYAN + className + RESET + ']';

            PrintUtil.ERR(THIS, errMsg, jo);
            rdpErrorHandler.accept(new RDPError(errMsg, jo));
            return;
        }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // SUCCESS
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        else browserEvent = (BrowserEvent) event;

        THIS.app()
            .append(BGREEN_BKGND + " Browser Event Received: " + RESET + '\n')
            .append("Event-Class: " + event.getClass().getCanonicalName() + '\n')
            .append(StrIndent.indent(event.toString(), 8))
            .append('\n');

        eventHandler.accept(browserEvent);
    }
}