طريقة عمل المتصفِّحات

وراء كواليس متصفحات الويب الحديثة

تمهيد

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

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

"بول أيرش"، فريق علاقات مطوّري برامج Chrome

مقدمة

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

المتصفحات التي سنتحدث عنها

هناك خمسة متصفّحات رئيسية يتم استخدامها على أجهزة الكمبيوتر المكتبي اليوم: Chrome وInternet Explorer وFirefox وSafari وOpera. على الأجهزة الجوّالة، تتوفّر المتصفحات الرئيسية التالية: متصفّح Android ومتصفّح iPhone ومتصفّح Opera Mini ومتصفّح Opera Mobile ومتصفّح UC ومتصفّحات Nokia S40/S60 ومتصفّح Chrome، وجميعها تستند إلى WebKit باستثناء متصفّحات Opera. سأقدّم أمثلة من متصفّحات Firefox وChrome وSafari (التي تعتمد على مصدر مفتوح جزئيًا). وفقًا لإحصائيات StatCounter (اعتبارًا من حزيران (يونيو) 2013)، يشكّل Chrome وFirefox وSafari حوالي 71% من استخدامات المتصفحات المتوافقة مع أجهزة الكمبيوتر المكتبي على مستوى العالم. على الأجهزة الجوّالة، يشكّل متصفّح Android وiPhone وChrome حوالي %54 من معدّل الاستخدام.

الوظيفة الرئيسية للمتصفّح

تتمثّل الوظيفة الرئيسية للمتصفّح في عرض مورد الويب الذي تختاره، وذلك من خلال طلبه من الخادم وعرضه في نافذة المتصفّح. يكون المرجع عادةً مستند HTML، ولكن قد يكون أيضًا ملف PDF أو صورة أو نوعًا آخر من المحتوى. يحدّد المستخدم موقع المورد باستخدام معرّف موارد منتظم (URI).

يتم تحديد طريقة تفسير المتصفّح لملفات HTML وعرضها في مواصفات HTML وCSS. يتم الاحتفاظ بهذه المواصفات من قِبل مؤسسة W3C (اتحاد شبكة الويب العالمية)، وهي المؤسسة التي تنظم المعايير على الويب. لعدة سنوات، كانت المتصفّحات متوافقة مع جزء من المواصفات فقط وطوّرت إضافاتها الخاصة. وقد تسبَّب ذلك في مشاكل خطيرة تتعلّق بالتوافق مع مؤلفي محتوى الويب. تتوافق معظم المتصفّحات اليوم مع المواصفات بشكلٍ أو بآخر.

تتشارك واجهات مستخدم المتصفّحات الكثير من العناصر. من بين عناصر واجهة المستخدم الشائعة:

  1. شريط العناوين لإدراج معرّف موارد منتظم
  2. زرَّا الرجوع وللأمام
  3. خيارات وضع إشارة مرجعية
  4. زر "إعادة التحميل" و"إيقاف" لإعادة تحميل المستندات الحالية أو إيقاف تحميلها
  5. زر الصفحة الرئيسية الذي ينقلك إلى صفحتك الرئيسية

والغريب أنّه لا يتم تحديد واجهة مستخدم المتصفّح ضمن أي مواصفات رسمية، بل هو نابع من ممارسات جيدة مصمَّمة على مدار سنوات من الخبرة ومن خلال محاكاة أحد المتصفحات لبعضها البعض. لا تحدّد مواصفات HTML5 عناصر واجهة المستخدم التي يجب أن يتضمّنها المتصفّح، ولكنها تسرد بعض العناصر الشائعة. ومن بين هذه الأشرطة شريط العناوين وشريط الحالة وشريط الأدوات. هناك بالطبع ميزات فريدة لمتصفّح معيّن، مثل مدير عمليات التنزيل في Firefox.

البنية الأساسية العالية المستوى

تشمل المكونات الرئيسية للمتصفّح ما يلي:

  1. واجهة المستخدم: تشمل شريط العناوين وزر الرجوع/الأمام وقائمة وضع الإشارات المرجعية وما إلى ذلك. ويتم عرض كل جزء من أجزاء المتصفّح باستثناء النافذة التي تظهر فيها الصفحة المطلوبة.
  2. محرك المتصفّح: يوجّه الإجراءات بين واجهة المستخدم ومحرك العرض.
  3. محرك العرض: هو المسؤول عن عرض المحتوى المطلوب. على سبيل المثال، إذا كان المحتوى المطلوب هو HTML، يحلّل محرّك العرض HTML وCSS ويعرض المحتوى الذي تم تحليله على الشاشة.
  4. الشبكات: لطلبات الشبكة، مثل طلبات HTTP، باستخدام عمليات تنفيذ مختلفة لأنظمة التشغيل المختلفة من خلال واجهة مستقلة عن النظام الأساسي
  5. الخلفية في واجهة المستخدم: تُستخدَم لرسم التطبيقات المصغّرة الأساسية، مثل مربّعات الاختيار والنوافذ. تعرِض هذه الخلفية واجهة عامة ليست خاصة بالنظام الأساسي. أسفله، يستخدم طرق واجهة مستخدم نظام التشغيل.
  6. مترجم JavaScript تُستخدَم لتحليل رمز JavaScript وتنفيذه.
  7. تخزين البيانات: هذه طبقة ثبات. قد يحتاج المتصفّح إلى حفظ جميع أنواع البيانات محليًا، مثل ملفات تعريف الارتباط. وتتوافق المتصفّحات أيضًا مع آليات التخزين مثل localStorage وIndexedDB وWebSQL وFileSystem.
مكونات المتصفّح
الشكل 1: مكوّنات المتصفّح

من المهمّ الإشارة إلى أنّ المتصفّحات، مثل Chrome، تشغّل عدّة نُسخ من محرّك العرض: نسخة لكل علامة تبويب. يتم تشغيل كل علامة تبويب في عملية منفصلة.

محركات العرض

تقع على عاتق محرّك العرض مسؤولية العرض، أي عرض المحتوى المطلوب على شاشة المتصفّح.

يمكن لمحرك العرض تلقائيًا عرض المستندات والصور بتنسيق HTML وXML. ويمكنه عرض أنواع أخرى من البيانات من خلال الإضافات أو الإضافات، على سبيل المثال، عرض مستندات PDF باستخدام مكوّن إضافي لعارض PDF. ومع ذلك، سنركّز في هذا الفصل على حالة الاستخدام الرئيسية: عرض صفحات HTML والصور التي تم تنسيقها باستخدام CSS.

تستخدم المتصفحات المختلفة محرّكات عرض مختلفة: يستخدم Internet Explorer محرّك Trident، ويستخدم Firefox محرّك Gecko، ويستخدم Safari محرّك WebKit. ويستخدم Chrome وOpera (من الإصدار 15) Blink، وهي متشعبة من WebKit.

WebKit هو محرّك عرض مفتوح المصدر بدأ كمحرك لنظام التشغيل Linux وتم تعديله من قِبل Apple ليتوافق مع نظامَي التشغيل Mac وWindows.

المسار الرئيسي

سيبدأ محرّك العرض في الحصول على محتوى المستند المطلوب من طبقة الشبكة. وسيتم ذلك عادةً في أجزاء بحجم 8 كيلوبايت.

بعد ذلك، إليك الخطوات الأساسية لمحرك العرض:

خطوات عمل محرّك العرض الأساسية
الشكل 2: التدفق الأساسي لمحرّك العرض

سيبدأ محرّك العرض في تحليل مستند HTML وتحويل العناصر إلى عُقد DOM في شجرة اسمها "شجرة المحتوى". سيحلّل المحرّك بيانات الأنماط، سواء في ملفات CSS الخارجية أو في عناصر الأنماط. سيتم استخدام معلومات التصميم مع التعليمات المرئية في HTML لإنشاء شجرة أخرى: شجرة العرض.

تحتوي شجرة العرض على مستطيلات تتضمّن سمات مرئية مثل اللون والسمات. المستطيلات بالترتيب الصحيح ليتم عرضها على الشاشة

بعد إنشاء شجرة التقديم، تخضع لمعالجة "التنسيق". وهذا يعني منح كل عقدة الإحداثيات الدقيقة التي يجب أن تظهر بها على الشاشة. تتمثل المرحلة التالية في الطلاء - سيتم اجتياز شجرة العرض وسيتم رسم كل عقدة باستخدام طبقة الخلفية لواجهة المستخدم.

من المهمّ فهم أنّ هذه عملية تدريجية. لتوفير تجربة أفضل للمستخدم، سيحاول محرّك العرض عرض المحتوى على الشاشة في أقرب وقت ممكن. ولن ينتظر حتى يتم تحليل كل محتوى HTML قبل البدء في إنشاء شجرة العرض وتنسيقها. سيتم تحليل أجزاء من المحتوى وعرضها، بينما تستمر العملية مع بقية المحتوى الذي يستمر وصوله من الشبكة.

أمثلة على مسارات المستخدمين الرئيسية

المسار الرئيسي WebKit.
الشكل 3: العملية الرئيسية لمحرك WebKit
التدفق الرئيسي لمحرّك عرض Gecko من Mozilla
الشكل 4: العملية الرئيسية لمحرك عرض المحتوى Gecko من Mozilla

من الشكلَين 3 و4، يمكنك ملاحظة أنّه على الرغم من أنّ WebKit وGecko يستخدمان مصطلحات مختلفة قليلاً، إلا أنّ الخطوات متشابهة بشكل أساسي.

يسمى الوزغ شجرة العناصر المنسقة مرئيًا "شجرة الإطار". كل عنصر هو إطار. يستخدم WebKit مصطلح "شجرة التقديم" ويتألف من "عناصر التقديم". تستخدم مجموعة أدوات WebKit مصطلح "تخطيط" لوضع العناصر، بينما يطلق عليها Gecko اسم "Reflow". "المرفق" هو مصطلح WebKit لربط عقد نموذج DOM والمعلومات المرئية لإنشاء شجرة العرض. هناك اختلاف بسيط غير دلالي وهو أنّ Gecko يحتوي على طبقة إضافية بين HTML وشجرة DOM. ويُطلق على هذا العنصر اسم "وحدة معالجة المحتوى"، وهو عبارة عن مصنع لإنشاء عناصر DOM. سنتحدث عن كل جزء من التدفق:

التحليل - العام

بما أنّ التحليل هو عملية مهمة جدًا في محرّك العرض، سنتناوله بمزيد من التفصيل. لنبدأ بمقدمة صغيرة عن التحليل.

تحليل المستند يعني ترجمته إلى بنية يمكن للرمز البرمجي استخدامها. وتكون نتيجة التحليل عادةً شجرة من العقد التي تمثّل بنية المستند. وهذا ما يسمى شجرة التحليل أو شجرة بناء الجملة.

على سبيل المثال، يمكن أن يؤدي تحليل التعبير 2 + 3 - 1 إلى عرض هذه الشجرة:

عقدة شجرة التعبير الرياضي
الشكل 5: عقدة شجرة التعبير الحسابي

القواعد اللغوية

يستند التحليل إلى قواعد البنية التي يلتزم بها المستند: اللغة أو التنسيق الذي كتب به. يجب أن يتضمّن كل تنسيق يمكنك تحليله قواعد نحوية محدّدة تتألف من المفردات وقواعد التركيب. وتُعرف هذه اللغة بالقواعد النحوية الخالية من السياق، علمًا بأنّ اللغات البشرية ليست بهذه اللغات، وبالتالي لا يمكن تحليلها باستخدام أساليب التحليل التقليدية.

مجموعة المحلّل اللغوي - أداة تحليل البنية

يمكن تقسيم التحليل إلى عمليتين فرعيتين: التحليل المعجمي والتحليل النحوي.

التحليل اللفظي هو عملية تقسيم الإدخال إلى وحدات. الرموز هي مفردات اللغة: مجموعة من الوحدات الأساسية الصالحة. باللغة البشرية، سيتألف من جميع الكلمات التي تظهر في القاموس الخاص بهذه اللغة.

تحليل بناء الجملة هو تطبيق قواعد بناء جملة اللغة.

عادةً ما تقسم برامج التحليل العمل بين مكوّنين: أداة تحليل البنية (يُشار إليها أحيانًا باسم أداة تقسيم السلسلة إلى وحدات) المسؤولة عن تقسيم الإدخال إلى وحدات صالحة، وأداة التحليل المسؤولة عن إنشاء شجرة التحليل من خلال تحليل بنية المستند وفقًا لقواعد بنية اللغة.

يعرف أداة تحليل البنية كيفية إزالة الأحرف غير ذات الصلة، مثل المسافات البيضاء وفواصل الأسطر.

من المستند المصدر إلى أشجار التحليل
الشكل 6: من المستند المصدر إلى أشجار التحليل

عملية التحليل تكرارية. سيطلب المُحلِّل عادةً من أداة تحليل البنية رمزًا مميزًا جديدًا وسيحاول مطابقة الرمز المميّز مع إحدى قواعد البنية. في حال تطابق قاعدة، ستتم إضافة عقدة تشير إلى الرمز المميّز إلى شجرة التحليل وسيطلب منك المُحلِّل رمزًا مميّزًا آخر.

إذا لم تتطابق أي قاعدة، فسيخزن المحلل الرمز المميز داخليًا، ويستمر في طلب الرموز المميزة حتى يتم العثور على قاعدة تطابق جميع الرموز المميزة المخزنة داخليًا. وإذا لم يتم العثور على أي قاعدة، فسيقدم المحلل استثناءً. وهذا يعني أنّ المستند غير صالح وكان يتضمّن أخطاء في البنية.

ترجمة

في كثير من الحالات، لا تكون شجرة التحليل هي المنتج النهائي. غالبًا ما يتم استخدام التحليل في الترجمة: تحويل مستند الإدخال إلى تنسيق آخر. ومن الأمثلة على ذلك عملية التجميع. يقوم برنامج التحويل البرمجي، الذي يجمع رمز المصدر إلى رمز الجهاز، أولاً بتحليله إلى شجرة تحليل ثم ترجمة الشجرة إلى مستند رمز جهاز.

مسار التحويل البرمجي
الشكل 7: تسلسل عملية التجميع

مثال على التحليل

في الشكل 5، أنشأنا شجرة تحليل من تعبير رياضي. لنحاول تحديد لغة رياضية بسيطة والاطّلاع على عملية التحليل.

البنية:

  1. الوحدات الأساسية لبنية اللغة هي عبارة عن تعبيرات ومصطلحات وعمليات.
  2. يمكن أن تتضمّن لغتنا أي عدد من التعبيرات.
  3. يتم تعريف التعبير على أنه "عبارة" متبوعة بـ "عملية" متبوعة بمصطلح آخر
  4. العملية هي رمز مميز موجب أو رمز مميز سالب.
  5. العبارة هي رمز عدد صحيح أو تعبير.

لنحلِّل الإدخال 2 + 3 - 1.

أول سلسلة فرعية تتطابق مع قاعدة هي 2: وفقًا للقاعدة رقم 5، هي عبارة. المطابقة الثانية هي 2 + 3: تتطابق هذه مع القاعدة الثالثة: عبارة تليها عملية تليها عبارة أخرى. لن يتم تسجيل المطابقة التالية إلا في نهاية الإدخال. 2 + 3 - 1 هو تعبير لأنّنا نعلم أنّ 2 + 3 عبارة، لذا لدينا عبارة متبوعة بعملية متبوعة بعبارة أخرى. لن تتطابق 2 + + مع أي قاعدة، وبالتالي فإنّها إدخال غير صالح.

تعريفات رسمية للمفردات والبنية

يتم التعبير عن المفردات عادةً باستخدام التعبيرات العادية.

على سبيل المثال، سيتم تعريف لغتنا على النحو التالي:

INTEGER: 0|[1-9][0-9]*
PLUS: +
MINUS: -

كما ترى، يتم تعريف الأعداد الصحيحة من خلال تعبير عادي.

يتم عادةً تعريف البنية بتنسيق يُعرف باسم BNF. سيتم تعريف لغتنا على النحو التالي:

expression :=  term  operation  term
operation :=  PLUS | MINUS
term := INTEGER | expression

قلنا أنّه يمكن تحليل اللغة بواسطة المحللات العادية إذا كانت قواعدها النحوية خالية من السياق. إنّ التعريف السهل للقواعد النحوية الحرة للسياق هو قاعدة يمكن التعبير عنها بالكامل في BNF. للحصول على تعريف رسمي، يُرجى الاطّلاع على مقالة ويكيبيديا حول القواعد النحوية بدون سياق.

أنواع الأدوات التحليلية

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

لنرى كيف سيحلّل نوعا المُحلِّلَين مثالنا.

سيبدأ المحلل اللغوي من أعلى لأسفل من قاعدة المستوى الأعلى: سيحدد 2 + 3 كتعبير. سيحدّد بعد ذلك 2 + 3 - 1 كتعبير (تتطوّر عملية تحديد التعبير، وتتطابق مع القواعد الأخرى، ولكن نقطة البداية هي قاعدة المستوى الأعلى).

سيفحص المُحلِّل من الأسفل إلى الأعلى الإدخال إلى أن تتم مطابقة قاعدة. وسيتم عند ذلك استبدال الإدخال المطابق بالقاعدة. سيستمر ذلك إلى أن تنتهي عملية الإدخال. يتم وضع التعبير المطابق جزئيًا في مكدس المحلل اللغوي.

تكديس الإدخال
2 + 3 - 1
عبارة + 3 - 1
عملية محدّدة 3 - 1
تعبير - 1
عملية التعبير 1
تعبير -

يُطلق على هذا النوع من المحللات اللغوية من أسفل إلى أعلى اسم المحلل اللغوي لتقليل shift، لأن الإدخال تم نقله إلى اليمين (تخيل مؤشرًا يشير أولاً إلى بداية الإدخال وينتقل إلى اليمين) ويتم تقليله تدريجيًا إلى قواعد البنية.

إنشاء برامج تحليل تلقائيًا

هناك أدوات يمكنها إنشاء أداة تحليل. ويمكنك تزويدها بقواعد لغتك، أي مفرداتها وقواعد النحو، وتنشئ هذه الأدوات معرِّفًا نحويًا صالحًا. يتطلب إنشاء محلل لغوي فهمًا عميقًا للتحليل وليس من السهل إنشاء محلل محلل محسَّن يدويًا، لذا يمكن أن تكون أدوات إنشاء المحللات مفيدة للغاية.

وتستخدم مجموعة أدوات WebKit مولّدي محللي بيانات معروفين هما: Flex لإنشاء قارئ لغوي وBison لإنشاء محلّل لغوي (قد تواجههما باسم Lex وYacc). الإدخال المرن هو ملف يحتوي على تعريفات التعبير العادي للرموز المميزة. تتمثل مدخلات Bison في قواعد بنية اللغة بتنسيق BNF.

محلّل HTML

وظيفة محلِّل HTML هي تحليل ترميز HTML إلى شجرة تحليل.

قواعد HTML

يتم تحديد مفردات وبناء جملة HTML في المواصفات التي أنشأتها مؤسسة W3C.

كما رأينا في مقدمة التحليل، يمكن تعريف بنية القواعد النحوية رسميًا باستخدام تنسيقات مثل BNF.

للأسف، لا تنطبق جميع مواضيع المحلِّل اللغوي التقليدي على HTML (لم أطرحها على سبيل المتعة فقط، بل سيتم استخدامها في تحليل CSS وJavaScript). لا يمكن تحديد HTML بسهولة من خلال القواعد النحوية الخالية من السياق التي تحتاج إليها المحللات.

هناك تنسيق رسمي لتعريف HTML، وهو DTD (تعريف نوع المستند)، ولكنّه ليس قواعد نحوية خالية من السياق.

قد يبدو هذا الأمر غريبًا للوهلة الأولى، لأنّ HTML قريب من XML. هناك الكثير من برامج تحليل ملفات XML المتاحة. هناك اختلاف في XML بين HTML - XHTML - فما الفرق الكبير؟

والفرق هو أنّ نهج HTML أكثر "تسامحًا": فهو يتيح لك حذف علامات معيّنة (تتم إضافتها بعد ذلك بشكل ضمني)، أو حذف علامات البداية أو النهاية في بعض الأحيان، وما إلى ذلك. بشكل عام، يُعدّ هذا التنسيق "لينًا"، على عكس تنسيق XML الجامد والمتطلّب.

هذه التفاصيل التي تبدو بسيطة تُحدث فرقًا. من ناحية، هذا هو السبب الرئيسي لانتشار لغة HTML كثيرًا، فهي تغفر أخطائك وتسهّل حياة مؤلفي محتوى الويب. من ناحية أخرى، يجعل ذلك من الصعب كتابة قواعد نحوية رسمية. باختصار، لا يمكن تحليل HTML بسهولة باستخدام أدوات التحليل التقليدية، لأنّ قواعده النحوية ليست خالية من السياق. لا يمكن لأدوات تحليل XML تحليل صفحات HTML.

ملفات HTML DTD

تعريف HTML بتنسيق DTD يُستخدَم هذا التنسيق لتحديد لغات عائلة SGML. يحتوي التنسيق على تعريفات لجميع العناصر المسموح بها وسمات هذه العناصر وترتيبها الهرمي. كما رأينا سابقًا، لا يشكّل DTD في HTML بنية نحوية خالية من السياق.

هناك بعض الصيغ المختلفة لـ DTD. يتوافق الوضع المتشدد مع المواصفات فقط، إلا أن الأوضاع الأخرى تحتوي على دعم للترميز الذي استخدمته المتصفحات في الماضي. ويهدف ذلك إلى التوافق مع الإصدارات السابقة من المحتوى. تتوفّر الضريبة الصارمة الحالية للضريبة المستقطعة من المنبع هنا: www.w3.org/TR/html4/strict.dtd

نموذج العناصر في المستند (DOM)

شجرة الإخراج (المعروفة باسم "شجرة التحليل") هي شجرة لعناصر DOM وعقد السمات. يشير الاختصار DOM إلى نموذج كائن المستند. وهو تمثيل الكائن لمستند HTML وواجهة عناصر HTML للعالم الخارجي، مثل JavaScript.

جذر الشجرة هو عنصر المستند.

علاقة نموذج العناصر في المستند (DOM) تقريبًا بالترميز. على سبيل المثال:

<html>
  <body>
    <p>
      Hello World
    </p>
    <div> <img src="example.png"/></div>
  </body>
</html>

سيتم ترجمة هذا الترميز إلى شجرة نموذج DOM التالية:

شجرة نموذج العناصر في المستند لنموذج الترميز
الشكل 8: شجرة نموذج عناصر المستند لمثال الترميز

مثل HTML، تحدّد مؤسسة W3C لغة DOM. يُرجى الاطّلاع على www.w3.org/DOM/DOMTR. وهي عبارة عن مواصفة عامة لمعالجة المستندات. تصف وحدة معيّنة عناصر معيّنة في HTML. يمكن العثور على تعريفات HTML هنا: www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

عندما أقول أنّ الشجرة تحتوي على عقد DOM، أقصد أنّ الشجرة تم إنشاؤها من عناصر تنفِّذ إحدى واجهات DOM. تستخدم المتصفّحات عمليات تنفيذ محدّدة لها سمات أخرى يستخدمها المتصفّح داخليًا.

خوارزمية التحليل

كما رأينا في الأقسام السابقة، لا يمكن تحليل صفحات HTML باستخدام برامج التحليل العادية من الأعلى إلى الأسفل أو من الأسفل إلى الأعلى.

في ما يلي الأسباب:

  1. طبيعة اللغة المرنة
  2. حقيقة أنّ المتصفّحات تتضمّن سعة تقليدية للخطأ لتتوافق مع الحالات المعروفة لصفحات HTML غير الصالحة
  3. عملية التحليل قابلة لإعادة الدخول. بالنسبة إلى اللغات الأخرى، لا يتغيّر المصدر أثناء التحليل، ولكن في HTML، يمكن أن يضيف الرمز الديناميكي (مثل عناصر النصوص البرمجية التي تحتوي على طلبات document.write()) وحدات ترميز إضافية، لذا تعدّل عملية التحليل الإدخال فعليًا.

لا يمكن استخدام تقنيات التحليل العادية، لذا تنشئ المتصفّحات أدوات تحليل مخصّصة لتحليل HTML.

يتم وصف خوارزمية التحليل بالتفصيل في مواصفات HTML5. تتألف الخوارزمية من مرحلتين: إنشاء الرموز وإنشاء الشجرة.

والترميز هو التحليل المعجم، الذي يحلّل الإدخال إلى رموز مميّزة. تشمل الرموز المميّزة في HTML علامات البداية والنهاية وأسماء السمات وقيمها.

يتعرف أداة إنشاء الرموز على الرمز المميز، ويقدمه إلى الدالة الإنشائية للشجرة، ويستهلك الحرف التالي للتعرف على الرمز المميز التالي، وهكذا حتى نهاية الإدخال.

مسار تحليل HTML (مأخوذ من مواصفات HTML5)
الشكل 9: عملية تحليل HTML (مأخوذة من مواصفات HTML5)

خوارزمية تقسيم المحتوى إلى رموز مميّزة

الناتج الذي تقدّمه الخوارزمية هو رمز مميّز لـ HTML. ويتم التعبير عن الخوارزمية في صورة جهاز حالة. تستهلك كل حالة حرفًا واحدًا أو أكثر من بث الإدخال وتُعدّل الحالة التالية وفقًا لهذه الأحرف. ويتأثر القرار بحالة تقسيم المحتوى إلى وحدات حاليًا وحالة إنشاء الشجرة. وهذا يعني أنّ الحرف المستهلك نفسه سيؤدي إلى نتائج مختلفة للحالة التالية الصحيحة، وذلك استنادًا إلى الحالة الحالية. الخوارزمية معقّدة للغاية بحيث لا يمكن وصفها بالكامل، لذا فلنشاهد مثالاً بسيطًا سيساعدنا على فهم المبدأ.

مثال أساسي: تقسيم رمز HTML التالي إلى وحدات:

<html>
  <body>
    Hello world
  </body>
</html>

الحالة الأولية هي "حالة البيانات". عند العثور على الحرف <، يتم تغيير الحالة إلى "حالة فتح العلامة". يؤدي استخدام الحرف a-z إلى إنشاء "رمز بدء العلامة"، ويتم تغيير الحالة إلى "حالة اسم العلامة". ونبقى في هذه الحالة إلى أن يتم استخدام الحرف >. تتم إضافة كل حرف إلى اسم الرمز المميّز الجديد. وفي هذه الحالة، يكون الرمز المميّز الذي تم إنشاؤه هو رمز html.

وعند الوصول إلى العلامة >، يتم إطلاق الرمز المميّز الحالي وتتغيّر الحالة مرة أخرى إلى "حالة البيانات". سيتم التعامل مع علامة <body> بالخطوات نفسها. تمّ حتى الآن عرض علامتَي html وbody. لقد عدنا الآن إلى "حالة البيانات". سيؤدي استخدام الحرف H من Hello world إلى إنشاء رمز مميّز لأحد الأحرف وإصداره، ويستمر ذلك حتى يتم الوصول إلى < بقيمة </body>. سنُنشئ رمزًا مميزًا لكل حرف من Hello world.

لقد عدنا الآن إلى "حالة فتح العلامة". سيؤدي استخدام الإدخال التالي / إلى إنشاء end tag token والانتقال إلى "حالة اسم العلامة". سنبقى في هذه الحالة مرة أخرى إلى أن نصل إلى >.بعد ذلك، سيتمّ إصدار رمز العلامة الجديد وسنعود إلى "حالة البيانات". سيتم التعامل مع الإدخال </html> كما في الحالة السابقة.

تقسيم مثال الإدخال إلى وحدات
الشكل 10: تقسيم مثال الإدخال إلى وحدات ترميز

خوارزمية إنشاء الأشجار

عند إنشاء المُحلِّل، يتم إنشاء عنصر المستند. خلال مرحلة إنشاء الشجرة، سيتم تعديل شجرة نموذج DOM التي تحتوي على المستند في جذرها وسيتمّ إضافة عناصر إليها. ستتم معالجة كل عقدة يُنشئها أداة تقسيم الكلمات بواسطة أداة إنشاء الشجرة. بالنسبة إلى كل رمز مميّز، تحدّد المواصفة عنصر نموذج عناصر المستند (DOM) ذي الصلة به والذي سيتم إنشاؤه لهذا الرمز المميّز. تتم إضافة العنصر إلى شجرة نموذج عناصر المستند (DOM) وكذلك إلى حزمة العناصر المفتوحة. وتُستخدم هذه الحزمة لتصحيح حالات عدم التطابق في التداخل والعلامات غير المغلقة. وتُعرف الخوارزمية أيضًا باسم آلة الحالات. تُسمى الحالات "أوضاع الإدراج".

لنطّلِع على عملية إنشاء الشجرة لمثال الإدخال التالي:

<html>
  <body>
    Hello world
  </body>
</html>

إنّ الإدخال في مرحلة إنشاء الشجرة هو تسلسل من الرموز المميزة من مرحلة إنشاء الرموز المميزة. الوضع الأول هو "الوضع الأوّلي". سيؤدي تلقّي الرمز المميّز "html" إلى الانتقال إلى وضع "قبل html" وإعادة معالجة الرمز المميّز في هذا الوضع. سيؤدي ذلك إلى إنشاء عنصر HTMLHtmlElement، الذي سيتم إلحاقه بكائن المستند الجذر.

سيتم تغيير الحالة إلى "قبل العنوان". بعد ذلك، يتم استلام الرمز المميّز "body". سيتم إنشاء HTMLHeadElement ضمنيًا على الرغم من عدم توفّر رمز "head" المميّز وستتم إضافته إلى العرض التدرّجي.

ننتقل الآن إلى وضع "في العنوان" ثم إلى وضع "بعد العنوان". تتم إعادة معالجة الرمز المميّز للنص الأساسي، ويتم إنشاء عنصر HTMLBodyElement وإدراجه، ويتم نقل الوضع إلى "in body".

تم الآن استلام الرموز المميّزة للحروف في سلسلة "مرحبًا بالجميع". سيؤدي العنصر الأول إلى إنشاء عقدة "Text" وإدراجها وسيتم إلحاق الأحرف الأخرى بهذه العقدة.

سيؤدي استلام الرمز المميّز لنهاية النص إلى نقل البيانات إلى وضع "بعد النص". سنتلقّى الآن علامة نهاية html التي ستنقلنا إلى وضع "بعد الجسم". سيؤدي تلقّي رمز مميز لنهاية الملف إلى إنهاء عملية التحليل.

إنشاء شجرة لمثال على ملف HTML
الشكل 11: إنشاء شجرة لمثال على ملف HTML

الإجراءات التي يتم تنفيذها عند انتهاء التحليل

في هذه المرحلة، سيضع المتصفح علامة على المستند على أنه تفاعلي ويبدأ في تحليل النصوص البرمجية الموجودة في الوضع "المؤجَّل": وهي النصوص التي يجب تنفيذها بعد تحليل المستند. سيتم بعد ذلك ضبط حالة المستند على "مكتمل" وسيتم تنشيط حدث "تحميل".

يمكنك الاطّلاع على الخوارزميات الكاملة لتجزئة المحتوى وإنشاء الشجرة في مواصفات HTML5.

مدى التسامح مع الأخطاء في المتصفّحات

لا تظهر لك أبدًا رسالة الخطأ "البنية غير صالحة" في صفحة HTML. تعمل المتصفّحات على حلّ أي محتوى غير صالح ومواصلة العمل.

خذ رمز HTML هذا على سبيل المثال:

<html>
  <mytag>
  </mytag>
  <div>
  <p>
  </div>
    Really lousy HTML
  </p>
</html>

من المؤكد أنّني انتهكت حوالي مليون قاعدة ("mytag" ليست علامة عادية، وتداخل عناصر "p" و"div" بشكل خاطئ وغير ذلك)، ولكن لا يزال المتصفّح يعرضها بشكل صحيح ولا يُبلغ عن أي مشكلة. وبالتالي، فإنّ الكثير من رموز التحليل تعمل على تصحيح أخطاء مؤلفي HTML.

إنّ معالجة الأخطاء متّسقة تمامًا في المتصفّحات، ولكن من المدهش أنّها لم تكن جزءًا من مواصفات HTML. مثل ميزة وضع الإشارات المرجعية وأزرار الرجوع/التقديم، فإنّ ميزة وضع العلامات على الصفحات هي ميزة تم تطويرها في المتصفّحات على مدار السنوات. هناك بنى HTML غير صالحة معروفة تتكرّر على العديد من المواقع الإلكترونية، ويحاول المتصفّحات إصلاحها بطريقة متوافقة مع المتصفّحات الأخرى.

وتحدّد مواصفات HTML5 بعض هذه المتطلبات. (يلخِّص WebKit ذلك بشكل جيد في التعليق في بداية فئة معالج HTML).

يحلّل المُحلِّل الإدخال المُقسَّم إلى وحدات في المستند، ما يؤدّي إلى إنشاء شجرة المستند. إذا كان المستند منظَّمًا بشكل جيد، يكون تحليله بسيطًا.

علينا التعامل مع العديد من ملفات HTML غير المنسَّقة بشكل جيد، لذا يجب أن يكون محلل النحو متسامحًا مع الأخطاء.

علينا الانتباه إلى حالات الخطأ التالية على الأقل:

  1. يُحظر صراحةً العنصر الذي تتم إضافته داخل بعض العلامات الخارجية. في هذه الحالة، يجب إغلاق جميع العلامات حتى العلامة التي تحظر العنصر، ثم إضافته بعد ذلك.
  2. لا يُسمح لنا بإضافة العنصر مباشرةً. قد يكون السبب أنّ الشخص الذي يكتب المستند قد نسي علامة معيّنة في المنتصف (أو أنّ العلامة بينهما اختيارية). قد يحدث ذلك مع العلامات التالية: HTML HEAD BODY TBODY TR TD LI (هل نسيت أي علامة؟).
  3. نريد إضافة عنصر حظر داخل عنصر مضمّن. أغلِق جميع العناصر المضمّنة حتى العنصر التالي من النوع "كتلة".
  4. إذا لم يساعد ذلك، أغلِق العناصر إلى أن يُسمح لنا بإضافة العنصر أو تجاهل العلامة.

إليك بعض الأمثلة على تحمل أخطاء WebKit:

</br> بدلاً من <br>

تستخدم بعض المواقع الإلكترونية </br> بدلاً من <br>. ولكي يكون WebKit متوافقًا مع IE وFirefox، يتعامل معه WebKit مع هذا الأمر مثل <br>.

الرمز:

if (t->isCloseTag(brTag) && m_document->inCompatMode()) {
     reportError(MalformedBRError);
     t->beginTag = true;
}

يُرجى العلم أنّ معالجة الخطأ داخلية، ولن يتم عرضها للمستخدم.

جدول غير مرغوب فيه

الجدول الضال هو جدول داخل جدول آخر، ولكن ليس داخل خلية جدول.

على سبيل المثال:

<table>
  <table>
    <tr><td>inner table</td></tr>
  </table>
  <tr><td>outer table</td></tr>
</table>

سيغيّر WebKit التدرّج الهرمي إلى جدولَين متشابهَين:

<table>
  <tr><td>outer table</td></tr>
</table>
<table>
  <tr><td>inner table</td></tr>
</table>

الرمز:

if (m_inStrayTableContent && localName == tableTag)
        popBlock(tableTag);

يستخدم WebKit حزمة لمحتوى العنصر الحالي: سيُخرج الجدول الداخلي من حزمة الجدول الخارجي. ستصبح الجداول الآن أخوة.

عناصر النموذج المتداخلة

في حال وضع المستخدم نموذجًا داخل نموذج آخر، يتم تجاهل النموذج الثاني.

الرمز:

if (!m_currentFormElement) {
        m_currentFormElement = new HTMLFormElement(formTag,    m_document);
}

تسلسل هرمي للعلامة عميق جدًا

يوضّح التعليق ذلك بوضوح.

bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)
{

unsigned i = 0;
for (HTMLStackElem* curr = m_blockStack;
         i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;
     curr = curr->next, i++) { }
return i != cMaxRedundantTagDepth;
}

علامات نهاية html أو النص الأساسي في غير مكانها

مرة أخرى، يوضّح التعليق نفسه.

if (t->tagName == htmlTag || t->tagName == bodyTag )
        return;

لذا، يجب على مؤلفي الويب الانتباه إلى كتابة رمز HTML صحيح، ما لم يريدوا أن يظهروا كمثال في مقتطف رمز WebKit للتسامح مع الأخطاء.

تحليل CSS

هل تتذكر مفاهيم التحليل في المقدمة؟ على عكس HTML، لغة CSS هي بنية نحوية بدون سياق ويمكن تحليلها باستخدام أنواع التحليلات الموضّحة في المقدّمة. في الواقع، تحدّد مواصفات CSS قواعد النحو والمفردات في CSS.

لنلقِ نظرة على بعض الأمثلة:

يتم تحديد القواعد النحوية للمفردات (المفردات) من خلال التعبيرات العادية لكل رمز:

comment   \/\*[^*]*\*+([^/*][^*]*\*+)*\/
num       [0-9]+|[0-9]*"."[0-9]+
nonascii  [\200-\377]
nmstart   [_a-z]|{nonascii}|{escape}
nmchar    [_a-z0-9-]|{nonascii}|{escape}
name      {nmchar}+
ident     {nmstart}{nmchar}*

"ident" هي اختصار لكلمة معرّف، مثل اسم فئة. "name" هو معرّف عنصر (يُشار إليه بـ "#" )

يتم وصف قواعد البنية في تنسيق BNF.

ruleset
  : selector [ ',' S* selector ]*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;
selector
  : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
  ;
simple_selector
  : element_name [ HASH | class | attrib | pseudo ]*
  | [ HASH | class | attrib | pseudo ]+
  ;
class
  : '.' IDENT
  ;
element_name
  : IDENT | '*'
  ;
attrib
  : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
    [ IDENT | STRING ] S* ] ']'
  ;
pseudo
  : ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ]
  ;

الشرح:

مجموعة القواعد هي هذا الهيكل:

div.error, a.error {
  color:red;
  font-weight:bold;
}

div.error وa.error هما أداتا اختيار. يحتوي الجزء داخل الأقواس المعقوفة على القواعد التي تطبّقها مجموعة القواعد هذه. يتم تحديد هذه البنية رسميًا في هذا التعريف:

ruleset
  : selector [ ',' S* selector ]*
    '{' S* declaration [ ';' S* declaration ]* '}' S*
  ;

هذا يعني أن مجموعة القواعد هي أداة اختيار أو بشكل اختياري عدد من المحددات مفصولة بفاصلة والمسافات (يرمز الحرف S إلى المسافة البيضاء). تحتوي مجموعة القواعد على قوسين معقوفين وداخلهما بيان أو اختياريًا عدد من البيانات مفصولة بفاصلة منقوطة. سيتم تعريف "البيان" و "أداة الاختيار" في تعريفات BNF التالية.

محلل لغة CSS في WebKit

يستخدم WebKit أدات إنشاء برامج تحليل Flex وBison لإنشاء برامج تحليل تلقائيًا من ملفات قواعد صفحات الأنماط المتتالية (CSS). كما تذكّر من مقدمة المُحلِّل، ينشئ Bison مُحلِّلًا للتحويل والاختزال من الأسفل إلى الأعلى. يستخدم Firefox منظِّمًا نحويًا من الأعلى إلى الأسفل مكتوبًا يدويًا. وفي كلتا الحالتَين، يتم تحليل كل ملف CSS إلى عنصر StyleSheet. يحتوي كل عنصر على قواعد CSS. تحتوي عناصر قواعد CSS على عناصر محدد وعناصر تعريف وعناصر أخرى تتوافق مع قواعد لغة CSS النحوية.

تحليل صفحات الأنماط المتتالية (CSS)
الشكل 12: تحليل CSS

معالجة طلب النصوص البرمجية وأوراق الأنماط

النصوص البرمجية

نموذج الويب متزامن. يتوقّع المؤلفون أن يتم تحليل النصوص البرمجية وتنفيذها على الفور عندما يصل المحلّل إلى علامة <script>. ويتم إيقاف تحليل المستند إلى أن يتم تنفيذ النص البرمجي. إذا كان النص البرمجي خارجيًا، يجب أولاً جلب المورد من الشبكة، ويتم ذلك أيضًا بشكل متزامن، وتتوقّف عملية التحليل إلى أن يتم جلب المورد. وقد كان هذا النموذج مستخدمًا لعدة سنوات، وهو محدّد أيضًا في مواصفات HTML4 و5. يمكن للمؤلفين إضافة السمة "defer" إلى نص برمجي، وفي هذه الحالة لن يؤدي ذلك إلى إيقاف تحليل المستند وسيتم تنفيذه بعد تحليل المستند. تضيف لغة HTML5 خيارًا لتمييز النص البرمجي على أنّه غير متزامن حتى يتم تحليله وتنفيذه من خلال سلسلة محادثات مختلفة.

التحليل التوقُّعي

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

أوراق الأنماط

من ناحية أخرى، تتّبع جداول الأنماط نموذجًا مختلفًا. من الناحية النظرية، يبدو أنّه ما مِن سبب للانتظار إلى أن يتم تحميل أوراق الأنماط وإيقاف تحليل المستند، لأنّ أوراق الأنماط لا تغيّر شجرة DOM. ومع ذلك، هناك مشكلة في النصوص البرمجية التي تطلب معلومات النمط أثناء مرحلة تحليل المستند. إذا لم يتم تحميل النمط وتحليله بعد، سيحصل النص البرمجي على إجابات خاطئة، ويبدو أنّ هذا تسبب في حدوث الكثير من المشاكل. يبدو أنّه حالة نادرة، ولكنها شائعة جدًا. يحظر Firefox جميع النصوص البرمجية عندما يكون هناك جدول أسلوب لا يزال يتم تحميله وتحليله. لا يحظر WebKit النصوص البرمجية إلا عند محاولة الوصول إلى خصائص نمط معيّنة قد تتأثر بجداول الأنماط غير المحمَّلة.

إنشاء شجرة العرض

أثناء إنشاء شجرة نموذج العناصر في المستند، ينشئ المتصفّح شجرة أخرى، وهي شجرة العرض. تتضمّن هذه الشجرة العناصر المرئية بالترتيب الذي سيتم عرضه بها. وهو التمثيل المرئي للمستند. الغرض من هذه الشجرة هو إتاحة طلاء المحتويات بترتيبها الصحيح.

يُطلق Firefox على العناصر في شجرة العرض اسم "اللقطات". يستخدم WebKit مصطلح "العارض" أو "كائن التقديم".

يعرف العارض كيفية وضع العناصر الخارجية وتلوينها.

تحتوي فئة RenderObject في WebKit، وهي الفئة الأساسية لأدوات التحويل، على التعريف التالي:

class RenderObject{
  virtual void layout();
  virtual void paint(PaintInfo);
  virtual void rect repaintRect();
  Node* node;  //the DOM node
  RenderStyle* style;  // the computed style
  RenderLayer* containgLayer; //the containing z-index layer
}

يمثّل كلّ مُنشئ رسومات منطقة مستطيلة تتوافق عادةً مع مربّع CSS للعقدة، كما هو موضّح في مواصفات CSS2. ويتضمّن معلومات هندسية مثل العرض والارتفاع والموضع.

يتأثر نوع المربّع بقيمة "display" لسمة style ذات الصلة بالعقدة (راجِع قسم احتساب التنسيق). في ما يلي رمز WebKit لتحديد نوع أداة العرض التي يجب إنشاؤها لعقدة DOM، وفقًا لسمة العرض:

RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
    Document* doc = node->document();
    RenderArena* arena = doc->renderArena();
    ...
    RenderObject* o = 0;

    switch (style->display()) {
        case NONE:
            break;
        case INLINE:
            o = new (arena) RenderInline(node);
            break;
        case BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case INLINE_BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case LIST_ITEM:
            o = new (arena) RenderListItem(node);
            break;
       ...
    }

    return o;
}

يتم أيضًا مراعاة نوع العنصر: على سبيل المثال، تحتوي عناصر التحكّم في النماذج والجداول على إطارات خاصة.

في WebKit، إذا أراد عنصر إنشاء أداة عرض خاصة، سيتم إلغاء طريقة createRenderer(). تشير عناصر العرض إلى عناصر الأنماط التي تحتوي على معلومات غير هندسية.

علاقة شجرة العرض بشجرة نموذج العناصر في المستند

تتطابق عناصر العرض مع عناصر نموذج DOM، ولكن العلاقة ليست بين عنصرَين فقط. لن يتم إدراج عناصر نموذج DOM غير المرئية في شجرة العرض. ومن الأمثلة على ذلك عنصر "head". ولن تظهر أيضًا في الشجرة العناصر التي تم ضبط قيمة العرض لها على "none" (في حين ستظهر في الشجرة العناصر التي تكون مستوى ظهورها "مخفي").

هناك عناصر DOM تتوافق مع العديد من العناصر المرئية. هذه عادة ما تكون عناصر ذات هيكل معقد لا يمكن وصفها بمستطيل واحد. على سبيل المثال، يحتوي العنصر select على ثلاثة عناصر عرض: أحدها لمنطقة العرض، وأحدهما لمربّع القائمة المنسدلة، وأحدهما للزر. بالإضافة إلى ذلك، عند تقسيم النص إلى أسطر متعددة لأنّ العرض غير كافٍ لسطر واحد، ستتم إضافة الأسطر الجديدة كمعدّلات عرض إضافية.

ومن الأمثلة الأخرى على أدوات التحويل المتعددة علامات HTML غير الصالحة. وفقًا لمواصفات CSS، يجب أن يحتوي العنصر المضمّن على عناصر كتلة فقط أو عناصر مضمّنة فقط. في حال المحتوى المختلط، سيتم إنشاء عناصر عرض كتل مجهولة الهوية لتغليف العناصر المضمّنة.

تتطابق بعض عناصر العرض مع عقدة نموذج عناصر في المستند، ولكن ليس في المكان نفسه في الشجرة. تكون العناصر العائمة والعناصر ذات الموضع المطلق خارج التدفق، ويتم وضعها في جزء مختلف من الشجرة، ويتم ربطها بالإطار الفعلي. إطار نائب في المكان الذي كان من المفترض أن يظهر فيه

شجرة العرض وشجرة عناصر DOM المقابلة
الشكل 13: شجرة العرض وشجرة نموذج DOM المقابلة لها "مساحة العرض" هي العنصر الأول الذي يحتوي على المحتوى. وفي WebKit، يكون الكائن "RenderView"

خطوات إنشاء الشجرة

في Firefox، يتم تسجيل العرض التقديمي كمستمع لتعديلات DOM. يفوّض العرض التقديمي إنشاء الإطار إلى FrameConstructor ويحلّ المُنشئ النمط (راجِع حساب النمط) وينشئ إطارًا.

في WebKit، تُعرف عملية حلّ النمط وإنشاء أداة عرض باسم "الربط". تحتوي كل عقدة DOM على طريقة "attach". المرفق متزامن، ويستدعي إدراج العقدة إلى شجرة نموذج العناصر في المستند طريقة "إرفاق" للعقدة الجديدة.

تؤدي معالجة علامتَي html وbody إلى إنشاء جذر شجرة العرض. يتطابق عنصر العرض الجذر مع ما تُطلق عليه مواصفات CSS اسم "الوحدة المُحتوية": وهي الوحدة العلوية التي تحتوي على جميع الوحدات الأخرى. وتكون أبعاده هي إطار العرض: أبعاد منطقة عرض نافذة المتصفح. يُطلق Firefox على هذا العنصر اسم ViewPortFrame ويُطلق WebKit عليه اسم RenderView. هذا هو عنصر العرض الذي يشير إليه المستند. يتم إنشاء بقية الشجرة على شكل إدراج عقد DOM.

اطّلِع على مواصفات CSS2 حول نموذج المعالجة.

احتساب الأنماط

يتطلّب إنشاء شجرة التقديم احتساب السمات المرئية لكلّ عنصر تقديم. ويتم ذلك من خلال احتساب سمات الأنماط لكل عنصر.

يتضمّن النمط أوراق أنماط من أصول مختلفة وعناصر أنماط مضمّنة وسمات مرئية في HTML (مثل السمة bgcolor)، ويتم تحويل هذه السمات لاحقًا إلى سمات أنماط CSS مطابقة.

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

تؤدي عملية احتساب الأنماط إلى ظهور بعض الصعوبات:

  1. بيانات الأنماط هي بنية كبيرة جدًا، تحتوي على العديد من سمات الأنماط، ما قد يتسبب في حدوث مشاكل في الذاكرة.
  2. يمكن أن يؤدي العثور على قواعد المطابقة لكل عنصر إلى حدوث مشاكل في الأداء في حال عدم تحسينها. إنّ تصفُّح قائمة القواعد بالكامل لكل عنصر للعثور على المطابقات هو مهمة صعبة. يمكن أن تحتوي أدوات الاختيار على بنية معقّدة يمكن أن تؤدي إلى بدء عملية المطابقة على مسار واعدٍ ثبت أنّه عديم الجدوى ويجب تجربة مسار آخر.

    على سبيل المثال - أداة الاختيار المركّبة هذه:

    div div div div{
    ...
    }
    

    يعني ذلك أنّ القواعد تنطبق على <div> الذي هو أحد العناصر الفرعية لثلاثة عناصر div. لنفترض أنّك تريد التحقّق مما إذا كانت القاعدة تنطبق على عنصر <div> معيّن. يمكنك اختيار مسار معيّن في التسلسل الهرمي للتحقّق منه. قد تحتاج إلى الانتقال إلى أعلى شجرة العقد لمعرفة أنّ هناك قسمَين فقط من div وأنّ القاعدة لا تنطبق. بعد ذلك، عليك تجربة مسارات أخرى في الشجرة.

  3. يتضمن تطبيق القواعد قواعد متسلسلة معقّدة للغاية تحدّد التسلسل الهرمي للقواعد.

لنلقِ نظرة على كيفية تعامل المتصفّحات مع هذه المشاكل:

مشاركة بيانات الأنماط

تشير عُقد WebKit إلى كائنات الأنماط (RenderStyle). يمكن مشاركة هذه العناصر من خلال العقد في بعض الحالات. تكون العقدة هي أخ أو قريب، ويجب استيفاء الشروط التالية:

  1. يجب أن تكون العناصر في حالة الماوس نفسها (على سبيل المثال، لا يمكن أن يكون أحدهما في حالة hover بينما الآخر ليس كذلك).
  2. يجب ألا يحتوي أيّ من العنصرَين على معرّف.
  3. يجب أن تتطابق أسماء العلامات.
  4. ينبغي أن تتطابق سمات الفئة
  5. يجب أن تكون مجموعة السمات المرتبطة متطابقة.
  6. يجب أن تتطابق حالات الربط.
  7. يجب أن تتطابق حالات التركيز
  8. يجب ألا يتأثّر أيّ من العنصرَين بأدوات اختيار السمات، حيث يتمّ تعريف التأثّر على أنّه توفّر أيّ مطابقة محدّد تستخدِم أداة اختيار سمة في أيّ موضع داخل المحدّد على الإطلاق.
  9. يجب ألا تحتوي العناصر على سمة نمط مضمّن.
  10. يجب عدم استخدام أيّ محدّدات أشقاء على الإطلاق. يُجري WebCore تبديلًا عامًا عند العثور على أيّ أداة اختيار لأخوة ويوقف مشاركة الأنماط للمستند بأكمله عند توفّرها. ويشمل ذلك أداة الاختيار + وأدوات الاختيار مثل :first-child و :last-child.

شجرة قواعد Firefox

يحتوي Firefox على شجرتين إضافيتين لتسهيل احتساب الأنماط: شجرة القواعد وشجرة سياق الأنماط. يحتوي WebKit أيضًا على عناصر أنماط، ولكن لا يتم تخزينها في شجرة مثل شجرة سياق الأنماط، وتشير عقدة DOM فقط إلى النمط ذي الصلة.

شجرة سياق على غرار Firefox
الشكل 14: شجرة السياق بأسلوب Firefox

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

يتمّ تخزين جميع القواعد المتطابقة في شجرة. تكون للعقد السفلية في المسار أولوية أعلى. تحتوي الشجرة على جميع مسارات مطابقات القواعد التي تم العثور عليها. يتم تخزين القواعد بشكلٍ غير فعّال. لا يتم احتساب الشجرة في البداية لكل عقدة، ولكن كلما كان من الضروري احتساب نمط عقدة، تتم إضافة المسارات المحسوبة إلى الشجرة.

والفكرة هي عرض مسارات الشجرة ككلمات في قاموس. لنفترض أنّنا سبق أن احتسبنا شجرة القواعد هذه:

العرض التدرّجي للقاعدة المحسوبة
الشكل 15: العرض التدرّجي للقاعدة المحسوبة

لنفترض أنّنا نحتاج إلى مطابقة قواعد لعنصر آخر في شجرة المحتوى، ونجد أنّ القواعد المطابقة (بالترتيب الصحيح) هي B-E-I. لدينا هذا المسار في الشجرة لأنّنا سبق أن احتسبنا المسار A-B-E-I-L. سيكون لدينا الآن عمل أقل.

لنطّلِع على كيفية توفير الشجرة للوقت والجهد.

التقسيم إلى بنى

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

تُساعدنا الشجرة في التخزين المؤقت للبنى الكاملة (التي تحتوي على القيم النهائية المحسوبة) في الشجرة. الفكرة هي أنّه إذا لم تقدّم العقدة السفلية تعريفًا لبنية، يمكن استخدام بنية محفوظة مؤقتًا في عقدة أعلى.

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

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

إذا وجدنا تعريفات جزئية، ننتقل إلى أعلى الشجرة حتى يتم ملء الهيكل.

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

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

وفي حال كان لأحد العناصر شقيق أو أخ يشير إلى العقدة الشجرية نفسها، يمكن مشاركة سياق النمط بأكمله بينهما.

دعنا نرى مثالاً: لنفترض أن لدينا هذا HTML

<html>
  <body>
    <div class="err" id="div1">
      <p>
        this is a <span class="big"> big error </span>
        this is also a
        <span class="big"> very  big  error</span> error
      </p>
    </div>
    <div class="err" id="div2">another error</div>
  </body>
</html>

والقواعد التالية:

div {margin: 5px; color:black}
.err {color:red}
.big {margin-top:3px}
div span {margin-bottom:4px}
#div1 {color:blue}
#div2 {color:green}

لتبسيط الأمور، لنفترض أنّنا نحتاج إلى ملء عنصرَين فقط: بنية اللون وبنية الهامش. يحتوي هيكل اللون على عضو واحد فقط: اللون يحتوي هيكل الهامش على الجوانب الأربعة.

ستظهر شجرة القواعد الناتجة على النحو التالي (يتم وضع علامة على العقد باسم العقدة: رقم القاعدة التي تشير إليها):

شجرة القواعد
الشكل 16: شجرة القواعد

ستبدو شجرة السياق على النحو التالي (اسم العقدة: عقدة القاعدة التي تشير إليها):

العرض التدرّجي للسياق
الشكل 17: شجرة السياق

لنفترض أنّنا نفكّر رمز HTML ونصل إلى علامة <div> الثانية. نحتاج إلى إنشاء سياق نمط لهذه العقدة وملء بنيات النمط الخاصة بها.

سنطابق القواعد ونكتشف أن القواعد المطابقة لـ <div> هي 1 و2 و6. هذا يعني أنّ هناك مسارًا حاليًا في الشجرة يمكن لعنصرنا استخدامه، ونحتاج فقط إلى إضافة عقدة أخرى إليه للقاعدة 6 (العقدة "و" في شجرة القواعد).

سننشئ سياق نمط ونضعه في شجرة السياق. سيشير سياق النمط الجديد إلى العقدة F في شجرة القواعد.

نحتاج الآن إلى ملء بنية الأنماط. سنبدأ بملء بنية الهامش. بما أنّ عقدة القاعدة الأخيرة (F) لا تُضيف إلى بنية الهامش، يمكننا الانتقال للأعلى في الشجرة إلى أن نعثر على بنية مؤرشَفة تم احتسابها في عملية إدراج عقدة سابقة ونستخدمها. سنجدها في العقدة B، وهي العقدة العلوية التي حدّدت قواعد الهامش.

لدينا تعريف لهيكل اللون، لذا لا يمكننا استخدام بنية محفوظة في ذاكرة التخزين المؤقت. بما أن اللون له سمة واحدة، لا نحتاج إلى الانتقال إلى الشجرة لملء السمات الأخرى. سنحسب القيمة النهائية (تحويل السلسلة إلى RGB وما إلى ذلك) وسنخزّن البنية المحسوبة في هذه العقدة.

إنّ العمل على العنصر <span> الثاني أسهل. سنطابق القواعد ونصل إلى استنتاج بأنّها تشير إلى القاعدة "ص"، مثل النطاق السابق. بما أنّ لدينا عناصر متسلسلة تشير إلى العقدة نفسها، يمكننا مشاركة سياق النمط بالكامل والإشارة إلى سياق النطاق السابق فقط.

بالنسبة إلى العناصر التي تحتوي على قواعد مكتسَبة من العنصر الرئيسي، يتم تخزينها مؤقتًا في شجرة السياق (يتم اكتساب سمة اللون في الواقع، ولكن يتعامل Firefox معها على أنّها تمّت إعادة ضبطها ويخزّنها مؤقتًا في شجرة القواعد).

على سبيل المثال، إذا أضفنا قواعد للخطوط في فقرة:

p {font-family: Verdana; font size: 10px; font-weight: bold}

بعد ذلك، يمكن أن يكون عنصر الفقرة، وهو عنصر فرعي لعنصر div في شجرة السياق، قد شارك بنية الخط نفسها مع العنصر الرئيسي. يحدث ذلك في حال عدم تحديد أي قواعد للخط في الفقرة.

في WebKit، الذي لا يتضمّن شجرة قواعد، يتمّ عبور البيانات المطابقة أربع مرّات. أولاً، يتم تطبيق الخصائص غير المهمة ذات الأولوية العالية (الخصائص التي يجب تطبيقها أولاً لأنّ غيرها يعتمد عليها، مثل العرض)، ثم الخصائص المهمة ذات الأولوية العالية، ثم الخصائص غير المهمة ذات الأولوية العادية، ثم الخصائص المهمة ذات الأولوية العادية. وهذا يعني أن الخصائص التي تظهر عدة مرات سيتم تحليلها وفقًا للترتيب التدريجي الصحيح. يفوز آخر لاعب.

باختصار، تؤدي مشاركة عناصر الأنماط (بالكامل أو بعض العناصر داخلها) إلى حلّ المشكلةَين 1 و3. تساعد أيضًا شجرة قواعد Firefox في تطبيق السمات بالترتيب الصحيح.

التلاعب بالقواعد لإجراء مطابقة سهلة

هناك عدة مصادر لقواعد الأنماط:

  1. قواعد CSS، سواء في أوراق الأنماط الخارجية أو في عناصر الأنماط css p {color: blue}
  2. سمات الأنماط المضمّنة، مثل html <p style="color: blue" />
  3. السمات المرئية لـ HTML (التي يتم ربطها بقواعد الأنماط ذات الصلة) html <p bgcolor="blue" /> يمكن مطابقة السمتَين الأخيرتَين بسهولة مع العنصر لأنّه يملك سمات الأنماط ويمكن ربط سمات HTML باستخدام العنصر كمفتاح.

كما ذكرنا سابقًا في المشكلة رقم 2، يمكن أن يكون مطابقة قواعد CSS أكثر تعقيدًا. لحلّ هذه المشكلة، يتم التلاعب بالقواعد لتسهيل الوصول.

بعد تحليل ورقة الأنماط، تتم إضافة القواعد إلى إحدى خرائط التجزئة العديدة، وفقًا لأداة الاختيار. تتوفّر خرائط حسب المعرّف واسم الفئة واسم العلامة وخرائط عامة لأي عناصر لا تندرج ضمن هذه الفئات. إذا كان أداة الاختيار هي معرّف، ستتم إضافة القاعدة إلى خريطة المعرّفات، وإذا كانت فئة، ستتم إضافتها إلى خريطة الفئات، وما إلى ذلك.

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

لنطّلِع على قواعد الأنماط التالية على سبيل المثال:

p.error {color: red}
#messageDiv {height: 50px}
div {margin: 5px}

سيتم إدراج القاعدة الأولى في خريطة الفئة. والثاني في خريطة رقم التعريف والثالث في خريطة العلامات.

بالنسبة إلى مقتطف HTML التالي:

<p class="error">an error occurred</p>
<div id=" messageDiv">this is a message</div>

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

على سبيل المثال، إذا كانت قاعدة العنصر div هي:

table div {margin: 5px}

وسيظلّ يتم استخراجه من خريطة العلامات، لأنّ المفتاح هو أداة الاختيار الأيمن، ولكنّه لن يتطابق مع عنصر div الذي لا يتضمّن سلفًا للجدول.

يُجري كلٌّ من WebKit وFirefox هذا التلاعب.

ترتيب تسلسلي لأوراق الأنماط

يحتوي عنصر التصميم على خصائص تتوافق مع كل سمة مرئية (جميع سمات CSS ولكن بشكل أكثر عمومية). إذا لم يتم تحديد السمة من خلال أيّ من القواعد المطابقة، يمكن أن يتم اكتساب بعض السمات من خلال عنصر نمط العنصر الرئيسي. تحتوي السمات الأخرى على قيم تلقائية.

تبدأ المشكلة عندما يكون هناك أكثر من تعريف - وهنا يأتي ترتيب التتابع لحل المشكلة.

يمكن أن يظهر تعريف خاصية النمط في أوراق أنماط متعددة، وعدة مرات داخل ورقة أنماط. وهذا يعني أنّ ترتيب تطبيق القواعد مهم جدًا. ويُعرف هذا الترتيب باسم "الترتيب التدرّجي". وفقًا لمواصفات CSS2، يكون الترتيب التدريجي (من الأدنى إلى الأعلى):

  1. بيانات تعريف المتصفِّح
  2. تعريفات المستخدم العادية
  3. التصريحات العادية للمؤلف
  4. مؤلفات التصريحات المهمة
  5. تصريحات المستخدمين المهمة

تكون بيانات المتصفّح هي الأقل أهمية، ولا يتجاوز المستخدم المؤلف إلا إذا تم وضع علامة على البيان على أنّه مهم. سيتم ترتيب البيانات ذات الترتيب نفسه حسب الخصوصيّة ثم الترتيب المحدَّد. يتم ترجمة السمات المرئية لـ HTML إلى إعلانات CSS مطابقة . ويتم التعامل معها كقواعد مؤلفين ذات أولوية منخفضة.

الدقة

يتم تحديد مدى دقة أداة الاختيار من خلال مواصفات CSS2 على النحو التالي:

  1. يتم احتساب 1 إذا كان البيان الذي ينتمي إليه هو سمة "style" بدلاً من قاعدة تحتوي على أداة اختيار، و0 في الحالات الأخرى (= a).
  2. احتساب عدد سمات المعرّفات في أداة الاختيار (= ب)
  3. حساب عدد السمات الأخرى والفئات الزائفة في المحدد (= c)
  4. احتساب عدد أسماء العناصر والعناصر الزائفة في المحدّد (= d)

تؤدي تسلسل الأرقام الأربعة a-b-c-d (في نظام رقمي بقاعدة كبيرة) إلى تحديد القيمة المحددة.

يتم تحديد قاعدة الأعداد التي تحتاج إلى استخدامها حسب أكبر عدد لديك في إحدى الفئات.

على سبيل المثال، إذا كان a=14، يمكنك استخدام قاعدة سداسية عشرية. في حال كان a=17، ستحتاج إلى قاعدة رقمية من 17 رقمًا. يمكن أن يحدث الموقف الأخير باستخدام عنصر اختيار مثل هذا: html body div div p… (17 علامة في عنصر الاختيار… من غير المرجّح حدوث ذلك).

إليك بعض الأمثلة:

 *             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
 li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
 li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
 ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
 ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
 h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
 ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
 li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
 #x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
 style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

ترتيب القواعد

بعد مطابقة القواعد، يتم ترتيبها وفقًا لقواعد التسلسل. تستخدم مجموعة أدوات WebKit فرز الفقاعات للقوائم الصغيرة ودمج الفرز مع القوائم الكبيرة. تنفِّذ WebKit عملية الترتيب من خلال تجاوز عامل التشغيل > للقواعد:

static bool operator >(CSSRuleData& r1, CSSRuleData& r2)
{
    int spec1 = r1.selector()->specificity();
    int spec2 = r2.selector()->specificity();
    return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;
}

عملية تدريجية

يستخدم WebKit علامة تشير إلى ما إذا تم تحميل جميع أوراق الأنماط ذات المستوى الأعلى (بما في ذلك @imports). إذا لم يتم تحميل النمط بالكامل عند إرفاقه، يتم استخدام عناصر نائبة ووضع علامة عليها في المستند، وستتم إعادة حسابها بمجرد تحميل أوراق الأنماط.

التنسيق

عند إنشاء العارض وإضافته إلى الشجرة، لا يكون له موضع وحجم. ويُطلق على عملية احتساب هذه القيم اسم التنسيق أو إعادة التدفق.

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

يكون نظام الإحداثيات نسبيًا إلى الإطار الجذر. يتم استخدام الإحداثيات العلوية واليسارية.

التنسيق هو عملية تكرارية. ويبدأ العرض من عارض الجذر، الذي يتوافق مع العنصر <html> في مستند HTML. يستمرّ التنسيق بشكلٍ متكرّر من خلال بعض أو كل التسلسل الهرمي للإطار، ويحسب المعلومات الهندسية لكلّ أداة عرض تتطلّب ذلك.

موضع أداة عرض الجذر هو 0,0 وأبعادها هي إطار العرض، أي الجزء المرئي من نافذة المتصفّح.

تحتوي جميع أدوات العرض على طريقة "تنسيق" أو "إعادة تدفق"، ويُطلِق كلّ مُعرِّف طريقة التنسيق لعناصره الفرعية التي تحتاج إلى تنسيق.

نظام البتات غير النظيفة

حتى لا يتم تنفيذ تخطيط كامل مع كل تغيير صغير، تستخدم المتصفحات نظام "بت نظيف". يضع عارض الرسومات الذي تم تغييره أو إضافته علامة "غير صالح" على نفسه وعلى عناصره الفرعية، ما يعني أنّه يحتاج إلى تنسيق.

هناك علامتَان: "غير صالح" و"العناصر الفرعية غير صالحة"، ما يعني أنّه على الرغم من أنّ أداة العرض نفسها قد تكون صالحة، إلا أنّها تحتوي على عنصر فرعي واحد على الأقل يحتاج إلى تنسيق.

التنسيق الشامل والتدريجي

يمكن تشغيل التنسيق على شجرة العرض بالكامل، وهذا هو التنسيق "العمومي". يمكن أن يحدث ذلك نتيجةً لأحد الأسباب التالية:

  1. تغيير نمط عام يؤثر في جميع أدوات العرض، مثل تغيير حجم الخط
  2. نتيجةً لتغيير حجم الشاشة

يمكن أن يكون التخطيط تدريجيًا، ولن يتم تنفيذ سوى برامج العرض غير النظيفة (يمكن أن يتسبب ذلك في بعض الضرر ويتطلب تخطيطات إضافية).

يتم تشغيل التنسيق التزايدي (بشكل غير متزامن) عندما تكون برامج العرض غير صالحة. على سبيل المثال، عند إلحاق أدوات عرض جديدة بشجرة العرض بعد أن يصل محتوى إضافي من الشبكة ويتمّ إضافته إلى شجرة DOM.

التنسيق المتزايد
الشكل 18: التنسيق المتزايد: يتمّ ترتيب عناصر العرض غير الصالحة وعناصر العرض الفرعية فقط

التخطيط غير المتزامن والمتزامن

يتم تنفيذ التنسيق التدريجي بشكل غير متزامن. يضع Firefox "أوامر إعادة التدفق" في "قائمة الانتظار" للتصاميم المتزايدة، ويشغّل جدول التشغيل تنفيذ هذه الأوامر بشكل مجمّع. يحتوي WebKit أيضًا على موقّت ينفّذ تنسيقًا متزايدًا، ويتمّ تنقّل الشجرة وتنسيق عناصر العرض "غير الصالحة".

يمكن أن تؤدي النصوص البرمجية التي تطلب معلومات عن التصميم، مثل "offsetHeight"، إلى بدء التنسيق المتزايد بشكل متزامن.

سيتم عادةً تشغيل التنسيق العام بشكل متزامن.

في بعض الأحيان، يتم تشغيل التنسيق كاستدعاء بعد تنسيق أولي بسبب تغيير بعض السمات، مثل موضع التمرير.

تحسينات

عند تشغيل تنسيق من خلال "تغيير الحجم" أو تغيير موضع العارض(وليس الحجم)، يتم أخذ أحجام العرض من ذاكرة تخزين مؤقت ولا تتم إعادة احتسابها...

في بعض الحالات، يتم تعديل شجرة فرعية فقط ولا يبدأ التنسيق من الجذر. ويمكن أن يحدث هذا في الحالات التي يكون فيها التغيير محليًا ولا يؤثر في البيئة المحيطة، مثل إدراج النص في حقول نصية (وإلا ستؤدي كل ضغطة مفتاح إلى تشغيل تنسيق بدءًا من الجذر).

عملية التنسيق

يتّبع التنسيق عادةً النمط التالي:

  1. يحدّد العارض الرئيسي العرض الخاص به.
  2. يراجع الوالدان المحتوى الخاص بالطفلَين وينفّذ ما يلي:
    1. ضَع وحدة المعالجة الفرعية (يُستخدَم لضبط x وy).
    2. تستدعي هذه الوظيفة تنسيق الطفل إذا لزم الأمر، سواء كان التنسيق غير صحيح أو إذا كان التنسيق العام مفعّلاً أو لأي سبب آخر، ما يؤدي إلى احتساب ارتفاع الطفل.
  3. يستخدم أحد الوالدَين الارتفاعات التراكمية للأطفال وارتفاعات الهوامش والمساحة المتروكة لضبط ارتفاعها الخاص، وسيستخدمها أحد الوالدَين العارض الرئيسي.
  4. ضبط القيمة "غير محفوظة" على "خطأ"

يستخدم فايرفوكس كائن "state" (nsHTMLReflowState) كمعلمة للتخطيط (يسمى "reflow"). ومن بين غيرها، تتضمّن الحالة عرض العناصر الرئيسية.

ناتج تنسيق Firefox هو عنصر "مقاييس" (nsHTMLReflowMetrics). وسيحتوي على الارتفاع المحسوب للعارض.

حساب العرض

ويتم احتساب عرض العارض باستخدام عرض كتلة الحاوية وسمة العرض (width) لنمط العارض والهوامش والحدود.

على سبيل المثال، عرض العنصر div التالي:

<div style="width: 30%"/>

سيتم احتسابه بواسطة WebKit على النحو التالي(طريقة calcWidth لفئة RenderBox):

  • عرض الحاوية هو الحد الأقصى لعرض الحاوية المتاح و0. إنّ availableWidth في هذه الحالة هو contentWidth الذي يتم احتسابه على النحو التالي:
clientWidth() - paddingLeft() - paddingRight()

يمثّل clientWidth وclientHeight الجزء الداخلي من العنصر باستثناء الحدود وشريط التمرير.

  • عرض العناصر هو سمة التصميم "width". وسيتم احتسابها كقيمة مطلقة من خلال حساب النسبة المئوية لعرض الحاوية.

  • تمت إضافة الحدود الأفقية والحشوات الآن.

حتى الآن كان هذا هو حساب "العرض المفضل". سيتم الآن احتساب الحد الأدنى والأقصى للعرض.

إذا كان العرض المفضّل أكبر من الحد الأقصى للعرض، يتم استخدام الحد الأقصى للعرض. وإذا كان العرض أقل من الحد الأدنى (أصغر وحدة غير قابلة للكسر)، يتم استخدام الحد الأدنى للعرض.

يتم تخزين القيم مؤقتًا في حال الحاجة إلى تنسيق، ولكن لا يتغيّر العرض.

تقسيم الأسطر

عندما يقرر عارض في منتصف التخطيط أنه يجب تعطّله، يتوقف العارض وينشر إلى الجزء الرئيسي للتخطيط الذي يلزم تعطّله. ينشئ العنصر الرئيسي عناصر المعالجة الإضافية ويطلب تنسيقها.

رسم

في مرحلة الرسم، يتمّ التنقّل في شجرة التقديم ويتمّ استدعاء طريقة "paint()" لعارض الرسومات لعرض المحتوى على الشاشة. يستخدم الرسم مكوّن بنية واجهة المستخدم.

عالمية ومتصاعدة

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

ترتيب اللوحة

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

  1. لون الخلفية
  2. صورة الخلفية
  3. border
  4. الأطفال
  5. مخطط

قائمة عرض Firefox

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

وبهذه الطريقة يجب رسم الشجرة مرة واحدة فقط لإعادة الرسم بدلاً من عدة مرات، مثل طلاء جميع الخلفيات ثم كل الصور ثم كل الحدود وما إلى ذلك.

يحسِّن Firefox العملية من خلال عدم إضافة عناصر ستتم إخفاؤها، مثل العناصر التي تكون أسفل عناصر غير شفافة تمامًا.

تخزين مستطيل WebKit

قبل إعادة الطلاء، يحفظ WebKit المستطيل القديم كصورة نقطية. بعد ذلك، يتم رسم الفرق فقط بين المستطيلَين الجديد والقديم.

التغييرات الديناميكية

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

سلاسل تعليمات محرّك العرض

محرك العرض أحادي السلسلة. تحدث كل العمليات تقريبًا، باستثناء عمليات الشبكة، في سلسلة محادثات واحدة. وهذا هو سلسلة التعليمات الرئيسية في المتصفح في Firefox وSafari. في Chrome، يكون هذا الخيط هو الخيط الرئيسي لمعالجة علامة التبويب.

يمكن تنفيذ عمليات الشبكة من خلال عدة سلاسل محادثات متوازية. يكون عدد عمليات الاتصال المتوازي محدودًا (عادةً من اتصالَين إلى 6 عمليات اتصال).

حلقة الأحداث

سلسلة المهام الرئيسية للمتصفّح هي حلقة أحداث. وهي عبارة عن حلقة لانهائية تحافظ على استمرارية هذه العملية. وينتظر هذا المعالج الأحداث (مثل أحداث التنسيق والرسم) ويعالجها. هذا هو رمز Firefox لحلقة الأحداث الرئيسية:

while (!mExiting)
    NS_ProcessNextEvent(thread);

النموذج المرئي لصفحات الأنماط المتتالية 2

اللوحة

وفقًا لمواصفات CSS2، يصف مصطلح "لوحة الرسم" "المساحة التي يتم فيها عرض بنية التنسيق": حيث يرسم المتصفّح المحتوى.

تكون لوحة الرسم غير محدودة لكل بُعد من أبعاد المساحة، بينما تختار المتصفحات عرضًا أوليًا بناءً على أبعاد إطار العرض.

وفقًا لموقع www.w3.org/TR/CSS2/zindex.html، تكون اللوحة شفافة إذا كانت مضمّنة في لوحة أخرى، ويتم منحها لونًا يحدّده المتصفّح إذا لم يكن كذلك.

نموذج CSS Box

يصف نموذج مربع CSS المربعات المستطيلة التي يتم إنشاؤها للعناصر في شجرة المستند ويتم وضعها وفقًا لنموذج التنسيق المرئي.

يحتوي كل مربّع على منطقة محتوى (مثل نص أو صورة أو غير ذلك) ومناطق اختيارية للتباعد والحدود والهوامش المحيطة.

نموذج المربّعات في CSS2
الشكل 19: نموذج المربّع في CSS2

وتنشئ كل عقدة 0...n من هذه المربعات.

تحتوي كل العناصر على سمة display التي تحدّد نوع المربّع الذي سيتم إنشاؤه.

أمثلة:

block: generates a block box.
inline: generates one or more inline boxes.
none: no box is generated.

الإعداد التلقائي هو مضمّن، ولكن قد تضبط جدول أسلوب المتصفّح إعدادات تلقائية أخرى. على سبيل المثال، العرض التلقائي للعنصر "div" هو "block".

يمكنك العثور على مثال على جدول الأنماط التلقائي هنا: www.w3.org/TR/CSS2/sample.html.

مخطّط تحديد الموضع

هناك ثلاثة مخططات:

  1. عادي: يتم وضع العنصر وفقًا لموقعه في المستند. وهذا يعني أنّ موضعه في شجرة العرض يشبه مكانه في شجرة نموذج العناصر في المستند، ويتم وضعه وفقًا لنوع صندوقه وأبعاده.
  2. عائم: يتم وضع الكائن أولاً كتدفق عادي، ثم يتم تحريكه إلى أقصى اليسار أو اليمين قدر الإمكان
  3. مطلق: يتم وضع العنصر في شجرة العرض في مكان مختلف عن مكانه في شجرة نموذج DOM

يتمّ ضبط مخطّط تحديد الموضع من خلال السمة "position" والسمة "float".

  • الثابتة والنسبية تتسبب في تدفق طبيعي
  • تؤدي القيم المطلقة والثابتة إلى تحديد موضع مطلق.

ولا يتم تحديد موضع في الموضع الثابت، ويتم استخدام الموضع التلقائي. في المخططات الأخرى، يحدّد المؤلف الموضع: أعلى أو أسفل أو يسار أو يمين.

يتم تحديد طريقة وضع الصندوق من خلال:

  • نوع العلبة
  • أبعاد العلبة
  • مخطّط تحديد الموضع
  • المعلومات الخارجية، مثل حجم الصورة وحجم الشاشة

أنواع الحزم

مربع حظر: يشكل كتلة - وله مستطيل خاص به في نافذة المتصفح.

مربّع الحظر
الشكل 20: مربّع الحظر

المربّع المضمّن: لا يتضمّن مربّعًا خاصًا به، بل يكون داخل مربّع يحتوي عليه.

المربّعات المضمّنة
الشكل 21: المربّعات المضمّنة

يتم تنسيق القوالب رأسيًا واحدة تلو الأخرى. يتم تنسيق الأسطر المضمنة أفقيًا.

التنسيقات المجمّعة والمضمّنة
الشكل 22: التنسيقات المجمّعة والمضمّنة

يتم وضع المربّعات المضمّنة داخل الخطوط أو "مربّعات الخطوط". لا يقل طول الخطوط على الأقل عن أطول مربع ولكن يمكن أن تكون أطول عند محاذاة المربعات "الخط المرجعي" - مما يعني محاذاة الجزء السفلي من العنصر عند نقطة في مربع آخر ثم الجزء السفلي. إذا كان عرض الحاوية غير كافٍ، سيتم وضع التضمينات في عدة أسطر. وهذا ما يحدث عادةً في الفقرة.

الخطوط
الشكل 23: الأسطر

المكانة في السوق

قريب

الموضع النسبي: يتم تحديد الموضع مثل المعتاد ثم يتم تحريكه بمقدار الدلتا المطلوبة.

تحديد الموضع النسبي
الشكل 24: الموضع النسبي

عائمة

يتم نقل مربّع عائم إلى يمين أو يسار سطر. والميزة المثيرة للاهتمام هي أنّ المربّعات الأخرى تتدفق حوله. مصدر HTML:

<p>
  <img style="float: right" src="images/image.gif" width="100" height="100">
  Lorem ipsum dolor sit amet, consectetuer...
</p>

سيظهر على النحو التالي:

طفو
الشكل 25: قيمة عددية قابلة للتغيير

مطلقة وثابتة

ويتم تحديد التنسيق تمامًا بغض النظر عن التدفق العادي. لا يشارك العنصر في التدفق العادي. تكون السمات نسبةً إلى الحاوية. في الوضع الثابت، تكون الحاوية هي إطار العرض.

موضع ثابت
الشكل 26: موضع ثابت

التمثيل المتعدّد الطبقات

يتم تحديد ذلك من خلال خاصية z-index في CSS. ويمثّل السمة الثالثة للمربّع: موضعه على طول "محور z".

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

يتم ترتيب الحِزم وفقًا لسمة z-index. تشكل المربّعات التي تحتوي على السمة z-index حزمة محلية. يحتوي إطار العرض على الحزمة الخارجية.

مثال:

<style type="text/css">
  div {
    position: absolute;
    left: 2in;
    top: 2in;
  }
</style>

<p>
  <div
    style="z-index: 3;background-color:red; width: 1in; height: 1in; ">
  </div>
  <div
    style="z-index: 1;background-color:green;width: 2in; height: 2in;">
  </div>
</p>

ستكون النتيجة على النحو التالي:

موضع ثابت
الشكل 27: موضع ثابت

على الرغم من أنّ div الأحمر يسبق div الأخضر في الترميز، وكان من المفترض أن يتم رسمه قبل ذلك في التدفق العادي، تكون قيمة سمة z-index أعلى، لذا يكون أكثر تقدّمًا في الحزمة التي يحتفظ بها مربّع الجذر.

الموارد

  1. بنية المتصفّح

    1. Grosskurth, Alan. بنية مرجعية لمتصفّحات الويب (pdf)
    2. غوبتا، فينيت طريقة عمل المتصفّحات - الجزء 1: البنية
  2. التحليل

    1. Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (المعروفة أيضًا باسم "كتاب التنين")، Addison-Wesley، 1986
    2. ريك جيليف الخط الغامق والجميل: مسودتان جديدتان للغة HTML 5
  3. Firefox

    1. L. ديفيد بارون، Faster HTML and CSS: Layout Engine Internals for Web Developers (تحسين أداء HTML وCSS: العناصر الداخلية لمحرك التنسيق لمطوّري الويب)
    2. L. "ديفيد بارون"، مقالة Faster HTML and CSS: Layout Engine Inters for Web Developers (فيديو عن Google Tech Talk)
    3. L. "ديفيد بارون"، محرك تنسيق Mozilla
    4. L. ديفيد بارون، مستندات نظام نمط Mozilla
    5. كريس ووترسون، ملاحظات حول إعادة تدفق HTML
    6. كريس ووترسون، نظرة عامة على Gecko
    7. ألكسندر لارسون، مسار طلب HTTP في HTML
  4. WebKit

    1. ديفيد هايات، Implementing CSS(part 1)
    2. ديفيد هايات، نظرة عامة على WebCore
    3. "ديفيد هايات"، WebCore Rendering
    4. David Hyatt, The FOUC Problem
  5. مواصفات W3C

    1. مواصفات HTML 4.01
    2. مواصفات HTML5 من W3C
    3. مواصفات صفحات الأنماط المتتالية، المستوى 2، المراجعة 1 (CSS 2.1)
  6. تعليمات إنشاء المتصفحات

    1. Firefox. https://developer.mozilla.org/Build_Documentation
    2. WebKit‏: http://webkit.org/building/build.html

الترجمات

تمت ترجمة هذه الصفحة إلى اليابانية مرتين:

يمكنك عرض الترجمات المُستضافة خارجيًا للّغتين الكورية والتركية.

شكرًا للجميع.