- Видимое окно Chrome остается нарисованным на странице новой вкладки (или на ранее показанной странице).
- DOM, который я читаю через CDP, является правильной страницей навигации — document.title, document.URL, запросы элементов, клики, заполнение форм, снимки экрана (*) — все это отражает реальную страницу, по которой осуществляется навигация, и автоматизация продолжает работать комплексно.
- Если я нажму на омнибокс, фактический пользовательский интерфейс страницы "выпадет" из панели поиска (реальная страница на мгновение станет видимой под раскрывающимся списком) или средство визуализации выйдет из строя и вкладка закроется.
Ручной запуск Chrome с использованием того же профиля с последующим чистым выходом устраняет ошибку до следующего принудительного завершения сценария. Таким образом, неработающее состояние — это то, что нечистый выход оставляет в профиле или в каком-либо состоянии Chrome на уровне macOS.
Настройка
- последняя версия macOS
- Google Chrome (текущая стабильная версия)
- Python 3.12
- (текущий)
Код: Выделить всё
nodriver
Минимальный воспроизводимый пример
Код: Выделить всё
import asyncio
import shutil
import tempfile
from pathlib import Path
import nodriver as nd
REAL_BASE = Path("/Users/me/Library/Application Support/Google/Chrome")
PROFILE = "Profile 4"
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
def prepare_temp_profile() -> Path:
"""Copy the real profile to a fresh temp dir (matches what my real code does)."""
tmp = Path(tempfile.mkdtemp(prefix="repro-chrome-"))
if (REAL_BASE / "Local State").exists():
shutil.copy2(REAL_BASE / "Local State", tmp / "Local State")
shutil.copytree(
REAL_BASE / PROFILE,
tmp / PROFILE,
dirs_exist_ok=True,
ignore_dangling_symlinks=True,
ignore=shutil.ignore_patterns("RunningChromeVersion"),
)
return tmp
async def main():
user_data_dir = prepare_temp_profile()
config = nd.Config(
headless=False,
browser_executable_path=CHROME,
user_data_dir=str(user_data_dir),
browser_args=[
f"--profile-directory={PROFILE}",
"--window-size=1366,768",
],
)
browser = await nd.start(config=config)
tab = await browser.get("https://example.org")
title = await tab.evaluate("document.title")
url = await tab.evaluate("document.URL")
print("CDP says title =", title, "| url =", url)
# Keep window open so you can compare the painted pixels to the CDP truth.
await asyncio.sleep(120)
if __name__ == "__main__":
asyncio.run(main())
- Запустите сценарий. Убедитесь, что в окне отображается example.org и напечатанный заголовок соответствует.
- Нажмите Ctrl+C, пока окно открыто.
- Запустите сценарий еще раз. Повторите шаги 2–3 несколько раз.
Вещи, которые я пробовал, которые не исправляют
- Удаление большего количества файлов во время копирования временного каталога, поэтому ничего из предыдущего нечистого запуска не сохраняется. В ignore_patterns(...) я добавил:
Код: Выделить всё
Singleton*- , текущий сеанс, текущие вкладки, последний сеанс, последние вкладки
Код: Выделить всё
Sessions - , ShaderCache, GrShaderCache, DawnGraphiteCache, DawnWebGPUCache
Код: Выделить всё
GPUCache - , Crashpad Metrics-active.pma, Crashpad Metrics-spare.pma, Отчеты о сбоях
Код: Выделить всё
Crashpad Код: Выделить всё
lockfile
- Перезапись скопированных настроек, чтобы они выглядели как чистый предыдущий выход, чтобы Chrome не входил в аварийное восстановление:
Код: Выделить всё
prefs["profile"]["exit_type"] = "Normal" prefs["profile"]["exited_cleanly"] = True prefs["session"]["restore_on_startup"] = 5 # open NTP prefs["session"]["startup_urls"] = [] - Перезапись скопированного локального состояния таким же образом:
Код: Выделить всё
ls["user_experience_metrics"]["stability"]["exited_cleanly"] = True ls["profile"]["info_cache"]["Profile 4"]["exited_cleanly"] = True - Пропуск собственного окна запуска Chrome, чтобы первая навигация не была переключением средства рендеринга с вкладки NTP на целевой URL. Я запустил Chrome с --no-startup-window и попросил nodriver создать первую вкладку через CDP (, который вызывает Target.createTarget под капотом).
Код: Выделить всё
browser.get(url, new_tab=True) - Передача --disable-session-crashed-bubble (также передается по умолчанию nodriver).
Что я спрашиваю
Какое состояние Chrome/Chromium — помимо флагов выхода профиля JSON, больших двоичных объектов Sessions/, кэшей графического процессора и шейдеров, а также пути подкачки рендеринга — необходимо сбросить для восстановления композитора после нечистого выхода в macOS?
- Существует ли флаг запуска Chrome, который отключает любое кэшированное состояние уровня macOS, которое Chrome повторно использует при запусках с одним и тем же каталогом пользовательских данных (например, что-либо в ~/Library/Saved Application State/com.google.Chrome.savedState/ или кэшах Core Animation/IOSurface)?
- Есть ли команда CDP, которую я могу выполнить сразу после nd.start(), которая принудительно видимое окно для воссоздания поверхности компоновщика с нуля (что-то эквивалентное отсоединению + воссозданию, реальному изменению размера Browser.setWindowBounds, циклу Emulation.setDeviceMetricsOverride и т. д.)?
- Или это известная ошибка Chromium, и в этом случае в чем проблема/обходной путь?
Когда браузер находится в этом состоянии с ошибкой и я нажимаю на панель поиска, я вижу это:
