Как добавить скрипт в браузер
Перейти к содержимому

Как добавить скрипт в браузер

  • автор:

Внешние скрипты, порядок исполнения

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/script-async-defer.

Если JavaScript-кода много – его выносят в отдельный файл, который подключается в HTML:

Здесь /path/to/script.js – это абсолютный путь к файлу, содержащему скрипт (из корня сайта).

Браузер сам скачает скрипт и выполнит.

Можно указать и полный URL, например:

Вы также можете использовать путь относительно текущей страницы. Например, src=»https://learn.javascript.ru/lodash.js» обозначает файл из текущей директории.

Чтобы подключить несколько скриптов, используйте несколько тегов:

На заметку:

Как правило, в HTML пишут только самые простые скрипты, а сложные выносят в отдельный файл.

Браузер скачает его только первый раз и в дальнейшем, при правильной настройке сервера, будет брать из своего кеша.

Благодаря этому один и тот же большой скрипт, содержащий, к примеру, библиотеку функций, может использоваться на разных страницах без полной перезагрузки с сервера.

Если указан атрибут src , то содержимое тега игнорируется.

В одном теге SCRIPT нельзя одновременно подключить внешний скрипт и указать код.

Вот так не сработает:

  

Нужно выбрать: либо SCRIPT идёт с src , либо содержит код. Тег выше следует разбить на два: один – с src , другой – с кодом, вот так:

   

Асинхронные скрипты: defer/async

Браузер загружает и отображает HTML постепенно. Особенно это заметно при медленном интернет-соединении: браузер не ждёт, пока страница загрузится целиком, а показывает ту часть, которую успел загрузить.

Если браузер видит тег , то он по стандарту обязан сначала выполнить его, а потом показать оставшуюся часть страницы.

Например, в примере ниже – пока все кролики не будут посчитаны – нижний

не будет показан:

    

Начинаем считать:

Кролики посчитаны!

Такое поведение называют «синхронным». Как правило, оно вполне нормально, но есть важное следствие.

Если скрипт – внешний, то пока браузер не выполнит его, он не покажет часть страницы под ним.

То есть, в таком документе, пока не загрузится и не выполнится big.js , содержимое будет скрыто:

  Этот текст не будет показан, пока браузер не выполнит big.js. 

И здесь вопрос – действительно ли мы этого хотим? То есть, действительно ли оставшуюся часть страницы нельзя показывать до загрузки скрипта?

Есть ситуации, когда мы не только НЕ хотим такой задержки, но она даже опасна.

Например, если мы подключаем внешний скрипт, который показывает рекламу или вставляет счётчик посещений, а затем идёт наша страница. Конечно, неправильно, что пока счётчик или реклама не подгрузятся – оставшаяся часть страницы не показывается. Счётчик посещений не должен никак задерживать отображение страницы сайта. Реклама тоже не должна тормозить сайт и нарушать его функциональность.

А что, если сервер, с которого загружается внешний скрипт, перегружен? Посетитель в этом случае может ждать очень долго!

Вот пример, с подобным скриптом (стоит искусственная задержка загрузки):

Важная информация не покажется, пока не загрузится скрипт.

. Важная информация!

Можно поставить все подобные скрипты в конец страницы – это уменьшит проблему, но не избавит от неё полностью, если скриптов несколько. Допустим, в конце страницы 3 скрипта, и первый из них тормозит – получается, другие два его будут ждать – тоже нехорошо.

Кроме того, браузер дойдёт до скриптов, расположенных в конце страницы, они начнут грузиться только тогда, когда вся страница загрузится. А это не всегда правильно. Например, счётчик посещений наиболее точно сработает, если загрузить его пораньше.

Поэтому «расположить скрипты внизу» – не лучший выход.

Кардинально решить эту проблему помогут атрибуты async или defer :

Поддерживается всеми браузерами, кроме IE9-. Скрипт выполняется полностью асинхронно. То есть, при обнаружении браузер не останавливает обработку страницы, а спокойно работает дальше. Когда скрипт будет загружен – он выполнится.

Поддерживается всеми браузерами, включая самые старые IE. Скрипт также выполняется асинхронно, не заставляет ждать страницу, но есть два отличия от async .

Первое – браузер гарантирует, что относительный порядок скриптов с defer будет сохранён.

То есть, в таком коде (с async ) первым сработает тот скрипт, который раньше загрузится:

А в таком коде (с defer ) первым сработает всегда 1.js , а скрипт 2.js , даже если загрузился раньше, будет его ждать.

Поэтому атрибут defer используют в тех случаях, когда второй скрипт 2.js зависит от первого 1.js , к примеру – использует что-то, описанное первым скриптом.

Второе отличие – скрипт с defer сработает, когда весь HTML-документ будет обработан браузером.

Например, если документ достаточно большой…

 Много много много букв

…То скрипт async.js выполнится, как только загрузится – возможно, до того, как весь документ готов. А defer.js подождёт готовности всего документа.

Это бывает удобно, когда мы в скрипте хотим работать с документом, и должны быть уверены, что он полностью получен.

async вместе с defer

При одновременном указании async и defer в современных браузерах будет использован только async , в IE9- – только defer (не понимает async ).

Атрибуты async/defer – только для внешних скриптов

Атрибуты async/defer работают только в том случае, если назначены на внешние скрипты, т.е. имеющие src .

При попытке назначить их на обычные скрипты , они будут проигнорированы.

Тот же пример с async :

Важная информация теперь не ждёт, пока загрузится скрипт.

. Важная информация!

При запуске вы увидите, что вся страница отобразилась тут же, а alert из внешнего скрипта появится позже, когда загрузится скрипт.

Эти атрибуты давно «в ходу»

Большинство современных систем рекламы и счётчиков знают про эти атрибуты и используют их.

Перед вставкой внешнего тега понимающий программист всегда проверит, есть ли у него подобный атрибут. Иначе медленный скрипт может задержать загрузку страницы.

Забегая вперёд

Для продвинутого читателя, который знает, что теги можно добавлять на страницу в любой момент при помощи самого javascript, заметим, что скрипты, добавленные таким образом, ведут себя так же, как async . То есть, выполняются как только загрузятся, без сохранения относительного порядка.

Если же нужно сохранить порядок выполнения, то есть добавить несколько скриптов, которые выполнятся строго один за другим, то используется свойство script.async = false .

Выглядит это примерно так:

function addScript(src) < var script = document.createElement('script'); script.src = src; script.async = false; // чтобы гарантировать порядок document.head.appendChild(script); >addScript('1.js'); // загружаться эти скрипты начнут сразу addScript('2.js'); // выполнятся, как только загрузятся addScript('3.js'); // но, гарантированно, в порядке 1 -> 2 -> 3

Более подробно работу со страницей мы разберём во второй части учебника.

Итого

  • Скрипты вставляются на страницу как текст в теге , либо как внешний файл через
  • Специальные атрибуты async и defer используются для того, чтобы пока грузится внешний скрипт – браузер показал остальную (следующую за ним) часть страницы. Без них этого не происходит.
  • Разница между async и defer : атрибут defer сохраняет относительную последовательность скриптов, а async – нет. Кроме того, defer всегда ждёт, пока весь HTML-документ будет готов, а async – нет.

Очень важно не только читать учебник, но делать что-то самостоятельно.

Решите задачки, чтобы удостовериться, что вы всё правильно поняли.

Задачи

Какой скрипт выполнится первым?

важность: 4

В примере ниже подключены два скрипта small.js и big.js .

Если предположить, что small.js загружается гораздо быстрее, чем big.js – какой выполнится первым?

«Пользовательские скрипты могут быть добавлены только из Интернет-магазина Chrome»? Нет, не только

В браузере Chrome версии 21 появилось новшество, известное ещё с её бета-версии: при попытке установить юзерскрипт браузер выдаёт провокационное, но некорректное по сути сообщение (в баре на странице вверху):

Расширения, приложения и пользовательские скрипты могут быть добавлены только из Интернет-магазина Chrome. Подробнее…

Ещё 20 августа было всё нормально, но с установкой новой версии политика начала действовать.

На самом деле, как они дальше поясняют, это утверждение неверно и представляет собой, по сути, дешёвый провокационный трюк, подталкивающий авторов опубликовать расширения в их «магазине». Выгода небольшая, но прямая, потому что регистрация автора — платная (однократно 5$), и часть авторов пойдут на это, потому что не каждый пользователь нажмёт ссылку «Подробнее. » и так и будет думать, что устанавливать расширения стало возможным только из «магазина». Даже поиск в Яндексе показывает такой типичный ход мыслей пользователей:

цитата из поиска

При установке пишет: «Расширения, приложения и пользовательские скрипты могут быть добавлены только из Интернет-магазина Chrome.» … простите меня может глупость говорю, новый хром версия 21.0.1180.60 m — ни в какую не хочет устанавливать ваше расширение, пишет, что расширения могут быть установлены только из магазина Chrome APP, дайте пож-та ссылку на ваше расширение в магазине хрома, или точное название чтобы найти его там по поиску.

И даже часть авторов в масштабе всего мира — это довольно много, счёт пойдёт, думается на десятки тысяч; можно ожидать ликующие отчёты маркетологов в служебной переписке компании уже в первый месяц нововведений.

Однозначно, это что-то новое в мире настольных браузеров, ранее встречавшееся только в Аппсторе и подобных закрытых средах. Что ж, читаем дальше, можно ли продолжить использование расширений и скриптов в этом браузере, не внося денег в не имеющий отношения к вам магазин.

По клику на «Подробнее. » видим страницу:

Скриншот текста

Повторение на случай непоказа картинки

Добавление расширений с других веб-сайтов

Мы стремимся сделать работу в Интернете более безопасной. Для этого мы недавно изменили правила добавления в браузер расширений, которых нет в Интернет-магазине Chrome. Раньше любой веб-сайт мог предложить вам добавить в браузер свое расширение. В последней же версии Google Chrome пользователь должен явно указать, что хочет установить такой компонент, добавив его на странице «Расширения».

Причины изменений

Чтобы вы могли безопасно работать в Интернете, группа аналитиков и технических специалистов проверяет все расширения в нашем магазине. Это позволяет найти вредоносные программы и не дать им попасть в магазин. К сожалению, мы не можем гарантировать надежность расширений на других веб-сайтах. Например, хакеры могут создавать сайты, которые автоматически запускают установку вредоносных программ. Такие расширения могут тайно отслеживать все данные, которые вы вводите в Интернете. Это позволяет злоумышленникам использовать информацию в своих целях.

Благодаря изменениям в процедуре установки расширений веб-сайты не могут автоматически запускать этот процесс, а вы получаете возможность лучше контролировать расширения, которые добавляете в Chrome.

Разработчикам: если вы распространяете расширение с помощью своего веб-сайта, рекомендуем вам добавить его в Интернет-магазин Chrome. Дополнительные инструкции по добавлению продуктов в Интернет-магазин Chrome и использованию встроенных средств, позволяющих устанавливать расширение прямо с вашего сайта, можно найти на веб-сайте Google для разработчиков.
Администраторам корпоративных систем: вы можете указать URL, с которых разрешено устанавливать расширения, приложения и темы, непосредственно в правилах ExtensionInstallSources.

Инструкции по добавлению расширений с других веб-сайтов

Рекомендуется добавлять расширения только с тех сайтов, которым вы доверяете. Чтобы сделать это в Chrome, выполните указанные ниже действия.

1. Загрузите файл расширения с веб-сайта и сохраните его на компьютере.
2. Нажмите на значок гаечного ключа на панели инструментов.
3. Выберите Инструменты > Расширения.
4. Найдите сохраненный файл и перетащите его на страницу «Расширения».
5. В открывшемся диалоговом окне проверьте список разрешений. Чтобы продолжить, нажмите кнопку Установить.

Дисклеймер по `разводу на жмота`

Специально для любителей выкрикнуть «Вам что, жалко 5 долларов для поддержки браузера?», вспомним, что, например, Майкрософт никогда не «ведётся» на выплаты компенсаций, чтобы не создавать прецедентов, мы никогда не подаём цыганке «копеечку» и не «ведёмся» на раскрутку хора цыганок-провокаторш за спиной. Вспоминаем поговорку «Дай ему палец — он и руку откусит» и видим принципиальную разницу между схемой поддержки разработчиков в 3 других браузерах и нововведением в Хроме.

Да, начиная с изменения формата собственных расширений Хрома, начавшихся 1-1.5 месяца назад, они ведут планомерную работу по перекрыванию источников взлома браузера с использованием вредоносных юзерскриптов и расширений. Правда, 1.5 года назад они «невзначай» ввели плату (одноразовую) для разработчиков на право публикации расширений, даже бесплатных. Поэтому, если разработчик делает расширение для себя или для узкого круга знакомых или для небольшого сообщества вокруг сайта (что чаще всего бывает), он уже не может опубликовать расширение в «магазине», не вступив в денежные отношения с создателем браузера — один раз, но придётся сделать платёж, что примерно равно регистрации с помощью мобильного — компания узнаёт о держателе счёта дополнительную информацию.

В общем, для защиты от хакеров, для поиска нарушителей это даже хорошо (похожую схему платных писем предлагала MS для защиты от спаммеров), хотя, наверное, без труда найдутся способы создания фиктивного счёта. С третьей стороны, они утверждают, что каждое приложение проходит проверку, поэтому получаемые деньги, очевидно, тратятся на поддержку службы проверки, а это будут немалые расходы, если она нужна действительно эффективной.

Получение статуса разработчика для других браузеров

Для сравнения, Safari создаёт статус разработчика (Start developing Safari extensions today) с лицензией на год, но бесплатно (для расширений). (Существует Safari 5.1.7 for Windows от мая 2012, задвинуто сюда support.apple.com/kb/DL1531 (us), ru.)

Firefox никогда не производил попыток монетизировать процесс вовлечения разработчиков; у него есть раздел сайта с аддонами, на нём может зарегистрироваться любой пользователь или разработчик, что не составляет напряжения или проблемы. Пользователи могут составлять списки (подборки) своих аддонов, следить за списками других и писать отзывы, а разработчики — ещё и публиковать расширения (аддоны, темы, обои).

Opera проводит тоже политику модерации разработчиков, но на бесплатной основе. Разработчик должен зарегистрироваться: my.opera.com/community/terms-of-service (англ., гугл-транс-руc.), но в условиях нет ни слова на платность. Гугл, конечно, перешёл границы необходимости бесплатной популяризации и уже может позволить подкручивать гайки, а у Оперы пока не очень велик объём модерации.

Кроме того, Опера имеет встроенную поддержку юзерскриптов и юзерстилей 2 способами — для всех сайтов и посайтово. Способ установки — самый сложный из всех остальных браузеров, но осиливаемый пользователями.

IE… Ну, они вообще ничего не делают в плане поддержки расширений, за них делают другие (в том числе и браузеры). Имеется лишь примитивная возможность установки мультисайтовых (для всех сайтов сразу) юзерстилей по аналогии с похожей функцией Сафари, но существующей с версии IE7.

Оставшиеся возможности для Chrome

Однако, в Chrome предусмотрены, судя по тексту правил, и 2 обходных способа (и снова, не все) установки расширений и юзерскриптов: через прописывание специального тега с кодом разработчика расширения прямо на вебстранице (смешно — если есть доступ к странице и коду, то зачем юзерскрипты) или через установку из локального файла, как описано в 5 пунктах инструкции. Второе удлиняет процесс инсталляции, что правильно с точки зрения развеивания зомбированности пользователя, не осознающего свои действия в окне браузера и считающего их безопасными. Впрочем, и на 5 пунктов можно прозомбировать, но уже сложнее убедить проделать нетипичную цепочку действий в системе по сравнению с работой в окне браузера.

Значит, некоторый прогресс в безопасности есть, права разработчика не ущемлены, из зоны бесплатности он и его браузер не вышли. Надо всего лишь не размещать скрипты в «Магазине» или однократно заплатить Гуглу 5$ за такое право.

Теперь придётся описать длинную инструкцию по установке расширения в браузер Chrome не из его «магазина». Она почти побивает рекорды сложности Оперы, ссылка на инструкцию к которой приведена выше.

Есть ещё третий способ исхитриться (подсказывают из Chromium): при запуске браузера указать параметр —enable-easy-off-store-extension-install — установка расширений останется такой же простой, как прежде. В Windows удобно будет поставить параметр в ярлык, написав в его свойствах (к примеру):

С:\Program Files\Google\Chrome\Application\chrome.exe --enable-easy-off-store-extension-install 

+ 4-й способ, указанный SpiritOfVox в комментарии habrahabr.ru/post/150693/#comment_5104872 — доверенные сайты записать в реестр в Windows.

Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallSources] "1"="http://userscripts.org/*" 

Инструкция по установке юзерскрипта из userscripts.org

(Похожая инструкция на английском для английской версии браузера.)
Находимся на странице описания скрипта, с которой обычно инсталлируют юзерскрипт.

1. Нажать на кнопку «Install». Видим процесс загрузки в баре файлов внизу страницы.

2. Получить расширение, загруженное в компьютер как файл (бар загрузок внизу) и «отлуп» вверху (ла-ла-ла-«… только из Интернет-магазина»).

3. Не верим провокации и действуем по плану «Це«: открываем страницу расширений браузера.

(Или по закладке с адресом chrome://chrome/extensions/ .)

4. Если расширение уже установлено в виде прежней версии, удаляем старую версию (обязательно, иначе сохранится старая до перезагрузки браузера).

5. Открыть из нижнего бара папку, содержащую скачанный файл юзерскрипта.

UPD 7.09.2012: @kraleksandr подсказал, что 5-6 можно объединить, потащив значок файла сразу из панели загрузок на список расширений — хорошо, лишнего действия с окном файл-менеджера нет.

(Вот он, выделенный уже в момент просмотра.)

6. Перетащить файл юзерскрипта на список расширений, так, чтобы появилась надпись «Чтобы установить расширение, перетащите его».

(Появление надписи принципиально, потому что если мы перетащим и бросим НЕ на список расширений, а рядом с ним, тоже появится сакраментальная жёлтая надпись вверху — якобы, Хром не разрешает устанавливать расширения не из магазина.)

Обратите внимание, что допустимое ранее бросание юзерскрипта на любую другую открытую страницу тоже приведёт к сакраментальной фразе из пункта 2. Установить скрипт можно, лишь открыв страницу расширений. Если обнаружен такой диалог — это уже неправильный ход установки, вслед за ним будет «пункт 2».
Не делать так:

7. Отпускаем файл, получаем стандартное предупреждение безопасности.

8. После согласия с установкой получаем беспроблемно установленный юзерскрипт, которым можно пользоваться без перезагрузки браузера, лишь с перезагрузкой страницы сайта, для которого он предназначен.

Заключение

Вначале Chrome сразу стал лидером по простоте установки расширений. Бросок на любую страницу, пара кликов подтверждений — готово. Или клик по ссылке и пара подтверждений. Сейчас имеем сложный многоступенчатый процесс, если только ссылка не находится в «магазине». Лидером простоты снова стал Firefox, исторически первый браузер, введший юзерскрипты в их общепризнанном ныне виде.

Процесс забрасывания скрипта в файле в окошко расширений — это типичный способ отладки юзерскриптов, который знаком разработчику. Но он будет нов и сложен (или хотя бы необычен) для пользователя, поэтому пользователи, скорее всего, будут активнее хотеть простой ссылки в «магазине», чем даже скрипта с известного и признанного userscripts.org. С другой стороны, это даст время подумать, стоит ли доверять скрипту, находящемуся не в магазине — ведь магазинные скрипты обещают контролировать, модерировать, что создатели браузера не могли бы обеспечить на любом другом сайте.

Некоторые пользователи будут использовать запуск Хрома с параметром —enable-easy-off-store-extension-install — он позволит забыть о политике и заниматься делом. Скорее всего, это будет удобно для тех, кто слишком часто ставит расширения, чтобы выполнять каждый раз несложную, но многоходовую процедуру установки через сохраняемый файл.

P.S. Подскажите, как пишутся сообщения в английской версии Хрома, чтобы внести их в инструкцию.

  • юзерскрипты
  • userscripts
  • chrome web store
  • GreaseMonkey
  • Google Chrome
  • Расширения для браузеров

Встраиваемый скрипт

Встраиваемый скрипт — это часть расширения, которая выполняется в контексте отдельной веб-страницы (в отличии от фоновых скриптов, выполняющихся в контексте целого браузера).

Фоновые скрипты имеют доступ ко всем методам WebExtension JavaScript APIs, но они не имеют доступа к контенту отдельных веб-страниц. Так что если вашему расширению необходимо взаимодействие с контентом веб-страницы, вам нужен встраиваемый скрипт.

Точно так же, как скрипты загружаемые веб-страницами, встраиваемый скрипт может читать и изменять контент веб-страницы, используя DOM API.

Встраиваемые скрипты имеют доступ только к небольшому подмножеству методов WebExtension API, но они могут взаимодействовать с фоновыми скриптами, используя систему передачи сообщений, таким образом опосредовано имея доступ ко всему WebExtension API.

Примечание: Обратите внимание, что встраивание скриптов блокируется на следующих доменах:

  • accounts-static.cdn.mozilla.net
  • accounts.firefox.com
  • addons.cdn.mozilla.net
  • addons.mozilla.org
  • api.accounts.firefox.com
  • content.cdn.mozilla.net
  • content.cdn.mozilla.net
  • discovery.addons.mozilla.org
  • input.mozilla.org
  • install.mozilla.org
  • oauth.accounts.firefox.com
  • profile.accounts.firefox.com
  • support.mozilla.org
  • sync.services.mozilla.com
  • testpilot.firefox.com

Если вы попытаетесь встроить скрипт на страницы, находящиеся на вышеперечисленных доменах, у вас ничего не выйдет, и веб-страница сделает запись о CSP ошибке.

По причине того, что список запрещённых доменов включает в себя addons.mozilla.org, пользователи, которые попытаются испытать ваше расширение прямо на странице загрузки, могут посчитать, что расширение не работает! В связи с этим вы могли бы предоставить им соответствующее предупреждение или onboarding page чтобы немедленно переадресовать их с addons.mozilla.org.

Примечание: Значения, определённые в глобальной области видимости встраиваемого скрипта с помощью var foo или window.foo = «bar» могут исчезать по причине бага 1408996.

Загрузка встраиваемого скрипта

Загрузка встраиваемых скриптов на веб-страницу происходит следующими тремя способами:

  1. Автоматическая загрузка скрипта на страницах с определёнными URL, объявленными при установке: используя content_scripts (en-US) ключ в manifest.json, вы можете запросить браузер загружать встраиваемый скрипт каждый раз, когда браузер загружает веб-страницу, чей URL совпадает с объявленными шаблонами.
  2. Автоматическая загрузка скрипта на страницах с определёнными URL, объявленными в момент работы расширения: используя contentScripts (en-US) API, вы можете запросить браузер загружать встраиваемый скрипт каждый раз, когда браузер загружает веб-страницу, чей URL совпадает с объявленными шаблонами. Это очень похоже на первый метод, но позволяет добавлять и удалять правила динамически во время работы расширения.
  3. Загрузка скрипта расширением в конкретную вкладку: используя tabs.executeScript() (en-US) API, вы можете загружать встраиваемые скрипты в определённые вкладки когда захотите: например, в ответ на нажатие пользователя на browser action.

Существует только одна глобальная область видимости для одного фрейма, для одного расширения. Это означает, что значения из одного встраиваемого скрипта могут быть доступны для другого встраиваемого скрипта, не зависимо от того, как встраиваемый скрипт был загружен на страницу.

Используя методы (1) и (2), вы можете загружать скрипты на страницы, чьи URL могут быть представлены, используя шаблон совпадения.

Используя метод (3), вы к тому же можете загружать скрипты на страницы вашего расширения, но вы не можете загружать скрипты на привилегированные страницы браузеры (например «about:debugging» или «about:addons»).

Условия работы встраиваемого скрипта

Доступ к DOM

Встраиваемые скрипты имеют доступ и могут изменять DOM на веб-странице, так же как и обычные скрипты. Так же они могут видеть любые изменения сделанные с DOM страничными скриптами.

При этом, встраиваемые скрипты имеют «чистое DOM представление». Это значит:

  • Встраиваемые скрипты не могут видеть JavaScript переменных, определённых страничными скриптами.
  • Если страничный скрипт переназначит встроенное свойство DOM, встраиваемый скрипт всё равно будет видеть его изначальное значение, а не переназначенное.

Например, рассмотрите эту веб-страницу:

doctype html> html> head> meta http-equiv="content-type" content="text/html; charset=utf-8" /> head> body> script src="page-scripts/page-script.js"> script> body> html> 

Скрипт «page-script.js» делает следующее:

// page-script.js // добавляет новый элемент к DOM var p = document.createElement("p"); p.textContent = "Этот параграф был добавлен страничным скриптом."; p.setAttribute("id", "page-script-para"); document.body.appendChild(p); // определяет новое свойство на объекте window window.foo = "Эта глобальная переменная была добавлена страничным скриптом"; // переопределяет встроенную window.confirm() функцию window.confirm = function ()  alert("Страничный скрипт так же переопределил 'confirm'"); >; 

Теперь расширение загружает встраиваемый скрипт на страницу:

// content-script.js // получает доступ и изменяет DOM var pageScriptPara = document.getElementById("page-script-para"); pageScriptPara.style.backgroundColor = "blue"; // не может видеть свойств определённых страничным скриптом console.log(window.foo); // undefined // видит изначальное значение свойства, переопределённого страничным скриптом window.confirm("Вы уверены?"); // вызывает оригинальный window.confirm() 

Те же самые правила применяются и наоборот: страничный скрипт не может видеть JavaScript свойств, добавленных встраиваемым скриптом.

Это означает, что встраиваемый скрипт может полагаться на то, что DOM свойства всегда будут вести себя предсказуемо, и не беспокоиться о том, что его переменные будут иметь конфликт с переменными из страничного скрипта.

Одно из последствий такого поведения состоит в том, что встраиваемый скрипт не будет иметь доступ к JavaScript библиотекам, загруженным страничным скриптом. Например, если веб-страница загружает jQuery, встраиваемый скрипт не сможет увидеть эту библиотеку.

Если встраиваемому скрипту необходимо использовать какую-либо JavaScript библиотеку, тогда эта библиотека должна быть загружена, тем же способом, как и встраиваемый скрипт вместе с ним:

"content_scripts": [  "matches": ["*://*.mozilla.org/*"], "js": ["jquery.js", "content-script.js"] > ] 

Примечание: Firefox предоставляет некоторое API, позволяющее встраиваемому скрипту иметь доступ к JavaScript объектам, созданным страничным скриптом, и предоставлять свои собственные JavaScript объекты страничному скрипту.

Доступные WebExtension API

В дополнение стандартному DOM API, встраиваемый скрипт может использовать следующие методы WebExtension APIs:

Работа с XHR и Fetch

Встраиваемые скрипты могут делать запросы используя window.XMLHttpRequest и window.fetch() API.

Примечание: В Firefox, запросы, совершаемые из встраиваемого скрипта (например, используя fetch()) происходят в контексте расширения, так что вам необходимо предоставлять полный URL для доступа к контенту страниц. В Chrome, эти запросы совершаются в контексте страницы, так что URL может быть относительным, например /api будет трансформирован в https://[current page URL]/api .

Встраиваемый скрипт имеет точно такие же кросс-доменные привилегии, как и всё остальное расширение: так что если расширение запросило кросс-доменный доступ на какой-либо домен используя permissions ключ в manifest.json, тогда его встраиваемый скрипт тоже будет иметь доступ к этому домену.

Это достигается, предоставляя встраиваемому скрипту более привилегированные XHR and fetch объекты. Что имеет побочный эффект, связанный с отсутствием Origin и Referer заголовков, которые имел бы запрос, выполняемый из страничного скрипта. Зачастую это предпочитаемо, для того чтобы предотвратить раскрытие кросс-доменной натуры запроса. Начиная с версии 58 и дальше, расширения, которым необходимо выполнять запросы, которые должны выглядеть будто они отправлены встраиваемым скриптом, могут использовать content.XMLHttpRequest и content.fetch() . Кросс-браузерные расширения должны проверять присутствие этих методов.

Взаимодействие с фоновыми скриптами

Хотя встраиваемые скрипты не могут напрямую использовать большинство методов из WebExtension APIs, они могут взаимодействовать с фоновыми скриптами расширения, используя систему сообщений, и таким образом могут опосредованно иметь доступ к тем же самым API, что и фоновые скрипты.

Существует два способа общения между фоновым и встраиваемым скриптами: вы можете посылать одиночные сообщения, ожидая необязательного ответа, или вы можете установить долгосрочное соединение на двух сторонах и использовать это соединение для обмена сообщениями.

Одиночные сообщения

Для отправки одиночного сообщения и ожидания необязательного ответа, вы можете использовать следующее API:

Во встраиваемом скрипте В фоновом скрипте
Отправка сообщения browser.runtime.sendMessage() (en-US) browser.tabs.sendMessage() (en-US)
Получение сообщения browser.runtime.onMessage (en-US) browser.runtime.onMessage (en-US)

Например, представьте встраиваемый скрипт, который обрабатывает нажатие левой кнопки мыши на веб-странице.

Если нажатие было произведено по ссылке, встраиваемый скрипт отправляет сообщение фоновому скрипту со значение URL в ссылке:

// content-script.js window.addEventListener("click", notifyExtension); function notifyExtension(e)  if (e.target.tagName != "A")  return; > browser.runtime.sendMessage( url: e.target.href >); > 

Фоновый скрипт ожидает эти сообщения и, при их получении, отображает уведомления, используя notifications (en-US) API:

// background-script.js browser.runtime.onMessage.addListener(notify); function notify(message)  browser.notifications.create( type: "basic", iconUrl: browser.extension.getURL("link.png"), title: "Вы нажали на ссылку!", message: message.url, >); > 

(Этот пример кода частично взят из notify-link-clicks-i18n примера на GitHub.)

Обмен сообщениями через постоянное соединение

Отправка одиночных сообщений может стать довольно обременительной, если вы пересылаете много сообщений между встраиваемым и фоновым скриптами. Альтернативное решение в этой ситуации будет установить постоянное соединение между двумя скриптами, и использовать его для обмена сообщениями.

Каждая из сторон имеет runtime.Port (en-US) объект, который они могут использовать для обмена сообщениями.

Для создания постоянного соединения:

  • Одна из сторон должна ждать соединений, используя runtime.onConnect (en-US)
  • Другая сторона должна вызвать:
    • tabs.connect() (en-US) (при соединении со встраиваемым скриптом); или
    • runtime.connect() (en-US) (при соединении с фоновым скриптом).

    Результатом вызова этих методов будет возвращение runtime.Port (en-US) объекта.

    • runtime.onConnect (en-US) методу передаётся аргумент, являющийся собственным портом этого скрипта, runtime.Port (en-US) объект.

    Как только обе стороны имеют порт, они могут:

    • отправлять сообщения, используя runtime.Port.postMessage() , и
    • получать сообщения используя runtime.Port.onMessage()

    Например, сразу после загрузки, нижерасположенный встраиваемый скрипт:

    • Соединяется с фоновым скриптом
    • Сохраняет Port в переменную myPort
    • Начинает ожидать и записывать сообщения приходящие на myPort
    • Использует myPort для отправки сообщений на фоновый скрипт, когда пользователь нажимает левую кнопку мыши на документе
    // content-script.js var myPort = browser.runtime.connect( name: "port-from-cs" >); myPort.postMessage( greeting: "Привет из встраиваемого скрипта" >); myPort.onMessage.addListener(function (m)  console.log( "Во встраиваемом скрипте, получено сообщение из фонового скрипта: ", ); console.log(m.greeting); >); document.body.addEventListener("click", function ()  myPort.postMessage( greeting: "Они кликнули по странице!" >); >); 

    Соответствующий фоновый скрипт:

    • Ожидает попытки соединения от встраиваемого скрипта
    • Когда он получает эту попытку соединения:
      • сохраняет свой порт в переменную portFromCS
      • отправляет сообщение встроенному скрипту, используя этот порт
      • начинает ожидать сообщений, приходящих на порт, и записывать их
      // background-script.js var portFromCS; function connected(p)  portFromCS = p; portFromCS.postMessage( greeting: "Привет, встраиваемый скрипт!" >); portFromCS.onMessage.addListener(function (m)  console.log( "В фоновом скрипте, получено сообщение от встраиваемого скрипта", ); console.log(m.greeting); >); > browser.runtime.onConnect.addListener(connected); browser.browserAction.onClicked.addListener(function ()  portFromCS.postMessage( greeting: "Они нажали кнопку!" >); >); 
      Несколько встраиваемых скриптов

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

      // background-script.js var ports = []; function connected(p)  ports[p.sender.tab.id] = p; //. > browser.runtime.onConnect.addListener(connected); browser.browserAction.onClicked.addListener(function ()  ports.forEach((p) =>  p.postMessage( greeting: "Они нажали на кнопку!" >); >); >); 

      Взаимодействие с веб-страницей

      Хотя по умолчанию встраиваемые скрипты не имеют доступ к объектам, которые созданы страничными скриптами, они могут взаимодействовать со страничными скриптами, используя window.postMessage и window.addEventListener API.

      // page-script.js var messenger = document.getElementById("from-page-script"); messenger.addEventListener("click", messageContentScript); function messageContentScript()  window.postMessage( direction: "from-page-script", message: "Сообщение со страницы" >, "*"); 
      // content-script.js window.addEventListener("message", function (event)  if ( event.source == window && event.data && event.data.direction == "from-page-script" )  alert( 'Встраиваемый скрипт получил сообщение: "' + event.data.message + '"', ); > >); 

      Для просмотра законченного и полностью рабочего примера, посетите эту страницу на GitHub и следуйте инструкциям.

      Предупреждение: Заметьте, при взаимодействии с непроверенными страницами этим способом, вам нужно быть очень осторожными . Расширения имеют возможность запускать привилегированный код, который может обладать серьёзными возможностями, и вредоносные веб-страницы легко могут обмануть ваше расширение, ради доступа к этим возможностям.

      Как простейший пример, представим встраиваемый скрипт, который получает сообщение от веб-страницы и делает что-то вроде этого:

      // content-script.js window.addEventListener("message", function (event)  if ( event.source == window && event.data.direction && event.data.direction == "from-page-script" )  eval(event.data.message); > >); 

      Теперь страничный скрипт может запускать любой код со всеми привилегиями встраиваемого скрипта.

      Использование eval() во встраиваемых скриптах

      В Chrome, eval() всегда выполняет код в контексте встраиваемого скрипта, а не в контексте веб-страницы.

      • Если вы вызываете eval() , код выполняется в контексте встраиваемого скрипта.
      • Если вы вызываете window.eval() , код выполняется в контексте страничного скрипта.

      Например, рассмотрите этот встраиваемый скрипт:

      // content-script.js window.eval("window.x = 1;"); eval("window.y = 2"); console.log(`Во встраиваемом скрипте, window.x: $window.x>`); console.log(`Во встраиваемом скрипте, window.y: $window.y>`); window.postMessage(  message: "check", >, "*", ); 

      Этот код создаёт переменные x and y , используя window.eval() и eval() , затем записывает их значения, и отправляет сообщение на веб-страницу.

      Получая сообщение, страничный скрипт записывает те же самые переменные:

      .addEventListener("message", function (event)  if (event.source === window && event.data && event.data.message === "check")  console.log(`In page script, window.x: $window.x>`); console.log(`In page script, window.y: $window.y>`); > >); 

      В Chrome, в консоли будут записаны следующие строки:

      In content script, window.x: 1 In content script, window.y: 2 In page script, window.x: undefined In page script, window.y: undefined

      В Firefox, будут записаны следующие строки:

      In content script, window.x: undefined In content script, window.y: 2 In page script, window.x: 1 In page script, window.y: undefined

      Предупреждение: Будьте очень осторожны, запуская код в контексте страничного скрипта. Окружение страничного скрипта может контролироваться потенциально зловредным веб-страницей, которая может переопределить объекты, с которыми вы взаимодействуете, так что их поведение станет совершенно непредсказуемым:

      // page.js переопределяет console.log var original = console.log; console.log = function ()  original(true); >; 
      // content-script.js вызывает переопределённую версию window.eval("console.log(false)"); 

      Found a content problem with this page?

      • Edit the page on GitHub.
      • Report the content issue.
      • View the source on GitHub.

      This page was last modified on 3 авг. 2023 г. by MDN contributors.

      Your blueprint for a better internet.

      Делаем своё расширение для браузера за 10 минут

      В конце 2020 года мы делали проект со снежинками — писали специальный скрипт, который запускал падающий снег на сайтах. Если бы мы хотели сделать такой снег на любом своём сайте, это не составило бы труда: добавляешь скрипт в код страницы, и готово.

      А вот на чужих сайтах была проблема. Скрипт нужно было вставлять через консоль. А если на сайте была настроена политика безопасности, которая запрещает запуск внешних скриптов, то магия не срабатывала.

      Сегодня мы это исправим — сделаем расширение для браузера, которое может запускать любой скрипт на любой странице. Мы охватим принцип, на основе которого вы сможете сделать собственные расширения, в том числе намного более сложные.

      �� Что такое расширение

      Расширение для Chrome — это небольшая программа, которая выполняется внутри браузера и помогает расширить возможности сайтов. Сила расширения в том, что оно может выполняться прямо из меню браузера и не зависит от политик безопасности.

      Примеры того, что может сделать расширение:

      • Сохранить ваши пароли и другие личные данные,
      • Выдрать из страницы скрытые данные (например, ссылку на скачивание музыки).
      • Менять что-то на странице, например, отключать рекламу, переводить текст, удалять ненужное.
      • Собирать статистику вашей работы в интернете.
      • Подключаться к другим сервисам (почте, чатам, файлообменникам) и взаимодействовать с ними из браузера.

      В этой статье

      Мы сделаем самое простое расширение для браузера Chrome, которое позволит запускать скрипт со снежинками на любом сайте, независимо от настроенной политики безопасности. Для этого воспользуемся официальным руководством Google по созданию расширений.

      Манифест

      В каждом расширении для браузера должен быть манифест — документ, в котором написано:

      • как называется расширение;
      • к чему расширение может получить доступ;
      • какие скрипты будут работать в фоне;
      • как должна выглядеть иконка расширения;
      • что показать или что выполнить, когда пользователь нажмёт на иконку расширения.

      Манифест задаёт общие правила для всего расширения, поэтому манифест — единственный обязательный компонент. Можно обойтись без иконок и скриптов, но манифест обязательно должен быть.Каждый манифест хранится в файле manifest.json — создадим пустой файл с таким именем и напишем внутри такое:

      «name»: «Запускаем снежинки на любом сайте»,
      «description»: «Проект журнала Код»,
      «version»: «1.0»,
      «manifest_version»: 3
      >

      Первые две строчки — это название и подробное описание расширения. Третья отвечает за номер версии расширения, а последняя говорит браузеру, какая версия манифеста используется в описании. На момент выхода статьи в феврале 2021 года используется третья версия.

      Сохраняем файл и всё, расширение готово. Оно ничего не умеет, ничего не делает, зато мы уже можем добавить его в браузер. Для этого запускаем Хром и в адресной строке пишем:

      Мы попадаем на страницу, которая нам покажет все установленные расширения:

      Делаем своё расширение для браузера за 10 минут

      Чтобы добавить своё расширение, в правом верхнем углу включаем режим разработчика, а затем нажимаем «Загрузить распакованное расширение»:

      Делаем своё расширение для браузера за 10 минут

      Теперь выбираем папку, в которой лежит наш манифест:

      Делаем своё расширение для браузера за 10 минут

      Отлично, мы только что добавили в браузер новое расширение:

      Делаем своё расширение для браузера за 10 минут

      Теперь мы можем обновлять наш манифест, класть в ту же папку дополнительные файлы, а для обновления в браузере достаточно будет нажать на круглую стрелку на карточке расширения.

      Чтобы было проще работать и тестировать расширение, закрепим его на панели браузера:

      Делаем своё расширение для браузера за 10 минут

      Иконки

      У расширения есть две иконки, которыми мы можем управлять:

      1. Картинка в карточке расширения на странице настроек.
      2. Иконка на панели браузера.

      Чтобы не рисовать всё с нуля, скачаем папку с иконками из того же руководства Google и положим её в ту же папку, что и манифест:

      Теперь добавим иконки в манифест. За картинку в карточке отвечает блок icon, а за иконку на панели — блок action. Разные размеры картинки нужны для того, чтобы на разных мониторах с любой плотностью пикселей иконки выглядели хорошо:

       < "name": "Запускаем снежинки на любом сайте", "description": "Проект журнала Код", "version": "1.0", "manifest_version": 3, "action": < "default_icon": < "16": "/images/get_started16.png", "32": "/images/get_started32.png", "48": "/images/get_started48.png", "128": "/images/get_started128.png" >>, "icons": < "16": "/images/get_started16.png", "32": "/images/get_started32.png", "48": "/images/get_started48.png", "128": "/images/get_started128.png" >>

      Сохраняем манифест, обновляем расширение на странице настроек и смотрим результат:

      Добавляем иконки в манифест

      Настраиваем разрешения

      Разрешения — это то, что браузер позволяет делать расширению со страницами и с их содержимым. Для запуска снежинок нам нужно сделать две вещи:

      1. Понять, какая вкладка сейчас активная, чтобы запустить снежинки именно на ней.
      2. Запустить наш скрипт со снежинками.

      Чтобы получить доступ к активной вкладке и к запуску скриптов, добавим в манифест такую строку:

      «permissions»: [«activeTab», «scripting»],

      Показываем меню

      Если мы сейчас нажмём на иконку расширения на панели браузера, то ничего не призойдёт, потому что мы ничего не запрограммировали. Исправим это — сделаем так, чтобы при нажатии расширение показывало кнопку запуска. Когда расширение будет уметь больше, вместо одной кнопки можно будет показать целое меню.

      Чтобы сделать всплывающее меню, добавим в манифест в раздел action такую строку:

      Она означает, что при нажатии на иконку мы увидим рядом с ней мини-страничку, на которой что-то будет.Создадим в той же папке расширения файл popup.html и добавим в него такой код:

           /* задаём размеры кнопки и размер текста на кнопке */ button   

      Чтобы браузер не ругался, что у нас нет файла popup.js , создадим пустой файл с таким названием и положим его в ту же папку:

      Показываем меню расширения

      Сохраняем манифест, обновляем его на странице настроек и видим, что у нашего расширения появилось меню с кнопкой:

      Показываем меню расширения

      Запускаем снежинки

      Вся магия будет происходить в файле popup.js — откроем его и добавим такой код:

      // получаем доступ к кнопке let snow = document.getElementById("snow"); // когда кнопка нажата — находим активную вкладку и запускаем нужную функцию snow.addEventListener("click", async () => < // получаем доступ к активной вкладке let [tab] = await chrome.tabs.query(< active: true, currentWindow: true >); // выполняем скрипт chrome.scripting.executeScript(< // скрипт будет выполняться во вкладке, которую нашли на предыдущем этапе target: < tabId: tab.id >, // вызываем функцию, в которой лежит запуск снежинок function: snowFall, >); >); // запускаем снег function snowFall()

      Последнее, что нам осталось сделать, — положить в функцию snowFall() полный код скрипта из проекта со снежинками и сохранить файл.

      Проверка

      В прошлый раз мы не смогли запустить скрипт на любой странице Яндекса — мешала политика безопасности. Теперь всё работает:

      Проверяем расширение

      Скачать упакованное расширение. Перед установкой его нужно распаковать в любую папку.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *