Changeset 263621 in webkit


Ignore:
Timestamp:
Jun 27, 2020 9:08:59 AM (4 years ago)
Author:
Chris Dumez
Message:

Update web-platform-tests/common from upstream
https://bugs.webkit.org/show_bug.cgi?id=213665

Reviewed by Sam Weinig.

Update web-platform-tests/common from upstream b076c305a256e7fb7.

  • web-platform-tests/common/*: Updated.
Location:
trunk/LayoutTests/imported/w3c
Files:
7 added
32 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r263613 r263621  
     12020-06-27  Chris Dumez  <cdumez@apple.com>
     2
     3        Update web-platform-tests/common from upstream
     4        https://bugs.webkit.org/show_bug.cgi?id=213665
     5
     6        Reviewed by Sam Weinig.
     7
     8        Update web-platform-tests/common from upstream b076c305a256e7fb7.
     9
     10        * web-platform-tests/common/*: Updated.
     11
    1122020-06-26  Chris Dumez  <cdumez@apple.com>
    213
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/META.yml

    r245004 r263621  
    22  - zqzhang
    33  - deniak
    4   - gsnedders
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/arrays.js

    r232903 r263621  
    1 // Returns true if the given arrays are equal. Optionally can pass an equality function.
     1/**
     2 * Callback for checking equality of c and d.
     3 *
     4 * @callback equalityCallback
     5 * @param {*} c
     6 * @param {*} d
     7 * @returns {boolean}
     8 */
     9
     10/**
     11 * Returns true if the given arrays are equal. Optionally can pass an equality function.
     12 * @param {Array} a
     13 * @param {Array} b
     14 * @param {equalityCallback} callbackFunction - defaults to `c === d`
     15 * @returns {boolean}
     16 */
    217export function areArraysEqual(a, b, equalityFunction = (c, d) => { return c === d; }) {
    318  try {
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/get-host-info.sub.js

    r253683 r263621  
     1/**
     2 * Host information for cross-origin tests.
     3 * @returns {Object} with properties for different host information.
     4 */
    15function get_host_info() {
    26
     
    3943}
    4044
     45/**
     46 * When a default port is used, location.port returns the empty string.
     47 * This function attempts to provide an exact port, assuming we are running under wptserve.
     48 * @param {*} loc - can be Location/<a>/<area>/URL, but assumes http/https only.
     49 * @returns {string} The port number.
     50 */
    4151function get_port(loc) {
    42   // When a default port is used, location.port returns the empty string.
    43   // To compare with wptserve `ports` substitution we need a port...
    44   // loc can be Location/<a>/<area>/URL, but assumes http/https only.
    4552  if (loc.port) {
    4653    return loc.port;
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/media.js

    r225781 r263621  
    1 //
    2 // Returns the URI of a supported video source based on the user agent
    3 //
     1/**
     2 * Returns the URL of a supported video source based on the user agent
     3 * @param {string} base - media URL without file extension
     4 * @returns {string}
     5 */
    46function getVideoURI(base)
    57{
     
    1719}
    1820
    19 //
    20 // Returns the URI of a supported audio source based on the user agent
    21 //
     21/**
     22 * Returns the URL of a supported audio source based on the user agent
     23 * @param {string} base - media URL without file extension
     24 * @returns {string}
     25 */
    2226function getAudioURI(base)
    2327{
     
    3539}
    3640
     41/**
     42 * Returns the MIME type for a media URL based on the file extension.
     43 * @param {string} url
     44 * @returns {string}
     45 */
    3746function getMediaContentType(url) {
    3847    var extension = new URL(url, location).pathname.split(".").pop();
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/object-association.js

    r245004 r263621  
    33// For now this only has per-Window tests, but we could expand it to also test per-Document
    44
     5/**
     6 * Run tests for window[propertyName] after discarding the browsing context, navigating, etc.
     7 * @param {string} propertyName
     8 */
    59window.testIsPerWindow = propertyName => {
    610  test(t => {
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/redirect.py

    r179439 r263621  
    77    """
    88    status = 302
    9     if "status" in request.GET:
     9    if b"status" in request.GET:
    1010        try:
    11             status = int(request.GET.first("status"))
     11            status = int(request.GET.first(b"status"))
    1212        except ValueError:
    1313            pass
     
    1515    response.status = status
    1616
    17     location = request.GET.first("location")
     17    location = request.GET.first(b"location")
    1818
    19     response.headers.set("Location", location)
     19    response.headers.set(b"Location", location)
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/reftest-wait.js

    r179439 r263621  
     1/**
     2 * Remove the `reftest-wait` class on the document element.
     3 * The reftest runner will wait with taking a screenshot while
     4 * this class is present.
     5 *
     6 * See https://web-platform-tests.org/writing-tests/reftests.html#controlling-when-comparison-occurs
     7 */
    18function takeScreenshot() {
    29    document.documentElement.classList.remove("reftest-wait");
    310}
    411
     12/**
     13 * Call `takeScreenshot()` after a delay of at least |timeout| milliseconds.
     14 * @param {number} timeout - milliseconds
     15 */
    516function takeScreenshotDelayed(timeout) {
    617    setTimeout(function() {
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/README.md

    r253683 r263621  
    1 
    2 
    31This directory contains the common infrastructure for the following tests (also referred below as projects).
    42
     
    2624# Test generator
    2725
    28 The test generator (`common/security-features/tools`) generates test HTML files from templates and a seed (`spec.src.json`) that defines all the test scenarios.
     26The test generator ([common/security-features/tools/generate.py](tools/generate.py)) generates test HTML files from templates and a seed (`spec.src.json`) that defines all the test scenarios.
    2927
    3028The project (i.e. a WPT subdirectory, for example `referrer-policy/`) that uses the generator should define per-project data and invoke the common generator logic in `common/security-features/tools`.
     
    3533common/security-features/
    3634└── tools/ - the common test generator logic
     35    ├── spec.src.json
    3736    └── template/ - the test files templates
    3837project-directory/ (e.g. referrer-policy/)
     
    4039├── generic/
    4140│   ├── test-case.sub.js - Per-project test helper
    42 │   └── tools/
    43 │       └── generator.py - Per-project generator script
     41│   ├── sanity-checker.js (Used by debug target only)
     42│   └── spec_json.js (Used by debug target only)
    4443└── gen/ - generated tests
    4544```
    4645
    47 Invoking `project-directory/generic/tools/generate.py` will parse the spec JSON and determine which tests to generate (or skip) while using templates.
    48 
    4946## Generating the tests
    5047
    51 The repository already contains generated tests, so if you're making changes, see the [Removing all generated tests](#removing-all-generated-tests) section below, on how to remove them before you start generating tests which include your changes.
     48Note: When the repository already contains generated tests, [remove all generated tests](#removing-all-generated-tests) first.
    5249
    5350```bash
    54 # Chdir into the project directory.
    55 cd ~/web-platform-tests/project-directory
     51# Install json5 module if needed.
     52pip install --user json5
    5653
    5754# Generate the test files under gen/ (HTMLs and .headers files).
    58 ./generic/tools/generate.py
     55path/to/common/security-features/tools/generate.py --spec path/to/project-directory/
    5956
    6057# Add all generated tests to the repo.
    61 git add gen/ && git commit -m "Add generated tests"
    62 ```
     58git add path/to/project-directory/gen/ && git commit -m "Add generated tests"
     59```
     60
     61This will parse the spec JSON5 files and determine which tests to generate (or skip) while using templates.
     62
     63- The default spec JSON5: `common/security-features/tools/spec.src.json`.
     64    - Describes common configurations, such as subresource types, source context types, etc.
     65- The per-project spec JSON5: `project-directory/spec.src.json`.
     66    - Describes project-specific configurations, particularly those related to test generation patterns (`specification`), policy deliveries (e.g. `delivery_type`, `delivery_value`) and `expectation`.
     67
     68For how these two spec JSON5 files are merged, see [Sub projects](#sub-projects) section.
     69
     70Note: `spec.src.json` is transitioning to JSON5 [#21710](https://github.com/web-platform-tests/wpt/issues/21710).
    6371
    6472During the generation, the spec is validated by ```common/security-features/tools/spec_validator.py```. This is specially important when you're making changes to  `spec.src.json`. Make sure it's a valid JSON (no comments or trailing commas). The validator reports specific errors (missing keys etc.), if any.
     
    6977
    7078```bash
    71 # Chdir into the project directory.
    72 cd ~/web-platform-tests/project-directory
    73 
    74 # Remove all generated tests.
    75 rm -r gen/
     79rm -r path/to/project-directory/gen/
    7680```
    7781
     
    8084Note: this section is currently obsolete. Only the release template is working.
    8185
    82 The generator script ```./generic/tools/generate.py``` has two targets: ```release``` and ```debug```.
     86The generator script has two targets: ```release``` and ```debug```.
    8387
    8488* Using **release** for the target will produce tests using a template for optimizing size and performance. The release template is intended for the official web-platform-tests and possibly other test suites. No sanity checking is done in release mode. Use this option whenever you're checking into web-platform-tests.
     
    8791
    8892Note that **release** is the default target when invoking ```generate.py```.
     93
     94
     95## Sub projects
     96
     97Projects can be nested, for example to reuse a single `spec.src.json` across similar but slightly different sets of generated tests.
     98The directory structure would look like:
     99
     100```
     101project-directory/ (e.g. referrer-policy/)
     102├── spec.src.json - Parent project's spec JSON
     103├── generic/
     104│   └── test-case.sub.js - Parent project's test helper
     105├── gen/ - parent project's generated tests
     106└── sub-project-directory/ (e.g. 4K)
     107    ├── spec.src.json - Child project's spec JSON
     108    ├── generic/
     109    │   └── test-case.sub.js - Child project's test helper
     110    └── gen/ - child project's generated tests
     111```
     112
     113`generate.py --spec project-directory/sub-project-directory` generates test files under `project-directory/sub-project-directory/gen`, based on `project-directory/spec.src.json` and `project-directory/sub-project-directory/spec.src.json`.
     114
     115- The child project's `spec.src.json` is merged into parent project's `spec.src.json`.
     116    - Two spec JSON objects are merged recursively.
     117    - If a same key exists in both objects, the child's value overwrites the parent's value.
     118        - If both (child's and parent's) values are arrays, then the child's value is concatenated to the parent's value.
     119    - For debugging, `generate.py` dumps the merged spec JSON object as `generic/debug-output.spec.src.json`.
     120- The child project's generated tests include both of the parent and child project's `test-case.sub.js`:
     121  ```html
     122  <script src="project-directory/test-case.sub.js"></script>
     123  <script src="project-directory/sub-project-directory/test-case.sub.js"></script>
     124  <script>
     125    TestCase(...);
     126  </script>
     127  ```
    89128
    90129
     
    130169* Regenerate the tests and MANIFEST.json
    131170
    132 
    133 ## The spec JSON format
    134 
    135 For examples of spec JSON files, see [referrer-policy/spec.src.json](../../referrer-policy/spec.src.json) or  [mixed-content/spec.src.json](../../mixed-content/spec.src.json).
    136 
    137 ### Main sections
     171## How the generator works
     172
     173This section describes how `spec.src.json` is turned into scenario data in test HTML files which are then processed by JavaScript test helpers and server-side scripts, and describes the objects/types used in the process.
     174
     175### The spec JSON
     176
     177`spec.src.json` is the input for the generator that defines what to generate. For examples of spec JSON files, see [referrer-policy/spec.src.json](../../referrer-policy/spec.src.json) or  [mixed-content/spec.src.json](../../mixed-content/spec.src.json).
     178
     179#### Main sections
    138180
    139181* **`specification`**
     
    144186* **`excluded_tests`**
    145187
    146   List of ```test_expansion``` patterns expanding into selections which get skipped when generating the tests (aka. blacklisting/suppressing)
     188  List of ```test_expansion``` patterns expanding into selections which get skipped when generating the tests (aka. blocklisting/suppressing)
    147189
    148190* **`test_expansion_schema`**
     
    161203* **`source_context_list_schema`**
    162204
    163   Provides possible nested combinations of source contexts. See [Source Contexts](#source-contexts) section below for details.
    164 
    165 ### Test Expansion Patterns
    166 
     205  Provides possible nested combinations of source contexts. See [SourceContexts Resolution](#sourcecontexts-resolution) section below for details.
     206
     207### Test Expansion Pattern Object
     208
     209Test expansion patterns (`test_expansion`s in `specification` section) define the combinations of test configurations (*selections*) to be generated.
    167210Each field in a test expansion can be in one of the following formats:
    168211
     
    173216* Match all: ```"*"```
    174217
    175 
    176 **NOTE:** An expansion is always constructive (inclusive), there isn't a negation operator for explicit exclusion. Be aware that using an empty list ```[]``` matches (expands into) exactly nothing. Tests which are to be excluded should be defined in the ```excluded_tests``` section instead.
    177 
    178 A single test expansion pattern, be it a requirement or a suppressed pattern, gets expanded into a list of **selections** as follows:
     218The following fields have special meaning:
     219
     220- **`name`**: just ignored. (Previously this was used as a part of filenames but now this is merely a label for human and is never used by generator. This field might be removed in the future (https://github.com/web-platform-tests/wpt/issues/21708))
     221- **`expansion`**: if there is more than one pattern expanding into a same selection, the pattern appearing later in the spec JSON will overwrite a previously generated selection. To make clear this is intentional, set the value of the ```expansion``` field to ```default``` for an expansion appearing earlier and ```override``` for the one appearing later.
     222
     223For example a test expansion pattern (taken from [referrer-policy/spec.src.json](../../referrer-policy/spec.src.json), sorted/formatted for explanation):
     224
     225```json
     226{
     227  "name": "insecure-protocol",
     228  "expansion": "default",
     229
     230  "delivery_type": "*",
     231  "delivery_value": "no-referrer-when-downgrade",
     232  "source_context_list": "*",
     233
     234  "expectation": "stripped-referrer",
     235  "origin": ["same-http", "cross-http"],
     236  "redirection": "*",
     237  "source_scheme": "http",
     238  "subresource": "*"
     239}
     240```
     241
     242means: "All combinations with all possible `delivery_type`,  `delivery_value`=`no-referrer-when-downgrade`, all possible `source_context_list`, `expectation`=`stripped-referrer`, `origin`=`same-http` or `cross-http`,  all possible `redirection`, `source_scheme`=`http`, and all possible `subresource`.
     243
     244### Selection Object
     245
     246A selection is an object that defines a single test, with keys/values from `test_expansion_schema`.
     247
     248A single test expansion pattern gets expanded into a list of selections as follows:
    179249
    180250* Expand each field's pattern (single, any of, or all) to list of allowed values (defined by the ```test_expansion_schema```)
    181251
    182 * Permute - Recursively enumerate all **selections** across all fields
    183 
    184 Be aware that if there is more than one pattern expanding into a same selection, the pattern appearing later in the spec JSON will overwrite a previously generated selection. To make sure this is not undetected when generating, set the value of the ```expansion``` field to ```default``` for an expansion appearing earlier and ```override``` for the one appearing later.
    185 
    186 A **selection** is a single **test instance** (scenario) with explicit values that defines a single test. The scenario is then evaluated by the ```TestCase``` in JS. For the rest of the arranging part, examine ```/common/security-features/tools/generate.py``` to see how the values for the templates are produced.
    187 
     252* Permute - Recursively enumerate all selections across all fields
     253
     254The following field has special meaning:
     255
     256- **`delivery_key`**: This doesn't exist in test expansion patterns, and instead is taken from `delivery_key` field of the spec JSON and added into selections. (TODO(https://github.com/web-platform-tests/wpt/issues/21708): probably this should be added to test expansion patterns to remove this special handling)
     257
     258For example, the test expansion in the example above generates selections like the following selection (which eventually generates [this test file](../../referrer-policy/gen/worker-classic.http-rp/no-referrer-when-downgrade/fetch/same-http.no-redirect.http.html )):
     259
     260```json
     261{
     262  "delivery_type": "http-rp",
     263  "delivery_key": "referrerPolicy",
     264  "delivery_value": "no-referrer-when-downgrade",
     265  "source_context_list": "worker-classic",
     266
     267  "expectation": "stripped-referrer",
     268  "origin": "same-http",
     269  "redirection": "no-redirect",
     270  "source_scheme": "http",
     271  "subresource": "fetch"
     272}
     273```
     274
     275### Excluding Test Expansion Patterns
     276
     277The ```excluded_tests``` section have objects with the same format as [Test Expansion Patterns](#test-expansion-patterns) that define selections to be excluded.
    188278
    189279Taking the spec JSON, the generator follows this algorithm:
     
    191281* Expand all ```excluded_tests``` to create a blacklist of selections
    192282
    193 * For each specification requirement: Expand the ```test_expansion``` pattern into selections and check each against the blacklist, if not marked as suppresed, generate the test resources for the selection
    194 
    195 
    196 ### Source Contexts
    197 
    198 In **`source_context_list_schema`**, we can specify
    199 
    200 - source contexts from where subresource requests are sent, and
    201 - how policies are delivered, by source contexts and/or subresource requests.
    202 
    203 - `sourceContextList`: an array of `SourceContext` objects, and
    204 - `subresourcePolicyDeliveries`: an array of `PolicyDelivery` objects.
    205 
    206 They have the same object format as described in
    207 `common/security-features/resources/common.js` comments, and are directly
    208 serialized to generated HTML files and passed to JavaScript test code,
    209 except that:
    210 
    211 - The first entry of `sourceContextList`'s `sourceContextType` should be
    212   always `top`, which represents the top-level generated test HTML.
    213   (This entry is omitted in the JSON passed to JavaScript, but
    214   the policy deliveries specified here are written as e.g.
    215   `<meta>` elements in the generated test HTML or HTTP headers)
    216 - Instead of `PolicyDelivery` object (in `sourceContextList` or
    217   `subresourcePolicyDeliveries`), following placeholder strings can be used.
    218 
    219 The keys of `source_context_list_schema` can be used as the values of
    220 `source_context_list` fields, to indicate which source context configuration
    221 to be used.
    222 
    223 ### PolicyDelivery placeholders
    224 
    225 Each test contains
    226 
    227 - `delivery_key` (derived from the top-level `delivery_key`) and
    228 - `delivery_value`, `delivery_type` (derived from `test_expansion`),
    229 
    230 which represents the **target policy delivery**, the policy delivery to be
    231 tested.
    232 
    233 The following placeholder strings in `source_context_list_schema` can be used:
     283* For each `specification` entries: Expand the ```test_expansion``` pattern into selections and check each against the blacklist, if not marked as suppresed, generate the test resources for the selection
     284
     285###  SourceContext Resolution
     286
     287The `source_context_list_schema` section of `spec.src.json` defines templates of policy deliveries and source contexts.
     288The `source_context_list` value in a selection specifies the key of the template to be used in `source_context_list_schema`, and the following fields in the selection are filled into the template (these three values define the **target policy delivery** to be tested):
     289
     290- `delivery_type`
     291- `delivery_key`
     292- `delivery_value`
     293
     294#### Source Context List Schema
     295
     296Each entry of **`source_context_list_schema`**, defines a single template of how/what policies to be delivered in what source contexts (See also [PolicyDelivery](types.md#policydelivery) and [SourceContext](types.md#sourcecontext)).
     297
     298- The key: the name of the template which matches with the `source_context_list` value in a selection.
     299- `sourceContextList`: an array of `SourceContext` objects that represents a (possibly nested) context.
     300    - `sourceContextType` of the first entry of `sourceContextList` should be always `"top"`, which represents the top-level generated test HTML. This entry is omitted in the scenario JSON object passed to JavaScript runtime, but the policy deliveries specified here are handled by the generator, e.g. written as `<meta>` elements in the generated test HTML.
     301- `subresourcePolicyDeliveries`: an array of `PolicyDelivery` objects that represents policies specified at subresource requests (e.g. `referrerPolicy` attribute of `<img>` elements).
     302
     303#### PolicyDelivery placeholders
     304
     305Instead to ordinal `PolicyDelivery` objects, the following placeholder strings can be used in `sourceContextList` or `subresourcePolicyDeliveries`.
    234306
    235307- `"policy"`:
    236308    - Replaced with the target policy delivery.
    237     - Can be used to specify where the target policy delivery should be
    238       delivered.
    239309- `"policyIfNonNull"`:
    240     - Replaced with the target policy delivery, only if it has non-null value.
    241       If the value is null, then the test file is not generated.
     310    - Replaced with the target policy delivery, only if `delivery_value` is not `null`.
     311      If `delivery_value` is `null`, then the test is not generated.
    242312- `"anotherPolicy"`:
    243313    - Replaced with a `PolicyDelivery` object that has a different value from
     
    246316      the target policy delivery.
    247317
    248 For example, when the target policy delivery is
    249 `{deliveryType: "http-rp", key: "referrerPolicy", value: "no-referrer"}`,
    250 
    251 ```json
    252 "sourceContextList": [
    253   {
    254     "sourceContextType": "top",
    255     "policyDeliveries": [
    256       "anotherPolicy"
    257     ]
    258   },
    259   {
    260     "sourceContextType": "classic-worker",
    261     "policyDeliveries": [
    262       "policy"
    263     ]
    264   }
    265 ]
    266 ```
    267 
    268 is replaced with
    269 
    270 ```json
    271 "sourceContextList": [
    272   {
    273     "sourceContextType": "top",
    274     "policyDeliveries": [
    275       {
    276         "deliveryType": "meta",
    277         "key": "referrerPolicy",
    278         "value": "unsafe-url"
    279       }
    280     ]
    281   },
    282   {
    283     "sourceContextType": "classic-worker",
    284     "policyDeliveries": [
    285       {
    286         "deliveryType": "http-rp",
    287         "key": "referrerPolicy",
    288         "value": "no-referrer"
    289       }
    290     ]
    291   }
    292 ]
    293 ```
    294 
    295 which indicates
    296 
    297 - The top-level Document has `<meta name="referrer" content="unsafe-url">`.
    298 - The classic worker is created with
    299   `Referrer-Policy: no-referrer` HTTP response headers.
    300 
    301 ### `source_context_schema` and `subresource_schema`
     318#### `source_context_schema` and `subresource_schema`
    302319
    303320These represent supported delivery types and subresources
     
    305322
    306323- To filter out test files for unsupported combinations of delivery types,
    307   source contexts and subresources.
    308 - To determine what delivery types should be used for `anotherPolicy`
     324  source contexts and subresources during SourceContext resolution.
     325- To determine what delivery types can be used for `anotherPolicy`
    309326  placeholder.
     327
     328#### Example
     329
     330For example, the following entry in `source_context_list_schema`:
     331
     332```json
     333"worker-classic": {
     334  "sourceContextList": [
     335    {
     336      "sourceContextType": "top",
     337      "policyDeliveries": [
     338        "anotherPolicy"
     339      ]
     340    },
     341    {
     342      "sourceContextType": "worker-classic",
     343      "policyDeliveries": [
     344        "policy"
     345      ]
     346    }
     347  ],
     348  "subresourcePolicyDeliveries": []
     349}
     350```
     351
     352Defines a template to be instantiated with `delivery_key`, `delivery_type` and `delivery_value` values defined outside `source_context_list_schema`, which reads:
     353
     354- A classic `WorkerGlobalScope` is created under the top-level Document, and has a policy defined by `delivery_key`, `delivery_type` and `delivery_value`.
     355- The top-level Document has a policy different from the policy given to the classic worker (to confirm that the policy of the classic worker, not of the top-level Document, is used).
     356- The subresource request is sent from the classic `WorkerGlobalScope`, with no additional policies specified at the subresource request.
     357
     358And when filled with the following values from a selection:
     359
     360- `delivery_type`: `"http-rp"`
     361- `delivery_key`: `"referrerPolicy"`
     362- `delivery_value`: `"no-referrer-when-downgrade"`
     363
     364This becomes:
     365
     366```json
     367"worker-classic": {
     368  "sourceContextList": [
     369    {
     370      "sourceContextType": "top",
     371      "policyDeliveries": [
     372        {
     373          "deliveryType": "meta",
     374          "key": "referrerPolicy",
     375          "value": "no-referrer"
     376        }
     377      ]
     378    },
     379    {
     380      "sourceContextType": "worker-classic",
     381      "policyDeliveries": [
     382        {
     383          "deliveryType": "http-rp",
     384          "key": "referrerPolicy",
     385          "value": "no-referrer-when-downgrade"
     386        }
     387      ]
     388    }
     389  ],
     390  "subresourcePolicyDeliveries": []
     391}
     392```
     393
     394which means
     395
     396- The top-level Document has `<meta name="referrer" content="no-referrer">`.
     397- The classic worker is created with
     398  `Referrer-Policy: no-referrer-when-downgrade` HTTP response headers.
     399
     400### Scenario Object
     401
     402The **scenario** object is the JSON object written to the generated HTML files, and passed to JavaScript test runtime (as an argument of `TestCase`).
     403A scenario object is an selection object, minus the keys used in [SourceContext Resolution](#sourceContext-resolution):
     404
     405- `source_context_list`
     406- `delivery_type`
     407- `delivery_key`
     408- `delivery_value`
     409
     410plus the keys instantiated by [SourceContext Resolution](#sourceContext-resolution):
     411
     412- `source_context_list`, except for the first `"top"` entry.
     413- `subresource_policy_deliveries`
     414
     415For example:
     416
     417```json
     418{
     419  "source_context_list": [
     420    {
     421      "sourceContextType": "worker-classic",
     422      "policyDeliveries": [
     423        {
     424          "deliveryType": "http-rp",
     425          "key": "referrerPolicy",
     426          "value": "no-referrer-when-downgrade"
     427        }
     428      ]
     429    }
     430  ],
     431  "subresource_policy_deliveries": [],
     432
     433  "expectation": "stripped-referrer",
     434  "origin": "same-http",
     435  "redirection": "no-redirect",
     436  "source_scheme": "http",
     437  "subresource": "fetch"
     438}
     439```
     440
     441### TopLevelPolicyDelivery Object
     442
     443The ***TopLevelPolicyDelivery** object is the first `"top"` entry of `SourceContextList` instantiated by [SourceContext Resolution](#sourceContext-resolution), which represents the policy delivery of the top-level HTML Document.
     444
     445The generator generates `<meta>` elements and `.headers` files of the top-level HTML files from the TopLevelPolicyDelivery object.
     446
     447This is handled separately by the generator from other parts of selection objects and scenario objects, because the `<meta>` and `.headers` are hard-coded directly to the files in the WPT repository, while policies of subcontexts are generated via server-side `common/security-features/scope` scripts.
     448
     449TODO(https://github.com/web-platform-tests/wpt/issues/21710): Currently the name `TopLevelPolicyDelivery` doesn't appear in the code.
     450
     451## How the test runtime works
     452
     453All the information needed at runtime is contained in an scenario object. See the code/comments of the following files.
     454
     455- `project-directory/generic/test-case.js` defines `TestCase`, the entry point that receives a scenario object. `resources/common.sub.js` does the most of common JavaScript work.
     456    - Subresource URLs (which point to `subresource/` scripts) are calculated from `origin` and `redirection` values.
     457    - Initiating fetch requests based on `subresource` and `subresource_policy_deliveries`.
     458- `scope/` server-side scripts serve non-toplevel contexts, while the top-level Document is generated by the generator.
     459   TODO(https://github.com/web-platform-tests/wpt/issues/21709): Merge the logics of `scope/` and the generator.
     460- `subresource/` server-side scripts serve subresource responses.
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/resources/common.sub.js

    r253683 r263621  
    1414// The objects are represented as JSON objects (not JavaScript/Python classes
    1515// in a strict sense) to be passed between JavaScript/Python code.
    16 
    17 // Note: So far this document covers:
    18 // - resources/common.sub.js : client-side test infra code
    19 // - scope/ - server-side scripts that serves nested source contexts
    20 // but doesn't cover:
    21 // - tools/ - generator scripts that generates top-level HTML documents.
    22 // There are some policies only handled by generators (e.g. mixed-content
    23 // opt-ins) and not yet covered by the docs here.
     16//
     17// See also common/security-features/Types.md for high-level description.
    2418
    2519/**
     
    9387  @typedef SourceContext
    9488  @type {object}
    95   Requests can be possibly sent from various kinds of source contexts, i.e.
    96   fetch client's environment settings objects:
    97   top-level windows, iframes, or workers.
    98   A SourceContext object specifies one environment settings object, and
    99   an Array<SourceContext> specifies a possibly nested context,
    100   from the outer-most to inner-most environment settings objects.
    101 
    102   For example:
    103     [{sourceContextType: "srcdoc"}, {sourceContextType: "worker-classic"}]
    104   means that a subresource request is to be sent from
    105   a classic dedicated worker created from <iframe srcdoc>
    106   inside the top-level HTML document.
    107   Note: the top-level document is not included in the array and
    108   is assumed implicitly.
    109 
    110   SourceContext (or Array<SourceContext>) is set based on
    111   the fetch client's settings object that is used for the subresource request,
    112   NOT on module map settings object, and
    113   NOT on the inner-most settings object that appears in the test.
    114   For example, Array<SourceContext> is `[]` (indicating the top Window)
    115   for `worker.js`
    116   - When it is the root worker script: `new Worker('worker.js')`, or
    117   - When it is imported from the root worker script:
    118     `new Worker('top.js', {type: 'module'})`
    119     where `top.js` has `import 'worker.js'`.
    120   because the request for `worker.js` uses the Window as its fetch client's
    121   settings object, while a WorkerGlobalScope is created though.
    12289
    12390  @property {string} sourceContextType
     
    399366  requestViaScript                 2        Y        Y       -
    400367  requestViaSendBeacon             3        -        Y       -
    401   requestViaSharedWorker           2        Y        -       -
     368  requestViaSharedWorker           2        Y        Y       Y
    402369  requestViaVideo                  3        -        Y       -
    403370  requestViaWebSocket              3        -        Y       -
     
    520487
    521488function workerUrlThatImports(url) {
     489  return `/common/security-features/subresource/static-import.py` +
     490      `?import_url=${encodeURIComponent(url)}`;
     491}
     492
     493function workerDataUrlThatImports(url) {
    522494  return `data:text/javascript,import '${url}';`;
    523495}
     
    543515}
    544516
    545 function requestViaSharedWorker(url) {
     517function requestViaSharedWorker(url, options) {
    546518  var worker;
    547519  try {
    548     worker = new SharedWorker(url);
     520    worker = new SharedWorker(url, options);
    549521  } catch(e) {
    550522    return Promise.reject(e);
     
    912884    invoker: url => requestViaDedicatedWorker(url, {type: "module"}),
    913885  },
     886  "worker-import": {
     887    path: "/common/security-features/subresource/worker.py",
     888    invoker: url =>
     889        requestViaDedicatedWorker(workerUrlThatImports(url), {type: "module"}),
     890  },
    914891  "worker-import-data": {
    915892    path: "/common/security-features/subresource/worker.py",
    916893    invoker: url =>
    917         requestViaDedicatedWorker(workerUrlThatImports(url), {type: "module"}),
     894        requestViaDedicatedWorker(workerDataUrlThatImports(url), {type: "module"}),
    918895  },
    919896  "sharedworker-classic": {
    920897    path: "/common/security-features/subresource/shared-worker.py",
    921     invoker: requestViaSharedWorker,
     898    invoker: url => requestViaSharedWorker(url),
     899  },
     900  "sharedworker-module": {
     901    path: "/common/security-features/subresource/shared-worker.py",
     902    invoker: url => requestViaSharedWorker(url, {type: "module"}),
     903  },
     904  "sharedworker-import": {
     905    path: "/common/security-features/subresource/shared-worker.py",
     906    invoker: url =>
     907        requestViaSharedWorker(workerUrlThatImports(url), {type: "module"}),
     908  },
     909  "sharedworker-import-data": {
     910    path: "/common/security-features/subresource/shared-worker.py",
     911    invoker: url =>
     912        requestViaSharedWorker(workerDataUrlThatImports(url), {type: "module"}),
    922913  },
    923914
     
    935926      path: "/common/security-features/subresource/worker.py",
    936927      invoker: url =>
    937           requestViaWorklet(workletType, workerUrlThatImports(url))
     928          requestViaWorklet(workletType, workerDataUrlThatImports(url))
    938929    };
    939930}
     
    11211112    "worker-classic": {
    11221113      // Classic dedicated worker loaded from same-origin.
    1123       invoker: invokeFromWorker.bind(undefined, false, {}),
     1114      invoker: invokeFromWorker.bind(undefined, "worker", false, {}),
    11241115    },
    11251116    "worker-classic-data": {
    11261117      // Classic dedicated worker loaded from data: URL.
    1127       invoker: invokeFromWorker.bind(undefined, true, {}),
     1118      invoker: invokeFromWorker.bind(undefined, "worker", true, {}),
    11281119    },
    11291120    "worker-module": {
    11301121      // Module dedicated worker loaded from same-origin.
    1131       invoker: invokeFromWorker.bind(undefined, false, {type: 'module'}),
     1122      invoker: invokeFromWorker.bind(undefined, "worker", false, {type: 'module'}),
    11321123    },
    11331124    "worker-module-data": {
    11341125      // Module dedicated worker loaded from data: URL.
    1135       invoker: invokeFromWorker.bind(undefined, true, {type: 'module'}),
     1126      invoker: invokeFromWorker.bind(undefined, "worker", true, {type: 'module'}),
     1127    },
     1128    "sharedworker-classic": {
     1129      // Classic shared worker loaded from same-origin.
     1130      invoker: invokeFromWorker.bind(undefined, "sharedworker", false, {}),
     1131    },
     1132    "sharedworker-classic-data": {
     1133      // Classic shared worker loaded from data: URL.
     1134      invoker: invokeFromWorker.bind(undefined, "sharedworker", true, {}),
     1135    },
     1136    "sharedworker-module": {
     1137      // Module shared worker loaded from same-origin.
     1138      invoker: invokeFromWorker.bind(undefined, "sharedworker", false, {type: 'module'}),
     1139    },
     1140    "sharedworker-module-data": {
     1141      // Module shared worker loaded from data: URL.
     1142      invoker: invokeFromWorker.bind(undefined, "sharedworker", true, {type: 'module'}),
    11361143    },
    11371144  };
     
    11541161
    11551162/**
     1163  @param {string} workerType
     1164    "worker" (for dedicated worker) or "sharedworker".
    11561165  @param {boolean} isDataUrl
    11571166    true if the worker script is loaded from data: URL.
     
    11621171  Other parameters and return values are the same as those of invokeRequest().
    11631172*/
    1164 function invokeFromWorker(isDataUrl, workerOptions,
     1173function invokeFromWorker(workerType, isDataUrl, workerOptions,
    11651174                          subresource, sourceContextList) {
    11661175  const currentSourceContext = sourceContextList[0];
     
    11861195  return promise
    11871196    .then(url => {
    1188       const worker = new Worker(url, workerOptions);
    1189       worker.postMessage({subresource: subresource,
    1190                           sourceContextList: sourceContextList.slice(1)});
    1191       return bindEvents2(worker, "message", worker, "error", window, "error");
     1197      if (workerType === "worker") {
     1198        const worker = new Worker(url, workerOptions);
     1199        worker.postMessage({subresource: subresource,
     1200                            sourceContextList: sourceContextList.slice(1)});
     1201        return bindEvents2(worker, "message", worker, "error", window, "error");
     1202      } else if (workerType === "sharedworker") {
     1203        const worker = new SharedWorker(url, workerOptions);
     1204        worker.port.start();
     1205        worker.port.postMessage({subresource: subresource,
     1206                                 sourceContextList: sourceContextList.slice(1)});
     1207        return bindEvents2(worker.port, "message", worker, "error", window, "error");
     1208      } else {
     1209        throw new Error('Invalid worker type: ' + workerType);
     1210      }
    11921211    })
    11931212    .then(event => {
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/scope/template/worker.js.template

    r253683 r263621  
    11%(import)s
    22
     3if ('DedicatedWorkerGlobalScope' in self &&
     4    self instanceof DedicatedWorkerGlobalScope) {
     5  self.onmessage = event => onMessageFromParent(event, self);
     6} else if ('SharedWorkerGlobalScope' in self &&
     7    self instanceof SharedWorkerGlobalScope) {
     8  onconnect = event => {
     9    const port = event.ports[0];
     10    port.onmessage = event => onMessageFromParent(event, port);
     11  };
     12}
     13
    314// Receive a message from the parent and start the test.
    4 function onMessageFromParent(event) {
    5   // Because this window might receive messages from child context during
    6   // tests, we first remove the listener here before staring the test.
    7   self.removeEventListener('message', onMessageFromParent);
    8 
     15function onMessageFromParent(event, port) {
    916  const configurationError = "%(error)s";
    1017  if (configurationError.length > 0) {
    11     postMessage({error: configurationError});
     18    port.postMessage({error: configurationError});
    1219    return;
    1320  }
     
    1522  invokeRequest(event.data.subresource,
    1623                event.data.sourceContextList)
    17     .then(result => postMessage(result))
     24    .then(result => port.postMessage(result))
    1825    .catch(e => {
    1926        const message = (e.error && e.error.stack) || e.message || "Error";
    20         postMessage({error: message});
     27        port.postMessage({error: message});
    2128      });
    2229}
    23 
    24 self.addEventListener('message', onMessageFromParent);
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/scope/worker.py

    r253683 r263621  
    2424      if delivery['key'] == 'referrerPolicy':
    2525        maybe_additional_headers['Referrer-Policy'] = delivery['value']
     26      elif delivery['key'] == 'mixedContent' and delivery['value'] == 'opt-in':
     27        maybe_additional_headers['Content-Security-Policy'] = 'block-all-mixed-content'
     28      elif delivery['key'] == 'upgradeInsecureRequests' and delivery['value'] == 'upgrade':
     29        maybe_additional_headers['Content-Security-Policy'] = 'upgrade-insecure-requests'
    2630      else:
    2731        error = 'invalid delivery key for http-rp: %s' % delivery['key']
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/template/w3c-import.log

    r245004 r263621  
    2020/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/template/script.js.template
    2121/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/template/shared-worker.js.template
     22/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/template/static-import.js.template
    2223/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/template/svg.css.template
    2324/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/template/svg.embedded.template
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/w3c-import.log

    r245004 r263621  
    2121/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/font.py
    2222/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/image.py
     23/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/referrer.py
    2324/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/script.py
    2425/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/shared-worker.py
     26/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/static-import.py
    2527/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/stylesheet.py
    2628/LayoutTests/imported/w3c/web-platform-tests/common/security-features/subresource/subresource.py
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/format_spec_src_json.py

    r253683 r263621  
    88    script_directory = os.path.dirname(os.path.abspath(__file__))
    99    for dir in [
    10             'mixed-content', 'referrer-policy', 'upgrade-insecure-requests'
     10            'mixed-content', 'referrer-policy', 'referrer-policy/4K-1',
     11            'referrer-policy/4K', 'referrer-policy/4K+1',
     12            'upgrade-insecure-requests'
    1113    ]:
    1214        filename = os.path.join(script_directory, '..', '..', '..', dir,
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/generate.py

    r253683 r263621  
     1#!/usr/bin/env python
     2
    13from __future__ import print_function
    24
    35import argparse
     6import collections
    47import copy
    58import json
     
    5255
    5356# Dumps the test config `selection` into a serialized JSON string.
    54 # We omit `name` parameter because it is not used by tests.
    5557def dump_test_parameters(selection):
    56     selection = dict(selection)
    57     del selection['name']
    58 
    5958    return json.dumps(
    6059        selection,
     
    6564
    6665
    67 def get_test_filename(config, selection):
     66def get_test_filename(spec_directory, spec_json, selection):
    6867    '''Returns the filname for the main test HTML file'''
    6968
     
    7372        selection_for_filename['delivery_value'] = 'unset'
    7473
    75     return os.path.join(config.spec_directory,
    76                         config.test_file_path_pattern % selection_for_filename)
     74    return os.path.join(
     75        spec_directory,
     76        spec_json['test_file_path_pattern'] % selection_for_filename)
    7777
    7878
     
    128128
    129129
    130 def generate_selection(spec_json, config, selection, spec,
    131                        test_html_template_basename):
    132     test_filename = get_test_filename(config, selection)
     130def generate_selection(spec_json, selection):
     131    '''
     132    Returns a scenario object (with a top-level source_context_list entry,
     133    which will be removed in generate_test_file() later).
     134    '''
    133135
    134136    target_policy_delivery = util.PolicyDelivery(selection['delivery_type'],
     
    165167            ['supported_delivery_type'][selection['subresource']])
    166168
     169    # Generate per-scenario test description.
     170    selection['test_description'] = spec_json[
     171        'test_description_template'] % selection
     172
     173    return selection
     174
     175
     176def generate_test_file(spec_directory, test_helper_filenames,
     177                       test_html_template_basename, test_filename, scenarios):
     178    '''
     179    Generates a test HTML file (and possibly its associated .headers file)
     180    from `scenarios`.
     181    '''
     182
     183    # Scenarios for the same file should have the same `source_context_list`,
     184    # including the top-level one.
     185    # Note: currently, non-top-level source contexts aren't necessarily required
     186    # to be the same, but we set this requirement as it will be useful e.g. when
     187    # we e.g. reuse a worker among multiple scenarios.
     188    for scenario in scenarios:
     189        assert (scenario['source_context_list'] == scenarios[0]
     190                ['source_context_list'])
     191
    167192    # We process the top source context below, and do not include it in
    168     # `scenario` field in JavaScript.
    169     top_source_context = selection['source_context_list'].pop(0)
     193    # the JSON objects (i.e. `scenarios`) in generated HTML files.
     194    top_source_context = scenarios[0]['source_context_list'].pop(0)
    170195    assert (top_source_context.source_context_type == 'top')
    171 
    172     # Adjust the template for the test invoking JS. Indent it to look nice.
    173     indent = "\n" + " " * 8
    174     selection['scenario'] = dump_test_parameters(selection).replace(
    175         "\n", indent)
    176 
    177     selection['spec_name'] = spec['name']
    178     selection[
    179         'test_page_title'] = config.test_page_title_template % spec['title']
    180     selection['spec_description'] = spec['description']
    181     selection['spec_specification_url'] = spec['specification_url']
    182     selection['helper_js'] = config.helper_js
    183     selection['sanity_checker_js'] = config.sanity_checker_js
    184     selection['spec_json_js'] = config.spec_json_js
     196    for scenario in scenarios[1:]:
     197        assert (scenario['source_context_list'].pop(0) == top_source_context)
     198
     199    parameters = {}
     200
     201    # Sort scenarios, to avoid unnecessary diffs due to different orders in
     202    # `scenarios`.
     203    serialized_scenarios = sorted(
     204        [dump_test_parameters(scenario) for scenario in scenarios])
     205
     206    parameters['scenarios'] = ",\n".join(serialized_scenarios).replace(
     207        "\n", "\n" + " " * 10)
     208
     209    test_directory = os.path.dirname(test_filename)
     210
     211    parameters['helper_js'] = ""
     212    for test_helper_filename in test_helper_filenames:
     213        parameters['helper_js'] += '    <script src="%s"></script>\n' % (
     214            os.path.relpath(test_helper_filename, test_directory))
     215    parameters['sanity_checker_js'] = os.path.relpath(
     216        os.path.join(spec_directory, 'generic', 'sanity-checker.js'),
     217        test_directory)
     218    parameters['spec_json_js'] = os.path.relpath(
     219        os.path.join(spec_directory, 'generic', 'spec_json.js'),
     220        test_directory)
    185221
    186222    test_headers_filename = test_filename + ".headers"
    187     test_directory = os.path.dirname(test_filename)
    188223
    189224    test_html_template = util.get_template(test_html_template_basename)
     
    195230        % {'generating_script_filename': os.path.relpath(sys.argv[0],
    196231                                                         util.test_root_directory),
    197            'html_template_filename': os.path.relpath(html_template_filename,
    198                                                      util.test_root_directory)}
     232           'spec_directory': os.path.relpath(spec_directory,
     233                                             util.test_root_directory)}
    199234
    200235    # Adjust the template for the test invoking JS. Indent it to look nice.
    201     selection['generated_disclaimer'] = generated_disclaimer.rstrip()
    202     selection[
    203         'test_description'] = config.test_description_template % selection
    204     selection['test_description'] = \
    205         selection['test_description'].rstrip().replace("\n", "\n" + " " * 33)
     236    parameters['generated_disclaimer'] = generated_disclaimer.rstrip()
    206237
    207238    # Directory for the test files.
     
    218249                f.write('%s: %s\n' % (header, delivery['headers'][header]))
    219250
    220     selection['meta_delivery_method'] = delivery['meta']
     251    parameters['meta_delivery_method'] = delivery['meta']
    221252    # Obey the lint and pretty format.
    222     if len(selection['meta_delivery_method']) > 0:
    223         selection['meta_delivery_method'] = "\n    " + \
    224                                             selection['meta_delivery_method']
     253    if len(parameters['meta_delivery_method']) > 0:
     254        parameters['meta_delivery_method'] = "\n    " + \
     255                                            parameters['meta_delivery_method']
    225256
    226257    # Write out the generated HTML file.
    227     util.write_file(test_filename, test_html_template % selection)
    228 
    229 
    230 def generate_test_source_files(config, spec_json, target):
     258    util.write_file(test_filename, test_html_template % parameters)
     259
     260
     261def generate_test_source_files(spec_directory, test_helper_filenames,
     262                               spec_json, target):
    231263    test_expansion_schema = spec_json['test_expansion_schema']
    232264    specification = spec_json['specification']
    233265
    234     spec_json_js_template = util.get_template('spec_json.js.template')
    235     generated_spec_json_filename = os.path.join(config.spec_directory,
    236                                                 "spec_json.js")
    237     util.write_file(
    238         generated_spec_json_filename,
    239         spec_json_js_template % {'spec_json': json.dumps(spec_json)})
     266    if target == "debug":
     267        spec_json_js_template = util.get_template('spec_json.js.template')
     268        util.write_file(
     269            os.path.join(spec_directory, "generic", "spec_json.js"),
     270            spec_json_js_template % {'spec_json': json.dumps(spec_json)})
     271        util.write_file(
     272            os.path.join(spec_directory, "generic",
     273                         "debug-output.spec.src.json"),
     274            json.dumps(spec_json, indent=2, separators=(',', ': ')))
    240275
    241276    # Choose a debug/release template depending on the target.
    242277    html_template = "test.%s.html.template" % target
    243278
    244     artifact_order = test_expansion_schema.keys() + ['name']
     279    artifact_order = test_expansion_schema.keys()
    245280    artifact_order.remove('expansion')
    246281
     282    excluded_selection_pattern = ''
     283    for key in artifact_order:
     284        excluded_selection_pattern += '%(' + key + ')s/'
     285
    247286    # Create list of excluded tests.
    248     exclusion_dict = {}
     287    exclusion_dict = set()
    249288    for excluded_pattern in spec_json['excluded_tests']:
    250289        excluded_expansion = \
     
    252291        for excluded_selection in permute_expansion(excluded_expansion,
    253292                                                    artifact_order):
    254             excluded_selection_path = config.selection_pattern % excluded_selection
    255             exclusion_dict[excluded_selection_path] = True
     293            excluded_selection['delivery_key'] = spec_json['delivery_key']
     294            exclusion_dict.add(excluded_selection_pattern % excluded_selection)
     295
     296    # `scenarios[filename]` represents the list of scenario objects to be
     297    # generated into `filename`.
     298    scenarios = {}
    256299
    257300    for spec in specification:
     
    265308            for selection in permute_expansion(expansion, artifact_order):
    266309                selection['delivery_key'] = spec_json['delivery_key']
    267                 selection_path = config.selection_pattern % selection
    268                 if not selection_path in exclusion_dict:
    269                     if selection_path in output_dict:
    270                         if expansion_pattern['expansion'] != 'override':
    271                             print(
    272                                 "Error: %s's expansion is default but overrides %s"
    273                                 % (selection['name'],
    274                                    output_dict[selection_path]['name']))
    275                             sys.exit(1)
    276                     output_dict[selection_path] = copy.deepcopy(selection)
    277                 else:
    278                     print('Excluding selection:', selection_path)
     310                selection_path = spec_json['selection_pattern'] % selection
     311                if selection_path in output_dict:
     312                    if expansion_pattern['expansion'] != 'override':
     313                        print("Error: expansion is default in:")
     314                        print(dump_test_parameters(selection))
     315                        print("but overrides:")
     316                        print(dump_test_parameters(
     317                            output_dict[selection_path]))
     318                        sys.exit(1)
     319                output_dict[selection_path] = copy.deepcopy(selection)
    279320
    280321        for selection_path in output_dict:
    281322            selection = output_dict[selection_path]
     323            if (excluded_selection_pattern % selection) in exclusion_dict:
     324                print('Excluding selection:', selection_path)
     325                continue
    282326            try:
    283                 generate_selection(spec_json, config, selection, spec,
    284                                    html_template)
     327                test_filename = get_test_filename(spec_directory, spec_json,
     328                                                  selection)
     329                scenario = generate_selection(spec_json, selection)
     330                scenarios[test_filename] = scenarios.get(test_filename,
     331                                                         []) + [scenario]
    285332            except util.ShouldSkip:
    286333                continue
    287334
    288 
    289 def main(config):
     335    for filename in scenarios:
     336        generate_test_file(spec_directory, test_helper_filenames,
     337                           html_template, filename, scenarios[filename])
     338
     339
     340def merge_json(base, child):
     341    for key in child:
     342        if key not in base:
     343            base[key] = child[key]
     344            continue
     345        # `base[key]` and `child[key]` both exists.
     346        if isinstance(base[key], list) and isinstance(child[key], list):
     347            base[key].extend(child[key])
     348        elif isinstance(base[key], dict) and isinstance(child[key], dict):
     349            merge_json(base[key], child[key])
     350        else:
     351            base[key] = child[key]
     352
     353
     354def main():
    290355    parser = argparse.ArgumentParser(
    291356        description='Test suite generator utility')
     
    301366        '--spec',
    302367        type=str,
    303         default=None,
     368        default=os.getcwd(),
    304369        help='Specify a file used for describing and generating the tests')
    305370    # TODO(kristijanburnik): Add option for the spec_json file.
    306371    args = parser.parse_args()
    307372
    308     if args.spec:
    309         config.spec_directory = args.spec
    310 
    311     spec_filename = os.path.join(config.spec_directory, "spec.src.json")
    312     spec_json = util.load_spec_json(spec_filename)
     373    spec_directory = os.path.abspath(args.spec)
     374
     375    # Read `spec.src.json` files, starting from `spec_directory`, and
     376    # continuing to parent directories as long as `spec.src.json` exists.
     377    spec_filenames = []
     378    test_helper_filenames = []
     379    spec_src_directory = spec_directory
     380    while len(spec_src_directory) >= len(util.test_root_directory):
     381        spec_filename = os.path.join(spec_src_directory, "spec.src.json")
     382        if not os.path.exists(spec_filename):
     383            break
     384        spec_filenames.append(spec_filename)
     385        test_filename = os.path.join(spec_src_directory, 'generic',
     386                                     'test-case.sub.js')
     387        assert (os.path.exists(test_filename))
     388        test_helper_filenames.append(test_filename)
     389        spec_src_directory = os.path.abspath(
     390            os.path.join(spec_src_directory, ".."))
     391
     392    spec_filenames = list(reversed(spec_filenames))
     393    test_helper_filenames = list(reversed(test_helper_filenames))
     394
     395    if len(spec_filenames) == 0:
     396        print('Error: No spec.src.json is found at %s.' % spec_directory)
     397        return
     398
     399    # Load the default spec JSON file, ...
     400    default_spec_filename = os.path.join(util.script_directory,
     401                                         'spec.src.json')
     402    spec_json = collections.OrderedDict()
     403    if os.path.exists(default_spec_filename):
     404        spec_json = util.load_spec_json(default_spec_filename)
     405
     406    # ... and then make spec JSON files in subdirectories override the default.
     407    for spec_filename in spec_filenames:
     408        child_spec_json = util.load_spec_json(spec_filename)
     409        merge_json(spec_json, child_spec_json)
     410
    313411    spec_validator.assert_valid_spec_json(spec_json)
    314 
    315     generate_test_source_files(config, spec_json, args.target)
     412    generate_test_source_files(spec_directory, test_helper_filenames,
     413                               spec_json, args.target)
     414
     415
     416if __name__ == '__main__':
     417    main()
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/spec_validator.py

    r253683 r263621  
    8888    details['object'] = spec_json
    8989    assert_contains_only_fields(spec_json, [
     90        "selection_pattern", "test_file_path_pattern",
     91        "test_description_template", "test_page_title_template",
    9092        "specification", "delivery_key", "subresource_schema",
    9193        "source_context_schema", "source_context_list_schema",
     
    100102    excluded_tests = spec_json['excluded_tests']
    101103
    102     valid_test_expansion_fields = ['name'] + test_expansion_schema.keys()
     104    valid_test_expansion_fields = test_expansion_schema.keys()
    103105
    104106    # Should be consistent with `sourceContextMap` in
     
    106108    valid_source_context_names = [
    107109        "top", "iframe", "iframe-blank", "srcdoc", "worker-classic",
    108         "worker-module", "worker-classic-data", "worker-module-data"
     110        "worker-module", "worker-classic-data", "worker-module-data",
     111        "sharedworker-classic", "sharedworker-module",
     112        "sharedworker-classic-data", "sharedworker-module-data"
    109113    ]
    110114
     
    133137        # Validate required fields for a single spec.
    134138        assert_contains_only_fields(spec, [
    135             'name', 'title', 'description', 'specification_url',
    136             'test_expansion'
     139            'title', 'description', 'specification_url', 'test_expansion'
    137140        ])
    138         assert_non_empty_string(spec, 'name')
    139141        assert_non_empty_string(spec, 'title')
    140142        assert_non_empty_string(spec, 'description')
     
    142144        assert_non_empty_list(spec, 'test_expansion')
    143145
    144         # Validate spec's test expansion.
    145         used_spec_names = {}
    146 
    147146        for spec_exp in spec['test_expansion']:
    148147            details['object'] = spec_exp
    149             assert_non_empty_string(spec_exp, 'name')
    150             # The name is unique in same expansion group.
    151             assert_value_unique_in((spec_exp['expansion'], spec_exp['name']),
    152                                    used_spec_names)
    153148            assert_contains_only_fields(spec_exp, valid_test_expansion_fields)
    154149
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/template/disclaimer.template

    r245004 r263621  
    1 <!-- DO NOT EDIT! Generated by %(generating_script_filename)s using %(html_template_filename)s. -->
     1<!-- DO NOT EDIT! Generated by `%(generating_script_filename)s --spec %(spec_directory)s/` -->
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/template/test.debug.html.template

    r253683 r263621  
    33<html>
    44  <head>
    5     <title>%(test_page_title)s</title>
    6     <meta charset='utf-8'>
    7     <meta name="description" content="%(spec_description)s">
    8     <link rel="author" title="Kristijan Burnik" href="burnik@chromium.org">
    9     <link rel="help" href="%(spec_specification_url)s">
    10     <meta name="assert" content="%(test_description)s">%(meta_delivery_method)s
     5    <meta charset="utf-8">
     6    <meta name="timeout" content="long">%(meta_delivery_method)s
    117    <script src="/resources/testharness.js"></script>
    128    <script src="/resources/testharnessreport.js"></script>
     
    1612    <!-- Internal checking of the tests -->
    1713    <script src="%(sanity_checker_js)s"></script>
    18     <script src="%(helper_js)s"></script>
    19   </head>
     14%(helper_js)s  </head>
    2015  <body>
    2116    <script>
    2217      TestCase(
    23         %(scenario)s,
    24         document.querySelector("meta[name=assert]").content,
     18        [
     19          %(scenarios)s
     20        ],
    2521        new SanityChecker()
    2622      ).start();
    27       </script>
     23    </script>
    2824    <div id="log"></div>
    2925  </body>
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/template/test.release.html.template

    r253683 r263621  
    33<html>
    44  <head>
    5     <title>%(test_page_title)s</title>
    6     <meta charset='utf-8'>
    7     <meta name="description" content="%(spec_description)s">
    8     <link rel="author" title="Kristijan Burnik" href="burnik@chromium.org">
    9     <link rel="help" href="%(spec_specification_url)s">
    10     <meta name="assert" content="%(test_description)s">%(meta_delivery_method)s
     5    <meta charset="utf-8">
     6    <meta name="timeout" content="long">%(meta_delivery_method)s
    117    <script src="/resources/testharness.js"></script>
    128    <script src="/resources/testharnessreport.js"></script>
    139    <script src="/common/security-features/resources/common.sub.js"></script>
    14     <script src="%(helper_js)s"></script>
    15   </head>
     10%(helper_js)s  </head>
    1611  <body>
    1712    <script>
    1813      TestCase(
    19         %(scenario)s,
    20         document.querySelector("meta[name=assert]").content,
     14        [
     15          %(scenarios)s
     16        ],
    2117        new SanityChecker()
    2218      ).start();
    23       </script>
     19    </script>
    2420    <div id="log"></div>
    2521  </body>
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/util.py

    r253683 r263621  
    11from __future__ import print_function
    22
    3 import os, sys, json, re
     3import os, sys, json, json5, re
     4import collections
    45
    56script_directory = os.path.dirname(os.path.abspath(__file__))
     
    3132    with open(path_to_spec, "r") as f:
    3233        try:
    33             return json.load(f)
     34            return json5.load(f, object_pairs_hook=collections.OrderedDict)
    3435        except ValueError as ex:
    3536            print(ex.message)
     
    6768        self.key = key
    6869        self.value = value
     70
     71    def __eq__(self, other):
     72        return type(self) is type(other) and self.__dict__ == other.__dict__
    6973
    7074    @classmethod
     
    121125            policy_delivery = target_policy_delivery.get_another_policy(
    122126                supported_delivery_types[0])
    123         elif type(obj) == dict:
     127        elif isinstance(obj, dict):
    124128            policy_delivery = PolicyDelivery(obj['deliveryType'], obj['key'],
    125129                                             obj['value'])
     
    148152            else:
    149153                return PolicyDelivery(delivery_type, self.key, 'no-referrer')
     154        elif self.key == 'mixedContent':
     155            if self.value == 'opt-in':
     156                return PolicyDelivery(delivery_type, self.key, None)
     157            else:
     158                return PolicyDelivery(delivery_type, self.key, 'opt-in')
     159        elif self.key == 'upgradeInsecureRequests':
     160            if self.value == 'upgrade':
     161                return PolicyDelivery(delivery_type, self.key, None)
     162            else:
     163                return PolicyDelivery(delivery_type, self.key, 'upgrade')
    150164        else:
    151165            raise Exception('delivery key is invalid: ' + self.key)
     
    157171        self.source_context_type = source_context_type
    158172        self.policy_deliveries = policy_deliveries
     173
     174    def __eq__(self, other):
     175        return type(self) is type(other) and self.__dict__ == other.__dict__
    159176
    160177    @classmethod
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/w3c-import.log

    r253683 r263621  
    1717/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/format_spec_src_json.py
    1818/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/generate.py
     19/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/spec.src.json
    1920/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/spec_validator.py
    2021/LayoutTests/imported/w3c/web-platform-tests/common/security-features/tools/util.py
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/security-features/w3c-import.log

    r245004 r263621  
    1616List of files:
    1717/LayoutTests/imported/w3c/web-platform-tests/common/security-features/README.md
     18/LayoutTests/imported/w3c/web-platform-tests/common/security-features/types.md
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/stringifiers.js

    r179439 r263621  
    1 // Tests <http://heycam.github.io/webidl/#es-stringifier>.
     1/**
     2 * Runs tests for <http://heycam.github.io/webidl/#es-stringifier>.
     3 * @param {Object} aObject - object to test
     4 * @param {string} aAttribute - IDL attribute name that is annotated with `stringifier`
     5 * @param {boolean} aIsUnforgeable - whether the IDL attribute is `[Unforgeable]`
     6 */
    27function test_stringifier_attribute(aObject, aAttribute, aIsUnforgeable) {
    38  // Step 1.
    49  test(function() {
    510    [null, undefined].forEach(function(v) {
    6       assert_throws(new TypeError(), function() {
     11      assert_throws_js(TypeError, function() {
    712        aObject.toString.call(v);
    813      });
     
    1621    assert_false("Window" in window && aObject instanceof window.Window);
    1722    [{}, window].forEach(function(v) {
    18       assert_throws(new TypeError(), function() {
     23      assert_throws_js(TypeError, function() {
    1924        aObject.toString.call(v)
    2025      });
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/subset-tests-by-key.js

    r239693 r263621  
    1 // Only test a subset of tests with ?include=Foo or ?exclude=Foo in the URL.
    2 // Can be used together with <meta name="variant" content="...">
    3 // Sample usage:
    4 // for (const test of tests) {
    5 //   subsetTestByKey("Foo", async_test, test.fn, test.name);
    6 // }
    71(function() {
    82  var subTestKeyPattern = null;
     
    4943    }
    5044  }
     45  /**
     46   * Check if `key` is in the subset specified in the URL.
     47   * @param {string} key
     48   * @returns {boolean}
     49   */
    5150  function shouldRunSubTest(key) {
    5251    if (key && subTestKeyPattern) {
     
    5958    return true;
    6059  }
    61   function subsetTestByKey(key, testFunc, ...args) {
     60  /**
     61   * Only test a subset of tests with `?include=Foo` or `?exclude=Foo` in the URL.
     62   * Can be used together with `<meta name="variant" content="...">`
     63   * Sample usage:
     64   * for (const test of tests) {
     65   *   subsetTestByKey("Foo", async_test, test.fn, test.name);
     66   * }
     67   */
     68   function subsetTestByKey(key, testFunc, ...args) {
    6269    if (collectKeys) {
    6370      if (collectCounts && key in keys) {
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/subset-tests.js

    r232903 r263621  
    1 // Only test a subset of tests with, e.g., ?1-10 in the URL.
    2 // Can be used together with <meta name="variant" content="...">
    3 // Sample usage:
    4 // for (const test of tests) {
    5 //   subsetTest(async_test, test.fn, test.name);
    6 // }
    71(function() {
    82  var subTestStart = 0;
     
    3832    }
    3933  }
     34  /**
     35   * Check if `currentSubTest` is in the subset specified in the URL.
     36   * @param {number} currentSubTest
     37   * @returns {boolean}
     38   */
    4039  function shouldRunSubTest(currentSubTest) {
    4140    return currentSubTest >= subTestStart && currentSubTest <= subTestEnd;
    4241  }
    4342  var currentSubTest = 0;
     43  /**
     44   * Only test a subset of tests with, e.g., `?1-10` in the URL.
     45   * Can be used together with `<meta name="variant" content="...">`
     46   * Sample usage:
     47   * for (const test of tests) {
     48   *   subsetTest(async_test, test.fn, test.name);
     49   * }
     50   */
    4451  function subsetTest(testFunc, ...args) {
    4552    currentSubTest++;
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/test-setting-immutable-prototype.js

    r214135 r263621  
    11self.testSettingImmutablePrototypeToNewValueOnly =
    2   (prefix, target, newValue, newValueString, { isSameOriginDomain }) => {
     2  (prefix, target, newValue, newValueString, { isSameOriginDomain },
     3   targetGlobal = window) => {
    34  test(() => {
    4     assert_throws(new TypeError, () => {
     5    assert_throws_js(TypeError, () => {
    56      Object.setPrototypeOf(target, newValue);
    67    });
     
    1011  let dunderProtoErrorName = "\"SecurityError\" DOMException";
    1112  if (isSameOriginDomain) {
    12     dunderProtoError = new TypeError();
     13    // We're going to end up calling the __proto__ setter, which will
     14    // enter the Realm of targetGlobal before throwing.
     15    dunderProtoError = targetGlobal.TypeError;
    1316    dunderProtoErrorName = "TypeError";
    1417  }
    1518
    1619  test(() => {
    17     assert_throws(dunderProtoError, function() {
     20    const func = function() {
    1821      target.__proto__ = newValue;
    19     });
     22    };
     23    if (isSameOriginDomain) {
     24      assert_throws_js(dunderProtoError, func);
     25    } else {
     26      assert_throws_dom(dunderProtoError, func);
     27    }
    2028  }, `${prefix}: setting the prototype to ${newValueString} via __proto__ should throw a ${dunderProtoErrorName}`);
    2129
     
    2634
    2735self.testSettingImmutablePrototype =
    28   (prefix, target, originalValue, { isSameOriginDomain }, newValue = {}, newValueString = "an empty object") => {
    29   testSettingImmutablePrototypeToNewValueOnly(prefix, target, newValue, newValueString, { isSameOriginDomain });
     36  (prefix, target, originalValue, { isSameOriginDomain }, targetGlobal = window) => {
     37  const newValue = {};
     38  const newValueString = "an empty object";
     39  testSettingImmutablePrototypeToNewValueOnly(prefix, target, newValue, newValueString, { isSameOriginDomain }, targetGlobal);
    3040
    3141  const originalValueString = originalValue === null ? "null" : "its original value";
     
    4555  } else {
    4656    test(() => {
    47       assert_throws("SecurityError", function() {
     57      assert_throws_dom("SecurityError", function() {
    4858        target.__proto__ = newValue;
    4959      });
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/utils.js

    r179439 r263621  
     1/**
     2 * Create an absolute URL from `options` and defaulting unspecified properties to `window.location`.
     3 * @param {Object} options - a `Location`-like object
     4 * @param {string} options.hostname
     5 * @param {string} options.subdomain - prepend subdomain to the hostname
     6 * @param {string} options.port
     7 * @param {string} options.path
     8 * @param {string} options.query
     9 * @param {string} options.hash
     10 * @returns {string}
     11 */
    112function make_absolute_url(options) {
    213    var loc = window.location;
     
    4253}
    4354
     55/** @private */
    4456function get(obj, name, default_val) {
    4557    if (obj.hasOwnProperty(name)) {
     
    4961}
    5062
     63/**
     64 * Generate a new UUID.
     65 * @returns {string}
     66 */
    5167function token() {
    5268    var uuid = [to_hex(rand_int(32), 8),
     
    5874}
    5975
     76/** @private */
    6077function rand_int(bits) {
    6178    if (bits < 1 || bits > 53) {
     
    7289}
    7390
     91/** @private */
    7492function to_hex(x, length) {
    7593    var rv = x.toString(16);
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/w3c-import.log

    r253683 r263621  
    3939/LayoutTests/imported/w3c/web-platform-tests/common/reftest-wait.js
    4040/LayoutTests/imported/w3c/web-platform-tests/common/reftest-wait.js.headers
     41/LayoutTests/imported/w3c/web-platform-tests/common/rendering-utils.js
     42/LayoutTests/imported/w3c/web-platform-tests/common/sab.js
    4143/LayoutTests/imported/w3c/web-platform-tests/common/slow.py
    4244/LayoutTests/imported/w3c/web-platform-tests/common/stringifiers.js
  • trunk/LayoutTests/imported/w3c/web-platform-tests/common/worklet-reftest.js

    r245004 r263621  
    1 // Imports code into a worklet. E.g.
    2 //
    3 // importWorklet(CSS.paintWorklet, {url: 'script.js'});
    4 // importWorklet(CSS.paintWorklet, '/* javascript string */');
     1/**
     2 * Imports code into a worklet. E.g.
     3 *
     4 * importWorklet(CSS.paintWorklet, {url: 'script.js'});
     5 * importWorklet(CSS.paintWorklet, '(javascript string)');
     6 *
     7 * @param {Worklet} worklet
     8 * @param {(Object|string)} code
     9 */
    510function importWorklet(worklet, code) {
    611    let url;
     
    1520}
    1621
     22/** @private */
    1723async function animationFrames(frames) {
    1824  for (let i = 0; i < frames; i++)
     
    2026}
    2127
     28/** @private */
    2229async function workletPainted() {
    2330    await animationFrames(2);
    2431}
    2532
    26 // To make sure that we take the snapshot at the right time, we do double
    27 // requestAnimationFrame. In the second frame, we take a screenshot, that makes
    28 // sure that we already have a full frame.
     33/**
     34 * To make sure that we take the snapshot at the right time, we do double
     35 * requestAnimationFrame. In the second frame, we take a screenshot, that makes
     36 * sure that we already have a full frame.
     37 *
     38 * @param {Worklet} worklet
     39 * @param {(Object|string)} code
     40 */
    2941async function importWorkletAndTerminateTestAfterAsyncPaint(worklet, code) {
    3042    if (typeof worklet === 'undefined') {
  • trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/history/the-location-interface/location-prototype-setting-same-origin-domain.sub-expected.txt

    r259900 r263621  
    44FAIL Same-origin-domain prerequisite check: the original prototype is accessible assert_not_equals: got disallowed value null
    55PASS Same-origin-domain: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError
    6 FAIL Same-origin-domain: setting the prototype to an empty object via __proto__ should throw a TypeError assert_throws: function "function () {
     6FAIL Same-origin-domain: setting the prototype to an empty object via __proto__ should throw a TypeError assert_throws_js: function "function () {
    77      target.__proto__ = newValue;
    8     }" threw object "SecurityError: Blocked a frame with origin "http://localhost:8800" from accessing a cross-origin frame. Protocols, domains, and ports must match." ("SecurityError") expected object "TypeError" ("TypeError")
     8    }" threw object "SecurityError: Blocked a frame with origin "http://localhost:8800" from accessing a cross-origin frame. Protocols, domains, and ports must match." ("SecurityError") expected instance of function "function TypeError() {
     9    [native code]
     10}" ("TypeError")
    911PASS Same-origin-domain: setting the prototype to an empty object via Reflect.setPrototypeOf should return false
    1012PASS Same-origin-domain: the prototype must still be null
  • trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub-expected.txt

    r259900 r263621  
    44FAIL Same-origin-domain prerequisite check: the original prototype is accessible assert_not_equals: got disallowed value null
    55PASS Same-origin-domain: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError
    6 FAIL Same-origin-domain: setting the prototype to an empty object via __proto__ should throw a TypeError assert_throws: function "function () {
     6FAIL Same-origin-domain: setting the prototype to an empty object via __proto__ should throw a TypeError assert_throws_js: function "function () {
    77      target.__proto__ = newValue;
    8     }" threw object "SecurityError: Blocked a frame with origin "http://localhost:8800" from accessing a cross-origin frame. Protocols, domains, and ports must match." ("SecurityError") expected object "TypeError" ("TypeError")
     8    }" threw object "SecurityError: Blocked a frame with origin "http://localhost:8800" from accessing a cross-origin frame. Protocols, domains, and ports must match." ("SecurityError") expected instance of function "function TypeError() {
     9    [native code]
     10}" ("TypeError")
    911PASS Same-origin-domain: setting the prototype to an empty object via Reflect.setPrototypeOf should return false
    1012PASS Same-origin-domain: the prototype must still be null
Note: See TracChangeset for help on using the changeset viewer.