Juanjo Tugores cf40f73057 Fix unexpected ViewFocus events when Text Editing utilities change focus in the middle of a blur call. (flutter/engine#54965)
In [some cases][1], text editing utilities re-focus the `<input />` element during a blur event. This causes an unusual sequence of `focusin` and `focusout` events, leading to the engine sending unintended events.

Consider the following HTML code:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div id="container">
    <input type="" value="1" id="input1">
    <input type="" value="2" id="input2">
    <input type="" value="3" id="input3">
  </div>

  <script>
    container.addEventListener('focusin', (ev) => {
      console.log('focusin: focus was gained by', ev.target);
    });
    container.addEventListener('focusout', (ev) => {
      console.log('focusout: focus is leaving', ev.target, 'and it will go to', ev.relatedTarget);
    });
  </script>
</body>
</html>
```

Clicking input1, then input2, then input3 produces the following console logs:

```
// Input1 is clicked
focusin: focus was gained by <input type value=​"1" id=​"input1">​

// Input2 is clicked
focusout: focus is leaving <input type value=​"1" id=​"input1">​ and it will go to <input type value=​"2" id=​"input2">​
focusin: focus was gained by <input type value=​"2" id=​"input2">​

// Input3 is clicked
focusout: focus is leaving <input type value=​"2" id=​"input2">​ and it will go to <input type value=​"3" id=​"input3">​
focusin: focus was gained by <input type value=​"3" id=​"input3">​
```

Now, let's add a blur handler that changes focus:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div id="container">
    <input type="" value="1" id="input1">
    <input type="" value="2" id="input2">
    <input type="" value="3" id="input3">
  </div>

  <script>
    container.addEventListener('focusin', (ev) => {
      console.log('focusin: focus was gained by', ev.target);
    });
    container.addEventListener('focusout', (ev) => {
      console.log('focusout: focus is leaving', ev.target, 'and it will go to', ev.relatedTarget);
    });
    input2.addEventListener('blur', (ev) => {
      input2.focus();
    });
  </script>
</body>
</html>
```

The log sequence changes and gives the wrong impression that no dom element has focus:

```
// Input1 is clicked
focusin: focus was gained by <input type value=​"1" id=​"input1">​

// Input2 is clicked
focusout: focus is leaving <input type value=​"1" id=​"input1">​ and it will go to <input type value=​"2" id=​"input2">​
focusin: focus was gained by <input type value=​"2" id=​"input2">​

// Input3 is clicked, but the handler kicks in and instead of the following line being a focusout, it results in a focusin call first.
focusin: focus was gained by <input type value=​"2" id=​"input2">​
focusout: focus is leaving <input type value=​"2" id=​"input2">​ and it will go to null
```

In addition to that, during `focusout` processing, `activeElement` typically points to `<body />`. However, if an element is focused during a `blur` event, `activeElement` points to that focused element.  Although, undocumented it can be verified with:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <div id="container">
    <input type="" value="1" id="input1">
    <input type="" value="2" id="input2">
    <input type="" value="3" id="input3">
  </div>

  <script>
    container.addEventListener('focusin', (ev) => {
      console.log('focusin: was gained by', ev.target);
    });
    container.addEventListener('focusout', (ev) => {
      console.log('document.hasFocus()', document.hasFocus());     
      console.log('document.activeElement', document.activeElement);
      console.log('focusout: focus is leaving', ev.target, 'and it will go to', ev.relatedTarget);
    });
    input2.addEventListener('blur', (ev) => {
      input2.focus();
    });
  </script>
</body>
</html>
```

We leverage these behaviors to ignore `focusout` events when the document has focus but `activeElement` is not `<body />`.

https://github.com/flutter/flutter/issues/153022

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
2024-09-05 18:23:55 +00:00
..

...............................................................................

Flutter Web Engine Test Suites

The flutter engine unit tests can be run with a number of different configuration options that affect both compile time and run time. The permutations of these options are specified in the felt_config.yaml file that is colocated with this README. Here is an overview of the way the test suite configurations are structured:

compile-configs

Specifies how the tests should be compiled. Each compile config specifies the following:

  • name - The name of the compile configuration.
  • compiler - What compiler is used to compile the tests. Currently we support dart2js and dart2wasm as values.
  • renderer - Which renderer to use when compiling the tests. Currently we support html, canvaskit, and skwasm.

test-sets

A group of files that contain unit tests. Each test set specifies the following:

  • name - The name of the test set.
  • directory - The name of the directory under flutter/lib/web_ui/test that contains all the test files.

test-bundles

Specifies a group of tests and a compile configuration of those tests. The output of the test bundles appears in flutter/lib/web_ui/build/test_bundles/<name> where <name> is replaced by the name of the bundle. Each test bundle may be used by multiple test suites. Each test bundle specifies the following:

  • name - The name of the test bundle.
  • test-set - The name of the test set that contains the tests to be compiled.
  • compile-config - The name of the compile configuration to use.

run-configs

Specifies the test environment that should be provided to a unit test. Each run config specifies the following:

  • name - Name of the run configuration.
  • browser - The browser with which to run the tests. Valid values for this are chrome, firefox, safari or edge.
  • canvaskit-variant - An optionally supplied argument that forces the tests to use a particular variant of CanvasKit, either full or chromium. If none is specified, the engine will select the variant based on its normal selection logic.

test-suites

This is a fully specified run of a group of unit tests. They specify the following:

  • name - Name of the test suite.
  • test-bundle - Which compiled test bundle to use when running the suite.
  • run-config - Which run configuration to use when runnin the tests.
  • artifact-deps - Which gn/ninja build artifacts are needed to run the suite. Valid values are canvaskit, canvaskit_chromium or skwasm.