From 2b30be424f8c485e57b2ce456288589ffc1a96b6 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 23 May 2023 12:56:32 +0000 Subject: [PATCH] [FIX] core: content tab discovery in headful mode It's not clear when and how this happened but apparently "headful" chrome has a built-in background_page for hangouts which appears before the `about:blank` page in the list of targets, and possibly appears before the `about:blank` page has opened at all. odoo/odoo#111422 was tested with chromium which apparently doesn't have this feature either (or does it?), which probably contributes to having no idea when it appears. This feature also doesn't respond to `--disable-extensions`, despite its url marking it as one: chrome-extension://nkeimhogjdpnpccoofpliimaahmaaome/background.html The result was that the tour runner would hook onto the hangouts target and try to load pages, which it would reject with `net::ERR_ABORTED`, hence the tours just getting stuck. Fix by improving the heuristic to find a content page: look for a target of type `page`, and with the url `about:blank`, rather than just take whichever tab target is listed first. Requires modifying `stop` as it can now be called after we've started the browser, but before we've created the websocket connection. Also move `--no-first-run` from the headless to the default switches to avoid Chrome's migration & default browser popup, apparently it doesn't cause Chromium grief anymore (???). If this turns out to be a concern, add a condition on the `executable` or something. closes odoo/odoo#122117 Signed-off-by: Xavier Morel (xmo) --- odoo/tests/common.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/odoo/tests/common.py b/odoo/tests/common.py index 1c046c45dce..6d38c8dab00 100644 --- a/odoo/tests/common.py +++ b/odoo/tests/common.py @@ -907,8 +907,7 @@ class ChromeBrowser: os._exit(0) def stop(self): - # only cleanup chrome if it was started before SIGXCPU triggered - if self.chrome: + if hasattr(self, 'ws'): self._websocket_send('Page.stopScreencast') if self.screencasts_dir and os.path.isdir(self.screencasts_frames_dir): shutil.rmtree(self.screencasts_frames_dir) @@ -928,6 +927,7 @@ class ChromeBrowser: self._websocket_send('Browser.close') self._logger.info("Closing websocket connection") self.ws.close() + if self.chrome: self._logger.info("Terminating chrome headless with pid %s", self.chrome.pid) self.chrome.terminate() @@ -986,7 +986,6 @@ class ChromeBrowser: '--disable-dev-shm-usage': '', '--disable-namespace-sandbox': '', '--disable-translate': '', - '--no-first-run': '', '--no-sandbox': '', '--disable-gpu': '', } @@ -1000,6 +999,7 @@ class ChromeBrowser: '--remote-debugging-port': str(self.remote_debugging_port), '--user-data-dir': user_data_dir, '--window-size': window_size, + '--no-first-run': '', # '--enable-precise-memory-info': '', # uncomment to debug memory leaks in qunit suite # '--js-flags': '--expose-gc', # uncomment to debug memory leaks in qunit suite } @@ -1023,7 +1023,7 @@ class ChromeBrowser: return proc, devtools_port - def _json_command(self, command, timeout=3, get_key=None): + def _json_command(self, command, timeout=3): """Queries browser state using JSON Available commands: @@ -1056,15 +1056,10 @@ class ChromeBrowser: except ProcessLookupError: message = 'Chrome crashed at startup' break - res = None try: r = requests.get(url, timeout=3) if r.ok: - res = r.json() - if get_key is None: - return res - else: - return res[get_key] + return r.json() except requests.ConnectionError as e: failure_info = str(e) message = 'Connection Error while trying to connect to Chrome debugger' @@ -1072,8 +1067,7 @@ class ChromeBrowser: failure_info = str(e) message = 'Connection Timeout while trying to connect to Chrome debugger' break - except (KeyError, IndexError): - message = 'Key "%s" not found in json result "%s" after connecting to Chrome debugger' % (get_key, res) + time.sleep(delay) timeout -= delay delay = delay * 1.5 @@ -1087,8 +1081,23 @@ class ChromeBrowser: def _open_websocket(self): version = self._json_command('version') self._logger.info('Browser version: %s', version['Browser']) - infos = self._json_command('', get_key=0) # Infos about the first tab - ws_url = infos['webSocketDebuggerUrl'] + + start = time.time() + while (time.time() - start) < 5.0: + ws_url = next(( + target['webSocketDebuggerUrl'] + for target in self._json_command('') + if target['type'] == 'page' + if target['url'] == 'about:blank' + ), None) + if ws_url: + break + + time.sleep(0.1) + else: + self.stop() + raise unittest.SkipTest("Error during Chrome connection: never found 'page' target") + self._logger.info('Websocket url found: %s', ws_url) ws = websocket.create_connection(ws_url, enable_multithread=True, suppress_origin=True) if ws.getstatus() != 101: