10

我正在尝试确定某种方法来为满足以下条件的 Chrome 选项卡建立唯一 ID:

  • 唯一标识每个选项卡
  • 浏览器重新启动之间的给定选项卡保持不变(会话恢复选项卡)
  • 如果选项卡已关闭然后使用撤消关闭的选项卡 (Ctrl+Shift+T) 重新打开,则保持不变
  • 如果选项卡重复,则保持不同

我进行了一些相当积极的研究以找到一个全面的解决方案,但似乎没有什么能完全奏效。以下是我尝试过的方法,按功效递增的顺序:

  • 使用 Chrome 提供的 tab.id:不会在浏览器会话或关闭/撤消关闭之间持续存在
  • 将 GUID 放入 cookie:每个选项卡不是唯一的,仅每个域/URL
  • 将 GUID 放入 localStorage:在浏览器会话和关闭/撤消关闭之间持续存在,但每个选项卡不是唯一的,仅每个域
  • 将 GUID 放入 sessionStorage:每个选项卡唯一,在关闭/撤消关闭期间持续存在,对于重复的选项卡是唯一的,但在浏览器会话之间被清除
  • 使用可识别的网页文档属性作为唯一键:这是迄今为止我发现的最佳方法。可以通过内容脚本从以下值构造密钥:[location.href, document.referrer, history.length].

关于最后一种方法,构造的键在所有共享公共 URL、引荐来源和历史长度的选项卡中是唯一的。对于浏览器重新启动/会话恢复和关闭/撤消关闭之间的给定选项卡,这些值将保持不变。虽然这个键是“相当”独特的,但在某些情况下它是模棱两可的:例如,打开到http://www.google.com的 3 个新标签都将具有相同的键(这种事情发生得很经常在实践中)。

在当前浏览器会话期间,“将 GUID 放入 sessionStorage”方法还可用于消除具有相同构造键的多个选项卡之间的歧义,以用于关闭/撤消关闭和重复选项卡的情况。但这并不能解决浏览器重启之间的歧义问题。

在会话恢复期间,可以通过观察 Chrome 在哪些窗口中同时打开哪些选项卡,并根据预期的“兄弟”选项卡的存在推断给定的模棱两可键哪个选项卡属于哪个窗口(在前一个浏览器期间记录会议)。正如您可能想象的那样,实施此解决方案非常复杂且相当狡猾。它只能消除 Chrome 恢复到不同窗口的相同键选项卡之间的歧义。这使得恢复到同一个窗口的相同键的选项卡变得不可调和的模棱两可。

有没有更好的办法?在浏览器重新启动(会话恢复)和关闭/撤消关闭之间保持唯一的、浏览器生成的、每个选项卡的 GUID 是理想的,但到目前为止我还没有找到类似的东西。

4

3 回答 3

4

这里的问题完成了大部分发现工作,并且接受的答案基本上完成了它,但是对于希望实现需要持久选项卡 ID 的东西的人来说,仍然存在很大的实现差距。我试图将其提炼成实际的实现。

回顾一下:通过维护一个选项卡寄存器,可以(几乎)根据问题的要求唯一且一致地识别选项卡,该寄存器将以下变量组合存储在本地持久存储中:

  • Tab.id
  • Tab.index
  • 在选项卡中打开的文档的“指纹” -[location.href, document.referrer, history.length]

可以使用以下事件组合上的侦听器来跟踪这些变量并将其存储在注册表中:

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

仍然有方法可以欺骗这种方法,但实际上它们可能非常罕见 - 主要是边缘情况。

因为看起来我不是唯一需要解决这个问题的人,所以我将我的实现构建为一个库,目的是它可以在任何 Chrome 扩展程序中使用。它是 MIT 许可的,可在 GitHub 上用于分叉和拉取请求(事实上,欢迎任何反馈 - 肯定有可能的改进)。

于 2013-01-25T09:23:45.727 回答
1

如果我正确理解了您的问题,那么您的第 5 种方法应该可以解决问题,但要符合以下两个标准:

  • chrome.tabs.windowId(包含选项卡的窗口的 ID)
  • chrome.tabs.index(其窗口中选项卡的从零开始的索引)

所有这些值都需要存储在您的扩展中。chrome.tabs.onUpdated()除此之外,当标签被拖动、跨所有者窗口等移动时,您还必须连接您的扩展并相应地更新。

于 2012-06-15T06:25:54.297 回答
1

把它作为一个持久的后台脚本放在 manifest.json 中:

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

这是背景.js。希望代码是不言自明的。

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();

我使用数组来保存数据,因为这样我可以保留制表符顺序,如果数据保存在对象中,这是不太可能的。在浏览器重新启动后加载数据时,即使 url 不是唯一的,我可以相信它会在某个“足够接近”的索引下。我会做的更复杂,例如反向检查是否找不到选项卡,但到目前为止这工作正常。

于 2017-02-10T17:53:50.917 回答