chrome插件 API

1.chrome.storage API实现的浏览器存储

Chrome浏览器扩展通过chrome.storage.* API,可以存取数据或监听数据的变化。

在manifest.json文件中注册storage如下:

      {
        "permissions": [
          "storage"
        ],
      }
1.1 chrome.storage.local

只能够将数据存储在当前登录的设备本地。

1.2 chrome.storage.sync

方式实现了自动数据同步,相同的用户无论使用什么物理设备,只要以相同的账户登录即可访问存储的数据。设备离线时数据存储在本地,一旦设备上线则同步数据。如果用户禁止了数据同步,则采用chrome.storage.local方式。

1.3 chrome.storage.managed

方式是只读存储,只有域管理员能够在其中存储数据,Chrome浏览器扩展只能读取其中的数据。

chrome.storage机制采用一系列的存储格子(tubes)存储数据,存储空间有限。存储数据时只能一个一进行写入操作,并发性能不高。

下面以chrome.storage.sync为例介绍具体方法的使用:

存储一个或多个数据

chrome.storage.sync.set(object items, function() {...})

其中的items对象包含若干“键值对”的映射,一个键值对就是一个存储的数据项。

获取指定key的数据项

chrome.storage.sync.get(string or array of string or object keys, function(object items) {...})

其中的keys如果为null则返回全部存储的数据项,如果为””或[]将返回空对象{}。

回调函数中的items对象就是获取的数据项,其中包含“键值对”的映射。

删除指定key的一个或多个数据项

chrome.storage.sync.remove(string or array of string keys, function() {...})

清空存储的所有数据项

chrome.storage.sync.clear(function(){…})

获取当前已经被使用的存储空间的数量(以字节为单位)

chrome.storage.sync.getBytesInUse(string or array of string keys, function(integer bytesInUse) {...})

其中的keys属性是数据项的key,如果为null表示取全部数据项的使用空间,””或[]将返回0

1.4 chrome.storage.onChanged.addListener

此外,对于某些敏感数据的变化,可以通过onChanged事件进行监听。存储格子中的任何变化都将触发该事件,示例如下:

chrome.storage.onChanged.addListener(function(changes, namespace) {
    for (key in changes) {
        var storageChange = changes[key];
        console.log('Storage key "%s" in namespace "%s" changed. ' + 'Old value was "%s", new value is "%s".', key, //数据的索引key
            namespace, //数据的存储空间类型,枚举值`"sync"`, `"local"`, `"managed"`
            storageChange.oldValue, //变化前的值
            storageChange.newValue); //变化后的值
    }
});
1.5 一次性请求与响应模式

对于一次性请求与响应模式,chrome.runtime.sendMessage(obj, function(response){})是从content scripts发生请求消息给Google Chrome扩展程序页面。

从Google Chrome扩展程序页面发送请求消息给content scripts的时候,需要给出当前tab的ID。

 chrome.tabs.query({
     active: true,
     currentWindow: true
 }, function(tabs) {
     chrome.tabs.sendMessage(tabs[0].id, {
         greeting: "hello"
     }, function(response) {
         console.log(response.farewell);
     });
 });

监听消息时,需要注册要监听的消息。

 chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
     console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension");
     if (request.greeting == "hello") //判断是否为要处理的消息
         sendResponse({
             farewell: "goodbye"
         });
 });

注意:如果为同一个消息注册了多个监听器,则只有第一个监听器能够调用sendResponse()方法,其他的监听器将被忽略。

1.6 保持长期连接的模式

对于保持长期连接的模式,在content scripts与Chrome扩展程序页面之间建立通道(可以为通道命名),可以处理多个消息。在通道的两端分别拥有一个chrome.runtime.Port对象,用以收发消息。

在content scripts主动建立通道如下:

var port = chrome.runtime.connect({
    name: "yisheng"
}); //通道名称
port.postMessage({
    joke: "Knock knock"
}); //发送消息
port.onMessage.addListener(function(msg) { //监听消息
    if (msg.question == "Who's there?") port.postMessage({
        answer: "yisheng"
    });
    else if (msg.question == "Madame who?") port.postMessage({
        answer: "Madame... Bovary"
    });
});

在Google Chrome扩展程序页面主动建立通道如下:

chrome.tabs.query({
    active: true,
    currentWindow: true
}, function(tabs) {
    var port = chrome.tabs.connect( //建立通道
        tabs[0].id, {
            name: "yisheng"
        } //通道名称
    );
});

在content scripts或Google Chrome扩展程序页面,监听建立连接的请求如下:

chrome.runtime.onConnect.addListener(function(port) {
    console.assert(port.name == "yisheng");
    port.onMessage.addListener(function(msg) {
        if (msg.joke == "Knock knock") port.postMessage({
            question: "Who's there?"
        });
        else if (msg.answer == "Madame") port.postMessage({
            question: "Madame who?"
        });
        else if (msg.answer == "Madame... Bovary") port.postMessage({
            question: "I don't get it."
        });
    });
});

在content scripts或Google Chrome扩展程序页面的任一端,调用chrome.runtime.Port.disconnect()则关闭连接,同时出发disconnect事件。这时,只有另一端监听chrome.runtime.Port.onDisconnect事件,则可以知道连接关闭。

1.7 Google Chrome扩展程序之间消息模式

还可以在不同的Google Chrome扩展程序之间发送消息,只要知道Google Chrome扩展程序的ID。这使得Google Chrome扩展程序可以发布服务为其他扩展程序所用。

这种Google Chrome扩展程序之间的消息也分为一次性请求与响应模式保持长期连接的模式

Google Chrome扩展程序监听调用其服务的消息如下:

//一次性请求与响应模式:
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
    if (sender.id == blacklistedExtension) //黑名单
        return; // don't allow this extension access
    else if (request.getTargetData) sendResponse({
        targetData: targetData
    });
    else if (request.activateLasers) {
        var success = activateLasers();
        sendResponse({
            activateLasers: success
        });
    }
});
//保持长期连接的模式:
chrome.runtime.onConnectExternal.addListener(function(port) {
    port.onMessage.addListener(function(msg) {
        // See other examples for sample onMessage handlers.
    });
});

发送调用服务的消息如下:

// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(laserExtensionId, {
    getTargetData: true
}, function(response) {
    if (targetInRange(response.targetData)) chrome.runtime.sendMessage(laserExtensionId, {
        activateLasers: true
    });
});
// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);
1.8 Google Chrome扩展程序接收指定的Web页面发送的消息

Google Chrome扩展程序可以与一些指定地点Web页面直接收发消息。

首先,在Google Chrome扩展程序的manifest.json文件设置可以通信的Web页面范围:

"externally_connectable": {
    "matches": ["*://*.example.com/*"]
}

其次,在Google Chrome扩展程序中监听Web页面的消息如下:

chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
    if (sender.url == blacklistedWebsite) return; // don't allow this web page access
    if (request.openUrlInEditor) openUrl(request.openUrlInEditor);
});

最后,在指定的Web页面中,发送消息如下:

// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {
    openUrlInEditor: url
}, function(response) {
    if (!response.success) handleError(url);
});

通过将浏览器所在客户端的本地应用注册为Chrome浏览器扩展的“本地消息主机(native messaging host)”,Chrome浏览器扩展还可以与客户端本地应用之间收发消息。

客户端的本地应用注册为Chrome浏览器扩展的“本地消息主机”之后,Chrome浏览器会在独立的进程中启动该本地应用,并通过标准输入/输出流(stdin/stdout)进行消息通信。

\1) 本地应用的配置文件的内容

本地应用要能够成为“本地消息主机”,必须有一个manifest.json配置文件(文件名任意),该文件是一个有效的JSON文件,示例如下。

 {
     "name": "com.my_company.my_application",
     "description": "My Application",
     "path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe",
     "type": "stdio",
     "allowed_origins": ["chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"]
 }

其中的属性含义及用法如下。

属性名 含义
name 标识名称。只能由小写字母、数字、_和.组成;首尾不能是.且不能有. 连续出现
description 描述
path 本地应用的完整路径,Linux和OS X上必须是绝对路径,Windows上可以是基于配置文件所在位置的相对路径
type 与本地消息主机进行通信的接口类型。目前只支持stdio,表示Chrome浏览器通过stdin和stdout与本地消息主机通信
allowed_origins 授权与本地消息主机进行通信的Chrome浏览器扩展。不能使用通配符

\2) 本地应用的配置文件的路径

配置文件的具体位置与操作系统有关。在Windows操作系统上,配置文件可以位于任何路径,只需在安装本地应用时修改注册表指明其位置即可。

创建key:

**HKEY_LOCAL_MACHINE**``\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application

**HKEY_CURRENT_USER**``\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application

设置默认值Default为配置文件所在的绝对路径:

C:\path\to\nmh-manifest.json

也可以直接创建注册表文件.reg如下,安装时执行即可:

1 Windows Registry Editor Version 5.00
2 [HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application]
3 @="C:\\path\\to\\nmh-manifest.json"

对于Linux和OS X操作系统,本地应用的配置文件的位置首先与安装的Chrome浏览器(Google Chrome或Chromium)有关。其次,如果是系统级别的本地应用,其配置文件应该位于固定位置。如果是用户级别的本地应用,则其配置文件位于用户主目录下的名为NativeMessagingHosts的子目录中。具体位置如下表所示。

Google Chrome Chromium
Linux系统应用 /etc/opt/chrome/native-messaging-hosts/com.my_company.my_application.json /etc/chromium/native-messaging-hosts/com.my_company.my_application.json
Linux用户应用 ~/.config/google-chrome/NativeMessagingHosts/com.my_company.my_application.json ~/.config/chromium/NativeMessagingHosts/com.my_company.my_application.json
OS X系统应用 /Library/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json /Library/Application Support/Chromium/NativeMessagingHosts/com.my_company.my_application.json
OS X用户应用 ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json ~/Library/Application Support/Chromium/NativeMessagingHosts/com.my_company.my_application.json

\3) Chrome浏览器扩展与本地应用之间的消息通信

Chrome浏览器扩展要与客户端本地应用进行通信,首先必须在Chrome浏览器扩展的manifest.json配置文件中声明权限如下:

1 {
2   "permissions": [
3     "nativeMessaging"
4   ],
5 }

Chrome浏览器扩展与客户端本地应用之间的消息通信非常类似于不同Chrome浏览器扩展之间的通信,示例如下。

在Chrome浏览器扩展中创建端口,监听来自本地应用的多个消息:

 1 var port = chrome.runtime.connectNative('com.my_company.my_application');
 2  //参数为本地应用在其配置文件中声明的标识名称
 3 
 4 port.onMessage.addListener(function(msg) {//收到消息后的处理函数
 5   console.log("Received" + msg);
 6 });
 7 port.onDisconnect.addListener(function() {
 8   console.log("Disconnected");
 9 });
10 
11 port.postMessage({ text: "Hello, my_application" }); //发送一条消息

在Chrome浏览器扩展中也可以不打开端口,发送一次性消息如下:

1 chrome.runtime.sendNativeMessage(
2   'com.my_company.my_application',
3   { text: "Hello" },
4   function(response) {//收到返回消息后的处理函数
5     console.log("Received " + response);
6   });

如果Chrome浏览器扩展在调用本地应用时发生异常,将会在stderr输出错误信息。如果因违反了本地消息的协议约束而出错,将会在Chrome浏览器的错误日志文件输出错误信息。Linux和OS X操作系统上,通过命令行启动Chrome浏览器,就可以在命令行窗口看到错误信息。Windows操作系统上,启动Chrome浏览器时带上--enable-logging参数(在chrome.exe的右键属性菜单中,General面板顶部的文本框中,chrome.exe后面直接跟上参数即可),就可以在Chrome浏览器的日志文件中看到错误信息。

  1. 本地消息的协议约束

客户端的本地应用注册为Chrome浏览器扩展的“本地消息主机”之后,Chrome浏览器会在独立的进程中启动该本地应用,并通过标准输入/输出流(stdin/stdout)进行消息通信。

Chrome浏览器扩展与客户端的本地应用之间的双向通信采用消息机制,该消息以JSON格式,UTF-8编码,带32位(操作系统本地字节序)的消息长度作为前缀。从本地应用发送到Chrome浏览器扩展的消息,最大尺寸是1M字节。从Chrome浏览器扩展发送到本地应用的消息,最大尺寸是4G字节。

1.9 chrome.tabs API中的常用方法:

创建一个新的浏览器窗口

chrome.windows.create(object createData, function(Window window){…})

createData对象是可选的,表示采用默认值创建窗口。createData对象的属性同chrome.windows.Window对象。

关闭一个指定的浏览器窗口

chrome.windows.remove(integer windowId, function(){…})

修改一个指定的浏览器窗口的参数

chrome.windows.update(integer windowId, object updateInfo, function(Window window){…})

updateInfo对象是必选的,其中未涉及的Window属性则保持不变。

获取指定ID的窗口对象

chrome.windows.get(integer windowId, object getInfo, function(Window window){…})

getInfo对象是可选的,其有两个参数。一个是名为populate的布尔型参数,true表示该方法得到的chrome.windows.Window对象里包含chrome.tabs.Tab类型的数组。另一个名为windowTypes的WindowType型参数,表示通过windowTypes进行筛选窗口。

获取所有的窗口对象

chrome.windows.getAll(object getInfo, function(array of Window){…})

getInfo对象是可选的,其有两个参数。一个是名为populate的布尔型参数,true表示该方法得到的chrome.windows.Window对象里包含chrome.tabs.Tab类型的数组。另一个名为windowTypes的WindowType型参数,表示通过windowTypes进行筛选窗口。

复制一个新tab

chrome.tabs.duplicate(integer tabId, function(Tab tab){…})

修改tab的属性

chrome.tabs.update(integer tabId, object updateProperties, function(Tab tab){…})

updateProperties对象是必选的,其中未涉及的tab属性则保持不变。

根据指定的若干属性查找tabs

chrome.tabs.query(object queryInfo, function(array of Tab){…})

1.10 chrome.tabs API中的常用方法:

chrome.events中定义了一些常见的事件类型,可以供Chrome浏览器扩展程序发出对应的事件对象。

对于关注的事件,首先要通过addListener()在对应的事件上注册监听器,示例如下:

chrome.alarms.onAlarm.addListener(function(alarm) {
    appendToLog('alarms.onAlarm --' + ' name: ' + alarm.name + ' scheduledTime: ' + alarm.scheduledTime);
});

addListener()总是带一个参数,这就是事件处理函数。一旦事件发生,监听器即调用事件处理函数。

事件处理函数还可以有参数,具体参数的数量和类型与监听的事件相关。

chrome.events中,事件没有任何属性,只包含如下的常用方法:

方法名 返回值类型 注释
addListener(function() {...}) 为事件注册监听器,并给出事件处理函数
removeListener(function() {...}) 删除事件的监听器
hasListener(function() {...}) boolean 判断事件上是否注册有监听器
hasListeners() boolean 判断事件上是否注册有监听器,没有回调函数
addRules(array of Rule rules, function (array of Rule rules) {...}) 为事件注册规则如果第一个参数的数组中有无效规则,则不会注册任何规则
removeRules(array of string ruleIdentifiers, function() {...}) 删除事件上注册的所有规则。如果有第一个参数(可以没有),则只删除注册的、在范围内的规则
getRules(array of string ruleIdentifiers, function (array of Rule rules) {...}) 返回事件上注册的所有规则。如果有第一个参数(可以没有),则只返回注册的、在范围内的规则

有这样一种场景,对于发生的事件,只有当满足某些条件时才进行处理,即并非要对发生的该事件都进行处理。对于这种情况,可以通过为事件处理函数附加过滤条件实现,也可以采用全新的事件规则机制。下面分别讨论。

过滤事件

过滤事件,就是对于监听的事件,只有当其符合过滤条件时,才会调用事件处理函数。多个过滤条件之间是“或”的关系。示例如下:

chrome.webNavigation.onCommitted.addListener(function(e) {
        // ...
    }, {
        url: [{
                hostSuffix: 'google.com'
            }, //过滤条件1
            {
                hostSuffix: 'google.com.au'
            }
        ]
    } //过滤条件2
);
声明式事件处理器(事件规则)

声明式事件处理器就是为事件定义处理规则。注册了规则的事件对象,当事件对象发生时,不会再调用事件处理函数,而是检测注册的规则的条件是否有匹配,如果匹配则执行规则的行为。

通常,规则必须包含声明条件和处理行为。示例如下:

 var rule = {
     id: "my rule", // 标识符可选, 没有则自动生成
     priority: 100, // 优先级可选, 默认为100
     conditions: [ /* my conditions */ ],
     actions: [ /* my actions */ ]
 };

一个规则可以拥有多个条件,条件之间是“或”的关系,即任意一个条件满足则表示匹配规则。

一个规则可以拥有多个处理行为,行为之间是“与”的关系,即所有的行为作为一个整体,在条件匹配的时候一起执行。

chrome.events.Rule对象的属性如下:

属性名 类型 必选/****可选 注释
id string 可选 标识符
tags array of string 可选 规则的标签,多个规则可以根据标签分组
conditions array of any 必选 触发处理行为的条件
actions array of any 必选 任一条件匹配时触发的处理行为
priority integer 可选 规则的优先级,默认100

一个规则在整个浏览器的会话中都是有效得到,所以通常在安装Chrome扩展的chrome.runtime.onInstalled事件处理函数中安装规则。而在Chrome扩展升级时也会触发chrome.runtime.onInstalled事件,所以要注意在该事件处理函数中,先判断是否有已经安装了的规则,如果有则首先删除已经安装的规则,然后再安装规则。

声明式条件的检查由浏览器直接进行,而无需JavaScript引擎,所以减少了延迟。

声明式事件处理器往往用于chrome.declarativeWebRequest API(目前还不稳定)和chrome.declarativeContent API。

事件规则的最佳实践:

  • 以数组的方式批量注册规则,而非逐个注册规则;
  • 规则的条件是URL时,将URL分成若干个子字符串进行匹配,而非整个URL的表达式;(对URL的过滤,有一个专用的chrome.events.UrlFilter可用,此处略。)
  • 如果几个条件对应相同的处理行为,则将这些条件放进一个规则中
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • 插件功能 平时对我来说最浪费时间的莫过于刷「知乎」,但是手贱停不下来呀。Chrome 上面装了个 StayFocu...
    王兵阅读 1,268评论 3 26
  • 1. 什么是Chrome插件 Chrome插件是一个用Web技术开发、用来增强浏览器功能的软件,它其实就是一个由H...
    William92阅读 1,438评论 0 5
  • 前言 周末学习了下Chrome插件的开发,总体来说入门还是比较容易的,动手配合一些demo就能了解基本的开发过程。...
    Bbang呀_阅读 2,154评论 1 3
  • 谷歌插件开发笔记 插件开发相关及问题记录。 第一阶段,原生chrome extension阶段,调用chorme的...
    小朴同学阅读 1,803评论 0 0
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 120,556评论 2 7