Окно Chrome рисует устаревшую поверхность (NTP/предыдущая страница) поверх реальной страницы навигации после того, как пPython

Программы на Python
Ответить
Anonymous
 Окно Chrome рисует устаревшую поверхность (NTP/предыдущая страница) поверх реальной страницы навигации после того, как п

Сообщение Anonymous »

Я использую настоящий Chrome с nodriver, используя копию одного из моих профилей пользователей Chrome. Обычно это работает. Но если я нажимаю Ctrl+C на выполнение сценария, следующий запуск часто приводит к нарушению визуального состояния в macOS:
  • Видимое окно Chrome остается нарисованным на странице новой вкладки (или на ранее показанной странице).
  • DOM, который я читаю через CDP, является правильной страницей навигации — document.title, document.URL, запросы элементов, клики, заполнение форм, снимки экрана (*) — все это отражает реальную страницу, по которой осуществляется навигация, и автоматизация продолжает работать комплексно.
  • Если я нажму на омнибокс, фактический пользовательский интерфейс страницы "выпадет" из панели поиска (реальная страница на мгновение станет видимой под раскрывающимся списком) или средство визуализации выйдет из строя и вкладка закроется.
(*) Page.captureScreenshot фиксирует реальную страницу, а не то, что видно на экране.
Ручной запуск Chrome с использованием того же профиля с последующим чистым выходом устраняет ошибку до следующего принудительного завершения сценария. Таким образом, неработающее состояние — это то, что нечистый выход оставляет в профиле или в каком-либо состоянии Chrome на уровне macOS.
Настройка
  • последняя версия macOS
  • Google Chrome (текущая стабильная версия)
  • Python 3.12
  • Код: Выделить всё

    nodriver
    (текущий)
При каждом запуске я копирую настоящий каталог пользовательских данных Chrome в новый каталог tempfile.mkdtemp(...) и запускаю Chrome для копии с --profile-directory=Profile 4, поэтому исходный профиль никогда не записывается.
Минимальный воспроизводимый пример

Код: Выделить всё

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 несколько раз.
Примерно 30–60% запусков после уничтожения заканчиваются тем, что видимое окно зависает на устаревшем содержимом (NTP или ранее обработанной странице), в то время как CDP говорит title = Пример домена | url = https://example.org/ печатается правильно.
Вещи, которые я пробовал, которые не исправляют
  • Удаление большего количества файлов во время копирования временного каталога, поэтому ничего из предыдущего нечистого запуска не сохраняется. В ignore_patterns(...) я добавил:
  • Перезапись скопированных настроек, чтобы они выглядели как чистый предыдущий выход, чтобы 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 (

    Код: Выделить всё

    browser.get(url, new_tab=True)
    , который вызывает Target.createTarget под капотом).
  • Передача --disable-session-crashed-bubble (также передается по умолчанию nodriver).
Ни один из них не меняет поведение. Ошибка по-прежнему срабатывает стохастически после нажатия Ctrl+C.
Что я спрашиваю
Какое состояние 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, и в этом случае в чем проблема/обходной путь?
Любой указатель на фактическую первопричину будет очень признателен — каждый «очевидный» кандидат, о котором я могу думать (восстановление сеанса, кеш графического процессора, кеш шейдеров, пузырь сбоя, замена рендерера), не является источником.
Когда браузер находится в этом состоянии с ошибкой и я нажимаю на панель поиска, я вижу это:
Изображение
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»