I'm trying to build a chrome extension and setting up a background task to check if the tab and window is active, however I seem to be having a bit of confusion.

So in my content_page javascript I have the following:

chrome.runtime.sendMessage("checkActive", function(response) {
    if( response === false ) {
        console.log('tab is inactive');
    } else {
        console.log('tab is active');
    }
});

Then in my background page, I'm adding a listener. I can successfully check if the tab is active, however I can't seem to figure out how to include if the window is active in my check. Here's what I've got:

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        if (request == "checkActive") {
            var bRet = sender.tab.active;
            chrome.windows.getCurrent({}, function(oWin) {
                bRet = (oWin.focused && bRet);
                console.log('this is logged second');
                sendResponse(bRet); // This never gets to the content page
            });
            console.log('this is logged first');
            sendResponse(bRet); // this gets to the content page, but not updated with oWin.focused
        }
    }
);

What am I missing? I assume it has something to do with my lack of understanding on scopes, callbacks, and the like 🙂

    I've never done any extensions, so perhaps some of my questions may seem silly.

    Derokorian;11038745 wrote:

    I'm trying to build a chrome extension and setting up a background task to check if the tab and window is active

    If the tab is active, must the window not also be active in this case? Or can a tab of an inactive window be considered "active"?

    Derokorian;11038745 wrote:
    chrome.runtime.sendMessage("checkActive", function(response) {
    

    Can one message receive several responses? Or just one?

    Then in my background page, I'm adding a listener. I can successfully check if the tab is active, however I can't seem to figure out how to include if the window is active in my check. Here's what I've got:

    chrome.runtime.onMessage.addListener(
        function (request, sender, sendResponse) {
            if (request == "checkActive") {
                var bRet = sender.tab.active;
                chrome.windows.getCurrent({}, function(oWin) {
                    // this refers to bRet in the enclosing scope, i.e. 2 lines above
                    bRet = (oWin.focused && bRet);
                    console.log('this is logged second');
                    sendResponse(bRet); // This never gets to the content page
                });
                /* This is still inside the closure. If you wait here long enough for the callback to
                 * be executed, bRet should change value depending on oWin. And the order of
                 * the console.logs should change accordingly.
                 * But if my guess about a tab only being considered active if its window is active
                 * then you'd never see a difference since you'd have
                 * true (tab) && true (window always true)
                 * tab (false) && whatever
                 * and the whole expression can be simplified to: tab.active
                 */
                console.log('this is logged first');
                sendResponse(bRet); // this gets to the content page, but not updated with oWin.focused
            }
        }
    );
    Derokorian;11038745 wrote:

    What am I missing? I assume it has something to do with my lack of understanding on scopes, callbacks, and the like 🙂

    If you wish to separate the inner callback from the current closure, you may do so like this

    chrome.runtime.onMessage.addListener(
        function (request, sender, sendResponse) {
            "use strict";
            if (request === "checkActive") {
                var bRet = sender.tab.active;
    
            /* Anonymous self executing function taking one argument, bRet
             * This creates a new lexing scope, inside which bRet is local
             * and not an upvalue. You could even change the parameter name
             * from
             * (function (bRet) {}(bRet));
             * to
             * (function (bRetLocal) {}(bRet));
             * if you find it easier to keep track of which is which in this way.
             * 
             * Also, this
             *    (function (param) {})(arg);
             * is the same as this
             *    (function (param) {}(arg));
             * Except that the latter form is required by jslint
             */
            (function (bRetLocal) {
                chrome.windows.getCurrent({}, function (oWin) {
                    bRetLocal = (oWin.focused && bRetLocal);
                    // since you do get both logs in the console, add the activity status there for inspection
                    console.log('this is logged second: ' + (bRetLocal ? 'true' : 'false'));
                    sendResponse(bRetLocal); // This never gets to the content page
                });
            }(bRet));
    
            // since you do get both logs in the console, add the activity status there for inspection
            console.log('this is logged first: ' + (bRet ? 'true' : 'false');
            sendResponse(bRet); // this gets to the content page, but not updated with oWin.focused
        }
    }
    );
    

    If you wish to get more information about what is active and not, try

    // First check
    var bRet = sender.tab.active ? 'tab' : '';
    
    // Second check, inside the callback
    bRetLocal += oWin.focused ? 'window' : '';
    

    Setting breakpoints and inspecting the code as it is executing usually helps gain a better understanding of what is happening.

    Finally, would it work to use chrome.tabs.query({ active : true, currentWindow : true }) to get the currently active tab of the current window and check if that tab matches your needs?

    Hopefully something helped in some way.

      johanafm;11038781 wrote:

      /* Anonymous self executing function

      So much for answering questions in the middle of the night. While what I describe works and is sometimes the desired solution, in this case I'd rather recommend creating a new variable

          function (request, sender, sendResponse) {
              var bRet;   // Usually recommended to declare all variables at the top of the function.
                              // There is no block scope, only function scope
              if (request == "checkActive") {
                  bRet = sender.tab.active;
                  chrome.windows.getCurrent({}, function(oWin) {
                      var bWinRet = oWin.focused && bRet;
      

      This way you don't add overhead for setting up the function and calling it.

        Thanks johanafm, I will give it a shot this weekend. Last night I tried out using promises, but it would seem like only scalar data in an object is passed between background and content pages for the extension. Its unfortunate, because its also not a reference so I can't simply wait for a property to be set.

          johanfm wrote:

          If the tab is active, must the window not also be active in this case? Or can a tab of an inactive window be considered "active"?

          A tab is active if it has focus within its window. Therefore there is as many total active tabs as there are windows. A window is considered focused if its the active window within the desktop view system. Therefore, if I switch to my editor the tab is still considered active, even tho I'm not able to see it. For now this is working, but for where I'd like to take it, this will not be ideal.

          johanafm wrote:

          Can one message receive several responses? Or just one?

          Only one.

          As for declaring all variables at the top, I generally do that, but only when used. Specifically, I've trimmed those code to to the minimum viable problem. There are many different "request" actions, and I see no need to declare a variable above the if when it is only used in one if block. That would mean every other action would declare a variable it never uses.

            Derokorian wrote:

            There are many different "request" actions, and I see no need to declare a variable above the if when it is only used in one if block. That would mean every other action would declare a variable it never uses.

            But the problem with JavaScript is that no matter where the variable is declared, its scope is the entire function - even if the "var" statement hasn't been reached yet! So as long as you use var to declare (function-)local variables, every action has all of them whether they're used or not.

            Recent versions of JS have let, which does provide block-level scoping.

              Weedpacket;11038805 wrote:

              But the problem with JavaScript is that no matter where the variable is declared, its scope is the entire function - even if the "var" statement hasn't been reached yet! So as long as you use var to declare (function-)local variables, every action has all of them whether they're used or not.

              Recent versions of JS have let, which does provide block-level scoping.

              Oh wow, I knew it would fall through the whole function call, but I didn't think they were declared unless the line of code with var was hit. Thanks for the heads up.

                Derokorian;11038789 wrote:

                Only one (response)

                Then that is why only the second (in row-sequence) call to sendResponse goes through. After that one response, the other sendResponse will do nothing.

                sendResponse(bRet); 
                

                But could you not simply rewrite it like this?

                if (sender.tab.active) {
                    chrome.windows.getCurrent({}, function(oWin) {
                        console.log('window possibly active');
                        sendResponse(oWin.focused);
                    });
                } else {
                    console.log('tab not active');
                    sendReponse(false)
                }
                

                  well the posted code was an example of what I have tried, but I never had most than one sendResponse in the code at once... I guess could have made it multiple code blocks, but was trying to be brief. For the record your last post doesn't work at all, I tried that 😛 I also have now tried the self-executing anonymous function (which I could have sworn I tried) but either way it still didn't work because getFocused returns, so the anonymous function is then complete, but the callback passed to getFocused still hasn't fired. Tonight I plan on using a localized promise in the background-page script and see if that works (though I doubt it for much the same reasons).

                    Write a Reply...