ایجاد یکنواختی فعال سازی کاربر در بین APIها

Mustaq Ahmed
جو مدلی
Joe Medley

برای جلوگیری از سوء استفاده اسکریپت های مخرب از API های حساس مانند پنجره های بازشو، تمام صفحه و غیره، مرورگرها دسترسی به آن API ها را از طریق فعال سازی کاربر کنترل می کنند. فعال سازی کاربر وضعیت یک جلسه مرور با توجه به اقدامات کاربر است: حالت "فعال" معمولاً به این معنی است که کاربر در حال حاضر با صفحه در حال تعامل است یا از زمان بارگیری صفحه یک تعامل را تکمیل کرده است. اشاره کاربر یک اصطلاح رایج اما گمراه کننده برای همان ایده است. به عنوان مثال، یک حرکت تند کشیدن یا تلنگر توسط کاربر، صفحه ای را فعال نمی کند و از این رو، از دیدگاه اسکریپت، یک فعال سازی کاربر نیست.

امروزه مرورگرهای اصلی رفتار متفاوتی را در مورد نحوه کنترل فعال‌سازی کاربر API‌های فعال‌سازی شده نشان می‌دهند. در کروم، این پیاده‌سازی بر اساس یک مدل مبتنی بر توکن بود که برای تعریف یک رفتار ثابت در همه API‌های دارای دروازه فعال‌سازی بسیار پیچیده بود. برای مثال، Chrome از طریق setTimeout() postMessage() و setTimeout() اجازه دسترسی ناقص به API‌های دارای دروازه فعالسازی را داده است. و فعال‌سازی کاربر با Promises ، XHR ، Gamepad تعامل و غیره پشتیبانی نمی‌شود. توجه داشته باشید که برخی از این اشکالات محبوب و در عین حال قدیمی هستند.

در نسخه 72، Chrome User Activation v2 را ارسال می‌کند که باعث می‌شود در دسترس بودن فعال‌سازی کاربر برای همه APIهای دارای دروازه فعال‌سازی کامل شود. این تناقضات ذکر شده در بالا (و چند مورد دیگر مانند MessageChannels ) را برطرف می کند، که ما معتقدیم توسعه وب در مورد فعال سازی کاربر را تسهیل می کند. علاوه بر این، پیاده‌سازی جدید یک پیاده‌سازی مرجع برای یک مشخصات پیشنهادی جدید ارائه می‌کند که هدف آن گردآوری همه مرورگرها در طولانی مدت است.

User Activation v2 چگونه کار می کند؟

API جدید یک حالت فعال‌سازی کاربر دو بیتی را در هر شی window در سلسله مراتب قاب حفظ می‌کند: یک بیت چسبنده برای وضعیت فعال‌سازی کاربر تاریخی (اگر یک فریم تا به حال فعال‌سازی کاربر را دیده باشد)، و یک بیت گذرا برای وضعیت فعلی (اگر فریم در حدود یک ثانیه یک کاربر فعال شده است). بیت چسبنده در طول عمر قاب پس از تنظیم مجدد هرگز تنظیم مجدد نمی شود. بیت گذرا در هر تعامل کاربر تنظیم می شود و پس از یک بازه زمانی انقضا (حدود یک ثانیه) یا از طریق تماس با یک API فعال سازی (مثلاً window.open() ) بازنشانی می شود.

توجه داشته باشید که API های فعال سازی مختلف به روش های مختلف به فعال سازی کاربر متکی هستند. API جدید هیچ یک از این رفتارهای خاص API را تغییر نمی دهد. به عنوان مثال، تنها یک پنجره بازشو برای هر فعال سازی کاربر مجاز است زیرا window.open() Navigator.prototype.vibrate() سازی کاربر را مانند گذشته مصرف می کند. ، و غیره.

چه چیزی در حال تغییر است؟

  • User Activation v2 مفهوم قابلیت مشاهده فعال‌سازی کاربر را در سراسر مرزهای فریم رسمی می‌کند: تعامل کاربر با یک فریم خاص اکنون همه فریم‌های حاوی (و فقط آن فریم‌ها) را بدون در نظر گرفتن مبدا آنها فعال می‌کند. (در Chrome 72، ما یک راه‌حل موقت برای گسترش دید به همه فریم‌های با مبدا یکسان داریم. زمانی که راهی برای انتقال صریح فعال‌سازی کاربر به فریم‌های فرعی داشته باشیم، این راه‌حل را حذف می‌کنیم.)
  • هنگامی که یک API دارای دروازه فعال سازی از یک قاب فعال شده، اما از خارج از کد کنترل کننده رویداد فراخوانی می شود، تا زمانی که حالت فعال سازی کاربر "فعال" باشد (به عنوان مثال نه منقضی شده و نه مصرف شده است) کار خواهد کرد. قبل از User Activation v2، بدون قید و شرط شکست می خورد.
  • چندین تعامل کاربر استفاده نشده در بازه زمانی انقضا به یک فعال سازی منفرد مربوط به آخرین تعامل تبدیل می شود.

نمونه هایی از سازگاری در API های دارای دروازه فعال سازی

در اینجا دو مثال با پنجره‌های بازشو (با استفاده از window.open() ) باز می‌شوند که نشان می‌دهند چگونه User Activation v2 رفتار API‌های دارای دروازه فعال‌سازی را سازگار می‌کند.

فراخوانی های زنجیره ای setTimeout()

این مثال از نسخه ی نمایشی setTimeout() ما است. اگر یک کنترل کننده click سعی کند یک پنجره بازشو را در عرض یک ثانیه باز کند، انتظار می رود بدون توجه به اینکه کد تاخیر را چگونه "نوشتن" کند، موفق خواهد شد. User Activation v2 این انتظار را برآورده می‌کند، بنابراین هر یک از کنترل‌کننده‌های رویداد زیر یک پنجره بازشو با یک click باز می‌کنند (با 100 میلی‌ثانیه تاخیر):

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

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

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

بدون User Activation v2، دومین کنترل کننده رویداد در همه مرورگرهایی که ما آزمایش کردیم با مشکل مواجه می شود. (حتی اولین مورد در برخی موارد با شکست مواجه می شود.)

فراخوانی های 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);
});

بدون User Activation v2، قاب والد نمی تواند پس از دریافت پیام دوم، یک پنجره بازشو باز کند. حتی اولین پیام در صورتی که به یک فریم متقاطع دیگر "زنجیره شده" باشد شکست می خورد (به عبارت دیگر، اگر اولین گیرنده پیام را به دیگری ارسال کند).

این کار با User Activation v2، هم به صورت اصلی و هم با زنجیره‌سازی کار می‌کند.