گسترش DevTools

نمای کلی

یک افزونه‌ی DevTools قابلیت‌هایی را به DevTools کروم اضافه می‌کند. این افزونه می‌تواند پنل‌ها و سایدبارهای رابط کاربری جدید اضافه کند، با صفحه‌ی بررسی‌شده تعامل داشته باشد، اطلاعاتی در مورد درخواست‌های شبکه دریافت کند و موارد دیگر. افزونه‌های ویژه‌ی DevTools را مشاهده کنید. افزونه‌های DevTools به مجموعه‌ای اضافی از APIهای افزونه‌ی مخصوص DevTools دسترسی دارند:

یک افزونه‌ی DevTools مانند هر افزونه‌ی دیگری ساختار یافته است: می‌تواند یک صفحه‌ی پس‌زمینه، اسکریپت‌های محتوا و موارد دیگر داشته باشد. علاوه بر این، هر افزونه‌ی DevTools دارای یک صفحه‌ی DevTools است که به APIهای DevTools دسترسی دارد.

نمودار معماری که صفحه DevTools را در حال ارتباط با پنجره بازرسی شده و صفحه پس‌زمینه نشان می‌دهد. صفحه پس‌زمینه در حال ارتباط با اسکریپت‌های محتوا و دسترسی به APIهای افزونه نشان داده شده است.  صفحه DevTools به APIهای DevTools دسترسی دارد، به عنوان مثال، ایجاد پنل‌ها.

صفحه DevTools

هر بار که یک پنجره DevTools باز می‌شود، یک نمونه از صفحه DevTools افزونه ایجاد می‌شود. صفحه DevTools در طول عمر پنجره DevTools وجود دارد. صفحه DevTools به APIهای DevTools و مجموعه‌ای محدود از APIهای افزونه دسترسی دارد. به طور خاص، صفحه DevTools می‌تواند:

  • با استفاده از APIهای devtools.panels ، پنل‌ها را ایجاد و با آنها تعامل کنید.
  • با استفاده از APIهای devtools.inspectedWindow ، اطلاعاتی در مورد پنجره‌ی بازرسی‌شده دریافت کنید و کد موجود در پنجره‌ی بازرسی‌شده را ارزیابی کنید.
  • با استفاده از APIهای devtools.network ، اطلاعات مربوط به درخواست‌های شبکه را دریافت کنید.

صفحه DevTools نمی‌تواند مستقیماً از اکثر APIهای افزونه‌ها استفاده کند. این صفحه به همان زیرمجموعه‌ای از APIهای extension و runtime دسترسی دارد که یک اسکریپت محتوا به آنها دسترسی دارد. مانند یک اسکریپت محتوا، یک صفحه DevTools می‌تواند با استفاده از ارسال پیام (Message Passing ) با صفحه پس‌زمینه ارتباط برقرار کند. برای مثال، به تزریق یک اسکریپت محتوا (Injecting a Content Script ) مراجعه کنید.

ایجاد یک افزونه DevTools

برای ایجاد یک صفحه DevTools برای افزونه خود، فیلد devtools_page را در مانیفست افزونه اضافه کنید:

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

یک نمونه از devtools_page که در مانیفست افزونه شما مشخص شده است، برای هر پنجره DevTools که باز می‌شود، ایجاد می‌شود. این صفحه می‌تواند با استفاده از API devtools.panels ، صفحات افزونه دیگری را به عنوان پنل و سایدبار به پنجره DevTools اضافه کند.

ماژول‌های API مربوط به chrome.devtools.* فقط برای صفحاتی که در پنجره‌ی DevTools بارگذاری شده‌اند، در دسترس هستند. اسکریپت‌های محتوا و سایر صفحات افزونه این APIها را ندارند. بنابراین، APIها فقط در طول عمر پنجره‌ی DevTools در دسترس هستند.

همچنین برخی از APIهای DevTools هنوز در مرحله آزمایشی هستند. برای مشاهده فهرست APIهای آزمایشی و دستورالعمل‌های نحوه استفاده از آنها، به chrome.experimental.* APIs مراجعه کنید.

عناصر رابط کاربری DevTools: پنل‌ها و پنل‌های نوار کناری

علاوه بر عناصر رابط کاربری معمول افزونه‌ها، مانند اقدامات مرورگر، منوهای زمینه و پنجره‌های بازشو، یک افزونه DevTools می‌تواند عناصر رابط کاربری را به پنجره DevTools اضافه کند:

  • یک پنل، یک تب سطح بالا است، مانند پنل‌های عناصر، منابع و شبکه.
  • یک پنل نوار کناری، رابط کاربری تکمیلی مربوط به یک پنل را ارائه می‌دهد. پنل‌های Styles، Computed Styles و Event Listeners در پنل Elements نمونه‌هایی از پنل‌های نوار کناری هستند. (توجه داشته باشید که بسته به نسخه کرومی که استفاده می‌کنید و محل قرارگیری پنجره DevTools، ممکن است ظاهر پنل‌های نوار کناری با تصویر مطابقت نداشته باشد.)

پنجره‌ی DevTools که پنل Elements و پنل کناری Styles را نشان می‌دهد.

هر پنل، فایل HTML مخصوص به خود را دارد که می‌تواند شامل منابع دیگری (جاوااسکریپت، CSS، تصاویر و غیره) باشد. ایجاد یک پنل ساده به این شکل است:

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

جاوا اسکریپتی که در یک پنل یا نوار کناری اجرا می‌شود، به همان APIهایی دسترسی دارد که صفحه DevTools به آنها دسترسی دارد.

ایجاد یک پنل سایدبار پایه برای پنل Elements به این شکل است:

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

چندین روش برای نمایش محتوا در یک پنل سایدبار وجود دارد:

  • محتوای HTML. برای تعیین یک صفحه HTML که در پنجره نمایش داده می‌شود، تابع setPage را فراخوانی کنید.
  • داده‌های JSON. یک شیء JSON را به setObject ارسال کنید.
  • عبارت جاوا اسکریپت. یک عبارت را به setExpression ارسال کنید. DevTools عبارت را در چارچوب صفحه مورد بررسی ارزیابی می‌کند و مقدار بازگشتی را نمایش می‌دهد.

برای هر دو setObject و setExpression ، این پنجره مقدار را همانطور که در کنسول DevTools ظاهر می‌شود، نمایش می‌دهد. با این حال، setExpression به شما امکان نمایش عناصر DOM و اشیاء دلخواه جاوا اسکریپت را می‌دهد، در حالی که setObject فقط از اشیاء JSON پشتیبانی می‌کند.

ارتباط بین اجزای افزونه

بخش‌های زیر برخی از سناریوهای معمول برای برقراری ارتباط بین اجزای مختلف یک افزونه DevTools را شرح می‌دهند.

تزریق یک اسکریپت محتوا

صفحه DevTools نمی‌تواند مستقیماً tabs.executeScript را فراخوانی کند. برای تزریق یک اسکریپت محتوا از صفحه DevTools، باید شناسه تب پنجره بازرسی شده را با استفاده از ویژگی 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);
    });
});

ارزیابی جاوا اسکریپت در پنجره بازرسی شده

شما می‌توانید از متد inspectedWindow.eval برای اجرای کد جاوا اسکریپت در متن صفحه‌ی بازرسی‌شده استفاده کنید. می‌توانید متد eval را از یک صفحه، پنل یا نوار کناری DevTools فراخوانی کنید.

به طور پیش‌فرض، عبارت در چارچوب اصلی صفحه ارزیابی می‌شود. حال، ممکن است با ویژگی‌های API خط فرمان DevTools مانند بازرسی عنصر ( inspect(elem) )، شکستن توابع ( debug(fn) )، کپی کردن در کلیپ‌بورد ( copy() ) و موارد دیگر آشنا باشید. inspectedWindow.eval() از همان زمینه اجرای اسکریپت و گزینه‌های کد تایپ شده در کنسول DevTools استفاده می‌کند که امکان دسترسی به این 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 استفاده کنید. برای هشدارهای دقیق و مقایسه دو متد، به inspectedWindow مراجعه کنید.

ارسال عنصر انتخاب شده به یک اسکریپت محتوا

اسکریپت محتوا دسترسی مستقیم به عنصر انتخاب شده فعلی ندارد. با این حال، هر کدی که با استفاده از inspectedWindow.eval اجرا کنید، به کنسول DevTools و APIهای خط فرمان دسترسی دارد. برای مثال، در کد ارزیابی شده می‌توانید $0 برای دسترسی به عنصر انتخاب شده استفاده کنید.

برای ارسال عنصر انتخاب شده به یک اسکریپت محتوا:

  • یک متد در اسکریپت محتوا ایجاد کنید که عنصر انتخاب شده را به عنوان آرگومان دریافت کند.
  • با استفاده از inspectedWindow.eval و با استفاده از گزینه useContentScriptContext: true متد را از صفحه DevTools فراخوانی کنید.

کد موجود در اسکریپت محتوای شما ممکن است چیزی شبیه به این باشد:

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

متد را از صفحه DevTools به صورت زیر فراخوانی کنید:

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

گزینه useContentScriptContext: true مشخص می‌کند که عبارت باید در همان چارچوب اسکریپت‌های محتوا ارزیابی شود، بنابراین می‌تواند به متد setSelectedElement دسترسی داشته باشد.

دریافت window پنل مرجع

برای postMessage از یک پنل devtools، به یک ارجاع به شیء window آن نیاز دارید. پنجره iframe یک پنل را از کنترل‌کننده رویداد panel.onShown دریافت کنید:

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

پیام‌رسانی از اسکریپت‌های محتوا به صفحه DevTools

پیام‌رسانی بین صفحه DevTools و اسکریپت‌های محتوا غیرمستقیم و از طریق صفحه پس‌زمینه است.

هنگام ارسال پیام به یک اسکریپت محتوا، صفحه پس‌زمینه می‌تواند از متد tabs.sendMessage استفاده کند که همانطور که در بخش تزریق اسکریپت محتوا نشان داده شده است، پیامی را به اسکریپت‌های محتوا در یک تب خاص هدایت می‌کند.

هنگام ارسال پیام از یک اسکریپت محتوا، هیچ روش آماده‌ای برای ارسال پیام به نمونه صحیح صفحه DevTools مرتبط با برگه فعلی وجود ندارد. به عنوان یک راه حل، می‌توانید کاری کنید که صفحه DevTools یک اتصال طولانی مدت با صفحه پس‌زمینه برقرار کند و صفحه پس‌زمینه نقشه‌ای از شناسه‌های برگه به ​​اتصالات را نگه دارد، بنابراین می‌تواند هر پیام را به اتصال صحیح هدایت کند.

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

صفحه DevTools (یا پنل یا نوار کناری) اتصال را به این صورت برقرار می‌کند:

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

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

پیام‌رسانی از اسکریپت‌های تزریق‌شده به صفحه DevTools

اگرچه راه حل فوق برای اسکریپت‌های محتوا کار می‌کند، کدی که مستقیماً به صفحه تزریق می‌شود (مثلاً از طریق افزودن یک تگ <script> یا از طریق inspectedWindow.eval ) به استراتژی متفاوتی نیاز دارد. در این زمینه، runtime.sendMessage پیام‌ها را آنطور که انتظار می‌رود به اسکریپت پس‌زمینه منتقل نمی‌کند.

به عنوان یک راه حل، می‌توانید اسکریپت تزریق شده خود را با یک اسکریپت محتوا که به عنوان واسطه عمل می‌کند، ترکیب کنید. برای ارسال پیام‌ها به اسکریپت محتوا، می‌توانید از API window.postMessage استفاده کنید. در اینجا مثالی با فرض اسکریپت پس‌زمینه از بخش قبلی آورده شده است:

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

اکنون پیام شما از اسکریپت تزریق‌شده، به اسکریپت محتوا، اسکریپت پس‌زمینه و در نهایت به صفحه DevTools جریان می‌یابد.

همچنین می‌توانید دو تکنیک جایگزین برای ارسال پیام را در اینجا در نظر بگیرید.

تشخیص زمان باز و بسته شدن DevTools

اگر افزونه شما نیاز دارد که پیگیری کند آیا پنجره DevTools باز است یا خیر، می‌توانید یک شنونده onConnect به صفحه پس‌زمینه اضافه کنید و connect را از صفحه DevTools فراخوانی کنید. از آنجایی که هر تب می‌تواند پنجره DevTools خود را باز داشته باشد، ممکن است چندین رویداد اتصال دریافت کنید. برای پیگیری اینکه آیا هر پنجره DevTools باز است یا خیر، باید رویدادهای اتصال و قطع اتصال را مانند تصویر زیر بشمارید:

// 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 اتصالی مانند این ایجاد می‌کند:

// devtools.js

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

نمونه‌های افزونه DevTools

منبع این نمونه‌های افزونه DevTools را مرور کنید:

  • افزونه‌ی Polymer Devtools - از بسیاری از helperهایی که در صفحه‌ی میزبان اجرا می‌شوند برای پرس‌وجو از وضعیت DOM/JS و ارسال آن به پنل سفارشی استفاده می‌کند.
  • افزونه React DevTools - از یک زیرماژول Blink برای استفاده مجدد از کامپوننت‌های رابط کاربری DevTools استفاده می‌کند.
  • Ember Inspector - هسته افزونه مشترک با آداپتورهایی برای کروم و فایرفاکس.
  • Coquette-inspect - یک افزونه‌ی مبتنی بر React تمیز با یک عامل اشکال‌زدایی که به صفحه‌ی میزبان تزریق شده است.
  • گالری افزونه‌های DevTools و افزونه‌های نمونه ما، برنامه‌های ارزشمندتری برای نصب، امتحان کردن و یادگیری دارند.

اطلاعات بیشتر

برای اطلاعات در مورد APIهای استانداردی که افزونه‌ها می‌توانند از آنها استفاده کنند، به chrome.* APIها و APIهای وب مراجعه کنید.

به ما بازخورد بدهید! نظرات و پیشنهادات شما به ما در بهبود APIها کمک می‌کند.

مثال‌ها

می‌توانید مثال‌هایی را که از APIهای DevTools استفاده می‌کنند، در Samples پیدا کنید.