Tập lệnh nội dung

Tập lệnh nội dung là những tệp chạy trong bối cảnh của các trang web. Bằng cách sử dụng Mô hình đối tượng tài liệu (DOM) tiêu chuẩn, các tập lệnh nội dung có thể đọc thông tin chi tiết về các trang web mà trình duyệt truy cập, thực hiện các thay đổi đối với các trang web đó và truyền thông tin đến tiện ích mẹ.

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 vào các API Chrome mà tiện ích mẹ của tập lệnh đó dùng bằng cách trao đổi thông báo với tiện ích. Chúng cũng có thể truy cập vào URL của tệp tiện ích bằng chrome.runtime.getURL() và sử dụng kết quả tương tự như các URL khác.

// Code for displaying EXTENSION_DIR/images/myimage.png:
var imgURL = chrome.runtime.getURL("images/myimage.png");
document.getElementById("someImage").src = imgURL;

Ngoài ra, tập lệnh nội dung có thể truy cập trực tiếp vào các API Chrome sau:

Tập lệnh nội dung không thể truy cập trực tiếp vào các API khác.

Làm việc trong các thế giới riêng biệt

Tập lệnh nội dung nằm trong một môi trường biệt lập, cho phép tập lệnh nội dung thực hiện các thay đổi đối với môi trường JavaScript mà không xung đột với trang hoặc các tập lệnh nội dung bổ sung.

Tiện ích có thể chạy trong một trang web có mã tương tự như ví dụ bên dưới.

<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>

Tiện ích đó có thể chèn tập lệnh nội dung sau.

var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
  alert(greeting + button.person_name + ".");
}, false);

Cả hai cảnh báo sẽ xuất hiện nếu người dùng nhấn nút.

Các thế giới riêng biệt không cho phép tập lệnh nội dung, tiện ích và trang web truy cập vào bất kỳ biến hoặc hàm nào do các thành phần khác tạo. Điều này cũng cho phép tập lệnh nội dung bật chức năng mà trang web không truy cập được.

Chèn tập lệnh

Bạn có thể chèn Content Script theo cách có lập trình hoặc khai báo.

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 trong những trường hợp cụ thể.

Để chèn một tập lệnh nội dung theo chương trình, hãy cung cấp quyền activeTab trong tệp kê khai. Điều này cấp quyền truy cập an toàn vào máy chủ lưu trữ của trang web đang hoạt động và quyền truy cập tạm thời vào quyền thẻ, cho phép tập lệnh nội dung chạy trên thẻ đang hoạt động hiện tại mà không cần chỉ định quyền truy cập trên nhiều nguồn gốc.

{
  "name": "My extension",
  ...
  "permissions": [
    "activeTab"
  ],
  ...
}

Bạn có thể chèn tập lệnh nội dung dưới dạng mã.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "changeColor"){
      chrome.tabs.executeScript({
        code: 'document.body.style.backgroundColor="orange"'
      });
    }
  });

Hoặc bạn có thể chèn toàn bộ tệp.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "runContentScript"){
      chrome.tabs.executeScript({
        file: 'contentScript.js'
      });
    }
  });

Chèn một cách khai báo

Sử dụng tính năng chèn khai báo cho các tập lệnh nội dung cần được tự động chạy trên các trang được chỉ định.

Các tập lệnh được chèn theo cách khai báo sẽ được đăng ký trong tệp kê khai trong trường "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 đều phải chỉ định mẫu so khớp.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
Tên Loại Mô tả
matches {: #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. Hãy xem phần Mẫu khớp để biết thêm thông tin về cú pháp của các chuỗi này và phần Mẫu khớp và mẫu chung để biết thông tin về cách loại trừ URL.
css {: #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. Các đoạn mã này được chèn theo thứ tự xuất hiện trong mảng này, trước khi bất kỳ DOM nào được tạo hoặc hiển thị cho trang.
js {: #js } mảng chuỗi Không bắt buộc. Danh sách các tệp JavaScript sẽ được chèn vào các trang phù hợp. Các đối tượng này được chèn theo thứ tự xuất hiện trong mảng này.
match_about_blank {: #match_about_blank } boolean Không bắt buộc. Liệu 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.

Loại trừ các mẫu khớp và mẫu chung

Bạn có thể tuỳ chỉnh tính năng so khớp trang được chỉ định bằng cách thêm các trường sau vào quá trình đăng ký tệp kê khai.

Tên Loại Mô tả
exclude_matches {: #exclude_matches } mảng chuỗi Không bắt buộc. Loại trừ những trang mà tập lệnh nội dung này sẽ được chèn vào. Hãy xem phần Mẫu khớp để biết thêm thông tin chi tiết về cú pháp của các chuỗi này.
include_globs {: #include_globs } mảng chuỗi Không bắt buộc. Được áp dụng sau matches để chỉ bao gồm những URL cũng khớp với mẫu chung này. Dự định mô phỏng từ khoá @include Greasemonkey.
exclude_globs {: #exclude_globs } mảng chuỗi Không bắt buộc. Được áp dụng sau matches để loại trừ những URL khớp với glob này. Dự định mô phỏng từ khoá @excludeGreasemonkey.

Tập lệnh nội dung sẽ được chèn vào một trang nếu URL của trang đó khớp với bất kỳ mẫu matches và mẫu include_globs nào, miễn là 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, nên bạn chỉ có thể dùng exclude_matches, include_globsexclude_globs để 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 http://www.nytimes.com/ health nhưng không chèn vào http://www.nytimes.com/ business .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Thuộc tính glob tuân theo một cú pháp khác, linh hoạt hơn so với mẫu so khớp. Các chuỗi glob được chấp nhận là những URL có thể chứa dấu hoa thị và dấu chấm hỏi "đại diện". Dấu hoa thị * khớp với mọi chuỗi có độ dài bất kỳ, kể cả chuỗi trống, trong khi dấu chấm hỏi ? khớp với mọi ký tự đơn.

Ví dụ: glob http:// ??? .example.com/foo/ * khớp với bất kỳ nội dung nào sau đây:

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

Tuy nhiên, nó không khớp với những nội dung sau:

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

Tiện ích này sẽ chèn tập lệnh nội dung vào http:/www.nytimes.com/ arts /index.htmlhttp://www.nytimes.com/ jobs /index.html nhưng không chèn vào http://www.nytimes.com/ sports /index.html.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.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 http:// history .nytimes.comhttp://.nytimes.com/ history nhưng không chèn vào http:// science .nytimes.com hoặc http://www.nytimes.com/ science .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Bạn có thể thêm một, một số hoặc tất cả các thông tin này để đạt được phạm vi phù hợp.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Thời gian chạy

Thời điểm các tệp JavaScript được chèn vào trang web do trường run_at kiểm soát. Trường ưu tiên và mặc định là "document_idle", nhưng bạn cũng có thể chỉ định là "document_start" hoặc "document_end" nếu cần.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Tên Loại Mô tả
document_idle {: #document_idle } chuỗi Nên ưu tiên. Sử dụng "document_idle" bất cứ khi nào có thể.

Trình duyệt sẽ chọn thời điểm chèn tập lệnh giữa "document_end" và ngay sau khi sự kiện windowonload kích hoạt. Thời điểm chính xác của quá trình chèn phụ thuộc vào độ phức tạp của tài liệu và thời gian tải, đồng thời được tối ưu hoá cho tốc độ tải trang.

Các tập lệnh nội dung chạy tại "document_idle" không cần theo dõi sự kiện window.onload, chúng chắc chắn 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, thì tiện ích có thể kiểm tra xem onload đã kích hoạt hay chưa bằng cách sử dụng thuộc tính document.readyState.
document_start {: #document_start } chuỗi Các tập lệnh được chèn sau mọi tệp từ css, nhưng trước khi bất kỳ DOM nào khác được tạo hoặc bất kỳ tập lệnh nào khác được chạy.
document_end {: #document_end } chuỗi Các tập lệnh được chèn ngay sau khi DOM hoàn tất, nhưng trước khi các tài nguyên phụ như hình ảnh và khung hình được tải.

Chỉ định khung

Trường "all_frames" cho phép tiện ích chỉ định xem các tệp JavaScript và CSS có được chèn vào tất cả khung hình khớp với các yêu cầu về URL đã chỉ định hay chỉ được chèn vào khung hình trên cùng trong một thẻ.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
Tên Loại Mô tả
all_frames {: #all_frames } boolean Không bắt buộc. Mặc định là false, nghĩa là chỉ khung trên cùng được so khớp.

Nếu được chỉ định true, thì nó sẽ chèn vào tất cả các khung, 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 theo các yêu cầu về URL, 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.

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 đó tách biệt với nhau, nhưng chúng có quyền truy cập vào DOM của trang. Nếu 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, trang phải thực hiện việc này thông qua DOM dùng chung.

Bạn có thể thực hiện một ví dụ bằng cách sử dụng window.postMessage:

var port = chrome.runtime.connect();

window.addEventListener("message", function(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);
document.getElementById("theButton").addEventListener("click",
    function() {
  window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);

Trang không phải tiện ích (example.html) tự đăng thông báo cho chính nó. Thông báo này sẽ bị tập lệnh nội dung chặn và kiểm tra, sau đó được đăng lên quy trình tiện ích. Bằng cách này, trang sẽ thiết lập một đường truyền thông tin đến quy trình tiện ích. Bạn có thể làm ngược lại bằng các phương tiện tương tự.

Giữ an toàn

Mặc dù các thế giới riêng biệt cung cấp 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 các lỗ hổng trong tiện ích và trang web. Nếu tập lệnh nội dung nhận nội dung từ một trang web riêng biệt, chẳng hạn như thực hiện một XMLHttpRequest, hãy cẩn thận lọc nội dung tấn công tập lệnh trên nhiều trang web trước khi chèn nội dung đó. Chỉ giao tiếp qua HTTPS để tránh các cuộc tấn công "man-in-the-middle".

Hãy nhớ lọc các trang web độc hại. Ví dụ: các mẫu sau đây là nguy hiểm:

var data = document.getElementById("json-data")
// WARNING! Might be evaluating an evil script!
var parsed = eval("(" + data + ")")
var 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 không chạy tập lệnh:

var data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
var parsed = JSON.parse(data);
var elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(function() {
  animate(elmt_id);
}, 200);