איך מצאתי באג בכרומיום שמקריס את הטאב
כך הצלחתי לשחזר ולדווח על באג בכרומיום, שהופיע רק בשילוב די נדיר של iframe, שימוש במקלדת וקוד ישן.
איך זה התחיל
גלשתי באתר של חילן כדי לעדכן את ימי המילואים שלי. נפתחה הודעה על המסמכים הדרושים, ולחצתי על Enter כדי לסגור אותה. במקום שההודעה תיסגר, כל הטאב קרס והופיעה הודעת Aw, Snap!.
ניסיתי שוב וקיבלתי בדיוק את אותה תוצאה. לחיצה עם העכבר עבדה, אבל לחיצה על Enter או על Space גרמה לטאב לקרוס.
בהתחלה חשבתי שמשהו בקוד של הדף גורם לקריסה. כשפתחתי את כלי הפיתוח ראיתי שההודעה נמצאת בתוך iframe, ושהקריסה מתרחשת כשה-iframe מוסר מה-DOM.
ניסיונות השחזור
ניסיתי ליצור בעזרת Cursor דוגמה מינימלית: iframe ובתוכו כפתור, שכאשר לוחצים עליו הוא מסיר את ה-iframe מהדף. הדוגמה הראשונה לא קרסה.
ניסיתי כמה וריאציות: ליצור את ה-iframe באופן דינמי, לשים את הכפתור בתוך form ולהעביר חלקים מהקוד בין ה-iframe לדף הראשי. שום דבר מזה לא שחזר את הבעיה.
היה ברור שחסר פרט כלשהו, אבל בקוד המקורי היו מאות שורות ולא היה קל לדעת איזה מהן רלוונטי. בסוף נתתי ל-Cursor את כל הקוד של ה-iframe. הפעם הוא הצליח ליצור דוגמה ששחזרה את הקריסה. משם הורדתי בהדרגה קוד שלא היה נחוץ, עד שנשארה הדוגמה הבאה.
הדף הראשי (index.html)
1<!DOCTYPE html>
2<html>
3<head>
4 <title>Chrome IFrame Bug</title>
5</head>
6<body>
7 <h1>Parent Page</h1>
8 <iframe id="iframe" src="iframe.html" width="400" height="300"></iframe>
9 <script>
10 const iframe = document.getElementById('iframe');
11
12 function hide() {
13 iframe.remove();
14 }
15 </script>
16</body>
17</html>
18תוכן ה-iframe (iframe.html)
1<!DOCTYPE html>
2<html>
3<head>
4 <title>IFrame Content</title>
5</head>
6<body>
7 <h2>IFrame Content</h2>
8 <button id="btn">OK</button>
9 <script>
10 const btn = document.getElementById('btn');
11
12 btn.onkeypress = function (e) {
13 if (e.keyCode == 13 || e.charCode == 32) {
14 window.parent.hide();
15 return e.preventDefault();
16 }
17 };
18 </script>
19</body>
20</html>
21החלק החשוב הוא ה-onkeypress. בזמן הטיפול באירוע, הקוד קורא לפונקציה בחלון האב שמסירה את ה-iframe. לאחר מכן הוא קורא ל-preventDefault(), אף על פי שה-iframe שבתוכו התחיל הטיפול באירוע כבר לא נמצא בדף. השילוב הזה גורם לכרומיום לקרוס.
למה קשה להיתקל בבאג הזה?
בדקתי את הדוגמה בגרסאות שונות של כרום באמצעות BrowserStack. הקריסה הופיעה החל מגרסה 91 של כרום, שיצאה במאי 2021, ורק ב-macOS. היא עדיין הופיעה גם ב-Chrome Canary שהיה עדכני בזמן הבדיקה.
נוסף על כך נדרש iframe מאותו origin, שקורא ישירות לפונקציה בחלון האב. בהרבה מערכות חדשות התקשורת עם iframe נעשית באמצעות postMessage, ושם לא הצלחתי לשחזר את הבאג.
גם צורת הרישום לאירוע משנה. הקוד המקורי השתמש ב-onkeypress. כשהחלפתי אותו ב-addEventListener, הקריסה לא התרחשה. זה מסביר איך הבאג נשאר בכרומיום כמה שנים: צריך שילוב די נדיר של מערכת הפעלה מסוימת, iframe מאותו origin וקוד ישן לטיפול באירועי מקלדת.
הדיווח לכרומיום
יצרתי דוגמה חיה שמשחזרת את הבאג, ופתחתי דיווח בכרומיום. זמן קצר לאחר מכן מישהו מצוות כרומיום הצליח לשחזר אותו.
עיקר העבודה לא היה לגלות שהטאב קורס, אלא לצמצם מאות שורות קוד לדוגמה קצרה וברורה. Cursor עזר למצוא את השילוב הרלוונטי, אבל עדיין היה צריך לעבור על התוצאה ולהסיר ממנה כל מה שלא נחוץ.
הקוד המלא נמצא ב-GitHub. אם אתם משתמשים בכרום על Mac, אפשר לנסות גם את הדוגמה החיה.