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

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

تمهيد

هذا التحضير الشامل للعمليات الداخلية لـ 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. شريط العناوين لإدراج معرّف الموارد المنتظم (URI)
  2. زرا الانتقال للأمام والخلف
  3. خيارات الإشارات المرجعية
  4. زرا إعادة التحميل والإيقاف لإعادة تحميل المستندات الحالية أو إيقاف تحميلها
  5. زر الصفحة الرئيسية الذي ينقلك إلى الصفحة الرئيسية

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

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

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

  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 مصطلح "Layout" لوضع العناصر، بينما تسميه Gecko "Reflow". "المرفق" هو مصطلح WebKit يربط عُقد DOM والمعلومات المرئية لإنشاء شجرة العرض. يتمثل الاختلاف البسيط غير الدلالي في أن Gecko لديها طبقة إضافية بين HTML وشجرة DOM. ويطلق عليها "مستنق المحتوى" وهي شركة مصنعة لإنشاء عناصر DOM. سنتحدث عن كل جزء من التدفق:

التحليل - عام

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

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

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

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

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

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

المحلل اللغوي - تركيبة Lexer

يمكن فصل التحليل إلى عمليتين فرعيتين: التحليل المعجمي وتحليل بناء الجملة.

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

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

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

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

من المستند المصدر إلى تحليل الأشجار
الشكل 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
term + 3 - 1
تشغيل المصطلح من 3 إلى 1
تعبير - 1
عملية التعبير 1
تعبير -

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

جارٍ إنشاء المحللين اللغويين تلقائيًا

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

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

محلل HTML اللغوي

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

قواعد HTML

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

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

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

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

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

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

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

ملف HTML DTD

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

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

نموذج كائن المستند (DOM)

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

جذر الشجرة هو الكائن "Document".

هناك علاقة بين نموذج الإعلان وكائن المستند (DOM) بالترميز. مثال:

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

ستتم ترجمة هذا الترميز إلى شجرة نموذج العناصر في المستند التالية:

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

مثلما هو الحال في HTML، يتم تحديد DOM من خلال مؤسسة W3C. يمكنك الاطّلاع على 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" لدينا وستتم إضافته إلى الشجرة.

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

تم الآن استلام الرموز المميزة للأحرف الخاصة بسلسلة "Hello world". سيؤدي العنوان الأول إلى إنشاء عقدة "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>. ولكي يصبح متوافقًا مع 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 and Bison لإنشاء برامج تحليلية تلقائيًا من ملفات القواعد النحوية للغة CSS. كما تتذكر من مقدمة المحلل اللغوي، يقوم Bison بإنشاء محلل لغوي من أسفل إلى أعلى وShift وخفض. يستخدم Firefox محللاً لغويًا من أعلى لأسفل مكتوبًا يدويًا. وفي كلتا الحالتين، يتم تحليل كل ملف CSS إلى كائن ورقة أنماط. يحتوي كل عنصر على قواعد CSS. تحتوي عناصر قواعد CSS على عناصر أداة الاختيار والتعريف وعناصر أخرى متوافقة مع قواعد CSS.

تحليل CSS.
الشكل 12: تحليل CSS

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

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

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

التحليل المبني على توقُّع

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

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

من ناحية أخرى، تشتمل أوراق الأنماط على نموذج مختلف. من الناحية النظرية، يبدو أنّ أوراق الأنماط لا تغيّر شجرة DOM، فلا داعي لانتظارها وإيقاف تحليل المستند. ومع ذلك، هناك مشكلة في النصوص البرمجية التي تطلب معلومات عن النمط أثناء مرحلة تحليل المستندات. إذا لم يتم تحميل النمط وتحليله بعد، فإن النص البرمجي سيحصل على إجابات خاطئة ويبدو أن ذلك قد تسبب في الكثير من المشكلات. تبدو هذه الحالة حالة هامشية ولكنها شائعة جدًا. يحظر فايرفوكس جميع النصوص البرمجية عندما تكون هناك ورقة أنماط لا تزال يتم تحميلها وتحليلها. تحظر 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. وتتضمن معلومات هندسية مثل العرض والارتفاع والموضع.

يتأثر نوع المربع بقيمة "عرض" لسمة النمط ذات الصلة بالعقدة (راجع القسم احتساب النمط). إليك رمز 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، ولكن العلاقة ليست واحدًا لواحد. لن يتم إدراج عناصر DOM غير المرئية في شجرة العرض. أحد الأمثلة على ذلك هو عنصر "head". كما أنّ العناصر التي تم تحديد قيمة العرض لها بالقيمة "none" لن تظهر في العرض التدرّجي (بينما تظهر العناصر ذات مستوى الرؤية "المخفي" في العرض التدرّجي).

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

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

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

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

تدفق تشييد الشجرة

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

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

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

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

احتساب النمط

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

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

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

ويسبب احتساب النمط بعض الصعوبات:

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

    على سبيل المثال - محدد المركّب هذا:

    div div div div{
    ...
    }
    

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

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

لنرَ كيف تواجه المتصفّحات هذه المشاكل:

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

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

  1. يجب أن تكون العناصر في نفس حالة الماوس (على سبيل المثال، لا يمكن أن يكون أحدهما في :تمرير بينما الآخر ليس كذلك)
  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 في شجرة القواعد.

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

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

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

بالنسبة إلى التركيبات التي تحتوي على قواعد مُكتسَبة من الأصل، يتم التخزين المؤقت في شجرة السياق (يتم اكتساب خاصية اللون في الواقع، ولكن يتعامل معها 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 إذا كان التعريف الوارد منه سمة "نمط" وليس قاعدة تحتوي على أداة اختيار، أو 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 علامة تشير إلى ما إذا كان قد تم تحميل جميع أوراق الأنماط ذات المستوى الأعلى (بما في ذلك عمليات الاستيراد @). إذا لم يتم تحميل النمط بالكامل عند إرفاقه، يتم استخدام عناصر نائبة ووضع علامة عليه في المستند، وستتم إعادة احتسابها بمجرد تحميل أوراق الأنماط.

التنسيق

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

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

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

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

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

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

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

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

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

التنسيق العام والتزايدي

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

  1. يشير ذلك المصطلح إلى تغيير في النمط العام يؤثر في جميع برامج العرض، مثل تغيير حجم الخط.
  2. نتيجة لتغيير حجم شاشة

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

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

التخطيط الإضافي.
الشكل 18: التنسيق الإضافي - يتم فقط عرض أجهزة العرض غير النظيفة وأطفالها

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

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

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

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

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

تحسينات

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

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

عملية التخطيط

عادة ما يكون للتخطيط النمط التالي:

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

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

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

حساب العرض

ويتم احتساب عرض العارض باستخدام عرض مجموعة الحاوية وخاصية "width" لنمط العارض والهوامش والحدود.

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

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

سيتم حسابه بواسطة WebKit على النحو التالي(class RenderBox طريقة calc width):

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

يمثل clientعرض و clientHeight الجزء الداخلي للكائن باستثناء الحد وشريط التمرير.

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

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

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

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

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

كسر الأسطر

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

رسم

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

عالمي وتزايدي

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

ترتيب الطلاء

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

  1. background color
  2. صورة الخلفية
  3. border
  4. الأطفال
  5. outline

قائمة عرض Firefox

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

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

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

مساحة تخزين مستطيلة من WebKit

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

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

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

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

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

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

حلقة الأحداث

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

while (!mExiting)
    NS_ProcessNextEvent(thread);

النموذج المرئي لـ CSS2

اللوحة

وفقًا لمواصفات 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" هو حظر.

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

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

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

  1. عادي: يتم وضع الكائن وفقًا لمكانه في المستند. يعني ذلك أنّ مكانها في شجرة العرض يشبه مكانها في شجرة نموذج العناصر في المستند (DOM) ويتم تنظيمها وفقًا لنوع المربّع والأبعاد.
  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. غروسكرث، آلان. بنية مرجعية لمتصفّحات الويب (pdf)
    2. غوبتا، فينيت. آلية عمل المتصفّحات - الجزء 1 - البنية
  2. التحليل

    1. آهو، وسيثي، وأولمان، مؤلفو التجزئة: المبادئ، والتقنيات، والأدوات (المعروف أيضًا بـ "كتاب التنّين")، وأديسون ويسلي، 1986
    2. ريك جيليف. The Bold and the Beautiful: مسودتان جديدتان لـ HTML 5.
  3. Firefox

    1. L. "ديفيد بارون"، استجابة سريعة لـ HTML وCSS: التصميمات الداخلية لمحرك التنسيق لمطوّري البرامج على الويب.
    2. L. ديفيد بارون، Faster HTML وCSS: التنسيق الداخلي لمحرك البحث لمطوّري البرامج على الويب (فيديو عن التكنولوجيا في Google)
    3. L. ديفيد بارون، محرك تصميم Mozilla
    4. L. ديفيد بارون، وثائق نظام أنماط Mozilla
    5. كريس واترسون، Notes on HTML Reflow
    6. كريس واترسون، Gecko Overview
    7. ألكسندر لارسون، حياة طلب HTML HTTP
  4. WebKit

    1. ديفيد حياة، تنفيذ لغة CSS(الجزء 1)
    2. "ديفيد حياة"، نظرة عامة على WebCore
    3. ديفيد حياة، WebCore Rendering
    4. "ديفيد حياة"، مشكلة FOUC
  5. مواصفات W3C

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

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

الترجمات

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

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

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