001package Torello.Browser;
002
003import static Torello.Java.C.BYELLOW;
004import static Torello.Java.C.RESET;
005
006import Torello.JSON.ReadJSON;
007import Torello.JavaDoc.Annotations.LinkJavaSource;
008import NeoVisionaries.WebSockets.WebSocketException;
009
010import java.util.Objects;
011import java.util.stream.Stream;
012
013import java.io.IOException;
014
015import javax.json.JsonObject;
016import javax.json.JsonArray; // javadoc needs this
017
018/**
019 * A class which represents a connection to a particular Page or Tab that has been opened within
020 * the Browser, rather than a connection to the Browser itself.
021 * 
022 * <BR /><BR />
023 * Represents a snapshot of metadata returned from Chrome’s
024 * <B STYLE='color:red;'>{@code /json/version}</B> End-Point. This includes protocol versioning, V8
025 * details, and the root <B>{@link NeoVisionaries.WebSockets.WebSocket WebSocket}</B> {@code URL}
026 * for establishing DevTools sessions.
027 *
028 * <BR /><BR />
029 * Although this class offers a public constructor, the recommended way to build an instance is via
030 * the {@code static} method: <B>{@link #getAllPageConn getAllPageConn}</B>.  This stream of
031 * instances which are returned each contain a few informational fields that were scraped from the
032 * Json-Response to the above mentioned Chrome response object.
033 * 
034 * <BR /><BR /><B CLASS=JDDescLabel2>A {@link WebSocketSender} Instance:</B>
035 * 
036 * <BR />
037 * To begin sending CDP commands, it is mandatory to obtain an instance of the
038 * <B>{@link WebSocketSender}</B> class which has been connected to a Web Browser.  This instance
039 * should be passed as the sender to any of the available API methods offered by the Domain Classes
040 * in the packages
041 * <B><CODE><A HREF='BrowserAPI/package-summary.html'>BrowserAPI</A></CODE></B>
042 * and / or the classes in
043 * <B><CODE><A HREF='JavaScriptAPI/package-summary.html'>JavaScriptAPI</A></CODE></B>.
044 * You should review the methods exported by each of the domains and classes within those packages,
045 * and note that the <B>{@link Script}</B> objects returned by them expose an
046 * <B>{@link Script#exec}</B> method, which cannot be invoked without an instance of
047 * <B>{@link WebSocketSender}</B>.
048 * 
049 * <BR /><BR />
050 * Obtaining the <B>{@link WebSocketSender}</B> instance can be achieved by invoking
051 * <B>{@link PageConn#createSender PageConn.createSender}</B>, a
052 * method offered by this class.  This method initializes a <B>{@link WebSocketSender}</B> which is
053 * connected to the associated <B>{@link #webSocketDebuggerUrl}</B>. That sender can be used to
054 * make invocations into all CDP Domains.
055 *
056 * <BR /><BR />
057 * The individual fields exported by this class are documented below, and within their respective
058 * documentation.  This class, itself, is largely a structured data holder with minimal logic.
059 * Mostly, it parses the Json which is returned by the Web-Browser endpoint
060 * <B STYLE='color:red;'>{@code /json/list}</B>
061 * 
062 * <EMBED CLASS='external-html' DATA-FILE-ID=PCBC_CHECKLIST>
063 */
064@Torello.JavaDoc.Annotations.CSSLinks(FileNames="BrowserAndPageConn.css")
065public class PageConn implements java.io.Serializable, Comparable<PageConn>, Cloneable
066{
067    /** <EMBED CLASS=external-html DATA-FILE-ID=SVUID> */
068    protected static final long serialVersionUID = 1;
069
070
071    // ********************************************************************************************
072    // ********************************************************************************************
073    // Public Fields
074    // ********************************************************************************************
075    // ********************************************************************************************
076
077
078    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_ID> */
079    public final String id;
080
081    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_TYPE> */
082    public final String type;
083
084    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_TITLE> */
085    public final String title;
086
087    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_URL> */
088    public final String url;
089
090    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_DESCRIPTION> */
091    public final String description;
092
093    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_WS_DEBUG_URL> */
094    public final String webSocketDebuggerUrl;
095
096    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_DEVTOOLS_URL> */
097    public final String devtoolsFrontendUrl;
098
099    /** <EMBED CLASS='external-html' DATA-FILE-ID=PC_FAVICON_URL> */
100    public final String faviconUrl;
101
102
103    // ********************************************************************************************
104    // ********************************************************************************************
105    // Package Private Constructors
106    // ********************************************************************************************
107    // ********************************************************************************************
108
109
110    /** Constructs an instance of this type. */
111    public PageConn(
112            final String id,
113            final String type,
114            final String title,
115            final String url,
116            final String description,
117            final String webSocketDebuggerUrl,
118            final String devtoolsFrontendUrl,
119            final String faviconUrl
120        )
121    {
122        Objects.requireNonNull(webSocketDebuggerUrl, "webSocketDebuggerUrl is null");
123
124        this.id                     = id;
125        this.type                   = type;
126        this.title                  = title;
127        this.url                    = url;
128        this.description            = description;
129        this.webSocketDebuggerUrl   = webSocketDebuggerUrl;
130        this.devtoolsFrontendUrl    = devtoolsFrontendUrl;
131        this.faviconUrl             = faviconUrl;
132    }
133
134    /**
135     * <EMBED CLASS='external-html' DATA-FILE-ID=PAGE_CONN>
136     * 
137     * @param jo Any one of the {@link JsonObject} instqnces that were generated by querying the
138     * HTTP End-Point <B STYLE='color:red;'>{@code /json/list}</B>, and selecting one of pages 
139     * listed within the returned {@link JsonArray}.
140     * 
141     * @see ReadJSON#getString(JsonObject, String, boolean, boolean)
142     */
143    public PageConn(final JsonObject jo)
144    {
145        // Boolean #1: 'isOptional'
146        //      FALSE   ==> throws if Missing
147        //      TRUE    ==> Assigns Null if Missing
148        // 
149        // Boolean #2: 'throwOnNull'
150        //      FALSE   ==> returns null if Json-Null has been passed
151        //      TRUE    ==> throws if Json-Null has been passed
152
153        this.id     = ReadJSON.getString(jo, "id",      false, true);
154        this.type   = ReadJSON.getString(jo, "type",    false, true);
155        this.title  = ReadJSON.getString(jo, "title",   true, false);
156        this.url    = ReadJSON.getString(jo, "url",     false, true);
157    
158        this.description =
159            ReadJSON.getString(jo, "description", true, false);
160    
161        this.webSocketDebuggerUrl =
162            ReadJSON.getString(jo, "webSocketDebuggerUrl", false, true);
163    
164        this.devtoolsFrontendUrl =
165            ReadJSON.getString(jo, "devtoolsFrontendUrl", true, false);
166    
167        this.faviconUrl =
168            ReadJSON.getString(jo, "faviconUrl", true, false);
169    }
170
171
172    // ********************************************************************************************
173    // ********************************************************************************************
174    // Builder Methods
175    // ********************************************************************************************
176    // ********************************************************************************************
177
178
179    /**
180     * Creates a {@link WebSocketSender} connected to the {@link #webSocketDebuggerUrl} for this
181     * tab.  This URL is the endpoint that speaks the Chrome DevTools Protocol (CDP).
182     * 
183     * @param connRec Messaging settings for this connection
184     */
185    public WebSocketSender createSender(final ConnRecord connRec)
186        throws IOException, WebSocketException
187    { return new WebSocketSender(this.webSocketDebuggerUrl, connRec); }
188
189    /**
190     * Retrieves all pages from the CDP-RDP endpoint currently open on the named port.
191     * This queries {@code /json/list} and returns a Java-{@code Stream} of the instnces of this 
192     * class.
193     *
194     * @param port  The DevTools port Chrome is listening on (default is 9222 if {@code null}).
195     * @param quiet If {@code TRUE}, output is shunted.
196     * @return A list of all Open-Pages, as instances of {@code PageConn}
197     */
198    @LinkJavaSource(handle="PCBuilder")
199    public static Stream<PageConn> getAllPageConn(Integer port, final boolean quiet)
200    { return PCBuilder.getAllPageConn(port, quiet); }
201
202
203    // ********************************************************************************************
204    // ********************************************************************************************
205    // java.lang.Object Methods
206    // ********************************************************************************************
207    // ********************************************************************************************
208
209
210    @LinkJavaSource(handle="PCHelper", name="toString")
211    public String toString() { return PCHelper.toString(this); }
212
213    @LinkJavaSource(handle="PCHelper", name="hashCode")
214    public int hashCode() { return PCHelper.hashCode(this); }
215
216    @LinkJavaSource(handle="PCHelper", name="equals")
217    public boolean equals(Object o) { return PCHelper.equals(this, o); }
218
219    @LinkJavaSource(handle="PCHelper", name="clone")
220    public PageConn clone() { return PCHelper.clone(this); }
221
222    public int compareTo(PageConn other)
223    { return this.webSocketDebuggerUrl.compareTo(other.webSocketDebuggerUrl); }
224}
225