001package Torello.Browser.BrowserAPI;
002
003import java.util.*;
004import javax.json.*;
005import javax.json.stream.*;
006import java.io.*;
007
008import java.lang.reflect.Method;
009import java.lang.reflect.Parameter;
010import java.util.function.Function;
011
012import Torello.Browser.BrowserEvent;
013import Torello.Browser.JavaScriptAPI.*;
014import Torello.Browser.helper.*;
015
016import Torello.Java.Additional.*;
017import Torello.Java.JSON.*;
018
019import static Torello.Java.JSON.JFlag.*;
020
021import Torello.Java.StrCmpr;
022import Torello.JavaDoc.StaticFunctional;
023import Torello.JavaDoc.JDHeaderBackgroundImg;
024import Torello.JavaDoc.Excuse;
025
026/**
027 * <SPAN CLASS=COPIEDJDK><B>Defines commands and events for browser extensions.</B></SPAN>
028 * 
029 * <EMBED CLASS='external-html' DATA-FILE-ID=CODE_GEN_NOTE>
030 */
031@StaticFunctional(Excused={"counter"}, Excuses={Excuse.CONFIGURATION})
032@JDHeaderBackgroundImg(EmbedTagFileID="WOOD_PLANK_NOTE")
033public class Extensions
034{
035    // ********************************************************************************************
036    // ********************************************************************************************
037    // Class Header Stuff
038    // ********************************************************************************************
039    // ********************************************************************************************
040
041
042    // No Pubic Constructors
043    private Extensions () { }
044
045    // These two Vector's are used by all the "Methods" exported by this class.  java.lang.reflect
046    // is used to generate the JSON String's.  It saves thousands of lines of Auto-Generated Code.
047    private static final Map<String, Vector<String>>    parameterNames = new HashMap<>();
048    private static final Map<String, Vector<Class<?>>>  parameterTypes = new HashMap<>();
049
050    // Some Methods do not take any parameters - for instance all the "enable()" and "disable()"
051    // I simply could not get ride of RAW-TYPES and UNCHECKED warnings... so there are now,
052    // offically, two empty-vectors.  One for String's, and the other for Classes.
053
054    private static final Vector<String>     EMPTY_VEC_STR = new Vector<>();
055    private static final Vector<Class<?>>   EMPTY_VEC_CLASS = new Vector<>();
056
057    static
058    {
059        for (Method m : Extensions.class.getMethods())
060        {
061            // This doesn't work!  The parameter names are all "arg0" ... "argN"
062            // It works for java.lang.reflect.Field, BUT NOT java.lang.reflect.Parameter!
063            //
064            // Vector<String> parameterNamesList = new Vector<>(); -- NOPE!
065
066            Vector<Class<?>> parameterTypesList = new Vector<>();
067        
068            for (Parameter p : m.getParameters()) parameterTypesList.add(p.getType());
069
070            parameterTypes.put(
071                m.getName(),
072                (parameterTypesList.size() > 0) ? parameterTypesList : EMPTY_VEC_CLASS
073            );
074        }
075    }
076
077    static
078    {
079        Vector<String> v = null;
080
081        v = new Vector<String>(1);
082        parameterNames.put("loadUnpacked", v);
083        Collections.addAll(v, new String[]
084        { "path", });
085
086        v = new Vector<String>(1);
087        parameterNames.put("uninstall", v);
088        Collections.addAll(v, new String[]
089        { "id", });
090
091        v = new Vector<String>(3);
092        parameterNames.put("getStorageItems", v);
093        Collections.addAll(v, new String[]
094        { "id", "storageArea", "keys", });
095
096        v = new Vector<String>(3);
097        parameterNames.put("removeStorageItems", v);
098        Collections.addAll(v, new String[]
099        { "id", "storageArea", "keys", });
100
101        v = new Vector<String>(2);
102        parameterNames.put("clearStorageItems", v);
103        Collections.addAll(v, new String[]
104        { "id", "storageArea", });
105
106        v = new Vector<String>(3);
107        parameterNames.put("setStorageItems", v);
108        Collections.addAll(v, new String[]
109        { "id", "storageArea", "values", });
110    }
111
112
113    // ********************************************************************************************
114    // ********************************************************************************************
115    // Types - Static Inner Classes
116    // ********************************************************************************************
117    // ********************************************************************************************
118
119    /** Storage areas. */
120    public static final String[] StorageArea =
121    { "session", "local", "sync", "managed", };
122    
123    
124    // Counter for keeping the WebSocket Request ID's distinct.
125    private static int counter = 1;
126    
127    /**
128     * Installs an unpacked extension from the filesystem similar to
129     * --load-extension CLI flags. Returns extension ID once the extension
130     * has been installed. Available if the client is connected using the
131     * --remote-debugging-pipe flag and the --enable-unsafe-extension-debugging
132     * flag is set.
133     * 
134     * @param path Absolute file path.
135     * 
136     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
137     * String&gt;</CODE>
138     * 
139     * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using
140     * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE>&lt;JsonObject,
141     * String&gt;</CODE> will be returned.
142     *
143     * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>,
144     * using {@link Promise#await()}, <I>and the returned result of this Browser Function may
145      * may be retrieved.</I>
146     *
147     * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B>
148     * <BR /><BR /><UL CLASS=JDUL>
149     * <LI><CODE>String (<B>id</B></CODE>)
150     *     <BR />Extension id.
151     * </LI>
152     * </UL> */
153    public static Script<String, JsonObject, String> loadUnpacked(String path)
154    {
155        // Exception-Check(s) to ensure that if any parameters which are not declared as
156        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
157        
158        if (path == null) THROWS.throwNPE("path");
159        
160        final int       webSocketID = 10000000 + counter++;
161        final boolean[] optionals   = { false, };
162        
163        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
164        String requestJSON = WriteJSON.get(
165            parameterTypes.get("loadUnpacked"),
166            parameterNames.get("loadUnpacked"),
167            optionals, webSocketID,
168            "Extensions.loadUnpacked",
169            path
170        );
171        
172        // 'JSON Binding' ... Converts Browser Response-JSON to 'String'
173        Function<JsonObject, String> responseProcessor = (JsonObject jo) ->
174            ReadJSON.getString(jo, "id", false, true);
175        
176        return new Script<>(webSocketID, requestJSON, responseProcessor);
177    }
178    
179    /**
180     * Uninstalls an unpacked extension (others not supported) from the profile.
181     * Available if the client is connected using the --remote-debugging-pipe flag
182     * and the --enable-unsafe-extension-debugging.
183     * 
184     * @param id Extension id.
185     * 
186     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
187     * {@link Ret0}&gt;</CODE>
188     *
189     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
190     * browser receives the invocation-request.
191     *
192     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
193     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
194     * {@code >} to ensure the Browser Function has run to completion.
195     */
196    public static Script<String, JsonObject, Ret0> uninstall(String id)
197    {
198        // Exception-Check(s) to ensure that if any parameters which are not declared as
199        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
200        
201        if (id == null) THROWS.throwNPE("id");
202        
203        final int       webSocketID = 10001000 + counter++;
204        final boolean[] optionals   = { false, };
205        
206        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
207        String requestJSON = WriteJSON.get(
208            parameterTypes.get("uninstall"),
209            parameterNames.get("uninstall"),
210            optionals, webSocketID,
211            "Extensions.uninstall",
212            id
213        );
214        
215        // This Remote Command does not have a Return-Value.
216        return new Script<>
217            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
218    }
219    
220    /**
221     * Gets data from extension storage in the given {@code storageArea}. If {@code keys} is
222     * specified, these are used to filter the result.
223     * 
224     * @param id ID of extension.
225     * 
226     * @param storageArea StorageArea to retrieve data from.
227     * 
228     * @param keys Keys to retrieve.
229     * <BR /><B CLASS=Opt>OPTIONAL</B>
230     * 
231     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
232     * JsonObject&gt;</CODE>
233     * 
234     * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using
235     * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE>&lt;JsonObject,
236     * JsonObject&gt;</CODE> will be returned.
237     *
238     * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>,
239     * using {@link Promise#await()}, <I>and the returned result of this Browser Function may
240      * may be retrieved.</I>
241     *
242     * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B>
243     * <BR /><BR /><UL CLASS=JDUL>
244     * <LI><CODE>JsonObject (<B>data</B></CODE>)
245     *     <BR />-
246     * </LI>
247     * </UL> */
248    public static Script<String, JsonObject, JsonObject> getStorageItems
249        (String id, String storageArea, String[] keys)
250    {
251        // Exception-Check(s) to ensure that if any parameters which are not declared as
252        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
253        
254        if (id == null)          THROWS.throwNPE("id");
255        if (storageArea == null) THROWS.throwNPE("storageArea");
256        
257        // Exception-Check(s) to ensure that if any parameters which must adhere to a
258        // provided List of Enumerated Values, fails, then IllegalArgumentException shall throw.
259        
260        THROWS.checkIAE("storageArea", storageArea, "Extensions.StorageArea", Extensions.StorageArea);
261        
262        final int       webSocketID = 10002000 + counter++;
263        final boolean[] optionals   = { false, false, true, };
264        
265        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
266        String requestJSON = WriteJSON.get(
267            parameterTypes.get("getStorageItems"),
268            parameterNames.get("getStorageItems"),
269            optionals, webSocketID,
270            "Extensions.getStorageItems",
271            id, storageArea, keys
272        );
273        
274        // 'JSON Binding' ... Converts Browser Response-JSON to 'JsonObject'
275        Function<JsonObject, JsonObject> responseProcessor = (JsonObject jo) ->
276            jo.getJsonObject("data");
277        
278        return new Script<>(webSocketID, requestJSON, responseProcessor);
279    }
280    
281    /**
282     * Removes {@code keys} from extension storage in the given {@code storageArea}.
283     * 
284     * @param id ID of extension.
285     * 
286     * @param storageArea StorageArea to remove data from.
287     * 
288     * @param keys Keys to remove.
289     * 
290     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
291     * {@link Ret0}&gt;</CODE>
292     *
293     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
294     * browser receives the invocation-request.
295     *
296     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
297     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
298     * {@code >} to ensure the Browser Function has run to completion.
299     */
300    public static Script<String, JsonObject, Ret0> removeStorageItems
301        (String id, String storageArea, String[] keys)
302    {
303        // Exception-Check(s) to ensure that if any parameters which are not declared as
304        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
305        
306        if (id == null)          THROWS.throwNPE("id");
307        if (storageArea == null) THROWS.throwNPE("storageArea");
308        if (keys == null)        THROWS.throwNPE("keys");
309        
310        // Exception-Check(s) to ensure that if any parameters which must adhere to a
311        // provided List of Enumerated Values, fails, then IllegalArgumentException shall throw.
312        
313        THROWS.checkIAE("storageArea", storageArea, "Extensions.StorageArea", Extensions.StorageArea);
314        
315        final int       webSocketID = 10003000 + counter++;
316        final boolean[] optionals   = { false, false, false, };
317        
318        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
319        String requestJSON = WriteJSON.get(
320            parameterTypes.get("removeStorageItems"),
321            parameterNames.get("removeStorageItems"),
322            optionals, webSocketID,
323            "Extensions.removeStorageItems",
324            id, storageArea, keys
325        );
326        
327        // This Remote Command does not have a Return-Value.
328        return new Script<>
329            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
330    }
331    
332    /**
333     * Clears extension storage in the given {@code storageArea}.
334     * 
335     * @param id ID of extension.
336     * 
337     * @param storageArea StorageArea to remove data from.
338     * 
339     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
340     * {@link Ret0}&gt;</CODE>
341     *
342     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
343     * browser receives the invocation-request.
344     *
345     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
346     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
347     * {@code >} to ensure the Browser Function has run to completion.
348     */
349    public static Script<String, JsonObject, Ret0> clearStorageItems
350        (String id, String storageArea)
351    {
352        // Exception-Check(s) to ensure that if any parameters which are not declared as
353        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
354        
355        if (id == null)          THROWS.throwNPE("id");
356        if (storageArea == null) THROWS.throwNPE("storageArea");
357        
358        // Exception-Check(s) to ensure that if any parameters which must adhere to a
359        // provided List of Enumerated Values, fails, then IllegalArgumentException shall throw.
360        
361        THROWS.checkIAE("storageArea", storageArea, "Extensions.StorageArea", Extensions.StorageArea);
362        
363        final int       webSocketID = 10004000 + counter++;
364        final boolean[] optionals   = { false, false, };
365        
366        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
367        String requestJSON = WriteJSON.get(
368            parameterTypes.get("clearStorageItems"),
369            parameterNames.get("clearStorageItems"),
370            optionals, webSocketID,
371            "Extensions.clearStorageItems",
372            id, storageArea
373        );
374        
375        // This Remote Command does not have a Return-Value.
376        return new Script<>
377            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
378    }
379    
380    /**
381     * Sets {@code values} in extension storage in the given {@code storageArea}. The provided {@code values}
382     * will be merged with existing values in the storage area.
383     * 
384     * @param id ID of extension.
385     * 
386     * @param storageArea StorageArea to set data in.
387     * 
388     * @param values Values to set.
389     * 
390     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
391     * {@link Ret0}&gt;</CODE>
392     *
393     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
394     * browser receives the invocation-request.
395     *
396     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
397     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
398     * {@code >} to ensure the Browser Function has run to completion.
399     */
400    public static Script<String, JsonObject, Ret0> setStorageItems
401        (String id, String storageArea, JsonObject values)
402    {
403        // Exception-Check(s) to ensure that if any parameters which are not declared as
404        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
405        
406        if (id == null)          THROWS.throwNPE("id");
407        if (storageArea == null) THROWS.throwNPE("storageArea");
408        if (values == null)      THROWS.throwNPE("values");
409        
410        // Exception-Check(s) to ensure that if any parameters which must adhere to a
411        // provided List of Enumerated Values, fails, then IllegalArgumentException shall throw.
412        
413        THROWS.checkIAE("storageArea", storageArea, "Extensions.StorageArea", Extensions.StorageArea);
414        
415        final int       webSocketID = 10005000 + counter++;
416        final boolean[] optionals   = { false, false, false, };
417        
418        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
419        String requestJSON = WriteJSON.get(
420            parameterTypes.get("setStorageItems"),
421            parameterNames.get("setStorageItems"),
422            optionals, webSocketID,
423            "Extensions.setStorageItems",
424            id, storageArea, values
425        );
426        
427        // This Remote Command does not have a Return-Value.
428        return new Script<>
429            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
430    }
431    
432}