Skrip konten adalah file yang berjalan dalam konteks halaman web. Menggunakan Dokumen standar Model Objek (DOM), mereka dapat membaca detail halaman web yang dikunjungi browser, perubahan pada mereka, dan meneruskan informasi ke ekstensi induknya.
Memahami kemampuan skrip konten
Skrip konten dapat mengakses API ekstensi berikut secara langsung:
dom
i18n
storage
runtime.connect()
runtime.getManifest()
runtime.getURL()
runtime.id
runtime.onConnect
runtime.onMessage
runtime.sendMessage()
Skrip konten tidak dapat mengakses API lain secara langsung. Namun, pengguna dapat mengaksesnya secara tidak langsung dengan bertukar pesan dengan bagian lain dari ekstensi Anda.
Anda juga dapat mengakses file lain dalam ekstensi dari skrip konten, menggunakan
API seperti fetch()
. Untuk melakukan ini, Anda
perlu mendeklarasikannya sebagai
referensi yang dapat diakses dari web. Perhatikan bahwa ini juga mengekspos resource ke
skrip pihak pertama atau pihak ketiga yang berjalan di situs yang sama.
Bekerja di dunia terisolasi
Skrip konten berada di dunia yang terisolasi, sehingga skrip konten dapat membuat perubahan pada Lingkungan JavaScript tanpa bertentangan dengan halaman atau ekstensi lainnya skrip konten.
Ekstensi dapat berjalan di halaman web dengan kode yang mirip dengan contoh berikut.
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>
Ekstensi itu bisa menyuntikkan skrip konten berikut menggunakan salah satu teknik yang diuraikan dalam Bagian Memasukkan skrip.
content-script.js
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
Dengan perubahan ini, kedua pemberitahuan muncul secara berurutan saat tombol diklik.
Memasukkan skrip
Skrip konten dapat dideklarasikan secara statis, dideklarasikan secara dinamis, atau dimasukkan secara terprogram.
Memasukkan dengan deklarasi statis
Menggunakan deklarasi skrip konten statis dalam manifest.json untuk skrip yang harus secara otomatis berjalan pada serangkaian halaman yang sudah dikenal.
Skrip yang dideklarasikan secara statis didaftarkan dalam manifes dengan kunci "content_scripts"
.
File tersebut dapat menyertakan file JavaScript, file CSS, atau keduanya. Semua skrip konten yang dijalankan secara otomatis harus menentukan
pola pencocokan.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
Nama | Jenis | Deskripsi |
---|---|---|
matches |
array string | Wajib. Menentukan halaman tempat skrip konten ini akan dimasukkan. Lihat Pola Pencocokan untuk mengetahui detail tentang sintaksis string ini serta Mencocokkan pola dan glob untuk informasi tentang cara mengecualikan URL. |
css |
array string | Opsional. Daftar file CSS yang akan dimasukkan ke halaman yang cocok. Berikut adalah diinjeksikan sesuai urutan kemunculannya dalam array ini, sebelum DOM dibuat atau ditampilkan pada halaman. |
js |
|
Opsional. Daftar file JavaScript yang akan dimasukkan ke halaman yang cocok. File diinjeksikan sesuai urutan kemunculannya dalam {i>array<i} ini. Setiap {i>string<i} dalam daftar ini harus berisi jalur relatif ke resource di direktori utama ekstensi. Garis miring (`/`) di awal adalah dipangkas secara otomatis. |
run_at |
RunAt | Opsional. Menentukan kapan skrip harus dimasukkan ke halaman. Nilai defaultnya adalah document_idle . |
match_about_blank |
boolean | Opsional. Apakah skrip harus dimasukkan ke dalam frame about:blank
di mana {i>frame<i} induk atau pembuka cocok
dengan salah satu pola yang dideklarasikan di
matches . Nilai defaultnya adalah false (salah). |
match_origin_as_fallback |
boolean |
Opsional. Apakah skrip harus dimasukkan dalam {i>frame<i} yang
dibuat oleh origin yang cocok, tetapi yang URL atau originnya mungkin tidak secara langsung
sesuai dengan polanya. Ini termasuk {i>frame<i} dengan skema yang berbeda, seperti
about: , data: , blob: , dan
filesystem: . Lihat juga
Memasukkan dalam frame terkait.
|
world |
ExecutionWorld |
Opsional. Dunia JavaScript untuk skrip yang akan dieksekusi. Nilai defaultnya adalah ISOLATED . Lihat juga
Bekerja di dunia yang terisolasi.
|
Memasukkan dengan deklarasi dinamis
Skrip konten dinamis berguna ketika pola pencocokan untuk skrip konten tidak dikenal atau ketika skrip konten tidak seharusnya selalu dimasukkan pada {i>host<i} yang dikenal.
Diperkenalkan di Chrome 96, deklarasi dinamis mirip dengan deklarasi statis
deklarasi tambahan, tetapi objek skrip konten
didaftarkan ke Chrome menggunakan
di namespace chrome.scripting
, bukan di
manifest.json. Scripting API juga memungkinkan developer ekstensi
menjadi:
- Daftarkan skrip konten.
- Dapatkan daftar skrip konten yang terdaftar.
- Perbarui daftar skrip konten terdaftar.
- Hapus skrip konten yang terdaftar.
Seperti deklarasi statis, deklarasi dinamis dapat menyertakan file JavaScript, file CSS, atau keduanya.
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"));
Memasukkan secara terprogram
Gunakan injeksi terprogram untuk skrip konten yang perlu dijalankan sebagai respons terhadap peristiwa atau kesempatan.
Untuk memasukkan skrip konten secara terprogram, ekstensi Anda memerlukan izin host untuk
laman tempat kita mencoba
memasukkan skrip. Izin akses {i>host<i} dapat
diberikan oleh
memintanya sebagai bagian dari manifes ekstensi Anda atau menggunakan "activeTab"
untuk sementara.
Berikut adalah versi lain dari ekstensi berbasis ActiveTab.
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
Skrip konten dapat dimasukkan sebagai file.
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"]
});
});
Atau, isi fungsi dapat dimasukkan dan dieksekusi sebagai skrip konten.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
Ketahuilah bahwa fungsi yang dimasukkan adalah salinan dari fungsi yang dirujuk dalam
Panggilan chrome.scripting.executeScript()
, bukan fungsi aslinya itu sendiri. Akibatnya,
tubuh harus mandiri; referensi ke variabel di luar {i>function
<i}akan menyebabkan isi
skrip untuk menampilkan ReferenceError
.
Saat menginjeksikan sebagai fungsi, Anda juga dapat meneruskan argumen ke fungsi.
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" ],
});
});
Kecualikan kecocokan dan glob
Untuk menyesuaikan pencocokan halaman tertentu, sertakan kolom berikut dalam instance pendaftaran.
Nama | Jenis | Deskripsi |
---|---|---|
exclude_matches |
array string | Opsional. Tidak mencakup halaman yang akan dimasukkan oleh skrip konten ini ke dalamnya. Lihat Pola Pencocokan untuk mengetahui detail sintaksis {i>string<i} ini. |
include_globs |
array string | Opsional. Diterapkan setelah matches untuk hanya menyertakan URL yang juga
cocok dengan glob ini. Ini dimaksudkan untuk mengemulasi @include
Kata kunci greaseMonkey. |
exclude_globs |
array string | Opsional. Diterapkan setelah matches untuk mengecualikan URL yang cocok dengan nilai ini
glob. Dimaksudkan untuk mengemulasi @exclude
Kata kunci greaseMonkey. |
Skrip konten akan dimasukkan ke halaman jika kedua hal berikut berlaku:
- URL-nya cocok dengan pola
matches
dan polainclude_globs
apa pun. - URL tersebut juga tidak cocok dengan pola
exclude_matches
atauexclude_globs
. Karena propertimatches
wajib ada,exclude_matches
,include_globs
, danexclude_globs
hanya dapat digunakan untuk membatasi halaman mana yang akan terpengaruh.
Ekstensi berikut memasukkan skrip konten ke https://www.nytimes.com/health
tetapi tidak ke 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" ],
}]);
Properti Glob mengikuti sintaksis yang berbeda dan lebih fleksibel daripada pola pencocokan. Globo yang dapat diterima
string adalah URL yang mungkin berisi "karakter pengganti" tanda bintang dan tanda tanya. Tanda bintang (*
)
cocok dengan string apa pun dengan panjang berapa pun, termasuk string kosong, sedangkan tanda tanya (?
) cocok
karakter tunggal apa pun.
Misalnya, https://???.example.com/foo/\*
glob cocok dengan salah satu dari yang berikut ini:
https://www.example.com/foo/bar
https://the.example.com/foo/
Namun, duplikat ini tidak cocok dengan yang berikut:
https://my.example.com/foo/bar
https://example.com/foo/
https://www.example.com/foo
Ekstensi ini memasukkan skrip konten ke dalam https://www.nytimes.com/arts/index.html
dan
https://www.nytimes.com/jobs/index.htm*
, tetapi tidak menjadi
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"]
}
],
...
}
Ekstensi ini memasukkan skrip konten ke dalam https://history.nytimes.com
dan
https://.nytimes.com/history
, tetapi tidak ke dalam https://science.nytimes.com
atau
https://www.nytimes.com/science
:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Satu, semua, atau beberapa di antaranya dapat disertakan untuk mencapai cakupan yang benar.
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"]
}
],
...
}
Waktu proses
Kolom run_at
mengontrol kapan file JavaScript dimasukkan ke halaman web. Pilihan dan
nilai defaultnya adalah "document_idle"
. Lihat jenis RunAt untuk mengetahui
masing-masing.
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" ],
}]);
Nama | Jenis | Deskripsi |
---|---|---|
document_idle |
string | Lebih disukai. Gunakan "document_idle" jika memungkinkan.Browser memilih waktu untuk memasukkan skrip antara "document_end" dan segera setelah
window.onload
jika terjadi kebakaran. Momen injeksi yang tepat bergantung pada seberapa rumit dokumen dan seberapa rumit
lama waktu pemuatan, dan dioptimalkan untuk kecepatan pemuatan halaman.Skrip konten yang berjalan pada "document_idle" tidak perlu memproses
window.onload , peristiwa tersebut dijamin berjalan setelah DOM selesai. Jika
skrip harus dijalankan setelah window.onload . Ekstensi dapat memeriksa apakah
onload telah diaktifkan menggunakan document.readyState
saat ini. |
document_start |
string | Skrip dimasukkan setelah file apa pun dari css , tetapi sebelum DOM lain
atau skrip lainnya dijalankan. |
document_end |
string | Skrip langsung dimasukkan setelah DOM selesai, tetapi sebelum subresource seperti gambar dan {i>frame<i} telah dimuat. |
Tentukan frame
Kolom "all_frames"
memungkinkan ekstensi menentukan apakah file JavaScript dan CSS harus
dimasukkan ke semua frame yang sesuai dengan persyaratan URL yang ditentukan atau hanya ke frame teratas dalam
.
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" ],
}]);
Nama | Jenis | Deskripsi |
---|---|---|
all_frames |
boolean | Opsional. Default-nya adalah false , artinya hanya frame atas yang
cocok.Jika true ditentukan, semua frame akan dimasukkan, meskipun
{i>frame<i} bukanlah {i>frame<i}
paling atas dalam tab. Setiap frame diperiksa secara terpisah untuk URL
lainnya. Ini tidak akan dimasukkan ke dalam frame turunan jika persyaratan URL tidak terpenuhi. |
Memasukkan frame terkait
Ekstensi mungkin ingin menjalankan skrip dalam frame yang terkait dengan pencocokan {i>frame<i}, tetapi tidak sesuai dengan itu. Skenario yang umum terjadi adalah untuk frame dengan URL yang dibuat oleh frame yang cocok, tetapi URL-nya tidak cocok dengan pola yang ditentukan skrip.
Inilah yang terjadi ketika ekstensi ingin
memasukkan bingkai dengan URL yang
memiliki skema about:
, data:
, blob:
, dan filesystem:
. Dalam kasus ini,
URL tidak akan cocok dengan pola skrip konten (dan, untuk about:
serta
data:
, bahkan jangan sertakan URL induk atau origin di URL
sama sekali, seperti dalam about:blank
atau data:text/html,<html>Hello, World!</html>
).
Namun, frame ini masih dapat dikaitkan dengan frame yang dibuat.
Untuk menginjeksikan ke dalam {i>frame<i} ini, ekstensi dapat menentukan
"match_origin_as_fallback"
pada spesifikasi skrip konten di
manifes.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
Jika ditentukan dan disetel ke true
, Chrome akan melihat asal
inisiator {i>frame<i} untuk menentukan
apakah {i>frame<i} itu cocok, bukan di
URL dari {i>frame<i} itu sendiri. Perhatikan bahwa ini mungkin juga berbeda dengan
origin frame target (mis., URL data:
memiliki origin null).
Inisiator frame adalah frame yang membuat atau membuka target {i>frame<i}. Meskipun ini biasanya orang tua atau pembuka secara langsung, namun mungkin juga tidak (seperti dalam kasus frame yang menavigasi iframe dalam iframe).
Karena ini membandingkan origin frame inisiator, frame inisiator
bisa berada di jalur mana pun
dari titik asal tersebut. Untuk memperjelas implikasi ini, Chrome
memerlukan skrip konten yang ditentukan dengan "match_origin_as_fallback"
ditetapkan ke true
untuk menentukan jalur *
juga.
Jika "match_origin_as_fallback"
dan "match_about_blank"
ditentukan,
"match_origin_as_fallback"
diprioritaskan.
Komunikasi dengan halaman penyematan
Meskipun lingkungan eksekusi skrip konten dan halaman yang menghostingnya terpisah saling berbagi akses ke DOM halaman. Jika halaman ingin berkomunikasi dengan skrip konten, atau dengan ekstensi melalui skrip konten, hal itu harus dilakukan melalui DOM bersama.
Contoh dapat dilakukan menggunakan 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);
Halaman non-ekstensi, example.html, memposting pesan ke halaman itu sendiri. Pesan ini disadap dan diperiksa oleh skrip konten dan kemudian diposting ke proses ekstensi. Dengan cara ini, halaman menetapkan jalur komunikasi dengan proses ekstensi. Kebalikannya dimungkinkan melalui sarana yang serupa.
Mengakses file ekstensi
Untuk mengakses file ekstensi dari skrip konten, Anda dapat memanggil
chrome.runtime.getURL()
untuk mendapatkan URL absolut aset ekstensi Anda seperti yang ditunjukkan pada contoh berikut (content.js
):
content-script.js
let image = chrome.runtime.getURL("images/my_image.png")
Untuk menggunakan font atau gambar dalam file CSS, Anda dapat menggunakan @@extension_id
untuk membuat URL seperti yang ditunjukkan dalam contoh berikut (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');
}
Semua aset harus dideklarasikan sebagai referensi yang dapat diakses web dalam file manifest.json
:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Tetap aman
Dunia yang terisolasi memberikan lapisan perlindungan, tapi skrip konten dapat menciptakan
kerentanan di ekstensi dan laman web. Jika skrip konten menerima konten dari
di situs terpisah, seperti memanggil fetch()
, berhati-hatilah saat memfilter konten
Serangan pembuatan skrip lintas situs sebelum menginjeksikannya. Hanya berkomunikasi melalui HTTPS untuk
menghindari serangan "man-in-the-middle".
Pastikan untuk menyaring laman web yang berbahaya. Misalnya, pola berikut berbahaya, dan tidak diizinkan di Manifes 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);
Sebagai gantinya, pilih API yang lebih aman yang tidak menjalankan skrip:
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);