جعل عملية تنشيط المستخدمين متسقة عبر واجهات برمجة التطبيقات

Mustaq Ahmed
Joe Medley
Joe Medley

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

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

في الإصدار 72، يطرح Chrome الإصدار 2 من ميزة "تفعيل المستخدم"، ما يجعل ميزة تفعيل المستخدم متاحة بالكامل لجميع واجهات برمجة التطبيقات التي تتطلب التفعيل. يحلّ ذلك مشكلة عدم الاتساق المذكورة أعلاه (وبعض المشاكل الأخرى، مثل MessageChannels)، ما نعتقد أنّه سيسهّل تطوير الويب في ما يتعلّق بتنشيط المستخدِم. بالإضافة إلى ذلك، توفّر طريقة التنفيذ الجديدة طريقة تنفيذ مرجعية لمواصفة جديدة مقترَحةتهدف إلى توحيد جميع المتصفّحات على المدى الطويل.

كيف تعمل الإصدار 2 من ميزة "تفعيل المستخدِم"؟

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

يُرجى العلم أنّ واجهات برمجة التطبيقات المختلفة التي تتطلب تفعيلًا تعتمد على تفعيل المستخدمين بطرق مختلفة، ولا تغيّر واجهة برمجة التطبيقات الجديدة أيًا من هذه السلوكيات الخاصة بواجهة برمجة التطبيقات. على سبيل المثال، يُسمح بظهور نافذة منبثقة واحدة فقط لكل عملية تفعيل مستخدم لأنّ window.open() تستهلك تفعيل المستخدم كما كان من قبل، ويستمرNavigator.prototype.vibrate() في أن يكون فعّالاً إذا ظهرت نافذة منبثقة (أو أيّ من إطاراتها الفرعية) في أيّ وقت على إجراء المستخدِم، وهكذا.

ما الذي سيتغيّر؟

  • يوفّر الإصدار 2 من User Activation (تفعيل المستخدِم) تعريفًا رسميًا لمستوى رؤية تفعيل المستخدِم على مستوى حدود اللقطات: سيؤدي تفاعل المستخدِم مع لقطة معيّنة الآن إلى تفعيل كل اللقطات التي تحتوي عليها (هذه اللقطات فقط) بغض النظر عن مصدرها. (في الإصدار 72 من Chrome، لدينا حلّ مؤقت لتوسيع نطاق الرؤية ليشمل جميع الإطارات التي لها مصدر واحد. سنزيل هذا الحلّ البديل بعد أن تتوفّر لدينا طريقة ل تمرير تفعيل المستخدم إلى الإطارات الفرعية بشكل صريح.)
  • عند طلب واجهة برمجة تطبيقات متاحة بعد التفعيل من إطار نشط ولكن خارج رمز معالِج الأحداث، ستعمل هذه الواجهة طالما كانت حالة تفعيل المستخدِم "نشطة" (على سبيل المثال، لم تنته صلاحيتها ولم يتم استخدامها). قبل الإصدار 2 من User Activation، كان من المؤكد أنّه سيتعذّر إكمال عملية التفعيل.
  • يتم دمج تفاعلات المستخدِم المتعدّدة غير المستخدَمة خلال الفاصل الزمني لانتهاء الصلاحية في تنشيط واحد يتوافق مع التفاعل الأخير.

أمثلة على الاتساق في واجهات برمجة التطبيقات التي تتطلب التفعيل

في ما يلي مثالان يتضمّنان نافذتَي منبثقتَين (يتم فتحهما باستخدام window.open()) يُظهران كيفية تحسين الإصدار 2 من User Activation لسلوك واجهات برمجة التطبيقات التي تتطلب التفعيل.

طلبات setTimeout() متسلسلة

هذا المثال مأخوذ من العرض التوضيحي setTimeout(). إذا حاول معالِج click فتح نافذة منبثقة في غضون ثانية، من المتوقّع أن يتم النجاح بغض النظر عن كيفية "إنشاء" الرمز للتأخير. تلبي الإصدار 2 من User Activation هذا التوقّع، لذا يفتح كلّ من معالِجات الأحداث التالية نافذة منبثقة عند click (مع تأخير 100 ملي ثانية):

function popupAfter100ms() {
  setTimeout(callWindowOpen, 100);
}

function asyncPopupAfter100ms() {
  setTimeout(popupAfter100ms, 0);
}

someButton.addEventListener('click', popupAfter100ms);
someButton.addEventListener('click', asyncPopupAfter100ms);

في حال عدم توفّر الإصدار 2 من ميزة "تفعيل المستخدِم"، يتعذّر على معالِج الأحداث الثاني تنفيذ مهامه في جميع المتصفّحات التي اختبرناها. (حتى الخطوة الأولى قد لا تنجح في بعض الحالات).

مكالمات postMessage() بين نطاقات مختلفة

في ما يلي مثال من العرض التوضيحي postMessage(). لنفترض أنّ معالِج click في إطار فرعي من مصدر مختلف يرسل رسالتَين مباشرةً إلى الإطار الرئيسي. يجب أن يكون بإمكان الإطار الرئيسي فتح نافذة منبثقة عندتلقّي أيّ من الرسالتَين التاليتَين (وليس كليهما):

// Parent frame code
window.addEventListener('message', e => {
  if (e.data === 'open_popup' && e.origin === child_origin)
    window.open('about:blank');
});

// Child frame code:
someButton.addEventListener('click', () => {
  parent.postMessage('hi_there', parent_origin);
  parent.postMessage('open_popup', parent_origin);
});

بدون الإصدار 2 من ميزة "تفعيل المستخدم"، لا يمكن للإطار الرئيسي فتح نافذة منبثقة عند تلقّي الرسالة الثانية. وحتى الرسالة الأولى لا تجتاز الفحص إذا كانت "مرتبطة" بإطار آخر من مصدر مختلف (بمعنى آخر، إذا أعاد المستلِم الأول توجيه الرسالة إلى مستلِم آخر).

يعمل هذا الإجراء مع الإصدار 2 من User Activation (تفعيل المستخدم)، سواء في شكله الأصلي أو مع التسلسل.