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>This domain allows interacting with the browser to control PWAs.</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 PWA 034{ 035 // ******************************************************************************************** 036 // ******************************************************************************************** 037 // Class Header Stuff 038 // ******************************************************************************************** 039 // ******************************************************************************************** 040 041 042 // No Pubic Constructors 043 private PWA () { } 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 : PWA.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("getOsAppState", v); 083 Collections.addAll(v, new String[] 084 { "manifestId", }); 085 086 v = new Vector<String>(2); 087 parameterNames.put("install", v); 088 Collections.addAll(v, new String[] 089 { "manifestId", "installUrlOrBundleUrl", }); 090 091 v = new Vector<String>(1); 092 parameterNames.put("uninstall", v); 093 Collections.addAll(v, new String[] 094 { "manifestId", }); 095 096 v = new Vector<String>(2); 097 parameterNames.put("launch", v); 098 Collections.addAll(v, new String[] 099 { "manifestId", "url", }); 100 101 v = new Vector<String>(2); 102 parameterNames.put("launchFilesInApp", v); 103 Collections.addAll(v, new String[] 104 { "manifestId", "files", }); 105 106 v = new Vector<String>(1); 107 parameterNames.put("openCurrentPageInApp", v); 108 Collections.addAll(v, new String[] 109 { "manifestId", }); 110 111 v = new Vector<String>(3); 112 parameterNames.put("changeAppUserSettings", v); 113 Collections.addAll(v, new String[] 114 { "manifestId", "linkCapturing", "displayMode", }); 115 } 116 117 118 // ******************************************************************************************** 119 // ******************************************************************************************** 120 // Types - Static Inner Classes 121 // ******************************************************************************************** 122 // ******************************************************************************************** 123 124 /** If user prefers opening the app in browser or an app window. */ 125 public static final String[] DisplayMode = 126 { "standalone", "browser", }; 127 128 /** 129 * The following types are the replica of 130 * https://crsrc.org/c/chrome/browser/web_applications/proto/web_app_os_integration_state.proto;drc=9910d3be894c8f142c977ba1023f30a656bc13fc;l=67 131 */ 132 public static class FileHandlerAccept 133 extends BaseType 134 implements java.io.Serializable 135 { 136 /** For Object Serialization. java.io.Serializable */ 137 protected static final long serialVersionUID = 1; 138 139 public boolean[] optionals() 140 { return new boolean[] { false, false, }; } 141 142 /** 143 * New name of the mimetype according to 144 * https://www.iana.org/assignments/media-types/media-types.xhtml 145 */ 146 public final String mediaType; 147 148 /** <CODE>[No Description Provided by Google]</CODE> */ 149 public final String[] fileExtensions; 150 151 /** 152 * Constructor 153 * 154 * @param mediaType 155 * New name of the mimetype according to 156 * https://www.iana.org/assignments/media-types/media-types.xhtml 157 * 158 * @param fileExtensions - 159 */ 160 public FileHandlerAccept(String mediaType, String[] fileExtensions) 161 { 162 // Exception-Check(s) to ensure that if any parameters which are not declared as 163 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 164 165 if (mediaType == null) THROWS.throwNPE("mediaType"); 166 if (fileExtensions == null) THROWS.throwNPE("fileExtensions"); 167 168 this.mediaType = mediaType; 169 this.fileExtensions = fileExtensions; 170 } 171 172 /** 173 * JSON Object Constructor 174 * @param jo A Json-Object having data about an instance of {@code 'FileHandlerAccept'}. 175 */ 176 public FileHandlerAccept (JsonObject jo) 177 { 178 this.mediaType = ReadJSON.getString(jo, "mediaType", false, true); 179 this.fileExtensions = (jo.getJsonArray("fileExtensions") == null) 180 ? null 181 : RJArrIntoStream.strArr(jo.getJsonArray("fileExtensions"), null, 0).toArray(String[]::new); 182 183 } 184 185 186 /** Checks whether {@code 'this'} equals an input Java-{@code Object} */ 187 public boolean equals(Object other) 188 { 189 if (this == other) return true; 190 if (other == null) return false; 191 if (other.getClass() != this.getClass()) return false; 192 193 FileHandlerAccept o = (FileHandlerAccept) other; 194 195 return 196 Objects.equals(this.mediaType, o.mediaType) 197 && Arrays.deepEquals(this.fileExtensions, o.fileExtensions); 198 } 199 200 /** Generates a Hash-Code for {@code 'this'} instance */ 201 public int hashCode() 202 { 203 return 204 Objects.hashCode(this.mediaType) 205 + Arrays.deepHashCode(this.fileExtensions); 206 } 207 } 208 209 /** <CODE>[No Description Provided by Google]</CODE> */ 210 public static class FileHandler 211 extends BaseType 212 implements java.io.Serializable 213 { 214 /** For Object Serialization. java.io.Serializable */ 215 protected static final long serialVersionUID = 1; 216 217 public boolean[] optionals() 218 { return new boolean[] { false, false, false, }; } 219 220 /** <CODE>[No Description Provided by Google]</CODE> */ 221 public final String action; 222 223 /** <CODE>[No Description Provided by Google]</CODE> */ 224 public final PWA.FileHandlerAccept[] accepts; 225 226 /** <CODE>[No Description Provided by Google]</CODE> */ 227 public final String displayName; 228 229 /** 230 * Constructor 231 * 232 * @param action - 233 * 234 * @param accepts - 235 * 236 * @param displayName - 237 */ 238 public FileHandler(String action, PWA.FileHandlerAccept[] accepts, String displayName) 239 { 240 // Exception-Check(s) to ensure that if any parameters which are not declared as 241 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 242 243 if (action == null) THROWS.throwNPE("action"); 244 if (accepts == null) THROWS.throwNPE("accepts"); 245 if (displayName == null) THROWS.throwNPE("displayName"); 246 247 this.action = action; 248 this.accepts = accepts; 249 this.displayName = displayName; 250 } 251 252 /** 253 * JSON Object Constructor 254 * @param jo A Json-Object having data about an instance of {@code 'FileHandler'}. 255 */ 256 public FileHandler (JsonObject jo) 257 { 258 this.action = ReadJSON.getString(jo, "action", false, true); 259 this.accepts = (jo.getJsonArray("accepts") == null) 260 ? null 261 : RJArrIntoStream.objArr(jo.getJsonArray("accepts"), null, 0, PWA.FileHandlerAccept.class).toArray(PWA.FileHandlerAccept[]::new); 262 263 this.displayName = ReadJSON.getString(jo, "displayName", false, true); 264 } 265 266 267 /** Checks whether {@code 'this'} equals an input Java-{@code Object} */ 268 public boolean equals(Object other) 269 { 270 if (this == other) return true; 271 if (other == null) return false; 272 if (other.getClass() != this.getClass()) return false; 273 274 FileHandler o = (FileHandler) other; 275 276 return 277 Objects.equals(this.action, o.action) 278 && Arrays.deepEquals(this.accepts, o.accepts) 279 && Objects.equals(this.displayName, o.displayName); 280 } 281 282 /** Generates a Hash-Code for {@code 'this'} instance */ 283 public int hashCode() 284 { 285 return 286 Objects.hashCode(this.action) 287 + Arrays.deepHashCode(this.accepts) 288 + Objects.hashCode(this.displayName); 289 } 290 } 291 292 293 // Counter for keeping the WebSocket Request ID's distinct. 294 private static int counter = 1; 295 296 /** 297 * Returns the following OS state for the given manifest id. 298 * 299 * @param manifestId 300 * The id from the webapp's manifest file, commonly it's the url of the 301 * site installing the webapp. See 302 * https://web.dev/learn/pwa/web-app-manifest. 303 * 304 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 305 * {@link Ret2}></CODE> 306 * 307 * <BR /><BR />This {@link Script} may be <B STYLE='color:red'>executed</B> (using 308 * {@link Script#exec()}), and a {@link Promise} returned. 309 * 310 * <BR /><BR />When the {@code Promise} is <B STYLE='color: red'>awaited</B> 311 * (using {@link Promise#await()}), the {@code Ret2} will subsequently 312 * be returned from that call. 313 * 314 * <BR /><BR />The <B STYLE='color: red'>returned</B> values are encapsulated 315 * in an instance of <B>{@link Ret2}</B> 316 * 317 * <BR /><BR /><UL CLASS=JDUL> 318 * <LI><CODE><B>Ret2.a:</B> Integer (<B>badgeCount</B>)</CODE> 319 * <BR />- 320 * <BR /><BR /></LI> 321 * <LI><CODE><B>Ret2.b:</B> {@link PWA.FileHandler}[] (<B>fileHandlers</B>)</CODE> 322 * <BR />- 323 * </LI> 324 * </UL> 325 */ 326 public static Script<String, JsonObject, Ret2<Integer, PWA.FileHandler[]>> getOsAppState 327 (String manifestId) 328 { 329 // Exception-Check(s) to ensure that if any parameters which are not declared as 330 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 331 332 if (manifestId == null) THROWS.throwNPE("manifestId"); 333 334 final int webSocketID = 52000000 + counter++; 335 final boolean[] optionals = { false, }; 336 337 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 338 String requestJSON = WriteJSON.get( 339 parameterTypes.get("getOsAppState"), 340 parameterNames.get("getOsAppState"), 341 optionals, webSocketID, 342 "PWA.getOsAppState", 343 manifestId 344 ); 345 346 // 'JSON Binding' ... Converts Browser Response-JSON into Java-Type 'Ret2' 347 Function<JsonObject, Ret2<Integer, PWA.FileHandler[]>> 348 responseProcessor = (JsonObject jo) -> new Ret2<>( 349 ReadBoxedJSON.getInteger(jo, "badgeCount", true), 350 (jo.getJsonArray("fileHandlers") == null) 351 ? null 352 : RJArrIntoStream.objArr(jo.getJsonArray("fileHandlers"), null, 0, PWA.FileHandler.class).toArray(PWA.FileHandler[]::new) 353 ); 354 355 return new Script<>(webSocketID, requestJSON, responseProcessor); 356 } 357 358 /** 359 * Installs the given manifest identity, optionally using the given installUrlOrBundleUrl 360 * 361 * IWA-specific install description: 362 * manifestId corresponds to isolated-app:// + web_package::SignedWebBundleId 363 * 364 * File installation mode: 365 * The installUrlOrBundleUrl can be either file:// or http(s):// pointing 366 * to a signed web bundle (.swbn). In this case SignedWebBundleId must correspond to 367 * The .swbn file's signing key. 368 * 369 * Dev proxy installation mode: 370 * installUrlOrBundleUrl must be http(s):// that serves dev mode IWA. 371 * web_package::SignedWebBundleId must be of type dev proxy. 372 * 373 * The advantage of dev proxy mode is that all changes to IWA 374 * automatically will be reflected in the running app without 375 * reinstallation. 376 * 377 * To generate bundle id for proxy mode: 378 * 1. Generate 32 random bytes. 379 * 2. Add a specific suffix 0x00 at the end. 380 * 3. Encode the entire sequence using Base32 without padding. 381 * 382 * If Chrome is not in IWA dev 383 * mode, the installation will fail, regardless of the state of the allowlist. 384 * 385 * @param manifestId - 386 * 387 * @param installUrlOrBundleUrl 388 * The location of the app or bundle overriding the one derived from the 389 * manifestId. 390 * <BR /><B CLASS=Opt>OPTIONAL</B> 391 * 392 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 393 * {@link Ret0}></CODE> 394 * 395 * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the 396 * browser receives the invocation-request. 397 * 398 * <BR /><BR />This Browser-Function <I>does not have</I> a return-value. You may choose to 399 * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0} 400 * {@code >} to ensure the Browser Function has run to completion. 401 */ 402 public static Script<String, JsonObject, Ret0> install 403 (String manifestId, String installUrlOrBundleUrl) 404 { 405 // Exception-Check(s) to ensure that if any parameters which are not declared as 406 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 407 408 if (manifestId == null) THROWS.throwNPE("manifestId"); 409 410 final int webSocketID = 52001000 + counter++; 411 final boolean[] optionals = { false, true, }; 412 413 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 414 String requestJSON = WriteJSON.get( 415 parameterTypes.get("install"), 416 parameterNames.get("install"), 417 optionals, webSocketID, 418 "PWA.install", 419 manifestId, installUrlOrBundleUrl 420 ); 421 422 // This Remote Command does not have a Return-Value. 423 return new Script<> 424 (webSocketID, requestJSON, VOID_RETURN.NoReturnValues); 425 } 426 427 /** 428 * Uninstalls the given manifest_id and closes any opened app windows. 429 * 430 * @param manifestId - 431 * 432 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 433 * {@link Ret0}></CODE> 434 * 435 * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the 436 * browser receives the invocation-request. 437 * 438 * <BR /><BR />This Browser-Function <I>does not have</I> a return-value. You may choose to 439 * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0} 440 * {@code >} to ensure the Browser Function has run to completion. 441 */ 442 public static Script<String, JsonObject, Ret0> uninstall(String manifestId) 443 { 444 // Exception-Check(s) to ensure that if any parameters which are not declared as 445 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 446 447 if (manifestId == null) THROWS.throwNPE("manifestId"); 448 449 final int webSocketID = 52002000 + counter++; 450 final boolean[] optionals = { false, }; 451 452 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 453 String requestJSON = WriteJSON.get( 454 parameterTypes.get("uninstall"), 455 parameterNames.get("uninstall"), 456 optionals, webSocketID, 457 "PWA.uninstall", 458 manifestId 459 ); 460 461 // This Remote Command does not have a Return-Value. 462 return new Script<> 463 (webSocketID, requestJSON, VOID_RETURN.NoReturnValues); 464 } 465 466 /** 467 * Launches the installed web app, or an url in the same web app instead of the 468 * default start url if it is provided. Returns a page Target.TargetID which 469 * can be used to attach to via Target.attachToTarget or similar APIs. 470 * 471 * @param manifestId - 472 * 473 * @param url - 474 * <BR /><B CLASS=Opt>OPTIONAL</B> 475 * 476 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 477 * String></CODE> 478 * 479 * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using 480 * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE><JsonObject, 481 * String></CODE> will be returned. 482 * 483 * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>, 484 * using {@link Promise#await()}, <I>and the returned result of this Browser Function may 485 * may be retrieved.</I> 486 * 487 * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B> 488 * <BR /><BR /><UL CLASS=JDUL> 489 * <LI><CODE>String (<B>targetId</B></CODE>) 490 * <BR />ID of the tab target created as a result. 491 * </LI> 492 * </UL> */ 493 public static Script<String, JsonObject, String> launch(String manifestId, String url) 494 { 495 // Exception-Check(s) to ensure that if any parameters which are not declared as 496 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 497 498 if (manifestId == null) THROWS.throwNPE("manifestId"); 499 500 final int webSocketID = 52003000 + counter++; 501 final boolean[] optionals = { false, true, }; 502 503 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 504 String requestJSON = WriteJSON.get( 505 parameterTypes.get("launch"), 506 parameterNames.get("launch"), 507 optionals, webSocketID, 508 "PWA.launch", 509 manifestId, url 510 ); 511 512 // 'JSON Binding' ... Converts Browser Response-JSON to 'String' 513 Function<JsonObject, String> responseProcessor = (JsonObject jo) -> 514 ReadJSON.getString(jo, "targetId", false, true); 515 516 return new Script<>(webSocketID, requestJSON, responseProcessor); 517 } 518 519 /** 520 * Opens one or more local files from an installed web app identified by its 521 * manifestId. The web app needs to have file handlers registered to process 522 * the files. The API returns one or more page Target.TargetIDs which can be 523 * used to attach to via Target.attachToTarget or similar APIs. 524 * If some files in the parameters cannot be handled by the web app, they will 525 * be ignored. If none of the files can be handled, this API returns an error. 526 * If no files are provided as the parameter, this API also returns an error. 527 * 528 * According to the definition of the file handlers in the manifest file, one 529 * Target.TargetID may represent a page handling one or more files. The order 530 * of the returned Target.TargetIDs is not guaranteed. 531 * 532 * TODO(crbug.com/339454034): Check the existences of the input files. 533 * 534 * @param manifestId - 535 * 536 * @param files - 537 * 538 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 539 * String[]></CODE> 540 * 541 * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using 542 * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE><JsonObject, 543 * String[]></CODE> will be returned. 544 * 545 * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>, 546 * using {@link Promise#await()}, <I>and the returned result of this Browser Function may 547 * may be retrieved.</I> 548 * 549 * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B> 550 * <BR /><BR /><UL CLASS=JDUL> 551 * <LI><CODE>String[] (<B>targetIds</B></CODE>) 552 * <BR />IDs of the tab targets created as the result. 553 * </LI> 554 * </UL> */ 555 public static Script<String, JsonObject, String[]> launchFilesInApp 556 (String manifestId, String[] files) 557 { 558 // Exception-Check(s) to ensure that if any parameters which are not declared as 559 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 560 561 if (manifestId == null) THROWS.throwNPE("manifestId"); 562 if (files == null) THROWS.throwNPE("files"); 563 564 final int webSocketID = 52004000 + counter++; 565 final boolean[] optionals = { false, false, }; 566 567 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 568 String requestJSON = WriteJSON.get( 569 parameterTypes.get("launchFilesInApp"), 570 parameterNames.get("launchFilesInApp"), 571 optionals, webSocketID, 572 "PWA.launchFilesInApp", 573 manifestId, files 574 ); 575 576 // 'JSON Binding' ... Converts Browser Response-JSON to 'String[]' 577 Function<JsonObject, String[]> responseProcessor = (JsonObject jo) -> 578 (jo.getJsonArray("targetIds") == null) 579 ? null 580 : RJArrIntoStream.strArr(jo.getJsonArray("targetIds"), null, 0).toArray(String[]::new); 581 582 return new Script<>(webSocketID, requestJSON, responseProcessor); 583 } 584 585 /** 586 * Opens the current page in its web app identified by the manifest id, needs 587 * to be called on a page target. This function returns immediately without 588 * waiting for the app to finish loading. 589 * 590 * @param manifestId - 591 * 592 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 593 * {@link Ret0}></CODE> 594 * 595 * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the 596 * browser receives the invocation-request. 597 * 598 * <BR /><BR />This Browser-Function <I>does not have</I> a return-value. You may choose to 599 * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0} 600 * {@code >} to ensure the Browser Function has run to completion. 601 */ 602 public static Script<String, JsonObject, Ret0> openCurrentPageInApp(String manifestId) 603 { 604 // Exception-Check(s) to ensure that if any parameters which are not declared as 605 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 606 607 if (manifestId == null) THROWS.throwNPE("manifestId"); 608 609 final int webSocketID = 52005000 + counter++; 610 final boolean[] optionals = { false, }; 611 612 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 613 String requestJSON = WriteJSON.get( 614 parameterTypes.get("openCurrentPageInApp"), 615 parameterNames.get("openCurrentPageInApp"), 616 optionals, webSocketID, 617 "PWA.openCurrentPageInApp", 618 manifestId 619 ); 620 621 // This Remote Command does not have a Return-Value. 622 return new Script<> 623 (webSocketID, requestJSON, VOID_RETURN.NoReturnValues); 624 } 625 626 /** 627 * Changes user settings of the web app identified by its manifestId. If the 628 * app was not installed, this command returns an error. Unset parameters will 629 * be ignored; unrecognized values will cause an error. 630 * 631 * Unlike the ones defined in the manifest files of the web apps, these 632 * settings are provided by the browser and controlled by the users, they 633 * impact the way the browser handling the web apps. 634 * 635 * See the comment of each parameter. 636 * 637 * @param manifestId - 638 * 639 * @param linkCapturing 640 * If user allows the links clicked on by the user in the app's scope, or 641 * extended scope if the manifest has scope extensions and the flags 642 * {@code DesktopPWAsLinkCapturingWithScopeExtensions} and 643 * {@code WebAppEnableScopeExtensions} are enabled. 644 * 645 * Note, the API does not support resetting the linkCapturing to the 646 * initial value, uninstalling and installing the web app again will reset 647 * it. 648 * 649 * TODO(crbug.com/339453269): Setting this value on ChromeOS is not 650 * supported yet. 651 * <BR /><B CLASS=Opt>OPTIONAL</B> 652 * 653 * @param displayMode - 654 * <BR /><B CLASS=Opt>OPTIONAL</B> 655 * 656 * @return An instance of <CODE>{@link Script}<String, {@link JsonObject}, 657 * {@link Ret0}></CODE> 658 * 659 * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the 660 * browser receives the invocation-request. 661 * 662 * <BR /><BR />This Browser-Function <I>does not have</I> a return-value. You may choose to 663 * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0} 664 * {@code >} to ensure the Browser Function has run to completion. 665 */ 666 public static Script<String, JsonObject, Ret0> changeAppUserSettings 667 (String manifestId, Boolean linkCapturing, String displayMode) 668 { 669 // Exception-Check(s) to ensure that if any parameters which are not declared as 670 // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw. 671 672 if (manifestId == null) THROWS.throwNPE("manifestId"); 673 674 // Exception-Check(s) to ensure that if any parameters which must adhere to a 675 // provided List of Enumerated Values, fails, then IllegalArgumentException shall throw. 676 677 THROWS.checkIAE("displayMode", displayMode, "PWA.DisplayMode", PWA.DisplayMode); 678 679 final int webSocketID = 52006000 + counter++; 680 final boolean[] optionals = { false, true, true, }; 681 682 // Convert Method Parameters into JSON. Build the JSON Request-Object (as a String) 683 String requestJSON = WriteJSON.get( 684 parameterTypes.get("changeAppUserSettings"), 685 parameterNames.get("changeAppUserSettings"), 686 optionals, webSocketID, 687 "PWA.changeAppUserSettings", 688 manifestId, linkCapturing, displayMode 689 ); 690 691 // This Remote Command does not have a Return-Value. 692 return new Script<> 693 (webSocketID, requestJSON, VOID_RETURN.NoReturnValues); 694 } 695 696}