Применить Greasemonkey/Tampermonkey/userscript к iframe?

Название довольно много:

Можно ли добавить Greasemonkey script на iframed-сайт?

Если да, то как?

Спасибо.

Ответ 1

В Greasemonkey (And Tampermonkey и большинстве пользовательских скриптов) скрипт автоматически запускает iframe, если он соответствует директивам @include, @exclude и/или @match.
И, популярный вопрос, как остановить Greasemonkey от запуска iframes.

Итак, если ваш сценарий имеет совпадение:

@match https://fiddle.jshell.net/*

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


Если вы хотите использовать только простой контент:

Тогда вы бы проверили свойство window.self.
Например, предположим, что у вас была целевая страница, например:

<body>
    <h1>I'm some webpage, either same-domain or not.</h1>
    <iframe src="//domain_B.com/somePath/somePage.htm">
...

Тогда вы можете использовать скрипт как:

// ==UserScript==
// @name    _Fires specially on domain_B.com iframes
// @match   *://domain_B.com/somePath/*
// ==/UserScript==

if (window.top === window.self) {
    //--- Script is on domain_B.com when/if it is the MAIN PAGE.
}
else {
    //--- Script is on domain_B.com when/if it is IN AN IFRAME.
    // DO YOUR STUFF HERE.
}

Важный:

С выпуском Greasemonkey 4 обработка iframes сильно ограничена (и многие другие, кроме того, нарушены).
Он по- прежнему работает должным образом с Tampermonkey, Violentmonkey и практически со всеми другими движками usercript.
Настоятельно рекомендуется (в том числе самой Greasemonkey) не использовать Greasemonkey 4 или более позднюю версию.

Ответ 2

Обратите внимание, что если вы создаете расширение chrome для вашего пользователя, вам также нужно добавить "all_frames": true в ваш манифест или расширение не будет работать на iframes.

Пример:

"content_scripts": [
    {
      "matches": ["*://*/*"],
      "all_frames": true,
      "js":["dont.js"],
      "run_at":"document_start"
    }
  ]

Ответ 3

Это решение для случаев, когда у iframe нет места для запуска @include или @match.

Это работает с Greasemonkey 4.

Мы должны ждать загрузки каждого кадра, прежде чем мы сможем работать с ним. Я делаю это с помощью waitForKeyElements.js, который ожидает элементы, соответствующие заданному селектору CSS, точно так же, как просматривая совпадения в document.querySelectorAll("selector"), а затем применяет данную функцию к ответу:

// ==UserScript==
// @include https://blah.example.com/*
// @require https://git.io/waitForKeyElements.js
// ==/UserScript==

function main(where) {
  // do stuff here with  where  instead of  document
  // e.g. use  where.querySelector()  in place of  document.querySelector()
  // and add stylesheets with  where.head.appendChild(stylesheet)
}

main(document); // run it on the top level document (as normal)

waitForKeyElements("iframe, frame", function(elem) {
  elem.removeAttribute("wfke_found"); // cheat wfke been_there, use our own
  for (let f=0; f < frames.length; f++) {
    if (!frames[f].document.body.getAttribute("been_there")) {

      main(frames[f].document);

      frames[f].document.body.setAttribute("been_there", 1);
    }
  }
});

Обратите внимание, что выбранный элемент является просто заполнителем, указывающим, что загружен iframe. Мы удаляем трекер "бывал там" из waitForKeyElements потому что кадр может быть снова загружен позже (мы не можем просто использовать этот iframe потому что его содержимое загружено в другом месте).

Когда мы знаем, что фрейм загружен, мы перебираем каждый фрейм и ищем наш маркер, HTML-атрибут в body фрейма, называемый been_there (например, <body been_there="1">). Если он отсутствует, мы можем запустить нашу функцию main() для документа фрейма. Когда мы закончим, мы добавим атрибут been_there чтобы мы больше не запускались.