Search code examples
javascriptgoogle-chrome-extensiondom-eventssandbox

Can a Chrome extension's content script guarantee that a DOM event was user-initiated?


I have an extension injecting HTML elements into pages and watching for click events on those elements. I want to be sure that any given click event came from a user action, rather than JS on the page creating and dispatching a click event. Is there a way of doing so?


Solution

  • You're looking for event.isTrusted, which has not yet been implemented.

    But it is still possible to detect whether a click event was user-initiated. The chrome.permissions.request API requires a user gesture, or else it will report a failure. The chrome.permissions API cannot be used in content scripts (since Chrome 33). Fortunately, the user gesture state is preserved when you use the messaging API to exchange a message from a content script and the background page (Since Chrome 36). So, you can use the following logic to detect whether the click event was generated by a user and act accordingly:

    background.js

    chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
        if (message === 'is-user-gesture') {
            chrome.permissions.request({}, function() {
                // If an error occurred, then the message was not
                // initiated by a user gesture.
                var is_user = !chrome.runtime.lastError;
                sendResponse(is_user);
            });
            return true; // Async
        }
    });
    

    contentscript.js

    function isUserEvent(callback) {
        chrome.runtime.sendMessage('is-user-gesture', function(is_user) {
            // Note: is_user could be undefined if the extension was reloaded
            // since the content script started.
            // We are conservative and assume that the action was not triggered
            // by a user gesture. Use "use is_user !== false" if you want to
            // assume that the action was triggered by the user if the extension
            // has reloaded.
            is_user = is_user === true;
            callback(is_user);
        });
    }
    document.body.onclick = function() {
         isUserEvent(function(is_user) {
             alert(is_user ? 'Triggered by user' : 'Spoofed event');
         });
    };
    

    To test this method, run the following steps in the page / content script:

    // Test fake event (will display "Spoofed event")
    document.body.dispatchEvent(new CustomEvent('click'));
    // TODO: Click on the body with your mouse (will display "Triggered by user")