콘텐츠 스크립트

콘텐츠 스크립트는 웹페이지의 컨텍스트에서 실행되는 파일입니다. 표준 Document 사용 객체 모델 (DOM)을 사용하면 브라우저에서 방문하는 웹페이지의 세부정보를 읽고 상위 확장 프로그램에 정보를 전달할 수 있습니다.

콘텐츠 스크립트 기능 이해

콘텐츠 스크립트는 다음 확장 프로그램 API에 직접 액세스할 수 있습니다.

콘텐츠 스크립트는 다른 API에 직접 액세스할 수 없습니다. 하지만 확장 프로그램의 다른 부분과 메시지를 교환하여 간접적으로 액세스할 수 있습니다.

또한 API(예: fetch()) 이렇게 하려면 웹에서 액세스할 수 있는 리소스와 동일합니다. 이렇게 하면 동일한 사이트에서 실행되는 퍼스트 파티 또는 서드 파티 스크립트를 생성합니다.

고립된 세상에서 일하기

콘텐츠 스크립트는 격리된 환경에 있기 때문에 콘텐츠 스크립트가 페이지 또는 다른 확장 프로그램과 충돌하지 않는 자바스크립트 환경 있습니다.

확장 프로그램은 다음 예와 유사한 코드로 웹페이지에서 실행될 수 있습니다.

webPage.html

<html>
  <button id="mybutton">click me</button>
  <script>
    var greeting = "hello, ";
    var button = document.getElementById("mybutton");
    button.person_name = "Bob";
    button.addEventListener(
        "click", () => alert(greeting + button.person_name + "."), false);
  </script>
</html>

이 확장 프로그램은 스크립트 삽입 섹션의 안내를 따르세요.

content-script.js

var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
    "click", () => alert(greeting + button.person_name + "."), false);

이 변경사항에 따라 두 알림은 버튼을 클릭할 때 순차적으로 표시됩니다.

스크립트 삽입

콘텐츠 스크립트는 정적으로 선언하거나 선언할 수 있습니다. 동적으로 또는 프로그래매틱 방식으로 삽입합니다.

정적 선언으로 삽입

자동으로 실행되어야 하는 스크립트의 경우 manifest.json에서 정적 콘텐츠 스크립트 선언을 사용하세요. 잘 알려진 페이지 집합에서 실행되도록 할 수 있습니다

정적으로 선언된 스크립트는 매니페스트에서 "content_scripts" 키 아래에 등록됩니다. 자바스크립트 파일, CSS 파일 또는 둘 다를 포함할 수 있습니다. 모든 자동 실행 콘텐츠 스크립트에서는 일치 패턴 비교

manifest.json

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["https://*.nytimes.com/*"],
     "css": ["my-styles.css"],
     "js": ["content-script.js"]
   }
 ],
 ...
}

이름 유형 설명
matches 문자열 배열 필수사항. 이 콘텐츠 스크립트를 삽입할 페이지를 지정합니다. 이러한 문자열의 구문에 관한 자세한 내용은 일치 패턴을 참고하세요. 및 패턴과 glob 일치를 참조하세요. URL을 클릭합니다.
css 문자열 배열 선택사항. 일치하는 페이지에 삽입할 CSS 파일의 목록입니다. 이는 DOM이 구성되거나 표시되기 전에 이 배열에 나타나는 순서대로 삽입됩니다. 입니다.
js 문자열 배열 선택사항. 일치하는 페이지에 삽입할 JavaScript 파일의 목록입니다. 파일 이 배열에 나타나는 순서대로 삽입됩니다. 이 목록의 각 문자열에는 확장 프로그램의 루트 디렉터리에 있는 리소스의 상대 경로입니다. 선행 슬래시 (`/`)는 자동으로 잘립니다.
run_at RunAt 선택사항. 스크립트를 페이지에 삽입해야 하는 시기를 지정합니다. 기본값은 document_idle입니다.
match_about_blank 부울 선택사항. 스크립트를 about:blank 프레임에 삽입해야 하는지 여부 여기서 상위 또는 오프너 프레임은 matches입니다. 기본값은 false입니다.
match_origin_as_fallback 부울 선택사항. 스크립트가 이전 프레임의 일치하는 출처에서 생성되었지만, 그 URL 또는 출처가 패턴을 찾습니다. 여기에는 about:, data:, blob:filesystem:입니다. 참고 항목 관련 프레임에 삽입
world ExecutionWorld 선택사항. 스크립트를 실행할 JavaScript 환경입니다. 기본값은 ISOLATED입니다. 참고 항목 고립된 세상에서 작업하기.

동적 선언으로 삽입

동적 콘텐츠 스크립트는 콘텐츠 스크립트의 일치 패턴이 잘 알려져 있지 않거나 콘텐츠 스크립트가 알려진 호스트에 항상 삽입되어서는 안 되는 경우 등입니다.

Chrome 96에서 도입된 동적 선언은 static 선언되지만 콘텐츠 스크립트 개체는 메서드가 아닌 chrome.scripting 네임스페이스에 있는 메서드 manifest.json. 또한 Scripting API를 사용하면 확장 프로그램 개발자가 다음과 같이 변경합니다.

정적 선언과 마찬가지로 동적 선언에는 JavaScript 파일, CSS 파일 또는 둘 다를 포함할 수 있습니다.

service-worker.js

chrome.scripting
  .registerContentScripts([{
    id: "session-script",
    js: ["content.js"],
    persistAcrossSessions: false,
    matches: ["*://example.com/*"],
    runAt: "document_start",
  }])
  .then(() => console.log("registration complete"))
  .catch((err) => console.warn("unexpected error", err))

service-worker.js

chrome.scripting
  .updateContentScripts([{
    id: "session-script",
    excludeMatches: ["*://admin.example.com/*"],
  }])
  .then(() => console.log("registration updated"));

service-worker.js

chrome.scripting
  .getRegisteredContentScripts()
  .then(scripts => console.log("registered content scripts", scripts));

service-worker.js

chrome.scripting
  .unregisterContentScripts({ ids: ["session-script"] })
  .then(() => console.log("un-registration complete"));

프로그래매틱 방식으로 삽입

이벤트 또는 특정 시점에 대응하여 실행해야 하는 콘텐츠 스크립트에 프로그래매틱 삽입을 사용합니다. 있습니다.

콘텐츠 스크립트를 프로그래매틱 방식으로 삽입하려면 확장 프로그램에 다음 항목에 대한 호스트 권한이 필요합니다. 페이지를 방문해야 합니다 호스트 권한은 확장 프로그램 매니페스트의 일부로 또는 "activeTab"를 일시적으로 사용하여 요청하세요.

다음은 ActiveTab 기반 확장 프로그램의 다른 버전입니다.

manifest.json:

{
  "name": "My extension",
  ...
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_title": "Action Button"
  }
}

콘텐츠 스크립트는 파일로 삽입될 수 있습니다.

content-script.js


document.body.style.backgroundColor = "orange";

service-worker.js:

chrome.action.onClicked.addListener((tab) => {
  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    files: ["content-script.js"]
  });
});

함수 본문을 콘텐츠 스크립트로 삽입하여 실행할 수도 있습니다.

service-worker.js:

function injectedFunction() {
  document.body.style.backgroundColor = "orange";
}

chrome.action.onClicked.addListener((tab) => {
  chrome.scripting.executeScript({
    target : {tabId : tab.id},
    func : injectedFunction,
  });
});

삽입된 함수는 chrome.scripting.executeScript() 호출을 사용합니다. 결과적으로 함수의 본문이 독립적이어야 합니다. 함수 외부의 변수를 참조하면 콘텐츠가 스크립트를 사용하여 ReferenceError을 발생시킵니다.

함수로 삽입할 때 함수에 인수를 전달할 수도 있습니다.

service-worker.js

function injectedFunction(color) {
  document.body.style.backgroundColor = color;
}

chrome.action.onClicked.addListener((tab) => {
  chrome.scripting.executeScript({
    target : {tabId : tab.id},
    func : injectedFunction,
    args : [ "orange" ],
  });
});

일치 및 glob 제외

지정된 페이지 일치를 맞춤설정하려면 다음 필드를 선언적에 포함합니다. 있습니다.

이름 유형 설명
exclude_matches 문자열 배열 선택사항. 이 콘텐츠 스크립트가 삽입될 수 있는 페이지를 제외합니다. 있습니다. 다음 구문의 구문에 관한 자세한 내용은 일치 패턴을 참고하세요. 지정할 수 있습니다.
include_globs 문자열 배열 선택사항. matches 이후에 적용되어 이 glob과 일치합니다. 이는 @include를 에뮬레이션하기 위한 것입니다. Greasemonkey 키워드입니다.
exclude_globs 문자열 배열 선택사항. 일치하는 URL을 제외하기 위해 matches 이후에 적용됨 glob @exclude를 에뮬레이션하기 위한 용도입니다. Greasemonkey 키워드입니다.

다음 두 조건을 모두 충족하는 경우 콘텐츠 스크립트가 페이지에 삽입됩니다.

  • URL이 모든 matches 패턴 및 include_globs 패턴과 일치합니다.
  • URL이 exclude_matches 또는 exclude_globs 패턴과도 일치하지 않습니다. matches 속성이 필요하므로 exclude_matches, include_globs, exclude_globs 는 영향을 받는 페이지를 제한하는 데만 사용할 수 있습니다.

다음 확장 프로그램은 콘텐츠 스크립트를 https://www.nytimes.com/health에 삽입합니다. https://www.nytimes.com/business로는 가져올 수 없습니다 .

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

service-worker.js

chrome.scripting.registerContentScripts([{
  id : "test",
  matches : [ "https://*.nytimes.com/*" ],
  excludeMatches : [ "*://*/*business*" ],
  js : [ "contentScript.js" ],
}]);

Glob 속성은 일치 패턴과는 다른 더 유연한 구문을 따릅니다. 허용되는 glob 문자열은 '와일드 카드'를 포함할 수 있는 URL임 별표 및 물음표 별표 (*) 는 빈 문자열을 포함하여 모든 길이의 모든 문자열과 일치하지만 물음표 (?)는 다음과 일치합니다. 지정할 수 있습니다.

예를 들어 glob https://???.example.com/foo/\*는 다음 중 하나와 일치합니다.

  • https://www.example.com/foo/bar
  • https://the.example.com/foo/

하지만 다음은 일치하지 않습니다.

  • https://my.example.com/foo/bar
  • https://example.com/foo/
  • https://www.example.com/foo

이 확장 프로그램은 콘텐츠 스크립트를 https://www.nytimes.com/arts/index.htmlhttps://www.nytimes.com/jobs/index.htm*, 내부로는 제외 https://www.nytimes.com/sports/index.html:

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

이 확장 프로그램은 콘텐츠 스크립트를 https://history.nytimes.comhttps://.nytimes.com/history이지만 https://science.nytimes.com 또는 https://www.nytimes.com/science:

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

이 중 하나, 전부 또는 일부를 포함하여 올바른 범위를 달성할 수 있습니다.

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

실행 시간

run_at 필드는 JavaScript 파일이 웹페이지에 삽입되는 시기를 제어합니다. 선호 및 기본값은 "document_idle"입니다. 가능한 다른 내용은 RunAt 유형을 참고하세요. 값으로 사용됩니다.

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}

service-worker.js

chrome.scripting.registerContentScripts([{
  id : "test",
  matches : [ "https://*.nytimes.com/*" ],
  runAt : "document_idle",
  js : [ "contentScript.js" ],
}]);
이름 유형 설명
document_idle 문자열 우선 적용. 가능하면 "document_idle"을(를) 사용하세요.

브라우저가 "document_end"과 그 직후에 스크립트를 삽입할 시간을 선택 window.onload 있습니다. 삽입이 이루어지는 정확한 시점은 문서가 얼마나 복잡한지와 로드 시간이 오래 걸리며 페이지 로드 속도에 최적화되어 있습니다.

콘텐츠 스크립트 "document_idle"에서 실행 중인 경우 window.onload 이벤트를 사용하면 DOM이 완료된 후 실행이 보장됩니다. 만약 스크립트를 반드시 window.onload 후에 실행해야 하는 경우 확장 프로그램은 onload은(는) document.readyState를 사용하여 이미 실행되었습니다. 속성
document_start 문자열 스크립트가 css의 파일 다음에 삽입되지만 다른 DOM이 삽입되기 전에 삽입됩니다. 다른 스크립트가 실행됩니다.
document_end 문자열 스크립트는 DOM이 완료된 직후, 즉 확인할 수 있습니다

프레임 지정

"all_frames" 필드를 사용하면 확장 프로그램에서 JavaScript 및 CSS 파일이 있어야 하는지 여부를 지정할 수 있습니다. 지정된 URL 요구사항과 일치하는 모든 프레임에 삽입되거나 탭

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}

service-worker.js

chrome.scripting.registerContentScripts([{
  id: "test",
  matches : [ "https://*.nytimes.com/*" ],
  allFrames : true,
  js : [ "contentScript.js" ],
}]);
이름 유형 설명
all_frames 부울 선택사항. 기본값은 false입니다. 즉, 상단 프레임만

true가 지정되면 frame이 탭의 최상위 프레임이 아닙니다. 각 프레임에 URL이 별도로 확인됨 요구사항을 충족해야 합니다 URL 요구사항이 충족되지 않으면 하위 프레임에 삽입되지 않습니다.

확장 프로그램은 일치하는 것과 관련된 프레임에서 스크립트를 실행하려고 할 수 있음 일치하지 않지만 그 자체는 일치하지 않습니다. 이 경우 일반적인 시나리오는 는 일치하는 프레임에 의해 생성되었지만 다음과 같은 URL이 없는 프레임의 경우 스크립트의 지정된 패턴과 일치합니다.

이는 확장 프로그램이 about:, data:, blob:, filesystem: 스키마가 있습니다. 이 경우 URL은 콘텐츠 스크립트의 패턴과 일치하지 않습니다. about:data:, URL에 상위 URL이나 출처를 포함하지 않음 (예: about:blank 또는 data:text/html,<html>Hello, World!</html>)). 그러나 이러한 프레임은 여전히 프레임 생성과 연결될 수 있습니다.

이러한 프레임에 삽입하기 위해 확장 프로그램은 "match_origin_as_fallback" 속성을 사용하여 합니다.

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.google.com/*"],
      "match_origin_as_fallback": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}

이 플래그를 지정하고 true로 설정하면 Chrome에서 이때 프레임의 시작자를 호출하여 프레임이 일치하는지 확인합니다. 프레임 자체의 URL입니다. 이는 앞서 말씀드린 타겟 프레임의 origin (예: data: URL의 출처가 null임).

프레임의 시작자는 타겟을 만들거나 탐색한 프레임입니다. 있습니다. 일반적으로 직접적인 부모 또는 오프너이지만, iframe 내에서 iframe을 탐색하는 프레임의 경우).

이는 시작자 프레임의 origin을 비교하므로 시작자 프레임은 모든 경로에 있을 수 있습니다 이 의미를 명확히 하기 위해 Chrome은 "match_origin_as_fallback"로 지정된 콘텐츠 스크립트가 필요합니다. true로 설정하여 *의 경로도 지정합니다.

"match_origin_as_fallback""match_about_blank"가 모두 지정된 경우 "match_origin_as_fallback"이(가) 우선합니다.

삽입 페이지와의 통신

콘텐츠 스크립트의 실행 환경과 이를 호스팅하는 페이지는 격리되어 있지만 페이지의 DOM에 대한 액세스를 공유합니다. 페이지가 콘텐츠 스크립트를 통해 확장 프로그램을 사용할 경우 공유 DOM을 통해 해야 합니다.

window.postMessage()를 사용하여 예시를 실행할 수 있습니다.

content-script.js

var port = chrome.runtime.connect();

window.addEventListener("message", (event) => {
  // We only accept messages from ourselves
  if (event.source !== window) {
    return;
  }

  if (event.data.type && (event.data.type === "FROM_PAGE")) {
    console.log("Content script received: " + event.data.text);
    port.postMessage(event.data.text);
  }
}, false);

example.js

document.getElementById("theButton").addEventListener("click", () => {
  window.postMessage(
      {type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);

비확장 페이지(example.html)는 자체에 메시지를 게시합니다. 이 메시지는 가로채기되어 검사한 다음 확장 프로그램 프로세스에 게시됩니다. 이렇게 하면 확장 프로세스와 커뮤니케이션 라인을 구축합니다. 그 반대로는 사용할 수 있습니다.

확장 파일 액세스

콘텐츠 스크립트에서 확장 파일에 액세스하려면 chrome.runtime.getURL(): 다음 예 (content.js)에 표시된 대로 확장 애셋의 절대 URL을 가져옵니다.

content-script.js

let image = chrome.runtime.getURL("images/my_image.png")

CSS 파일에서 글꼴이나 이미지를 사용하려면 다음 예 (content.css)와 같이 @@extension_id를 사용하여 URL을 구성하면 됩니다.

content.css

body {
 background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}

@font-face {
 font-family: 'Stint Ultra Expanded';
 font-style: normal;
 font-weight: 400;
 src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}

모든 애셋은 manifest.json 파일에서 웹 액세스 가능한 리소스로 선언해야 합니다.

manifest.json

{
 ...
 "web_accessible_resources": [
   {
     "resources": [ "images/*.png" ],
     "matches": [ "https://example.com/*" ]
   },
   {
     "resources": [ "fonts/*.woff" ],
     "matches": [ "https://example.com/*" ]
   }
 ],
 ...
}

보안 유지

격리된 환경이 보호 계층을 제공하는 반면, 콘텐츠 스크립트를 사용하면 확장 프로그램 및 웹페이지의 취약점을 파악할 수 있습니다 콘텐츠 스크립트가 별도의 웹사이트(예: fetch() 호출)를 사용하는 경우 교차 사이트 스크립팅 공격을 사전에 차단 HTTPS를 통해서만 통신하면 &quot;man-in-the-middle&quot; 공격을 피해야 합니다.

반드시 악성 웹페이지를 필터링하세요. 예를 들어, 다음 패턴은 위험하며 다음과 같습니다.

금지사항

content-script.js

const data = document.getElementById("json-data");
// WARNING! Might be evaluating an evil script!
const parsed = eval("(" + data + ")");
금지사항

content-script.js

const elmt_id = ...
// WARNING! elmt_id might be '); ... evil script ... //'!
window.setTimeout("animate(" + elmt_id + ")", 200);

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

권장사항

content-script.js

const data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
const parsed = JSON.parse(data);
권장사항

content-script.js

const elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(() => animate(elmt_id), 200);