Chuyển tin nhắn

Vì tập lệnh nội dung chạy trong ngữ cảnh của trang web chứ không phải tiện ích, nên chúng thường cần một số lệnh cách giao tiếp với phần còn lại của tiện ích. Ví dụ: Tiện ích trình đọc RSS có thể sử dụng các tập lệnh nội dung để phát hiện sự hiện diện của nguồn cấp dữ liệu RSS trên trang, sau đó thông báo cho trang nền trong để hiển thị biểu tượng hành động trên trang cho trang đó.

Hoạt động giao tiếp giữa tiện ích và tập lệnh nội dung của tiện ích hoạt động bằng cách truyền thông báo. Một trong hai có thể nghe tin nhắn được gửi từ đầu bên kia và trả lời trên cùng một kênh. Một tin nhắn có thể chứa mọi đối tượng JSON hợp lệ (null, boolean, số, chuỗi, mảng hoặc đối tượng). Có một lựa chọn API cho các yêu cầu một lần và một API phức tạp hơn cho phép bạn có thời gian tồn tại lâu dài kết nối để trao đổi nhiều thư trong cùng một ngữ cảnh. Bạn cũng có thể gửi một thông báo đến một tiện ích khác nếu bạn biết mã của tiện ích đó. Thông tin này được đề cập trong tiện ích chéo tin nhắn.

Yêu cầu một lần đơn giản

Nếu bạn chỉ cần gửi một tin nhắn duy nhất đến một phần khác trong tiện ích (và có thể nhận phản hồi lại), bạn nên sử dụng runtime.sendMessage hoặc tabs.sendMessage đơn giản hoá . Thao tác này cho phép bạn gửi thông báo có thể chuyển đổi tuần tự JSON một lần từ tập lệnh nội dung đến tiện ích hoặc ngược lại ngược lại . Tham số gọi lại không bắt buộc cho phép bạn xử lý phản hồi từ nếu có.

Cách gửi yêu cầu từ tập lệnh nội dung có dạng như sau:

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

Việc gửi yêu cầu từ tiện ích đến một tập lệnh nội dung trông rất giống nhau, ngoại trừ việc bạn cần chỉ định thẻ nào để gửi báo cáo đó đến. Ví dụ này minh hoạ cách gửi một thông báo đến tập lệnh nội dung trong thẻ đã chọn.

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

Ở bên nhận, bạn cần thiết lập trình nghe sự kiện runtime.onMessage để xử lý . Tập lệnh nội dung hoặc trang tiện ích cũng tương tự như vậy.

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"});
  }
);

Trong ví dụ trên, sendResponse được gọi đồng bộ. Nếu bạn muốn sử dụng không đồng bộ sendResponse, thêm return true; vào trình xử lý sự kiện onMessage.

Lưu ý: Nếu nhiều trang đang theo dõi sự kiện onMessage, thì chỉ trang đầu tiên gọi sendResponse() cho một sự kiện cụ thể mới gửi thành công phản hồi. Tất cả các phản hồi khác cho sự kiện đó sẽ bị bỏ qua.
Lưu ý: Lệnh gọi lại sendResponse chỉ hợp lệ nếu được sử dụng đồng bộ hoặc nếu trình xử lý sự kiện trả về true để cho biết sẽ phản hồi không đồng bộ. Lệnh gọi lại của hàm sendMessage sẽ tự động được gọi nếu không có trình xử lý nào trả về giá trị true hoặc nếu lệnh gọi lại sendResponse được thu thập rác.

Kết nối lâu dài

Đôi khi, sẽ rất hữu ích nếu một cuộc trò chuyện kéo dài hơn một yêu cầu và phản hồi. Trong trường hợp này, bạn có thể mở một kênh dài hạn từ tập lệnh nội dung đến trang tiện ích , hoặc ngược lại, dùng runtime.connect hoặc tabs.connect để thực hiện . Kênh đó có thể (không bắt buộc) có tên, giúp bạn phân biệt giữa các loại kết nối khác nhau.

Một trường hợp sử dụng có thể là tiện ích tự động điền biểu mẫu. Tập lệnh nội dung có thể mở kênh để trang của tiện ích đối với thông tin đăng nhập cụ thể và gửi thông báo đến tiện ích cho mỗi lần nhập trên trang để yêu cầu điền dữ liệu biểu mẫu. Kết nối dùng chung cho phép tiện ích để duy trì trạng thái được chia sẻ liên kết một số thông báo đến từ tập lệnh nội dung.

Khi thiết lập kết nối, mỗi đầu sẽ được cấp một đối tượng runtime.Port dùng cho gửi và nhận tin nhắn qua kết nối đó.

Dưới đây là cách bạn mở kênh từ một tập lệnh nội dung, cũng như gửi và nghe thông báo:

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"});
});

Việc gửi yêu cầu từ tiện ích đến một tập lệnh nội dung trông rất giống nhau, ngoại trừ việc bạn cần chỉ định thẻ mà bạn muốn kết nối. Chỉ cần thay thế cuộc gọi để kết nối trong ví dụ trên bằng tabs.connect.

Để xử lý các kết nối đến, bạn cần thiết lập sự kiện runtime.onConnect trình nghe. Tập lệnh nội dung hoặc trang tiện ích cũng tương tự như vậy. Khi một phần khác của tiện ích sẽ gọi "connect()", sự kiện này sẽ được kích hoạt cùng với đối tượng runtime.Port sử dụng để gửi và nhận tin nhắn qua kết nối. Sau đây là giao diện của bạn khi phản hồi kết nối đến:

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name == "knockknock");
  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."});
  });
});

Tuổi thọ của cổng

Các cổng được thiết kế như một phương thức liên lạc hai chiều giữa các phần khác nhau của tiện ích, trong đó khung (cấp cao nhất) được xem là phần nhỏ nhất. Khi gọi tabs.connect, runtime.connect hoặc runtime.connectNative, một Cổng sẽ được tạo. Cổng này có thể được dùng ngay để gửi thư đến đầu bên kia qua postMessage.

Nếu có nhiều khung trong một thẻ, việc gọi tabs.connect sẽ dẫn đến nhiều lệnh gọi hàm sự kiện runtime.onConnect (một lần cho mỗi khung trong thẻ). Tương tự, nếu Bạn sử dụng runtime.connect, sau đó sự kiện onConnect có thể được kích hoạt nhiều lần (một lần cho mỗi khung trong quá trình mở rộng).

Bạn có thể muốn biết thời điểm kết nối bị đóng, ví dụ: nếu bạn vẫn đang tách biệt trạng thái cho mỗi cổng mở. Để thực hiện việc này, bạn có thể nghe sự kiện runtime.Port.onDisconnect. Chiến dịch này được kích hoạt khi không có cổng hợp lệ ở phía bên kia của kênh. Điều này xảy ra trong các trường hợp sau:

  • Không có trình nghe nào cho runtime.onConnect ở đầu kia.
  • Thẻ chứa cổng đã bị huỷ tải (ví dụ: thẻ đang được điều hướng).
  • Khung từ nơi connect được gọi đã huỷ tải.
  • Tất cả khung đã nhận cổng (thông qua runtime.onConnect) đã huỷ tải.
  • runtime.Port.disconnect do đầu bên kia gọi. Xin lưu ý rằng nếu cuộc gọi connect vào nhiều cổng ở phía đầu nhận và disconnect() được gọi trên bất kỳ cổng nào trong số này, sau đó sự kiện onDisconnect chỉ được kích hoạt tại cổng của người gửi chứ không được kích hoạt tại các cổng khác.

Thông báo trên nhiều tiện ích

Ngoài việc gửi thông báo giữa các thành phần khác nhau trong tiện ích, bạn có thể sử dụng Nhắn tin để giao tiếp với các tiện ích khác. Thao tác này cho phép bạn hiển thị một API công khai mà có thể tận dụng.

Việc theo dõi các yêu cầu và kết nối được gửi đến cũng tương tự như trường hợp nội bộ, ngoại trừ việc bạn sử dụng Phương thức runtime.onMessageExternal hoặc runtime.onConnectExternal. Sau đây là ví dụ về mỗi:

// For simple requests:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id == blocklistedExtension)
      return;  // don't allow this extension access
    else if (request.getTargetData)
      sendResponse({targetData: targetData});
    else if (request.activateLasers) {
      var success = activateLasers();
      sendResponse({activateLasers: success});
    }
  });

// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
  port.onMessage.addListener(function(msg) {
    // See other examples for sample onMessage handlers.
  });
});

Tương tự, việc gửi tin nhắn đến một tiện ích khác cũng tương tự như gửi tin nhắn trong tiện ích của bạn. Điểm khác biệt duy nhất là bạn phải truyền mã nhận dạng của tiện ích mà bạn muốn giao tiếp. Ví dụ:

// 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(...);

Gửi tin nhắn từ các trang web

Tương tự như tính năng gửi thông báo trên nhiều tiện ích, ứng dụng hoặc tiện ích của bạn có thể nhận và phản hồi thư từ các trang web thông thường. Để sử dụng tính năng này, trước tiên, bạn phải chỉ định trong tệp manifest.json trang web nào bạn muốn giao tiếp. Ví dụ:

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

Thao tác này sẽ hiển thị API thông báo cho mọi trang khớp với mẫu URL mà bạn chỉ định. URL mẫu phải chứa ít nhất một miền cấp hai – tức là các mẫu tên máy chủ như "*", "*.com", "*.co.uk" và "*.appspot.com" đều bị cấm. Từ trang web, hãy sử dụng Các API runtime.sendMessage hoặc runtime.connect để gửi thông báo đến một ứng dụng cụ thể hoặc tiện ích. Ví dụ:

// 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);
  });

Từ ứng dụng hoặc tiện ích của mình, bạn có thể nghe tin nhắn từ trang web thông qua API runtime.onMessageExternal hoặc runtime.onConnectExternal, tương tự như cross-extension nhắn tin. Chỉ trang web mới có thể bắt đầu kết nối. Dưới đây là ví dụ:

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

Nhắn tin gốc

Tiện ích và ứng dụng có thể trao đổi thông báo với các ứng dụng gốc được đăng ký làm máy chủ nhắn tin gốc. Để tìm hiểu thêm về tính năng này, hãy xem Nhắn tin gốc.

Lưu ý về bảo mật

Tập lệnh nội dung không đáng tin cậy

Tập lệnh nội dung không đáng tin cậy hơn so với trang nền của tiện ích (ví dụ: một trang web độc hại có thể ảnh hưởng đến quy trình kết xuất đồ hoạ nơi tập lệnh nội dung chạy). Giả sử thông báo từ một tập lệnh nội dung có thể đã bị kẻ tấn công tạo và đảm bảo xác thực và dọn dẹp mọi dữ liệu đầu vào. Giả sử mọi dữ liệu được gửi đến tập lệnh nội dung đều có thể bị rò rỉ ra trang web. Giới hạn phạm vi của các thao tác đặc quyền mà tin nhắn nhận được từ nội dung có thể kích hoạt các tập lệnh.

Tập lệnh trên nhiều trang web

Khi nhận được thông báo từ một tập lệnh nội dung hoặc một tiện ích khác, các tập lệnh của bạn phải cẩn thận để không trở thành nạn nhân của hoạt động viết tập lệnh trên nhiều trang web. Lời khuyên này áp dụng cho các tập lệnh chạy bên trong trang nền của tiện ích mở rộng cũng như các tập lệnh nội dung chạy bên trong các nguồn gốc web khác. Cụ thể, hãy tránh sử dụng các API nguy hiểm như các API dưới đây:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating an evil script!
  var resp = eval("(" + response.farewell + ")");
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be injecting a malicious script!
  document.getElementById("resp").innerHTML = response.farewell;
});

Thay vào đó, hãy ưu tiên các API an toàn hơn và không chạy tập lệnh:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse does not evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});

Ví dụ

Bạn có thể tìm thấy các ví dụ đơn giản về hoạt động giao tiếp qua tin nhắn trong phần examples/api/Messaging thư mục. Mẫu thông báo gốc minh hoạ cách một ứng dụng Chrome có thể giao tiếp với ứng dụng gốc. Để xem thêm ví dụ và để được trợ giúp xem mã nguồn, hãy xem phần Mẫu.