Reputation: 259
I am writing a Chrome extension that needs to prevent webpages from triggering the document visibilitychange event. At the very least I need to be able to overwrite the document.visibilityState (even though it is a read-only property). If not possible, since this extension is for my purposes only and will not be on the Chrome extension store, is there a way I can config my Chrome Browser to achieve what I want? I only need to use this extension while Chrome "Developer Mode" is on, no other time.
I hope someone can think of a creative way to achieve this. Thank you.
Please note! There was a solution in an answer 4 years ago that no longer takes effect in newer versions of Chrome: Spoof or disable the Page Visibility API
Test it out yourself:
// This codes worked 4 years ago but not anymore
var c='(function(){var a=Node.prototype.addEventListener;Node.prototype.addEventListener=function(e){if(e=="visibilitychange"||e=="webkitvisibilitychange"){}else a.apply(this,arguments)}})()'
, E=document.documentElement;
E.setAttribute('onreset', c);
E.dispatchEvent(new CustomEvent('reset'));
E.removeAttribute('onreset');
// THIS WILL STILL LOG THE STATES EVEN WITH THE ABOVE CODE RUNNING
document.addEventListener("visibilitychange", function() {
console.log( document.visibilityState );
});
If its not possible in Chrome, is there Firefox/Safari/Opera Browser code that can achieve this?
Upvotes: 10
Views: 8212
Reputation: 388
This userscript works well in my case. It's code is relatively compact.
unsafeWindow.onblur = null;
unsafeWindow.blurred = false;
unsafeWindow.document.hasFocus = function () {return true;};
unsafeWindow.window.onFocus = function () {return true;};
Object.defineProperty(document, "hidden", { value : false});
Object.defineProperty(document, "mozHidden", { value : false});
Object.defineProperty(document, "msHidden", { value : false});
Object.defineProperty(document, "webkitHidden", { value : false});
Object.defineProperty(document, 'visibilityState', { get: function () { return "visible"; } });
unsafeWindow.document.onvisibilitychange = undefined;
for (event_name of ["visibilitychange",
"webkitvisibilitychange",
"blur", // may cause issues on some websites
"mozvisibilitychange",
"msvisibilitychange"]) {
window.addEventListener(event_name, function(event) {
event.stopImmediatePropagation();
}, true);
}
Note that unsafeWindow
is only available inside a userscript host extension (like Tampermonkey) and is an unwrapped window
object as seen by a page.
Upvotes: 1
Reputation: 39
I found a great solution on StackExchange:
Object.defineProperty(document, 'visibilityState', {value: 'visible', writable: true});
Object.defineProperty(document, 'hidden', {value: false, writable: true});
document.dispatchEvent(new Event("visibilitychange"));
In case anyone has trouble getting the above solution or any of the other solutions on this page to work inside their browser extension (like I did), the solution is to add the script that spoofs the visibility API directly to the DOM.
In manifest.json I added the following:
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["disable.js"]
}
]
And then I used disable.js to add a script that actually manipulates the page.
let s = document.createElement("script");
s.setAttribute("id", "yourScriptID");
s.textContent = `
/**
* Monitors changes to the page visibility.
*/
document.addEventListener('visibilitychange', () => {
/**
* Uncomment this line to debug whether the
* Page Visibility API is being spoofed.
* console.log('Document.hidden = "${document.hidden}".');
**/
Object.defineProperty(document, 'visibilityState', {
value: 'visible',
writable: true,
});
Object.defineProperty(document, 'hidden', { value: false, writable: true });
});`;
(document.head || document.documentElement).appendChild(s);
The reason for doing things this way is that "content scripts live in an isolated world."
What this means is that they have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page.
You'll find the following discussion very helpful when it comes to understanding how content scripts interact with pages and how to safely/properly inject scripts via your extension:
Access variables and functions defined in page context using a content script
HTH, Mwale
Upvotes: 2
Reputation: 4107
Here's my solution:
for (event_name of ["visibilitychange", "webkitvisibilitychange", "blur"]) {
window.addEventListener(event_name, function(event) {
event.stopImmediatePropagation();
}, true);
}
I added the blur
event because the video I wanted to skip (everfi.net) used it to detect when I switched windows. Blocking that event along with visibilitychange
and webkitvisibilitychange
did the trick :)
I also modified the extension's manifest so that it works inside iframes.
Full code (chrome extension): https://github.com/NavinF/dont
Confirmed working with the following dog tags:
Google Chrome 63.0.3239.132 (Official Build) (64-bit)
Revision 2e6edcfee630baa3775f37cb11796b1603a64360-refs/branch-heads/3239@{#709}
OS Mac OS X
JavaScript V8 6.3.292.49
Command Line /Applications/Google Chrome.app/Contents/MacOS/Google Chrome --flag-switches-begin --flag-switches-end
Upvotes: 24