Skip to content

Instantly share code, notes, and snippets.

@kdzwinel
Last active May 20, 2021 07:57
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save kdzwinel/783df9b129ae5c8443dd96c0d4ed9723 to your computer and use it in GitHub Desktop.
Save kdzwinel/783df9b129ae5c8443dd96c0d4ed9723 to your computer and use it in GitHub Desktop.
Small collection of solutions used to detect private mode
// credit https://www.bostonglobe.com
function isIE10OrLater(user_agent) {
var ua = user_agent.toLowerCase();
if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
return false;
}
var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
if (match && parseInt(match[1], 10) >= 10) {
return true;
}
// MS Edge Detection from this gist: https://gist.github.com/cou929/7973956
var edge = /edge/.exec(ua);
if (edge && edge[0] == "edge") {
return true;
}
return false;
}
function detectPrivateMode(callback) {
var is_private;
if (window.webkitRequestFileSystem) {
window.webkitRequestFileSystem(
window.TEMPORARY, 1,
function() {
is_private = false;
},
function(e) {
console.log(e);
is_private = true;
}
);
} else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
var db;
try {
db = window.indexedDB.open('test');
} catch(e) {
is_private = true;
}
if (typeof is_private === 'undefined') {
retry(
function isDone() {
return db.readyState === 'done' ? true : false;
},
function next(is_timeout) {
if (!is_timeout) {
is_private = db.result ? false : true;
}
}
);
}
} else if (isIE10OrLater(window.navigator.userAgent)) {
is_private = false;
try {
if (!window.indexedDB) {
is_private = true;
}
} catch (e) {
is_private = true;
}
} else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
// One-off check for weird sports 2.0 polyfill
// This also impacts iOS Firefox and Chrome (newer versions), apparently
// @see bglobe-js/containers/App.js:116
if (window.safariIncognito) {
is_private = true;
} else {
try {
window.openDatabase(null, null, null, null);
} catch (e) {
is_private = true;
}
try {
window.localStorage.setItem('test', 1);
} catch(e) {
is_private = true;
}
}
if (typeof is_private === 'undefined') {
is_private = false;
window.localStorage.removeItem('test');
}
}
retry(
function isDone() {
return typeof is_private !== 'undefined' ? true : false;
},
function next(is_timeout) {
callback(is_private);
}
);
}
// credit https://www.breakingviews.com/
function incognitoBrowsers() {
return new Promise(function(resolve) {
var on = function() {
resolve(!0)
}
, off = function() {
resolve(!1)
};
if (!window.webkitRequestFileSystem) {
if ('MozAppearance'in document.documentElement.style) {
var db = indexedDB.open('test');
return db.onerror = on,
void (db.onsuccess = off)
}
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
var isPrivate = !1;
try {
window.openDatabase(null, null, null, null)
} catch (_) {
isPrivate = !0
}
isPrivate ? on() : off()
}
return off()
}
window.webkitRequestFileSystem(0, 0, off, on)
})
}
/* eslint-disable consistent-return */
let on;
let off;
const isSafari = () => {
if (Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
return true;
}
if (
// eslint-disable-next-line no-undef
(!window.safari || safari.pushNotification).toString() === '[object SafariRemoteNotification]'
) {
return true;
}
try {
return window.localStorage && /Safari/.test(window.navigator.userAgent);
} catch (e) {
return false;
}
};
const isMozilla = () => 'MozAppearance' in document.documentElement.style;
const Webkit = () => {
if (window.webkitRequestFileSystem) {
window.webkitRequestFileSystem(window.TEMPORARY, 1, off, on);
return true;
}
};
const Mozilla = () => {
if (isMozilla()) {
const db = indexedDB.open('test');
db.onerror = on;
db.onsuccess = off;
return true;
}
};
const Safari = () => {
if (isSafari()) {
// iOS 11
// Origin: https://gist.github.com/cou929/7973956#gistcomment-2272103
try {
window.openDatabase(null, null, null, null);
} catch (e) {
on();
return true;
}
// Older Safari
try {
if (localStorage.length) off();
else {
localStorage.x = 1;
localStorage.removeItem('x');
off();
}
} catch (e) {
// Original gist: https://gist.github.com/jherax/a81c8c132d09cc354a0e2cb911841ff1
// Safari only enables cookie in private mode
// if cookie is disabled then all client side storage is disabled
// if all client side storage is disabled, then there is no point
// in using private mode
navigator.cookieEnabled ? on() : off(); // eslint-disable-line no-unused-expressions
}
return true;
}
};
const IE10Edge = () => {
if (!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)) {
on();
return true;
}
};
export const checkPrivate = (onCb, offCb) => {
on = onCb || (() => {});
off = offCb || (() => {});
Webkit() || Mozilla() || Safari() || IE10Edge() || off(); // eslint-disable-line no-unused-expressions
};
export const checkPrivateWhitelist = (onCb, offCb) => {
const whitelistOn = onCb || (() => {});
const whitelistOff = offCb || (() => {});
const privateButAllowed = [
'FBAV',
'FBAN',
'FBIOS',
'FBBV',
'FBDV',
'FBMD',
'FBSN',
'FBSV',
'FBSS',
'FBCR',
'FBID',
'FBLC',
'FBOP',
'Twitter for iPhone',
'TwitterAndroid',
'nytios',
'nytiphone',
'nytipad',
'nyt-android',
'AppleNews',
'Flipboard',
];
const re = new RegExp(privateButAllowed.join('|'), 'i');
if (re.test(navigator.userAgent)) {
whitelistOn();
return true;
}
whitelistOff();
return false;
};
function detectPrivateMode(cb) {
if(
navigator.userAgent.indexOf('WebKit') !== -1
&& navigator.userAgent.indexOf('Mobile') !== -1
){
try {
window.openDatabase(null, null, null, null);
cb(false);
} catch (_) {
cb(true);
}
}else{
var db,
on = cb.bind(null, true),
off = cb.bind(null, false)
function tryls() {
try {
localStorage.length ? off() : (localStorage.x = 1, localStorage.removeItem("x"), off());
} catch (e) {
navigator.cookieEnabled ? on() : off();
}
}
window.webkitRequestFileSystem ? webkitRequestFileSystem(0, 0, off, on)
: "MozAppearance" in document.documentElement.style ? (db = indexedDB.open("test"), db.onerror = on, db.onsuccess = off)
: /constructor/i.test(window.HTMLElement) || window.safari ? tryls()
: !window.indexedDB && (window.PointerEvent || window.MSPointerEvent) ? on()
: off()
}
}
@kdzwinel
Copy link
Author

kdzwinel commented Oct 30, 2018

live version of boston globe code - https://output.jsbin.com/qanacid/1

Copy link

ghost commented Aug 5, 2019

Thank you for publishing the great code.

I'm sorry if I become strange English for the following Japanese translation.

ttps: //output.jsbin.com/qanacid/1

I was open to test the judgment of the private mode, naturally it does not correspond to Chrome 76. This is because Google took measures.

iOS12 Private mode has been determined normally on iPhone Safari.
This mechanism is unknown,
bostonglobe.js
Line 68 of

if (window.safariIncognito) {

The part is likely to be a key, but no information is given.
Is it possible to determine iOS12 Safari with only simple Javascript?

Copy link

ghost commented Aug 6, 2019

This morning on the bostonglobe site, when I saw an article in Chrome (ver76) in private mode / guest mode, both received a private browser decision.
Normally, an account is created in the browser.
This is a perfect measure, but do you understand the structure?
I am very surprised that bostonglobe's countermeasures are too early.

@emaza
Copy link

emaza commented Aug 20, 2019

Hi, since chrome 76 version this not work but i see that if you change, "window.TEMPORARY" to "window.PERSISTENT" in bostonglobe.js line 25 it's works.

@emaza
Copy link

emaza commented Aug 20, 2019

Hi infoziko,
to detect browser I use that function:
function getBrowserInfo () { var ua = navigator.userAgent; var tem; var M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; if (/trident/i.test(M[1])) { tem = /\brv[ :]+(\d+)/g.exec(ua) || []; return { browser: 'IE', version: (tem[1] || '') } } if (M[1] === 'Chrome') { tem = ua.match(/\b(OPR|Edge)\/(\d+)/); if (tem != null) { return { browser: tem[1].replace('OPR', 'Opera'), version: (tem[2] || '') } } } M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'] if ((tem = ua.match(/version\/(\d+)/i)) != null) M.splice(1, 1, tem[1]) return { browser: M[0], version: M[1] } }

Thank you for publishing the great code.

I'm sorry if I become strange English for the following Japanese translation.

ttps: //output.jsbin.com/qanacid/1

I was open to test the judgment of the private mode, naturally it does not correspond to Chrome 76. This is because Google took measures.

iOS12 Private mode has been determined normally on iPhone Safari.
This mechanism is unknown,
bostonglobe.js
Line 68 of

if (window.safariIncognito) {

The part is likely to be a key, but no information is given.
Is it possible to determine iOS12 Safari with only simple Javascript?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment