Tập lệnh nội dung là các tệp chạy trên trang web. Sử dụng Tài liệu chuẩn Mô hình đối tượng (DOM), họ có thể đọc chi tiết về các trang web mà trình duyệt truy cập, các thay đổi đối với chúng và chuyển thông tin vào tiện ích mẹ của chúng.
Tìm hiểu các chức năng của tập lệnh nội dung
Tập lệnh nội dung có thể truy cập trực tiếp vào các API tiện ích sau đây:
dom
i18n
storage
runtime.connect()
runtime.getManifest()
runtime.getURL()
runtime.id
runtime.onConnect
runtime.onMessage
runtime.sendMessage()
Tập lệnh nội dung không thể truy cập trực tiếp vào các API khác. Tuy nhiên, họ có thể truy cập chúng gián tiếp bằng cách trao đổi tin nhắn với các phần khác trong tiện ích của bạn.
Bạn cũng có thể truy cập các tệp khác trong tiện ích của mình từ tập lệnh nội dung, sử dụng
Các API như fetch()
. Để làm việc này, bạn cần khai báo chúng là
tài nguyên có thể truy cập trên web. Lưu ý rằng việc này cũng sẽ hiển thị các tài nguyên cho bất kỳ
tập lệnh của bên thứ nhất hoặc bên thứ ba chạy trên cùng một trang web.
Làm việc trong những thế giới tách biệt
Tập lệnh nội dung tồn tại trong một môi trường tách biệt, cho phép tập lệnh nội dung thay đổi Môi trường JavaScript không xung đột với trang hoặc các tiện ích khác tập lệnh nội dung.
Tiện ích có thể chạy trong trang web có mã tương tự như ví dụ sau.
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>
Tiện ích đó có thể chèn tập lệnh nội dung sau đây bằng một trong các kỹ thuật được nêu trong Phần Chèn tập lệnh.
content-script.js
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
Với thay đổi này, cả hai cảnh báo sẽ xuất hiện theo trình tự khi người dùng nhấp vào nút.
Chèn tập lệnh
Tập lệnh nội dung có thể được khai báo theo cách tĩnh, được khai báo động hoặc được chèn có lập trình.
Chèn bằng các khai báo tĩnh
Sử dụng nội dung khai báo tập lệnh nội dung tĩnh trong manifest.json cho các tập lệnh tự động chạy trên một tập hợp các trang phổ biến.
Các tập lệnh khai báo tĩnh được đăng ký trong tệp kê khai bằng khoá "content_scripts"
.
Chúng có thể bao gồm tệp JavaScript, tệp CSS hoặc cả hai. Tất cả tập lệnh nội dung tự động chạy phải chỉ định
mẫu khớp.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
Tên | Loại | Mô tả |
---|---|---|
matches |
mảng chuỗi | Bắt buộc. Chỉ định những trang mà tập lệnh nội dung này sẽ được chèn vào. Xem Mẫu so khớp để biết chi tiết về cú pháp của các chuỗi này và So khớp các mẫu và cụm từ tìm kiếm để biết thông tin về cách loại trừ URL. |
css |
mảng chuỗi | Không bắt buộc. Danh sách các tệp CSS sẽ được chèn vào các trang phù hợp. Đây là được chèn theo thứ tự chúng xuất hiện trong mảng này, trước khi bất kỳ DOM nào được xây dựng hoặc hiển thị cho trang. |
js |
|
Không bắt buộc. Danh sách tệp JavaScript sẽ được đưa vào các trang phù hợp. Tệp được chèn theo thứ tự chúng xuất hiện trong mảng này. Mỗi chuỗi trong danh sách này phải chứa đường dẫn tương đối đến một tài nguyên trong thư mục gốc của tiện ích. Dấu gạch chéo ở đầu (`/`) là cắt tự động. |
run_at |
RunAt | Không bắt buộc. Chỉ định thời điểm chèn tập lệnh vào trang. Mặc định là
document_idle . |
match_about_blank |
boolean | Không bắt buộc. Tập lệnh có nên chèn vào khung about:blank hay không
trong đó khung mẹ hoặc khung mở khớp với một trong các mẫu được khai báo trong
matches . Giá trị mặc định là false. |
match_origin_as_fallback |
boolean |
Không bắt buộc. Tập lệnh có nên chèn vào các khung
được tạo bởi một nguồn gốc trùng khớp, nhưng URL hoặc nguồn gốc có thể không trực tiếp
khớp với mẫu. Các khung hình này bao gồm các giao thức khác nhau, chẳng hạn như
about: , data: , blob: và
filesystem: . Xem thêm
Chèn vào các khung liên quan.
|
world |
ExecutionWorld |
Không bắt buộc. Thế giới JavaScript để thực thi một tập lệnh bên trong. Giá trị mặc định là ISOLATED . Xem thêm
Làm việc trong những thế giới tách biệt.
|
Chèn bằng các nội dung khai báo động
Tập lệnh nội dung động rất hữu ích khi các mẫu so khớp cho tập lệnh nội dung là không phổ biến hoặc khi tập lệnh nội dung không phải lúc nào cũng được chèn vào các máy chủ đã biết.
Ra mắt trong Chrome 96, tính năng khai báo động sẽ tương tự như nội dung tĩnh
, nhưng đối tượng tập lệnh nội dung lại được đăng ký với Chrome bằng
trong không gian tên chrome.scripting
thay vì trong
manifest.json. Scripting API cũng cho phép nhà phát triển tiện ích
thành:
- Đăng ký tập lệnh nội dung.
- Nhận danh sách tập lệnh nội dung đã đăng ký.
- Cập nhật danh sách tập lệnh nội dung đã đăng ký.
- Xoá các tập lệnh nội dung đã đăng ký.
Giống như khai báo tĩnh, nội dung khai báo động có thể bao gồm tệp JavaScript, tệp CSS hoặc cả hai.
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"));
Chèn theo phương thức lập trình
Sử dụng tính năng chèn có lập trình cho các tập lệnh nội dung cần chạy để phản hồi các sự kiện hoặc trên các tập lệnh cụ thể ngoại lệ.
Để chèn tập lệnh nội dung theo phương thức lập trình, tiện ích của bạn cần có quyền của máy chủ để
trang mà máy chủ đang cố chèn tập lệnh vào. Quyền từ phía máy chủ có thể được cấp bằng
việc yêu cầu các API này trong tệp kê khai của tiện ích hoặc tạm thời sử dụng "activeTab"
.
Sau đây là các phiên bản khác của một tiện ích dựa trên thẻ hoạt động.
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
Tập lệnh nội dung có thể được chèn dưới dạng tệp.
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"]
});
});
Hoặc phần thân hàm có thể được chèn và thực thi dưới dạng tập lệnh nội dung.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
Lưu ý rằng hàm được chèn là bản sao của hàm được tham chiếu trong
Lệnh gọi chrome.scripting.executeScript()
, không phải chính hàm gốc. Kết quả là, giá trị của hàm
nội dung phải được kiểm soát; tham chiếu đến các biến bên ngoài hàm sẽ khiến nội dung
để gửi ReferenceError
.
Khi chèn dưới dạng hàm, bạn cũng có thể truyền các đối số vào hàm.
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" ],
});
});
Loại trừ kết quả trùng khớp và khối hình cầu
Để tuỳ chỉnh việc so khớp trang được chỉ định, hãy đưa các trường sau vào phần khai báo của bạn.
Tên | Loại | Mô tả |
---|---|---|
exclude_matches |
mảng chuỗi | Không bắt buộc. Không bao gồm những trang mà tập lệnh nội dung này sẽ được chèn vào vào. Xem Mẫu so khớp để biết chi tiết về cú pháp của các chuỗi này. |
include_globs |
mảng chuỗi | Không bắt buộc. Được áp dụng sau ngày matches để chỉ bao gồm các URL cũng
phù hợp với khối cầu này. Tính năng này nhằm mô phỏng @include
Từ khoá Greasesh. |
exclude_globs |
mảng chuỗi | Không bắt buộc. Được áp dụng sau ngày matches để loại trừ những URL khớp với chính sách này
hình cầu. Nhằm mô phỏng @exclude
Từ khoá Greasesh. |
Tập lệnh nội dung sẽ được chèn vào một trang nếu cả hai điều sau đây đều xảy ra:
- URL của mẫu khớp với mọi mẫu
matches
và mẫuinclude_globs
bất kỳ. - URL cũng không khớp với mẫu
exclude_matches
hoặcexclude_globs
. Vì thuộc tínhmatches
là bắt buộc,exclude_matches
,include_globs
vàexclude_globs
chỉ có thể được sử dụng để giới hạn những trang sẽ bị ảnh hưởng.
Tiện ích sau đây sẽ chèn tập lệnh nội dung vào https://www.nytimes.com/health
nhưng không được đưa vào 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" ],
}]);
Các thuộc tính glob tuân theo một cú pháp khác và linh hoạt hơn so với mẫu so khớp. Hình tròn có thể chấp nhận
chuỗi là các URL có thể chứa "ký tự đại diện" dấu hoa thị và dấu chấm hỏi. Dấu hoa thị (*
)
khớp với chuỗi bất kỳ có độ dài bất kỳ, bao gồm cả chuỗi trống, trong khi dấu chấm hỏi (?
) khớp với
ký tự đơn bất kỳ.
Ví dụ: khối cầu https://???.example.com/foo/\*
khớp với bất kỳ điều kiện nào sau đây:
https://www.example.com/foo/bar
https://the.example.com/foo/
Tuy nhiên, mã này không phù hợp với những điều sau:
https://my.example.com/foo/bar
https://example.com/foo/
https://www.example.com/foo
Tiện ích này sẽ chèn tập lệnh nội dung vào https://www.nytimes.com/arts/index.html
và
https://www.nytimes.com/jobs/index.htm*
, nhưng không chuyển sang
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"]
}
],
...
}
Tiện ích này sẽ chèn tập lệnh nội dung vào https://history.nytimes.com
và
https://.nytimes.com/history
, nhưng không được đưa vào https://science.nytimes.com
hoặc
https://www.nytimes.com/science
:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Bạn có thể sử dụng một, tất cả hoặc một vài trong số đó để đạt được phạm vi chính xác.
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"]
}
],
...
}
Thời gian chạy
Trường run_at
kiểm soát thời điểm chèn tệp JavaScript vào trang web. Phương thức ưu tiên và
giá trị mặc định là "document_idle"
. Xem loại RunAt để biết các phương diện khác có thể
giá trị.
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" ],
}]);
Tên | Loại | Mô tả |
---|---|---|
document_idle |
string | Ưu tiên. Sử dụng "document_idle" bất cứ khi nào có thể.Trình duyệt chọn thời điểm để chèn tập lệnh giữa "document_end" và ngay sau đó
window.onload
kích hoạt sự kiện. Thời điểm chèn chính xác phụ thuộc vào độ phức tạp của tài liệu cũng như
mất nhiều thời gian để tải và được tối ưu hoá cho tốc độ tải trang.Tập lệnh nội dung chạy lúc "document_idle" không cần phải lắng nghe
window.onload , chúng được đảm bảo sẽ chạy sau khi DOM hoàn tất. Nếu một
tập lệnh chắc chắn cần chạy sau window.onload , tiện ích này có thể kiểm tra xem
onload đã được kích hoạt bằng cách sử dụng document.readyState
thuộc tính này. |
document_start |
string | Các tập lệnh được chèn sau bất kỳ tệp nào từ css , nhưng trước bất kỳ DOM nào khác là
được tạo hoặc bất kỳ tập lệnh nào khác được chạy. |
document_end |
string | Tập lệnh được chèn ngay sau khi DOM hoàn tất, nhưng trước các tài nguyên phụ như hình ảnh và khung đã tải. |
Chỉ định khung
Trường "all_frames"
cho phép tiện ích chỉ định liệu có phải tệp JavaScript và CSS hay không
được chèn vào tất cả các khung phù hợp với các yêu cầu URL được chỉ định hoặc chỉ vào khung trên cùng trong một
.
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" ],
}]);
Tên | Loại | Mô tả |
---|---|---|
all_frames |
boolean | Không bắt buộc. Giá trị mặc định là false , nghĩa là chỉ khung hình trên cùng được
khớp.Nếu bạn chỉ định true , tất cả khung hình sẽ được chèn vào, ngay cả khi
khung không phải là khung trên cùng trong thẻ. Mỗi khung được kiểm tra độc lập để tìm URL
các yêu cầu liên quan. Quảng cáo này sẽ không chèn vào các khung con nếu không đáp ứng các yêu cầu về URL. |
Chèn vào các khung liên quan
Các tiện ích nên chạy tập lệnh trong các khung có liên quan đến một lệnh so khớp nhưng không khớp với nhau. Một tình huống phổ biến trong trường hợp này là cho các khung có URL được tạo bởi khung phù hợp, nhưng có URL không tự khớp với mẫu được chỉ định của tập lệnh.
Đây là trường hợp khi một tiện ích muốn chèn URL vào khung hình
có giao thức about:
, data:
, blob:
và filesystem:
. Trong những trường hợp này,
URL sẽ không khớp với mẫu của tập lệnh nội dung (và trong trường hợp about:
và
data:
, thậm chí không bao gồm URL gốc hoặc nguồn gốc trong URL
như trong about:blank
hoặc data:text/html,<html>Hello, World!</html>
).
Tuy nhiên, những khung hình này vẫn có thể được liên kết với khung đang tạo.
Để chèn vào các khung này, tiện ích có thể chỉ định
"match_origin_as_fallback"
trên một thông số kỹ thuật của tập lệnh nội dung trong phần tử
tệp kê khai.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
Khi được chỉ định và đặt thành true
, Chrome sẽ xem xét nguồn gốc của
trình khởi tạo của khung để xác định xem khung đó có khớp hay không, thay vì
URL của chính khung đó. Xin lưu ý rằng mã này cũng có thể khác với
origin của khung mục tiêu (ví dụ: data:
URL có nguồn gốc rỗng).
Trình khởi tạo của khung là khung đã tạo hoặc điều hướng mục tiêu khung. Mặc dù đây thường là phần tử mẹ hoặc mở trực tiếp, nhưng có thể không (như trong trường hợp một khung điều hướng đến iframe trong iframe).
Vì hoạt động này so sánh nguồn gốc của khung khởi tạo, nên khung khởi tạo
có thể ở bất kỳ đường dẫn nào từ nguồn gốc đó. Để làm rõ ràng ngụ ý này, Chrome
yêu cầu mọi tập lệnh nội dung được chỉ định bằng "match_origin_as_fallback"
đặt thành true
để đồng thời chỉ định đường dẫn của *
.
Khi bạn chỉ định cả "match_origin_as_fallback"
và "match_about_blank"
,
"match_origin_as_fallback"
sẽ được ưu tiên.
Giao tiếp với trang nhúng
Mặc dù môi trường thực thi của tập lệnh nội dung và các trang lưu trữ tập lệnh đó được tách riêng với nhau, chúng cùng chia sẻ quyền truy cập vào DOM của trang. Nếu trang này muốn giao tiếp với tập lệnh nội dung hoặc với tiện ích thông qua tập lệnh nội dung, tập lệnh này phải thực hiện việc này thông qua DOM được chia sẻ.
Bạn có thể hoàn thành một ví dụ bằng cách sử dụng 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);
Trang không có tiện ích, example.html, đăng thông báo lên chính trang đó. Tin nhắn này sẽ bị chặn và được kiểm tra bởi tập lệnh nội dung và sau đó được đăng lên quy trình tiện ích. Bằng cách này, trang thiết lập kênh giao tiếp với quá trình mở rộng. Điều ngược lại có thể thực hiện thông qua có nghĩa tương tự.
Truy cập vào tệp tiện ích
Để truy cập vào một tệp tiện ích từ một tập lệnh nội dung, bạn có thể gọi
chrome.runtime.getURL()
để nhận URL tuyệt đối của thành phần phần mở rộng như trong ví dụ sau (content.js
):
content-script.js
let image = chrome.runtime.getURL("images/my_image.png")
Để sử dụng phông chữ hoặc hình ảnh trong tệp CSS, bạn có thể sử dụng @@extension_id
để tạo URL như trong ví dụ sau (content.css
):
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');
}
Bạn phải khai báo mọi thành phần là tài nguyên có thể truy cập trên web trong tệp manifest.json
:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Giữ an toàn
Mặc dù các thế giới tách biệt mang đến một lớp bảo vệ, nhưng việc sử dụng tập lệnh nội dung có thể tạo ra
lỗ hổng bảo mật trong tiện ích và trang web. Nếu tập lệnh nội dung nhận được nội dung từ một
trang web riêng biệt, chẳng hạn như bằng cách gọi fetch()
, hãy cẩn thận lọc nội dung dựa trên
Cross-site scripting trước khi chèn tập lệnh đó. Chỉ giao tiếp qua HTTPS để
tránh các cuộc tấn công của "man-in-the-middle".
Nhớ lọc ra các trang web độc hại. Ví dụ: các mẫu sau đây nguy hiểm và không được phép trong Manifest V3:
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);
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:
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);