parse-features-string.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /**
  2. * Utilities to parse comma-separated key value pairs used in browser APIs.
  3. * For example: "x=100,y=200,width=500,height=500"
  4. */
  5. import { BrowserWindowConstructorOptions } from 'electron/main';
  6. type RequiredBrowserWindowConstructorOptions = Required<BrowserWindowConstructorOptions>;
  7. type IntegerBrowserWindowOptionKeys = {
  8. [K in keyof RequiredBrowserWindowConstructorOptions]:
  9. RequiredBrowserWindowConstructorOptions[K] extends number ? K : never
  10. }[keyof RequiredBrowserWindowConstructorOptions];
  11. // This could be an array of keys, but an object allows us to add a compile-time
  12. // check validating that we haven't added an integer property to
  13. // BrowserWindowConstructorOptions that this module doesn't know about.
  14. const keysOfTypeNumberCompileTimeCheck: { [K in IntegerBrowserWindowOptionKeys] : true } = {
  15. x: true,
  16. y: true,
  17. width: true,
  18. height: true,
  19. minWidth: true,
  20. maxWidth: true,
  21. minHeight: true,
  22. maxHeight: true,
  23. opacity: true
  24. };
  25. // Note `top` / `left` are special cases from the browser which we later convert
  26. // to y / x.
  27. const keysOfTypeNumber = ['top', 'left', ...Object.keys(keysOfTypeNumberCompileTimeCheck)];
  28. /**
  29. * Note that we only allow "0" and "1" boolean conversion when the type is known
  30. * not to be an integer.
  31. *
  32. * The coercion of yes/no/1/0 represents best effort accordance with the spec:
  33. * https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean
  34. */
  35. type CoercedValue = string | number | boolean;
  36. function coerce (key: string, value: string): CoercedValue {
  37. if (keysOfTypeNumber.includes(key)) {
  38. return parseInt(value, 10);
  39. }
  40. switch (value) {
  41. case 'true':
  42. case '1':
  43. case 'yes':
  44. case undefined:
  45. return true;
  46. case 'false':
  47. case '0':
  48. case 'no':
  49. return false;
  50. default:
  51. return value;
  52. }
  53. }
  54. export function parseCommaSeparatedKeyValue (source: string) {
  55. const parsed = {} as { [key: string]: any };
  56. for (const keyValuePair of source.split(',')) {
  57. const [key, value] = keyValuePair.split('=').map(str => str.trim());
  58. if (key) { parsed[key] = coerce(key, value); }
  59. }
  60. return parsed;
  61. }
  62. export function parseWebViewWebPreferences (preferences: string) {
  63. return parseCommaSeparatedKeyValue(preferences);
  64. }
  65. const allowedWebPreferences = ['zoomFactor', 'nodeIntegration', 'javascript', 'contextIsolation', 'webviewTag'] as const;
  66. type AllowedWebPreference = (typeof allowedWebPreferences)[number];
  67. /**
  68. * Parses a feature string that has the format used in window.open().
  69. */
  70. export function parseFeatures (features: string) {
  71. const parsed = parseCommaSeparatedKeyValue(features);
  72. const webPreferences: { [K in AllowedWebPreference]?: any } = {};
  73. allowedWebPreferences.forEach((key) => {
  74. if (parsed[key] === undefined) return;
  75. webPreferences[key] = parsed[key];
  76. delete parsed[key];
  77. });
  78. if (parsed.left !== undefined) parsed.x = parsed.left;
  79. if (parsed.top !== undefined) parsed.y = parsed.top;
  80. return {
  81. options: parsed as Omit<BrowserWindowConstructorOptions, 'webPreferences'>,
  82. webPreferences
  83. };
  84. }