การขยายเครื่องมือสำหรับนักพัฒนาเว็บ

ภาพรวม

ส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บจะเพิ่มฟังก์ชันการทำงานให้กับเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome โดยสามารถเพิ่มแผงและแถบด้านข้าง UI ใหม่ๆ โต้ตอบกับหน้าที่ตรวจสอบ รับข้อมูลเกี่ยวกับคำขอเครือข่าย และอื่นๆ ดู ส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บที่แนะนำ ส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บมีสิทธิ์เข้าถึงชุด API ส่วนขยายเฉพาะของเครื่องมือสำหรับนักพัฒนาเว็บเพิ่มเติม ดังนี้

ส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บมีโครงสร้างเหมือนกับส่วนขยายอื่นๆ โดยอาจมีหน้าพื้นหลัง สคริปต์เนื้อหา และรายการอื่นๆ นอกจากนี้ ส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บแต่ละรายการยังมีหน้าเครื่องมือสำหรับนักพัฒนาเว็บ ซึ่งมีสิทธิ์เข้าถึง API เครื่องมือสำหรับนักพัฒนาเว็บ

แผนภาพสถาปัตยกรรมที่แสดงหน้าเครื่องมือสำหรับนักพัฒนาเว็บที่สื่อสารกับ
       หน้าต่างที่ตรวจสอบและหน้าพื้นหลัง หน้าพื้นหลังจะแสดง
       สื่อสารกับสคริปต์เนื้อหาและเข้าถึง API ของส่วนขยาย
       หน้าเครื่องมือสำหรับนักพัฒนาเว็บมีสิทธิ์เข้าถึง API ของเครื่องมือสำหรับนักพัฒนาเว็บ เช่น การสร้างแผง

หน้าเครื่องมือสำหรับนักพัฒนาเว็บ

ระบบจะสร้างอินสแตนซ์ของหน้าเครื่องมือสำหรับนักพัฒนาเว็บของส่วนขยายทุกครั้งที่เปิดหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บ หน้าเครื่องมือสำหรับนักพัฒนาเว็บจะคงอยู่ตลอดอายุการใช้งานของหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บ หน้าเครื่องมือสำหรับนักพัฒนาเว็บมีสิทธิ์เข้าถึง API เครื่องมือสำหรับนักพัฒนาเว็บและ API ส่วนขยายบางรายการ โดยเฉพาะอย่างยิ่ง หน้าเครื่องมือสำหรับนักพัฒนาเว็บสามารถทำสิ่งต่อไปนี้ได้

  • สร้างและโต้ตอบกับแผงโดยใช้ devtools.panels API
  • รับข้อมูลเกี่ยวกับหน้าต่างที่ตรวจสอบและประเมินโค้ดในหน้าต่างที่ตรวจสอบโดยใช้ devtools.inspectedWindow API
  • รับข้อมูลเกี่ยวกับคำขอเครือข่ายโดยใช้ devtools.network API

หน้าเครื่องมือสำหรับนักพัฒนาเว็บไม่สามารถใช้ API ส่วนขยายส่วนใหญ่ได้โดยตรง แต่มีสิทธิ์เข้าถึง API extension และ runtime ชุดย่อยเดียวกัน กับที่สคริปต์เนื้อหามีสิทธิ์เข้าถึง หน้าเครื่องมือสำหรับนักพัฒนาเว็บสามารถสื่อสารกับหน้าพื้นหลังได้โดยใช้การส่งข้อความ เช่นเดียวกับสคริปต์เนื้อหา ดูตัวอย่างได้ที่ การแทรกสคริปต์เนื้อหา

การสร้างส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บ

หากต้องการสร้างหน้าเครื่องมือสำหรับนักพัฒนาเว็บสำหรับส่วนขยาย ให้เพิ่มช่อง devtools_page ในไฟล์ Manifest ของส่วนขยาย

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

ระบบจะสร้างอินสแตนซ์ของ devtools_page ที่ระบุไว้ในไฟล์ Manifest ของส่วนขยายสำหรับหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บทุกหน้าต่างที่เปิดขึ้น หน้าเว็บอาจเพิ่มหน้าส่วนขยายอื่นๆ เป็นแผงและแถบด้านข้างลงใน หน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บโดยใช้ devtools.panels API

โมดูล API chrome.devtools.* ใช้ได้เฉพาะกับหน้าที่โหลดภายในหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บเท่านั้น สคริปต์เนื้อหาและหน้าส่วนขยายอื่นๆ จะไม่มี API เหล่านี้ ดังนั้น API จึงใช้ได้เฉพาะในช่วงเวลาที่หน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บเปิดอยู่เท่านั้น

นอกจากนี้ยังมี API เครื่องมือสำหรับนักพัฒนาเว็บบางรายการที่ยังอยู่ในช่วงทดลอง โปรดดู chrome.experimental.* API สำหรับรายการ API ทดลองและหลักเกณฑ์เกี่ยวกับวิธีใช้

องค์ประกอบ UI ของเครื่องมือสำหรับนักพัฒนาเว็บ: แผงและบานหน้าต่างแถบด้านข้าง

นอกเหนือจากองค์ประกอบ UI ของส่วนขยายทั่วไป เช่น การทำงานของเบราว์เซอร์ เมนูตามบริบท และป๊อปอัปแล้ว ส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บยังเพิ่มองค์ประกอบ UI ลงในหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บได้ด้วย ดังนี้

  • แผง คือแท็บระดับบนสุด เช่น แผงองค์ประกอบ แหล่งที่มา และเครือข่าย
  • บานหน้าต่างแถบด้านข้าง จะแสดง UI เสริมที่เกี่ยวข้องกับแผง บานหน้าต่างรูปแบบ รูปแบบที่คำนวณแล้ว และตัวจัดการเหตุการณ์ในแผงองค์ประกอบเป็นตัวอย่างของบานหน้าต่างแถบด้านข้าง (โปรดทราบว่าลักษณะที่ปรากฏของบานหน้าต่างแถบด้านข้างอาจไม่ตรงกับรูปภาพ ทั้งนี้ขึ้นอยู่กับเวอร์ชันของ Chrome ที่คุณใช้และตำแหน่งที่หน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บถูกตรึงไว้)

หน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บที่แสดงแผงองค์ประกอบและแผงแถบด้านข้างของรูปแบบ

แผงแต่ละแผงเป็นไฟล์ HTML ของตัวเอง ซึ่งอาจมีทรัพยากรอื่นๆ (JavaScript, CSS, รูปภาพ และอื่นๆ) การสร้างแผงพื้นฐานจะมีลักษณะดังนี้

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

JavaScript ที่ดำเนินการในแผงหรือบานหน้าต่างแถบด้านข้างมีสิทธิ์เข้าถึง API เดียวกันกับหน้าเครื่องมือสำหรับนักพัฒนาเว็บ

การสร้างบานหน้าต่างแถบด้านข้างพื้นฐานสำหรับแผงองค์ประกอบจะมีลักษณะดังนี้

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

คุณแสดงเนื้อหาในบานหน้าต่างแถบด้านข้างได้หลายวิธี ดังนี้

  • เนื้อหา HTML เรียก setPage เพื่อระบุหน้า HTML ที่จะแสดงในบานหน้าต่าง
  • ข้อมูล JSON ส่งออบเจ็กต์ JSON ไปยัง setObject
  • นิพจน์ JavaScript ส่งนิพจน์ไปยัง setExpression. เครื่องมือสำหรับนักพัฒนาเว็บจะประเมินนิพจน์ในบริบทของหน้าที่ตรวจสอบและแสดงค่าที่ส่งคืน

สำหรับทั้ง setObject และ setExpression บานหน้าต่างจะแสดงค่าตามที่ปรากฏในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ อย่างไรก็ตาม setExpression ช่วยให้คุณแสดงองค์ประกอบ DOM และออบเจ็กต์ JavaScript ที่กำหนดเองได้ ในขณะที่ setObject รองรับเฉพาะออบเจ็กต์ JSON

การสื่อสารระหว่างคอมโพเนนต์ส่วนขยาย

ส่วนต่อไปนี้จะอธิบายสถานการณ์ทั่วไปบางอย่างสำหรับการสื่อสารระหว่างคอมโพเนนต์ต่างๆ ของส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บ

การแทรกสคริปต์เนื้อหา

หน้าเครื่องมือสำหรับนักพัฒนาเว็บไม่สามารถเรียก tabs.executeScript ได้โดยตรง หากต้องการแทรกสคริปต์เนื้อหาจาก หน้าเครื่องมือสำหรับนักพัฒนาเว็บ คุณต้องดึงข้อมูลรหัสของแท็บหน้าต่างที่ตรวจสอบโดยใช้พร็อพเพอร์ตี้ inspectedWindow.tabId แล้วส่งข้อความไปยังหน้าพื้นหลัง จากหน้าพื้นหลัง ให้เรียก tabs.executeScript เพื่อแทรกสคริปต์

ข้อมูลโค้ดต่อไปนี้แสดงวิธีแทรกสคริปต์เนื้อหาโดยใช้ 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"
});

โค้ดสำหรับหน้าพื้นหลัง

// 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);
    });
});

การประเมิน JavaScript ในหน้าต่างที่ตรวจสอบ

คุณสามารถใช้วิธี inspectedWindow.eval เพื่อดำเนินการโค้ด JavaScript ในบริบทของ หน้าที่ตรวจสอบ คุณสามารถเรียกใช้วิธี eval จากหน้าเครื่องมือสำหรับนักพัฒนาเว็บ แผง หรือบานหน้าต่างแถบด้านข้าง

โดยค่าเริ่มต้น ระบบจะประเมินนิพจน์ในบริบทของเฟรมหลักของหน้า ตอนนี้คุณอาจ คุ้นเคยกับฟีเจอร์ API บรรทัดคำสั่งของเครื่องมือสำหรับนักพัฒนาเว็บ เช่น การตรวจสอบองค์ประกอบ (inspect(elem)) การหยุดชั่วคราวในฟังก์ชัน (debug(fn)) การคัดลอกไปยังคลิปบอร์ด (copy()) และอื่นๆ inspectedWindow.eval() ใช้บริบทและตัวเลือกการดำเนินการสคริปต์เดียวกันกับโค้ดที่พิมพ์ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ ซึ่งช่วยให้เข้าถึง API เหล่านี้ได้ภายใน eval ตัวอย่างเช่น SOAK ใช้ฟีเจอร์นี้ เพื่อตรวจสอบองค์ประกอบ

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

หรือใช้ตัวเลือก useContentScriptContext: true สำหรับ inspectedWindow.eval() เพื่อประเมินนิพจน์ในบริบทเดียวกับสคริปต์เนื้อหา การเรียก eval ด้วย useContentScriptContext: true จะไม่ สร้าง บริบทสคริปต์เนื้อหา ดังนั้นคุณต้องโหลดสคริปต์บริบทก่อนที่จะเรียก eval โดยการเรียก executeScript หรือระบุสคริปต์เนื้อหาในไฟล์ manifest.json

เมื่อมีบริบทสคริปต์เนื้อหาแล้ว คุณจะใช้ตัวเลือกนี้เพื่อแทรกสคริปต์เนื้อหาเพิ่มเติมได้

วิธี eval มีประสิทธิภาพเมื่อใช้ในบริบทที่เหมาะสมและเป็นอันตรายเมื่อใช้ไม่ถูกต้อง ใช้วิธี tabs.executeScript หากคุณไม่จำเป็นต้องเข้าถึง บริบท JavaScript ของหน้าที่ตรวจสอบ ดูข้อควรระวังโดยละเอียดและการเปรียบเทียบวิธีการทั้ง 2 วิธีได้ที่ ดู inspectedWindow

การส่งองค์ประกอบที่เลือกไปยังสคริปต์เนื้อหา

สคริปต์เนื้อหาไม่มีสิทธิ์เข้าถึงองค์ประกอบที่เลือกในปัจจุบันโดยตรง อย่างไรก็ตาม โค้ดที่คุณ ดำเนินการโดยใช้ inspectedWindow.eval มีสิทธิ์เข้าถึงคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บและ API บรรทัดคำสั่ง ตัวอย่างเช่น ในโค้ดที่ประเมินแล้ว คุณสามารถใช้ $0 เพื่อเข้าถึงองค์ประกอบที่เลือก

วิธีส่งองค์ประกอบที่เลือกไปยังสคริปต์เนื้อหา

  • สร้างเมธอดในสคริปต์เนื้อหาที่รับองค์ประกอบที่เลือกเป็นอาร์กิวเมนต์
  • เรียกเมธอดจากหน้าเครื่องมือสำหรับนักพัฒนาเว็บโดยใช้ inspectedWindow.eval พร้อมตัวเลือก useContentScriptContext: true

โค้ดในสคริปต์เนื้อหาอาจมีลักษณะดังนี้

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

เรียกเมธอดจากหน้าเครื่องมือสำหรับนักพัฒนาเว็บดังนี้

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

ตัวเลือก useContentScriptContext: true ระบุว่าต้องประเมินนิพจน์ในบริบทเดียวกับสคริปต์เนื้อหา จึงจะเข้าถึงเมธอด setSelectedElement ได้

การรับ window ของแผงอ้างอิง

หากต้องการ postMessage จากแผงเครื่องมือสำหรับนักพัฒนาเว็บ คุณจะต้องมีการอ้างอิงไปยังออบเจ็กต์ window ของแผงนั้น รับหน้าต่าง iframe ของแผงจากตัวจัดการเหตุการณ์ panel.onShown ดังนี้

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

การส่งข้อความจากสคริปต์เนื้อหาไปยังหน้าเครื่องมือสำหรับนักพัฒนาเว็บ

การส่งข้อความระหว่างหน้าเครื่องมือสำหรับนักพัฒนาเว็บและสคริปต์เนื้อหาเป็นการส่งแบบอ้อมผ่านหน้าพื้นหลัง

เมื่อส่งข้อความ ไปยัง สคริปต์เนื้อหา หน้าพื้นหลังสามารถใช้วิธี tabs.sendMessage ซึ่งจะส่งข้อความไปยังสคริปต์เนื้อหาในแท็บที่เฉพาะเจาะจง ดังที่แสดงใน การแทรกสคริปต์เนื้อหา

เมื่อส่งข้อความ จาก สคริปต์เนื้อหา จะไม่มีวิธีสำเร็จรูปในการส่งข้อความไปยังอินสแตนซ์หน้าเครื่องมือสำหรับนักพัฒนาเว็บที่ถูกต้องซึ่งเชื่อมโยงกับแท็บปัจจุบัน วิธีแก้ปัญหาคือ คุณสามารถให้หน้าเครื่องมือสำหรับนักพัฒนาเว็บสร้างการเชื่อมต่อระยะยาวกับหน้าพื้นหลัง และให้หน้าพื้นหลังเก็บแผนที่ของรหัสแท็บไปยังการเชื่อมต่อ เพื่อให้สามารถกำหนดเส้นทางข้อความแต่ละข้อความไปยังการเชื่อมต่อที่ถูกต้องได้

// 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;
});

หน้าเครื่องมือสำหรับนักพัฒนาเว็บ (หรือแผงหรือบานหน้าต่างแถบด้านข้าง) จะสร้างการเชื่อมต่อดังนี้

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

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

การส่งข้อความจากสคริปต์ที่แทรกไปยังหน้าเครื่องมือสำหรับนักพัฒนาเว็บ

แม้ว่าโซลูชันข้างต้นจะใช้ได้กับสคริปต์เนื้อหา แต่โค้ดที่แทรกลงในหน้าโดยตรง (เช่น ผ่านการเพิ่มแท็ก <script> หรือผ่าน inspectedWindow.eval) ต้องใช้กลยุทธ์อื่น ในบริบทนี้ runtime.sendMessage จะไม่ส่งข้อความไปยัง สคริปต์พื้นหลังตามที่คาดไว้

วิธีแก้ปัญหาคือ คุณสามารถรวมสคริปต์ที่แทรกกับสคริปต์เนื้อหาที่ทำหน้าที่เป็นตัวกลาง หากต้องการส่งข้อความไปยังสคริปต์เนื้อหา คุณสามารถใช้ window.postMessage API ตัวอย่างเช่น สมมติว่าสคริปต์พื้นหลังมาจากส่วนก่อนหน้า

// 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);
});

ตอนนี้ข้อความจะไหลจากสคริปต์ที่แทรก ไปยังสคริปต์เนื้อหา ไปยังสคริปต์พื้นหลัง และสุดท้ายไปยังหน้าเครื่องมือสำหรับนักพัฒนาเว็บ

นอกจากนี้ คุณยังพิจารณาเทคนิคการส่งข้อความทางเลือก 2 วิธีได้ที่นี่

การตรวจหาเมื่อเครื่องมือสำหรับนักพัฒนาเว็บเปิดและปิด

หากส่วนขยายต้องติดตามว่าหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บเปิดอยู่หรือไม่ คุณสามารถเพิ่ม Listener onConnect ลงในหน้าพื้นหลัง แล้วเรียก connect จากหน้าเครื่องมือสำหรับนักพัฒนาเว็บ เนื่องจากแต่ละแท็บสามารถเปิดหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บของตัวเองได้ คุณจึงอาจได้รับเหตุการณ์ connect หลายรายการ หากต้องการติดตามว่าหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บเปิดอยู่หรือไม่ คุณต้องนับเหตุการณ์ connect และ disconnect ดังที่แสดงด้านล่าง

// 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.");
          }
      });
    }
});

หน้าเครื่องมือสำหรับนักพัฒนาเว็บจะสร้างการเชื่อมต่อดังนี้

// devtools.js

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

ตัวอย่างส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บ

เรียกดูแหล่งที่มาของตัวอย่างส่วนขยายเครื่องมือสำหรับนักพัฒนาเว็บเหล่านี้

ข้อมูลเพิ่มเติม

ดูข้อมูลเกี่ยวกับ API มาตรฐานที่ส่วนขยายใช้ได้ที่ chrome.* API และ Web API

ส่งความคิดเห็นถึงเรา ความคิดเห็นและคำแนะนำของคุณจะช่วยเราปรับปรุง API

ตัวอย่าง

ดูตัวอย่างที่ใช้ API เครื่องมือสำหรับนักพัฒนาเว็บได้ใน ตัวอย่าง