مشغّلو الخدمات من مصادر خارجية: تجربة الجلب من مصادر خارجية

الخلفية

مشغِّلو الخدمات يمنحون مطوّري برامج الويب القدرة على الاستجابة لطلبات الشبكة التي تجريها تطبيقات الويب، ما يتيح لهم مواصلة العمل حتى أثناء عدم الاتصال بالإنترنت، ومنع عدم الاتصال، وتنفيذ تفاعلات معقدة لذاكرة التخزين المؤقت، مثل إعادة التحقق من المحتوى القديم. مع ذلك، ارتبط عاملو الخدمة سابقًا بأصل معيّن، وبصفتك مالكًا لتطبيق ويب، تقع على عاتقك مسؤولية كتابة مشغّل الخدمات ونشره لاعتراض جميع طلبات الشبكة التي يجريها تطبيق الويب. في هذا النموذج، يكون كل مشغّل خدمات مسؤولاً عن معالجة الطلبات من مصادر متعددة، مثل واجهة برمجة تطبيقات تابعة لجهة خارجية أو لخطوط الويب.

ماذا لو كان لدى موفّر خارجي لواجهة برمجة تطبيقات، أو خطوط ويب، أو أي خدمة أخرى شائعة الاستخدام، القدرة على نشر مشغّل الخدمات الخاص به وتزويده بفرصة معالجة الطلبات المقدّمة من مصادر أخرى إلى المصدر؟ يمكن لمقدّمي الخدمات تنفيذ منطقتهم المخصّصة للشبكات والاستفادة من مثيل ذاكرة تخزين مؤقت واحد وموثوق به لتخزين ردودهم. والآن بفضل الجلب الأجنبي، أصبح هذا النوع من نشر مشغّلي الخدمات الخارجيين حقيقة واقعة.

إنّ نشر مشغّل خدمات يستخدم طريقة الجلب الخارجي أمر منطقي لأي مزود خدمة يتم الوصول إليه من خلال طلبات HTTPS من المتصفحات. ما عليك سوى التفكير في السيناريوهات التي يمكنك فيها توفير إصدار مستقل عن الشبكة من خدمتك، حيث يمكن للمتصفحات الاستفادة من ذاكرة التخزين المؤقت الشائعة للموارد. تشمل الخدمات التي يمكن أن تستفيد من هذا، على سبيل المثال لا الحصر:

  • موفِّرو واجهات برمجة التطبيقات الذين لديهم واجهات RESTful
  • موفِّرو الخطوط على الويب
  • موفِّرو خدمة التحليلات
  • مستضيفو الصور
  • شبكات عرض المحتوى العامة

لتتخيل، على سبيل المثال، أنك مقدم تحليلات. ومن خلال نشر عامل خدمة جلب أجنبي، يمكنك التأكد من وضع جميع الطلبات إلى خدمتك التي تفشل أثناء عدم اتصال المستخدم في قائمة الانتظار وإعادة تشغيلها عند عودة الاتصال. على الرغم من أنّه بإمكان عملاء الخدمة تنفيذ سلوك مشابه من خلال مشغِّلي الخدمات التابعين للطرف الأول، فإنّ تكليف كل عميل بكتابة منطق مخصّص لخدمتك ليس قابلاً للتطوير بالقدر نفسه مثل الاعتماد على مشغّل خدمة استرجاع خارجي مشترك تنشره.

المتطلبات الأساسية

الرمز المميز للإصدار التجريبي الأصلي

ولا يزال الجلب الخارجي يُعتبر تجريبيًا. ولمنع محاولة استخدام هذا التصميم مبكرًا قبل أن يتم تحديده بالكامل والاتفاق عليه من قِبل مورِّدي المتصفح، تم تنفيذه في الإصدار 54 من Chrome باعتباره مرحلة التجربة والتقييم. ما دامت عملية الجلب الخارجية لا تزال تجريبية، لاستخدام هذه الميزة الجديدة مع الخدمة التي تستضيفها، ستحتاج إلى طلب رمز مميّز مخصّص للمصدر المحدّد لخدمتك. يجب تضمين الرمز المميّز كعنوان استجابة HTTP في جميع الطلبات من مصادر متعددة للموارد التي تريد معالجتها من خلال الجلب الخارجي، وكذلك في استجابة مورد JavaScript لعامل الخدمة:

Origin-Trial: token_obtained_from_signup

ستنتهي الفترة التجريبية في آذار (مارس) 2017. بحلول ذلك الوقت، نتوقع أن نكون قد اكتشفنا أي تغييرات ضرورية لتثبيت الميزة، و (ونأمل) تفعيلها بشكل افتراضي. وإذا لم يكن الجلب الخارجي مفعَّلاً تلقائيًا خلال ذلك الوقت، ستتوقف الوظيفة المرتبطة بالرموز المميّزة الحالية لـ Origin Trial عن العمل.

لتسهيل تجربة طريقة الجلب الأجنبية قبل التسجيل للحصول على الرمز المميز الرسمي للإصدار التجريبي من نظام التشغيل Origin، يمكنك تجاوز المتطلبات في Chrome لجهاز الكمبيوتر المحلي من خلال الانتقال إلى chrome://flags/#enable-experimental-web-platform-features وتفعيل علامة "ميزات النظام الأساسي التجريبية للويب". يُرجى ملاحظة أنّ هذا الإجراء يجب أن يتم في كل مثيل من Chrome تريد استخدامه في التجارب المحلية، بينما ستكون الميزة متاحة لجميع مستخدمي Chrome باستخدام الرمز المميز لـ Origin Trial.

HTTPS

كما هو الحال مع جميع عمليات نشر مشغّلي الخدمات، يجب أن يتم الوصول إلى خادم الويب الذي تستخدمه لخدمة كل من مواردك والنص البرمجي لعامل الخدمة عبر HTTPS. بالإضافة إلى ذلك، لا ينطبق اعتراض الجلب الخارجي إلا على الطلبات التي تنشأ من صفحات مستضافة على مصادر آمنة، لذلك يحتاج عملاء خدمتك إلى استخدام HTTPS للاستفادة من تنفيذ الجلب الخارجي.

استخدام الجلب الأجنبي

بعد تفويت المتطلبات الأساسية، دعونا نتعمق في التفاصيل الفنية اللازمة لتشغيل عامل خدمة الجلب الأجنبي.

تسجيل مشغّل الخدمات

التحدي الأول الذي قد تواجهه هو كيفية تسجيل عامل الخدمة لديك. إذا كنت قد عملت مع عاملي الخدمة من قبل، فمن المحتمل أن تكون على دراية بما يلي:

// You can't do this!
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('service-worker.js');
}

ويكون رمز JavaScript هذا لتسجيل مشغّل خدمات الطرف الأول منطقيًا في سياق تطبيق الويب، ويتم تشغيله من خلال انتقال مستخدم إلى عنوان URL يمكنك التحكّم فيه. لكنه ليس أسلوبًا قابلاً للتطبيق لتسجيل عامل خدمات تابع لجهة خارجية، عندما يطلب متصفح التفاعل الوحيد مع الخادم لديك موردًا فرعيًا محددًا، وليس تنقل كامل. إذا طلب المتصفح مثلاً صورة من خادم شبكة توصيل المحتوى (CDN) الذي تتم صيانته، لا يمكنك إضافة مقتطف JavaScript هذا في البداية إلى استجابتك وتوقُّع تشغيله. مطلوب طريقة مختلفة لتسجيل مشغّل الخدمات، خارج سياق تنفيذ JavaScript العادي.

ويكون الحل في شكل عنوان HTTP يمكن لخادمك تضمينه في أي استجابة:

Link: </service-worker.js>; rel="serviceworker"; scope="/"

لنقسِّم هذا المثال إلى مكوّناته، مع الفصل بين كل منها بحرف ;.

  • يجب استخدام </service-worker.js> لتحديد مسار ملف مشغّل الخدمات (استبدِل /service-worker.js بالمسار المناسب للنص البرمجي). يتجاوب هذا مباشرةً مع سلسلة scriptURL التي سيتم تمريرها كمَعلمة أولى إلى navigator.serviceWorker.register(). يجب تضمين القيمة بأحرف <> (وفقًا لمتطلبات مواصفات العنوان Link)، وفي حال تقديم عنوان URL نسبي وليس عنوان URL مطلقًا، سيتم تفسيره على أنّه نسبي إلى موقع الاستجابة.
  • إنّ السمة rel="serviceworker" مطلوبة أيضًا، ويجب تضمينها بدون الحاجة إلى تخصيصها.
  • scope=/ هو إعلان نطاق اختياري، يعادل سلسلة options.scope التي يمكنك إدخالها كمَعلمة ثانية في navigator.serviceWorker.register(). بالنسبة إلى العديد من حالات الاستخدام، يمكنك استخدام النطاق التلقائي، لذلك لا تتردد في استبعاده ما لم تكن تعرف أنّك بحاجة إليه. يتم تطبيق القيود نفسها حول الحد الأقصى المسموح به للنطاق، بالإضافة إلى إمكانية تخفيف تلك القيود باستخدام العنوان Service-Worker-Allowed، في عمليات تسجيل عناوين Link.

تمامًا كما هو الحال في تسجيل مشغّل الخدمات "التقليدي"، سيؤدي استخدام العنوان Link إلى تثبيت عامل خدمات سيتم استخدامه في الطلب التالي الذي يتم إجراؤه على النطاق المسجَّل. وسيتم استخدام نص الاستجابة الذي يتضمن رأسًا خاصًا كما هو، ويكون متاحًا للصفحة على الفور، بدون انتظار انتهاء عامل الخدمة الخارجية من التثبيت.

لا تنسَ أنّه يتم حاليًا تنفيذ الجلب الخارجي على أنّه تجربة أصل، لذا ستحتاج إلى جانب عنوان استجابة الرابط إلى تضمين عنوان Origin-Trial صالح أيضًا. الحد الأدنى لمجموعة عناوين الاستجابة المطلوب إضافتها لتسجيل عامل خدمة الجلب الخارجي هو

Link: </service-worker.js>; rel="serviceworker"
Origin-Trial: token_obtained_from_signup

التسجيل لتصحيح الأخطاء

أثناء التطوير، ربما تريد التأكد من أن عامل خدمة الجلب الخارجي تم تثبيت الطلبات ويعالجها بشكل صحيح. هناك بعض الأشياء التي يمكنك التحقق منها في أدوات المطورين في Chrome للتأكد من أن الأمور تعمل كما هو متوقع.

هل يتم إرسال عناوين الاستجابة المناسبة؟

من أجل تسجيل مشغِّل خدمات الجلب الأجنبي، عليك ضبط عنوان رابط في استجابة لمورد مستضاف على نطاقك، كما هو موضَّح سابقًا في هذه المشاركة. وخلال فترة الإصدار التجريبي الأوّلي وعلى افتراض عدم ضبط chrome://flags/#enable-experimental-web-platform-features، يجب أيضًا ضبط عنوان الاستجابة Origin-Trial. يمكنك التأكّد من أنّ خادم الويب يضبط هذه العناوين من خلال الاطّلاع على الإدخال في لوحة الشبكة ضِمن "أدوات مطوري البرامج":

العناوين المعروضة في لوحة الشبكة.

هل تم تسجيل عامل خدمة الجلب الأجنبي بشكل صحيح؟

يمكنك أيضًا تأكيد تسجيل مشغّل الخدمات الأساسي، بما في ذلك نطاقه، من خلال الاطّلاع على القائمة الكاملة للعاملين في الخدمات في لوحة التطبيق ضِمن "أدوات مطوري البرامج". تأكَّد من تحديد الخيار "عرض الكل"، حيث سيظهر لك تلقائيًا مشغّلو الخدمات للمصدر الحالي فقط.

عامل خدمة الجلب الخارجي في لوحة &quot;التطبيقات&quot;.

معالِج حدث التثبيت

الآن بعد أن سجّلت مشغّل الخدمات التابع لجهة خارجية، سيحصل على فرصة للردّ على حدثَي install وactivate، تمامًا كأي مشغّل خدمات آخر. ويمكن الاستفادة من هذه الأحداث، على سبيل المثال، تعبئة ذاكرات التخزين المؤقت بالموارد المطلوبة أثناء حدث install، أو إزالة ذاكرات التخزين المؤقت القديمة في حدث activate.

بالإضافة إلى أنشطة التخزين المؤقت العادية لأحداث install، هناك خطوة إضافية مطلوبة داخل معالج أحداث install لدى مشغّل الخدمات التابع لجهة خارجية. يجب أن يستدعي الرمز registerForeignFetch()، كما في المثال التالي:

self.addEventListener('install', event => {
    event.registerForeignFetch({
    scopes: [self.registration.scope], // or some sub-scope
    origins: ['*'] // or ['https://example.com']
    });
});

هناك خياران للضبط، كلاهما مطلوب:

  • تأخذ scopes صفيفًا من سلسلة واحدة أو أكثر، ويمثّل كل منها نطاقًا للطلبات التي ستؤدي إلى تشغيل حدث foreignfetch. ولكن انتظر، قد تفكر وتقول: لقد حددت بالفعل نطاقًا أثناء تسجيل عاملي الخدمات. هذا صحيح، ولا يزال النطاق العام ذا صلة - يجب أن يكون كل نطاق تحدده هنا إما مساويًا للنطاق الفرعي لعامل الخدمة أو نطاقًا فرعيًا له. تسمح لك القيود الإضافية لتحديد النطاق هنا بنشر مشغّل خدمات لجميع الأغراض يمكنه التعامل مع أحداث fetch للطرف الأول (للطلبات التي يتم إجراؤها من موقعك الإلكتروني) وأحداث foreignfetch التابعة لجهات خارجية (للطلبات التي يتم إجراؤها من النطاقات الأخرى)، مع توضيح أنّه يجب فقط مجموعة فرعية من نطاقك الأكبر تشغيل foreignfetch. من الناحية العملية، إذا كنت تنشر مشغّل خدمات مخصّصًا للتعامل مع أحداث foreignfetch التابعة لجهات خارجية فقط، ستحتاج إلى استخدام نطاق واحد واضح يوازي النطاق الكلي لعامل الخدمة. هذا ما سيفعله المثال أعلاه، باستخدام القيمة self.registration.scope.
  • تأخذ origins أيضًا مصفوفة من سلسلة واحدة أو أكثر، وتسمح لك بتقييد معالج foreignfetch للاستجابة للطلبات الواردة من نطاقات محددة فقط. على سبيل المثال، إذا سمحت صراحةً بعنوان "https://example.com"، سيؤدي الطلب من صفحة مستضافة على https://example.com/path/to/page.html لمورد يتم عرضه من نطاق الجلب الخارجي إلى تشغيل معالج الجلب الخارجي، ولكن لن تؤدي الطلبات التي يتم إجراؤها من https://random-domain.com/path/to/page.html إلى تشغيل المعالج. ما لم يكن لديك سبب محدّد لتفعيل منطق الجلب الخارجي لمجموعة فرعية من المصادر البعيدة فقط، يمكنك تحديد '*' على أنّها القيمة الوحيدة في المصفوفة، وسيتم السماح بجميع المصادر.

معالج حدث externalfetch

الآن بعد تثبيت مشغّل الخدمات التابع لجهة خارجية وضبطه من خلال registerForeignFetch()، سيكون بإمكانك اعتراض طلبات الموارد الفرعية المتعددة المصادر إلى خادمك والتي تقع ضمن نطاق الجلب الخارجي.

في مشغّل الخدمات التقليدي التابع للطرف الأول، سيؤدي كل طلب إلى تشغيل حدث fetch يمكن لعامل الخدمة الرد عليه. ويتم منح مشغّل الخدمات التابع لجهة خارجية فرصة للتعامل مع حدث مختلف قليلاً، يُسمى foreignfetch. من الناحية النظرية، يتشابه الحدثان إلى حدّ كبير، ويمنحانك الفرصة لفحص الطلب الوارد وتوفير الردّ عليه اختياريًا من خلال respondWith():

self.addEventListener('foreignfetch', event => {
    // Assume that requestLogic() is a custom function that takes
    // a Request and returns a Promise which resolves with a Response.
    event.respondWith(
    requestLogic(event.request).then(response => {
        return {
        response: response,
        // Omit to origin to return an opaque response.
        // With this set, the client will receive a CORS response.
        origin: event.origin,
        // Omit headers unless you need additional header filtering.
        // With this set, only Content-Type will be exposed.
        headers: ['Content-Type']
        };
    })
    );
});

على الرغم من أوجه التشابه بين المفاهيم، هناك بعض الاختلافات في الممارسة عند الاتصال بـ "respondWith()" على جهاز ForeignFetchEvent. بدلاً من تقديم السمة Response (أو Promise التي يتم تحليلها باستخدام Response) إلى respondWith()، كما هو الحال مع FetchEvent، يجب تمرير Promise يمكنها تحليلها مع "كائن" ذي خصائص معيّنة في respondWith() الخاص بـ ForeignFetchEvent:

  • إنّ السمة response مطلوبة ويجب ضبطها على العنصر Response الذي سيتم إرجاعه إلى العميل الذي أرسل الطلب. في حال تقديم أي معلومات بخلاف قيمة Response الصالحة، سيتم إنهاء طلب العميل وستظهر لك رسالة خطأ في الشبكة. على عكس طلب respondWith() داخل معالج أحداث fetch، يجب توفير Response هنا، وليس Promise الذي يتم حلّه باستخدام Response. يمكنك إنشاء ردّك من خلال سلسلة وعد وتمرير هذه السلسلة كمَعلمة إلى respondWith() في foreignfetch، ولكن يجب أن يتم تحليل السلسلة باستخدام "كائن" يحتوي على السمة response التي تم ضبطها على عنصر Response. يمكنك رؤية عرض توضيحي لذلك في نموذج التعليمات البرمجية أعلاه.
  • origin اختيارية، ويتم استخدامها لتحديد ما إذا كان الرد الذي يتم عرضه معتمًا أم لا. في حال ترك هذا الخيار بدون ضبط، ستكون الاستجابة معتمة، وستكون إمكانية وصول العميل محدودة إلى نص الاستجابة وعناوينها. إذا تم تقديم الطلب باستخدام mode: 'cors'، سيتم التعامل مع عرض استجابة مبهمة على أنّها خطأ. وإذا حدّدت قيمة سلسلة مساوية لأصل البرنامج البعيد (الذي يمكن الحصول عليه من خلال event.origin)، يعني ذلك أنّك توافق صراحةً على توفير استجابة تمكّن CORS للعميل.
  • headers أيضًا اختيارية ومفيدة فقط إذا كنت تحدِّد origin أيضًا وتعرض استجابة CORS. وفقًا للإعدادات التلقائية، لن يتم تضمين سوى العناوين في الرد في قائمة عناوين الاستجابة الآمنة في قائمة CORS. إذا كنت بحاجة إلى فلترة البيانات المعروضة بشكل أكبر، تدرج العناوين قائمة باسم واحد أو أكثر من أسماء العناوين، ويتم استخدامها كقائمة مسموح بها للعناوين التي سيتم عرضها في الاستجابة. يتيح لك ذلك تفعيل سياسة مشاركة الموارد المتعددة المصادر (CORS) مع منع الكشف المباشر عن عناوين الاستجابة التي يحتمل أن تكون حسّاسة للبرنامج البعيد.

من المهم ملاحظة أنّه عند تشغيل معالِج foreignfetch، يحصل على إمكانية الوصول إلى جميع بيانات الاعتماد والسلطة المحيطة للمصدر الذي يستضيف مشغّل الخدمة. بصفتك مطوّرًا ينشر مشغّل خدمة أجنبيًا يتم تفعيل ميزة الجلب به، تقع على عاتقك مسؤولية ضمان عدم تسرُّب أيّ بيانات استجابة مميّزة لم تكن متاحة بموجب بيانات الاعتماد هذه. ومن خلال طلب الموافقة على ردود CORS، يمكنك الحدّ من عرض المحتوى غير المقصود، ولكن بصفتك مطوِّرًا يمكنك إرسال طلبات fetch() بشكل صريح داخل معالج foreignfetch لا تستخدم بيانات الاعتماد الضمنية من خلال:

self.addEventListener('foreignfetch', event => {
    // The new Request will have credentials omitted by default.
    const noCredentialsRequest = new Request(event.request.url);
    event.respondWith(
    // Replace with your own request logic as appropriate.
    fetch(noCredentialsRequest)
        .catch(() => caches.match(noCredentialsRequest))
        .then(response => ({response}))
    );
});

اعتبارات العميل

هناك بعض الاعتبارات الإضافية التي تؤثر في كيفية تعامل عامل خدمة الجلب الأجنبي مع الطلبات التي يرسلها عملاء خدمتك.

العملاء الذين لديهم مشغّل خدمات خاص بهم من طرف أول

قد يكون لدى بعض عملاء خدمتك مشغّل خدمات من الطرف الأول الخاص بهم، والذي يعالج الطلبات الصادرة من تطبيق الويب لديهم. ماذا يعني ذلك بالنسبة إلى عامل خدمة الجلب الأجنبي التابع لجهة خارجية؟

يحصل معالِجات fetch في مشغّل الخدمات التابع للطرف الأول على الفرصة الأولى للاستجابة لجميع الطلبات التي يرسلها تطبيق الويب، حتى إذا كان هناك مشغّل خدمات تابع لجهة خارجية مع تفعيل foreignfetch بنطاق يشمل الطلب. لكن لا يزال بإمكان العملاء الذين لديهم عمال خدمة الطرف الأول الاستفادة من عامل خدمة الجلب الأجنبي الذي تتعامل معه!

داخل مشغّل خدمات الطرف الأول، سيؤدي استخدام fetch() لاسترداد الموارد من مصادر متعددة إلى تشغيل مشغّل خدمة الجلب الخارجي المناسب. ويعني هذا أنّ الرموز البرمجية مثل الرموز التالية يمكنها الاستفادة من معالج foreignfetch:

// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
    // If event.request is under your foreign fetch service worker's
    // scope, this will trigger your foreignfetch handler.
    event.respondWith(fetch(event.request));
});

وبالمثل، إذا كانت هناك معالِجات جلب تابعة للطرف الأول، ولكنها لا تستدعي event.respondWith() عند معالجة طلبات المورد المشترك المصدر، "سينتقل" الطلب تلقائيًا إلى معالج foreignfetch:

// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
    if (event.request.mode === 'same-origin') {
    event.respondWith(localRequestLogic(event.request));
    }

    // Since event.respondWith() isn't called for cross-origin requests,
    // any foreignfetch handlers scoped to the request will get a chance
    // to provide a response.
});

إذا استدعى معالج fetch التابع للطرف الأول event.respondWith() ولكنه لا يستخدم fetch() لطلب مورد ضمن نطاق الجلب الخارجي، لن تتاح لمشغِّل خدمة الجلب الخارجي فرصة معالجة الطلب.

العملاء الذين ليس لديهم مشغّل خدمات خاص بهم

يمكن لجميع العملاء الذين يرسلون طلبات إلى خدمة تابعة لجهة خارجية الاستفادة عندما تنشر الخدمة عامل خدمة استرجاع أجنبي، حتى إذا لم يكونوا يستخدمون مشغّل خدمات خاص بهم. ليس على العملاء اتّخاذ أي إجراء محدّد لتفعيل استخدام عامل خدمة استرجاع أجنبي، طالما أنهم يستخدمون متصفّحًا يتوافق مع هذه الميزة. وهذا يعني أنه من خلال نشر عامل خدمة استرجاع أجنبي، سيستفيد العديد من عملاء الخدمة من منطق الطلب المخصص وذاكرة التخزين المؤقت المشتركة على الفور، بدون اتخاذهم خطوات أخرى.

وضع كل شيء معًا: حيث يبحث العملاء عن رد

بالاستناد إلى المعلومات المذكورة أعلاه، يمكننا تجميع تسلسل هرمي للمصادر التي سيستخدمها العميل للعثور على ردّ بشأن طلب من مصادر متعددة.

  1. معالِج fetch لمشغّل خدمات الطرف الأول (إذا كان متوفّرًا)
  2. معالِج foreignfetch لمشغِّل الخدمات التابع لجهة خارجية (إذا كان متاحًا، ولطلبات الوصول من مصادر متعددة فقط)
  3. ذاكرة التخزين المؤقت لبروتوكول HTTP في المتصفح (في حال وجود استجابة جديدة)
  4. الشبكة

يبدأ المتصفِّح من الأعلى، وبناءً على تنفيذ مشغّل الخدمات، سيواصل أسفل القائمة إلى أن يعثر على مصدر للاستجابة.

مزيد من المعلومات

البقاء على اطّلاع

يخضع تنفيذ Chrome لتجربة مصدر الجلب الخارجي للتغيير أثناء تعاملنا مع الملاحظات الواردة من مطوّري البرامج. سنحافظ على تحديث هذه المشاركة باستمرار من خلال التغييرات المضمنة، وسندوّن التغييرات المحددة أدناه فور حدوثها. وسنشارك أيضًا معلومات عن التغييرات الرئيسية من خلال حساب @chromiumdev على Twitter.