Взаимодействие между привилегированными и непривилегированными страницами

Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

Отправка данных из непривилегированного документа в хром

Простым способом отправки данных с веб-страницы на расширение является использование пользовательских событий DOM. В оверлее browser.xul вашего расширения, написать код, который прослушивает событие пользовательского DOM. Здесь мы вызываем событие MyExtensionEvent.

var myExtension = {
  myListener: function(evt) {
    alert("Received from web page: " +
          evt.target.getAttribute("attribute1") + "/" + 
          evt.target.getAttribute("attribute2"));
  }
}
document.addEventListener("MyExtensionEvent", function(e) { myExtension.myListener(e); }, false, true);
// Последнее значение - специфичное для Mozilla значение, чтобы указать, что недоверенный контент разрешен для запуска события.

Данные с веб-страницы (непривилегированный код) будут значениями attribute1 и attribute2. Чтобы вызвать alert () в приемнике и передать данные с веб-страницы, напишите код, например, такой на веб-странице:

var element = document.createElement("MyExtensionDataElement");
element.setAttribute("attribute1", "foobar");
element.setAttribute("attribute2", "hello world");
document.documentElement.appendChild(element);

var evt = document.createEvent("Events");
evt.initEvent("MyExtensionEvent", true, false);
element.dispatchEvent(evt);

Этот код создает произвольный элемент -- <MyExtensionDataElement /> -- и вставляет его в DOM веб-страницы. Значения устанавливаются для двух произвольных атрибутов элемента. Их также можно назвать любыми, которые вам нравятся, но мы выбрали атрибуты attribute1 и attribute2. Наконец, код создает и отправляет пользовательское событие с именем MyExtensionEvent - подобно стандартным событиям нажатия DOM, которые вы ловите с обработчиками onclick. Событие всплывает на веб-странице и достигает расширения (привилегированный код), где ваш слушатель ловит его и читает значения атрибутов из элемента DOM, где возникло событие.

( Чтобы лучше гарантировать, что другие не реализуют одно и то же событие с другим значением, можно либо присоединить пространство имен к <MyExtensionDataElement /> и проверить обработчик события для правильного свойства namespaceURI, либо согласно спецификации DOM, использовать initEvent () с именем события, которое само является пространством имен (только для имен XML) : " Также настоятельно рекомендуется, чтобы третьи стороны, добавляющие свои собственные события, использовали свой собственный префикс, чтобы избежать путаницы и уменьшить вероятность конфликтов с другими новыми событиями. ")

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

var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                  .getInterface(Components.interfaces.nsIWebNavigation)
                  .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                  .rootTreeItem
                  .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                  .getInterface(Components.interfaces.nsIDOMWindow);
mainWindow.document.addEventListener("MyExtensionEvent", function(e) { myExtension.myListener(e); }, false, true);

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

Примечание: Если вы используете postMessage () HTML5 для отправки сообщения из непривилегированного кода в привилегированный код, добавление 'true' к концу прослушивателя событий в вашем привилегированном chrome коде позволит получить сообщение.

 

document.addEventListener("message", function(e) { yourFunction(e); }, false, true);

Отправка данных из хрома в непривилегированный документ

Чтобы «ответить» на веб-страницу (например, код возврата), ваше расширение может установить атрибут или присоединить дочерние элементы в элементе назначения события (<MyExtensionDataElement /> в этом примере).

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

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

Существует только одно расширение, но может быть много активных веб-страниц. Таким образом, чтобы вызвать нужное событие на правой странице, мы должны сообщить расширению, на какую страницу вызывать. Необходимая для этого информация содержится в файле evt.target.ownerDocument.

Мы можем расширить приведенный выше пример с некоторой передачи данных из расширения на веб-страницу. В следующем примере кода два метода объединены: Установка дополнительного атрибута в исходном целевом элементе события и создание нового сообщения о событии с новым целевым элементом события. Для этого нам нужно определить оригинальный целевой элемент глобально. Нам нужен новый триггер события на веб-странице и некоторый код, чтобы показать, что сообщение о событии действительно прибыло. В расширении нам нужно отправить сообщение о событии на нужную веб-страницу.

Код, содержащий обратный вызов, может выглядеть так:

В расширении:

var myExtension = 
{
  myListener: function(evt) 
  {
    alert("Received from web page: " + 
           evt.target.getAttribute("attribute1") + "/" + 
           evt.target.getAttribute("attribute2"));

/* the extension answers the page*/
    evt.target.setAttribute("attribute3", "The extension");
   
    var doc = evt.target.ownerDocument;
   
    var AnswerEvt = doc.createElement("MyExtensionAnswer");
    AnswerEvt.setAttribute("Part1", "answers this.");

    doc.documentElement.appendChild(AnswerEvt);

    var event = doc.createEvent("HTMLEvents");
    event.initEvent("MyAnswerEvent", true, false);
    AnswerEvt.dispatchEvent(event);
  }
}

document.addEventListener("MyExtensionEvent", function(e) { myExtension.myListener(e); }, false, true);
//  Последнее значение - специфичное для Mozilla значение, чтобы указать, что недоверенный контент разрешен для запуска события. 

На веб-странице:

document.addEventListener("MyAnswerEvent",function(e) { ExtensionAnswer(e); },false);

var element;

function CallExtension()
{
  var element = document.createElement("MyExtensionDataElement");
  element.setAttribute("attribute1", "foobar");
  element.setAttribute("attribute2", "hello world");
  document.documentElement.appendChild(element);
  var evt = document.createEvent("Events");
  evt.initEvent("MyExtensionEvent", true, false);
  element.dispatchEvent(evt);
}

function ExtensionAnswer(EvtAnswer)
{
  alert(element.getAttribute("attribute3") + " " +
        EvtAnswer.target.getAttribute("Part1"));
}

Основной пример подобной идеи, расширение передает информацию через атрибуты и запускает событие на div на странице, тут.

Хром-подобные сообщения: запрос json с обратным вызовом json

Веб-страница:

<html>
  <head>
    <script>
      var something = {
        send_request: function(data, callback) { // analogue of chrome.extension.sendRequest
          var request = document.createTextNode(JSON.stringify(data));

          request.addEventListener("something-response", function(event) {
            request.parentNode.removeChild(request);

            if (callback) {
              var response = JSON.parse(request.nodeValue);
              callback(response);
            }
          }, false);

          document.head.appendChild(request);

          var event = document.createEvent("HTMLEvents");
          event.initEvent("something-query", true, false);
          request.dispatchEvent(event);
        },

        callback: function(response) {
          return alert("response: " + (response ? response.toSource() : response));
        }
      }
    </script>
  </head>
  <body>
    <button onclick="return something.send_request({foo: 1}, something.callback)">send {foo: 1} with callback</button>
    <button onclick="return something.send_request({baz: 3}, something.callback)">send {baz: 3} with callback</button>
    <button onclick="return something.send_request({mozilla: 3})">send {mozilla: 3} without callback</button>
    <button onclick="return something.send_request({firefox: 4}, something.callback)">send {firefox: 4} with callback</button>
  </body>
</html>

Наложите оверлей на browser.xul в своем расширении:

var something = {
  listen_request: function(callback) { // analogue of chrome.extension.onRequest.addListener
    document.addEventListener("something-query", function(event) {
      var node = event.target;
      if (!node || node.nodeType != Node.TEXT_NODE)
        return;

      var doc = node.ownerDocument;
      callback(JSON.parse(node.nodeValue), doc, function(response) {
        node.nodeValue = JSON.stringify(response);

        var event = doc.createEvent("HTMLEvents");
        event.initEvent("something-response", true, false);
        return node.dispatchEvent(event);
      });
    }, false, true);
  },
 
  callback: function(request, sender, callback) {
    if (request.foo) {
      return setTimeout(function() {
      callback({bar: 2});
      }, 1000);
    }
 
    if (request.baz) {
      return setTimeout(function() {
      callback({quux: 4});
      }, 3000);
    }
 
    if (request.mozilla) {
      return alert("alert in chrome");
    }

    return callback(null);
  }
}

something.listen_request(something.callback);

Передача сообщения в хроме

Отправка структурированных данных

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

С атрибутами элементов и E4X это довольно просто. Однако вам нужно конвертировать данные в / из объектов E4X. И ваш chrome должен тщательно проверять каждое пройденное значение (вам нужно сделать это в любом случае).

var targetDoc = null;

function onLoad() {
  var iframe = document.getElementById("contentiframe");
  targetDoc = iframe.contentDocument;
  iframe.contentWindow.addEventListener("newStuff", receiveStuffFromPage, false);
}

function receiveStuffFromPage(event) {
  var uc = getEventData(event); // uc = unchecked data in form of E4X XML
  var stuff = {};
  stuff.id = sanitize.integer(uc.@id);
  stuff.name = sanitize.label(uc.@name);
}

function sendSomethingToPage (something) {
  var somethingXML = <something/>; // |something| object as E4X XML
  somethingXML.@id = something.id;
  somethingXML.@weight = something.weight;
  sendMsg("sendSomething", somethingXML);
}

/**
 * Отправить сообщение с chrome на страницу 
 * @param type {String} тип события. Получателю необходимо использовать 
 * при выполнении addEventListener (type, ...) 
 * @param dataXML {E4X} данные или детали 
 */
function sendMsg(type, dataXML) {
  var el = targetDoc.body;
  el.setAttribute("eventDataToPage", dataXML ? dataXML.toString() : "");
  var event = targetDoc.createEvent("Event")
  event.initEvent(type, true, true);
  el.dispatchEvent(event);
}

/**
 * Verifies that the event is indeed coming from our page
 * as expected, and returns the data for that event.
 * @returns {E4X} the (unchecked) detail data from the page.
 * You must check the data.
 * @see <https://developer.mozilla.org/en-US/docs/Code_snippets/
 * Interaction_between_privileged_and_non-privileged_pages#Security_notes>
 */
function getEventData(event) {
  if (event.target.ownerDocument != targetDoc)
    throw "event from unexpected source";
  return new XML(event.target.getAttribute("eventDataFromPage"));
}

Заметки о безопасности

  • Никогда не вызывайте JavaScript-функции веб-страницы из вашего расширения - это увеличивает вероятность создания брешей в безопасности, когда вредоносная веб-страница может обмануть браузер, чтобы запустить его код с расширенными привилегиями (как и ваше расширение), например, с помощью возможности удалять локальные файлы.
  • Настоятельно рекомендуется проверить источник события (через event.target.ownerDocument.location) и сделать так, чтобы ваше расширение игнорировало любые события со страниц, не связанных с вашим сервером.

Ресурсы

Обсуждения на форуме Mozillazine

Связь между HTML и вашим расширением

Смотрите также

Метки документа и участники

 Внесли вклад в эту страницу: Shychara
 Обновлялась последний раз: Shychara,