首页 > 代码库 > chrome extension message passing 消息传递
chrome extension message passing 消息传递
Chrome插件开发笔记
360翻译了Chrome插件开发的文档 (仍然建议出去看看)
看这里http://open.chrome.360.cn/extension_dev/overview.html
什么是扩展
一个应用(扩展)其实是压缩在一起的一组文件,包括HTML,CSS,Javascript脚本,图片文件,还有其它任何需要的文件。 应用(扩展)本质上来说就是web页面,它们可以使用所有的浏览器提供的API,从XMLHttpRequest到JSON到HTML5全都有。
扩展extension 和plugins是不同的
扩展(Extension),指的是通过调用 Chrome 提供的 Chrome API 来扩展浏览器功能的一种组件,工作在浏览器层面,使用 HTML + Javascript 语言开发[*]。比如著名的 Adblock plus。
插件(Plug-in),指的是通过调用 Webkit 内核 NPAPI 来扩展内核功能的一种组件,工作在内核层面,理论上可以用任何一种生成本地二进制程序的语言开发,比如 C/C++、Delphi 等。比如Flash player 插件,就属于这种类型。一般在网页中用 <object> 或者 <embed> 标签声明的部分,就要靠插件来渲染。
扩展的种类
browser action page action
当应用(扩展)的图标是否显示出来是取决于单个的页面时,应当选择page action;当其它情况时可以选择browser action。
扩展的构成
一个manifest文件
一个或多个html文件(除非这个应用是一个皮肤)
可选的一个或多个javascript文件
可选的任何需要的其他文件,例如图片
每一个扩展、可安装的WebApp、皮肤,都有一个JSON格式的manifest文件,叫manifest.json,里面提供了重要的信息 。
扩展的基本架构
一个browser action可以包含一个弹窗(popup)
如果一个应用(扩展)需要与web页面交互,那么就需要使用一个content script。
backgroundJS
绝大多数应用(扩展)都包含一个背景页面(background page),用来执行应用(扩展)的主要功能。
什么样的文件是backgroundJS? 这些会在manifest.json中指定的
background只会在插件加载的时候运行
background.html是一个始终运行于浏览器后台的页面,浏览器关闭时它才被关闭
contentScript
如果一个应用(扩展)需要与web页面交互,那么就需要使用一个content script。Content script脚本是指能够在浏览器已经加载的页面内部运行的javascript脚本。可以将content script看作是网页的一部分,而不是它所在的应用(扩展)的一部分。它和backgroundJS 是运行在不同的环境中 他们之间的通信需要通过message来完成 同样popupàcontent 也需要使用消息来传递数据
一些限制
不能使用除了chrome.extension之外的chrome.* 的接口
不能访问它所在extension中定义的函数和变量 也就是说得不到background
不能访问所在web页面或其它content script中定义的函数和变量
不能做 cross-site XMLHttpRequests
PopupJS(也就是UI的JS)
插件页面嵌入的JS
这是另一个环境中运行的JS 其运行环境是点击插件显示的页面
页面间的通信
可以使用chrome.extension中的方法来获取应用(扩展)中的页面,例如getViews()和getBackgroundPage()。一旦一个页面得到了对应用(扩展)中其它页面的引用,它就可以调用被引用页面中的函数,并操作被引用页面的DOM树。
如果你想要在插件页面中调用background page中得函数 chrome.extension.getBackgroundPage()
参考
http://stackoverflow.com/questions/11752341/chrome-extension-communication-between-content-script-and-background-html
如果想在background页面调用popup page中得函数
chrome.extension.getViews()
以数组的形式返回当前扩展运行的所有页面的JavaScript “window”对象。
PS content script是不能使用该函数的
参考
http://open.chrome.360.cn/extension_dev/extension.html#method-getViews
https://developer.chrome.com/extensions/extension#method-getViews
PS参数写法
http://stackoverflow.com/questions/18286684/how-do-i-use-chrome-extension-getviews-to-send-activate-a-function-on-the-fron
views=chrome.extension.getViews({type: ‘popup‘});
PS要想得到popup的views 首先要保证插件的popup处于显示状态
假如插件页面js有个函数是xxx 则调用方法为views[0].xxx()
一个Demo
setTimeout(function(){
views=chrome.extension.getViews({type: ‘popup‘});
console.log(views);
views[0].changeTextById(‘resultsRequest‘,new Date().toLocaleString());
},3000);
这段代码是在background中的 作用就是获取到一个popup的window 将当前时间传入 然后调用popup中的方法(作用就是更新一个元素内容)
另一种解决方案
参考
http://stackoverflow.com/questions/20570319/passing-variable-from-background-to-popup-on-chrome-extension
把popupJS的函数用一个对象都封装好 然后在popupJS中得到backgroundJS的window 将这个popupJS的对象保存在backgroundJS的某个变量a中 然后a.xxx()
通过这样的方式来调用popupJS中的函数
消息传递(一次对话)
参考
http://www.ituring.com.cn/article/60272
https://developer.chrome.com/extensions/messaging
向background发送消息(popupàbg content àbg )
https://developer.chrome.com/extensions/runtime#event-onMessage
popup发消息到background
chrome.runtime.sendMessage(‘toN‘, function(response) {
document.querySelector(‘#resultsRequest‘).innerHTML += response;
});
content发消息到background的demo
chrome.runtime.sendMessage(‘toBG from content‘, function(response) {
console.log(‘bg response is ‘+response);
});
这个log信息应该在对应的tab页中查看 因为content script是嵌入到页面中的
向tab发送消息(也就是向content script 发送消息)(popupàcontent bgàcontent)
为什么传递消息到tab会不同呢? 因为tab需要指定消息作用到哪一个tab上
参考https://developer.chrome.com/extensions/tabs#method-sendRequest
PS 其中提到sendRequest已不建议使用 用sendMessage代替
PS 且getSelected也不建议使用 用tabs.query {active: true}代替
源码参考(源码略旧 注意要使用新的API)
http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/messaging/timer/
一个demo
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
var tab = tabs[0];
console.log(tab);
chrome.tabs.sendMessage(tab.id, ‘something I want to say’, function handler(response) {
document.querySelector(‘#resultsRequest‘).innerHTML += response.counter;
});
});
}, false);
接收消息
(接收消息木有区别background 和 content script中都是如此)
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {});
参考https://developer.chrome.com/extensions/messaging
官方的demo
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"});
});
sender是发送消息者的信息 (可以知道是不是tab发出的消息)
request是消息对象本身 它可是很多形式 数字 字符 或是JSON格式的对象
sendResponse是做响应的函数名
关于sender
由popup发送消息时 Sender
Object {id: "jakklaaifkgohekmdejmehikkbakelgd"}
由page发送时
Object {id: "jakklaaifkgohekmdejmehikkbakelgd", url: "http://weibo.com/u/2371564754/home?wvr=5", tab: Object}
由bg发送消息时
Object {id: "jakklaaifkgohekmdejmehikkbakelgd"}
background到popup呢?
首先肯定是不能将上面的例子方法交换的
从background到popup 首先要明白 popup页面不止一个 我需要发送信息到指定的页面的运行环境
要在bg中更新popup的数据 需要getView获取到popup页面的window环境(上面已经说明 这里不再赘述)
消息传递(多次对话)
https://developer.chrome.com/extensions/messaging
popupJSà bg
var port = chrome.runtime.connect({
name: "knockknock"
});
port.postMessage({
joke: "Knock knock"
});
//这里的回调需要再写一个监听
port.onMessage.addListener(function(msg) {
if (msg.question == "Who‘s there?")
port.postMessage({
answer: "Madame"
});
else if (msg.question == "Madame who?")
port.postMessage({
answer: "Madame... Bovary"
});
});
接收
//监听长连接
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function(msg) {
console.log(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."});
});
});
由此可见 和SendMessage不同的是长连接可以进行多次消息传递
popupàcontent script
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
var tab = tabs[0];
console.log(tab);
var port = chrome.tabs.connect(tab.id,{
name: "knockknock"
});
console.log(‘port‘+port);
port.postMessage({
joke: "Knock knock"
});
//这里的回调需要再写一个监听
port.onMessage.addListener(function(msg) {
document.querySelector(‘#popToContentPortMsg‘).innerHTML = msg;
});
接收消息
//监听长连接
chrome.runtime.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
console.log(‘PORT MSG ===============‘);
console.log(msg);
port.postMessage(‘long port‘);
});
});