सभी एपीआई में उपयोगकर्ता की गतिविधि को एक जैसा बनाना

मुस्तक़ अहमद
जो मेडले
जो मेडली

नुकसान पहुंचाने वाली स्क्रिप्ट को पॉप-अप, फ़ुल स्क्रीन वगैरह जैसे संवेदनशील एपीआई का गलत इस्तेमाल करने से रोकने के लिए, ब्राउज़र, उपयोगकर्ता को चालू करने की सुविधा के ज़रिए उन एपीआई के ऐक्सेस को कंट्रोल करते हैं. उपयोगकर्ता ऐक्टिवेशन, उपयोगकर्ता की कार्रवाइयों के हिसाब से ब्राउज़िंग सेशन की स्थिति होती है: आम तौर पर, "ऐक्टिव" स्थिति का मतलब होता है कि या तो उपयोगकर्ता फ़िलहाल पेज से इंटरैक्ट कर रहा है या उसने पेज लोड होने के बाद कोई इंटरैक्शन पूरा किया है. उपयोगकर्ता जेस्चर एक ही सुझाव के लिए लोकप्रिय लेकिन गुमराह करने वाला शब्द है. उदाहरण के लिए, उपयोगकर्ता के स्वाइप या फ़्लिक जेस्चर से पेज चालू नहीं होता है और स्क्रिप्ट के हिसाब से, यह उपयोगकर्ता को चालू नहीं करता है.

आज के समय में मुख्य ब्राउज़र, ऐक्टिवेशन-गेटेड एपीआई को कंट्रोल करने के तरीके में बड़े पैमाने पर अलग-अलग तरह के व्यवहार दिखाते हैं. Chrome में, लागू करना टोकन-आधारित मॉडल पर आधारित था. यह इतना जटिल था कि सभी ऐक्टिवेशन-गेट किए गए एपीआई में एक जैसा व्यवहार तय कर सकता था. उदाहरण के लिए, Chrome postMessage() और setTimeout() कॉल के ज़रिए, ऐक्टिवेशन-गेटेड एपीआई को कभी भी ऐक्सेस करने की अनुमति नहीं देता था. साथ ही, प्रॉमिस, XHR, गेमपैड इंटरैक्शन वगैरह के साथ, उपयोगकर्ता ऐक्टिवेशन के लिए काम नहीं कर पाया. ध्यान दें कि इनमें से कुछ गड़बड़ियां लंबे समय से चल रही हैं.

वर्शन 72 में, Chrome, उपयोगकर्ता ऐक्टिवेशन v2 भेजता है. इससे, सभी ऐक्टिवेशन-गेट किए गए एपीआई के लिए, उपयोगकर्ता को चालू करने की उपलब्धता पूरी हो जाती है. इससे ऊपर बताई गई गड़बड़ियां ठीक हो जाती हैं. साथ ही, MessageChannels जैसी कुछ और गड़बड़ियां भी ठीक हो गई हैं, जिससे उपयोगकर्ता के खाते को चालू करने के समय, वेब डेवलपमेंट में आसानी होगी. साथ ही, इस नए तरीके से, सुझाए गए नए स्पेसिफ़िकेशन के लिए रेफ़रंस लागू करने का विकल्प मिलता है, जिसका मकसद सभी ब्राउज़र को लंबे समय तक एक साथ लाना है.

उपयोगकर्ता ऐक्टिवेशन v2 कैसे काम करता है?

एपीआई का नया वर्शन, फ़्रेम की हैरारकी में हर window ऑब्जेक्ट पर, उपयोगकर्ता को दो-बिट ऐक्टिवेशन के हिसाब से ऐक्टिवेट करता है: उपयोगकर्ता ऐक्टिवेशन की पुरानी स्थिति के लिए एक स्टिकी बिट (अगर किसी फ़्रेम ने कभी उपयोगकर्ता को चालू किया है) और मौजूदा स्थिति के लिए एक अस्थायी बिट (अगर किसी फ़्रेम में उपयोगकर्ता को एक सेकंड में चालू हुआ है, तो). स्टिकी बिट सेट होने के बाद, फ़्रेम के जीवनकाल के दौरान कभी भी रीसेट नहीं होता. अस्थायी बिट, उपयोगकर्ता के हर इंटरैक्शन पर सेट हो जाता है और समयसीमा खत्म होने के अंतराल (करीब एक सेकंड) के बाद या खाते को चालू करने वाले एपीआई (जैसे कि window.open()) पर कॉल के ज़रिए रीसेट किया जाता है.

ध्यान दें कि अलग-अलग ऐक्टिवेशन-गेट किए गए एपीआई, उपयोगकर्ता के ऐक्टिवेशन पर अलग-अलग तरीकों से भरोसा करते हैं. एपीआई का नया एपीआई, इन एपीआई के काम करने के खास तरीकों में कोई बदलाव नहीं कर रहा है. उदाहरण के लिए, हर उपयोगकर्ता ऐक्टिवेशन के लिए सिर्फ़ एक पॉप-अप की अनुमति है, क्योंकि window.open() पहले की तरह यूज़र ऐक्टिवेशन का इस्तेमाल करता था. अगर किसी फ़्रेम (या इसके किसी सबफ़्रेम) पर उपयोगकर्ता की कार्रवाई देखी गई हो, तो Navigator.prototype.vibrate() उस पर लगातार असरदार तरीके से काम करता है.

क्या बदलाव होने वाले हैं?

  • उपयोगकर्ता ऐक्टिवेशन v2 के ज़रिए फ़्रेम की सीमाओं में उपयोगकर्ता ऐक्टिवेशन देखे जाने के नियम को औपचारिक बनाया गया है: किसी खास फ़्रेम के साथ होने वाला उपयोगकर्ता इंटरैक्शन, अब सभी फ़्रेम (और सिर्फ़ वे फ़्रेम) को चालू करेगा, चाहे वे मूल रूप से कुछ भी हों. (Chrome 72 में, हमारे पास एक अस्थायी तरीका है, जिसका इस्तेमाल एक ही ऑरिजिन के सभी फ़्रेम के लिए किया जा सकता है. सब-फ़्रेम में उपयोगकर्ता ऐक्टिवेशन को साफ़ तौर पर पास करने का तरीका उपलब्ध होने पर, हम इस समाधान को हटा देंगे.)
  • जब किसी ऐक्टिवेटेड फ़्रेम से, लेकिन इवेंट हैंडलर कोड के बाहर से किसी ऐक्टिवेशन-गेट एपीआई को कॉल किया जाता है, तो यह तब तक काम करेगा, जब तक उपयोगकर्ता ऐक्टिवेशन की स्थिति "चालू" है. उदाहरण के लिए, उसकी समयसीमा खत्म न हुई हो और न ही उसे इस्तेमाल किया गया हो. उपयोगकर्ता ऐक्टिवेशन v2 से पहले, यह बिना किसी शर्त के काम नहीं करेगा.
  • समयसीमा खत्म होने की अवधि के दौरान, इस्तेमाल न किए गए कई उपयोगकर्ता इंटरैक्शन, आखिरी इंटरैक्शन के हिसाब से एक ऐक्टिवेशन में बदल जाते हैं.

ऐक्टिवेशन-गेट किए गए एपीआई में समानता के उदाहरण

यहां पॉप-अप विंडो (window.open() का इस्तेमाल करके खोली गई) के दो उदाहरण दिए गए हैं. इनमें दिखाया गया है कि उपयोगकर्ता ऐक्टिवेशन v2 किस तरह ऐक्टिवेशन-गेट किए गए एपीआई के काम करने के तरीके को एक जैसा बनाता है.

चेन वाले setTimeout() कॉल

यह उदाहरण हमारे setTimeout() डेमो से लिया गया है. अगर click हैंडलर एक सेकंड के अंदर पॉप-अप खोलने की कोशिश करता है, तो यह उम्मीद की जाती है कि कोड इस बार भी "कंपोज़" करता है. उपयोगकर्ता ऐक्टिवेशन v2 इस उम्मीद को पूरा करता है, इसलिए इनमें से हर इवेंट हैंडलर click पर (100 मि॰से॰ की देरी के साथ) पॉप-अप खोलता है:

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

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

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

उपयोगकर्ता ऐक्टिवेशन 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);
});

उपयोगकर्ता ऐक्टिवेशन v2 के बिना, दूसरा मैसेज मिलने पर पैरंट फ़्रेम पॉप-अप नहीं खोल सकता. अगर पहला मैसेज किसी दूसरे क्रॉस-ऑरिजिन फ़्रेम में "चेन" किया गया हो, तब भी पहला मैसेज नहीं चलता. दूसरे शब्दों में, अगर पहला मैसेज पाने वाला व्यक्ति, मैसेज को दूसरे पर फ़ॉरवर्ड करता है.

यह उपयोगकर्ता ऐक्टिवेशन v2 के साथ, ओरिजनल फ़ॉर्म और चेन, दोनों में काम करता है.