Chrome Extension : 事件交互实例 (消息传递, sendMessage,connect,onConnect,onMessage)

alert.js

alert('hello ' + document.location.href);

manifest.json

{
  "manifest_version": 2,

  "name": "Click to execute",
  "description": "Execute script after click in popup.html (chrome extension) http://stackoverflow.com/questions/20764517/execute-script-after-click-in-popup-html-chrome-extension.",
  "version": "1.0",

  "icons": {
    "48": "icon.png"
  },

  "permissions": [
    "tabs", "<all_urls>"
  ],

  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },

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

popup.html

<!DOCTYPE html>
<html>
  <body style="width: 300px">
    Open <a href="http://stackoverflow.com" target="_blank">this page</a> and then 
    <button id="clickme">click me</button>
    <script type="text/javascript" src="popup.js"></script>
  </body>
</html>

popup.js

// var app = chrome.runtime.getBackgroundPage();

function hello() {
  chrome.tabs.executeScript({
    file: 'alert.js'
  }); 
}

document.getElementById('clickme').addEventListener('click', hello);

GitHub:https://gist.github.com/greatghoul/8120275

实例下载:popup-background-event

如果您只需要向您的扩展程序的另一部分发送一个简单消息(以及可选地获得回应),您应该使用比较简单的 runtime.sendMessage 方法。这些方法允许您从内容脚本向扩展程序发送可通过 JSON 序列化的消息,可选的 callback 参数允许您在需要的时候从另一边处理回应。

如下列代码所示从内容脚本中发送请求:

chrome.runtime.sendMessage({greeting: "您好"}, function(response) {
  console.log(response.farewell);
});

从扩展程序向内容脚本发送请求与上面类似,唯一的区别是您需要指定发送至哪一个标签页。这一例子演示如何向选定标签页中的内容脚本发送消息。

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

在接收端,您需要设置一个 runtime.onMessage 事件监听器来处理消息。

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "来自内容脚本:" + sender.tab.url :
                "来自扩展程序");
    if (request.greeting == "您好")
      sendResponse({farewell: "再见"});
  });

注意: 如果多个页面都监听 onMessage 事件,对于某一次事件只有第一次调用 sendResponse() 能成功发出回应,所有其他回应将被忽略。

有时候需要长时间的对话,而不是一次请求和回应。在这种情况下,您可以使用runtime.connecttabs.connect 从您的内容脚本建立到扩展程序的长时间连接。建立的通道可以有一个可选的名称,让您区分不同类型的连接。

使用长时间连接的一种可能的情形为自动填充表单的扩展程序。对于一次登录操作,内容脚本可以连接到扩展程序页面,每次页面上的输入元素需要填写表单数据时向扩展程序发送消息。共享的连接允许扩展程序保留来自内容脚本的不同消息之间的状态联系。

建立连接时,两端都将获得一个 runtime.Port 对象,用来通过建立的连接发送和接收消息。

如下代码演示如何从内容脚本中建立连接,发送并监听消息:

var port = chrome.runtime.connect({name: "敲门"});
port.postMessage({joke: "敲门"});
port.onMessage.addListener(function(msg) {
  if (msg.question == "是谁?")
    port.postMessage({answer: "女士"});
  else if (msg.question == "哪位女士?")
    port.postMessage({answer: "Bovary 女士"});
});

为了处理传入连接,您需要设置一个 runtime.onConnect 事件监听器。这一步无论在内容脚本还是扩展程序页面中都是一样的。当您的扩展程序的另一部分调用 connect() 时,会产生这一事件,同时传递您可以通过建立的连接发送和接受消息的 runtime.Port 对象。如下代码演示如何回应传入连接:

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name == "敲门");
  port.onMessage.addListener(function(msg) {
    if (msg.joke == "敲门")
      port.postMessage({question: "是谁?"});
    else if (msg.answer == "女士")
      port.postMessage({question: "哪位女士?"});
    else if (msg.answer == "Bovary 女士")
      port.postMessage({question: "我没听清楚。"});
  });
});

您可能想知道连接何时关闭,例如您需要为每一个打开的端口单独保留状态。这种情况下您可以监听 runtime.Port.onDisconnect 事件,当连接的另一端调用 runtime.Port.disconnect 或包含该端口的页面已结束(例如标签页转到了另一个页面)时,对于每一个端口确保都会发生一次该事件。

更多有关消息传递:https://crxdoc-zh.appspot.com/apps/messaging

实例:

manifest.json

{
  "manifest_version": 2,

  "name": "Click to execute",
  "description": "justcode.ikeepstudying.com",
  "version": "1.0",

  "icons": {
    "48": "icon.png"
  },

  "permissions": [
    "tabs", "<all_urls>"
  ],

  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },

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

popup.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Popup</title>
    <style>
        *{font-family:"Microsoft Yahei","Monaco",sans-serif;font-size:13px;}
        button{cursor:pointer;padding:2px 4px 4px 4px;border:1px gray solid;background:none;}
        #wrapper{width:240px;height:120px;padding:0 12px;margin:auto;}
        #wrapper .clear{color: #fff;background-color: #d9534f;border-color:#d43f3a;}
        #waitingForClear{clear:both;display:inline-block;margin-top:4px;background:yellow;}
    </style>
</head>
<body>
    <div id="wrapper">
        <p>Welcome to use Amazon Seller picker!</p>
        <button class="clear" id="clearCache">Clear All Sellers From Cache</button>
        <span id="waitingForClear"></span>
    </div>
</body>
<script type="text/javascript" src="scripts/popup.js"></script>
</html>

popup.js等js文件最好写在最下方,否则操作内部html元素时,可能有无法获取元素的错误!

popup.js

var clearCacheBtn = document.getElementById("clearCache");
var waitingForClearAara = document.getElementById("waitingForClear");

// 简单一次性请求
// clearCacheBtn.addEventListener("click", function(e) {
//   waitingForClearAara.innerHTML("Clearing Cache, Please Waiting...");
//   chrome.runtime.sendMessage({'clearAllCache': true}, function(response){
//     waitingForClearAara.innerHTML("Clearing Cache, Please Waiting...");
//   });
// });

// 长时间的连接
clearCacheBtn.addEventListener("click", function(e) {
  waitingForClearAara.innerHTML = "Clearing Cache, Please Waiting...";

  var port = chrome.runtime.connect({name:"clearCache"});
  port.postMessage({clearAllCache:true});
  port.onMessage.addListener(function(response) {
    console.log(response);
    waitingForClearAara.innerHTML =
      (response.clearedAllCache ? "Cleared, Please Refresh Your Page To Load New Contents!" : "Fail To Clear Cache");
  });
});

background.js

// 简单的一次性请求
// chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
//   if(message.clearAllCache) {
//     console.log("try to clear all cache ...");
//     //console.log(sender.tab ? "来自内容脚本:" + sender.tab.url :"来自扩展程序");
//     chrome.storage.local.clear();
//     sendResponse({succes:true});
//   }
//   else console.log("fail to clear cache...");
// });

// 长时间的连接
chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name == "clearCache");
  port.onMessage.addListener(function(message) {
    console.log(message);
    if (message.clearAllCache) {
      console.log("try to clear all cache ...");
      chrome.storage.local.clear();
      port.postMessage({clearedAllCache:true});
    }
  });
});

更多参考:

Chrome 插件 DIY
如何查看chrome扩展的源代码, chrome扩展二次开发

Chrome插件(Extensions)开发攻略

Chrome Extension 学习笔记

文本:Chrome Extension : 事件交互实例 (sendMessage,connect,onConnect,onMessage)