arefn
arefn
خواندن ۵۷ دقیقه·۱ سال پیش

مرورگر ها چگونه کار می کنند ؟ نگاهی عمیق و تخصصی

در هنگام نوشتن و ترجمه این مطلب به دلیل طولانی بودن، ویرگول با مشکلات ذخیره سازی و سینک مواجه شده است و این مطلب تا زمان حل مشکل به صورت ناتمام و آرشیو نگه داری می شود. برای خواندن منبع اصلی لطفا به لینک زیر مراجعه کنید:

https://web.dev/howbrowserswork/



این مطلب به صورت تخصصی برای توسعه دهندگان نوشته شده است و برای مطالعه آن نیاز به اطلاعات حداقلی درباره توسعه وب دارید.برای توسعه دهندگان یادگیری اجزای داخلی مرورگر می تواند به تصمیم گیری شما کمک شایانی بکند و با آن دلیل پشت بهترین الگو ها را بفهمید.

امروزه در زمان خوبی سپری می کنیم که اکثر مرورگر های معروف متن باز هستند و می توانیم داخل این مرورگر ها رو مشاهده و بررسی کنیم بر عکس زمان IE که مرورگر ها مثل جعبه سیاه غیر قابل دسترسی بودند. اکنون زمان خوبی است تا ببینیم داخل این همه خط سی پلاس پلاس چه میگذرد و مرورگر ها چگونه کار می کنند.

مقدمه

مرورگر های وب پرکاربردترین نرم افزار های جهای هستند. در این آموزش ما نگاهی تخصصی به نحوه کار این نرم افزار می اندازیم و خواهیم دید که وقتی آدرس سایت گوگل را در نوار جستجو تایپ می کنیم چه اتفاقی می افتد تا زمانی صفحه گوگل را در مرورگر مشاهده می کنیم.


مرورگر هایی که درباره آن حرف می زنیم

امروز در سال 2023 هنوز هم مرورگر Google Chrome با اختلاف در شماره یک قرار دارد و بعد به ترتیب مرورگر های سافاری، ادج، اپرا، فایرفاکس و سامسونگ اینترنت برترین مرورگر های فعلی جهان هستند. گوگل کروم و سافاری هر دو از موتور متن باز وب‌کیت استفاده می کنند که البته گوگل از سال 2013 انشعاب (Fork) جدیدی از وب‌کیت را به نام بلینک شروع کرد که هم اکنون گوگل کروم نسخه 28، ادج نسخه 79 و اپرا نسخه 15، اندروید 4.4 به بعد یا حتی اسپاتیفای و Node.js هم از استفاده می کنند. بلینک متن باز و بخشی از پروژه Chromium است که با همکاری گوگل، متا، ماکروسافت، اپرا، ادوبی، اینتل، آی بی ام، سامسونگ و ... توسعه می یابد.

سهم بازار مرورگر در سراسر جهان
سهم بازار مرورگر در سراسر جهان

بنا به StatCounter statistics سهم بازار مرورگر در سراسر جهان از جولای 2022 تا جولای 2023 با برتری 63.55% گوگل کروم همراه بوده است.

وظیفه اصلی مرورگر ها

وظیفه اصلی مرورگر ها ارائه منابع انتخابی شما در دنیای وب است. مرورگر درخواست شما را به سرور می دهد و در نتیجه جواب را برای شما نمایش می دهد. این منابع معمولا سند های HTML هستند ولی ممکن PDF، عکس یا حتی نوع دیگری باشد. آدرس این منابع توسط کاربر به وسیله URI (Uniform Resource Identifier) مشخص می شود.

روش این تفسیر و نمایش HTML به چگونگی مشخص شدن خصوصیات HTML و CSS بر می گردد. این مشخصات توسط سازمان W3C (World Wide Web Consortium) اجرا می شود. این سازمان وظیفه حفظ استاندارد های وب را بر عهده دارد. برای سال ها مرورگر ها تنها بخشی از مشخصات را اجرا و باقی را با پسوند های خود توسعه دادند که باعث مشکلات جدی ناسازگاری شد. امروزه اکثر مرورگر ها کم و بیش با مشخصات مطابقت دارند.

مرورگر ها شباهت بسیاری با هم دیگر دارند که برخی رایج ترین آن ها :

  1. نوار آدرس برای URI
  2. دکمه های عقب و جلو
  3. نشانک گذاری (Bookmark)
  4. دکمه بازخوانی (Refresh) و توقف برای نگه داشتن بارگیری صفحه فعلی
  5. دکمه صفحه اصلی برای برگشت به صفحه اصلی مرورگر

شاید عجیب به نظر برسد ولی رابط کاربری مرورگر ها در هیچ جای رسمی تعریف نشده است و تمام چیزی که می بینید توسط بهترین تمرین ها و تجربه و حتی کپی مرورگر ها از یک دیگر بوجود امده است. مشخصات رسمی HTML5 هیچ تعریفی برای رابط کاربری مرورگر ها تعریف نکرده و فقط برخی عناصر مهم را فهرست بندی کرده است که می توان به نوار وضعیت و نوار ابزار اشاره کرد. البته برخی مرورگر ها ویژگی ها منحصر به فرد خودشان را دارند مانند هوش مصنوعی ادج.

ساختار سطح بالا مرورگر

اجزای اصلی مرورگر عبارتند از:

  1. رابط کاربری (user interface) : شامل نوار آدرس، دکمه برگشت/به جلو، منوی نشانک گذاری، و غیره است. در واقع تمام بخشی که کاربر مشاهده می کند به جز بخشی که در خواست داده است را شامل می شود.
  2. موتور مرورگر ( Browser Engine) : رابطی بین اقدامات کاربر و موتور رندر را کنترل می کند.
  3. موتور رندر (Rendering Engine) : مسئول نمایش محتوا درخواستی. برای مثال اگه محتوا درخواستی شما HTML باشد، موتور رندر HTML و CSS را تحلیل و سپس محتوا تحلیل شده را در صفحه نشان می دهد.
  4. شبکه سازی (Networking) : برای تماس های شبکه ای مثل درخواست HTTP، برای پلتفرم های متفاوت از پیاده سازی های متفاوتی استفاده می شود پشت یک رابط مستقیم از پلتفرم.
  5. بک اند رابط کاربری (UI backend) : برای ترسیم ویجت های اولیه استفاده می شود مثل جعبه های ترکیبی و پنجره ها. این بک اند یک رابط عمومی را افشا می کند که مربوط به پلتفرم خاصی نیست که در پایین از متد های رابط کاربری سیستم عامل استفاده می کند.
  6. مترجم جاوااسکریپت (JavaScript Interpreter) : برای تحلیل و اجرای کد جاوا اسکریپت استفاده می شود.
  7. ذخیره سازی داده ها (Data storage) : یک لایه مقاوم است. مرورگر ممکن است به دخیره سازی هرگونه اطلاعات محلی نیاز پیدا کند مانند کوکی ها. مرورگر ها از سازوکار ذخیره سازی دیگری نیز پشتیبانی می کنند مانند : localStorage، IndexedDB، WebSQL و فایل های سیستم.

به این نکته توجه کنید که مرورگر های مانند کروم از چندین لایه موتور رندر استفاده می کنند در واقع برای هر تب جدا گانه یک موتور تا هر تب در یک فرایند جداگانه اجرا شود.

موتور رندر

مسئولیت موتور رندر همان رندر کردن به معنای کلمه است : تولید ماشینی تصاویر بر پایهٔ مدل‌های محاسباتی، و سپس نمایاندن رایانه‌ای آن‌ها بر روی نمایشگر

به طور پیشفرض موتور رندر می تواند اسناد HTML و XML و حتی عکس را نمایش دهد. برای نمایش دیگر اطلاعات به افزونه نیاز دارد برای مثال برای نمایش PDF به افزونه مشاهده گر PDF نیاز دارد. در این فصل ما فقط بر روی وظیفه اصلی تمرکز می کنیم : نمایش HTML و عکس که با CSS فرمت شده است.

موتور های رندر

مرورگرهای مختلف از موتورهای رندر متفاوتی استفاده می کنند : IE از Trident، فایرفاکس از Gecko، سافاری از WebKit، کروم و اپرا از بلینک.

وب‌کیت یک موتور رندر متن باز است که ابتدا به عنوان موتور برای پلتفورم لینوکس شروع به کار کرد و توسط اپل برای مک و ویندوز در دسترس قرار گرفت.

جریان اصلی

موتور رندر کارش رو با دریافت اصلاعات از شبکه درخواستی آغاز می کند. این کار توسط تکه های 8 کیلوبایتی انجام می شود.

سپس مراحل زیر توسط موتور رندر انجام می شود :

روند ساده موتور رندر
روند ساده موتور رندر

موتور رندر شروع به تحلیل سند HTML می کند و عناصر رو تبدیل می کنه به گره های (nodes) DOM در درختی به نام content tree یا درخت محتوا، بعد موتور اطلاعات ظاهری (style) رو از عناصر و CSS خارجی تحلیل می کنه. اطلاعات ظاهری و عناصر با هم در HTML یک درخت دیگه ای رو شکل می دهند به نام : درخت رندر

درخت رندر شامل مستطیل هایی با ویژگی های بصری مثل رنگ و ابعاد است. این مستطیل ها به ترتیب بر روی نمایشگر نمایش داده می شوند.

بعد از ساخت و ساز درخت رندر مرحله بعدی پردازش لایه بندی است. پردازش لایه بندی به معنی دادن مختصات دقیق محل قرارگیری هر گره (node) است که کجا صفحه نمایش داده شود. مرحله بعدی نقاشی است - موتور رندر کارش تمام شده و حالا هر گره توسط بک اند رابط کاربری (UI backend) نقاشی می شود.

توجه کنید که این یک روند آهسته و تدریجی است. برای تجربه کاربری بهتر ، موتور رندر سعی می کند در سریع ترین زمان ممکن محتوا را در صفحه نمایش دهد. قبل از شروع ساخت و لایه بندی درخت رندر منتظر نمی ماند تا تمام عناصر HTML تحلیل شود. بخشی از محتوا تحلیل و نمایش داده می شوند درحالی که این روند با بقیه مطالبی که از شبکه دربافت می کند ادامه می دهد.

مثال هایی از جریان اصلی

WebKit
WebKit
Gecko
Gecko


در دو عکس بالا می توانید WebKit و Gecko مشاهده کنید که از دید فنی کمی متفاوت عمل می کنند ولی در جریان اصلی یکسان هستند.

گکو درختی از عناصر که بصری فرمت شده رو یک درخت قاب (Frame tree) می نامند و هر عنصر در آن یک قاب (Frame) است. وب کیت از اصطلاح درخت رندر (Render Tree) که از Render Objects تشکیل شده است استفاده می کند. وب کیت برای قراردادن عناصر از اصطلاح layout و گکو از اصطلاح Reflow استفاده می کند. پیوست (Attachment) اصطلاح وب کیت برای اتصال گره های (Nodes) DOM و اطلاعات است تا درخت رندر را ایجاد کند. یک تفاوت غیر معنایی جزئی گکو این است که بین HTML و درخت DOM یک لایه اضافه دارد به نام سینک محتوا (content sink) که کارخانه ای برای تولید عناصر DOM است.

تحلیل - عمومی

از ان جایی که تحلیل (parsing) مرحله ای مهم در موتور رندر می باشد، کمی عمیق تر وارد این مبحث می شویم اما اول با یک مقدمه کوچک از تحلیل شروع می کنیم.

تحلیل یک سند به معنی ترجمه آن به ساختاری است که کد می تواند از ان استفاده کند. نتیجه تحلیل معمولا یک درخت از گره ها است ساختار یک سند را ارائه می دهد که به آن درخت تحلیل (parse tree) یا درخت نحو (syntax tree) می گویند.

برای مثال، تحلیل عبارت 2 + 3 - 1این درخت را برمی گرداند:

عبارت ریاضی برای درخت گره
عبارت ریاضی برای درخت گره

دستور ها

تحلیل بر پایه قوانینی است که syntax سند از آن پیروی می کند : زبانی یا قالبی که در آن نوشته شده است. هر قالبی که تحلیل می شود باید دستورات قطعی از واژگان و قوانین نحوی داشته باشد که به آن دستور زبان مستقل از متن (Context-free grammar) می گویند.

تحلیل - ترکیب واژگان

تحلیل می تواند به دو بخش مجزا کوچک برای پردازش تقسیم شود : تحلیل واژگانی (lexical analysis) و تحلیل نحوی (syntax analysis)

تحلیل واژگانی فرآیندی است که ورودی را به توکن ها تبدیل می کند. توکن ها واژگان زبانی هستند : مجموعه ای از بلوک های ساختمانی. در زبان انسان شامل تمام کلماتی است که در فرهنگ لغت یک زبان آمده است.

تحلیل نحوی اعمال کردن نحوه دستور زبان است.

تحلیل کننده ها معمولا کار را به دو مولفه (Component) تقسیم می کنند: lexer یا tokenizer که وظیفه دارد ورودی را به توکن ها تبدیل کند و تحلیل کننده وظیفه ساختن درخت تحلیل را براساس تحلیل ساختار سند با توجه به نحوه دستور زبان دارد.

لکسر می داند که چگونه کاراکتر های نامربوط مانند فاصله و خط پایان را از بین ببرد.

از سند منبع تا درخت تجزیه
از سند منبع تا درخت تجزیه

پروسه تحلیل تکرار شونده است. تحلیل کننده معمولا از لکسر توکن جدید میخواهد و سعی می کند توکن را با یکی از قوانین نحوی (syntax rules) تطبیق دهد. اگر توکن مطابقت داشته باشد یک گره (node) متناظر با توکن به درخت تحلیل اضافه می شود و تحلیل کننده یک توکن دیگر را درخواست می کند.

اگر هیچ قانونی مطابقت نداشت، تحلیل کننده توکن را داخلی ذخیره می کند و اینقدر درخواست توکن می کند تا قانونی که با تمام توکن های ذخیره شده داخلی مطابقت داشته باشد پیدا شود. درصورتی که هیچ قانونی پیدا نشود تحلیل کننده یک اعتراض مطرح می کند به این معنی که این سند معتبر نیست و دارای خطای نحوی(syntax) می باشد.

ترجمه

در بسیاری از موارد درخت تحلیل محصول آخر نیست. تحلیل معمولا درون ترجمه استفاده می شود: تبدیل ورودی سند به یک قالب (format) دیگر همانند تلفیق (compilation). کامپایلر که کد منبع را به زبان ماشین کامپایل می کند ابتدا آن را تحلیل می کند به درخت تحلیل و سپس ترجمه درخت را به سند کد ماشین.

مثال تحلیل

در مثال بالا ما درخت تحلیل را توسط اصطلاحات ریاضی بیان کردیم. بیایید سعی کنیم یک زبان ریاضی ساده تعریف کنیم و پروسه تحلیل را ببینیم.

اصطلاح کلیدی
زبان ما می تواند شامل اعداد صحیح، علامت مثبت و منفی باشد.

نحو (Syntax):

1. بلوک های ساختار نحو زبان عبارت ها، اصطلاحات و عملیات هستند.

2. زبان ما می تواند شامل هر تعداد اصطلاح (expressions) باشد.
3. هر اصطلاح توسط شرایط (term) به دنبال عملیات (operation) که به دنبال یک شرایط دگیر است تعریف می شود.

4. یک عملیات یا توکن مثبت است یا توکن منفی.

5. یک شرایط یا یک توکن عدد صحیح است یا اصطلاح.


بیایید ورودی 2 + 3 -1 را بررسی کنیم.

اولین رشته فرعی که با یک قانون مطابقت دارد 2 است: طبق قانون 5 یک شرایط (term) است. دومین تطبیق 2+3 است: با قانون سوم مطابقت دارد: یک شرایط به دنبال یک عمل به دنبال عملی دیگر. مطابقت بعدی فقط در آخر ورودی انجام می شود. 2+3-1 یک اصطلاح است زیرا ما هم اکنون می دانیم که 2+3 یک شرایط است، پس ما یک شرایط به دنبال یک عملیات داریم که به دنبال یک شرایط دیگر است. 2++ با هیچ قوانینی مطابقت ندارد بنابراین یک ورودی نامعتبر است.


تعاریف رسمی برای واژگان و نحو

واژگان معمولا توسط عبارت باقاعده (regular expression) بیان می شوند.

برای مثال زبان ما به صورت زیر تعریف می شود:

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

همانطور که می بینید، اعداد صحیح توسط عبارت باقاعده تعریف شده است.

نحو معمولا در قالبی به نام فرم باکوس نائور (Backus–Naur form) تعریف می شود. زبان ما به صورت زیر تعریف می شود:

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

گفتیم که یک زبان می تواند تحلیل بشود با عبارت باقاعده اگر دستور های زبانش، دستور زبان مستقل از متن باشند. یک تعریف حسی از دستور زبان مستقل از متن یک دستور زبانی است که کاملا بتوان در فرم باکوس نائور بیان شود. برای کسب اطلاعات بیشتر به صفحه ویکی پدیا دستور زبان مستقل از متن مراجعه کنید.


انواع تحلیل کننده ها

دو نوع تحلیل کننده وجود دارد: تحلیل کننده بالا به پایین و تحلیل کننه پایین به بالا. یک تعریف حسی این است که تحلیل کننده بالا به پایین ساختار سطح بالا نحو را بررسی می کند و سعی می کند قوانین مرتبط را پیدا کنند. تحلیل کننده پایین به بالا با ورودی شروع می کند و به تدریج آن را به قوانین نحوی تبدیل می کند، از قوانین سطح پایین شروع می کند تا قوانین سطح بالا مطابقت داشته باشند.

بزارید ببینیم چگونه دو نوع تحلیل کننده مثال ما را تحلیل می کنند.

تحلیل کننده بالا به پایین با قوانین سطح بالا شروع می کند: 2+3 را به عنوان یک اصطلاح تشخیص می دهد. سپس 2+3-1 را به عنوان یک اصطلاح تشخیص می دهد (پروسه شناسایی یک اصطلاح تکامل می یابد و با قوانین دیگر تطبیق پیدا می کند اما نقطه شروع قوانین سطح بالا است).

تحلیل کننده سطح پایین به بالا ورودی را اسکن می کند تا زمانی که یک قانون پیدا شود. سپس ورودی پیدا شده را که تطبیق دارد با قانون جایگزین می کند. این کار تا پایان ورودی ادامه پیدا می کند. اصطلاح مطابقت شده در پشته (stack) تحلیل کننده قرار می گیرد.

Stack Input 2 + 3 - 1 term + 3 - 1 term operation 3 - 1 expression - 1 expression operation 1 expression -

این نوع تحلیل کننده از پایین به بالا را پارسر انتقال کاهش (shift-reduce parser) می گویند، زیرا ورودی به راست انتقال یافته است (یک اشاره گر را تصور کنید که ابتدا به شروع ورودی اشاره می کند و به سمت راست حرکت می کند) و به تدریج به قوانین نحوی کاهش می یابد.

تولید تحلیل کننده به صورت خودکار

ابزار هایی وجود دارند که می توانند تحلیل کننده تولید کنند. شما به آن ها دستور زبانی می دهید (واژگان و دستورات نحو) و برای شما یک تحلیل کننده کاری می دهند. ساختن یک تحلیل کننده نیاز به درک عمیقی از تحلیل دارد و اصلا ایجاد یک تحلیل کننده بهینه با دست کار راحتی نیست، پس تولید کننده تحلیل کننده می تواند خیلی مفید باشد.

وب کیت از دو نوع تولید کننده تحلیل کننده استفاده می کند: فلکس برای ایجاد لکسر و بایسون برای ایجاد یک تحلیل کننده (ممکن است با نام های لکس (Lex) و یاک (Yacc) هم بشناسید). ورودی فلکس فایلی است حاوی تعاریف عبارت باقاعده از توکن ها است.ورودی بایسون قوانین نحو زبان در قالب فرم باکوس نائور است.

تحلیل کننده HTML

وظیفه تحلیل کننده HTML این است که HTML را تبدیل کند به درخت تحلیل .

تعریف گرامری HTML

واژگان و دستورات نحوی HTML در مشخصات ایجاد شده توسط اتحادیه وب جهان‌گستر تعریف شده است.

دستور زبان مستقل از متن نیست

همانطور که در مقدمه تحلیل دیدیم، دستور زبان نحو را می توان به طور رسمی با استفاده از فرمت باکوس نائور تعریف کرد.
متاسفانه تمام موضوعات تحلیل کننده مرسوم در HTML اعمال نمی شود (من آنها را فقط برای سرگرمی مطرح نکردم - آنها در تحلیل CSS و جاوا اسکریپت استفاده خواهند شد). HTML را نمی توان به راحتی با دستور زبان مستقل از متن که تحلیل کننده ها به آن نیاز دارند تعریف کرد.

یک قالب رسمی برای تعریف HTML وجود دارد - تعریف نوع سند یا دی تی دی (Document Type Definition) - اما یک دستور زبان مستقل از متن نیست.
در نگاه اول عجیب به نظر می رسد: HTML بسیار به XML نزدیک است و تعداد زیادی تحلیل کننده XML وجود دارند. یک نوع XML از HTML وجود دارد به نام XHTML، پس تفارت بزرگ چیست ؟

تفاوت بزرگ در این است که رویکرد HTML بیشتر بخشنده است: به شما اجازه می دهد تگ های خاصی را حذف کنید (بعد به طور ضمنی اضافه می شوند)، یا بعضی وقت ها برچسب های (tags) شروع و پایان را حذف کنید یا ... در کل HTML ملایم است ولی XML برعکس سفت و سخت است.

این جزئیات به ظاهر کوچک دنیایی از تفاوت را ایجاد می کند. در یک سمت این دلیل اصلی محبوب بودن HTML است: اشتباهات شما را می بخشد و زندگی را برای توسعه دهنده آسان می کند. در یک سمت دیگر، نوشتن یک دستور زبان رسمی را دشوار می کند. بطور مختصر، HTML توسط تحلیل کننده های مرسوم به راحتی تحلیل نمی شود از آن جایی که یک دستور زبان مستقل از متن نیست و با تحلیل کننده های XML تحلیل نمی شود.

تعریف نوع سند (DTD)

تعریف HTML در قالب DTD است. این قالب برای تعریف زبان های خانواده اس‌جی‌ام‌ال (StandardGeneralized Markup Language) استفاده می شود. این قالب شامل تعاریفی برای همه عناصر مجاز، ویژگی ها و سلسه مراتب (Hierarchy) آنها است. همانطور که قبلا دیدیم، HTML DTD یک دستور زبان مستقل از متن را تشکیل نمی دهد.
تعداد کمی از نوع DTD وجود دارند. حالت سخت صرفاً با مشخصات فنی مطابقت دارد، اما سایر حالت ها شامل پشتیبانی از نشانه گذاری (markup) مورد استفاده مرورگرها در گذشته هستند. هدف سازگاری عقب‌رو (backward compatibility) با محتوا قدیمی است. DTD حالت سخت فعلی این جا است: www.w3.org/TR/html4/strict.dtd

مدل شیءگرای سند (DOM - Document Object Model)

درخت خروجی (درخت تحلیل) یک درخت از عناصر DOM و ویژگی گره های آن است. مدل شیءگرای سند ارائه شیء از سند HTML و رابط عناصر HTML با دنیای خارج مانند جاوااسکریپت (JavaScript) است.

ریشه این درخت شیء Document است.

مدل شیءگرای سند تقریباً رابطه یک به یک با نشانه گذاری دارد. مثلا:

<html> <body> <p> Hello World </p> <div> <img src=&quotexample.png&quot/></div> </body> </html>

این نشانه گذاری به درخت DOM زیر ترجمه می شود:

همانند HTML، مدل شیءگرای سند توسط اتحادیه وب جهان‌گستر تعریف شده است.

وقتی گفتم درخت شامل گره های DOM است. منظورم این است که درخت از عناصری ساخته شده است که یکی از رابط های DOM را پیاده سازی می کند. مرورگر از پیاده سازی های مشخصی استفاده می کند که ویژگی های دیگری دارد که در داخل مرورگر استفاده می شود.

الگوریتم تحلیل

همانطور که در قسمت قبل مشاهده کردیم، HTML نمی تواند توسط تحلیل کننده بالا به پایین یا پایین به بالا تحلیل شود.

دلایل عبارتند از:

1. ماهیت بخشنده زبان.

2. این واقعیت که مرورگرها تحمل خطای مرسوم برای پشتیبانی از موارد شناخته شده HTML نامعتبر را دارند.

3. پروسه تحلیل مجدد وارد می شود. برای زبان‌های دیگر، منبع در طول تحلیل تغییر نمی‌کند، اما در HTML، کد پویا (مانند عناصر اسکریپت حاوی فراخوان‌های ) می‌تواند گره های اضافی اضافه کند، بنابراین فرآیند تحلیل در واقع ورودی را تغییر می‌دهد.

مرورگرها قادر به استفاده از تکنیک های تحلیل معمولی نیستند پس تحلیل کننده های سفارشی خودشان را برای تحلیل HTML ایجاد می کنند.

الگورتم تحلیل با جزئیات کامل در اینجا توضیح داده شده است. الگوریتم از دو مرحله تشکیل شده است: توکن سازی (tokenization) و درخت سازی (tree construction).

توکن سازی یک تحلیل‌گر واژگانی است، ورودی را به توکن تحلیل می کند. در کنار توکن های HTML برچسب های شروع، پایان و نام صفت ها و ویژگی ها هستند.

توکن سازی توکن را می شناسد، آن را به درخت سازنده می دهد و کاراکتر بعدی را برای تشخیص نشانه بعدی مصرف می کند و تا پایان ورودی به همین ترتیب ادامه می دهد.

الگوریتم توکن سازی

خروجی الگوریتم توکن HTML است. الگوریتم به عنوان یک ماشین حالت بیان می‌شود. هر حالت یک یا چند کاراکتر از جریان ورودی را استفاده می‌کند و وضعیت بعدی را با توجه به آن کاراکترها به روز می‌کند. این تصمیم تحت تأثیر وضعیت توکن‌سازی فعلی و وضعیت درخت‌سازی است. این بدان معناست که همان کاراکتر استفاده شده، بسته به وضعیت فعلی، نتایج متفاوتی را برای حالت بعدی به همراه خواهد داشت. الگوریتم برای توصیف کامل بسیار پیچیده است، بنابراین بیایید یک مثال ساده را ببینیم که درک اصلی ما کمک می‌کند.

مثال ساده - ساخت توکن برای مثال زیر:

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

حالت اولیه "وضعیت داده" است. وقتی با کاراکتر < مواجه می‌شوید، وضعیت به "Tag open state - نشانه گذاری وضعیت باز" تغییر می‌کند. مصرف یک کاراکتر a-z باعث ایجاد یک "Start tag token - شروع نشانه گذاری توکن" می‌شود، وضعیت به "Tag name state - نشانه گذاری نام وضعیت" تغییر می‌کند. تا زمانی که کاراکتر > استفاده شود در این حالت می‌مانیم. هر کاراکتر به نام توکن جدید اضافه می‌شود. در مورد حالت ما توکن ایجاد شده یک توکن html است.

با رسیدن به تگ >، توکن فعلی منتشر می‌شود و وضعیت به "وضعیت داده" تغییر می‌کند. تگ <body> نیز با همین مراحل انجام می‌شود. تا کنون تگ‌های html و body منتشر شده است. ما اکنون به "وضعیت داده" بازگشته‌ایم. استفاده کاراکتر H از Hello world باعث ایجاد و انتشار یک توکن کاراکتر می‌شود، این کار تا رسیدن به < از </body> ادامه می‌یابد. ما برای هر کاراکتر از Hello world یک توکن کاراکتر منتشر می‌کنیم.

ما اکنون به "Tag open state - نشانه گذاری وضعیت باز" بازگشته‌ایم. مصرف ورودی بعدی / باعث ایجاد یک توکن تگ پایانی و انتقال به "Tag name state - نشانه گذاری نام وضعیت" می‌شود. دوباره در این وضعیت می‌مانیم تا به > برسیم. سپس توکن تگ جدید منتشر می‌شود و به "وضعیت داده" برمی گردیم. با ورودی </html> مانند مورد قبلی رفتار می‌شود.

الگوریتم درخت سازی

هنگامی که تحلیلگر ایجاد می‌شود، شی Document ایجاد می‌شود. در مرحله ساخت درخت، درخت DOM با سند در ریشه آن اصلاح می‌شود و عناصر به آن اضافه می‌شود. هر توکن منتشر شده توسط توکن سازی توسط سازنده درخت پردازش می‌شود. برای هر توکن، مشخصات مشخص می‌کند که کدام عنصر DOM مربوط به آن است و برای این توکن ایجاد می‌شود. این عنصر به درخت DOM و همچنین پشته (stack) عناصر باز اضافه می‌شود. این پشته برای تصحیح عدم تطابق تودرتو و تگ‌های بسته نشده استفاده می‌شود. این الگوریتم همچنین به عنوان یک ماشین حالت توصیف می‌شود. حالت‌ها «حالت‌های درج» نامیده می‌شوند.

بیایید فرآیند درخت سازی را برای مثال ورودی ببینیم:

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

ورودی مرحله درخت سازی، دنباله‌ای از نشانه‌ها از مرحله توکن‌سازی است. حالت اول "initial mode - حالت اولیه" است. دریافت توکن "html" باعث انتقال به حالت "before html" و پردازش مجدد توکن در آن حالت می‌شود. این باعث ایجاد عنصر HTMLHtmlElement می‌شود که به شی ریشه Document اضافه می‌شود.

وضعیت به "before head" تغییر خواهد کرد. سپس توکن "body" دریافت می‌شود. یک HTMLHeadElement به طور ضمنی ایجاد می‌شود، اگرچه ما یک توکن "head" نداریم و به درخت اضافه می‌شود.

اکنون به حالت "in head" و سپس به "after head" می‌رویم. توکن بدنه دوباره پردازش می‌شود، یک HTMLBodyElement ایجاد و درج می‌شود و حالت به "in body" منتقل می‌شود.

توکن های کاراکتر رشته "Hello world" اکنون دریافت شده است. اولین مورد باعث ایجاد و درج یک گره "متن" می‌شود و کاراکترهای دیگر به آن گره اضافه می‌شوند.

دریافت توکن پایان body باعث انتقال به حالت "after body" می‌شود. اکنون تگ پایان html را دریافت می‌کنیم که ما را به حالت "after after body" منتقل می‌کند. دریافت توکن پایان فایل، تحلیل را تمام می کند.

اقدامات پس از پایان تحلیل

در این مرحله، مرورگر سند را به‌عنوان تعاملی علامت‌گذاری می‌کند و شروع به تحلیل اسکریپت‌هایی می‌کند که در حالت "deferred - به تعویق افتاده" هستند: آنهایی که باید پس از تحلیل سند اجرا شوند. سپس حالت سند روی "complete" تنظیم می‌شود و رویداد "load" اجرا خواهد شد.

شما می‌توانید الگوریتم‌های کامل برای توکن‌سازی و درخت سازی را در مشخصات HTML5 مشاهده کنید.

قدرت تحمل خطای مرورگر ها

شما هرگز خطای "Invalid Syntax" را در صفحه HTML دریافت نمی‌کنید. مرورگرها هرگونه محتوای نامعتبر را برطرف می‌کنند و کارشان را ادامه می‌دهند.

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

من باید حدود یک میلیون قانون را نقض کرده باشم ("mytag" یک برچسب استاندارد نیست، اشتباه تودرتو عناصر "p" و "div" و موارد دیگر) اما مرورگر هنوز آن را به درستی نشان می‌دهد و شکایت نمی‌کند. بنابراین بسیاری از کدهای تحلیلگر اشتباهات نویسندهHTML را رفع می‌کنند.

رسیدگی به خطا در مرورگرها کاملاً ثابت است، اما به طرز شگفت انگیزی بخشی از مشخصاتHTML نبوده است. مانند نشانک گذاری (bookmarking) و دکمه‌های عقب/جلو، این چیزی است که در طول سال‌ها در مرورگرها توسعه یافته است. ساختارهای نامعتبر HTML وجود دارد که در بسیاری از سایت‌ها تکرار می‌شوند، و مرورگرها سعی می‌کنند آنها را به روشی مطابق با سایر مرورگرها اصلاح کنند.

مشخصاتHTML5 برخی از این الزامات را تعریف می کند. (WebKit این توضیح را به خوبی در ابتدای کلاس تحلیل کنندهHTML خلاصه می کند.)

متأسفانه، ما مجبوریم بسیاری از اسناد HTML را مدیریت کنیم که به خوبی شکل نگرفته اند، بنابراین تحلیل کننده باید نسبت به خطاها مدارا کند.

ما باید حداقل از شرایط خطای زیر مراقبت کنیم:

1. عنصری که اضافه می‌شود به صراحت در داخل برخی از برچسب‌های بیرونی ممنوع است. در این حالت باید تمام تگ‌ها را تا آن چیزی که عنصر را ممنوع می‌کند ببندیم و سپس آن را اضافه کنیم.

2. ما مجاز به اضافه کردن مستقیم عنصر نیستیم. ممکن است شخصی که سند را می‌نویسد برخی از برچسب‌ها را در این بین فراموش کرده باشد (یا اینکه تگ بین آن اختیاری است). این می‌تواند در مورد برچسب‌های زیر باشد: HTML HEAD BODY TBODY TR TD LI ...

3. ما می‌خواهیم یک عنصر بلوک (block element) را در یک عنصر درون خطی (inline element) اضافه کنیم. تمام عناصر درون خطی را تا عنصر بلوک بالاتر بعدی ببندید.

4. اگر این کمکی نکرد، عناصر را ببندید تا زمانی که اجازه اضافه کردن عنصر را پیدا کنیم - یا تگ را نادیده بگیرید.

بیایید چند نمونه از قدرت تحمل خطای WebKit را ببینیم:

کد:

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>

وب کیت سلسله مراتب (hierarchy) را به دو جدول هم نژاد تغییر می دهد:

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

کد:

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

وب کیت از یک پشته برای محتویات عنصر فعلی استفاده می کند: جدول داخلی را از پشته جدول بیرونی خارج می کند. اکنون میزها از یک نژاد خواهند بود.

عناصر فرم تو در تو

در صورتی که کاربر فرمی را در فرم دیگری قرار دهد، فرم دوم نادیده گرفته می شود.

کد:

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

سلسله مراتب تگ خیلی عمیق

این نظر خودش گویای داستان است:

سایت www.liceo.edu.mx نمونه‌ای از سایت‌ هایی است که به سطحی از تودرتو بودن در حدود 1500 تگ دست می‌یابد که همگی از مجموعه‌ای از <b>ها هستند. قبل از نادیده گرفتن همه آنها با هم، فقط حداکثر 20 تگ تودرتو از یک نوع را مجاز می کنیم.

کد:

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; }

تگ های body و html نابجا

دوباره این نظر خودش گویای داستان است:

پشتیبانی از HTML واقعا شکسته. ما هرگز تگ بدنه را نمی بندیم، زیرا برخی از صفحات وب احمقانه آن را قبل از پایان واقعی سند می بندند. برای بستن چیزها به فراخوانی end() تکیه کنیم.

کد:

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

بنابراین نویسندگان وب مراقب باشند - مگر اینکه بخواهید به عنوان نمونه در یک قطعه کد در قدرت تحمل خطای WebKit ظاهر شوید - HTML خوب بنویسید.






تحلیل CSS

مفاهیم تحلیل را در مقدمه به خاطر دارید؟ خب، برخلاف HTML این CSS یک دستور زبان مستقل از متن (Context-free grammar) است و می‌تواند با استفاده از انواع تحلیلگر هایی که در مقدمه توضیح داده شد، تحلیل شود. در واقع مشخصات CSS، گرامر واژگانی و نحوی CSS را تعریف می کند.

بیایید چند نمونه را ببینیم:

گرامر واژگانی با عبارات منظم برای هر نشانه تعریف می شود:

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

مشخص کننده یا identifier (خلاصه indent) است، مانند نام کلاس. "name" یک شناسه (id) عنصر است (که با "#" معرفی می شود)

گرامر نحو در فرم باکوس نائور توضیح داده شده است.

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 مخفف فضای سفید است). یک مجموعه قوانین شامل آکولاد و داخل آنها یک اعلان یا به صورت اختیاری تعدادی اعلان است که با یک نقطه ویرگول از هم جدا شده اند. "declaration - اعلامیه" و "selector - انتخاب کننده" در تعاریف فرم باکوس نائور زیر تعریف خواهند شد.

تحلیلگر CSS وب کیت

وبکیت از تولید کننده تحلیگر Flex و Bison برای ایجاد تحلیلگر به طور خودکار از فایل‌های دستور زبان CSS استفاده می‌کند. همانطور که از مقدمه تحلیلگر به یاد می‌آورید، Bison یک تحلیلگر کاهش شیفت از پایین به بالا ایجاد می‌کند. فایرفاکس از تحلیلگر بالا به پایین استفاده می‌کند که به صورت دستی نوشته شده است. در هر دو مورد، هر فایل CSS در یک شیء StyleSheet تحلیل می‌شود. هر شی شامل قوانین CSS است. اشیاء قانون CSS حاوی اشیاء انتخابگر و اعلان و سایر اشیاء مربوط به دستور زبان CSS است.

ترتیب پردازش اسکریپت ها و شیوه‌نامه ها

اسکریپت ها

مدل وب همگام (synchronous) است. نویسندگان انتظار دارند اسکریپت‌ها بلافاصله پس از رسیدن تحلیلگر به تگ تحلیل و اجرا شوند. تحلیل سند تا زمانی که اسکریپت اجرا نشود متوقف می‌شود. اگر اسکریپت خارجی است، ابتدا منبع باید از شبکه واکشی شود - این کار به صورت همزمان انجام می‌شود و تجزیه تا زمانی که منبع واکشی شود متوقف می‌شود. این مدل سال‌ها بود و در مشخصات HTML4 و 5 نیز مشخص شده است. نویسندگان می‌توانند ویژگی "defer" را به یک اسکریپت اضافه کنند، در این صورت تجزیه سند متوقف نمی‌شود و پس از تجزیه سند اجرا می‌شود. HTML5 گزینه‌ای را برای علامت‌گذاری اسکریپت به‌عنوان ناهمزمان اضافه می‌کند تا توسط یک رشته دیگر تجزیه و اجرا شود.

تحلیل نظری

هم WebKit و هم Firefox این بهینه‌سازی را انجام می‌دهند. در حین اجرای اسکریپت‌ها، رشته دیگری بقیه سند را تجزیه می‌کند و متوجه می‌شود که چه منابع دیگری باید از شبکه بارگیری شود و آنها را بارگذاری می‌کند. به این ترتیب می‌توان منابع را روی اتصالات موازی بارگذاری کرد و سرعت کلی بهبود یافت. توجه: تجزیه‌کننده حدسی فقط ارجاع به منابع خارجی مانند اسکریپت‌های خارجی، شیوه نامه‌ها و تصاویر را تجزیه می‌کند: درخت DOM را تغییر نمی‌دهد - که به تجزیه‌کننده اصلی واگذار می‌شود.

شیوه‌نامه ها

از طرف دیگر شیوه‌نامه ها (Style sheet) مدل متفاوتی دارند. از لحاظ مفهومی به نظر می‌رسد که از آنجایی که شیوه نامه‌ها درخت DOM را تغییر نمی‌دهند، دلیلی وجود ندارد که منتظر آنها باشیم و تجزیه سند را متوقف کنیم. با این حال، مشکلی وجود دارد که اسکریپت‌ها اطلاعات سبک را در مرحله تجزیه سند می‌خواهند. اگر استایل هنوز بارگذاری و تجزیه نشده باشد، اسکریپت پاسخ‌های اشتباه دریافت می‌کند و ظاهراً این باعث مشکلات زیادی شده است. به نظر می‌رسد یک مورد لبه است اما بسیار رایج است. فایرفاکس همه اسکریپت‌ها را وقتی که یک شیوه نامه وجود دارد که هنوز در حال بارگذاری و تجزیه است، مسدود می‌کند. WebKit تنها زمانی اسکریپت‌ها را مسدود می‌کند که سعی کنند به ویژگی‌های سبک خاصی دسترسی پیدا کنند که ممکن است توسط شیوه نامه‌های بارگیری نشده تحت تأثیر قرار گیرند.


رندر ساخت درخت

در حالی که درخت DOM در حال ساخت است، مرورگر درخت دیگری به نام درخت رندر می‌سازد. این درخت از عناصر بصری به ترتیب نمایش داده می‌شود. این نمایش تصویری سند است. هدف این درخت این است که امکان رنگ آمیزی محتویات به ترتیب صحیح آنها را فراهم کند.

فایرفاکس عناصر موجود در درخت رندر را فریم می نامد. WebKit از اصطلاح renderer یا render object استفاده می کند.

یک رندر می داند چگونه خود و فرزندانش را چیدمان و نقاشی کند.

کلاس 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 غیر بصری در درخت رندر درج نمی‌شوند. یک مثال عنصر "سر" است. همچنین عناصری که مقدار نمایش آنها به "none" اختصاص داده شده است در درخت ظاهر نمی‌شوند (در حالی که عناصر با نمای "پنهان" در درخت ظاهر می‌شوند).

عناصر DOM وجود دارد که با چندین شیء بصری مطابقت دارد. اینها معمولاً عناصری با ساختار پیچیده هستند که با یک مستطیل قابل توصیف نیستند. به عنوان مثال، عنصر "انتخاب" سه رندر دارد: یکی برای ناحیه نمایش، یکی برای کادر لیست کشویی و دیگری برای دکمه. همچنین هنگامی که متن به چند خط شکسته می‌شود زیرا عرض برای یک خط کافی نیست، خطوط جدید به عنوان رندر اضافی اضافه می‌شوند.

نمونه دیگری از رندرهای متعدد، HTML شکسته است. طبق مشخصات CSS، یک عنصر درون خطی باید فقط شامل عناصر بلوک یا فقط عناصر درون خطی باشد. در مورد محتوای مختلط، رندرهای بلوک ناشناس برای بسته بندی عناصر درون خطی ایجاد خواهند شد.

برخی از اشیاء رندر با یک گره DOM مطابقت دارند اما در همان مکان درخت نیستند. شناورها و عناصر کاملاً قرار گرفته خارج از جریان هستند، در قسمت دیگری از درخت قرار می‌گیرند و به قاب واقعی نگاشت می‌شوند. قاب نگهدارنده مکان جایی است که باید می‌بودند.

جریان ساخت درخت

در فایرفاکس، ارائه به عنوان شنونده برای به روز رسانی‌های DOM ثبت می‌شود. ارائه ایجاد فریم را به FrameConstructor واگذار می‌کند و سازنده سبک را حل می‌کند (به محاسبه سبک مراجعه کنید) و یک فریم ایجاد می‌کند.

در WebKit به فرآیند حل استایل و ایجاد رندر «پیوست» می‌گویند. هر گره DOM دارای یک روش "اتصال" است. پیوست سنکرون است، درج گره به درخت DOM، گره جدید را متد «پیوست» می‌نامد.

پردازش تگ‌های html و body منجر به ساخت ریشه درخت رندر می‌شود. شیء رندر ریشه مطابق با چیزی است که مشخصات CSS آن را بلوک حاوی می‌نامد: بالاترین بلوک که شامل تمام بلوک‌های دیگر است. ابعاد آن عبارتند از viewport: پنجره مرورگر ابعاد منطقه را نمایش می‌دهد. فایرفاکس آن را ViewPortFrame و WebKit آن را RenderView می‌نامد. این شی رندر است که سند به آن اشاره می‌کند. بقیه درخت به عنوان یک درج گره‌های DOM ساخته شده است.

مشخصات CSS2 در مدل پردازش را ببینید.

محاسبه استایل

ساخت درخت رندر مستلزم محاسبه خواص بصری هر شی رندر است. این کار با محاسبه ویژگی‌های سبک هر عنصر انجام می‌شود.

این سبک شامل برگه‌های سبک با ریشه‌های مختلف، عناصر سبک درون خطی و ویژگی‌های بصری در HTML است (مانند ویژگی "bgcolor"). این سبک به ویژگی‌های سبک CSS منطبق ترجمه می‌شود.

منشاء شیوه نامه‌ها، برگه‌های سبک پیش فرض مرورگر، شیوه نامه‌های ارائه شده توسط نویسنده صفحه و شیت‌های سبک کاربر هستند - اینها شیوه نامه‌هایی هستند که توسط کاربر مرورگر ارائه می‌شود (مرورگرها به شما امکان می‌دهند سبک‌های مورد علاقه خود را تعریف کنید. به عنوان مثال، در فایرفاکس، این با قرار دادن یک style sheet در پوشه "Firefox Profile" انجام می‌شود).

محاسبه سبک چند مشکل را به همراه دارد:

1. داده‌های سبک یک ساختار بسیار بزرگ است که دارای ویژگی‌های سبک متعدد است، این می‌تواند باعث مشکلات حافظه شود.

2. یافتن قوانین تطبیق برای هر عنصر در صورتی که بهینه نشده باشد می‌تواند باعث مشکلات عملکرد شود. پیمودن کل فهرست قوانین برای هر عنصر برای یافتن موارد منطبق، کار سنگینی است. انتخابگرها می‌توانند ساختار پیچیده‌ای داشته باشند که می‌تواند باعث شود فرآیند تطبیق در مسیری به ظاهر امیدوارکننده شروع شود که بی‌فایده بودن آن ثابت شده است و باید مسیر دیگری را امتحان کرد.

به عنوان مثال - این انتخابگر ترکیبی:

div div div div{ ... }

به این معنی که قوانین در مورد یک <div> که از نسل 3 div است اعمال می‌شود. فرض کنید می‌خواهید بررسی کنید که آیا این قانون برای عنصر <div> داده شده اعمال می‌شود یا خیر. شما مسیر خاصی را برای بررسی به بالای درخت انتخاب می‌کنید. ممکن است لازم باشد درخت گره را به سمت بالا طی کنید تا متوجه شوید که فقط دو div وجود دارد و این قانون اعمال نمی‌شود. سپس باید مسیرهای دیگری را در درخت امتحان کنید.

3. اعمال قوانین شامل قوانین آبشاری کاملاً پیچیده‌ای است که سلسله مراتب قوانین را تعریف می‌کند.

بیایید ببینیم مرورگرها چگونه با این مشکلات روبرو می‌شوند:

به اشتراک گذاری داده های استایل

گره‌های WebKit به اشیاء سبک ارجاع می‌دهند (RenderStyle). این اشیاء را می‌توان در برخی شرایط توسط گره‌ها به اشتراک گذاشت. گره‌ها خواهر و برادر یا پسر عمو هستند و:

1. عناصر باید در یک حالت ماوس باشند (به عنوان مثال، یکی نمی‌تواند در :hover باشد در حالی که دیگری نیست)

2. هیچ یک از عناصر نباید شناسه داشته باشند

3. نام تگ‌ها باید مطابقت داشته باشد

4. ویژگی‌های کلاس باید مطابقت داشته باشند

5. مجموعه ویژگی‌های نقشه برداری شده باید یکسان باشد

6. حالت‌های پیوند باید مطابقت داشته باشند

7. حالات تمرکز باید مطابقت داشته باشند

8. هیچ یک از عناصر نباید تحت تأثیر انتخابگرهای مشخصه قرار گیرند، جایی که تأثیر به این صورت تعریف می‌شود که منطبق بر انتخابگر باشد که از یک انتخابگر ویژگی در هر موقعیتی در انتخابگر استفاده می‌کند.

9. نباید هیچ ویژگی سبک درون خطی روی عناصر وجود داشته باشد

10. اصلا نباید انتخابگر خواهر و برادری در حال استفاده باشد. WebCore به سادگی یک سوئیچ سراسری را زمانی که انتخابگر خواهر و برادری با آن مواجه می‌شود پرتاب می‌کند و اشتراک‌گذاری سبک را برای کل سند در زمانی که آن‌ها حضور دارند غیرفعال می‌کند. این شامل انتخابگر + و انتخابگرهایی مانند :first-child و :last-child است.

درخت قانون فایرفاکس

فایرفاکس دارای دو درخت اضافی برای محاسبه سبک آسان‌تر است: درخت قانون و درخت زمینه سبک. WebKit همچنین دارای اشیاء سبک است اما آنها در درختی مانند درخت زمینه سبک ذخیره نمی‌شوند، فقط گره DOM به سبک مربوطه خود اشاره می‌کند.

زمینه‌های سبک حاوی مقادیر پایانی هستند. مقادیر با اعمال تمام قوانین تطبیق به ترتیب صحیح و انجام دستکاری‌هایی که آنها را از مقادیر منطقی به مقادیر مشخص تبدیل می‌کند، محاسبه می‌شوند. به عنوان مثال، اگر مقدار منطقی درصدی از صفحه باشد، محاسبه شده و به واحدهای مطلق تبدیل می‌شود. ایده درخت قانون واقعا هوشمندانه است. به اشتراک گذاری این مقادیر بین گره‌ها برای جلوگیری از محاسبه مجدد آنها را امکان‌پذیر می‌کند. این همچنین باعث صرفه جویی در فضا می‌شود.

همه قوانین مطابق در یک درخت ذخیره می‌شوند. گره‌های پایین در یک مسیر دارای اولویت بیشتری هستند. درخت شامل تمام مسیرهای منطبق قوانین یافت شده است. ذخیره قوانین با تنبلی انجام می‌شود. درخت در ابتدا برای هر گره محاسبه نمی‌شود، اما هر زمان که یک سبک گره باید محاسبه شود، مسیرهای محاسبه شده به درخت اضافه می‌شوند.

ایده این است که مسیرهای درخت را به عنوان کلمات در یک فرهنگ لغت ببینیم. بیایید بگوییم که قبلاً این درخت قانون را محاسبه کرده‌ایم:

فرض کنید باید قوانین را برای عنصر دیگری در درخت محتوا مطابقت دهیم و متوجه شویم که قوانین مطابق (به ترتیب صحیح) B-E-I هستند. ما قبلاً این مسیر را در درخت داریم زیرا قبلاً مسیر A-B-E-I-L را محاسبه کرده‌ایم. اکنون کار کمتری برای انجام دادن خواهیم داشت.

بیایید ببینیم درخت چگونه کار ما را نجات می‌دهد.

تقسیم به ساختارها

زمینه‌های سبک به ساختارها تقسیم می‌شوند. این ساختارها حاوی اطلاعات سبک برای یک دسته خاص مانند حاشیه یا رنگ هستند. تمام ویژگی‌های یک ساختار یا ارثی هستند یا غیر ارثی. ویژگی‌های ارثی ویژگی‌هایی هستند که مگر اینکه توسط عنصر تعریف شوند، از والد آن به ارث می‌رسند. ویژگی‌های غیر ارثی (که ویژگی‌های «تنظیم مجدد» نامیده می‌شوند) اگر تعریف نشده باشند، از مقادیر پیش‌فرض استفاده می‌کنند.

درخت با ذخیره‌سازی کل ساختارها (شامل مقادیر پایانی محاسبه شده) در درخت به ما کمک می‌کند. ایده این است که اگر گره پایینی تعریفی برای یک ساختار ارائه نمی‌کند، می‌توان از یک ساختار ذخیره‌شده در یک گره بالایی استفاده کرد.

محاسبه زمینه های سبک با استفاده از درخت قانون

هنگام محاسبه زمینه سبک برای یک عنصر خاص، ابتدا یک مسیر را در درخت قانون محاسبه می‌کنیم یا از یک مسیر موجود استفاده می‌کنیم. سپس شروع به اعمال قوانین در مسیر برای پر کردن ساختارها در زمینه سبک جدید خود می‌کنیم. ما از گره پایین مسیر شروع می‌کنیم - یکی با بالاترین اولویت (معمولاً خاص‌ترین انتخابگر) و درخت را تا زمانی که ساختار ما پر شود، طی می‌کنیم. اگر هیچ مشخصاتی برای ساختار در آن گره قانون وجود نداشته باشد، می‌توانیم تا حد زیادی بهینه‌سازی کنیم - از درخت بالا می‌رویم تا زمانی که گره‌ای را پیدا کنیم که آن را کاملاً مشخص کند و به سادگی به آن اشاره کنیم - این بهترین بهینه‌سازی است - کل ساختار به اشتراک گذاشته می‌شود. این باعث صرفه جویی در محاسبه مقادیر نهایی و حافظه می‌شود.

اگر تعاریف جزئی پیدا کنیم از درخت بالا می‌رویم تا ساختار پر شود.

اگر هیچ تعریفی برای ساختار خود پیدا نکردیم، در صورتی که ساختار یک نوع "ارثی" باشد، به ساختار والد خود در درخت زمینه اشاره می‌کنیم. در این مورد ما همچنین موفق به اشتراک گذاری ساختارها شدیم. اگر ساختار بازنشانی باشد، از مقادیر پیش‌فرض استفاده می‌شود.

اگر خاص‌ترین گره مقادیری را اضافه کند، باید محاسبات اضافی برای تبدیل آن به مقادیر واقعی انجام دهیم. سپس نتیجه را در گره درخت ذخیره می‌کنیم تا بتوان از آن برای کودکان استفاده کرد.

اگر عنصری خواهر یا برادر یا برادری داشته باشد که به همان گره درخت اشاره می‌کند، می‌توان کل زمینه سبک را بین آنها به اشتراک گذاشت.

بیایید یک مثال را ببینیم: فرض کنید ما این HTML را داریم

<html> <body> <div class=&quoterr&quot id=&quotdiv1&quot> <p> this is a <span class=&quotbig&quot> big error </span> this is also a <span class=&quotbig&quot> very big error</span> error </p> </div> <div class=&quoterr&quot id=&quotdiv2&quot>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}

برای ساده کردن کارها، فرض کنید باید فقط دو ساختار را پر کنیم: ساختار رنگ و ساختار حاشیه. ساختار رنگ فقط یک عضو دارد: رنگ ساختار حاشیه شامل چهار طرف است.

درخت قانون حاصل به این شکل خواهد بود (گره‌ها با نام گره مشخص می‌شوند: تعداد قاعده‌ای که روی آن اشاره می‌کنند):

درخت زمینه به این شکل خواهد بود (نام گره: گره قانون که به آن اشاره می کنند):

فرض کنید HTML را تجزیه می‌کنیم و به تگ دوم <div> می‌رسیم. ما باید یک زمینه سبک برای این گره ایجاد کنیم و ساختارهای سبک آن را پر کنیم.

ما قوانین را مطابقت می‌دهیم و متوجه می‌شویم که قوانین تطبیق برای <div> 1، 2 و 6 هستند. این بدان معنی است که یک مسیر موجود در درخت وجود دارد که عنصر ما می‌تواند از آن استفاده کند و فقط باید گره دیگری را به آن اضافه کنیم. قانون 6 (گره F در درخت قانون).

ما یک زمینه سبک ایجاد می‌کنیم و آن را در درخت زمینه قرار می‌دهیم. زمینه سبک جدید به گره F در درخت قانون اشاره می‌کند.

اکنون باید ساختارهای سبک را پر کنیم. ما با پر کردن ساختار حاشیه شروع خواهیم کرد. از آنجایی که آخرین گره قانون (F) به ساختار حاشیه اضافه نمی‌شود، می‌توانیم به بالای درخت برویم تا زمانی که یک ساختار ذخیره‌سازی شده محاسبه‌شده در درج گره قبلی را پیدا کنیم و از آن استفاده کنیم. ما آن را در گره B خواهیم یافت، که بالاترین گره‌ای است که قوانین حاشیه را مشخص می‌کند.

ما برای ساختار رنگی تعریفی داریم، بنابراین نمی‌توانیم از ساختار کش استفاده کنیم. از آنجایی که رنگ یک ویژگی دارد، لازم نیست برای پر کردن سایر ویژگی‌ها از درخت بالا برویم. ما مقدار پایانی را محاسبه می‌کنیم (رشته را به RGB و غیره تبدیل می‌کنیم) و ساختار محاسبه شده را در این گره ذخیره می‌کنیم.

کار بر روی عنصر دوم <span> حتی ساده‌تر است. قوانین را مطابقت می‌دهیم و به این نتیجه می‌رسیم که مانند دهانه قبلی به قانون G اشاره می‌کند. از آنجایی که ما خواهر و برادرهایی داریم که به یک گره اشاره می‌کنند، می‌توانیم کل زمینه سبک را به اشتراک بگذاریم و فقط به بافت دهانه قبلی اشاره کنیم.

برای ساختارهایی که حاوی قوانینی هستند که از والد به ارث برده شده‌اند، کش روی درخت زمینه انجام می‌شود (ویژگی رنگ در واقع به ارث می‌رسد، اما فایرفاکس آن را به عنوان تنظیم مجدد در نظر می‌گیرد و آن را در درخت قانون ذخیره می‌کند).

به عنوان مثال اگر قوانینی را برای فونت‌ها در یک پاراگراف اضافه کنیم:

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


سپس عنصر پاراگراف، که فرزندی از div در درخت زمینه است، می‌توانست همان ساختار فونت را با پدرش به اشتراک بگذارد. این در صورتی است که هیچ قانون فونتی برای پاراگراف مشخص نشده باشد.

در WebKit که درخت قانون ندارد، اعلان‌های همسان چهار بار پیمایش می‌شوند. ابتدا ویژگی‌های غیر مهم با اولویت بالا اعمال می‌شوند (خواصی که ابتدا باید اعمال شوند زیرا سایرین به آنها وابسته هستند، مانند نمایش)، سپس اولویت بالا مهم، سپس اولویت عادی غیر مهم، سپس قوانین مهم اولویت عادی. این به این معنی است که ویژگی‌هایی که چندین بار ظاهر می‌شوند مطابق ترتیب آبشاری صحیح حل می‌شوند. آخرین برنده است.

بنابراین به طور خلاصه: اشتراک گذاری اشیاء سبک (به طور کامل یا برخی از ساختارهای داخل آنها) مسائل 1 و 3 را حل می‌کند. درخت قانون فایرفاکس همچنین به اعمال خواص به ترتیب صحیح کمک می‌کند.

دستکاری قوانین برای یک مسابقه آسان

چندین منبع برای قوانین سبک وجود دارد:

1. قوانین CSS، چه در شیوه نامه های خارجی یا در عناصر سبک.

p {color: blue}

2. ویژگی های سبک درون خطی مانند

<p style=&quotcolor: blue&quot />

3. ویژگی های بصری HTML (که به قوانین سبک مربوطه نگاشت می شوند)

<p bgcolor=&quotblue&quot />

دو مورد آخر به راحتی با عنصر تطبیق داده می‌شوند زیرا او دارای ویژگی‌های سبک است و ویژگی‌های HTML را می‌توان با استفاده از عنصر به عنوان کلید نگاشت کرد.

همانطور که قبلا در شماره 2 ذکر شد، تطبیق قوانین CSS می‌تواند پیچیده‌تر باشد. برای حل مشکل، قوانین برای دسترسی آسان‌تر دستکاری می‌شوند.

پس از تجزیه صفحه سبک، طبق انتخابگر، قوانین به یکی از چندین نقشه هش اضافه می‌شوند. نقشه‌هایی بر اساس شناسه، نام کلاس، بر اساس نام تگ و یک نقشه کلی برای هر چیزی که در آن دسته بندی‌ها قرار نمی‌گیرد وجود دارد. اگر انتخابگر یک id باشد، قانون به نقشه شناسه اضافه می‌شود، اگر کلاس باشد به نقشه کلاس و غیره اضافه می‌شود.

این دستکاری تطبیق قوانین را بسیار آسان‌تر می‌کند. نیازی به جستجو در هر اعلان نیست: ما می‌توانیم قوانین مربوط به یک عنصر را از نقشه‌ها استخراج کنیم. این بهینه‌سازی 95+٪ از قوانین را حذف می‌کند، به طوری که حتی نیازی به در نظر گرفتن آنها در طول فرآیند تطبیق (4.1) نیست.

بیایید برای مثال قوانین سبک زیر را ببینیم:

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

اولین قانون در نقشه کلاس درج می شود. دومی در نقشه شناسه و سومی در نقشه تگ.

برای قطعه HTML زیر:

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

ابتدا سعی می‌کنیم قوانینی را برای عنصر p پیدا کنیم. نقشه کلاس حاوی یک کلید "error" است که تحت آن قانون "p.error" پیدا می‌شود. عنصر div قوانین مربوطه را در نقشه شناسه (کلید id است) و نقشه برچسب خواهد داشت. بنابراین تنها کار باقی مانده این است که بفهمیم کدام یک از قوانین استخراج شده توسط کلیدها واقعاً مطابقت دارند.

به عنوان مثال اگر قانون برای div بود

table div {margin: 5px}

همچنان از نقشه برچسب استخراج می‌شود، زیرا کلید سمت راست‌ترین انتخابگر است، اما با عنصر div ما که جد جدولی ندارد مطابقت ندارد.

هم WebKit و هم Firefox این دستکاری را انجام می‌دهند.

اعمال قوانین به ترتیب آبشار صحیح

شی استایل دارای ویژگی‌های مربوط به هر ویژگی بصری است (همه ویژگی‌های CSS اما عمومی تر). اگر خاصیت با هیچ یک از قوانین منطبق تعریف نشده باشد، برخی از ویژگی‌ها را می‌توان توسط شیء سبک عنصر والد به ارث برد. سایر ویژگی‌ها دارای مقادیر پیش فرض هستند.

مشکل زمانی شروع می‌شود که بیش از یک تعریف وجود داشته باشد - در اینجا دستور آبشار برای حل مسئله می‌آید.

سفارش آبشاری برگه سبک

یک اعلان برای یک ویژگی سبک می‌تواند در چندین شیوه نامه و چندین بار در یک شیوه نامه ظاهر شود. این بدان معناست که ترتیب اعمال قوانین بسیار مهم است. به این ترتیب "آبشار" می‌گویند. طبق مشخصات CSS2، ترتیب آبشار (از کم به زیاد) است:

1. اعلامیه‌های مرورگر

2. اظهارنامه‌های عادی کاربر

3. اظهارنامه‌های عادی نویسنده

4. نویسنده اظهارات مهم

5. اعلامیه‌های مهم کاربر

اعلان‌های مرورگر کمترین اهمیت را دارند و کاربر تنها در صورتی از نویسنده رد می‌کند که اعلان به عنوان مهم علامت گذاری شده باشد. اعلان‌های با همان ترتیب بر اساس ویژگی مرتب‌سازی می‌شوند و سپس به ترتیبی که مشخص می‌شوند. ویژگی‌های بصری HTML به اعلان‌های CSS منطبق ترجمه می‌شوند. آنها به عنوان قوانین نویسنده با اولویت پایین در نظر گرفته می‌شوند.

اختصاصی

ویژگی انتخابگر توسط مشخصات CSS2 به صورت زیر تعریف می‌شود:

1. اگر اعلانی که از آن گرفته شده است به جای یک قانون با انتخابگر، یک ویژگی «style» است، 0 در غیر این صورت (= a)

2. شمارش تعداد ویژگی‌های ID در انتخابگر (= b)

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=&quot&quot /* 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) بارگیری شده‌اند یا خیر. اگر استایل هنگام اتصال به طور کامل بارگذاری نشده باشد، از نگهدارنده‌های مکان استفاده می‌شود و در سند مشخص می‌شود و پس از بارگیری شیوه نامه‌ها مجدداً محاسبه می‌شوند.

چیدمان

وقتی رندر ایجاد می‌شود و به درخت اضافه می‌شود، موقعیت و اندازه ندارد. محاسبه این مقادیر را layout یا reflow می‌نامند.

HTML از یک مدل طرح‌بندی مبتنی بر جریان استفاده می‌کند، به این معنی که در بیشتر مواقع می‌توان هندسه را در یک پاس محاسبه کرد. عناصر بعدی "در جریان" معمولاً بر هندسه عناصری که زودتر "در جریان هستند" تأثیر نمی‌گذارند، بنابراین طرح‌بندی می‌تواند از چپ به راست، از بالا به پایین در سند ادامه یابد. استثنائاتی وجود دارد: برای مثال، جداول HTML ممکن است به بیش از یک پاس نیاز داشته باشند.

سیستم مختصات نسبت به قاب ریشه است. مختصات بالا و چپ استفاده می‌شود.

Layout یک فرآیند بازگشتی است. از رندر اصلی شروع می‌شود که با عنصر <html> سند HTML مطابقت دارد. Layout به صورت بازگشتی از طریق برخی یا تمام سلسله مراتب فریم ادامه می‌یابد و اطلاعات هندسی را برای هر رندری که به آن نیاز دارد محاسبه می‌کند.

موقعیت رندر ریشه 0،0 و ابعاد آن نمای درگاه است - قسمت قابل مشاهده پنجره مرورگر.

همه رندرها یک روش "layout" یا "reflow" دارند، هر رندر متد طرح بندی فرزندان خود را که نیاز به چیدمان دارند فراخوانی می‌کند.

سیستم بیت کثیف

برای اینکه برای هر تغییر کوچک یک طرح بندی کامل انجام نشود، مرورگرها از یک سیستم "کثیف بیت" استفاده می‌کنند. رندری که تغییر یا اضافه شده است، خود و فرزندانش را به عنوان "کثیف" علامت گذاری می‌کند: نیاز به طرح بندی.

دو پرچم وجود دارد: "کثیف"، و "کودکان کثیف هستند"، به این معنی که اگرچه خود رندر ممکن است خوب باشد، اما حداقل یک فرزند دارد که به طرح بندی نیاز دارد.

طرح کلی و افزایشی

Layout می‌تواند در کل درخت رندر فعال شود - این طرح "جهانی" است. این می‌تواند در نتیجه اتفاق بیفتد:

1. یک تغییر سبک جهانی که بر همه رندرها تأثیر می‌گذارد، مانند تغییر اندازه فونت.

2. در نتیجه تغییر اندازه صفحه نمایش

چیدمان می‌تواند افزایشی باشد، فقط رندرهای کثیف چیده می‌شوند (این می‌تواند باعث آسیب‌هایی شود که به چیدمان‌های اضافی نیاز دارد).

وقتی رندر کثیف باشد، طرح‌بندی افزایشی (ناهمزمان) فعال می‌شود. به عنوان مثال زمانی که رندرهای جدید به درخت رندر اضافه می‌شوند، پس از اینکه محتوای اضافی از شبکه آمد و به درخت DOM اضافه شد.

طرح ناهمزمان و همگام

چیدمان افزایشی به صورت ناهمزمان انجام می‌شود. فایرفاکس «فرمان‌های جریان مجدد» را برای طرح‌بندی‌های افزایشی در صف قرار می‌دهد و یک زمان‌بندی اجرای دسته‌ای این دستورات را آغاز می‌کند. WebKit همچنین دارای یک تایمر است که یک طرح بندی افزایشی را اجرا می‌کند - درخت عبور می‌کند و رندرهای "کثیف" طرح بندی می‌شوند.

اسکریپت‌هایی که اطلاعات سبک را می‌خواهند، مانند "offsetHeight" می‌توانند طرح بندی افزایشی را به طور همزمان فعال کنند.

طرح کلی معمولاً به صورت همزمان فعال می‌شود.

گاهی اوقات طرح به عنوان یک تماس پس از طرح اولیه فعال می‌شود زیرا برخی از ویژگی‌ها مانند موقعیت پیمایش تغییر کرده است.

بهینه سازی‌ها

هنگامی که یک طرح با یک "تغییر اندازه" یا تغییر در موقعیت رندر (و نه اندازه) راه اندازی می‌شود، اندازه‌های رندر از حافظه پنهان گرفته می‌شوند و دوباره محاسبه نمی‌شوند…

در برخی موارد فقط یک درخت فرعی اصلاح می‌شود و طرح از ریشه شروع نمی‌شود. این می‌تواند در مواردی اتفاق بیفتد که تغییر محلی است و محیط اطراف خود را تحت تأثیر قرار نمی‌دهد - مانند متن درج شده در فیلدهای متنی (در غیر این صورت هر ضربه کلید باعث ایجاد طرحی می‌شود که از ریشه شروع می‌شود).

روند چیدمان

طرح معمولاً دارای الگوی زیر است:

1. رندر والد عرض خود را تعیین می‌کند.

2. والدین به سراغ فرزندان می‌روند و:

--1. رندر فرزند را قرار دهید (x و y آن را تنظیم می‌کند).

--2. در صورت نیاز طرح فرزند را صدا می‌کند - آنها کثیف هستند یا ما در یک چیدمان جهانی هستیم یا به دلایل دیگر - که قد کودک را محاسبه می‌کند.

3. والدین از ارتفاع‌های انباشته کودکان و ارتفاع حاشیه‌ها و بالشتک‌ها برای تنظیم ارتفاع خود استفاده می‌کنند - این مورد توسط والدین رندر والد استفاده خواهد شد.

4. بیت کثیف خود را روی false تنظیم می‌کند.

فایرفاکس از یک شی "state" (nsHTMLReflowState) به عنوان پارامتری برای چیدمان استفاده می‌کند (که "reflow" نامیده می‌شود). در میان سایر موارد، این ایالت شامل عرض والدین نیز می‌شود.

خروجی طرح فایرفاکس یک شی "متریک" (nsHTMLReflowMetrics) است. این شامل ارتفاع محاسبه شده رندر خواهد بود.

محاسبه عرض

عرض رندر با استفاده از عرض بلوک کانتینر، ویژگی "width" سبک رندر، حاشیه‌ها و حاشیه‌ها محاسبه می‌شود.

به عنوان مثال عرض div زیر:

<div style=&quotwidth: 30%&quot/>

توسط WebKit به صورت زیر محاسبه می‌شود (روش کلاس RenderBox calcWidth):

عرض کانتینر حداکثر کانتینرهای موجودWidth و 0 است. در این مورد، عرض موجود در این حالت، ContentWidth است که به صورت زیر محاسبه می‌شود:

clientWidth() - paddingLeft() - paddingRight()

clientWidth و clientHeight نمایانگر فضای داخلی یک شی به استثنای حاشیه و نوار اسکرول است.

- عرض عناصر ویژگی سبک "width" است. با محاسبه درصد عرض ظرف به عنوان یک مقدار مطلق محاسبه می‌شود.

- حاشیه‌های افقی و بالشتک‌ها اکنون اضافه شده‌اند.

تا اینجا این محاسبه "عرض ترجیحی" بود. اکنون حداقل و حداکثر عرض محاسبه خواهد شد.

اگر عرض ترجیحی بیشتر از حداکثر عرض باشد، از حداکثر عرض استفاده می‌شود. اگر کمتر از حداقل عرض باشد (کوچکترین واحد نشکن) از حداقل عرض استفاده می‌شود.

در صورت نیاز به طرح بندی، مقادیر در حافظه پنهان ذخیره می‌شوند، اما عرض تغییر نمی‌کند.

خط شکستن

هنگامی که یک رندر در وسط یک چیدمان تصمیم می‌گیرد که باید شکسته شود، رندر متوقف می‌شود و به والد چیدمان می‌گوید که باید شکسته شود. والد رندرهای اضافی را ایجاد می‌کند و طرح بندی را روی آنها فراخوانی می‌کند.

رنگ آمیزی

در مرحله نقاشی، درخت رندر پیمایش می‌شود و متد "paint()" رندر برای نمایش محتوا در صفحه فراخوانی می‌شود. نقاشی از مؤلفه زیرساخت رابط کاربری استفاده می‌کند.

جهانی و افزایشی

مانند چیدمان، نقاشی نیز می‌تواند جهانی باشد - کل درخت رنگ شده است - یا افزایشی. در نقاشی افزایشی، برخی از رندرها به گونه‌ای تغییر می‌کنند که بر کل درخت تأثیر نمی‌گذارد. رندر تغییر یافته مستطیل آن را روی صفحه باطل می‌کند. این باعث می‌شود که سیستم عامل آن را به عنوان یک "منطقه کثیف" ببیند و یک رویداد "رنگ" ایجاد کند. سیستم عامل این کار را هوشمندانه انجام می‌دهد و چندین منطقه را در یک منطقه ادغام می‌کند. در کروم پیچیده‌تر است زیرا رندر در فرآیندی متفاوت از فرآیند اصلی است. کروم تا حدودی رفتار سیستم عامل را شبیه‌سازی می‌کند. ارائه به این رویدادها گوش می‌دهد و پیام را به ریشه رندر می‌دهد. درخت تا رسیدن به رندر مربوطه طی می‌شود. خودش (و معمولاً فرزندانش) را دوباره رنگ می‌کند.

سفارش نقاشی

CSS2 ترتیب فرآیند نقاشی را مشخص می‌کند. این در واقع ترتیبی است که عناصر در زمینه‌های انباشته روی هم چیده می‌شوند. این ترتیب روی نقاشی تأثیر می‌گذارد زیرا پشته‌ها از پشت به جلو رنگ می‌شوند. ترتیب انباشته شدن یک رندر بلوک به صورت زیر است:

1. رنگ پس زمینه

2. تصویر پس زمینه

3. حاشیه

4. فرزندان

5. طرح کلی

لیست نمایش فایرفاکس

فایرفاکس روی درخت رندر می‌رود و یک لیست نمایشی برای مستطیل رنگ شده می‌سازد. این شامل رندرهای مربوط به مستطیل، به ترتیب نقاشی درست (پس زمینه رندرها، سپس حاشیه‌ها و غیره) است.

به این ترتیب درخت باید فقط یک بار برای رنگ آمیزی مجدد به جای چندین بار پیمایش شود - تمام پس زمینه‌ها، سپس همه تصاویر، سپس تمام حاشیه‌ها و غیره.

فایرفاکس فرآیند را با اضافه نکردن عناصری که پنهان می‌شوند، بهینه می‌کند، مانند عناصری که کاملاً در زیر عناصر غیر شفاف دیگر قرار دارند.

ذخیره‌سازی مستطیلی WebKit

قبل از رنگ آمیزی مجدد، WebKit مستطیل قدیمی را به عنوان یک بیت مپ ذخیره می‌کند. سپس فقط دلتای بین مستطیل جدید و قدیمی را رنگ می‌کند.

تغییرات دینامیک

مرورگرها سعی می‌کنند حداقل اقدامات ممکن را در پاسخ به یک تغییر انجام دهند. بنابراین تغییر در رنگ یک عنصر فقط باعث رنگ آمیزی مجدد عنصر می‌شود. تغییر در موقعیت عنصر باعث چیدمان و رنگ آمیزی مجدد عنصر، فرزندان آن و احتمالاً خواهر و برادر می‌شود. افزودن یک گره DOM باعث طرح بندی و رنگ آمیزی مجدد گره می‌شود. تغییرات عمده، مانند افزایش اندازه قلم عنصر "html"، باعث بی‌اعتباری حافظه پنهان، تغییر شکل و رنگ آمیزی مجدد کل درخت می‌شود.

نخ‌های موتور رندر

موتور رندر تک رشته است. تقریباً همه چیز، به جز عملیات شبکه، در یک رشته اتفاق می‌افتد. در فایرفاکس و سافاری این موضوع اصلی مرورگر است. در کروم، موضوع اصلی پردازش برگه است.

عملیات شبکه می‌تواند توسط چندین رشته موازی انجام شود. تعداد اتصالات موازی محدود است (معمولاً 2 - 6 اتصال).

حلقه رویداد

موضوع اصلی مرورگر یک حلقه رویداد است. این یک حلقه بی‌نهایت است که روند را زنده نگه می‌دارد. منتظر رویدادها (مانند رویدادهای چیدمان و نقاشی) می‌ماند و آنها را پردازش می‌کند. این کد فایرفاکس برای حلقه رویداد اصلی است:

while (!mExiting) NS_ProcessNextEvent(thread);

مدل تصویری CSS2

بوم

با توجه به مشخصات CSS2، اصطلاح canvas «فضایی که ساختار قالب‌بندی ارائه می‌شود» را توصیف می‌کند: جایی که مرورگر محتوا را نقاشی می‌کند.

بوم برای هر بعد از فضا بی‌نهایت است اما مرورگرها یک عرض اولیه را بر اساس ابعاد درگاه دید انتخاب می‌کنند.

با توجه به www.w3.org/TR/CSS2/zindex.html، بوم اگر در بوم دیگری باشد شفاف است، و اگر این بوم نباشد، رنگ تعریف شده از مرورگر به آن داده می‌شود.

مدل CSS Box

مدل جعبه CSS جعبه‌های مستطیلی را که برای عناصر در درخت سند ایجاد می‌شوند و مطابق با مدل قالب‌بندی بصری چیده می‌شوند، توصیف می‌کند.

هر کادر دارای یک ناحیه محتوا (مثلاً متن، یک تصویر و غیره) و قسمت‌های انتخابی، حاشیه و حاشیه است.

هر گره 0…n چنین جعبه تولید می کند.

همه عناصر دارای ویژگی "نمایش" هستند که نوع جعبه ای را که تولید می شود تعیین می کند.

مثال ها:

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" تنظیم می شود.

  • static and relative cause a normal flow
  • absolute and fixed cause absolute positioning


در موقعیت یابی استاتیک هیچ موقعیتی تعریف نمی شود و از موقعیت یابی پیش فرض استفاده می شود. در طرح های دیگر، نویسنده موقعیت را مشخص می کند: بالا، پایین، چپ، راست.

نحوه چیدمان جعبه با موارد زیر تعیین می شود:

  • نوع جعبه
  • ابعاد جعبه
  • طرح موقعیت یابی
  • اطلاعات خارجی مانند اندازه تصویر و اندازه صفحه نمایش

انواع جعبه

جعبه بلوک: یک بلوک را تشکیل می دهد - مستطیل خود را در پنجره مرورگر دارد.

جعبه درون خطی: بلوک خود را ندارد، اما درون یک بلوک حاوی است.

بلوک ها به صورت عمودی یکی پس از دیگری قالب بندی می شوند. خطوط درونی به صورت افقی قالب بندی می شوند.

جعبه های درون خطی در داخل خطوط یا "جعبه های خط" قرار می گیرند. خطوط حداقل به بلندی بلندترین جعبه هستند، اما می توانند بلندتر باشند، زمانی که کادرها "خط پایه" تراز شوند - به این معنی که قسمت پایینی یک عنصر در نقطه ای از کادر دیگری غیر از پایین تراز شده است. اگر عرض ظرف کافی نباشد، خطوط درونی روی چندین خط قرار می گیرند. معمولاً در یک پاراگراف این اتفاق می افتد.

تثبیت موقعیت

نسبت

موقعیت یابی نسبی - مانند معمول قرار گرفته و سپس توسط دلتای مورد نیاز جابجا می شود.

شناورها

یک جعبه شناور به سمت چپ یا راست یک خط منتقل می شود. ویژگی جالب این است که جعبه های دیگر در اطراف آن جریان دارند. HTML:

<p> <img style=&quotfloat: right&quot src=&quotimages/image.gif&quot width=&quot100&quot height=&quot100&quot> Lorem ipsum dolor sit amet, consectetuer... </p>

به نظر خواهد رسید:

مطلق و ثابت #
مطلق و ثابت #

مطلق و ثابت

طرح دقیقاً بدون توجه به جریان عادی تعریف می شود. عنصر در جریان عادی شرکت نمی کند. ابعاد نسبت به ظرف است. در حالت ثابت، کانتینر درگاه دید است.

کادر ثابت حتی زمانی که سند اسکرول می شود حرکت نمی کند!

نمایش لایه ای

این توسط ویژگی z-index CSS مشخص می شود. نشان دهنده بعد سوم جعبه است: موقعیت آن در امتداد "محور z".

جعبه ها به پشته ها تقسیم می شوند (که زمینه های انباشتگی نامیده می شود). در هر پشته ابتدا عناصر پشتی و عناصر جلو در بالا، نزدیک‌تر به کاربر نقاشی می‌شوند. در صورت همپوشانی، عنصر اصلی، عنصر قبلی را پنهان می کند.

پشته ها بر اساس ویژگی z-index مرتب شده اند. جعبه هایی با ویژگی "z-index" یک پشته محلی را تشکیل می دهند. درگاه دید دارای پشته بیرونی است.

مثال:

<style type=&quottext/css&quot> div { position: absolute; left: 2in; top: 2in; } </style> <p> <div style=&quotz-index: 3;background-color:red; width: 1in; height: 1in; &quot> </div> <div style=&quotz-index: 1;background-color:green;width: 2in; height: 2in;&quot> </div> </p>

نتیجه این خواهد بود:

اگر چه div قرمز قبل از رنگ سبز در نشانه گذاری است، و قبلاً در جریان معمولی رنگ می شد، ویژگی z-index بالاتر است، بنابراین در پشته ای که توسط جعبه ریشه نگهداری می شود جلوتر است.







آموزشمرورگرتخصصیاینترنتدستور زبان
طراح و توسعه دهنده وب https://arefn.ir
شاید از این پست‌ها خوشتان بیاید