Tập lệnh nội dung

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:

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 mảng chuỗi 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: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:

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ẫu include_globs bất kỳ.
  • URL cũng không khớp với mẫu exclude_matches hoặc exclude_globs. Vì thuộc tính matches là bắt buộc, exclude_matches, include_globsexclude_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.htmlhttps://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.comhttps://.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.

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: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: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""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 &quot;man-in-the-middle&quot;.

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:

Không nên

content-script.js

const data = document.getElementById("json-data");
// WARNING! Might be evaluating an evil script!
const parsed = eval("(" + data + ")");
Không nên

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:

Nên

content-script.js

const data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
const parsed = JSON.parse(data);
Nên

content-script.js

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