Скрипты контента

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

Понимание возможностей скриптов контента

Скрипты содержимого могут получать доступ к API Chrome, используемым родительским расширением, путем обмена сообщениями с расширением. Они также могут получить доступ к URL-адресу файла расширения с помощью chrome.runtime.getURL() и использовать результат так же, как и для других URL-адресов.

// Code for displaying EXTENSION_DIR/images/myimage.png:
var imgURL = chrome.runtime.getURL("images/myimage.png");
document.getElementById("someImage").src = imgURL;

Кроме того, ContentScript может напрямую обращаться к следующим API Chrome:

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

Работа в изолированных мирах

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

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

<html>
  <button id="mybutton">click me</button>
  <script>
    var greeting = "hello, ";
    var button = document.getElementById("mybutton");
    button.person_name = "Bob";
    button.addEventListener("click", function() {
      alert(greeting + button.person_name + ".");
    }, false);
  </script>
</html>

Это расширение может внедрить следующий скрипт содержимого.

var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
  alert(greeting + button.person_name + ".");
}, false);

Оба предупреждения появятся при нажатии кнопки.

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

Внедрение скриптов

Скрипты контента могут внедряться программно или декларативно .

Внедрение программным способом

Используйте программное внедрение для скриптов контента, которые должны запускаться в определённые моменты времени.

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

{
  "name": "My extension",
  ...
  "permissions": [
    "activeTab"
  ],
  ...
}

Скрипты контента можно внедрять как код.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "changeColor"){
      chrome.tabs.executeScript({
        code: 'document.body.style.backgroundColor="orange"'
      });
    }
  });

Или же можно внедрить весь файл целиком.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "runContentScript"){
      chrome.tabs.executeScript({
        file: 'contentScript.js'
      });
    }
  });

Внедрить декларативно

Используйте декларативное внедрение для скриптов контента, которые должны автоматически запускаться на указанных страницах.

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

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Имя Тип Описание
matches {: #matches } массив строк Обязательный параметр. Указывает, на какие страницы будет внедрен этот скрипт содержимого. См. раздел «Шаблоны соответствия» для получения более подробной информации о синтаксисе этих строк, а также раздел «Шаблоны соответствия и шаблоны» для получения информации о том, как исключить URL-адреса.
css {: #css } массив строк Необязательный параметр. Список CSS-файлов, которые будут внедрены в соответствующие страницы. Они внедряются в том порядке, в котором они указаны в этом массиве, до того, как будет создан или отображен какой-либо DOM-элемент для страницы.
js {: #js } массив строк Необязательный параметр. Список JavaScript-файлов, которые будут внедрены в соответствующие страницы. Они внедряются в том порядке, в котором они указаны в этом массиве.
match_about_blank {: #match_about_blank } логический Необязательный параметр. Указывает, следует ли внедрять скрипт в фрейм about:blank если родительский или открывающий фрейм соответствует одному из шаблонов, указанных в matches . По умолчанию — false .

Исключить спички и шарики

Настройка соответствия страниц может быть выполнена путем включения следующих полей в регистр манифеста.

Имя Тип Описание
exclude_matches {: #exclude_matches } массив строк Необязательно. Исключает страницы, на которые в противном случае был бы внедрен этот скрипт содержимого. Дополнительные сведения о синтаксисе этих строк см. в разделе «Шаблоны соответствия» .
include_globs {: #include_globs } массив строк Необязательно. Применяется после matches , чтобы включить только те URL-адреса, которые также соответствуют этому шаблону. Предназначено для имитации ключевого слова @include Greasemonkey.
exclude_globs {: #exclude_globs } массив строк Необязательный параметр. Применяется после matches для исключения URL-адресов, соответствующих этому шаблону. Предназначен для имитации ключевого слова Greasemonkey @exclude .

Скрипт содержимого будет внедрен на страницу, если ее URL соответствует любому из шаблонов matches и include_globs , при условии, что URL также не соответствует шаблону exclude_matches или exclude_globs .

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

Следующее расширение внедрит скрипт содержимого на сайт http://www.nytimes.com/health , но не на сайт http://www.nytimes.com/business .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Свойства шаблонов (glob) используют другой, более гибкий синтаксис, чем шаблоны соответствия . Допустимыми строками шаблонов являются URL-адреса, которые могут содержать символы подстановки — звездочки и вопросительные знаки. Звездочка * соответствует любой строке любой длины, включая пустую строку, а вопросительный знак ? соответствует любому отдельному символу.

Например, шаблон http:// ??? .example.com/foo/ * соответствует любому из следующих вариантов:

  • http://www.example.com/foo/bar
  • http://the.example.com/foo/

Однако это не соответствует следующему:

  • http://my.example.com/foo/bar
  • http://example.com/foo/
  • http://www.example.com/foo

Это расширение внедрит скрипт содержимого в http:/www.nytimes.com/arts/index.html и http://www.nytimes.com/jobs/index.html, но не в http://www.nytimes.com/sports/index.html .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Это расширение внедрит скрипт содержимого в http://history.nytimes.com и http://.nytimes.com/history, но не в http://science.nytimes.com или http://www.nytimes.com/science .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

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

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Время выполнения

Процесс внедрения JavaScript-файлов в веб-страницу контролируется полем run_at . Предпочтительным и используемым по умолчанию полем является "document_idle" , но при необходимости его можно указать как "document_start" или "document_end" .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Имя Тип Описание
document_idle {: #document_idle } нить Предпочтительно. По возможности используйте "document_idle" .

Браузер выбирает момент для внедрения скриптов в промежутке между "document_end" и моментом сразу после срабатывания события windowonload . Точный момент внедрения зависит от сложности документа и времени его загрузки, и оптимизирован для скорости загрузки страницы.

Скрипты содержимого, работающие в состоянии "document_idle" не обязаны отслеживать событие window.onload , им гарантировано выполнение после завершения формирования DOM-дерева. Если скрипту определенно необходимо выполниться после window.onload , расширение может проверить, сработало ли onload используя свойство document.readyState .
document_start {: #document_start } нить Скрипты внедряются после любых файлов из css , но до того, как будет создан какой-либо другой DOM-элемент или запущен какой-либо другой скрипт.
document_end {: #document_end } нить Скрипты внедряются сразу после завершения формирования DOM-дерева, но до загрузки таких вложенных ресурсов, как изображения и фреймы.

Укажите рамки

Поле "all_frames" позволяет расширению указать, следует ли внедрять файлы JavaScript и CSS во все фреймы, соответствующие указанным требованиям URL, или только в самый верхний фрейм во вкладке.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Имя Тип Описание
all_frames {: #all_frames } логический Необязательный параметр. По умолчанию — false , что означает, что учитывается только верхний кадр.

Если указано значение true , внедрение будет происходить во все фреймы, даже если фрейм не является самым верхним фреймом на вкладке. Каждый фрейм проверяется независимо на соответствие требованиям URL-адреса; внедрение в дочерние фреймы не будет производиться, если требования URL-адреса не соблюдены.

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

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

Пример можно реализовать с помощью window.postMessage :

var port = chrome.runtime.connect();

window.addEventListener("message", function(event) {
  // We only accept messages from ourselves
  if (event.source != window)
    return;

  if (event.data.type && (event.data.type == "FROM_PAGE")) {
    console.log("Content script received: " + event.data.text);
    port.postMessage(event.data.text);
  }
}, false);
document.getElementById("theButton").addEventListener("click",
    function() {
  window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);

Страница, не являющаяся расширением, example.html, отправляет сообщения самой себе. Это сообщение перехватывается и анализируется скриптом содержимого, а затем отправляется в процесс расширения. Таким образом, страница устанавливает канал связи с процессом расширения. Обратный процесс возможен аналогичным образом.

Оставайтесь в безопасности

Хотя изолированные миры обеспечивают дополнительный уровень защиты, использование скриптов контента может создавать уязвимости как в расширении, так и на веб-странице. Если скрипт контента получает контент с отдельного веб-сайта, например, отправляя XMLHttpRequest , следует тщательно фильтровать контент на предмет атак межсайтового скриптинга (XSS) перед его внедрением. Для предотвращения атак типа «человек посередине» используйте только протокол HTTPS.

Обязательно отфильтруйте страницы на наличие вредоносного ПО. Например, опасны следующие шаблоны:

var data = document.getElementById("json-data")
// WARNING! Might be evaluating an evil script!
var parsed = eval("(" + data + ")")
var elmt_id = ...
// WARNING! elmt_id might be "); ... evil script ... //"!
window.setTimeout("animate(" + elmt_id + ")", 200);

Вместо этого отдавайте предпочтение более безопасным API, которые не запускают скрипты:

var data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
var parsed = JSON.parse(data);
var elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(function() {
  animate(elmt_id);
}, 200);