This article is based on analysis done by the disable-devtool project on Github.

Here I only analyze part of the code in the project, my ability is limited, please forgive me.

I won’t bother with the project structure, just analyze the files in the project SRC folder.

SRC main.js file, the first introduced is the disableKeyAndMenu method, which is used to prevent the right mouse button and keyboard from opening developer tools.

function disableKeyAndMenu () { window.addEventListener('keydown', (e) => { e = e || window.event; const keyCode = e.keyCode || e.which; // alert(e.keyCode); if (keyCode === 123 || (e.shiftKey && e.ctrlKey && e.keyCode === 73)) { e.returnValue = false; e.preventDefault(); return false; } }, false); if (config.disableMenu) { window.addEventListener('contextmenu', (e) => { e = e || window.event; e.returnValue = false; e.preventDefault(); return false; }, false); }}Copy the code

This one is obvious, listening for key events and blocking default events if a key is pressed that opens developer tools. At the same time to the page for listening, prevent the right mouse button events. Contextmenu is used to listen for the triggered content menu, either with the right mouse button or with two finger clicks on the laptop keyboard.

I then introduced the initInterval method, which is used to initialize the event loop so that the page does not know when the user opens the developer tool using another method in use.

export function initInterval () { let _pause = false; const pause = () => {_pause = true; }; const goon = () => {_pause = false; }; hackAlert(pause, goon); // Prevent methods such as alert from triggering debug delay calculation onPageShowHide(goon, pause); Interval = window.setInterval(() => {if (_pause) return; calls.forEach(fn => {fn(time++); }); console.clear(); }, config.interval); Timer = setTimeout(() => {if (! isPC()) { clearDDInterval(); } }, config.stopIntervalTime); }Copy the code

IsPc, hackAlter, onPageShowHIde are the code encapsulated in the project, which are relatively simple, I won’t go into details. In short, the function of this part of the code is to perform an event loop to detect the running status of the developer tool if it is opened on the PC side.

Then there is the code for the core part, initemulsion.

This method calls three methods to determine the open state of the developer tool.

1: toString

function detector () { const isQQ = isQQBrowser(); const isFF = isFirefox(); // Since this method has some problems executing in Chrome, it will simply return to chrome and not execute again. if (! isQQ && ! isFF) return; let lastTime = 0; const reg = /./; console.log(reg); reg.toString = function () { if (isQQ) { // ! Const time = new Date().getTime(); if (lastTime && time - lastTime < 100) { triggerOnDevOpen(DETECTOR_TYPE.TO_STRING); } else { lastTime = time; } }else if (isFF) { triggerOnDevOpen(DETECTOR_TYPE.TO_STRING); } return ''; }; registInterval(() => { console.log(reg); }); }Copy the code

This method is based on how console.log works. Because the console.log method is tailored to different browsers, different browsers have different execution logic. I’ll take Chrome as an example.

Console.log () in Chrome’s console takes a lazy approach to printing objects, retrieving them only when the user clicks to expand them. But Chrome executes the toString method of the reg when it executes console.log, which is why the toString method executes the following code if the developer tool is open or not. With chrome’s console. Log lazy loading and toString execution, you can tell that the developer tool is open twice and not open once.

Of course, different browsers have different situations that need to be modified during debugging.

2: defind – id

function detector () { const div = document.createElement('div'); div.defineGetter('id', function () { triggerOnDevOpen(DETECTOR_TYPE.DEFINE_ID); }); Object.defineProperty(div, 'id', { get: function () { triggerOnDevOpen(DETECTOR_TYPE.DEFINE_ID); }}); registInterval(() => { console.log(div); }); }Copy the code

By creating a div and listening for its ID to get, the console prints the div to know the status of the developer tool open. And because of the lazy loading of printing, you can also make sure that if the developer tools are not open, the div ID get method will not execute.

3: the size of

function checkWindowSizeUneven () {
    const threshold = 160;
    const widthUneven = window.outerWidth - window.innerWidth > threshold;
    const heightUneven = window.outerHeight - window.innerHeight > threshold;
    if (widthUneven || heightUneven) {
        triggerOnDevOpen(DETECTOR_TYPE.SIZE);
        return false;
    }
    return true;
}
export default function detector () {
    checkWindowSizeUneven();
    window.addEventListener('resize', () => {
        setTimeout(checkWindowSizeUneven, 100);
    }, true);
}
Copy the code

The third method is the simplest. When the page is open, the developer tool takes up a portion of the page and listens for the resize time. It responds when the page size changes. The 160 parameter can ensure that downloads and some other cases will not be misjudged.

But if the developer tools are separate pages it’s hard to make an accurate judgment. Also, Chrome puts developer tools at the bottom of the page, which can be as small as 153 pixels (version 94.0.4606.71 (official version) (x86_64)), potentially affecting the results.

These are three ways that the user doesn’t perceive, and there is one way that the user can perceive.

! function () { const handler = setInterval(() => { const before = new Date(); debugger; const after = new Date(); const cost = after.getTime() - before.getTime(); if (cost > 100) { consoleOpenCallback(); clearInterval(handler); }}, 1000); } ();Copy the code

The method is simple: compare the time difference between two dates. If the debugger executes, the developer tool is opened. However, this method can also be affected if the user turns debug mode off in Sources and cannot make the correct judgment.