4.5 标签

前面的章节中,多次提到了标签,本节将详细讲解对标签信息获取和操作的内容。在开始介绍之前,先让我们来看一下标签对象的结构:

{
    id: 标签id,
    index: 标签在窗口中的位置,以0开始,
    windowId: 标签所在窗口的id,
    openerTabId: 打开此标签的标签id,
    highlighted: 是否被高亮显示,
    active: 是否是活动的,
    pinned: 是否被固定,
    url: 标签的URL,
    title: 标签的标题,
    favIconUrl: 标签favicon的URL,
    status :标签状态,loading或complete,
    incognito: 是否在隐身窗口中,
    width: 宽度,
    height: 高度,
    sessionId: 用于sessions API的唯一id
}

Chrome通过tabs方法提供了管理标签的方法与监听标签行为的事件,大多数方法与事件是无需声明特殊权限的,但有关标签的urltitlefavIconUrl的操作(包括读取),都需要声明tabs权限。

"permissions": [
    "tabs"
]

获取标签信息。Chrome提供了三种获取标签信息的方法,分别是getgetCurrentqueryget方法可以获取到指定id的标签,getCurrent则获取运行的脚本本身所在的标签,query可以获取所有符合指定条件的标签。

chrome.tabs.get(tabId, function(tab){
    console.log(tab);
});

chrome.tabs.getCurrent(function(tab){
    console.log(tab);
});

query方法可以指定的匹配条件如下:

{
    active: 是否是活动的,
    pinned: 是否被固定,
    highlighted: 是否正被高亮显示,
    currentWindow: 是否在当前窗口,
    lastFocusedWindow: 是否是上一次选中的窗口,
    status: 状态,loading或complete,
    title: 标题,
    url: 所打开的url,
    windowId: 所在窗口的id,
    windowType: 窗口类型,normal、popup、panel或app,
    index: 窗口中的位置
}

下面的代码获取了所有在窗口中活动的标签:

chrome.tabs.query({
    active: true
}, function(tabArray){
    console.log(tabArray);
});

创建标签。创建标签与在浏览器中打开新的标签行为类似,但可以指定更加丰富的信息,如URL、窗口中的位置和活动状态等。

chrome.tabs.create({
    windowId: wId,
    index: 0,
    url: 'http://www.google.com',
    active: true,
    pinned: false,
    openerTabId: tId
}, function(tab){
    console.log(tab);
});

其中wId是创建标签所在窗口的id,如果不指定,则默认在当前窗口中打开。tId是打开此标签的标签id,可以不指定,但如果指定,那么所创建的标签必须与这个标签在同一窗口中。

除了用create方法,还可以使用duplicate方法“复制”指定标签:

chrome.tabs.duplicate(tabId, function(tab){
    console.log(tab);
});

更新标签。通过update方法可以更新标签的属性:

chrome.tabs.update(tabId, {
    url: 'http://www.google.com'
}, function(tab){
   console.log(tab);
});

更新标签时也可以不指定tabId,如果不指定,默认会更改当前窗口的活动标签。需要指出,直到31.0.1650.63 m,更新highlighted属性为true后,标签active属性也会被指定为true,所以如果只是想将某个标签高亮以引起用户的注意,需要先记录当前的标签id,更新后再将这个标签的active属性改回true。这个bug在之后的版本也许会被修正。

移动标签。move方法可以将指定的一个或多个标签移动到指定位置:

chrome.tabs.move(tabIds, {
    'windowId':wId,
    'index':0
}, function(tabs){
    console.log(tabs);
});

其中tabIds可以是一个数字型的标签id,也可以是一个包含多个标签id的数组。返回的tabs可能是标签对象也可能是包含多个标签对象的数组。如果指定的index-1,会将标签移动到指定窗口的最后面。

重载标签。reload方法可以重载指定标签,同时还可以指定是否跳过缓存(强制刷新):

chrome.tabs.reload(tabId, {
    bypassCache: true
}, function(){
    console.log('The tab has been reloaded.');
});

浏览器通常会对一些静态资源进行缓存,JavaScript中的location.reload()方法通常无法实现强制刷新,此时上面的方法就会很好地解决这个问题。

移除标签。通过remove方法可以关闭一个或多个标签:

chrome.tabs.remove(tabIds, function(){
    console.log('The tabs has been closed.');
});

其中tabIds可以是一个数字型的标签id,也可以是一个包含多个标签id的数组。

获取当前标签页面的显示语言。有时可能需要针对用户浏览内容语言的不同,采用不同的处理方法。比如翻译扩展就要根据不同的语言决定是否提示用户进行翻译。

chrome.tabs.detectLanguage(tabId, function(lang){
    console.log('The primary language of the tab is '+lang);
});

如果不指定tabId,则返回当前窗口当前标签的语言。

获取指定窗口活动标签可见部分的截图。Chrome提供了截取指定窗口活动标签页面为图片的接口:

chrome.tabs.captureVisibleTab(windowId, {
    format: 'jpeg',
    quality: 50
}, function(dataUrl){
    window.open(dataUrl, 'tabCapture');
});

其中format还支持png,如果指定为png,则quality属性会被忽略。如果指定jpeg格式,quality的取值范围为0-100,数值越高,图片质量越好,体积也越大。扩展只有声明activeTab<all_url>权限能获取到活动标签的截图:

"permissions": [
    "activeTab"
]

注入JS和CSS。之前我们接触过content_scripts,它可以向匹配条件的页面注入JS和CSS,但是却无法向用户指定的标签注入。通过executeScriptinsertCSS可以做到向指定的标签注入脚本。

chrome.tabs.executeScript(tabId, {
    file: 'js/ex.js',
    allFrames: true,
    runAt: 'document_start'
}, function(resultArray){
    console.log(resultArray);
});

也可以直接注入代码:

chrome.tabs.executeScript(tabId, {
    code: 'document.body.style.backgroundColor="red"',
    allFrames: true,
    runAt: 'document_start'
}, function(resultArray){
    console.log(resultArray);
});

向指定的标签注入CSS:

chrome.tabs.insertCSS(tabId, {
    file: 'css/insert.css',
    allFrames: false,
    runAt: 'document_start'
}, function(){
    console.log('The css has been inserted.');
});

插入CSS也可以指定具体代码。

executeScriptinsertCSS方法中runAt的值可以是'document_start''document_end''document_idle'

与指定标签中的内容脚本(content script)通信。前面章节介绍过扩展页面间的通信,我们也可以与指定的标签通信,方法如下:

chrome.tabs.sendMessage(tabId, message, function(response){
    console.log(response);
});

请注意,后台页面主动与content_scripts通信需要使用chrome.tabs.sendMessage方法1

1 chrome.tabs.executeScript方法也可以实现后台页面与内容脚本的通信,但更强调是后台页面向标签页注入脚本。

由于标签的操作行为比较多,所以相应的监视事件也很多。监控标签行为的事件包含onCreatedonUpdatedonMovedonActivatedonHighlightedonDetachedonAttachedonRemovedonReplaced

大部分事件都比较好理解,下面重点讲一讲不易理解的事件。onHighlighted是当标签被高亮显示时所触发的事件,activehighlight是有区别的,active是指标签在当前窗口中正被显示,highlight只是标签的颜色被显示成了白色——如果此标签没有被选中正常情况下是浅灰色。onDetached是当标签脱离窗口时所触发的事件,导致此事件触发的原因是用户在两个不同的窗口直接拖拽标签。onAttached是标签附着到窗口上时所触发的事件,同样是用户在两个不同的窗口直接拖拽标签导致的。onReplaced是当标签被其他标签替换时触发的事件2

2 要解释清楚 onReplaced 就不得不提一下即搜即得和预呈现(Instant search, Prerendering)。例如默认搜索引擎为 Google,启用了即搜即得,网络条件也足够好,在打开的另一个网页地址栏中开始输入关键字并且即时出现结果时,此时按下回车键,当前标签页就会被 Google搜索结果替换,产生 onReplaced 事件。如果扩展程序通过 tabId 追踪标签页的话就必须处理该事件。

chrome.tabs.onCreated.addListener(function(tab){
    console.log(tab);
});

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
    console.log('Tab '+tabId+' has been changed with these options:');
    console.log(changeInfo);
});

chrome.tabs.onMoved.addListener(function(tabId, moveInfo){
    console.log('Tab '+tabId+' has been moved:');
    console.log(moveInfo);
});

chrome.tabs.onActivated.addListener(function(activeInfo){
    console.log('Tab '+activeInfo.tabId+' in window '+activeInfo.windowId+' is active now.');
});

chrome.tabs.onHighlighted.addListener(function(highlightInfo){
    console.log('Tab '+activeInfo.tabId+' in window '+activeInfo.windowId+' is highlighted now.');
});

chrome.tabs.onDetached.addListener(function(tabId, detachInfo){
    console.log('Tab '+tabId+' in window '+detachInfo.oldWindowId+' at position '+detachInfo.oldPosition+' has been detached.');
});

chrome.tabs.onAttached.addListener(function(tabId, attachInfo){
    console.log('Tab '+tabId+' has been attached to window '+detachInfo.newWindowId+' at position '+detachInfo.newPosition+' .');
});

chrome.tabs.onRemoved.addListener(function(tabId, removeInfo){
    console.log('Tab '+tabId+' in window '+removeInfo.windowId+', and the window is '+(removeInfo.isWindowClosing?'closed.':'open.'));
});

chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId){
    console.log('Tab '+removedTabId+' has been replaced by tab '+addedTabId+'.');
);

通过标签接口,扩展可以更灵活地处理不同标签。虽然标签涉及到的内容很多,但常用的部分很有限,读者在阅读此节时,不妨先把精力重点放在那些常用易懂的方法事件上,对于剩下的部分随用随查即可。