tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

options.ts (6922B)


      1 import { unreachable } from '../../util/util.js';
      2 
      3 let windowURL: URL | undefined = undefined;
      4 function getWindowURL() {
      5  if (windowURL === undefined) {
      6    windowURL = new URL(window.location.toString());
      7  }
      8  return windowURL;
      9 }
     10 
     11 /** Parse a runner option that is always boolean-typed. False if missing or '0'. */
     12 export function optionEnabled(
     13  opt: string,
     14  searchParams: URLSearchParams = getWindowURL().searchParams
     15 ): boolean {
     16  const val = searchParams.get(opt);
     17  return val !== null && val !== '0';
     18 }
     19 
     20 /** Parse a runner option that is string-typed. If the option is missing, returns `null`. */
     21 export function optionString(
     22  opt: string,
     23  searchParams: URLSearchParams = getWindowURL().searchParams
     24 ): string | null {
     25  return searchParams.get(opt);
     26 }
     27 
     28 /** Runtime modes for running tests in different types of workers. */
     29 export type WorkerMode = 'dedicated' | 'service' | 'shared';
     30 /** Parse a runner option for different worker modes (as in `?worker=shared`). Null if no worker. */
     31 export function optionWorkerMode(
     32  opt: string,
     33  searchParams: URLSearchParams = getWindowURL().searchParams
     34 ): WorkerMode | null {
     35  const value = searchParams.get(opt);
     36  if (value === null || value === '0') {
     37    return null;
     38  } else if (value === 'service') {
     39    return 'service';
     40  } else if (value === 'shared') {
     41    return 'shared';
     42  } else if (value === '' || value === '1' || value === 'dedicated') {
     43    return 'dedicated';
     44  }
     45  unreachable('invalid worker= option value');
     46 }
     47 
     48 /**
     49 * The possible options for the tests.
     50 */
     51 export interface CTSOptions {
     52  worker: WorkerMode | null;
     53  debug: boolean;
     54  compatibility: boolean;
     55  forceFallbackAdapter: boolean;
     56  enforceDefaultLimits: boolean;
     57  blockAllFeatures: boolean;
     58  unrollConstEvalLoops: boolean;
     59  powerPreference: GPUPowerPreference | null;
     60  subcasesBetweenAttemptingGC: string;
     61  casesBetweenReplacingDevice: string;
     62  logToWebSocket: boolean;
     63 }
     64 
     65 export const kDefaultCTSOptions: Readonly<CTSOptions> = {
     66  worker: null,
     67  debug: false,
     68  compatibility: false,
     69  forceFallbackAdapter: false,
     70  enforceDefaultLimits: false,
     71  blockAllFeatures: false,
     72  unrollConstEvalLoops: false,
     73  powerPreference: null,
     74  subcasesBetweenAttemptingGC: '5000',
     75  casesBetweenReplacingDevice: 'Infinity',
     76  logToWebSocket: false,
     77 };
     78 
     79 /**
     80 * Extra per option info.
     81 */
     82 export interface OptionInfo {
     83  description: string;
     84  parser?: (key: string, searchParams?: URLSearchParams) => boolean | string | null;
     85  selectValueDescriptions?: { value: string | null; description: string }[];
     86 }
     87 
     88 /**
     89 * Type for info for every option. This definition means adding an option
     90 * will generate a compile time error if no extra info is provided.
     91 */
     92 export type OptionsInfos<Type> = Record<keyof Type, OptionInfo>;
     93 
     94 /**
     95 * Options to the CTS.
     96 */
     97 export const kCTSOptionsInfo: OptionsInfos<CTSOptions> = {
     98  worker: {
     99    description: 'run in a worker',
    100    parser: optionWorkerMode,
    101    selectValueDescriptions: [
    102      { value: null, description: 'no worker' },
    103      { value: 'dedicated', description: 'dedicated worker' },
    104      { value: 'shared', description: 'shared worker' },
    105      { value: 'service', description: 'service worker' },
    106    ],
    107  },
    108  debug: { description: 'show more info' },
    109  compatibility: { description: 'request adapters with featureLevel: "compatibility"' },
    110  forceFallbackAdapter: { description: 'pass forceFallbackAdapter: true to requestAdapter' },
    111  enforceDefaultLimits: {
    112    description: `force the adapter limits to the default limits.
    113 Note: May fail on tests for low-power/high-performance`,
    114  },
    115  blockAllFeatures: {
    116    description: `block all features on adapter - except 'core-features-and-limits'.
    117 Note: The spec requires bc or etc2+astc which means tests checking that one or other must exist will fail.
    118 `,
    119  },
    120  unrollConstEvalLoops: { description: 'unroll const eval loops in WGSL' },
    121  powerPreference: {
    122    description: 'set default powerPreference for some tests',
    123    parser: optionString,
    124    selectValueDescriptions: [
    125      { value: null, description: 'default' },
    126      { value: 'low-power', description: 'low-power' },
    127      { value: 'high-performance', description: 'high-performance' },
    128    ],
    129  },
    130  subcasesBetweenAttemptingGC: {
    131    description:
    132      'After this many subcases, run attemptGarbageCollection(). (For custom values, edit the URL.)',
    133    parser: optionString,
    134    selectValueDescriptions: [
    135      { value: null, description: 'default' },
    136      { value: 'Infinity', description: 'Infinity' },
    137      { value: '5000', description: '5000' },
    138      { value: '50', description: '50' },
    139      { value: '1', description: '1' },
    140    ],
    141  },
    142  casesBetweenReplacingDevice: {
    143    description:
    144      'After this many cases use a device, destroy and replace it to free GPU resources. (For custom values, edit the URL.)',
    145    parser: optionString,
    146    selectValueDescriptions: [
    147      { value: null, description: 'default' },
    148      { value: 'Infinity', description: 'Infinity' },
    149      { value: '5000', description: '5000' },
    150      { value: '50', description: '50' },
    151      { value: '1', description: '1' },
    152    ],
    153  },
    154  logToWebSocket: { description: 'send some logs to ws://localhost:59497/' },
    155 };
    156 
    157 /**
    158 * Converts camel case to snake case.
    159 * Examples:
    160 *    fooBar -> foo_bar
    161 *    parseHTMLFile -> parse_html_file
    162 */
    163 export function camelCaseToSnakeCase(id: string) {
    164  return id
    165    .replace(/(.)([A-Z][a-z]+)/g, '$1_$2')
    166    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
    167    .toLowerCase();
    168 }
    169 
    170 /**
    171 * Creates a Options from search parameters.
    172 */
    173 function getOptionsInfoFromSearchString<Type extends CTSOptions>(
    174  optionsInfos: OptionsInfos<Type>,
    175  searchString: string
    176 ): Type {
    177  const searchParams = new URLSearchParams(searchString);
    178  const optionValues: Record<string, boolean | string | null> = {};
    179  for (const [optionName, info] of Object.entries(optionsInfos)) {
    180    const parser = info.parser || optionEnabled;
    181    optionValues[optionName] = parser(camelCaseToSnakeCase(optionName), searchParams);
    182  }
    183  return optionValues as unknown as Type;
    184 }
    185 
    186 /**
    187 * Given a test query string in the form of `suite:foo,bar,moo&opt1=val1&opt2=val2
    188 * returns the query and the options.
    189 */
    190 export function parseSearchParamLikeWithOptions<Type extends CTSOptions>(
    191  optionsInfos: OptionsInfos<Type>,
    192  query: string
    193 ): {
    194  queries: string[];
    195  options: Type;
    196 } {
    197  const searchString = query.includes('q=') || query.startsWith('?') ? query : `q=${query}`;
    198  const queries = new URLSearchParams(searchString).getAll('q');
    199  const options = getOptionsInfoFromSearchString(optionsInfos, searchString);
    200  return { queries, options };
    201 }
    202 
    203 /**
    204 * Given a test query string in the form of `suite:foo,bar,moo&opt1=val1&opt2=val2
    205 * returns the query and the common options.
    206 */
    207 export function parseSearchParamLikeWithCTSOptions(query: string) {
    208  return parseSearchParamLikeWithOptions(kCTSOptionsInfo, query);
    209 }