是否有可能通过脚本确定Google Chrome是否处于隐身模式?
编辑:实际上我是想知道是否可以通过用户脚本来确定,但是答案假设JavaScript在网页上运行。我已经在这里重新提出了问题(链接),关于用户脚本的问题。
是否有可能通过脚本确定Google Chrome是否处于隐身模式?
编辑:实际上我是想知道是否可以通过用户脚本来确定,但是答案假设JavaScript在网页上运行。我已经在这里重新提出了问题(链接),关于用户脚本的问题。
这个答案的功能取决于Chrome的版本。最近的评论是在v90
上运行正常。
是的,文件系统API在隐身模式下被禁用。当您处于隐身模式和非隐身模式时,请查看https://jsfiddle.net/w49x9f1a/。
示例代码:
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
console.log("check failed?");
} else {
fs(window.TEMPORARY,
100,
console.log.bind(console, "not in incognito mode"),
console.log.bind(console, "incognito mode"));
}
if ('storage' in navigator && 'estimate' in navigator.storage) {
const {usage, quota} = await navigator.storage.estimate();
console.log(`Using ${usage} out of ${quota} bytes.`);
if(quota < 120000000){
console.log('Incognito')
} else {
console.log('Not Incognito')
}
} else {
console.log('Can not detect')
}
一种方法是访问唯一的URL,然后检查CSS是否将指向该URL的链接视为已访问。
你可以在“探测无痕模式”(链接失效)中看到一个例子。
在main.html
中添加一个iframe,
<iframe id='testFrame' name='testFrame' onload='setUniqueSource(this)' src='' style="width:0; height:0; visibility:hidden;"></iframe>
以及一些 JavaScript 代码:
function checkResult() {
var a = frames[0].document.getElementById('test');
if (!a) return;
var color;
if (a.currentStyle) {
color = a.currentStyle.color;
} else {
color = frames[0].getComputedStyle(a, '').color;
}
var visited = (color == 'rgb(51, 102, 160)' || color == '#3366a0');
alert('mode is ' + (visited ? 'NOT Private' : 'Private'));
}
function setUniqueSource(frame) {
frame.src = "test.html?" + Math.random();
frame.onload = '';
}
然后在加载到 iFrame 中的 test.html
文件中:
<style>
a:link { color: #336699; }
a:visited { color: #3366A0; }
</style>
<script>
setTimeout(function() {
var a = document.createElement('a');
a.href = location;
a.id = 'test';
document.body.appendChild(a);
parent.checkResult();
}, 100);
</script>
注意: 从文件系统尝试此操作可能会导致 Chrome 产生"不安全的JavaScript"警告。但是,如果通过web服务器进行服务,则可以正常工作。
:visited
相关的隐私变更:https://hacks.mozilla.org/2010/03/privacy-related-changes-coming-to-css-vistited/ - Denilson Sá MaiaChromium:类似于Vinnie James的答案,调用navigator.storage.estimate(),获取quota属性并将其与performance.memory.jsHeapSizeLimit进行比较。如果quota属性小于jsHeapSizeLimit,则为隐身模式。如果jsHeapSizeLimit未定义,则使用1073741824(1 GiB)。
Safari for macOS:在不存在的推送服务器上使用safari.pushNotification.requestPermission,并获取错误。如果错误中没有出现“gesture”,则处于私密模式。
Safari for iOS:创建一个iframe,并使用contentWindow.applicationCache添加错误事件监听器。如果错误触发,则为私密模式。
Firefox:在私密窗口中,navigator.serviceWorker将未定义。
Internet Explorer:在InPrivate模式下,window.indexedDB将未定义。
你可以在我在GitHub上提供的detectIncognito脚本中看到这些方法的实现。
更新这似乎不再起作用了
这里使用了promise来等待异步代码设置标志,以便我们可以在后续同步使用它。
let isIncognito = await new Promise((resolve, reject)=>{
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) reject('Check incognito failed');
else fs(window.TEMPORARY, 100, ()=>resolve(false), ()=>resolve(true));
});
if(isIncognito) alert('in incognito');
else alert('not in incognito');
注意,要使用await
,您需要在异步函数内部。如果不是,请将所有代码都包装在其中以便能够使用。
isIncognito
成为一个只能为true或false的布尔值,我能否用resolve(false)
替换reject('Check incognito failed')
?谢谢。 - Ryanawait
关键字必须放在一个async
函数内部才能正常使用。因此,我认为这段代码不会起作用。 - Ryanreject
,则会引发异常并且不会返回值,因此根据代码编写方式,isIncognito
始终为true
或false
。但是,如果您想要返回一个值,也可以使用“resolve”。这可能取决于环境如何处理全局范围内的等待?它在全局范围内的Chrome控制台上运行,但您可以尝试将所有内容包装在(async function() { 'everything here' })();
中,以便在异步函数内运行。 - aljgomisIncognito
而不是结果,然后在异步函数中运行它:let isIncognito = new Promise(...etc)
,然后在其他地方你可以有一个函数,例如(async function() { if( !(await isIncognito) ) {alert('not incognito') } })();
。 - aljgom这是建议的答案,使用ES6语法编写并稍微整理了一下。
const isIncognito = () => new Promise((resolve, reject) => {
const fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
reject('Cant determine whether browser is running in incognito mode!');
}
fs(window.TEMPORARY, 100, resolve.bind(null, false), resolve.bind(null, true));
});
// Usage
isIncognito()
.then(console.log)
.catch(console.error)
基于Alok的答案的快速函数(注意:这是异步的)
更新 - 不再起作用
function ifIncognito(incog,func){
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) console.log("checking incognito failed");
else {
if(incog) fs(window.TEMPORARY, 100, ()=>{}, func);
else fs(window.TEMPORARY, 100, func, ()=>{});
}
}
用法:
ifIncognito(true, ()=>{ alert('in incognito') });
// or
ifIncognito(false, ()=>{ alert('not in incognito') });
其他答案似乎在最近的Chrome版本中已经不再有效。
这个想法是找出存储估计值,以确定标签是否为隐身模式。隐身标签的存储大小较小。
在普通窗口和隐身窗口中运行此代码,并记录配额大小。
const {quota} = await navigator.storage.estimate();
console.log(quota);
使用配额大小差异来实现隐身模式检测的逻辑。
以下逻辑适用于Chrome v105:
const { quota } = await navigator.storage.estimate();
if (quota.toString().length === 10) {
console.log("gotcha: this is incognito tab"); //quota = 1102885027
} else {
console.log("this is a normal tab"); //quota = 296630877388
}