Внедрение JS-функций на страницу из скрипта Greasemonkey в Chrome

У меня есть сценарий Greasemonkey, который отлично работает в Firefox и Opera. Однако мне сложно заставить его работать в Chrome. Проблема заключается во внедрении на страницу функции, которая может быть вызвана кодом со страницы. Вот что я пока делаю:

Сначала я получаю вспомогательную ссылку на unsafeWindow для Firefox. Это позволяет мне использовать один и тот же код для FF и Opera (и Chrome, как я думал).

var uw = (this.unsafeWindow) ? this.unsafeWindow : window;

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

uw.setConfigOption = function(newValue) {
    setTimeout(setConfigOption, 0, newValue);
}

Затем прямо в моем скрипте есть соответствующая функция:

setConfigOption = function(newValue) {
    // do something with it, e.g. store in localStorage
}

Наконец, я вставляю на страницу HTML-код со ссылкой для вызова функции.

var p = document.createElement('p');
p.innerHTML = '<a href="javascript:setConfigOption(1)">set config option to 1</a>';
document.getElementById('injection-point').appendChild(p);

Подводя итог: в Firefox, когда пользователь щелкает эту внедренную ссылку, он выполняет вызов функции для unsafeWindow, который затем запускает тайм-аут, который вызывает соответствующую функцию в контексте моего сценария GM, который затем выполняет фактическую обработку. (Поправьте меня, если я здесь не прав.)

В Chrome я просто получаю ошибку «Uncaught ReferenceError: setConfigOption is not defined». И действительно, ввод «window.setConfigOption» в консоль дает «undefined». В Firebug и консоли разработчика Opera функция есть.

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

Я бегло просмотрел альтернативы unsafeWindow в вики Greasemonkey, но все они выглядят довольно уродливые. Я полностью ошибаюсь здесь или мне следует более внимательно изучить эти вопросы?

РЕШЕНИЕ. Я подписался на Макс С. ' совет, и теперь он работает как в Firefox, так и в Chrome. Поскольку функции, которые мне должны были быть доступны для страницы, должны были быть вызваны обратно в обычные, я переместил весь свой скрипт на страницу, то есть он полностью заключен в функцию, которую он назвал 'main ()'.

Чтобы сделать дополнительную уродливость этого взлома немного более терпимой, я мог бы, по крайней мере, отказаться от использования unsafeWindow и wrappedJSObject сейчас.

Мне до сих пор не удалось заставить работать средство выполнения содержимого из вики Greasemonkey. Он должен делать то же самое, и, кажется, он выполняется нормально, но мои функции никогда не доступны для <a> элементов со страницы, например. Я еще не понял, почему это так.


person Henrik Heimbuerger    schedule 20.02.2010    source источник


Ответы (4)


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

РЕДАКТИРОВАТЬ: Вот как это делает расширение Nice Alert:

function main () {
  // ...
  window.alert = function() {/* ... */};
  // ...
}

var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
person Max Shawabkeh    schedule 20.02.2010
comment
Я попробовал «бегунок области содержимого» (который, если не ошибаюсь, делает именно это), и хотя функции были доступны для моего скрипта, они, похоже, не были доступны для страницы (например, тег привязки). У вас есть какой-нибудь пример кода или что-нибудь, на что вы можете ссылаться? Меня не очень беспокоит порядок казни. Я могу обойти это, когда мне удается вызвать внедренную функцию со страницы. :) - person Henrik Heimbuerger; 20.02.2010
comment
Порядок выполнения скрипта содержимого можно определить с помощью run_at инструкции code.google.com/chrome/extensions /content_scripts.html - person NVI; 20.02.2010
comment
re run_at: Я не пишу расширение Chrome (и я не собираюсь, слишком много работы для поддержки двух скриптов). Это просто сценарий Greasemonkey, который я пытаюсь сделать достаточно совместимым, чтобы Chrome мог выполнять «автоматическое преобразование» сценариев GM в расширения. - person Henrik Heimbuerger; 20.02.2010
comment
Хотя ваш изолированный пример работает для меня, я еще не получил его в моем полном сценарии. Я полагаю, в отличие от использования unsafeWindow, я не могу выполнить обратный вызов функций, которые сейчас находятся прямо в моем скрипте. Думаю, тогда мне придется все переместить в контекст страницы. Я попробую завтра. - person Henrik Heimbuerger; 20.02.2010
comment
@NV: run_at=document_start, вероятно, будет иметь проблемы с вставкой <script> в DOM, который еще не загружен. @hheimbuerger: да, оберните весь свой код в функцию, которая вставит его с помощью скрипта. - person Max Shawabkeh; 20.02.2010
comment
Теперь он работает как в FF, так и в Chrome, используя ваш подход. Я все еще немного озадачен, почему он не работает с бегунком контекста (не вижу реальной разницы), но на данный момент я отказался от использования этого метода. Спасибо за вашу помощь! - person Henrik Heimbuerger; 21.02.2010
comment
Спасибо - это как раз то, что мне нужно для моего собственного проекта. Разве не забавно, что сейчас 2010 год, и мы все еще задаем вопросы, вроде того, что этот Javascript работает в браузере X, почему он не работает в браузере Y? Интересно, какова общая стоимость всемирной продуктивности из-за такого рода проблем совместимости с браузерами? - person Duncan Bayne; 28.09.2010
comment
В моем случае я действительно должен выполнить обратный вызов в области пользовательского скрипта. Мой текущий план состоит в том, чтобы отбрасывать сообщения из области содержимого в модель DOM, которую область пользовательского скрипта будет проверять по таймеру. Когда отображается сообщение, пользовательский скрипт может вызвать функцию в своей собственной области с аргументами, взятыми из сообщения. - person joeytwiddle; 11.12.2010
comment
Хорошо, но если вы используете document.getElementsByTagName("head")[0], он будет работать в любом браузере без необходимости отката. И еще проще использовать внедрение сценария содержимого: могут быть доступны функции wiki.greasespot.net/Content_Script_Injection за пределами GreaseMonkey, просто написав window.functionName = function() {alert("test");}; - person baptx; 22.10.2016

У меня есть это :

contentcript.js:

function injectJs(link) {
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=link;
document.getElementsByTagName('head')[0].appendChild(scr)
//document.body.appendChild(scr);
}

injectJs(chrome.extension.getURL('injected.js'));

injected.js:

function main() {
     alert('Hello World!');
}

main();
person Bernard Choi    schedule 26.02.2010
comment
Это здорово! Также ярлык для людей, использующих jQuery: function injectjs(link) { $('<script type="text/javascript" src="'+link+'"/>').appendTo($('head')); } - person Piotrek Okoński; 29.03.2012

Я бегло ознакомился с альтернативами unsafeWindow вики Greasemonkey, но все они выглядят довольно некрасиво. Я полностью ошибаюсь здесь или мне следует более внимательно изучить эти вопросы?

Стоит посмотреть, ведь это единственная доступная опция. Я бы предпочел использовать взлом местоположения.

myscript.user.js:

function myFunc(){
  alert('Hello World!');
}

location.href="javascript:(function(){" + myFunc + "})()"

example.com/mypage.html

<script>
myFunc() // Hello World!
</script>

Конечно, это некрасиво. Но работает хорошо.


Метод Content Scope Runner, упомянутый Максом С., лучше, чем взлом местоположения, потому что его легче отлаживать.

person NVI    schedule 20.02.2010
comment
Не могли бы вы опубликовать пример того, как это можно использовать для определения функции, которая может быть вызвана привязкой или объектом Flash? - person Henrik Heimbuerger; 20.02.2010
comment
Что вы имеете в виду под якорем? - person NVI; 20.02.2010
comment
С тегом ‹a›, как в моем примере кода, например ‹A href=javascript:setConfigOption(1)›. (Я думаю, что если это сработает, то должно быть и от объекта Flash.) - person Henrik Heimbuerger; 20.02.2010
comment
myscript.user.js: location.href="javascript:(function(){window.setConfigOption=function(){ /* ... */ }})()" - person NVI; 20.02.2010
comment
Думаю, это тоже сработает, но я выбрал Макса С. ' На данный момент подход, похожий на бегунок содержания. Спасибо за вашу помощь! - person Henrik Heimbuerger; 21.02.2010
comment
В Firefox версии 39.0.3 Mozilla исправила известную уязвимость системы безопасности. Как побочный эффект, это сломало взлом локации. - person user136036; 23.02.2018

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

Этот ответ добавит javascript на страницу прямо из исходного кода. Он будет использовать шаблонные литералы ECMAScript 6 (ES6). чтобы без труда перенести на страницу многострочную строку javascript.

var script = document.createElement('script'); 
script.type = "text/javascript"; 
script.innerHTML = ` 
   function test() {
      alert(1);
   }
`;
document.getElementsByTagName('head')[0].appendChild(script);

Обратите внимание на обратные кавычки ``, которые определяют начало и конец многострочной строки.

person user136036    schedule 23.02.2018