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
package Torello.Browser;

import static Torello.Java.C.BYELLOW;
import static Torello.Java.C.RESET;

import Torello.HTML.Scrape;

import java.net.URL;
import java.net.MalformedURLException;
import java.io.StringReader;
import javax.json.Json;
import javax.json.JsonObject;


// This builder class is used internally by BrowserConn to construct instances of
// BrowserConn based on JSON data returned from the Chrome DevTools Protocol.
// It is used during parsing but not exposed publicly.
// 
// ================================================================================================
// BCBuilder.java
// ================================================================================================
// This internal-use-only builder class is responsible for retrieving metadata from a
// running instance of a Chrome browser that was started with remote debugging enabled.
//
// It constructs a proper `BrowserConn` instance by:
//
//   1. Connecting to the /json/version endpoint on localhost (default port 9222)
//   2. Downloading JSON that describes the browser-level metadata
//   3. Parsing that JSON into a `JsonObject`
//   4. Feeding it into the `BrowserConn(JsonObject)` constructor
//
// This builder does not expose any public API — it exists solely to help hide the
// details of remote connection handling and JSON parsing logic.
// ================================================================================================
// 
// Every line of the above comments were created by Chat-GPT on August 5th, 2025
// 
// Now - allow me to add the following (written by a real live human)
// This class is a Constructor / Static Builder of sorts:
//
// 1) It is an "External" or "Helper" Class to help keep the Class 'BrowserConn' simple and
//    readable.
// 
// 2) It isolates every one of the potential Error-Cases that could concievably occur when trying
//    to build an instance of BrowserConn, and gathers every one of the potential Exception-Throw
//    Error-Checks into a single Method
// 
// 3) If you want to really understand what possible Errors are being checked, just look closely
//    at the Error-Messages being associated with each of the particular try-catch statements.

final class BCBuilder
{
    // This Helper Class' only method.  All it is doing is wrapping absolutely every possible 
    // statement that is about to be executed into a try-catch, and then outputing a well-worded
    // error message if the try-catch fails.

    static BrowserConn getBrowserConn(Integer port, final boolean quiet)
    {
        if (port == null) port = 9222;

        final String    urlStr ="http://127.0.0.1:" + port + "/json/version";
        final URL       url;

        try 
            { url = new URL(urlStr); }

        catch (MalformedURLException e)
        {
            throw new RDPException(
                "An Internal Error has occurred.  Unable to instantiate URL.\n" +
                "    URL: " + urlStr + "\n" +
                "    Exception: " + e.getClass().getSimpleName() + " - " + e.getMessage() + '\n' +
                "Please see cause-exception for details.",
                e
            );
        }

        if (!quiet) System.out.println(
            "\nQuerying Browser-Level WebSocket URL:\n\t" +
            BYELLOW + urlStr + RESET
        );

        final String json;

        try
            { json = Scrape.scrapePage(url); }

        catch (Exception e)
        {
            throw new RDPException(
                "Problem while querying browser-level WebSocket URL:\n" +
                "    URL: " + url + "\n" +
                "    Exception: " + e.getClass().getSimpleName() + " - " + e.getMessage() + '\n' +
                "Please see cause-exception for details.",
                e
            );
        }

        if (!quiet) System.out.println("Browser-Level JSON Response:\n" + json);

        final JsonObject obj;

        try 
            { obj = Json.createReader(new StringReader(json)).readObject(); }

        catch (Exception e)
        {
            throw new RDPException(
                "Failed to parse JSON object from Chrome's /json/version response:\n" +
                "    Exception: " + e.getClass().getSimpleName() + " - " + e.getMessage() + '\n' +
                "Please see cause-exception for details.",
                e
            );
        }

        try
            { return new BrowserConn(obj); }

        catch (Exception e)
        {
            throw new RDPException(
                "Failed to construct BrowserConn from parsed JSON object:\n" +
                "    Exception: " + e.getClass().getSimpleName() + " - " + e.getMessage() + '\n' +
                "Raw JSON was:\n" + json,
                e
            );
        }
    }
}