首页 > 代码库 > 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‘);

    });

});