WebExtension: React when a a tab is reloaded

2019-07-13 13:30发布

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

1条回答
迷人小祖宗
2楼-- · 2019-07-13 14:14

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));
});
查看更多
登录 后发表回答