كيفية بناء واستخدام امتدادات Chrome لتصحيح WebSocket

تتناول هذه المقالة كيفية استخدام امتدادات Chrome جنبًا إلى جنب مع أداة المصحح لاعتراض وفك تشفير حركة مرور WebSocket. باستخدام مثال عملي يُدعى "Deriv WebSocket Trace"، ستأخذك هذه المقالة خطوة بخطوة لإعداد امتداد Chrome بسرعة لتصحيح WebSocket.
إتقان أدوات Chrome لتصحيح اتصالات WebSocket
يقدم Chrome أداة مصحح متكاملة. تقريبًا كل ما يمكنك القيام به في نافذة الفحص/التصحيح يمكنك القيام به باستخدام كائن المصحح. ومع ذلك، فهو غير متاح على أي صفحة ويب عادية لمنع سوء الاستخدام المحتمل. ومع ذلك، يمكنك الاستفادة من هذه الميزة القوية عبر امتدادات Chrome.
المصحح يقتصر على الوصول إليه داخل "عامل خدمة" في الخلفية معزول، وأي بيانات تم جمعها يجب نقلها إلى نوافذ المحتوى في الواجهة الأمامية. نظرًا لعدم تمتع عمال الخدمة بإمكانية الوصول إلى نموذج كائن المستند (DOM)، يجب توصيل جميع البيانات الخاصة بالعرض إلى الواجهة الأمامية عبر المراسلة.

عند بناء ملف manifest.json لامتدادك، من المهم التصريح عن نيتك استخدام المصحح. هذا يتيح لـ Chrome إبلاغ المستخدم بقدرات امتدادك والأشياء التي سيصل إليها.
{
"manifest_version": 3,
"name": "Deriv WebSocket Trace",
"icons": {
"128": "images/wss_trace_128.png"
},
"description": "عرض أوقات الأوامر/الاستجابات على اتصالات websocket",
"version": "0.04",
"host_permissions": ["*://*.deriv.com/*"],
"permissions": ["debugger", "webRequest", "activeTab", "storage"],
"content_scripts": [
{
"matches": ["*://*.deriv.com/*"],
"js": ["scripts/content.js", "scripts/trace.js"]
}
],
"background": {
"service_worker": "scripts/service-worker.js"
},
"action": {
"default_popup": "html/content_popup.html",
"default_icon": "images/wss_trace_128.png"
},
"options_ui": {
"page": "html/options.html",
"open_in_tab": false,
"browser_style": true
}
}
مثال على بناء ملف manifest.json لامتداد تصحيح WebSocket
من المهم أن تتذكر أنه يوجد مثيل مصحح عام واحد فقط. لفحص البيانات من تبويب أو نافذة محددة، يجب أولاً إرفاق المصحح بتلك التبويب أو النافذة.
الوصول واستخدام أداة تصحيح WebSocket
داخل عامل الخدمة الخاص بك، ستحتاج إلى تقرير متى وإلى أي النوافذ يتم إرفاق المصححات. أحد الأساليب هو مراقبة التغيرات على التبويبات، مثل أحداث التحميل أو الاكتمال، باستخدام `chrome.tabs.onUpdated.addListener`. هناك عدة أنواع أخرى من الأحداث، يتم الإشارة إليها بـ `on…`، يمكن إرفاق المستمعين بها. بناءً على معايير محددة، يمكنك بعد ذلك اختيار متى تُرفق مصحح بتبويب. كما يُنصح بإضافة مستمعين لأحداث `onRemoved` لأغراض التنظيف.
من الناحية التقنية، ليس من الضروري مراقبة التبويبات التي تم إرفاق المصحح بها، إذ لا يمكن إرفاق أكثر من مصحح واحد لكل تبويب. يمكنك التأكد من ذلك في رد الاتصال عن طريق فحص ما إذا كانت `chrome.runtime.lastError` مُعينة، والتي عادةً ما تشير إلى أن المصحح مُرفق بالفعل بالتبويب. مع ذلك، يُعتبر من الممارسات الجيدة تتبع التبويبات التي تم إرفاق المصححات بها وإزالة المستمعين بمجرد عدم وجود اتصالات مصحح مستخدمة. هذا النهج يقدم عدة ميزات سنتحدث عنها لاحقًا.
let debuggerAttached = {};
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status == 'loading') {
if (tab.url && new URL(tab.url).hostname.endsWith('deriv.com')) {
console.log('تم الكشف عن تحميل تبويب deriv.com',tabId,changeInfo);
if (!debuggerAttached[tabId]) {
chrome.debugger.attach({tabId}, '1.0', () => {
if(chrome.runtime.lastError) {
// من المحتمل أن يكون المصحح مُرفقًا بالفعل
console.log('من المحتمل أن يكون المصحح مُرفقًا بالفعل:',
chrome.runtime.lastError.message);
} else {
console.log(`تم إرفاق المصحح بالتبويب برقم:${tabId}`);
chrome.debugger.sendCommand({tabId}, 'Network.enable');
// نريد فقط إرفاق مستمع الحدث مرة واحدة
// عندما يتم إرفاق أول مصحح
if (Object.keys(debuggerAttached).length === 0) {
console.log(`جارٍ إرفاق مستمع حدث المصحح`);
chrome.debugger.onEvent.addListener(handleDebuggerEvent);
}
// إشارة إلى أن المصحح مُرفق
debuggerAttached[tabId] = tabId;
}
});
} else {
console.log(`المصحح مُرفق بالفعل بالتبويب برقم:${tabId}`);
}
}
}
});
مثال على امتدادات Chrome في ملف manifest.json
بمجرد إرفاق المصحح بأي تبويب، ستظهر لافتة تحذير على جميع التبويبات لضمان وعي المستخدم بإجراءات الامتداد التي قد تُعتبر متطفلة. إذا نقر المستخدم زر الإلغاء، فسوف يُجبر على فصل المصحح عن جميع التبويبات التي أُرفق بها.

يمكنك أيضًا مراقبة متى يتم فصل المصحح، سواء بسبب إلغاء المستخدم أو إغلاق التبويب، واستخدام هذا الحدث لأغراض التنظيف. لا حاجة لإرفاق أو إزالة مستمعين إلا لغرض تجنب ظهور لافتة التحذير بشكل مستمر. من المهم، أنه لا يمكن إغلاق هذه اللافتة دون مقاطعة اتصال المصحح.
chrome.debugger.onDetach.addListener((source, reason) => {
if (source.tabId && debuggerAttached[source.tabId]) {
console.log(`تم فصل المصحح عن التبويب برقم:${source.tabId} بسبب ${reason}`);
delete debuggerAttached[source.tabId];
if (Object.keys(debuggerAttached).length === 0) {
// تم إزالة جميع المصححات المرفقة، لذا قم بإزالة المستمع
console.log(`تمت إزالة مستمع المصحح من التبويب برقم:${source.tabId}`);
chrome.debugger.onEvent.removeListener(handleDebuggerEvent);
}
}
});
مثال على التصحيح في ملف json.
من الضروري عدم إرفاق أكثر من مستمع واحد للمصحح إلا إذا كنت تنوي استخدام عدة ردود نداء للمستمع. المحاولات المبكرة لإرفاق مستمع في كل مرة يتم فيها إرفاق مصحح بتبويب أدت إلى فوضى، حيث تم استلام عدة رسائل مكررة — واحدة لكل مستمع مضاف لأن المصحح يعمل ككائن عام.
الآن بعد أن قمت بإرفاق مصحح وقمت بإعداد مستمع لتلقي الأحداث، يمكنك استكشاف قائمة الرسائل هنا للعثور على الرسائل ذات الصلة باحتياجاتك. لأغراضي، هناك أربعة كانت مثيرة للاهتمام:
function handleDebuggerEvent(debuggeeId, message, params) {
{
switch (message) {
case 'Network.webSocketCreated':
traceSocketCreate(debuggeeId, params);
break;
case 'Network.webSocketClosed':
traceSocketClose(debuggeeId, params);
break;
case 'Network.webSocketFrameSent':
traceSocketSend(debuggeeId, params);
break;
case 'Network.webSocketFrameReceived':
traceSocketReceive(debuggeeId, params);
break;
default:
}
}
}
مثال على الأحداث التي تم التقاطها بواسطة المصحح.
debuggeeId هو كائن يجب أن يحتوي على `tabId` الخاص بالتبويب الذي نشأت منه بيانات WebSocket. كائن الـ `params` بداخله سيحتوي على معلومات ذات صلة، تهم احتياجاتك الخاصة بشكل خاص:
FrameSent/FrameReceived
- requestId — المعرف الفريد لاتصال WebSocket
- response — حمولة الإرسال/الاستقبال
تم الإنشاء
- requestId — المعرف الفريد لاتصال WebSocket
- url — نقطة النهاية الوجهة لـ WebSocket
مغلق
- requestId — المعرف الفريد لاتصال WebSocket
لذا فإن الجمع بين `tabId` + `requestId` يتيح تتبعًا فريدًا للبيانات القادمة من أي WebSocket معين في أي تبويب مطلوب، مما يسهل المراقبة الدقيقة والتحليل.
الآن بعد أن قمت بإرفاق مصحح، وإعداد مستمع، وتأسيس تدفق من الرسائل التي تُمرر إلى `traceSocketSend` و`traceSocketReceive`، من السهل جدًا إخراج المحتوى المطلوب في سجلات وحدة التحكم، سواء كسلاسل نصية أو ككائنات، لأغراض التحليل أو التصحيح.
تعزيز تصحيح WebSocket من خلال تكامل عامل الخدمة
يمكنك الوصول إلى وحدة تحكم عامل الخدمة في أي وقت عن طريق الانتقال إلى صفحة الامتدادات أو صفحة التفاصيل الخاصة بامتدادك. ببساطة انقر على رابط عامل الخدمة، وستظهر وحدة التحكم، مما يتيح لك عرض السجلات وتصحيح الأخطاء في الوقت الحقيقي.


إذا كنت تطور فقط وتحتاج إلى بيانات إخراج خام، فقد يكون ذلك كافيًا لتصحيح الأخطاء أو الاستعمالات الأخرى. ولكن يمكنك أن تأخذ خطوة أبعد بإرسال هذه البيانات إلى الواجهة الأمامية، وعرضها في نافذة، وتحسين العرض بشكل كبير باستخدام React أو إطار عمل واجهة المستخدم المفضل لديك.
الخطوات النهائية في تصحيح اتصال WebSocket
لا يستطيع عامل الخدمة الوصول مباشرة إلى DOM، لذا يجب عليك التواصل مع صفحات المحتوى في الواجهة الأمامية عبر المستمعين. هذا يتطلب بعض التنسيق لضمان وجود مستمع جاهز قبل أن تبدأ في إرسال الرسائل. وإلا، قد ينتهي بك الأمر بإرسال الرسائل إلى العدم، مما يؤدي إلى العديد من أخطاء وحدة التحكم. بدلاً من ذلك، يمكنك تنفيذ آلية مصافحة لتأكيد متى تكون نافذة المحتوى نشطة وجاهزة لتلقي الرسائل.
فتح نافذة:
let popupWindowId;
chrome.windows.create({
'url': chrome.runtime.getURL("../html/content.html"),
'type': 'popup', // أو 'normal' لنافذة عادية
'width': 400,
'height': 600
}).then((window) => {
popupWindowId = window.id;
});
مثال على JavaScript المستخدم في تصحيح الأخطاء.
يرسل سكريبت `content.js`، الذي يتم تحميله في `html/content.html`، في البداية رسالة إلى عامل الخدمة تشير إلى أنه يعمل ويعمل بسلاسة:
chrome.runtime.sendMessage({ type: "CONTENT_SCRIPT_READY" });
مثال على JavaScript المستخدم في تصحيح الأخطاء.
في عامل الخدمة الخاص بي، أعددت مستمعين لكل من نافذة منبثقة للامتداد ونافذة المحتوى:
let contentWindowReady = false;
let popupWindowReady = false;
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
switch (request.type) {
case "POPUP_SCRIPT_READY":
popupWindowReady = true;
break;
case "CONTENT_SCRIPT_READY":
contentWindowReady = true;
break;
default:
console.log(`نوع رسالة غير معروف: ${request.type}`);
}
});
رمز لإعداد المستمعين في JavaScript.
بمجرد أن ترسل لنا نافذة المحتوى رسالة، يمكننا بأمان إرسال الرسائل إليها من `traceSocketSend` و`traceSocketReceive` كما يلي:
if (contentWindowReady) {
chrome.runtime.sendMessage({
destination: "CONTENT",
type: "MESSAGE_SENT",
content: `تم الإرسال => الأمر:${command}, req_id:${payload.req_id}`});
}
إرسال الرسائل في JavaScript.
محتوى sendMessage معرف بالكامل بواسطة المستخدم. ضع في اعتبارك أن أي مستمع يتم إعداده في الامتداد سيتلقى جميع الرسائل المرسلة. لذلك، يتيح تحديد الوجهات والأنواع لك إنشاء بروتوكول المراسلة الخاص بك.
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.destination === 'CONTENT') {
switch(request.type) {
case "EXTENSION_INFO":
document.getElementById('version-placeholder').textContent = request.version;
document.getElementById('name-placeholder').textContent = request.name;
break;
case "MESSAGE_SENT":
var newDiv = document.createElement("div");
newDiv.id = "tableItemSent";
newDiv.innerHTML = request.content;
document.getElementById("table").appendChild(newDiv);
break;
case "MESSAGE_RECEIVED":
var newDiv = document.createElement("div");
newDiv.id = "tableItemReceived";
newDiv.innerHTML = request.content;
document.getElementById("table").appendChild(newDiv);
break;
default:
console.log(`نوع رسالة غير معروف: ${request.type}`);
}
}
});
مثال على تحديد الوجهات في JavaScript.
نأمل أن تكون هذه المعلومات مفيدة وتساعدك على تشغيل امتداد تصحيح WebSocket في Chrome بسرعة وبأقل جهد. إطار عمل الامتدادات قوي جدًا ويمكّن مجموعة واسعة من الوظائف.
حول المؤلف
كيت ويلكنز مهندسة رئيسية في Deriv، تحب التعلم المستمر في جميع المجالات، وليس فقط في التكنولوجيا، بل من لغات الكمبيوتر وأطر العمل والتقنيات الأساسية إلى الإبحار والبناء.
لا يوجد محتوى لترجمته.