Как я могу заставить скрипт содержимого надстройки firefox вводить и запускать скрипт перед другими скриптами страниц?

Я работаю над расширением/надстройкой для браузера. У нас это работает в Chrome, поэтому я пытаюсь заставить его работать в Firefox.

Я получил дополнение для загрузки в Firefox Developer Edition 49.0a2 (25 июля 2016 г.).

В моем расширении используется content_script, для которого задано значение run_at: document_start, поэтому оно может внедрить тег script перед запуском других скриптов страницы, чтобы сделать объект глобально доступным для веб-сайтов.

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

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

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

// inject in-page script
console.log('STEP 1, this always happens first')
var scriptTag = document.createElement('script')
scriptTag.src = chrome.extension.getURL('scripts/inpage.js')
scriptTag.onload = function () { this.parentNode.removeChild(this) }
var container = document.head || document.documentElement
// append as first child
container.insertBefore(scriptTag, container.children[0])

Теперь, если файл scripts/inpage.js просто запускает журнал, например

console.log('STEP 2, this should always run second')

И я захожу на страницу с таким скриптом:

console.log('Step 3, the page itself, should run last')

На практике Шаг 2 и Шаг 3 выполняются в недетерминированном порядке.

Большое спасибо!

У меня есть совместимая с Firefox версия скрипта в общедоступном репозитории на специальной ветке, если вы осмелитесь попробовать сами: https://github.com/MetaMask/metamask-plugin/tree/FirefoxCompatibility


person DanF    schedule 25.07.2016    source источник
comment
Это строка, в которой я добавляю тег скрипта к текущей странице. Когда я делаю это с журналами, я вижу, что этот код запускается перед скриптом страницы, но сам внедренный скрипт не всегда запускается перед скриптами страницы. github.com/MetaMask/metamask-plugin/ blob/совместимость с Firefox/   -  person DanF    schedule 26.07.2016


Ответы (2)


Динамически вставленный скрипт с внешним источником (<script src>) не блокирует выполнение скриптов, поэтому нет гарантии, что ваш скрипт загрузится. Если ваше расширение работало в Chrome, то это была просто удача.

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

var actualCode = `
// Content of scripts/inpage.js here
`;

var s = document.createElement('script');
s.textContent = actualCode;
(document.head || document.documentElement).appendChild(s);
s.remove();

В идеале ваш скрипт сборки должен читать scripts/inpage.js, сериализовать его в строку и помещать в переменную actualCode. Но если inpage.js — это всего несколько строк кода, то можно использовать вышеописанное.

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

Если ваш сценарий содержимого не создается сценарием сборки, и вы все равно хотите хранить сценарии отдельно, вы также можете использовать синхронный XMLHttpRequest для получения сценария. Синхронный XHR устарел по соображениям производительности, поэтому используйте его на свой страх и риск. Код расширения обычно связан с вашим расширением, поэтому использование sync xhr должно быть с низким уровнем риска:

// Note: do not use synchronous XHR in production!
var x = new XMLHttpRequest();
x.open('GET', chrome.runtime.getURL('scripts/inpage.js'), false);
x.send();
var actualCode = x.responseText;

var s = document.createElement('script');
s.textContent = actualCode;
(document.head || document.documentElement).appendChild(s);
s.remove();
person Rob W    schedule 25.07.2016
comment
Я просто добавлю примечание, что в IRC вы также указали, что FireFox предоставляет дополнительные способы для четкого достижения моих целей по предоставлению API на главную страницу из сценария контента через помощники экспорта: developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM / - person DanF; 26.07.2016
comment
@DanF Component.utils.exportFunction и т. д. пока недоступны (и они не станут доступны в пространстве имен Components; WebExtensions не могут использовать Components). Когда патч на bugzil.la/1280482 появится, вы сможете получить ту же функциональность, используя глобальный метод exportFunction. - person Rob W; 26.07.2016

Если вы используете надстройку на основе bootstrap.js, вы можете использовать фреймскрипт и DOMWindowCreated для работы с документом даже до того, как HTML DOM (прошлые основы document.body и т. д.) отобразится — https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Frame_script_environment#Events — внутренний HTML будет быть доступным, но ни один сценарий не будет выполнен. Вы можете поместить свой встроенный скрипт вверху, как упоминал @Rob.

person Noitidart    schedule 26.07.2016