מבט מבפנים על דפדפן אינטרנט מודרני (חלק 2)

מריקו קוסאקה

מה קורה בניווט

זה החלק השני מתוך סדרת בלוגים בת 4 חלקים שמציגה את התכונות הפנימיות של Chrome. בפוסט הקודם הסברנו איך תהליכים ושרשורים שונים מטפלים בחלקים שונים של הדפדפן. בפוסט הזה נסביר בפירוט איך כל תהליך ושרשור מתקשרים כדי להציג אתר.

נבחן מקרה שימוש פשוט של גלישה באינטרנט: אתם מקלידים כתובת אתר בדפדפן, ולאחר מכן הדפדפן מאחזר נתונים מהאינטרנט ומציג דף. בפוסט הזה נתמקד בחלק שבו המשתמש מבקש אתר, והדפדפן מתכונן לעבד דף – שנקרא גם ניווט.

הוא מתחיל בתהליך בדפדפן

תהליכי הדפדפן
איור 1: ממשק המשתמש של הדפדפן למעלה, תרשים של תהליך הדפדפן עם ממשק משתמש, רשת ושרשור אחסון בתחתית

כפי שהסברנו בחלק 1: מעבד (CPU), GPU, זיכרון וארכיטקטורה מרובת תהליכים, כל מה שמחוץ לכרטיסייה מטופל בתהליך הדפדפן. בתהליך הדפדפן יש שרשורים כמו ה-thread של ממשק המשתמש, שמושך לחצנים ושדות קלט של הדפדפן, ה-thread של הרשת שעוסק בסטאק הרשת לקבלת נתונים מהאינטרנט, שרשור האחסון ששולט בגישה לקבצים ועוד. כשמקלידים כתובת URL בסרגל הכתובות, הקלט עובר עיבוד בשרשור ממשק המשתמש של תהליך הדפדפן.

ניווט פשוט

שלב 1: טיפול בקלט

כשמשתמש מתחיל להקליד בסרגל הכתובות, הדבר הראשון שמסומן בשרשור של ממשק המשתמש הוא "האם זו שאילתת חיפוש או כתובת URL?". ב-Chrome, גם סרגל הכתובות הוא שדה להזנת קלט בחיפוש, לכן צריך לנתח את שרשור ממשק המשתמש ולהחליט אם לשלוח אותו למנוע חיפוש או לאתר שביקשת.

טיפול בקלט של משתמשים
איור 1: שרשור ממשק משתמש ששואל אם הקלט הוא שאילתת חיפוש או כתובת URL

שלב 2: מתחילים בניווט

כשמשתמש לוחץ על Enter, השרשור בממשק המשתמש מפעיל קריאת רשת כדי לקבל את תוכן האתר. סימן גרפי של טעינה מוצג בפינת הכרטיסייה, ושרשור הרשת עובר פרוטוקולים מתאימים כמו חיפוש DNS ויצירת חיבור TLS (אבטחת שכבת התעבורה) עבור הבקשה.

התחלת ניווט
איור 2: ה-thread של ממשק המשתמש שמפנה לשרשור של הרשת כדי לנווט אל mysite.com

בשלב הזה, שרשור הרשת עשוי לקבל כותרת הפניה לשרת כמו HTTP 301. במקרה כזה, שרשור הרשת מתקשר עם שרשור ממשק המשתמש שהשרת מבקש הפניה אוטומטית. לאחר מכן, תופעל בקשה נוספת לכתובת URL.

שלב 3: קריאת התשובה

תגובת HTTP
איור 3: כותרת תגובה שמכילה Content-Type ומטען ייעודי (payload), שהם הנתונים בפועל

אחרי שגוף התגובה (payload) מתחיל להיכנס, שרשור הרשת בודק את הבייטים הראשונים של השידור, לפי הצורך. כותרת Content-Type של התגובה אמורה לציין את סוג הנתונים, אך מכיוון שייתכן שהיא חסרה או שגויה, כאן מתבצע סריקה של סוג MIME. זהו 'עסק מסובך', כפי שצוין בקוד המקור. אפשר לקרוא את התגובה כדי לראות איך דפדפנים שונים מטפלים בצמדים של סוג תוכן/מטען ייעודי (payload).

אם התשובה היא קובץ HTML, בשלב הבא צריך להעביר את הנתונים לתהליך הרינדור, אבל אם מדובר בקובץ ZIP או בקובץ אחר, מדובר בבקשת הורדה ולכן הם יצטרכו להעביר את הנתונים למנהל ההורדות.

סריקה של סוג MIME
איור 4: שרשור של רשת שבו נשאל אם נתוני התגובה הם HTML מאתר בטוח

כאן גם מתבצעת בדיקת SafeBrowsing. אם נראה שהדומיין ונתוני התגובה תואמים לאתר זדוני ידוע, יוצג דף אזהרה בשרשור הרשת. בנוסף, מתבצעת בדיקה של Cחסימה CC (C) כדי לוודא שנתונים רגישים מאתרים שונים לא מגיעים לתהליך הרינדור.

שלב 4: מוצאים תהליך של כלי רינדור

כשכל הבדיקות מסתיימות ו-Networkthread בטוח שהדפדפן צריך לנווט לאתר המבוקש, השרשור של Network מורה ל-thread של ממשק המשתמש שהנתונים מוכנים. לאחר מכן, ה-UI thread מוצא תהליך רינדור כדי להמשיך את הרינדור של דף האינטרנט.

איתור תהליך הרינדור
איור 5: שרשור של רשת שאומר ל-thread של ממשק המשתמש למצוא את תהליך כלי הרינדור

מכיוון שהתגובה לבקשה מהרשת יכולה להימשך כמה מאות אלפיות השנייה, מיושמת אופטימיזציה לזירוז התהליך. כששרשור ממשק המשתמש שולח בקשה לכתובת URL לשרשור הרשת בשלב 2, המערכת כבר יודעת לאיזה אתר המשתמשים מנווטים. המטרה של ה-UI ב-thread היא למצוא או להתחיל באופן יזום תהליך של רינדור במקביל לבקשת הרשת. כך, אם הכול יפעל כצפוי, תהליך של רינדור כבר נמצא במצב המתנה בזמן קבלת נתונים ב-thread של הרשת. ייתכן שלא ניתן יהיה להשתמש בתהליך ההמתנה אם הניווט מפנה אוטומטית בין אתרים, ובמקרה כזה ייתכן שיהיה צורך בתהליך אחר.

שלב 5: מבצעים ניווט

עכשיו, אחרי שהנתונים ותהליך הרינדור מוכנים, כתובת IPC נשלחת מתהליך הדפדפן לתהליך הרינדור כדי לבצע את הניווט. היא גם מעבירה את מקור הנתונים, כך שתהליך הרינדור יכול להמשיך לקבל נתוני HTML. אחרי שמתקבל אישור על כך שתהליך הדפדפן התבצעה בתהליך הרינדור, הניווט הושלם ושלב טעינת המסמך מתחיל.

בשלב הזה סרגל הכתובות מתעדכן ואינדיקטור האבטחה וממשק המשתמש של הגדרות האתר משקפים את פרטי האתר של הדף החדש. היסטוריית הסשנים של הכרטיסייה תעודכן כך שלחצני 'הקודם'/'הבא' יעבירו את המשתמשים לאתר שאליו בדיוק ניווטתם. כדי שיהיה קל לשחזר כרטיסייה או סשן כשסוגרים כרטיסייה או חלון, היסטוריית הסשנים נשמרת בדיסק.

ביצוע הניווט
איור 6: IPC בין הדפדפן לעיבוד של כלי הרינדור, שמבקש לעבד את הדף

שלב נוסף: הטעינה הראשונית הושלמה

לאחר התחייבות לניווט, תהליך הרינדור מכיל טעינה של משאבים ומעבד את הדף. אנו נעבור על הפרטים של מה שקורה בשלב זה בפוסט הבא. אחרי שהכלי לעיבוד "מסיים" את העיבוד, הוא שולח IPC חזרה לתהליך הדפדפן (כלומר, אחרי כל האירועים של onload הופעלו בכל המסגרות בדף וסיימו את ביצועם). בשלב הזה, ה-thread של ממשק המשתמש עוצר את סימן ההרצה של הטעינה בכרטיסייה.

אני אומר "סיום", מכיוון ש-JavaScript בצד הלקוח עדיין יכול לטעון משאבים נוספים ולהציג תצוגות חדשות לאחר נקודה זו.

טעינת דף סיום
איור 7: IPC מהמעבד אל תהליך הדפדפן כדי להודיע שהדף "נטען"

הניווט הפשוט הושלם! אבל מה קורה אם משתמש מוסיף שוב כתובת URL שונה לסרגל הכתובות? ובכן, תהליך הדפדפן מבצע אותם שלבים כדי לנווט לאתר השונה. עם זאת, לפני שאפשר יהיה לעשות זאת, הם צריכים לבדוק באתר שעבר עיבוד אם חשוב להם אירוע beforeunload.

ל-beforeunload יש אפשרות ליצור את ההתראה 'לצאת מהאתר הזה?' כשמנסים לנווט אל מחוץ לכרטיסייה או לסגור אותה. כל מה שנמצא בתוך הכרטיסייה, כולל קוד ה-JavaScript, מטופל בתהליך הרינדור, לכן תהליך הדפדפן צריך לבדוק את תהליך הרינדור הנוכחי כשמתקבלת בקשת ניווט חדשה.

הגורם המטפל באירועים beforeunload
איור 8: IPC מתהליך הדפדפן לתהליך רינדור שמודיע שהוא עומד לנווט לאתר אחר

אם הניווט הופעל מתהליך הרינדור (למשל, משתמש לחץ על קישור או שה-JavaScript בצד הלקוח הפעיל window.location = "https://newsite.com"), תהליך הרינדור יבדוק קודם את רכיבי ה-handler של beforeunload. לאחר מכן הוא עובר את אותו תהליך כמו תהליך הניווט ביוזמת הדפדפן. ההבדל היחיד הוא שבקשת הניווט מתחילה משלב הרינדור ועד לתהליך הדפדפן.

כשהניווט החדש נשלח לאתר אחר מזה שעבר עיבוד כרגע, מופעל תהליך עיבוד נפרד כדי לטפל בניווט החדש, בעוד שתהליך העיבוד הנוכחי נשמר כדי לטפל באירועים כמו unload. למידע נוסף, קראו את הסקירה הכללית על מצבי מחזור החיים של הדף ועל האופן שבו אתם יכולים להתחבר לאירועים בעזרת ה-API של מחזור החיים של הדף.

ניווט חדש והסרת טעינה
איור 9: שני ערכי IPC מתהליך דפדפן לתהליך רינדור חדש, שתפקידם לעבד את הדף ולומר לתהליך הרינדור הישן להסיר את הטעינה

במקרה של Service Worker

אחד מהשינויים שנערכו לאחרונה בתהליך הניווט הזה היה הוספת Service Worker. קובץ שירות (service worker) הוא דרך לכתוב שרת proxy של רשת בקוד האפליקציה, והוא מאפשר למפתחי אתרים לשלוט טוב יותר על הקבצים שנשמרים במטמון באופן מקומי ומתי הם מקבלים נתונים חדשים מהרשת. אם קובץ השירות מוגדר לטעון את הדף מהמטמון, אין צורך לבקש את הנתונים מהרשת.

מה שחשוב לזכור הוא שה-Service Worker הוא קוד JavaScript שפועל בתהליך רינדור. אבל כשבקשת הניווט נכנסת, איך תהליך דפדפן יודע לאתר יש קובץ שירות (service worker)?

חיפוש היקף של קובץ שירות (service worker)
איור 10: תהליך ה-thread של הרשת בתהליך הדפדפן לחיפוש היקף של קובץ השירות (service worker)

כאשר קובץ שירות (service worker) רשום, ההיקף של קובץ השירות נשמר בתור הפניה (מידע נוסף על ההיקף זמין במאמר מחזור החיים של Service Worker). במהלך ניווט, שרשור רשת בודק את הדומיין ביחס להיקפים של קובצי שירות (service worker) רשומים. אם רשום עבור כתובת ה-URL הזו קובץ שירות (service worker), ה-UI thread מוצא תהליך רינדור כדי להפעיל את הקוד של Service Worker. ה-Service Worker עשוי לטעון נתונים מהמטמון, וכך למנוע את הצורך לבקש נתונים מהרשת או לבקש משאבים חדשים מהרשת.

ניווט של Serviceworker
איור 11: ה-thread של ממשק המשתמש בתהליך של הדפדפן שמתחיל תהליך רינדור כדי לטפל ב-Service Workers; שרשור של עובד בתהליך רינדור מבקש נתונים מהרשת.

מעבר בין תהליך הדפדפן לתהליך הרינדור עלול לגרום לעיכובים, אם קובץ השירות (service worker) מחליט בסופו של דבר לבקש נתונים מהרשת. טעינה מראש של ניווט היא מנגנון לזירוז התהליך הזה על ידי טעינת משאבים במקביל להפעלה של Service Worker. היא מסמנת את הבקשות האלה עם כותרת, ומאפשרת לשרתים להחליט אם לשלוח תוכן שונה לבקשות האלה. לדוגמה, רק נתונים מעודכנים במקום מסמך מלא.

טעינה מראש של ניווט
איור 12: ה-thread של ממשק המשתמש בתהליך הדפדפן שמתחיל תהליך רינדור כדי לטפל ב-Service Worker תוך כדי יציאה של בקשת הרשת במקביל

סיכום

בפוסט הזה בדקנו מה קורה במהלך הניווט, והסברנו איך הקוד של אפליקציית האינטרנט, כמו כותרות התגובות ו-JavaScript בצד הלקוח, משתלבים עם הדפדפן. אם תדעו אילו שלבים הדפדפן עובר כדי לקבל נתונים מהרשת, תוכלו להבין בקלות למה פותחו ממשקי API, כמו טעינה מראש של ניווט. בפוסט הבא נפרט איך הדפדפן מעריך את HTML/CSS/JavaScript כדי לעבד דפים.

נהנית מהפוסט? אם יש לכם שאלות או הצעות לפוסט הבא, אשמח לשמוע מכם בקטע התגובות בהמשך או @kosamari ב-Twitter.

השלב הבא: תהליכים פנימיים של תהליך רינדור