TL;DR: API расширений был обновлен для поддержки обратного/прямого кэша и предварительной загрузки навигации. Подробности см. ниже.
Chrome усердно работает над тем, чтобы сделать навигацию быстрой. Технологии мгновенной навигации, такие как кэш назад/вперед ( поставляется в версии Chrome 96 для настольных компьютеров) и правила спекуляций ( поставляются в Chrome 103), улучшают возможности возврата как назад, так и вперед. В этом посте мы рассмотрим обновления, которые мы внесли в API расширений браузера для обеспечения соответствия этим новым рабочим процессам.
Понимание типов страниц
До появления обратного/прямого кэша и предварительной отрисовки на отдельной вкладке была только одна активная страница. Это всегда было то, что было видно. Если пользователь возвращается на предыдущую страницу, активная страница будет уничтожена (страница B), а предыдущая страница в истории будет полностью восстановлена (страница A). Расширениям не нужно было беспокоиться о том, в какой части жизненного цикла находятся страницы, поскольку для вкладки было только одно активное/видимое состояние.
![Выселение активной страницы](https://developer.chrome.google.cn/static/blog/extension-instantnav/image/eviction-the-active-page-28eaaebd9549a.png?authuser=5&hl=ru)
Благодаря обратному/прямому кэшированию и предварительному рендерингу между вкладками и страницами больше не существует связи один к одному. Теперь каждая вкладка фактически хранит несколько страниц, и страницы переходят между состояниями, а не уничтожаются и реконструируются.
Например, страница может начать свою жизнь как предварительно обработанная (невидимая) страница, перейти в активную (видимую) страницу, когда пользователь щелкнет ссылку, а затем сохраниться в кэше возврата/пересылки (невидимом), когда пользователь переходит на другую страницу, и все это без уничтожения страницы. Позже в этой статье мы рассмотрим новые свойства, которые помогут расширениям понять, в каком состоянии находятся страницы.
![Типы страниц](https://developer.chrome.google.cn/static/blog/extension-instantnav/image/types-pages-5bed5a04836ba.png?authuser=5&hl=ru)
Обратите внимание, что вкладка может содержать серию предварительно обработанных страниц (а не только одну), одну активную (видимую) страницу и серию кэшированных страниц «Назад/Вперед».
Что изменится для разработчиков расширений?
Идентификатор кадра == 0
В Chromium мы называем самый верхний/основной фрейм самым внешним фреймом.
У авторов расширений, которые предполагают, что идентификатор самого внешнего кадра равен 0 (предыдущая передовая практика), могут возникнуть проблемы. Поскольку вкладка теперь может иметь несколько внешних фреймов (предварительно обработанных и кэшированных страниц), предположение о том, что для вкладки существует один внешний фрейм, неверно. frameId == 0
по-прежнему будет представлять самый внешний кадр активной страницы, но самые внешние кадры других страниц на той же вкладке будут ненулевыми. Для решения этой проблемы было добавлено новое полеframeType . См. раздел «Как определить, является ли кадр самым внешним?» раздел этого поста.
Жизненный цикл фреймов и документов
Еще одна проблематичная концепция расширений — это жизненный цикл фрейма. Во фрейме размещается документ (который связан с зафиксированным URL-адресом). Документ может измениться (скажем, при навигации), но идентификатор фрейма — нет, поэтому трудно связать, что что-то произошло в конкретном документе, с помощью только идентификаторов фреймов . Мы вводим понятие documentId , которое является уникальным идентификатором для каждого документа. Если переход по фрейму открывает новый документ, идентификатор изменится. Это поле полезно для определения того, когда страницы меняют состояние своего жизненного цикла (между предварительной отрисовкой/активностью/кэшированием), поскольку оно остается неизменным.
События веб-навигации
События в пространстве имен chrome.webNavigation
могут срабатывать несколько раз на одной и той же странице в зависимости от жизненного цикла, в котором она находится. См. «Как узнать, в каком жизненном цикле находится страница?» и «Как определить момент перехода страницы?» разделы.
Как узнать, в каком жизненном цикле находится страница?
Тип DocumentLifecycle
был добавлен в ряд API расширений, в которых ранее был доступен frameId
. Если тип DocumentLifecycle
присутствует в событии (например, onCommitted
), его значением является состояние, в котором событие было создано. Вы всегда можете запросить информацию из методов WebNavigation
getFrame()
и getAllFrames()
, но всегда предпочтительнее использовать значение из события. Если вы используете любой метод, имейте в виду, что состояние фрейма может измениться между моментом создания события и моментом разрешения обещаний, возвращаемых обоими методами.
DocumentLifecycle
имеет следующие значения:
-
"prerender
»: в настоящее время не представлен пользователю, но готовится к возможному отображению пользователю. -
"active"
: в настоящее время отображается пользователю. -
"cached"
: хранится в обратном/переднем кэше. -
"pending_deletion"
: документ уничтожается.
Как определить, является ли кадр самым внешним?
Раньше расширения могли проверять, имеет ли frameId == 0
чтобы определить, происходит ли событие для самого внешнего кадра или нет. Теперь, когда на вкладке имеется несколько страниц, у нас есть несколько внешних фреймов, поэтому определение идентификатора фрейма становится проблематичным. Вы никогда не будете получать события о кэшированном кадре Back/Forward. Однако для предварительно обработанных кадров frameId
будет ненулевым для самого внешнего кадра. Поэтому использование frameId == 0
в качестве сигнала для определения того, является ли это самый внешний кадр, неверно.
Чтобы помочь в этом, мы ввели новый тип под названием FrameType
, поэтому определить, действительно ли кадр является самым внешним, теперь легко. FrameType
имеет следующие значения:
-
"outermost_frame"
: обычно называется самым верхним кадром. Обратите внимание, что их несколько. Например, если у вас есть предварительно обработанные и кэшированные страницы, каждая из них имеет самый внешний фрейм, который можно назвать самым верхним фреймом. -
"fenced_frame"
: зарезервировано для будущего использования. -
"sub_frame"
: обычно iframe.
Мы можем объединить DocumentLifecycle
с FrameType
и определить, является ли кадр активным самым внешним кадром. Например: tab.documentLifecycle === “active” && frameType === “outermost_frame”
Как решить проблемы времени использования фреймов?
Как мы уже говорили выше, во фрейме находится документ, и фрейм может перейти к новому документу, но frameId
не изменится. Это создает проблемы, когда вы получаете событие только с frameId
. Если вы посмотрите URL-адрес кадра, он может отличаться от того, когда произошло событие, это называется проблемой времени использования.
Чтобы решить эту проблему, мы ввели documentId
(и parentDocumentId
). Метод webNavigation.getFrame() теперь делает frameId
необязательным, если указан documentId
. documentId
будет меняться при каждом перемещении по кадру.
Как определить момент перехода страницы?
Существуют явные сигналы, определяющие переход страницы между состояниями.
Давайте посмотрим на события WebNavigation
.
При первом переходе на любую страницу вы увидите четыре события в порядке, указанном ниже. Обратите внимание, что эти четыре события могут произойти, когда состояние DocumentLifecycle
является либо "prerender"
, либо "active"
.
onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted
Это показано на диаграмме ниже, где показано, как documentId
меняется на "xyz"
, когда предварительно обработанная страница становится активной.
![DocumentId изменяется, когда предварительно обработанная страница становится активной страницей.](https://developer.chrome.google.cn/static/blog/extension-instantnav/image/the-documentid-changes-t-6afaa608cddf7.png?authuser=5&hl=ru)
documentId
изменяется, когда предварительно обработанная страница становится активной. Когда страница переходит из кэша Back/Forward или предварительной визуализации в активное состояние, произойдет еще три события (но DocumentLifecyle
будет "active"
).
onBeforeNavigate
onCommitted
onCompleted
documentId
останется таким же, как и в исходных событиях. Это показано выше, когда активируется documentId
== xyz. Обратите внимание, что срабатывают те же события навигации, за исключением события onDOMContentLoaded
, поскольку страница уже загружена.
Если у вас есть какие-либо комментарии или вопросы, не стесняйтесь задавать их в группе расширений хрома .