컨텐트 스크립트

컨텐트 스크립트는 웹페이지의 context에서 실행되는 자바스크립트 파일입니다. 표준 문서 객체 모델(DOM)을 사용해, 브라우저가 방문한 웹페이지를 읽고 수정할 수 있습니다.

다음은 컨텐트 스크립트가 어떠한 일을 하는지 보여줍니다.

  • 웹페이지 안에 링크되지 않은 URL을 찾아 링크를 걸어줍니다.
  • 텍스트를 읽기 쉽도록 폰트 크기를 키웁니다.
  • DOM에서 마이크로포맷(microformat) 데이터를 찾아 처리합니다.

컨텐트 스크립트가 하지 못하는 일도 있습니다:

  • chrome.* APIs 중 사용이 불가능한 API:
    • extension – (getURL. inIncognitoContext, lastError, onRequest, sendReqeust)
    • i18n
    • runtime – (connect, getManifest, getURL, id, onConnect, onMessage, sendMessage)
    • storage
  • 확장 프로그램 페이지에서 정의된 변수나 함수를 사용 못함
  • 웹페이지나 다른 컨텐트 스크립트에서 정의된 변수나 함수를 사용 못함

이러한 제한적인 부분이 있다고 해서 나쁜 것만은 아닙니다. 컨텐트 스크립트는 간접적으로 chrome.* APIs 를 사용할 수 있고 확장 프로그램의 데이터에 접근을 하거나, 부모 확장 프로그램과의 exchanging messages을 통해 어떠한 동작을 요청할 수 있습니다.

또한 부모 확장 프로그램과 같이 동일한 사이트에 cross-site XMLHttpRequests를 만들 수 있습니다. 그리고 공유하고 있는 DOM을 사용해 웹페이지간 통신(communicate with web pages)을 할 수 있습니다.

컨텐트 스크립트에 대한 더 자세한 내용은 execution environment를 참조하세요.

Manifest

컨텐트 스크립트의 코드가 항상 주입(injected)되어야 한다면 아래 예제와 같이 manifestcontent_scripts 필드에 등록하세요.

때에 따라 코드를 주입(inject) 한다면 ‘프로그래밍을 통한 주입‘에 서술된 것과 같이 permission 필드를 사용하세요.

content_scripts 필드를 사용하면, 확장 프로그램은 페이지 안에 여러개의 컨텐트 스크립트를 삽입할 수 있습니다; 각 컨텐트 스크립트는 여러개의 자바스크립트와 CSS 파일을 가질 수 있습니다. content_scripts 배열에 있는 각 아이템은 다음과 같은 프로퍼티를 가질 수 있습니다.

프로퍼티명타입설명
matches스트링 배열필수. 어떤 페이지에 컨텐트 스크립트를 주입할 지 지정합니다. Match Patterns 을 보면 이 문자열의 문법이 자세히 나와 있습니다. 그리고 매치패턴과 globs 에는 URL을 어떻게 제외하는지에 대한 정보가 있습니다.
exclude_matches스트링 배열옵션. 컨텐트 스크립트가 주입될 예외 페이지입니다. Match Patterns 을 보면 이 문자열의 문법이 자세히 나와 있습니다. 그리고 매치패턴과 globs 에는 URL을 어떻게 제외하는지에 대한 정보가 있습니다.
match_about_blankboolean옵션. about:blank 그리고 about:srcdoc 에 컨텐트 스크립트를 삽입할 지 여부. 컨텐트 스크립트는 상속 URL이 matches 필드에 정의된 패턴 중에 하나와 일치할 때에만 페이지에 주입된다. 상속 URL은 프레임이나 윈도우를 생성한 문서의 URL입니다. 컨텐트 스크립트는 샌드박스 프레임에는 삽입될 수 없습니다.

기본값은 false 입니다.
css스트링 배열옵션. 매칭되는 페이지 안으로 css 파일 목록이 주입됩니다. 주입되는 순서는 배열에 보이는 순입니다. 이 작업은 DOM이 페이지에 만들어지거나 보여지기 전에 이루어집니다.
js스트링 배열옵션. 매칭된 페이지에 자바스크립트 파일 목록이 주입됩니다. 주입은 배열에 정렬된 순입니다.
run_atstring옵션. js 안에 있는 파일이 주입되는 경우 제어를 합니다. "document_start", "document_end", "document_idle" 중에 하나가 올 수 있습니다. 기본값은 "document_idle" 입니다.

"document_start" 는 css 파일이 주입된 후, 그러나 DOM이 만들어지거나 다른 스크립트가 실행되기 전에 파일이 삽입됩니다.

"document_end"는 DOM이 완성된 후 즉시, 그러나 하위의 자원(이미지, 프레임 등)이 로드되기 전에 파일이 삽입됩니다.

"document_idle"은 "document_end"와 window.onload 이벤트가 발생한 직후, 그 사이에서 브라우저가 스크립트를 주입할 시간을 선택합니다. 파일이 삽입되는 정확한 시점은 문서가 얼마나 복잡한지, 그 문서를 불러오는데 얼마나 걸리는지에 따라 다른데, 페이지 로딩 속도를 고려하여 최적화 됩니다.

참고: "document_idle"에서 컨텐트 스크립트가 window.onload 이벤트를 반드시 받는 것은 아닙니다. 왜냐하면 이벤트가 발생한 이후에 실행되기 때문입니다. 대부분의 경우, "document_idle"에서는 컨텐트 스크립트를 실행하기 위해 onload 이벤트를 리스닝할 필요가 없습니다. DOM이 완성된 후 실행되도록 해당 옵션이 보장하고 있습니다. 만약 스크립트를 window.onload 이후에 실행해야 하는 확실한 필요성이 있다면 document.readyState 프로퍼티를 이용해 onload가 이미 발생했는지 확인해 볼 수 있습니다.
all_framesboolean옵션. 컨텐트 스크립트를 매칭된 모든 프레임에서 실행할 지, 오직 최상위 프레임만 실행할 지 여부를 제어합니다.

기본값은 false이고 오직 최상위 프레임이 매치되었을 때를 뜻합니다.
include_globs스트링 배열옵션. glob과 매칭되는 URL만을 포함시켜 매칭 후 적용합니다. @include 키워드를 에뮬레이트 하도록 되어있습니다. 아래의 매치패턴과 globs에서 더 자세한 내용을 확인하세요.
exclude_globs스트링 배열옵션. glob과 매칭되는 URL을 제외시켜 매칭 후 적용합니다. @exclude 키워드를 에뮬레이트 하도록 되어있습니다. 아래의 매치패턴과 globs에서 더 자세한 내용을 확인하세요.

매치 패턴과 globs

만약 페이지의 URL이 ‘matches‘ 패턴과 ‘include_globs‘ 패턴에 어느 하나라도 일치하면서, 또한 ‘exclude_matches‘ 또는 ‘exclude_globs‘ 패턴과 일치하지 않는다면 컨텐트 스크립트가 그 페이지 내에 주입될 것입니다,

matches‘ 프로퍼티는 필수값입니다, ‘exclude_matches‘, ‘include_globs‘, 그리고 ‘exclude_globs‘는 오직 영향을 받게 되는 페이지를 제한하기 위해 사용됩니다.

예를 들어, matches에 다음 값이 있다고 가정하면

[“http://*.nytimes.com/*”]:

  • 만약 exclude_matches가 [“*://*/*business*”] 이면, 컨텐트 스크립트는 “http://www.nytimes.com/business”는 제외하고 “http://www.nytimes.com/health”에 주입될 것입니다.
  • 만약 include_globs가 [“*nytimes.com/???s/*”] 이면, 컨텐트 스크립트는 “http://www.nytimes.com/sports/index.html”는 제외하고 “http:/www.nytimes.com/arts/index.html”와 “http://www.nytimes.com/jobs/index.html”에 주입될 것입니다.
  • 만약 exclude_globs가 [“*science*”]이면, 컨텐트 스크립트는 “http://science.nytimes.com” 또는 “http://www.nytimes.com/science”를 제외하고 “http://www.nytimes.com”에 주입될 것입니다.

Glob 프로퍼티는 match patterns와는 다르게 보다 유연한 문법을 따릅니다. glob 문자열은 (와일드카드를 뜻하는)별표와 물음표가 포함된 URL을 사용할 수 있습니다. 별표(*)는 어떠한 문자열이나 길이와도 매칭됩니다. (비어있는 문자열 포함) 물음표 (?)는 하나의 단일 문자와 매칭됩니다.

예를 들어 glob “http://???.example.com/foo/*”은 다음 중 어떠한 것과도 매칭이 됩니다:

  • “http://www.example.com/foo/bar”
  • “http://the.example.com/foo/”

하지만 다음 예시 중에는 매칭되는 것이 없습니다:

  • “http://my.example.com/foo/bar”
  • “http://example.com/foo/”
  • “http://www.example.com/foo”

프로그래밍을 통한 주입

프로그래밍을 통해 페이지에 코드를 삽입하는 것은 모든 싱글 페이지에 (자바스크립트나 CSS 코드가)주입되지 않도록 할 때 유용합니다. 예를 들어 사용자가 브라우저 액션에 있는 아이콘을 클릭했을 때에만 스크립트가 실행되길 원한다면 이 방법을 써야 합니다.

페이지에 코드를 삽입하기 위해서는 확장 프로그램이 해당 페이지에 대한 cross-origin permissions를 반드시 가져야 합니다. 또한 chrome.tabs 모듈을 사용할 수 있어야 합니다. 매니페스트 파일의 permissions 필드에서 두 종류의 퍼미션을 모두 얻을 수 있습니다.

퍼미션 설정을 하고 나면 tabs.executeScript를 호출해 자바스크립트를 페이지에 주입할 수 있습니다. CSS를 주입시키려면 tabs.insertCSS를 사용합니다.

다음 코드는(예제 make_page_red 에서 가져옴) 현재 탭에 있는 페이지에 자바스크립트를 삽입한 후 실행함으로써 사용자의 클릭에 반응합니다.

브라우저가 HTTP 페이지를 보여주고 있고 사용자가 브라우저 액션을 클릭했을 때, 확장 프로그램은 페이지의 배경색을 ‘red’로 설정합니다. 그 결과로 배경색을 설정하는 CSS 없이 페이지가 빨간색으로 바뀝니다.

보통은 직접 코드를 삽입하는 대신 파일에 코드를 넣습니다. 파일의 내용을 다음과 같이 주입시킵니다:

실행 환경

컨텐트 스크립트는 isolated world라고 부르는 특별한 환경에서 실행합니다. 컨텐트 스크립트는 자신들이 주입된 페이지의 DOM에 접근할 수 있지만, 페이지에서 생성된 자바스크립트 변수나 함수에 접근할 수 있는것은 아닙니다.

이것은 각 컨텐트 스크립트가 실행되는 동안 마치 페이지에서 동작하는 다른 자바스크립트는 없는것 처럼 보입니다.

그 반대도 마찬가지입니다: 페이지에서 실행되고 있는 자바스크립트는 컨텐트 스크립트에서 정의된 어떠한 함수도 호출할 수 없거나 어떠한 변수도 접근할 수 없습니다.

아래 hello.html 이라는 간단한 페이지가 있습니다:

hello.html에 다음 컨텐트 스크립트가 주입되었다고 가정하겠습니다:

버튼이 눌려지면, 여러분은 두 alert을 모두 보게될 것입니다.

‘Isolated worlds’는 페이지나 다른 컨텐트 스크립트들이 충돌할 걱정없이 각각의 컨텐트 스크립트가 해당 자바스크립트 환경에서 변경이 가능하도록 만들어 줍니다. 예를 들면 JQuery v1을 포함하는 컨텐트 스크립트와 JQuery v2를 포함하는 페이지가 있더라도 서로 충돌하지 않습니다.

또 다른 중요한 이점이 있는데 페이지에 있는 자바스크립트를 확장 프로그램에 있는 자바스크립트로부터 완전히 분리한다는 것입니다. 이것은 웹페이지에 접근할 수 없는 컨텐트 스크립트에게, 그것에 접근하는 웹페이지에 대한 걱정없이 우리가 별도의 기능을 제공하도록 허용합니다.

페이지와 확장프로그램에 의해 공유된 자바스크립트 객체에 어떠한 일이 생기는지 주목할 만한 가치가 있습니다 – window.onload 이벤트를 예로 들면, 각 isolated world는 자신이 소유한 객체의 버전을 바라봅니다. 객체에 할당을 하게되면 객체의 독립된 복사본에 영향을 미칩니다. 그 예로 페이지와 확장 프로그램 모두에서 window.onload를 할당할 수 있지만 그 어느쪽도 다른곳의 이벤트 핸들러를 읽을 수는 없습니다. 이벤트 핸들러는 할당된 순서에 따라 호출이 됩니다.

임베디드 페이지와의 통신

컨텐트 스크립트와 페이지의 호스트 실행환경이 서로 분리되어 있음에도 이들은 페이지 DOM에 대한 접근을 공유합니다. 페이지가 컨텐트 스크립트와 통신하길 바란다면 (또는 컨텐트 스크립트를 통해 확장 프로그램과 통신하길 원한다면), 공유되어 있는 DOM을 이용해 원하는 작업을 할 수 있습니다.

window.postMessage (또는 Transferable 객체의 window.webkitPostMessage)를 사용해 위에서 설명한 내용을 구현한 예입니다:

이 예제코드 중 두번째 코드는 확장 프로그램의 일부가 아닌 일반 html 파일입니다. 컨텐트 스크립트(첫번째 코드)에 의해 인터셉트 및 검사된 메시지를 그 자신에게 게시합니다. 그리고 나서 확장 프로그램의 프로세스에게 알려줍니다. 이러한 방법으로 페이지는 확장 프로그램 프로세스에게 커뮤니케이션 라인을 설정합니다. 그 반대도 유사한 방법을 통해 가능합니다.

보안 고려사항

컨텐트 스크립트를 작성할 때는 두 가지 보안 이슈를 주의해야 합니다.

첫번째로 컨텐트 스크립트가 주입된 웹사이트 내의 보안 취약점을 노출시키지 않도록 주의해야 합니다. 예를 들어 컨텐트 스크립트가 다른 웹사이트로부터 컨텐츠를 받는다면(XMLHttpReqeust에서 만들어진), 현재 페이지안에 컨텐츠를 주입하기 전에 cross-site scripting 공격을 하는 컨텐츠에 대해 조심스럽게 필터링 해야 합니다. 예를 들어 innerHTML 보다는 innerText를 통해 컨텐트 주입을 합니다. 특별히 주의할 점은 HTTPS 페이지에서 HTTP 컨텐츠를 가져올 때인데 그 이유는 사용자가 취약한 네트워크에 연결되어 있으면 통신 중간에 끼어들어 공격하는 것(man-in-the-middle attack)에 의해 HTTP 컨텐츠가 오염될 수 있기 때문입니다.

두번째는, 여러분이 웹페이지에서 컨텐츠를 가리지 않고 사용할 경우 악의적인 웹페이지가 여전히 컨텐트 스크립트를 공격할 수 있으므로, 컨텐트 스크립트가 ‘isolated world’에서 실행됨에도 불구하고 몇 가지 보호 기능을 제공하는 것입니다.

다음과 같은 패턴들은 위험합니다:

이 대신, 스크립트를 실행하지 않는 안전한 API를 우선시 합니다.

확장 프로그램 파일 참조

확장 프로그램 파일의 URL을 얻으려면 chrome.extension.getURL()을 사용합니다. 다음 코드에서 보여주는것 처럼, 어떠한 URL이든 원하는 것을 사용할 수 있습니다.

예제

컨텐트 스크립트를 사용한 예제가 많이 있습니다. 메시지 통신을 하는 간단한 예제는 ‘Message Timer‘ 입니다. 프로그래매틱 주입에 대한 예제는 ‘Page Redder‘와 ‘Email This Page‘를 보세요.

비디오

다음 비디오는 컨텐트 스크립트의 중요한 개념에 대해 이야기합니다. 첫번째 비디오는 컨텐트 스크립트와 ‘isolated worlds’에 대한 것입니다.

비디오1

다음 비디오는 메시지 패싱을 설명하는데, 컨텐트 스크립트가 부모 확장 프로그램에게 요청을 보내는 예제가 포함되어 있습니다.

비디오2