Mở rộng Công cụ cho nhà phát triển

Tổng quan

Tiện ích Công cụ cho nhà phát triển bổ sung chức năng cho Công cụ của Chrome cho nhà phát triển. Kiểm thử này có thể thêm bảng điều khiển giao diện người dùng và thanh bên mới, tương tác với trang được kiểm tra, nhận thông tin về các yêu cầu mạng, v.v. Xem các tiện ích Công cụ cho nhà phát triển nổi bật. Các tiện ích Công cụ cho nhà phát triển có quyền truy cập vào một bộ API tiện ích bổ sung dành riêng cho DevTools:

Tiện ích Công cụ cho nhà phát triển có cấu trúc giống như mọi tiện ích khác: có thể có trang nền, tập lệnh nội dung và các mục khác. Ngoài ra, mỗi tiện ích Công cụ cho nhà phát triển có một trang Công cụ cho nhà phát triển có quyền truy cập vào các API Công cụ cho nhà phát triển.

Sơ đồ cấu trúc cho thấy trang Công cụ cho nhà phát triển đang giao tiếp với cửa sổ được kiểm tra và trang nền. Trang nền sẽ xuất hiện khi giao tiếp với tập lệnh nội dung và truy cập vào các API tiện ích.
       Trang Công cụ cho nhà phát triển có quyền truy cập vào API Công cụ cho nhà phát triển, ví dụ: tạo bảng điều khiển.

Trang Công cụ cho nhà phát triển

Một phiên bản của trang Công cụ cho nhà phát triển của tiện ích được tạo mỗi khi cửa sổ Công cụ cho nhà phát triển mở ra. Trang DevTools tồn tại trong toàn bộ thời gian của cửa sổ Công cụ cho nhà phát triển. Trang Công cụ cho nhà phát triển có quyền truy cập vào các API DevTools và một tập hợp giới hạn các API tiện ích. Cụ thể, trang Công cụ cho nhà phát triển có thể:

  • Tạo và tương tác với các bảng điều khiển bằng API devtools.panels.
  • Nhận thông tin về cửa sổ được kiểm tra và đánh giá mã trong cửa sổ được kiểm tra bằng cách sử dụng API devtools.inspectedWindow.
  • Nhận thông tin về các yêu cầu mạng bằng cách sử dụng các API devtools.network.

Trang Công cụ cho nhà phát triển không thể sử dụng trực tiếp hầu hết các API tiện ích. Tập lệnh này có quyền truy cập vào cùng một tập hợp con các API extensionruntime mà tập lệnh nội dung có quyền truy cập. Giống như tập lệnh nội dung, trang Công cụ cho nhà phát triển có thể giao tiếp với trang nền bằng cách sử dụng tính năng Truyền thông báo. Để biết ví dụ, hãy xem phần Chèn tập lệnh nội dung.

Tạo tiện ích Công cụ cho nhà phát triển

Để tạo trang Công cụ cho nhà phát triển cho tiện ích của bạn, hãy thêm trường devtools_page vào tệp kê khai tiện ích:

{
  "name": ...
  "version": "1.0",
  "minimum_chrome_version": "10.0",
  "devtools_page": "devtools.html",
  ...
}

Một thực thể của devtools_page được chỉ định trong tệp kê khai của tiện ích sẽ được tạo cho mỗi cửa sổ DevTools được mở. Trang có thể thêm các trang tiện ích khác dưới dạng bảng điều khiển và thanh bên vào cửa sổ DevTools bằng cách sử dụng API devtools.panels.

Các mô-đun API chrome.devtools.* chỉ có sẵn cho những trang được tải trong cửa sổ Công cụ cho nhà phát triển. Tập lệnh nội dung và các trang tiện ích khác không có những API này. Do đó, các API chỉ có sẵn trong suốt thời gian hoạt động của cửa sổ Công cụ cho nhà phát triển.

Ngoài ra, còn có một số API Công cụ cho nhà phát triển vẫn đang trong giai đoạn thử nghiệm. Hãy tham khảo chrome.experimental.* API để biết danh sách API thử nghiệm và hướng dẫn về cách sử dụng chúng.

Phần tử trên giao diện người dùng của Công cụ cho nhà phát triển: bảng điều khiển và ngăn thanh bên

Ngoài các thành phần trên giao diện người dùng tiện ích thông thường, chẳng hạn như thao tác trên trình duyệt, trình đơn theo bối cảnh và cửa sổ bật lên, tiện ích DevTools có thể thêm các thành phần trên giao diện người dùng vào cửa sổ Công cụ cho nhà phát triển:

  • Bảng điều khiển là một thẻ cấp cao nhất, chẳng hạn như bảng Phần tử, Nguồn và Mạng.
  • Ngăn thanh bên hiển thị giao diện người dùng bổ sung liên quan đến một bảng điều khiển. Các ngăn Kiểu, Kiểu tính toán và Trình xử lý sự kiện trên bảng Phần tử là ví dụ về các ngăn thanh bên. (Xin lưu ý rằng hình thức của các ngăn bên có thể không khớp với hình ảnh, tuỳ thuộc vào phiên bản Chrome bạn đang sử dụng và vị trí gắn cửa sổ Công cụ cho nhà phát triển.)

Cửa sổ Công cụ cho nhà phát triển hiển thị bảng điều khiển Phần tử và ngăn thanh bên Kiểu.

Mỗi bảng điều khiển là một tệp HTML riêng, có thể bao gồm các tài nguyên khác (JavaScript, CSS, hình ảnh, v.v.). Việc tạo một bảng điều khiển cơ bản sẽ có dạng như sau:

chrome.devtools.panels.create("My Panel",
    "MyPanelIcon.png",
    "Panel.html",
    function(panel) {
      // code invoked on panel creation
    }
);

JavaScript được thực thi trong bảng điều khiển hoặc ngăn thanh bên có quyền truy cập vào cùng các API như trang Công cụ cho nhà phát triển.

Tạo một ngăn thanh bên cơ bản cho bảng điều khiển Phần tử có dạng như sau:

chrome.devtools.panels.elements.createSidebarPane("My Sidebar",
    function(sidebar) {
        // sidebar initialization code here
        sidebar.setObject({ some_data: "Some data to show" });
});

Có một số cách hiển thị nội dung trong ngăn thanh bên:

  • Nội dung HTML. Gọi setPage để chỉ định một trang HTML sẽ hiển thị trong ngăn.
  • Dữ liệu JSON. Truyền một đối tượng JSON đến setObject.
  • Biểu thức JavaScript. Truyền một biểu thức đến setExpression. Công cụ cho nhà phát triển sẽ đánh giá biểu thức trong ngữ cảnh của trang được kiểm tra và hiển thị giá trị trả về.

Đối với cả setObjectsetExpression, ngăn sẽ hiển thị giá trị như xuất hiện trong bảng điều khiển DevTools. Tuy nhiên, setExpression cho phép bạn hiển thị các phần tử DOM và đối tượng JavaScript tuỳ ý, trong khi setObject chỉ hỗ trợ đối tượng JSON.

Giao tiếp giữa các thành phần tiện ích

Các phần sau đây mô tả một số trường hợp điển hình trong việc giao tiếp giữa các thành phần của tiện ích Công cụ cho nhà phát triển.

Chèn một tập lệnh nội dung

Trang Công cụ cho nhà phát triển không thể gọi trực tiếp tabs.executeScript. Để chèn tập lệnh nội dung từ trang Công cụ cho nhà phát triển, bạn phải truy xuất mã nhận dạng của thẻ của cửa sổ đã kiểm tra bằng cách sử dụng thuộc tính inspectedWindow.tabId và gửi thông báo đến trang nền. Trên trang nền, hãy gọi tabs.executeScript để chèn tập lệnh.

Các đoạn mã sau đây cho biết cách chèn một tập lệnh nội dung bằng cách sử dụng executeScript.

// DevTools page -- devtools.js
// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "devtools-page"
});

backgroundPageConnection.onMessage.addListener(function (message) {
    // Handle responses from the background page, if any
});

// Relay the tab ID to the background page
chrome.runtime.sendMessage({
    tabId: chrome.devtools.inspectedWindow.tabId,
    scriptToInject: "content_script.js"
});

Mã cho trang nền:

// Background page -- background.js
chrome.runtime.onConnect.addListener(function(devToolsConnection) {
    // assign the listener function to a variable so we can remove it later
    var devToolsListener = function(message, sender, sendResponse) {
        // Inject a content script into the identified tab
        chrome.tabs.executeScript(message.tabId,
            { file: message.scriptToInject });
    }
    // add the listener
    devToolsConnection.onMessage.addListener(devToolsListener);

    devToolsConnection.onDisconnect.addListener(function() {
         devToolsConnection.onMessage.removeListener(devToolsListener);
    });
});

Đánh giá JavaScript trong cửa sổ được kiểm tra

Bạn có thể sử dụng phương thức inspectedWindow.eval để thực thi mã JavaScript trong bối cảnh trang được kiểm tra. Bạn có thể gọi phương thức eval từ trang Công cụ cho nhà phát triển, bảng điều khiển hoặc ngăn thanh bên.

Theo mặc định, biểu thức được đánh giá trong ngữ cảnh khung chính của trang. Bây giờ, có thể bạn đã quen thuộc với các tính năng API dòng lệnh của Công cụ cho nhà phát triển như kiểm tra phần tử (inspect(elem)), phá vỡ các hàm (debug(fn)), sao chép vào bảng nhớ tạm (copy()) và nhiều tính năng khác. inspectedWindow.eval() sử dụng ngữ cảnh và các tuỳ chọn thực thi tập lệnh giống như với mã đã nhập tại bảng điều khiển DevTools, cho phép truy cập vào các API này trong eval. Ví dụ: SOAK sử dụng hàm này để kiểm tra một phần tử:

chrome.devtools.inspectedWindow.eval(
  "inspect($$('head script[data-soak=main]')[0])",
  function(result, isException) { }
);

Ngoài ra, hãy sử dụng tuỳ chọn useContentScriptContext: true cho inspectedWindow.eval() để đánh giá biểu thức trong cùng ngữ cảnh với tập lệnh nội dung. Việc gọi eval bằng useContentScriptContext: true sẽ không tạo ngữ cảnh tập lệnh nội dung. Vì vậy, bạn phải tải tập lệnh theo ngữ cảnh trước khi gọi eval, bằng cách gọi executeScript hoặc bằng cách chỉ định tập lệnh nội dung trong tệp manifest.json.

Khi ngữ cảnh tập lệnh ngữ cảnh tồn tại, bạn có thể sử dụng tuỳ chọn này để chèn thêm tập lệnh nội dung.

Phương thức eval hiệu quả khi được dùng trong đúng ngữ cảnh và nguy hiểm khi bị dùng không đúng cách. Hãy sử dụng phương thức tabs.executeScript nếu bạn không cần quyền truy cập vào ngữ cảnh JavaScript của trang được kiểm tra. Để biết các cảnh báo chi tiết và bảng so sánh 2 phương thức này, hãy xem inspectedWindow.

Chuyển phần tử đã chọn đến tập lệnh nội dung

Tập lệnh nội dung không có quyền truy cập trực tiếp vào phần tử đã chọn hiện tại. Tuy nhiên, bất kỳ mã nào bạn thực thi bằng inspectedWindow.eval đều có quyền truy cập vào bảng điều khiển Công cụ cho nhà phát triển và API dòng lệnh. Ví dụ: trong mã đã đánh giá, bạn có thể sử dụng $0 để truy cập vào phần tử đã chọn.

Để chuyển phần tử đã chọn đến tập lệnh nội dung:

  • Tạo một phương thức trong tập lệnh nội dung để lấy phần tử đã chọn làm đối số.
  • Gọi phương thức từ trang Công cụ cho nhà phát triển bằng cách sử dụng inspectedWindow.eval với lựa chọn useContentScriptContext: true.

Mã trong tập lệnh nội dung của bạn có thể trông giống như sau:

function setSelectedElement(el) {
    // do something with the selected element
}

Gọi phương thức từ trang Công cụ cho nhà phát triển như sau:

chrome.devtools.inspectedWindow.eval("setSelectedElement($0)",
    { useContentScriptContext: true });

Tuỳ chọn useContentScriptContext: true chỉ định rằng biểu thức phải được đánh giá trong cùng một ngữ cảnh với tập lệnh nội dung để có thể truy cập vào phương thức setSelectedElement.

Lấy window của bảng điều khiển tham chiếu

Để postMessage qua bảng điều khiển devtools, bạn cần tham chiếu đến đối tượng window của nó. Truy xuất cửa sổ iframe của một bảng điều khiển từ trình xử lý sự kiện panel.onShown:

onShown.addListener(function callback)
extensionPanel.onShown.addListener(function (extPanelWindow) {
    extPanelWindow instanceof Window; // true
    extPanelWindow.postMessage( // …
});

Thông báo từ tập lệnh nội dung đến trang Công cụ cho nhà phát triển

Thông điệp giữa trang Công cụ cho nhà phát triển và tập lệnh nội dung là gián tiếp, thông qua trang nền.

Khi gửi thông báo đến một tập lệnh nội dung, trang nền có thể sử dụng phương thức tabs.sendMessage. Phương thức này sẽ chuyển hướng thông báo đến tập lệnh nội dung trong một thẻ cụ thể, như minh hoạ trong phần Chèn tập lệnh nội dung.

Khi gửi thông báo từ một tập lệnh nội dung, không có phương thức có sẵn nào để gửi thông báo đến đúng phiên bản trang Công cụ cho nhà phát triển được liên kết với thẻ hiện tại. Để khắc phục, bạn có thể yêu cầu trang Công cụ cho nhà phát triển thiết lập kết nối lâu dài với trang nền và yêu cầu trang nền lưu bản đồ mã thẻ với các kết nối để có thể định tuyến từng thông báo đến đúng kết nối.

// background.js
var connections = {};

chrome.runtime.onConnect.addListener(function (port) {

    var extensionListener = function (message, sender, sendResponse) {

        // The original connection event doesn't include the tab ID of the
        // DevTools page, so we need to send it explicitly.
        if (message.name == "init") {
          connections[message.tabId] = port;
          return;
        }

    // other message handling
    }

    // Listen to messages sent from the DevTools page
    port.onMessage.addListener(extensionListener);

    port.onDisconnect.addListener(function(port) {
        port.onMessage.removeListener(extensionListener);

        var tabs = Object.keys(connections);
        for (var i=0, len=tabs.length; i < len; i++) {
          if (connections[tabs[i]] == port) {
            delete connections[tabs[i]]
            break;
          }
        }
    });
});

// Receive message from content script and relay to the devTools page for the
// current tab
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    // Messages from content scripts should have sender.tab set
    if (sender.tab) {
      var tabId = sender.tab.id;
      if (tabId in connections) {
        connections[tabId].postMessage(request);
      } else {
        console.log("Tab not found in connection list.");
      }
    } else {
      console.log("sender.tab not defined.");
    }
    return true;
});

Trang Công cụ cho nhà phát triển (hoặc bảng điều khiển hoặc ngăn thanh bên) thiết lập kết nối như sau:

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "panel"
});

backgroundPageConnection.postMessage({
    name: 'init',
    tabId: chrome.devtools.inspectedWindow.tabId
});

Thông báo từ các tập lệnh được chèn đến trang Công cụ cho nhà phát triển

Mặc dù giải pháp trên dùng được cho các tập lệnh nội dung, nhưng mã được chèn trực tiếp vào trang (ví dụ: thông qua việc thêm thẻ <script> hoặc thông qua inspectedWindow.eval) sẽ cần một chiến lược khác. Trong trường hợp này, runtime.sendMessage sẽ không truyền thông báo đến tập lệnh nền như dự kiến.

Để giải quyết vấn đề này, bạn có thể kết hợp tập lệnh đã chèn với một tập lệnh nội dung đóng vai trò là bên trung gian. Để chuyển thông báo đến tập lệnh nội dung, bạn có thể sử dụng API window.postMessage. Dưới đây là một ví dụ, giả sử tập lệnh nền từ phần trước:

// injected-script.js

window.postMessage({
  greeting: 'hello there!',
  source: 'my-devtools-extension'
}, '*');
// content-script.js

window.addEventListener('message', function(event) {
  // Only accept messages from the same frame
  if (event.source !== window) {
    return;
  }

  var message = event.data;

  // Only accept messages that we know are ours
  if (typeof message !== 'object' || message === null ||
      !message.source === 'my-devtools-extension') {
    return;
  }

  chrome.runtime.sendMessage(message);
});

Bây giờ, thông báo của bạn sẽ chuyển từ tập lệnh đã chèn đến tập lệnh nội dung, đến tập lệnh nền và cuối cùng là đến trang Công cụ cho nhà phát triển.

Bạn cũng có thể cân nhắc 2 kỹ thuật chuyển thông báo khác tại đây.

Phát hiện thời điểm Công cụ cho nhà phát triển mở và đóng

Nếu tiện ích của bạn cần theo dõi xem cửa sổ Công cụ cho nhà phát triển có mở hay không, bạn có thể thêm trình nghe onConnect vào trang nền và gọi connect từ trang Công cụ cho nhà phát triển. Vì mỗi thẻ có thể mở cửa sổ Công cụ cho nhà phát triển riêng, nên bạn có thể nhận được nhiều sự kiện kết nối. Để theo dõi xem có cửa sổ DevTools nào đang mở hay không, bạn cần đếm các sự kiện kết nối và ngắt kết nối như sau:

// background.js
var openCount = 0;
chrome.runtime.onConnect.addListener(function (port) {
    if (port.name == "devtools-page") {
      if (openCount == 0) {
        alert("DevTools window opening.");
      }
      openCount++;

      port.onDisconnect.addListener(function(port) {
          openCount--;
          if (openCount == 0) {
            alert("Last DevTools window closing.");
          }
      });
    }
});

Trang Công cụ cho nhà phát triển tạo ra một kết nối như sau:

// devtools.js

// Create a connection to the background page
var backgroundPageConnection = chrome.runtime.connect({
    name: "devtools-page"
});

Ví dụ về tiện ích Công cụ cho nhà phát triển

Duyệt xem nguồn của các ví dụ về tiện ích Công cụ cho nhà phát triển sau đây:

Thông tin khác

Để biết thông tin về các API chuẩn mà tiện ích có thể sử dụng, hãy xem chrome.* APIAPI web.

Gửi ý kiến phản hồi cho chúng tôi! Nhận xét và đề xuất của bạn giúp chúng tôi cải thiện các API.

Ví dụ

Bạn có thể tìm thấy các ví dụ sử dụng API Công cụ cho nhà phát triển trong phần Mẫu.