WebExtension: React when a a tab is reloaded
Asked Answered
A

1

3

I am working on a Firefox WebExtension which has a popup and uses chrome.storage.local to store state. So far, I have got it to store data for specific tabs using the tab id.

I would like to reinitialize the local storage if the current tab is reloaded. This is because the extension may have made some changes to the DOM which will be lost if the tab is reloaded; what state it has stored is therefore incorrect.

The initial JavaScript code is called from the popup.

How can I run some code when the page is reloaded?

Thanks

Albinaalbinism answered 16/7, 2016 at 4:27 Comment(7)
Are you trying to differentiate between a page being loaded originally and when it is reloaded, or are you trying to do the same exact thing upon load and reload? How are you currently detecting that a page is loaded/a new tab opened?Aedes
When a tab is opened for the first time it gets a new tab id, so I’ve been using that. Now that you mention it, if the user loads a new page from the same tab, it would have the same id, so that’s not distinct enough. Maybe I should use the tab id in combinanation with the URL. What I should do is reinitialize when any page is loaded for the first time as well as when it is refreshed.Albinaalbinism
When you initialize data will depend on what is being done with it. You need to account for opening & closing tabs/windows, opening them again, navigating a new URL (typed or clicked), JavaScript opening a new URL, redirecting, reloading, etc. How exactly you do this will depend greatly on what you are really doing/doing with the data/your UI. You have given little information on this. Thus, no specific suggestions. You likely want to listen to one or more type of event (e.g. load, readystatechange, etc.). Some things you may be doing can provide inherent notification of some events.Aedes
When loading a new URL, either by typing it in or clicking on a link, the combination of tab id and url are sufficient to distinguish the settings. (I have installed the extension temporarily which seems to reload every time, so I am not sure about real life). I am ore interested in reinitialising the settings if the user reloads the current page in the current tab, either manually or through some page action. ThanksAlbinaalbinism
What have you already tried?Aedes
@Aedes I thought tabs.onUpdated might work, but the event doesn’t occur when the page is refreshed — only when the popup itself is updated, which is too late. I can’t see anything else in the documentation which remotely comes close. I am investigating webNavigation, but I think that’s a dead end, and I haven’t yet worked it out.Albinaalbinism
tabs.onUpdated certainly fires when the page is reloaded. I've been working on an answer for you using tabs.onUpdated. I'll post it shortly.Aedes
A
2

Without more information from you as to exactly what you desire to do, it is difficult to determine the best way to do this. In particular, it is hard to know exactly what situations to which you desire to respond.

It looks like you could use a listener to the tabs.onUpdated event. While this fires more often than you actually desire, it does fire on reloading of a page.

Below is code that calls a function completedLoadingURLInTab() when a URL has been loaded, or reloaded on a page. I've left in a bunch of console.log() calls as comments which I would normally remove. They can be uncommented to show in the console all the times the event fires. This can be useful to determine exactly the contents of the data received from the event and the sequence of events that fire during various page navigation.

Note 1: I found that the changeInfo object can be invalid under some circumstances. It was necessary to see if a property existed using hasOwnProperty() and then obtain the value from the tabs.Tab object that is also passed to the event handler.
Note 2: The tabs permission in manifest.json is required.

function completedLoadingURLInTab(tabId, newUrl,oldUrl) {
    //We have completed, loading a URL.
    if(newUrl === oldUrl) {
        //We re-loaded the previous URL
        console.log(">>>>>>> Re-load tab=" + tabId + " with URL=" + newUrl);
    } else {
        //This URL _may_ be just a new position in the same page.  That
        //  is something that needs to be checked for here.
        console.log(">>>>>>> New URL loaded in tab=" + tabId + ". URL=" + newUrl);
    }

    //Remember the newUrl so we can check against it the next time
    //  an event is fired.
    tabsInfo[tabId].previousCompleteUrl = newUrl;
    tabsInfo[tabId].loadingUrl = newUrl;
}

let tabsInfo = {};
function InfoForTab(_loadingUrl,_previousUrl,_status) {
    this.loadingUrl = _loadingUrl;
    this.previousCompleteUrl = (_previousUrl === undefined) ? "" : _previousUrl;
    this.status = (_status === undefined) ? "" : _status;
}

function foundNewTab(tabId) {
    //Create an object to hold the collected info for the tab.
    tabsInfo[tabId] = new InfoForTab();
    console.log("New tab. ID=" + tabId);
}

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    if(!tabsInfo.hasOwnProperty(tabId)) {
        //This is the first time we have encountered this tab.
        foundNewTab(tabId);
    }
    let output="";
    //We use the properties of "tab" instead of "changeInfo" because in testing it was
    //  clear that changeInfo was not always properly populated. The key(s) may just
    //  exist, but not have any value associated with them.

    /*
    //Testing output showing when the event is fired.
    //  This is used to determine, by experimentation, what events to ignore and which
    //  combinations and sequence of events occur during page navigation.
    output += (changeInfo.hasOwnProperty("status")) ? "\nstatus=" + tab.status : "";
    output += (changeInfo.hasOwnProperty("url")) ? "\nurl=" + tab.url : "";
    output += (changeInfo.hasOwnProperty("pinned")) ? "\npinned=" + tab.pinned : "";
    output += (changeInfo.hasOwnProperty("audible")) ? "\naudible=" + tab.audible : "";
    output += (changeInfo.hasOwnProperty("mutedInfo")) ? "\nmutedInfo="+tab.mutedInfo : "";
    output +=(changeInfo.hasOwnProperty("favIconUrl"))?"\nfavIconUrl="+tab.favIconUrl : "";
    console.log("tabs.updated event: tabId=" + tabId + ":: changeInfo="
                + Object.keys(changeInfo) + output
    );
    */
    if(changeInfo.hasOwnProperty("status") && changeInfo.hasOwnProperty("url")
        && (tab.status === "loading")) {
        //A URL is being loaded into the tab. This can be for the first time,
        //  or transitioning to a new URL, or reloading the page.
        let outputFirst = "";
        let outputLoading = "Loading";
        if(tabsInfo[tabId].previousCompleteUrl === tab.url) {
            //We are re-loading the same URL
            outputLoading = "Re-loading";
        }
        //console.log(outputLoading  + " URL=" + tab.url);
        //We save the URL which is being loaded, but we really don't do anything with it.
        tabsInfo[tabId].loadingUrl = tab.url;
        tabsInfo[tabId].status = "loading";
        return;
    } //else
    if(changeInfo.hasOwnProperty("status") && (tab.status === "complete")) {
        if( tabsInfo[tabId].status === "loading") {
            tabsInfo[tabId].status = "complete";
            //console.log("In tabId="+tabId+" completed loading URL="+tab.url);
            completedLoadingURLInTab(tabId, tab.url, tabsInfo[tabId].previousCompleteUrl);
            return;
        } //else
        if( tabsInfo[tabId].status === "complete") {
            if(tabsInfo[tabId].previousCompleteUrl === tab.url) {
                //console.log("In tabId=" + tabId + " got completed status change after"
                //              + "already complete with URL="
                //              + tabsInfo[tabId].previousCompleteUrl);
                return;
            }//else
            //console.log("In tabId=" + tabId + " completed directly loading new URL="
            //            + tab.url);
            completedLoadingURLInTab(tabId, tab.url, tabsInfo[tabId].previousCompleteUrl);
            return;
        }
    }//else
    if(changeInfo.hasOwnProperty("status") && (tab.status === "loading")
        && ( tabsInfo[tabId].status === "complete")) {
        //console.log("In tabId=" + tabId + " leaving page");
        return;
    }//else
    if(changeInfo.hasOwnProperty("status") ) {
        if((tab.status === "complete") && (tab.url === "about:newtab")
            && tabsInfo[tabId].loadingUrl === undefined ) {
            //We have completed loading about:newtab for the first time in this tab.
            tabsInfo[tabId].status = "complete";
            completedLoadingURLInTab(tabId, tab.url, tabsInfo[tabId].previousCompleteUrl);
            return;
        } //else
        //console.log("In tabId=" + tabId + " got status change to " + tab.status
        //              + " prior to loading a URL with current URL=" + tab.url);
        return;
    }//else
});

Given loading the add-on, opening a new tab and doing a bit of navigation (including a few page re-loads), the output in the console could look like:

New tab. ID=6
>>>>>>> New URL loaded in tab=6. URL=about:newtab
>>>>>>> New URL loaded in tab=6. URL=https://www.google.com/?gws_rd=ssl
>>>>>>> Re-load tab=6 with URL=https://www.google.com/?gws_rd=ssl
>>>>>>> New URL loaded in tab=6. URL=https://www.google.com/?gws_rd=ssl#q=test
>>>>>>> Re-load tab=6 with URL=https://www.google.com/?gws_rd=ssl#q=test
>>>>>>> New URL loaded in tab=6. URL=https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onUpdated
>>>>>>> New URL loaded in tab=6. URL=https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onUpdated#changeInfo
>>>>>>> Re-load tab=6 with URL=https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onUpdated#changeInfo

Alternately, use the webNavigation events

The webNavigation events provide you information directly about web navigation. They will probably provide you with a better solution than tabs.onUpdated.

If you use webNavigation events, you will need to experiment to see which combination of events fire for the situations your are concerned about. Most likely this will be Completed and/or ReferenceFragmentUpdated.

So you can get a feel for what when these events fire, the following code will log to the console all webNavigation events:

var webNavEvents = ['BeforeNavigate',
                    'Committed',
                    'Completed',
                    //'CreatedNavigationTarget', //Not supported by Firefox
                    'DOMContentLoaded',
                    'ErrorOccurred',
                    'HistoryStateUpdated',
                    'ReferenceFragmentUpdated'
                    //'TabReplaced' //Not supported by Firefox
                    ];


webNavEvents.forEach(function(navType){
    browser.webNavigation['on' + navType].addListener(function(type,details){
        console.log('\twebNavigation->' + type 
                    + ': tadId=' + details.tabId
                    + ':: url=' + details.url
                    + ((typeof details.transitionType === 'string') ? ':: transitionType='
                        + details.transitionType : '')
        );
    }.bind(undefined,navType));
});
Aedes answered 18/7, 2016 at 1:4 Comment(1)
@Manngo, I added information and code for showing webNavigation events. If you use them, you will need to experiment to see which combination of events fire for the situations your are concerned about. Most likely this will be Completed and/or ReferenceFragmentUpdated.Aedes

© 2022 - 2024 — McMap. All rights reserved.