Как получить доступ к объектам `window` (целевой странице), когда установлены значения @grant?

Допустим, я работаю со следующей веб-страницей:

<html>
<body>
<span id="click">click me</span>
<script>
var hello = function() {
    alert('hello');
}

document.getElementById('click').addEventListener('click', function(e) {
    hello();
});
</script>
</body>
</html>

и мой сценарий Greasemonkey:

// ==UserScript==
// @name        My Script
// @include     http://example.com/hello.html
// @version     1
// @grant       none
// ==/UserScript==

window.hello = function() {
    alert('goodbye');
}

С отключенным скриптом Greasemonkey, нажав #click элемент на странице отображает предупреждение "привет". Если скрипт включен, при щелчке на элементе появляется предупреждение "до свидания".

Достаточно просто. hello функция с веб-страницы заменяется функцией в скрипте Greasemonkey.

Теперь предположим, что я хочу использовать API Greasemonkey. Когда я установил @grant значение к действительному значению, отличному от 'none' (например, // @grant GM_setClipboard) [что заставляет Greasemonkey запускать сценарий как "сценарий содержимого", а не в области видимости страницы, как в случае с "none"], сценарий Greasemonkey не работает.

window.hello больше не нацеливается на правильный объект на странице.

Замена window.hello с unsafeWindow.hello похоже, что это будет работать, но вместо этого в консоли JS выдается следующая ошибка:

Ошибка: в доступе отказано в доступе к объекту

Как я могу переписать сценарий Greasemonkey, имея @grant GM_setClipboard установить цель и заменить оригинал hello функция на странице?

Системная информация:

  • Windows 7 64-битная
  • Firefox 32.0
  • Greasemonkey 2.2

1 ответ

Решение

Когда вы установите @grant значение, отличное от none, Greasemonkey активирует свою песочницу, а Greasemonkey 2.0 радикально изменил обработку unsafeWindow.

Теперь, чтобы создать или перезаписать переменные в области целевой страницы, вы должны правильно выбрать один из методов. НАПРИМЕР:

Читать:

  • Простая переменная:

    Target page sets:       var foo = "bar";
    GM script can read:     unsafeWindow.foo    //-- "bar"
    
  • Простой объект:

    Target page sets:       var obj = {A: 1};
    GM script can read:     unsafeWindow.obj    //-- Object { A: 1 }
    
  • Сложный объект: это не всегда возможно.

Звонить:

  • Простая функция:

    Target page sets:       function func () {console.log ('Hi');}
    GM script can call:     unsafeWindow.func() //-- "Hi"
    
  • Сложная функция: это не всегда возможно.

Написать / Установить:

  • Простая переменная:

    unsafeWindow.foo = "Apple";
    
  • Простой объект:

    var gmObject        = {X: "123"};
    unsafeWindow.obj    = cloneInto (gmObject, unsafeWindow);
    
  • Простая функция:

    function gmFunc () {
        console.log ("Lorem ipsum");
        //-- Can use GM_ functions in here! :)
    }
    unsafeWindow.func   = exportFunction (gmFunc, unsafeWindow);
    

Рассмотрим этот HTML:

<button id="helloBtn">Say "Hello".</button>

И этот JavaScript:

var simpleGlobalVar = "A simple, global var in the page scope.";
var globalObject    = {Letter: "A", Number: 2};

function simpleFunction () {
    console.log ("The target page's simpleFunction was called.");
}

var sayHello = function() {
    console.log ('Hello.');
}

document.getElementById ('helloBtn').addEventListener ('click', function () {
    sayHello ();
} );

который вы можете увидеть вживую на этой странице jsFiddle.

Если вы установите и запустите этот скрипт Greasemonkey для этой страницы:

// ==UserScript==
// @name     _Demonstrate accessing target-page variables with @grant values set
// @include  http://fiddle.jshell.net/sepwL7n6/*/show/
// @require  http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @grant    GM_addStyle
// ==/UserScript==

console.log ("*** Greasemonkey script start.");

$("body").append ('<div id="gmArea">Added by Greasemonkey:<p></p></div>');
$("#gmArea > p:first").append ('<button id="gmShow">Access select target-page variables and functions</button>');
$("#gmArea > p:first").append ('<button id="gmChange">Change javascript things in the target-page scope.</button>');

$("#gmShow").click ( function () {
    //-- Access things from the target-page scope:
    console.log ("----------------");
    console.log ("==> simpleGlobalVar is: ", unsafeWindow.simpleGlobalVar);
    console.log ("==> globalObject    is: ", unsafeWindow.globalObject);
    console.log ("==> Calling target's simpleFunction():");
    unsafeWindow.simpleFunction ();

    //-- WARNING! This next technique is not robust, but works in some cases.
    console.log ("==> Calling target's button's click().");
    unsafeWindow.document.getElementById ('helloBtn').click ();
} );

$("#gmChange").click ( function () {
    this.disabled = true;   //-- Can only click once.
    unsafeWindow.simpleGlobalVar    = "Simple var... Intercepted by GM!";
    unsafeWindow.globalObject       = cloneInto (gmObject, unsafeWindow);
    unsafeWindow.sayHello           = exportFunction (sayHello, unsafeWindow);
    console.log ("==> Target page objects were changed.");
} );

var gmMessageStr    = "Function... Intercepted by GM, but also can use GM_ functions!";
function sayHello () {
    sayHello.K      = (sayHello.K  ||  0) + 1;
    console.log (gmMessageStr);
    GM_addStyle ('body {background: ' + (sayHello.K % 2  ?  "lime"  :  "white") + ';}');
}
var gmObject        = {message: "Object overridden by GM."};


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


Заметки:

  1. Это все специфично для Firefox.
  2. Для кросс-платформенного кода и для некоторых сложных ситуаций вы можете использовать вместо этого инъекцию скрипта. Но внедренный код не может получить прямой доступ GM_ функции.
  3. Обратите внимание, что эти методы работают только для глобальных переменных и функций javascript.

Оберните свой код в лямбда-функцию, например:

(function(window){      // and more arguments if you need it

  console.log(window);  // here, should be the real 'window'

})(window.unsafeWindow)
Другие вопросы по тегам