메시지 전달

콘텐츠 스크립트는 확장 프로그램이 아닌 웹페이지의 컨텍스트에서 실행되기 때문에 통신 방식으로 사용할 수 있습니다. 예를 들어 RSS 리더 확장 프로그램은 감지한 다음 RSS 피드의 존재를 감지한 다음 해당 페이지에 페이지 작업 아이콘을 표시해야 합니다.

확장 프로그램과 콘텐츠 스크립트 간의 통신은 메시지 전달을 사용하여 작동합니다. 둘 중 하나 같은 쪽이 다른 쪽에서 보낸 메시지를 듣고 동일한 채널에서 응답할 수 있습니다. 메시지를 통해 유효한 JSON 객체 (null, 부울, 숫자, 문자열, 배열 또는 객체)를 포함해야 합니다. 간단한 일회성 요청용 API 및 오래 지속되는 요청이 가능한 좀 더 복잡한 API 연결 - 공유 컨텍스트로 여러 메시지를 교환합니다. 또한 해당 ID를 알고 있는 경우 다른 확장 프로그램에 교차 확장 프로그램 메시지 섹션에서 확인할 수 있습니다.

간단한 일회성 요청

확장 프로그램의 다른 부분에 하나의 메시지만 보내야 하는 경우 (선택적으로 되돌려 받는 경우) 간소화된 runtime.sendMessage 또는 tabs.sendMessage를 사용해야 합니다 . 이렇게 하면 콘텐츠 스크립트에서 확장 프로그램으로 또는 그 반대로 일회성 JSON 직렬화가 가능한 메시지를 보낼 수 있습니다. 와 같다고 가정해 보겠습니다 . 선택적 콜백 매개변수를 사용하면 한 쪽이 될 수도 있고요.

콘텐츠 스크립트에서 요청을 보내는 방법은 다음과 같습니다.

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

확장 프로그램에서 콘텐츠 스크립트로 요청을 보내는 것은 보낼 탭을 지정할 수 있습니다. 이 예는 콘텐츠 스크립트로 메시지를 보내는 방법을 보여줍니다. 표시됩니다.

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

수신 측에서는 다음과 같은 메시지를 처리하기 위해 runtime.onMessage 이벤트 리스너를 설정해야 합니다. 메시지가 표시됩니다. 이는 콘텐츠 스크립트 또는 확장 프로그램 페이지와 동일하게 보입니다.

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는 동기식으로 호출되었습니다. 단일 요청 모드를 사용하여 sendResponse인 경우 return true;onMessage 이벤트 핸들러에 추가합니다.

참고: 여러 페이지가 onMessage 이벤트를 수신 대기하고 있는 경우, 특정 이벤트에 대해 sendResponse()를 먼저 호출한 경우에만 응답을 보낼 수 있습니다. 해당 이벤트에 대한 다른 모든 응답은 무시됩니다.
참고: sendResponse 콜백은 동기식으로 사용되거나 이벤트 핸들러가 true를 반환하여 비동기식으로 응답함을 나타내는 경우에만 유효합니다. true를 반환하는 핸들러가 없거나 sendResponse 콜백이 가비지 컬렉션되면 sendMessage 함수의 콜백이 자동으로 호출됩니다.

장기 연결

때로는 단일 요청과 응답보다 오래 지속되는 대화가 있으면 유용할 수 있습니다. 이 경우 콘텐츠 스크립트에서 확장 프로그램 페이지로 장기 채널을 열거나 반대로 runtime.connect 또는 tabs.connect를 각각 사용합니다 . 채널은 선택적으로 이름을 가지므로 서로 다른 유형의 연결을 구분할 수 있습니다.

한 가지 사용 사례로는 자동 양식 작성 확장 프로그램이 있습니다. 콘텐츠 스크립트는 채널을 열어 특정 로그인에 대한 확장 프로그램 페이지로 이동하고 각 입력에 대해 확장 프로그램에 메시지를 전송합니다. 요소를 사용하여 양식 데이터를 채울 것을 요청합니다. 공유 연결을 사용하면 확장 프로그램에서 공유 상태를 유지할 수 있습니다.

연결을 설정할 때 양 끝은 다음을 위해 사용되는 runtime.Port 객체를 제공합니다. 해당 연결을 통해 메시지를 보내고 받을 수 있습니다.

콘텐츠 스크립트에서 채널을 열고 메시지를 주고받는 방법은 다음과 같습니다.

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

확장 프로그램에서 콘텐츠 스크립트로 요청을 보내는 것은 연결할 탭을 지정합니다. 위의 예에서 연결하기 위한 호출을 tabs.connect.

수신되는 연결을 처리하려면 runtime.onConnect 이벤트를 설정해야 합니다. 리스너입니다. 콘텐츠 스크립트 또는 확장 프로그램 페이지와 동일하게 보입니다. 광고의 다른 부분이 확장 프로그램이 'connect()'를 호출하면 이 이벤트가 시작되며, 연결 해제할 수 있는 runtime.Port 개체와 함께 연결을 통해 메시지를 주고받는 데 사용합니다. 에 응답하는 방법은 다음과 같습니다. 수신 연결:

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

포트 수명

포트는 확장 프로그램의 서로 다른 부분 간의 양방향 통신 방법으로 설계되었으며, 가장 작은 부분으로 표시됩니다. 포트tabs.connect, runtime.connect 또는 runtime.connectNative를 호출할 때 생성됩니다. 이 포트는 postMessage를 반환합니다.

탭에 여러 프레임이 있는 경우 tabs.connect를 호출하면 runtime.onConnect 이벤트 (탭의 각 프레임에 한 번)를 전송합니다. 마찬가지로 runtime.connect가 사용된 경우 onConnect 이벤트가 여러 번 발생할 수 있습니다 (매회 프레임이 포함될 수 있습니다.

예를 들어 별도의 연결을 유지하는 경우와 같이 연결이 닫히는 시점을 알고 싶을 수 있습니다. 상태를 설명합니다. 이를 위해 runtime.Port.onDisconnect 이벤트를 수신 대기할 수 있습니다. 이 이벤트는 채널의 다른 쪽에 유효한 포트가 없으면 시작됩니다. 이는 다음과 같은 상황이 발생할 수 있습니다.

  • 다른 쪽 끝에는 runtime.onConnect의 리스너가 없습니다.
  • 포트가 포함된 탭의 로드가 해제됩니다 (예: 탭이 탐색되는 경우).
  • connect가 호출된 프레임이 언로드되었습니다.
  • (runtime.onConnect를 통해) 포트를 수신한 모든 프레임이 언로드되었습니다.
  • runtime.Port.disconnect반대쪽에 의해 호출됩니다. connect 호출이 여러 포트에 연결할 수 있으며 이러한 포트 중 하나에서 disconnect()가 호출되면 onDisconnect 이벤트는 발신자의 포트에서만 시작되고 다른 포트에서는 발생하지 않습니다.

교차 확장 메시지

확장 프로그램의 여러 구성요소 간에 메시지를 전송하는 것 외에도 Messaging API를 사용하여 다른 확장 프로그램과 통신할 수 있습니다. 이를 통해 다른 API에 제공되는 공개 API를 살펴보겠습니다.

들어오는 요청 및 연결을 수신 대기하는 것은 내부 사례와 비슷합니다. 단, runtime.onMessageExternal 또는 runtime.onConnectExternal 메서드를 사용할 수 있습니다. 이것은 각각:

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

마찬가지로 다른 확장 프로그램으로 메시지를 보내는 것은 확장 프로그램 내에서 메시지를 보내는 것과 유사합니다. 유일한 차이점은 통신하려는 확장 프로그램의 ID를 전달해야 한다는 것입니다. 예를 들면 다음과 같습니다.

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

웹페이지에서 메일 보내기

교차 확장 메시지와 마찬가지로 앱 또는 확장 프로그램이 보내는 경우가 많습니다. 이 기능을 사용하려면 먼저 manifest.json에 지정해야 합니다. 선택할 수 있습니다. 예를 들면 다음과 같습니다.

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

이렇게 하면 지정한 URL 패턴과 일치하는 모든 페이지에 Messaging API가 노출됩니다. URL 패턴에는 두 번째 수준 도메인, 즉 '*'와 같은 호스트 이름 패턴이 포함되어야 합니다. '*.com', '*.co.uk', '*.appspot.com' 금지됩니다. 웹페이지에서 runtime.sendMessage 또는 runtime.connect API를 사용하여 특정 앱에 메시지를 보낼 수 있습니다. 확장자가 포함됩니다. 예를 들면 다음과 같습니다.

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

앱 또는 확장 프로그램에서 다음을 통해 웹페이지의 메시지를 들을 수 있습니다. 교차 확장 프로그램과 비슷한 runtime.onMessageExternal 또는 runtime.onConnectExternal API 메시지를 사용하세요. 웹페이지만 연결을 시작할 수 있습니다. 예를 들면 다음과 같습니다.

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

기본 메시지

확장 프로그램 및 앱은 네이티브 메시징 호스트의 사용 사례를 보여줍니다 이 기능에 관한 자세한 내용은 기본 메시지를 참고하세요.

보안 고려사항

콘텐츠 스크립트를 신뢰할 수 없음

콘텐츠 스크립트는 확장 프로그램 백그라운드 페이지 (예: 악성 웹 페이지가 콘텐츠 스크립트가 실행되는 렌더러 프로세스를 손상시킬 수 있습니다). 가정 콘텐츠 스크립트의 메시지는 공격자가 작성한 것일 수 있으므로 확인하고 모든 입력을 정리합니다. 콘텐츠 스크립트로 전송된 모든 데이터가 웹페이지로 유출될 수 있다고 가정합니다. 콘텐츠에서 수신된 메시지에 의해 트리거될 수 있는 권한 있는 작업의 범위 제한 사용할 수 있습니다

교차 사이트 스크립팅

콘텐츠 스크립트 또는 다른 확장 프로그램에서 메시지를 받을 때 스크립트에 주의가 필요합니다. 교차 사이트 스크립팅의 피해를 입지 않아야 합니다. 이 권장사항은 다른 웹 출처 내에서 실행되는 콘텐츠 스크립트에도 적용됩니다. 특히 다음과 같이 위험한 API는 사용하지 마세요.

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

대신 스크립트를 실행하지 않는 더 안전한 API를 사용하세요.

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

examples/api/messaging에서 메시지를 통한 커뮤니케이션의 간단한 예시를 확인할 수 있습니다. 디렉터리 기본 메시지 샘플은 Chrome 앱이 네이티브 앱을 만듭니다. 더 많은 예와 소스 코드를 보는 데 도움이 필요하면 샘플을 참조하세요.