问题
I'm writing a userscript to prevent a website to set document.body.innerHTML
, this is a typical sign of a website detecting adblock:
(function() {
'use strict';
console.log("Loading ...");
Object.defineProperty(document.body, "innerHTML", {
set: function() {
console.log("malicious activity detected");
throw "Don't try to fool my adblock!";
}
});
console.log("Test setting document.body ...");
try {
document.body.innerHTML = "";
} catch (e) {
console.log(e);
}
}) ();
The above userscript works fine for Chrome+Tampermonkey. But its behavior on Firefox+Greasemonkey-4 is weird.
The output in console is:
Loading ...
Test setting document.body ...
malicious activity detected
Don't try to fool my adblock!
So the userscript loads successfully, and the setter is successfully hooked as well. But when after loading and I try in the console:
document.body.innerHTML = ""
it just sets the innerHTML
without throwing error, as if the hook has not been installed. I've tried all @run-at
options, but none of them works.
OTAH, if I use Object.defineProperty()
in console, then it works as expected. Therefore I conclude that Firefox doesn't respect Object.defineProperty()
from userscript.
You can also try visiting this website: https://connectwww.com. With the above userscript installed in Tampermonkey in Chrome, the adblock-detection on the website is successfully intercepted. But the userscript doesn't work on Firefox+Greasemonkey.
Why does Firefox not respect Object.defineProperty()
from a userscript? Is there any workaround?
A side note:
Some well-known userscript like anti-adblock-killer also works on Chrome but not Firefox for the above testing website, I guess it's due to the same reason.
回答1:
Greasemonkey 4 sandboxes your script even in @grant none
mode. (That is about the only thing that Greasemonkey 4 does better than Tampermonkey or Violentmonkey.)
So your script is setting the script's scope/copy of innerHTML
.
In Tampermonkey that is shared with the target page scope but, in Greasemonkey, the 2 scopes are more properly separated. So the page scope (and the default console) do not see the change.
In this case, I don't think the unsafeWindow methods can be made to work; you must inject the override code.
Here is a script that works with both Greasemonkey 4+ and Tampermonkey (and should on Violentmonkey too, but I didn't test that). It works on both Chrome and Firefox:
// ==UserScript==
// @name _Overriding Target page functions can be tricky with GM 4
// @match *://YOUR_SERVER.COM/YOUR_PATH/*
// @grant none
// @run-at document-start
// ==/UserScript==
/* eslint-disable no-multi-spaces */
console.log("Loading ...");
function overrideIt () {
//-- Necessary check because of scope madness in TM, VM, etc.
if (document.body.innerHTML) {
Object.defineProperty (document.body, "innerHTML", {
set: function () {
var scopeStr = (typeof GM === "object" && GM.info) ? "script" : "page";
console.log (`Malicious activity detected - ${scopeStr} scope`);
throw "Don't try to fool my adblock!";
}
} );
}
}
overrideIt ();
if (typeof unsafeWindow === "object") {
console.log ("unsafeWindow detected.");
addJS_Node (null, null, overrideIt);
}
console.log ("Test setting document.body ...");
try {
document.body.innerHTML = "";
} catch (e) {
console.log ("Caught: ", e);
}
//-- addJS_Node is a standard(ish) function
function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
var D = document;
var scriptNode = D.createElement ('script');
if (runOnLoad) {
scriptNode.addEventListener ("load", runOnLoad, false);
}
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
targ.appendChild (scriptNode);
}
来源:https://stackoverflow.com/questions/54489964/firefox-doesnt-respect-object-defineproperty-from-a-greasemonkey-script