# Content Scripts
## Contents
1. [Manifest](content_scripts.html#registration)
1. [Include和exclude語句](content_scripts.html#include-exclude-globs)
2. [編程式注入](content_scripts.html#pi)
1. <a>h3Name</a>
3. [執行環境](content_scripts.html#execution-environment)
1. <a>h3Name</a>
4. [與嵌入的頁面通信](content_scripts.html#host-page-communication)
1. <a>h3Name</a>
5. [安全性](content_scripts.html#security-considerations)
1. <a>h3Name</a>
6. [引用擴展里的文件](content_scripts.html#extension-files)
1. <a>h3Name</a>
7. [例子](content_scripts.html#examples)
1. <a>h3Name</a>
8. [視頻(Youtube)](http://code.google.com/chrome/extensions/content_scripts.html#videos)
1. <a>h3Name</a>
## Content Scripts
Content scripts是在Web頁面內運行的javascript腳本。通過使用標準的DOM,它們可以獲取瀏覽器所訪問頁面的詳細信息,并可以修改這些信息。
下面是content scipt可以做的一些事情范例:
* 從頁面中找到沒有寫成超鏈接形式的url,并將它們轉成超鏈接。
* 放大頁面字體使文字更清晰
* 找到并處理DOM中的[microformat](http://microformats.org/)
當然,content scripts也有一些限制,它們不能做的事情包括 :
* 不能使用除了[chrome.extension](extension.html)之外的chrome.* 的接口
* 不能訪問它所在擴展中定義的函數和變量
* 不能訪問web頁面或其它content script中定義的函數和變量
* 不能做[cross-site XMLHttpRequests](xhr.html)
這些限制其實并不像看上去那么糟糕。Content scripts 可以使用[messages](messaging.html)機制與它所在的擴展通信,來間接使用chrome.*接口,或訪問擴展數據。Content scripts還可以通過共享的DOM來[與web頁面通信](content_scripts.html#host-page-communication)。更多功能參見[執行環境](content_scripts.html#execution-environment)。
## Manifest
如果content scipt的代碼總是需要注入,可以在[extension manifest](manifest.html)中的 content_scipt字段注冊它。如下面的例子:
```
{
"name": "My extension",
...
**"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"]
}
]**,
...
}
```
如果只是在某些情況下需要注入,可以使用[permission](manifest.html#permissions)字段,詳見[Programmatic injection](content_scripts.html#pi)。
```
{
"name": "My extension",
...
**"permissions": [
"tabs", "http://www.google.com/*"
]**,
...
}
```
使用 `content_scripts` 字段,一個擴展可以向一個頁面注入多個content_script腳本;每個content script腳本可以包括多個javascript腳本和css文件。content_script字段中的每一項都可以包括下列屬性:
| Name | Type | Description |
| --- | --- | --- |
| `matches` | array of strings | _必須。_ 定義哪些頁面需要注入content script。查看[Match Patterns](match_patterns.html)以了解詳細語法。 |
| `css` | array of strings | _可選。_需要向匹配頁面中注入的CSS文件。這些文件將在頁面的DOM樹創建和顯示之前,按照定義的順序依次注入。 |
| `js` | array of strings | _可選。_ 需要向頁面中注入的javascript文件,按定義順序注入。 |
| `run_at` | string | _可選。_ 控制content script注入的時機。可以是document_start, document_end或者document_idle。缺省時是document_idle。如果是document_start, 文件將在所有CSS加載完畢,但是沒有創建DOM并且沒有運行任何腳本的時候注入。如果是document_end,則文件將在創建完DOM之后,但還沒有加載類似于圖片或frame等的子資源前立刻注入。如果是document_idle,瀏覽器會在document_end和發出[window.onload](http://www.whatwg.org/specs/web-apps/current-work/#handler-onload)事件之間的某個時機注入。具體的時機取決與文檔加載的復雜度,為加快頁面加載而優化。**注意:**在document_idle的情況下,content script不一定會接收到window.onload事件,因為它有可能在事件發出之后才加載。在大多數情況下, 在content script中監聽onload事件是不必要的,因為瀏覽器會確保在DOM創建完成后才執行它。 如果一定要在window.onload的時候運行,可以通過[document.readyState](http://www.whatwg.org/specs/web-apps/current-work/#dom-document-readystate)屬性來檢查onload事件是否已經發出。 |
| `all_frames` | boolean | _可選。_控制是在匹配頁面的所有frame中運行還是只在最上層的frame中運行。缺省是false,也就是只在最上層frame中運行。 |
| `include_globs` | array of string | _可選。_控制將content_script注入到哪些匹配的頁面中。模擬Greasemonkey中的@include關鍵字。 詳細信息可以查看下面的[Include and exclude globs](content_scripts.html#include-exclude-globs)。 |
| `exclude_globs` | array of string | _可選。_控制將content_script注入到哪些匹配的頁面中。模擬Greasemonkey中的@exclude關鍵字。 詳細信息可以查看下面的[Include and exclude globs](content_scripts.html#include-exclude-globs)。 |
### Include和exclude語句
一個content script被注入頁面的條件是: 頁面url匹配任意一條match模式, 并且匹配任意一條include glob模式,并且不匹配任何exclude glob模式。 由于matches屬性是必選的,因此include glob和exclude glob都只能用來限制哪些匹配的頁面會被影響。
另外, 這兩個屬性與matches屬性的語法是不同的, 它們更靈活一些。 在這兩個屬性中可以包含*號和?號作為通配符。 其中*可以匹配任意長度的字符串,而?匹配任意的單個字符。
例如,語句"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"
## 編程式注入
如果不需要將javascript 和css注入到每一個匹配的網頁里面,可以通過程序來控制代碼的注入。 例如, 可以只在用戶點擊了一個browser action圖標后才注入腳本。
如果要將代碼注入頁面,擴展必須具有[cross-origin 權限](xhr.html#requesting-permission), 還必須可以使用chrome.tabs模塊。 可以通過在manifest文件的[permissions](manifest.html#permissions)字段里聲明來取得這些權限。
一旦設置好了權限,就可以通過調用[executeScript()](tabs.html#method-executeScript)來注入javascript腳本。如果要注入css,可以調用[insertCSS()](tabs.html#method-insertCSS)。
下面的代碼(見例子[make_page_red](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/browserAction/make_page_red/)) 演示了點擊按鈕后向當前標簽的頁面中注入并執行javascript代碼。
```
_/* in background.html */_
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null,
{code:"document.body.bgColor='red'"});
});
_/* in manifest.json */_
"permissions": [
"tabs", "http://*/*"
],
```
當瀏覽器顯示一個http網頁并且用戶點擊了擴展的browser action按鈕后,擴展會將頁面的bgcolor屬性設置為紅色。 如果這個頁面沒有用css設置它的背景顏色的話, 它會變成紅色。
一般來說,可以將代碼放在文件里面而不是像上面那個例子那樣直接注入。 可以這樣寫:
```
chrome.tabs.executeScript(null, {file: "content_script.js"});
```
## 執行環境
Content script是在一個特殊環境中運行的,這個環境成為isolated world(隔離環境)。它們可以訪問所注入頁面的DOM,但是不能訪問里面的任何javascript變量和函數。 對每個content script來說,就像除了它自己之外再沒有其它腳本在運行。 反過來也是成立的: 頁面里的javascript也不能訪問content script中的任何變量和函數。
例如,這個簡單的頁面:
```
hello.html
==========
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener("click", function() {
alert(greeting + button.person_name + ".");
}, false);
</script>
</html>
```
現在,將下面這個腳本注入hello.html:
```
contentscript.js
================
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
alert(greeting + button.person_name + ".");
}, false);
```
然后,如果按下按鈕, 可以同時看到兩個問候。
隔離環境使得content script可以修改它的javascript環境而不必擔心會與這個頁面上的其它content script沖突。 例如,一個content script可以包含JQuery v1而頁面可以包含JQuery v2, 它們之間不會產生沖突。
另一個重要的優點是隔離環境可以將頁面上的腳本與擴展中的腳本完全隔離開。這使得開發者可以在content script中提供更多的功能,而不讓web頁面利用它們。
## 與嵌入的頁面通信
盡管content script的執行環境與所在的頁面是隔離的,但它們還是共享了頁面的DOM。 如果頁面需要與content script通信(或者通過content script與擴展通信), 就必須通過這個共享的DOM。
下面這個例子是通過自定義的DOM事件和把數據放到固定的地方來實現的:
```
http://foo.com/example.html
===========================
var customEvent = document.createEvent('Event');
customEvent.initEvent('myCustomEvent', true, true);
function fireCustomEvent(data) {
hiddenDiv = document.getElementById('myCustomEventDiv');
hiddenDiv.innerText = data
hiddenDiv.dispatchEvent(customEvent);
}
```
```
contentscript.js
================
var port = chrome.extension.connect();
document.getElementById('myCustomEventDiv').addEventListener('myCustomEvent', function() {
var eventData = document.getElementById('myCustomEventDiv').innerText;
port.postMessage({message: "myCustomEvent", values: eventData});
});
```
在上面的例子中,html頁面(不屬于擴展)創建了一個自定義事件, 當它向DOM中的一個特定元素寫入事件數據后就會激活并派發這個自定義事件。 Content script在這個特定元素上監聽這個自定義事件, 從這個元素中獲取數據,并向擴展進程post一個消息。 通過這種方式, 頁面建立了與擴展的通信鏈接。 這個方法也適用于反向的通信。
## 安全性
在寫content script的時候,有兩個安全問題必須注意。 首先, 要小心不要給原頁面帶來新的安全漏洞。 例如, 如果content script要從其它網站獲取數據(比如[通過背景頁面做XMLHttpRequest調用](messaging.html)), 在將數據注入前,應該進行處理以防止[cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting)攻擊,比如用innerText注入而不是用innerHTML注入。特別要小心的是在一個HTTPS的頁面上獲取HTTP的內容,因為這個內容很可能是被人用[man-in-the-middle](http://en.wikipedia.org/wiki/Man-in-the-middle_attack)方式破壞過的。
其次,盡管在獨立環境中運行content script的機制已經提供了一些保護,如果不加區分的使用web頁面上的內容還是可以被惡意的web頁面攻擊的。
```
contentscript.js
================
var data = document.getElementById("json-data")
// WARNING! Might be evaluating an evil script!
var parsed = eval("(" + data + ")")
contentscript.js
================
var elmt_id = ...
// WARNING! elmt_id might be "); ... evil script ... //"!
window.setTimeout("animate(" + elmt_id + ")", 200);
```
建議使用安全一些的API:
```
contentscript.js
================
var data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
var parsed = JSON.parse(data)
contentscript.js
================
var elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(function() {
animate(elmt_id);
}, 200);
```
## 引用擴展里的文件
通過chrome.extension.getURL()來獲取擴展里文件的URL。可以像使用其它url一樣使用這些URL,如下面的例子所示:
```
_//Code for displaying <extensiondir>/images/myimage.png:</extensiondir>_
var imgURL = **chrome.extension.getURL("images/myimage.png")**;
document.getElementById("someImage").src = imgURL;
```
## 例子
例子[contentscript_xhr](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/howto/contentscript_xhr)顯示了一個擴展如何從content script里進行cross-site請求。可以在[examples/api/messaging](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/messaging/) 里的消息通信部分找到更多例子。
可以看[make_page_red](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/browserAction/make_page_red/) 和[email_this_page](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/extensions/email_this_page/) 這兩個例子了解程序注入。
更多例子和源代碼,可以查看[例子](samples.html)
- 基礎文檔
- 綜述
- 調試
- 格式:Manifest文件
- 模式匹配
- 改變瀏覽器外觀
- Browser Actions
- Context Menus
- 桌面通知
- Omnibox
- Override替代頁
- Page Actions
- 主題
- 與瀏覽器交互
- 書簽
- Cookies
- chrome.devtools.* APIs
- Events
- chrome.history
- Management
- 標簽
- 視窗
- 實現擴展
- 無障礙性(a11y)
- 背景頁
- Content Scripts
- 跨域 XMLHttpRequest 請求
- 國際化 (i18n)
- 消息傳遞
- Optional Permissions
- NPAPI 插件
- 完成并發布應用
- 自動升級
- 托管
- 打包
- 規范和協議
- 應用設計規范
- 開發人員協議
- 免責聲明