最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Persistent unique ID for Chrome tabs that lasts between browser sessions - Stack Overflow

programmeradmin3浏览0评论

I'm trying to ascertain some way to establish a unique ID for Chrome tabs that meets the following conditions:

  • Uniquely identifies each tab
  • Stays the same for a given tab between browser restarts (session-restored tabs)
  • Stays the same if a tab is closed and then reopened with Undo Closed Tab (Ctrl+Shift+T)
  • Stays distinct if a tab is duplicated

I've done some rather aggressive research to find a prehensive solution, but nothing seems to quite do the trick. Here are the methods I have tried, in increasing order of efficacy:

  • Use Chrome's provided tab.id: does not persist between browser sessions or close/undo-close
  • Put a GUID in cookies: is not unique per tab, only per domain/URL
  • Put a GUID in localStorage: persists between browser sessions and close/undo-close, but is not unique per tab, only per domain
  • Put a GUID in sessionStorage: unique per tab, persists across close/undo-close, unique for duplicated tabs, but is wiped out between browser sessions
  • Use identifiable webpage document attributes as a unique key: this is the best approach I've found so far. A key can be constructed via a content script from the following values: [location.href, document.referrer, history.length].

Regarding this last approach, the constructed key is unique across all tabs which share a mon URL, referrer, and history length. Those values will remain the same for a given tab between browser restarts/session-restores and close/undo-closes. While this key is "pretty" unique, there are cases where it is ambiguous: for example, 3 new tabs opened to would all have the same key in mon (and this kind of thing happens pretty often in practice).

The "put GUID in sessionStorage" method can additionally be used to disambiguate between multiple tabs with the same constructed key for the close/undo-close and duplicated-tab cases during the current browser session. But this does not solve the ambiguity problem between browser restarts.

This last ambiguity can be partially mitigated during session restore by observing which tabs Chrome opens together in which windows, and extrapolating for a given ambiguous key which tab belongs to which window based on the presence of expected 'sibling' tabs (recorded during the previous browser session). As you might imagine, implementing this solution is quite involved and rather dodgy. And it can only disambiguate between same-keyed tabs that Chrome restores into different windows. That leaves same-keyed tabs that restore into the same window as irreconcilably ambiguous.

Is there a better way? A guaranteed unique, browser-generated, per-tab GUID that persists between browser restarts (session restores) and close/undo-close would be ideal but so far I haven't found anything like this.

I'm trying to ascertain some way to establish a unique ID for Chrome tabs that meets the following conditions:

  • Uniquely identifies each tab
  • Stays the same for a given tab between browser restarts (session-restored tabs)
  • Stays the same if a tab is closed and then reopened with Undo Closed Tab (Ctrl+Shift+T)
  • Stays distinct if a tab is duplicated

I've done some rather aggressive research to find a prehensive solution, but nothing seems to quite do the trick. Here are the methods I have tried, in increasing order of efficacy:

  • Use Chrome's provided tab.id: does not persist between browser sessions or close/undo-close
  • Put a GUID in cookies: is not unique per tab, only per domain/URL
  • Put a GUID in localStorage: persists between browser sessions and close/undo-close, but is not unique per tab, only per domain
  • Put a GUID in sessionStorage: unique per tab, persists across close/undo-close, unique for duplicated tabs, but is wiped out between browser sessions
  • Use identifiable webpage document attributes as a unique key: this is the best approach I've found so far. A key can be constructed via a content script from the following values: [location.href, document.referrer, history.length].

Regarding this last approach, the constructed key is unique across all tabs which share a mon URL, referrer, and history length. Those values will remain the same for a given tab between browser restarts/session-restores and close/undo-closes. While this key is "pretty" unique, there are cases where it is ambiguous: for example, 3 new tabs opened to http://www.google. would all have the same key in mon (and this kind of thing happens pretty often in practice).

The "put GUID in sessionStorage" method can additionally be used to disambiguate between multiple tabs with the same constructed key for the close/undo-close and duplicated-tab cases during the current browser session. But this does not solve the ambiguity problem between browser restarts.

This last ambiguity can be partially mitigated during session restore by observing which tabs Chrome opens together in which windows, and extrapolating for a given ambiguous key which tab belongs to which window based on the presence of expected 'sibling' tabs (recorded during the previous browser session). As you might imagine, implementing this solution is quite involved and rather dodgy. And it can only disambiguate between same-keyed tabs that Chrome restores into different windows. That leaves same-keyed tabs that restore into the same window as irreconcilably ambiguous.

Is there a better way? A guaranteed unique, browser-generated, per-tab GUID that persists between browser restarts (session restores) and close/undo-close would be ideal but so far I haven't found anything like this.

Share Improve this question edited Jun 12, 2012 at 21:46 joelpt asked Jun 12, 2012 at 21:38 joelptjoelpt 5,0313 gold badges30 silver badges28 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 7

The question here does most of the discovery work, and the accepted answer basically pletes it, but there's a big implementation gap still for people looking to implement something which requires persistent tab IDs. I've attempted to distill this into an actual implementation.

To recap: Tabs can be (almost) uniquely and consistently identified as required by the question by maintaining a register of tabs which stores the following bination of variables in local persistent storage:

  • Tab.id
  • Tab.index
  • A 'fingerprint' of the document open in the tab - [location.href, document.referrer, history.length]

These variables can be tracked and stored in the registry using listeners on a bination of the following events:

  • onUpdated
  • onCreated
  • onMoved
  • onDetached
  • onAttached
  • onRemoved
  • onReplaced

There are still ways to fool this method, but in practice they are probably pretty rare - mostly edge cases.

Since it looks like I'm not the only one who has needed to solve this problem, I built my implementation as a library with the intention that it could be used in any Chrome extension. It's MIT licensed and available on GitHub for forking and pull requests (in fact, any feedback would be wele - there are definitely possible improvements).

If I correctly understand your problem, your 5th method should do the trick, but along with these two criteria:

  • chrome.tabs.windowId (The ID of the window the tab is contained within)
  • chrome.tabs.index (The zero-based index of the tab within its window)

All these values need to be stored inside your extension. Besides that, you will also have to hook up your extension to chrome.tabs.onUpdated() and updated accordingly, when tabs are being dragged around, moved across owner windows, etc.

Put this as a persistent background script in manifest.json:

"background": {
        "scripts": [ "background.js" ],
        "persistent": true
},

Here is background.js. Hopefully the code is self explanatory.

var tabs_hashes = {};
var tabs_hashes_save_queued = false;

function Start(){
    chrome.tabs.query({windowType: "normal"}, function(querytabs){
        querytabs.forEach(function(tab){
            tabs_hashes[tab.id] = GetHash(tab.url);
        });

        if (localStorage.getItem("tabs_hashes") !== null){

            var ref_load = JSON.parse(localStorage["tabs_hashes"]);
            var ref_tabId = {};


            querytabs.forEach(function(tab){
                for (var t = 0; t < ref_load.length; t++){
                    if (ref_load[t][1] === tabs_hashes[tab.id]){
                        ref_tabId[ref_load[t][0]] = tab.id;
                        ref_load.splice(t, 1);
                        break;
                    }
                }
            });

            // do what you have to do to convert previous tabId to the new one
            // just use ref_tabId[your_previous_tabId] to get the current corresponding new tabId
            console.log(ref_tabId);

        }
    });
}


function SaveHashes(){
    if (!tabs_hashes_save_queued && Object.keys(tabs_hashes).length > 0){
        tabs_hashes_save_queued = true;
        chrome.tabs.query({windowType: "normal"}, function(querytabs){
            var data = [];
            querytabs.forEach(function(tab){
                if (tabs_hashes[tab.id]){
                    data.push([tab.id, tabs_hashes[tab.id]]);
                } else {
                    data.push([tab.id, GetHash(tab.url)]);
                }
            });
            localStorage["tabs_hashes"] = JSON.stringify(data);
            setTimeout(function(){ tabs_hashes_save_queued = false; }, 1000);
        });
    }
}

function GetHash(s){
    var hash = 0;
    if (s.length === 0){
        return 0;
    }
    for (var i = 0; i < s.length; i++){
        hash = (hash << 5)-hash;
        hash = hash+s.charCodeAt(i);
        hash |= 0;
    }
    return Math.abs(hash);
}


chrome.tabs.onCreated.addListener(function(tab){
    SaveHashes();
});
chrome.tabs.onAttached.addListener(function(tabId){
    SaveHashes();
});
chrome.tabs.onRemoved.addListener(function(tabId){
    delete tabs_hashes[tabId];
    SaveHashes();
});
chrome.tabs.onDetached.addListener(function(tabId){
    SaveHashes();
});
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo){
    if (changeInfo.pinned != undefined || changeInfo.url != undefined){
        delete tabs_hashes[tabId];
        SaveHashes();
    }
});
chrome.tabs.onMoved.addListener(function(tabId){
    SaveHashes();
});
chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId){
    delete tabs_hashes[removedTabId];
    SaveHashes();
});


Start();

I use array to save data, because in this way I can preserve tabs order, which is unlikely if data would be saved in the object. When loading data after browser's restart, even if url is not unique, I can trust that it will be under some "close enough" index. I would do it more plex, for example reverse check if tab was not found, but this works ok so far.

发布评论

评论列表(0)

  1. 暂无评论