Open Bug 1727749 Opened 3 years ago Updated 3 years ago

Using simulateMouse after BrowserTestUtils.browserLoaded resolved fails intermittently on some platforms

Categories

(Testing :: Mochitest, defect, P3)

Default
defect

Tracking

(Not tracked)

People

(Reporter: jdescottes, Unassigned)

References

Details

After recent platform changes (eg devtools moved to linux webrender, or browser chrome mochitests moved to azure) we start having intermittents for very basic scenarios.

Summary

The issue usually goes like this:

  • open or load a tab
  • use BrowserTestUtils.browserLoaded to wait for the page to be loaded
  • try to simulate an event on the page (click on a button for instance)

The event simulation can silently fail and the test will timeout.

This seems to be very platform/performance dependant (see https://bugzilla.mozilla.org/show_bug.cgi?id=1717627#c13), but it feels like we are either missing the proper event to wait for in BrowserTestUtils.browserLoaded, or that there is some async gap that we need to fix on platform side.

This has been causing a lot of intermittent failures lately and it would be great to find a common solution to this issue rather than moving tests to faster platforms or adding workarounds in each failing test.

Isolated test example

The following try push illustrates the problem: https://treeherder.mozilla.org/jobs?repo=try&revision=2f131baf038da87cb1a5995c53ce5b19b395ad0c

It contains a simplistic test which simply loads a tab with a content page, which contains a button with an onclick handler. We wait for the page load using browserLoaded and then proceed to simulate a click using BrowserTestUtils.simulateMouse.

The test script is:

/* Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const URL = "http://example.com/browser/devtools/tests/browser/test_page.html";

add_task(async function() {
  const { gBrowser } = window;

  const tab = BrowserTestUtils.addTab(gBrowser, URL);
  gBrowser.selectedTab = tab;

  await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, URL);
  BrowserTestUtils.synthesizeMouse("button", 0, 0, {}, tab.linkedBrowser);

  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
    const doc = content.document;
    await ContentTaskUtils.waitForCondition(
      () => doc.querySelector("div").textContent.includes("CLICKED"),
      "DIV text was updated"
    );
  });

  ok(true, "successfully clicked on the button");
  BrowserTestUtils.removeTab(tab);
});

The test page is

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Test page</title>
</head>
<body>
  <button onclick="document.querySelector('div').innerHTML = 'CLICKED'">click</button>
  <div>WAITING</div>
</body>
</html>

The test suite only contains this test, and we still get timeouts.

Maybe there is another event we should wait for in browserLoaded to be sure that we can interact with the page? It seems that a test as simple as that should reliably pass all the time.

See Also: → 1727170, 1722136
See Also: → 1721938
See Also: → 1720248

This might simply be a duplicate of Bug 1720248

I could reproduce the test failure locally but only with opt builds.

Anyway, I did track down what's going on there. When we handle the mouse event in question, we try to find an nsIFrame on where the mouse event is happening, which is nsLayoutUtils::GetFramesForArea. The GetFramesForArea tries to build display items for hit testing, but unfortunately when the test fails, the pres context's mVisibleArea is still empty, thus we fail to find the correct target element. The reason why the mVisibleArea is empty is that, though the rect comes from nsSubDocumentFrame::ReflowFinished in the browser parent process, the reflow sometimes takes more time than the time we have finished loading the content in the content process and actually we have tried to paint the content (but with the empty visible area we paint nothing that what I've been observing in bug 1720248).

There are a couple of ways I can think of to solve this race, here is a try run with one of the ways which is defer sending mouse events until the window size is not empty;
https://treeherder.mozilla.org/jobs?repo=try&revision=37f9dca312c6c5584cea444a1f7d2a8824455348&selectedTaskRun=Sj18bjwbQeiEMAz0jMSgQA.1

CCing masayuki.

See Also: → 1716411

Adding a suggestion shared by :hiro on Matrix:

diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -412,6 +412,12 @@ var addTab = async function(url, options
     await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
     // Waiting for presShell helps with test timeouts in webrender platforms.
     await waitForPresShell(tab.linkedBrowser);
+    await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+      await ContentTaskUtils.waitForCondition(() => {
+        return this.content.window.innerHeight !=0 &&
+               this.content.window.innerWidth !=0;
+      });
+    });
     info("Tab added and finished loading");
   } else {
     info("Tab added");

Note that this fix could probably be directly added to browserLoaded, and then we could even remove the await waitForPresShell(tab.linkedBrowser); initially added in DevTools

Severity: -- → S4
Priority: -- → P3
You need to log in before you can comment on or make changes to this bug.