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

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

import Torello.JSON.ReadJSON;
import Torello.JavaDoc.Annotations.LinkJavaSource;
import NeoVisionaries.WebSockets.WebSocketException;

import java.util.Objects;
import java.util.stream.Stream;

import java.io.IOException;

import javax.json.JsonObject;
import javax.json.JsonArray; // javadoc needs this

/**
 * A class which represents a connection to a particular Page or Tab that has been opened within
 * the Browser, rather than a connection to the Browser itself.
 * 
 * <BR /><BR />
 * Represents a snapshot of metadata returned from Chrome’s
 * <B STYLE='color:red;'>{@code /json/version}</B> End-Point. This includes protocol versioning, V8
 * details, and the root <B>{@link NeoVisionaries.WebSockets.WebSocket WebSocket}</B> {@code URL}
 * for establishing DevTools sessions.
 *
 * <BR /><BR />
 * Although this class offers a public constructor, the recommended way to build an instance is via
 * the {@code static} method: <B>{@link #getAllPageConn getAllPageConn}</B>.  This stream of
 * instances which are returned each contain a few informational fields that were scraped from the
 * Json-Response to the above mentioned Chrome response object.
 * 
 * <BR /><BR /><B CLASS=JDDescLabel2>A {@link WebSocketSender} Instance:</B>
 * 
 * <BR />
 * To begin sending CDP commands, it is mandatory to obtain an instance of the
 * <B>{@link WebSocketSender}</B> class which has been connected to a Web Browser.  This instance
 * should be passed as the sender to any of the available API methods offered by the Domain Classes
 * in the packages
 * <B><CODE><A HREF='BrowserAPI/package-summary.html'>BrowserAPI</A></CODE></B>
 * and / or the classes in
 * <B><CODE><A HREF='JavaScriptAPI/package-summary.html'>JavaScriptAPI</A></CODE></B>.
 * You should review the methods exported by each of the domains and classes within those packages,
 * and note that the <B>{@link Script}</B> objects returned by them expose an
 * <B>{@link Script#exec}</B> method, which cannot be invoked without an instance of
 * <B>{@link WebSocketSender}</B>.
 * 
 * <BR /><BR />
 * Obtaining the <B>{@link WebSocketSender}</B> instance can be achieved by invoking
 * <B>{@link PageConn#createSender PageConn.createSender}</B>, a
 * method offered by this class.  This method initializes a <B>{@link WebSocketSender}</B> which is
 * connected to the associated <B>{@link #webSocketDebuggerUrl}</B>. That sender can be used to
 * make invocations into all CDP Domains.
 *
 * <BR /><BR />
 * The individual fields exported by this class are documented below, and within their respective
 * documentation.  This class, itself, is largely a structured data holder with minimal logic.
 * Mostly, it parses the Json which is returned by the Web-Browser endpoint
 * <B STYLE='color:red;'>{@code /json/list}</B>
 * 
 * <EMBED CLASS='external-html' DATA-FILE-ID=PCBC_CHECKLIST>
 */
@Torello.JavaDoc.Annotations.CSSLinks(FileNames="BrowserAndPageConn.css")
public class PageConn implements java.io.Serializable, Comparable<PageConn>, Cloneable
{
    /** <EMBED CLASS=external-html DATA-FILE-ID=SVUID> */
    protected static final long serialVersionUID = 1;


    // ********************************************************************************************
    // ********************************************************************************************
    // Public Fields
    // ********************************************************************************************
    // ********************************************************************************************


    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_ID> */
    public final String id;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_TYPE> */
    public final String type;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_TITLE> */
    public final String title;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_URL> */
    public final String url;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_DESCRIPTION> */
    public final String description;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_WS_DEBUG_URL> */
    public final String webSocketDebuggerUrl;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_DEVTOOLS_URL> */
    public final String devtoolsFrontendUrl;

    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_FAVICON_URL> */
    public final String faviconUrl;


    // ********************************************************************************************
    // ********************************************************************************************
    // Package Private Constructors
    // ********************************************************************************************
    // ********************************************************************************************


    /** Constructs an instance of this type. */
    public PageConn(
            final String id,
            final String type,
            final String title,
            final String url,
            final String description,
            final String webSocketDebuggerUrl,
            final String devtoolsFrontendUrl,
            final String faviconUrl
        )
    {
        Objects.requireNonNull(webSocketDebuggerUrl, "webSocketDebuggerUrl is null");

        this.id                     = id;
        this.type                   = type;
        this.title                  = title;
        this.url                    = url;
        this.description            = description;
        this.webSocketDebuggerUrl   = webSocketDebuggerUrl;
        this.devtoolsFrontendUrl    = devtoolsFrontendUrl;
        this.faviconUrl             = faviconUrl;
    }

    /**
     * <EMBED CLASS='external-html' DATA-FILE-ID=PAGE_CONN>
     * 
     * @param jo Any one of the {@link JsonObject} instqnces that were generated by querying the
     * HTTP End-Point <B STYLE='color:red;'>{@code /json/list}</B>, and selecting one of pages 
     * listed within the returned {@link JsonArray}.
     * 
     * @see ReadJSON#getString(JsonObject, String, boolean, boolean)
     */
    public PageConn(final JsonObject jo)
    {
        // Boolean #1: 'isOptional'
        //      FALSE   ==> throws if Missing
        //      TRUE    ==> Assigns Null if Missing
        // 
        // Boolean #2: 'throwOnNull'
        //      FALSE   ==> returns null if Json-Null has been passed
        //      TRUE    ==> throws if Json-Null has been passed

        this.id     = ReadJSON.getString(jo, "id",      false, true);
        this.type   = ReadJSON.getString(jo, "type",    false, true);
        this.title  = ReadJSON.getString(jo, "title",   true, false);
        this.url    = ReadJSON.getString(jo, "url",     false, true);
    
        this.description =
            ReadJSON.getString(jo, "description", true, false);
    
        this.webSocketDebuggerUrl =
            ReadJSON.getString(jo, "webSocketDebuggerUrl", false, true);
    
        this.devtoolsFrontendUrl =
            ReadJSON.getString(jo, "devtoolsFrontendUrl", true, false);
    
        this.faviconUrl =
            ReadJSON.getString(jo, "faviconUrl", true, false);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Builder Methods
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Creates a {@link WebSocketSender} connected to the {@link #webSocketDebuggerUrl} for this
     * tab.  This URL is the endpoint that speaks the Chrome DevTools Protocol (CDP).
     * 
     * @param connRec Messaging settings for this connection
     */
    public WebSocketSender createSender(final ConnRecord connRec)
        throws IOException, WebSocketException
    { return new WebSocketSender(this.webSocketDebuggerUrl, connRec); }

    /**
     * Retrieves all pages from the CDP-RDP endpoint currently open on the named port.
     * This queries {@code /json/list} and returns a Java-{@code Stream} of the instnces of this 
     * class.
     *
     * @param port  The DevTools port Chrome is listening on (default is 9222 if {@code null}).
     * @param quiet If {@code TRUE}, output is shunted.
     * @return A list of all Open-Pages, as instances of {@code PageConn}
     */
    @LinkJavaSource(handle="PCBuilder")
    public static Stream<PageConn> getAllPageConn(Integer port, final boolean quiet)
    { return PCBuilder.getAllPageConn(port, quiet); }


    // ********************************************************************************************
    // ********************************************************************************************
    // java.lang.Object Methods
    // ********************************************************************************************
    // ********************************************************************************************


    @LinkJavaSource(handle="PCHelper", name="toString")
    public String toString() { return PCHelper.toString(this); }

    @LinkJavaSource(handle="PCHelper", name="hashCode")
    public int hashCode() { return PCHelper.hashCode(this); }

    @LinkJavaSource(handle="PCHelper", name="equals")
    public boolean equals(Object o) { return PCHelper.equals(this, o); }

    @LinkJavaSource(handle="PCHelper", name="clone")
    public PageConn clone() { return PCHelper.clone(this); }

    public int compareTo(PageConn other)
    { return this.webSocketDebuggerUrl.compareTo(other.webSocketDebuggerUrl); }
}