إيه هي إضافات كروم دي أصلاً؟
بص يا سيدي، إضافات كروم دي زي برامج صغيرة كده بتركبها على متصفح كروم بتاعك عشان تضيفله حاجات جديدة. يعني مثلاً:
- تخلي الإعلانات المزعجة دي تختفي خالص
- تترجملك الصفحات اللي بتتفرج عليها لو مش فاهم حاجة
- تحفظلك الباسوردات بتاعتك عشان متنساهاش
- تغير شكل المواقع اللي بتدخل عليها
- تعملك شغل روتيني كل شوية بدالك
طب إيه هستفيده من المقالة دي؟
هنتعلم كل حاجة من الألف للياء:
- هنعرف إزاي الإضافة متركبة من جوه
- هنفهم الملف الأساسي اللي اسمه manifest.json ده
- هنتكلم عن السكريبتات اللي بتشتغل في الباك جراوند
- وكمان السكريبتات اللي بتشتغل جوه صفحات الويب نفسها
- هنشوف إزاي الحتت دي بتكلم بعضها
- هنعمل صفحة للإعدادات بتاعة الإضافة
- هنخزن الداتا بتاعتنا فين وإزاي
- هنعمل شكل حلو للإضافة
- هنشوف إزاي نختبرها ونصلح الأخطاء
- وأخيراً هنرفعها على المتجر بتاع جوجل
الـstructure الخاص بالإضافة
الإضافة بتاعتنا دي متركبة من شوية ملفات، تعالى نشوف أهمهم:
extension-folder/ ├── manifest.json # البطاقة الشخصية للإضافة ├── background.js # سكريبت الخلفية ├── content.js # سكريبت المحتوى ├── popup/ │ ├── popup.html # واجهة الإضافة │ ├── popup.js # منطق واجهة الإضافة │ └── popup.css # تنسيق واجهة الإضافة ├── options/ │ ├── options.html # صفحة الإعدادات │ ├── options.js # منطق الإعدادات │ └── options.css # تنسيق الإعدادات └── images/ ├── icon16.png # أيقونة صغيرة ├── icon48.png # أيقونة متوسطة └── icon128.png # أيقونة كبيرة
manifest.json: ده زي البطاقة الشخصية بتاعة الإضافة، بيقول كل حاجة عنها يعني:
- اسمها وإصدارها وبتعمل إيه
- المسموح ليها تعمله وإيه لأ
- السكريبتات بتاعتها هتشتغل فين وإمتى
- إيه الملفات اللي المتصفح هيعرف يوصلها
ملفات الجافاسكريبت:
- background.js: ده بيشتغل ورا الكواليس طول ما المتصفح شغال
- content.js: ده بيشتغل جوه صفحات النت نفسها
- popup.js: ده بيتحكم في الشكل اللي بيظهر لما تدوس على الإضافة
- options.js: ده بيتحكم في صفحة الإعدادات بتاعتك
ملفات HTML و CSS:
- popup.html/css: دول مسؤولين عن شكل وتنسيق الويندو اللي بتطلعلك لما تدوس على أيقونة الإضافة
- options.html/css: دول مسؤولين عن شكل وتنسيق صفحة الإعدادات
الصور والأيقونات:
- أيقونات بمقاسات مختلفة (16×16, 48×48, 128×128)
- أي صور تانية الإضافة محتاجاها
الملف الأساسي manifest.json ؟
ده أهم ملف في الموضوع كله، لازم يكون موجود في أول الفولدر بتاع الإضافة. تعالى نشوف مثال عليه:
{ "manifest_version": 3, // لازم يكون 3 في كروم الجديد لأن الإصدار 2 مش مدعوم بعد 2023 "name": "Awesome Extension", // اسم الإضافة - لازم يكون واضح ومعبر وأقل من 45 حرف "version": "1.0", // رقم الإصدار - يفضل يتبع Semantic Versioning (MAJOR.MINOR.PATCH) "description": "This is a cool extension you'll love", // وصف مختصر - لازم يكون واضح وأقل من 132 حرف "icons": { // الأيقونات - لازم تكون PNG وبأحجام محددة "16": "images/icon16.png", // للتابات والفافيكون "48": "images/icon48.png", // للقائمة وصفحة الإضافات "128": "images/icon128.png" // لمتجر كروم وصفحة التثبيت }, "action": { // إعدادات زر شريط الأدوات (حل محل browser_action في MV2) "default_popup": "popup.html", // الصفحة اللي بتظهر - مينفعش يكون حجمها كبير "default_title": "Click here!" // تلميح الماوس - يفضل يكون قصير ومفيد }, "background": { // سكريبت الخلفية - بيتم تحميله وإيقافه حسب الحاجة "service_worker": "background.js" // لازم يكون Service Worker في MV3 }, "content_scripts": [ // سكريبتات بتتحقن في صفحات الويب { "matches": ["https://www.facebook.com/*"], // نمط URL - يدعم wildcards "js": ["content.js"], // ملفات JS - بتتنفذ بالترتيب "css": ["style.css"], // ملفات CSS - بتتطبق فوراً "run_at": "document_end" // توقيت التنفيذ - في options: document_start/end/idle } ], "permissions": [ // الصلاحيات - اطلب أقل عدد ممكن "activeTab", // للوصول للتاب الحالي فقط "storage", // للتخزين المحلي والسحابي "scripting", // لتنفيذ JS في التابات "tabs", // للتحكم في كل التابات "notifications" // لإظهار إشعارات سطح المكتب ], "host_permissions": [ // أذونات المواقع - حددها بدقة "https://www.google.com/" // يدعم wildcards و match patterns ], "options_page": "options.html", // صفحة الإعدادات - اختيارية "web_accessible_resources": [ // موارد متاحة لصفحات الويب { "resources": ["style.css", "images/"], // الملفات المسموحة - يدعم wildcards "matches": ["https://www.facebook.com/*"] // المواقع المصرح لها } ] }
كل سطر في الملف ده ليه معنى:
- “manifest_version”: ده رقم الإصدار بتاع الملف نفسه، لازم يبقى 3 دلوقتي (مينفعش نستخدم 2 لأن جوجل بطلت تدعمه)
- “name” و “version”: دول اسم الإضافة ورقم الإصدار بتاعها (لازم يكونوا واضحين ومعبرين)
- “description”: ده وصف قصير للإضافة (مهم يكون دقيق وبيشرح الفايدة الرئيسية)
- “icons”: دي الأيقونات بتاعة الإضافة بأحجام مختلفة (16 للتابات، 48 للقائمة، 128 للمتجر)
- “action”: ده بيحدد إيه اللي هيحصل لما تدوس على أيقونة الإضافة (زي فتح بوب أب أو تنفيذ أمر معين)
- “background”: ده السكريبت اللي هيشتغل في الخلفية طول الوقت (بيتحكم في الإضافة وبيعمل المهام المستمرة)
- “content_scripts”: دول السكريبتات اللي هتشتغل جوه صفحات الويب نفسها (بتقدر تعدل في المحتوى وتضيف ميزات)
- “permissions”: دي الصلاحيات اللي الإضافة محتاجاها (زي التخزين والتنبيهات، اطلب بس اللي محتاجه فعلاً)
- “host_permissions”: دي المواقع اللي الإضافة هتقدر تشتغل عليها (حددها بدقة عشان الأمان)
- “options_page”: دي صفحة الإعدادات بتاعة الإضافة (خليها سهلة وواضحة للمستخدم)
- “web_accessible_resources”: دي الملفات اللي ممكن الويب يوصلها من الإضافة (زي الصور والستايلات، حددها كويس)
- “commands”: ده بيحدد الشورت كتس بتاعة الإضافة (اختار مفاتيح سهل المستخدم يفتكرها)
- “default_locale”: ده بيحدد اللغة الأساسية واللغات المدعومة (مهم للإضافات العالمية)
- “offline_enabled”: ده بيحدد إذا كانت الإضافة بتشتغل من غير نت ولا لأ
- “update_url”: ده لينك التحديثات التلقائية (لو هتنشر الإضافة برا متجر كروم)
سكريبتات الباك جراوند (Service Workers)
تخيل معايا إن سكريبتات الباك جراوند دي زي المخ بتاع الإضافة - بتفضل شغالة في الخلفية وبتتحكم في كل حاجة. يعني هي اللي:
- بتحفظ كل البيانات وبتفتكر إعدادات المستخدم
- بتعرف لما حد يثبت الإضافة أو يعمل تحديث وتتصرف
- بتتواصل مع كل أجزاء الإضافة وتنسق بينهم
- بتعمل شغل مهم في الباك جراوند زي إنها تبعت تنبيهات
خلينا نشوف مثال عملي يوضح الكلام ده:
// background.js // لما حد يثبت الإضافة أو يحدثها chrome.runtime.onInstalled.addListener(({ reason, previousVersion }) => { if (reason === 'install') { console.log("أهلاً بيك! الإضافة اتثبتت لأول مرة"); // نظبط الإعدادات الأساسية chrome.storage.sync.set({ theme: 'light', notifications: true }); } else if (reason === 'update') { console.log(`تم التحديث من نسخة ${previousVersion}`); // نعمل اللي محتاجينه للنسخة الجديدة } }); // لما المستخدم يدوس على أيقونة الإضافة chrome.action.onClicked.addListener(async (tab) => { try { // نتأكد إن التاب مسموح نشتغل عليه if (!tab.url.startsWith('chrome://')) { // نضيف واجهة المستخدم في الصفحة await chrome.scripting.executeScript({ target: { tabId: tab.id }, function: injectUI, }); // نغير شكل الأيقونة عشان نعرف إنها شغالة await chrome.action.setIcon({ tabId: tab.id, path: { 16: 'icons/active-16.png', 48: 'icons/active-48.png' } }); } } catch (error) { console.error("في مشكلة حصلت:", error); // نقول للمستخدم إن في مشكلة chrome.notifications.create({ type: 'basic', iconUrl: 'icons/error.png', title: 'عندنا مشكلة', message: error.message }); } }); // الواجهة اللي هتظهر للمستخدم function injectUI() { const dialog = document.createElement('dialog'); dialog.innerHTML = ` <h2>أهلاً بيك!</h2> <button onclick="this.parentElement.close()">قفل</button> `; document.body.appendChild(dialog); dialog.showModal(); // نستنى لو المستخدم عمل حاجة في الصفحة window.addEventListener('message', (event) => { if (event.source === window && event.data.type === 'EXTENSION_ACTION') { // نتصرف بناءً على اللي المستخدم عمله } }); } // نعمل مزامنة كل شوية chrome.alarms.create('sync', { periodInMinutes: 30 }); chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === 'sync') { // نزامن البيانات syncData(); } }); // نراقب لو حد غير في الإعدادات chrome.storage.onChanged.addListener((changes, namespace) => { for (let [key, { oldValue, newValue }] of Object.entries(changes)) { console.log(`الإعداد ${key} اتغير من ${oldValue} لـ ${newValue}`); } });
وفي كمان حاجات تانية ممكن نعملها زي:
- التحكم في طلبات النت اللي بتطلع من الإضافة
- نشغل عمليات تقيلة في الباك جراوند
- نربط الإضافة بخدمات على النت
- نخلي الإضافة خفيفة وسريعة
- نتعامل مع أي مشاكل بشكل محترف
- نخلي الإضافة تشتغل حتى من غير نت
سكريبتات المحتوى (Content Scripts)
سكريبتات المحتوى (Content Scripts) هي جزء أساسي في تطوير إضافات المتصفح. هذه السكريبتات تعمل داخل سياق صفحات الويب مباشرةً، مما يتيح لك التفاعل مع محتوى الصفحة وتعديله.
التكوين في manifest.json
لتفعيل سكريبت المحتوى، يجب أولاً تعريفه في ملف manifest.json:
{ "content_scripts": [ { "matches": ["*://*.example.com/*"], "js": ["content.js"], "css": ["styles.css"], "run_at": "document_idle" } ] }
مثال أساسي لسكريبت محتوى
هذا مثال بسيط لسكريبت محتوى يغير لون خلفية الصفحة:
console.log("تم تفعيل سكريبت المحتوى"); document.body.style.backgroundColor = "lightblue"; // تغيير اللون كل 5 ثواني setInterval(() => { const colors = ["lightblue", "lightgreen", "lightpink", "lightyellow"]; const randomColor = colors[Math.floor(Math.random() * colors.length)]; document.body.style.backgroundColor = randomColor; }, 5000);
هديك نبذة عن إمكانياته
1. الوصول للصفحة على طول
- تقدر تستخدم كل حاجة في الصفحة زي querySelector و getElementById
- تتعامل مع العناصر في الصفحة - تضيف وتشيل وتعدل براحتك
- تقدر تسمع لما حد يدوس أو يسكرول في الصفحة
- تغير الشكل والألوان بتاعة أي حاجة في الصفحة
2. بيشتغل لوحده
- السكريبت بيشتغل على طول لما الصفحة تفتح من غير ما تعمل حاجة
- تقدر تتحكم امتى يشتغل من خلال
run_at
:document_start
: قبل ما الصفحة تحملdocument_end
: بعد ما الصفحة تحملdocument_idle
: بعد ما كل حاجة تخلص
- ممكن تخليه يشتغل في صفحات معينة بس
3. تحديد المواقع اللي هيشتغل عليها
- تحط المواقع اللي عايزها في
matches
جوة manifest.json - في طرق كتير زي:
*://*.example.com/*
: كل الصفحات في موقع معينhttp://*/*
: كل المواقع العاديةhttps://*/*
: كل المواقع المؤمنة
- ممكن تستثني مواقع معينة بـ
exclude_matches
- تقدر تحط قواعد معقدة عشان تحدد الصفحات بالظبط
- استخدم
chrome.runtime.sendMessage
عشان تبعت رسايل للباك جراوند
متنساش تحط الصلاحيات المطلوبة في manifest.json
مثال متقدم: مراقبة تغييرات الصفحة
const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { // معالجة العناصر الجديدة mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { // تطبيق التغييرات على العناصر الجديدة customizeElement(node); } }); } } }); observer.observe(document.body, { childList: true, subtree: true }); function customizeElement(element) { // تطبيق التخصيصات المطلوبة element.style.border = '1px solid blue'; }
نظام الرسايل (Message Passing)
هو إيه نظام الرسايل ده؟
ببساطة، ده نظام بيخلي أجزاء الإضافة بتاعتك تتكلم مع بعض. زي ما لو عندك جروب واتساب، كل حد فيه بيبعت رسايل للتاني 😄
ليه محتاجين نظام الرسايل؟
عشان:
- نخلي الكونتنت سكريبت يكلم الباك جراوند
- نبعت داتا من البوب أب للباك جراوند
- نعمل أي اتصال بين أي جزئين في الإضافة
أنواع الرسايل
النوع الأول: رسالة واحدة (One-Time Message)
ده زي لما تبعت رسالة لصاحبك وتستنى رده. بص على المثال ده:
// في الكونتنت سكريبت // في الكونتنت سكريبت - بنبعت رسالة للباك جراوند عشان نجيب داتا chrome.runtime.sendMessage({ type: "getData", request: "get statistics" }, (response) => { console.log("Got response:", response); }); // في الباك جراوند - بنستقبل الرسالة ونرد عليها chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { // بنشوف لو الرسالة نوعها getData if (message.type === "getData") { // بنرد بأوبجكت فيه الداتا اللي عايزينها sendResponse({ status: "success", data: { visits: 100, views: 500 } }); } return true; // لازم نرجع true عشان نقدر نبعت الرد بعدين });
النوع التاني: محادثة مستمرة (Connection)
ده زي ما تفتح شات مع صاحبك وتفضلوا تتكلموا. شوف كده:
// في الكونتنت سكريبت - بنفتح اتصال مع الباك جراوند const port = chrome.runtime.connect({name: "chat"}); // بنستمع للرسايل اللي جاية من الباك جراوند port.onMessage.addListener((message) => { console.log("New message received:", message); }); // بنبعت رسالة للباك جراوند port.postMessage({ type: "greeting", content: "How are you basha?" }); // في الباك جراوند - بنستقبل الاتصال من الكونتنت سكريبت chrome.runtime.onConnect.addListener((port) => { // بنستمع للرسايل اللي جاية من الكونتنت سكريبت port.onMessage.addListener((message) => { // لو الرسالة نوعها تحية، بنرد عليها if (message.type === "greeting") { port.postMessage({ type: "reply", content: "El7amdullah, enta 3amel eh?" }); } }); });
طريقة حلوة للتعامل مع الرسايل (ده ليك مخصوص عشان وصلت هنا 😎)
class MessageManager { constructor() { // بنعمل اوبجكت فيه كل الفانكشنز اللي هتتنفذ لما نستقبل رسايل this.handlers = { 'getData': this.getData, 'saveData': this.saveData, 'doSomething': this.doSomething }; this.listenToMessages(); } listenToMessages() { // بنسمع للرسايل اللي جاية ونشوف هننفذ ايه chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (this.handlers[message.type]) { this.handlers[message.type](message).then(sendResponse); return true; } }); } async getData(message) { // هنا بنجيب الداتا اللي محتاجينها return { status: "success", data: {} }; } async saveData(message) { // هنا بنحفظ الداتا اللي جاية return { status: "saved successfully" }; } async doSomething(message) { // هنا بنعمل اي حاجة تانية محتاجينها return { status: "completed" }; } }
نصايح مهمة (عشان متقعش في مشاكل 😅)
تأكد من الرسايل وصلت
// طريقة آمنة لإرسال رسالة function sendMessageSafely(message) { // بنعمل وعد جديد عشان نتأكد ان الرسالة وصلت return new Promise((resolve, reject) => { // بنبعت الرسالة للباك جراوند chrome.runtime.sendMessage(message, response => { // لو في مشكلة حصلت if (chrome.runtime.lastError) { console.error("Error occurred:", chrome.runtime.lastError); reject(chrome.runtime.lastError); } else { // لو كله تمام نرجع الرد resolve(response); } }); }); }
متبعتش داتا كبيرة
- لو عندك داتا كبيرة، قسمها على أجزاء
- أو استخدم
chrome.storage
بدل الرسايل
اعمل تايم آوت للرسايل
async function sendMessageWithTimeout(message, timeout = 5000) { // بنعمل وعد بيبعت الرسالة بطريقة آمنة const messagePromise = sendMessageSafely(message); // بنعمل وعد تاني بيرفض لو عدى الوقت المحدد const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject("Timeout exceeded"), timeout); }); // بنشوف مين هيخلص الأول - الرسالة ولا الوقت return Promise.race([messagePromise, timeoutPromise]); }
مثال عملي وبسيط
تخيل إنك عايز تعمل إضافة بتعد عدد الصور في أي صفحة وتحفظ الإحصائيات:
// في الكونتنت سكريبت // بنجيب عدد الصور في الصفحة const imageCount = document.querySelectorAll('img').length; // بنبعت رسالة للباك جراوند عشان يحفظ الاحصائيات chrome.runtime.sendMessage({ type: "save_stats", data: { url: window.location.href, images_count: imageCount, timestamp: new Date().toISOString() } }); // في الباك جراوند const statistics = {}; // بنستمع للرسايل اللي جاية chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "save_stats") { const url = message.data.url; statistics[url] = message.data; sendResponse({ status: "saved" }); } return true; });
- الرسايل نوعين: رسالة واحدة أو محادثة مستمرة
- لازم تتأكد إن الرسايل وصلت
- ممكن تعمل نظام متقدم للرسايل حسب احتياجك
- خلي بالك من حجم الداتا اللي بتبعتها
صفحة الإعدادات (Options Page)
1. تعريف الصفحة في manifest.json
أولاً، نحتاج لتعريف صفحة الإعدادات في ملف manifest.json:
{ "options_ui": { "page": "options.html", "open_in_tab": true, // فتح في تاب جديد "chrome_style": true // استخدام ستايل كروم } }
هذا التعريف يخبر متصفح كروم:
- أي صفحة سيتم عرضها في الإعدادات
- كيف سيتم عرضها (في تاب أو نافذة منبثقة)
- هل ستستخدم ستايل كروم الافتراضي
2. HTML
نقوم بإنشاء صفحة الإعدادات الأساسية:
<!DOCTYPE html> <html dir="rtl" lang="ar"> <head> <meta charset="UTF-8"> <title>إعدادات الإضافة</title> <link rel="stylesheet" href="options.css"> </head> <body> <div class="container"> <!-- قسم إعدادات المظهر --> <section class="settings-section"> <h2>المظهر</h2> <!-- إعداد لون الخلفية --> <div class="setting-item"> <label>لون الخلفية: <input type="color" id="backgroundColor"> </label> </div> <!-- إعداد حجم الخط --> <div class="setting-item"> <label>حجم الخط: <select id="fontSize"> <option value="small">صغير</option> <option value="medium">متوسط</option> <option value="large">كبير</option> </select> </label> </div> </section> <!-- قسم الإعدادات المتقدمة --> <section class="settings-section"> <h2>إعدادات متقدمة</h2> <!-- إعداد الإشعارات --> <div class="setting-item"> <label> <input type="checkbox" id="notifications"> تفعيل الإشعارات </label> </div> <!-- إعداد فترة التحديث --> <div class="setting-item"> <label>فترة التحديث (دقائق): <input type="number" id="updateInterval" min="1" max="60"> </label> </div> </section> <!-- أزرار التحكم --> <div class="actions"> <button id="save" class="primary">حفظ الإعدادات</button> <button id="reset" class="secondary">إعادة الضبط</button> </div> </div> <script src="options.js"></script> </body> </html>
3. CSS
نضيف التنسيقات في ملف options.css:
/* تنسيقات عامة */ body { font-family: system-ui, -apple-system, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; } /* تنسيق الحاوية الرئيسية */ .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* تنسيق أقسام الإعدادات */ .settings-section { margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid #eee; } /* تنسيق عناصر الإعدادات */ .setting-item { margin: 12px 0; } /* تنسيق الأزرار */ .actions { display: flex; gap: 10px; margin-top: 20px; } button { padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; } .primary { background-color: #1a73e8; color: white; } .secondary { background-color: #f1f3f4; color: #202124; }
4. JavaScript
نكتب المنطق البرمجي في ملف options.js:
// الإعدادات الافتراضية const DEFAULT_SETTINGS = { backgroundColor: '#ffffff', fontSize: 'medium', notifications: true, updateInterval: 5 }; // دالة تحميل الإعدادات function loadSettings() { chrome.storage.sync.get(DEFAULT_SETTINGS, (settings) => { // تعيين قيم الإعدادات في الواجهة document.getElementById('backgroundColor').value = settings.backgroundColor; document.getElementById('fontSize').value = settings.fontSize; document.getElementById('notifications').checked = settings.notifications; document.getElementById('updateInterval').value = settings.updateInterval; }); } // دالة حفظ الإعدادات function saveSettings() { // جمع القيم من الواجهة const settings = { backgroundColor: document.getElementById('backgroundColor').value, fontSize: document.getElementById('fontSize').value, notifications: document.getElementById('notifications').checked, updateInterval: parseInt(document.getElementById('updateInterval').value) }; // حفظ الإعدادات chrome.storage.sync.set(settings, () => { showNotification('تم حفظ الإعدادات بنجاح!'); // إرسال إشعار للـ background script chrome.runtime.sendMessage({ type: 'SETTINGS_UPDATED', settings: settings }); }); } // دالة إعادة الضبط function resetSettings() { if (confirm('هل أنت متأكد من إعادة ضبط جميع الإعدادات؟')) { chrome.storage.sync.set(DEFAULT_SETTINGS, () => { loadSettings(); showNotification('تم إعادة ضبط الإعدادات'); }); } } // دالة إظهار الإشعارات function showNotification(message) { const notification = document.createElement('div'); notification.className = 'notification'; notification.textContent = message; document.body.appendChild(notification); // إزالة الإشعار بعد 3 ثواني setTimeout(() => { notification.remove(); }, 3000); }
5. إعداد الأحداث Events
نضيف الأحداث في نهاية ملف options.js:
// تحميل الإعدادات عند فتح الصفحة document.addEventListener('DOMContentLoaded', loadSettings); // ربط الأزرار بالدوال المناسبة document.getElementById('save').addEventListener('click', saveSettings); document.getElementById('reset').addEventListener('click', resetSettings); // مراقبة التغييرات في الإعدادات document.querySelectorAll('input, select').forEach(element => { element.addEventListener('change', () => { // إضافة class للإشارة إلى وجود تغييرات غير محفوظة document.getElementById('save').classList.add('modified'); }); });
تخزين البيانات (Storage API)
تعريف في manifest.json
{ "permissions": [ "storage" ] }
- يجب إضافة صلاحية
storage
في ملف manifest - بدون هذه الصلاحية لن تستطيع استخدام API التخزين
أنواع التخزين
1. التخزين المتزامن (chrome.storage.sync)
// تخزين البيانات chrome.storage.sync.set({ userName: "أحمد", preferences: { theme: "dark", fontSize: 16 } }); // قراءة البيانات chrome.storage.sync.get(['userName', 'preferences'], (result) => { console.log(result.userName); // أحمد console.log(result.preferences); // {theme: "dark", fontSize: 16} });
- يتزامن مع حساب جوجل للمستخدم
- الحد الأقصى: 100 كيلوبايت
- مناسب للإعدادات والتفضيلات
2. التخزين المحلي (chrome.storage.local)
// تخزين البيانات chrome.storage.local.set({ cachedData: largeDataObject, lastUpdate: new Date().getTime() }); // قراءة البيانات chrome.storage.local.get(['cachedData'], (result) => { console.log(result.cachedData); });
- تخزين محلي على الجهاز فقط
- الحد الأقصى: 10 ميجابايت
- مناسب للبيانات الكبيرة والمؤقتة
العمليات الأساسية
1. التخزين (set)
// تخزين قيمة واحدة chrome.storage.sync.set({ key: "value" }, () => { console.log("تم التخزين بنجاح"); }); // تخزين عدة قيم chrome.storage.sync.set({ key1: "value1", key2: "value2", key3: { nestedKey: "nestedValue" } }, () => { console.log("تم تخزين كل القيم"); });
2. القراءة (get)
// قراءة قيمة واحدة chrome.storage.sync.get(['key'], (result) => { console.log(result.key); }); // قراءة عدة قيم chrome.storage.sync.get(['key1', 'key2'], (result) => { console.log(result.key1, result.key2); }); // قراءة كل القيم chrome.storage.sync.get(null, (result) => { console.log(result); });
3. الحذف (remove)
// حذف قيمة واحدة chrome.storage.sync.remove('key', () => { console.log("تم حذف القيمة"); }); // حذف عدة قيم chrome.storage.sync.remove(['key1', 'key2'], () => { console.log("تم حذف القيم"); });
4. مسح كل البيانات (clear)
chrome.storage.sync.clear(() => { console.log("تم مسح كل البيانات"); });
أمثلة عملية
1. مثال: إدارة إعدادات المستخدم
// الإعدادات الافتراضية const defaultSettings = { theme: 'light', fontSize: 14, notifications: true, language: 'ar' }; // حفظ الإعدادات function saveSettings(settings) { return new Promise((resolve, reject) => { chrome.storage.sync.set({ userSettings: settings }, () => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(); } }); }); } // قراءة الإعدادات function loadSettings() { return new Promise((resolve, reject) => { chrome.storage.sync.get(['userSettings'], (result) => { if (chrome.runtime.lastError) { reject(chrome.runtime.lastError); } else { resolve(result.userSettings || defaultSettings); } }); }); } // استخدام الدوال async function updateSettings(newSettings) { try { await saveSettings(newSettings); console.log("تم حفظ الإعدادات بنجاح"); } catch (error) { console.error("حدث خطأ:", error); } }
2. مثال: مراقبة التغييرات
// بنراقب التغييرات اللي بتحصل في التخزين chrome.storage.onChanged.addListener((changes, namespace) => { for (let key in changes) { let change = changes[key]; console.log(` Change Key: ${key} Old Value: ${change.oldValue} New Value: ${change.newValue} Storage Type: ${namespace} `); } });
واجهات المستخدم (UI) المتقدمة
ممكن تستخدم زي React أو Vue عشان تعمل واجهة مستخدم جامدة. مثال بسيط باستخدام React:
// بوب_أب.js import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; function PopUp() { const [color, setColor] = useState(''); useEffect(() => { chrome.storage.sync.get(['backgroundColor'], (result) => { if (result.backgroundColor) { setColor(result.backgroundColor); } }); }, []); const handleColorChange = (e) => { const newColor = e.target.value; setColor(newColor); chrome.storage.sync.set({ backgroundColor: newColor }); }; return ( <div> <h1>الإضافة الجامدة</h1> <p>اختار لون الخلفية يا معلم:</p> <input type="color" value={color} onChange={handleColorChange} /> </div> ); } ReactDOM.render(<PopUp />, document.getElementById('root'));
(Debugging)
- افتح Chrome DevTools واختار الإضافة بتاعتك من القائمة.
- استخدم console.log() كتير في الكود بتاعك.
- اعمل breakpoints في الكود عشان توقف التنفيذ عند نقط معينة.
نشر الإضافة على متجر كروم
- اعمل ملف zip للإضافة بتاعتك.
- روح على Chrome Web Store Developer Dashboard.
- ادفع 5 دولار مرة واحدة عشان تبقى developer.
- ارفع الملف zip بتاعك وحط كل البيانات المطلوبة.
- استنى لحد ما جوجل توافق على الإضافة.
نصائح مهمة جداً:
استخدم Offscreen Documents عشان تتعامل مع DOM بره الصفحة الرئيسية.
استعمل Declarative Net Request API عشان تتحكم في طلبات الشبكة بكفاءة.
اتعلم Cross-origin communication عشان تتعامل مع مواقع مختلفة.
طبق Security Best Practices عشان تحمي الإضافة بتاعتك من الهاكرز.
استخدم Web Workers عشان تشغل كود معقد من غير ما تعطل الصفحة.
جرب Service Workers عشان تخلي الإضافة تشتغل حتى لو الإنترنت مقطوع.
استخدم IndexedDB عشان تخزن بيانات كبيرة بكفاءة.
جرب WebAssembly عشان تشغل كود سريع جداً في الإضافة.