เบราว์เซอร์ทำงานอย่างไร

เบื้องหลังของเว็บเบราว์เซอร์สมัยใหม่

บทนำ

คำแนะนำที่ครอบคลุมเกี่ยวกับการดำเนินการภายในของ WebKit และ Gecko คือผลการวิจัยจำนวนมากของ Tali Garsiel นักพัฒนาซอฟต์แวร์ชาวอิสราเอล ในช่วง 2-3 ปี เธอตรวจสอบข้อมูลที่เผยแพร่ทั้งหมดเกี่ยวกับภายในของเบราว์เซอร์และใช้เวลามากไปกับการอ่านซอร์สโค้ดของเว็บเบราว์เซอร์ เธอเขียนว่า:

ในฐานะนักพัฒนาเว็บ การเรียนรู้กลไกภายในของเบราว์เซอร์จะช่วยให้คุณตัดสินใจได้ดีขึ้นและทราบถึงเหตุผลเบื้องหลังแนวทางปฏิบัติแนะนำในการพัฒนา แม้ว่าเอกสารนี้จะค่อนข้างยาว แต่เราขอแนะนำให้คุณใช้เวลาทำความเข้าใจอย่างละเอียด แล้วคุณจะชอบ

Paul Ireland ฝ่ายนักพัฒนาซอฟต์แวร์ Chrome สัมพันธ์

เกริ่นนำ

เว็บเบราว์เซอร์เป็นซอฟต์แวร์ที่ใช้กันอย่างแพร่หลายมากที่สุด ในเกริ่นนำนี้ ฉันจะอธิบาย วิธีการทำงานเบื้องหลัง เราจะเห็นสิ่งที่เกิดขึ้นเมื่อคุณพิมพ์ google.com ในแถบที่อยู่จนกว่าคุณจะเห็นหน้า Google บนหน้าจอเบราว์เซอร์

เบราว์เซอร์ที่เราจะพูดถึง

ปัจจุบันมีเบราว์เซอร์หลัก 5 เบราว์เซอร์ที่ใช้บนเดสก์ท็อป ได้แก่ 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 (Uniform Resource Identifier)

วิธีที่เบราว์เซอร์แปลความหมายและแสดงไฟล์ HTML ระบุไว้ในข้อกำหนด HTML และ CSS ข้อกำหนดเหล่านี้ดูแลโดยองค์กร W3C (World Wide Web Consortium) ซึ่งเป็นองค์กรมาตรฐานสำหรับเว็บ หลายปีที่ผ่านมา เบราว์เซอร์ที่สอดคล้องกับข้อกำหนดเฉพาะบางส่วนและได้พัฒนาส่วนขยายของตัวเองขึ้นมา ซึ่งก่อให้เกิดปัญหาเรื่องความเข้ากันได้ที่ร้ายแรงสำหรับผู้เขียนในเว็บ ปัจจุบัน เบราว์เซอร์ส่วนใหญ่สอดคล้องกับข้อกำหนดเฉพาะมากกว่าหรือน้อยลง

อินเทอร์เฟซผู้ใช้ของเบราว์เซอร์มีอะไรที่เหมือนกันมากมาย องค์ประกอบทั่วไปของอินเทอร์เฟซผู้ใช้มีดังนี้

  1. แถบที่อยู่สำหรับแทรก URI
  2. ปุ่มย้อนกลับและไปข้างหน้า
  3. ตัวเลือกการบุ๊กมาร์ก
  4. ปุ่มรีเฟรชและหยุดสำหรับรีเฟรชหรือหยุดการโหลดเอกสารปัจจุบัน
  5. ปุ่มหน้าแรกที่จะนำคุณไปยังหน้าแรก

สิ่งที่แปลกประหลาดก็คือ อินเทอร์เฟซผู้ใช้ของเบราว์เซอร์ไม่ได้ระบุไว้ในข้อกำหนดอย่างเป็นทางการใดๆ แต่มาจากแนวทางปฏิบัติที่ดีที่เกิดขึ้นในหลายๆ ปีที่ผ่านมา และการที่เบราว์เซอร์เลียนแบบกันเอง ข้อกำหนดของ HTML5 ไม่ได้กำหนดองค์ประกอบ UI ที่เบราว์เซอร์ต้องมี แต่แสดงองค์ประกอบทั่วไปบางอย่าง ตัวอย่างได้แก่ แถบที่อยู่ แถบสถานะ และแถบเครื่องมือ แน่นอนว่ามีคุณลักษณะเฉพาะสำหรับแต่ละเบราว์เซอร์ เช่น Download Manager ของ Firefox

โครงสร้างพื้นฐานระดับสูง

องค์ประกอบหลักของเบราว์เซอร์คือ

  1. อินเทอร์เฟซผู้ใช้ ได้แก่ แถบที่อยู่ ปุ่มย้อนกลับ/ไปข้างหน้า เมนูการบุ๊กมาร์ก เป็นต้น ทุกส่วนของเบราว์เซอร์จะแสดง ยกเว้นหน้าต่างที่คุณเห็นหน้าที่ร้องขอ
  2. เครื่องมือเบราว์เซอร์: การดำเนินการมาร์ชซัลระหว่าง UI และเครื่องมือแสดงผล
  3. เครื่องมือการแสดงผล: ทำหน้าที่แสดงเนื้อหาที่ขอ ตัวอย่างเช่น หากเนื้อหาที่ขอคือ HTML เครื่องมือแสดงผลจะแยกวิเคราะห์ HTML และ CSS และแสดงเนื้อหาที่แยกวิเคราะห์แล้วบนหน้าจอ
  4. เครือข่าย: สำหรับการเรียกเครือข่าย เช่น คำขอ HTTP มีการติดตั้งใช้งานที่แตกต่างกันสำหรับแพลตฟอร์มต่างๆ ซึ่งอยู่เบื้องหลังอินเทอร์เฟซที่ขึ้นอยู่กับแพลตฟอร์ม
  5. แบ็กเอนด์ของ UI: ใช้สำหรับวาดวิดเจ็ตพื้นฐาน เช่น ช่องตัวเลือกและหน้าต่าง แบ็กเอนด์นี้แสดงอินเทอร์เฟซทั่วไปที่ไม่เจาะจงแพลตฟอร์ม ส่วนด้านล่างจะใช้เมธอดอินเทอร์เฟซผู้ใช้ของระบบปฏิบัติการ
  6. อินเทอร์พรีเตอร์ JavaScript ใช้เพื่อแยกวิเคราะห์และเรียกใช้โค้ด JavaScript
  7. พื้นที่เก็บข้อมูล นี่คือเลเยอร์การคงอยู่ เบราว์เซอร์อาจต้องบันทึกข้อมูลทุกประเภทไว้ในเครื่อง เช่น คุกกี้ นอกจากนี้ เบราว์เซอร์ยังรองรับกลไกการเก็บข้อมูล เช่น localStorage, IndexedDB, WebSQL และ FileSystem
คอมโพเนนต์ของเบราว์เซอร์
ภาพที่ 1: คอมโพเนนต์ของเบราว์เซอร์

คุณควรทราบว่าเบราว์เซอร์ เช่น Chrome จะเรียกใช้เครื่องมือแสดงผลหลายอินสแตนซ์ โดยแต่ละแท็บใช้ 1 อินสแตนซ์ แต่ละแท็บจะทำงานในกระบวนการที่แยกจากกัน

เครื่องมือแสดงภาพ

หน้าที่ของเครื่องมือการแสดงผลนั้นดี... การแสดงผลคือการแสดงเนื้อหาที่ขอบนหน้าจอเบราว์เซอร์

โดยค่าเริ่มต้น เครื่องมือแสดงผลสามารถแสดงเอกสารและรูปภาพ HTML และ XML โดยสามารถแสดงข้อมูลประเภทอื่นผ่านปลั๊กอินหรือส่วนขยาย ตัวอย่างเช่น การแสดงเอกสาร PDF โดยใช้ปลั๊กอินโปรแกรมอ่าน PDF อย่างไรก็ตาม ในบทนี้เราจะมุ่งเน้นไปที่กรณีการใช้งานหลัก นั่นก็คือการแสดง HTML และรูปภาพที่จัดรูปแบบโดยใช้ CSS

เบราว์เซอร์ที่ต่างกันใช้เครื่องมือการแสดงผลที่ต่างกัน: Internet Explorer ใช้ Trident, Firefox ใช้ Gecko, Safari ใช้ WebKit Chrome และ Opera (ตั้งแต่เวอร์ชัน 15) ใช้ Blink ซึ่งเป็นส้อมของ WebKit

WebKit เป็นเครื่องมือแสดงผลแบบโอเพนซอร์สที่เริ่มทำงานโดยเป็นเครื่องมือสำหรับแพลตฟอร์ม Linux และ Apple แก้ไขให้รองรับ Mac และ Windows

ขั้นตอนหลัก

เครื่องมือการแสดงผลจะเริ่มรับเนื้อหาของเอกสารที่ขอจากเลเยอร์เครือข่าย โดยปกติมักจะแบ่งเป็นส่วนๆ ขนาด 8 KB

หลังจากนั้น จะเป็นขั้นตอนพื้นฐานของเครื่องมือแสดงผลดังต่อไปนี้

ขั้นตอนพื้นฐานของเครื่องมือแสดงผล
ภาพที่ 2: ขั้นตอนพื้นฐานของเครื่องมือแสดงผล

เครื่องมือการแสดงผลจะเริ่มแยกวิเคราะห์เอกสาร HTML และแปลงองค์ประกอบเป็นโหนด DOM ในโครงสร้างที่เรียกว่า "โครงสร้างเนื้อหา" เครื่องมือจะแยกวิเคราะห์ข้อมูลรูปแบบทั้งในไฟล์ CSS ภายนอกและในองค์ประกอบของรูปแบบ ระบบจะใช้การจัดรูปแบบข้อมูลร่วมกับวิธีการแบบภาพใน HTML เพื่อสร้างอีกต้นไม้หนึ่ง นั่นคือต้นไม้การแสดงผล

แผนผังการแสดงผลมีสี่เหลี่ยมผืนผ้าที่มีแอตทริบิวต์แบบภาพ เช่น สีและขนาด สี่เหลี่ยมผืนผ้าอยู่ในลำดับที่ถูกต้องที่จะแสดงบนหน้าจอ

หลังจากสร้างโครงสร้างการแสดงผล ระบบจะดำเนินการ "เลย์เอาต์" ซึ่งหมายถึงการให้พิกัดที่ถูกต้องแก่แต่ละโหนดในจุดที่จะปรากฏบนหน้าจอ ขั้นตอนต่อไปคือการทาสี - ต้นไม้แสดงผลจะข้ามผ่านและลงสีแต่ละโหนดโดยใช้ชั้นแบ็กเอนด์ของ UI

คุณควรทราบว่ากระบวนการนี้จะดำเนินการแบบค่อยเป็นค่อยไป เครื่องมือการแสดงผลจะพยายามแสดงเนื้อหาบนหน้าจอโดยเร็วที่สุดเพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ดีขึ้น แต่จะไม่รอจนกว่าจะมีการแยกวิเคราะห์ HTML ทั้งหมดก่อนที่จะเริ่มสร้างและออกแบบทรีการแสดงผล เนื้อหาบางส่วนจะได้รับการแยกวิเคราะห์และแสดง ขณะที่กระบวนการจะดำเนินต่อไปด้วยเนื้อหาที่เหลือที่มาจากเครือข่าย

ตัวอย่างโฟลว์หลัก

ขั้นตอนหลักของ WebKit
ภาพที่ 3: ขั้นตอนหลักของ WebKit
ขั้นตอนหลักของเครื่องมือแสดงภาพ Gecko ของ Mozilla
ภาพที่ 4: ขั้นตอนหลักของเครื่องมือแสดงภาพ Gecko ของ Mozilla

จากภาพ 3 และ 4 คุณจะเห็นว่าถึงแม้ WebKit และ Gecko จะใช้คำศัพท์ที่แตกต่างกันเล็กน้อย แต่ขั้นตอนจะเหมือนกัน

ตุ๊กแกเรียกโครงสร้างขององค์ประกอบที่เป็นภาพว่า "Frame Tree" แต่ละองค์ประกอบคือเฟรม WebKit ใช้คำว่า "Render Tree" และประกอบด้วย "Render Objects" WebKit ใช้คำว่า "เลย์เอาต์" ในการวางองค์ประกอบ ส่วน Gecko เรียกสิ่งนี้ว่า "Reflow" "ไฟล์แนบ" เป็นคำศัพท์ของ WebKit สำหรับการเชื่อมต่อโหนด DOM และข้อมูลภาพเพื่อสร้างโครงสร้างการแสดงผล ความแตกต่างเล็กน้อยที่ไม่เกี่ยวกับความหมายคือ Gecko มีชั้นพิเศษระหว่าง HTML และต้นไม้ DOM เครื่องมือนี้เรียกว่า "ซิงก์เนื้อหา" และโรงงานสำหรับสร้างองค์ประกอบ DOM เราจะอธิบายขั้นตอนแต่ละส่วนดังนี้

การแยกวิเคราะห์ - ทั่วไป

เนื่องจากการแยกวิเคราะห์เป็นกระบวนการที่สำคัญมากภายในเครื่องมือการแสดงผล เราจะเจาะลึกรายละเอียดมากขึ้นเล็กน้อย เรามาเริ่มจากบทนำเล็กๆ น้อยๆ เกี่ยวกับการแยกวิเคราะห์กัน

การแยกวิเคราะห์เอกสารหมายถึงการแปลให้เป็นโครงสร้างที่โค้ดจะใช้ได้ ผลของการแยกวิเคราะห์มักจะเป็นแผนผังของโหนดที่แสดงถึงโครงสร้างของเอกสาร ซึ่งเรียกว่าโครงสร้างการแยกวิเคราะห์หรือโครงสร้างไวยากรณ์

ตัวอย่างเช่น การแยกวิเคราะห์นิพจน์ 2 + 3 - 1 อาจแสดงผลโครงสร้างต้นไม้นี้

โหนดต้นไม้ของนิพจน์ทางคณิตศาสตร์
ภาพที่ 5: โหนดแผนผังนิพจน์ทางคณิตศาสตร์

ไวยากรณ์

การแยกวิเคราะห์จะเป็นไปตามกฎไวยากรณ์ที่เอกสารปฏิบัติตาม เช่น ภาษาหรือรูปแบบที่ใช้เขียนเอกสาร รูปแบบทุกอย่างที่แยกวิเคราะห์ได้ต้องมีไวยากรณ์เชิงกำหนดซึ่งประกอบด้วยคำศัพท์และกฎไวยากรณ์ ซึ่งเรียกว่าไวยากรณ์ที่ไม่มีบริบท ภาษามนุษย์ไม่ใช่ภาษาดังกล่าว จึงไม่สามารถแยกวิเคราะห์ด้วยเทคนิคการแยกวิเคราะห์แบบดั้งเดิม

โปรแกรมแยกวิเคราะห์ - ชุดค่าผสม Lexer

การแยกวิเคราะห์สามารถแบ่งออกเป็น 2 กระบวนการย่อย ได้แก่ การวิเคราะห์คำศัพท์และการวิเคราะห์ไวยากรณ์

การวิเคราะห์คำศัพท์คือกระบวนการแยกอินพุตออกเป็นโทเค็น โทเค็นคือคำศัพท์ทางภาษา ซึ่งเป็นการรวบรวมองค์ประกอบที่ใช้สร้างสรรค์ที่ถูกต้อง ในภาษามนุษย์ จะประกอบด้วยคำทั้งหมดที่ปรากฏในพจนานุกรมสำหรับภาษานั้น

การวิเคราะห์ไวยากรณ์คือการใช้กฎไวยากรณ์ของภาษา

โปรแกรมแยกวิเคราะห์มักจะแบ่งงานระหว่าง 2 องค์ประกอบ ได้แก่ lexer (บางครั้งเรียกว่า Tokenizer) ที่มีหน้าที่แตกอินพุตเป็นโทเค็นที่ถูกต้อง และโปรแกรมแยกวิเคราะห์ที่รับผิดชอบในการสร้างโครงสร้างการแยกวิเคราะห์โดยการวิเคราะห์โครงสร้างเอกสารตามกฎไวยากรณ์ของภาษา

นักเรียนรู้วิธีตัดอักขระที่ไม่เกี่ยวข้องออก เช่น ช่องว่างและการขึ้นบรรทัดใหม่

จากเอกสารต้นฉบับไปยังแยกวิเคราะห์ต้นไม้
ภาพที่ 6: จากเอกสารต้นฉบับไปยังแยกวิเคราะห์ต้นไม้

กระบวนการแยกวิเคราะห์เป็นแบบทำซ้ำ โปรแกรมแยกวิเคราะห์จะขอโทเค็นใหม่จากโปรแกรมแยกวิเคราะห์และพยายามจับคู่โทเค็นกับกฎไวยากรณ์ หากกฎตรงกัน ระบบจะเพิ่มโหนดที่เกี่ยวข้องกับโทเค็นไปยังโครงสร้างการแยกวิเคราะห์และโปรแกรมแยกวิเคราะห์จะขอโทเค็นใหม่

หากไม่มีกฎที่ตรงกัน โปรแกรมแยกวิเคราะห์จะจัดเก็บโทเค็นไว้ภายใน และจะขอโทเค็นต่อไปจนกว่าจะพบกฎที่ตรงกับโทเค็นที่จัดเก็บภายในทั้งหมด หากไม่พบกฎ โปรแกรมแยกวิเคราะห์จะเพิ่มข้อยกเว้น ซึ่งหมายความว่าเอกสารไม่ถูกต้องหรือมีข้อผิดพลาดทางไวยากรณ์

การแปล

ในหลายกรณี โครงสร้างการแยกวิเคราะห์ไม่ใช่ผลที่ได้สุดท้าย การแยกวิเคราะห์มักใช้ในการแปล เช่น การแปลงเอกสารอินพุตเป็นรูปแบบอื่น เช่น การรวบรวมคลิป คอมไพเลอร์ที่คอมไพล์ซอร์สโค้ดเป็นโค้ดของเครื่องจะแยกวิเคราะห์เป็นแผนผังการแยกวิเคราะห์ก่อน แล้วจึงแปลต้นไม้เป็นเอกสารโค้ดเครื่อง

ขั้นตอนการคอมไพล์
ภาพที่ 7: ขั้นตอนการคอมไพล์

ตัวอย่างการแยกวิเคราะห์

ในรูปที่ 5 เราสร้างโครงสร้างการแยกวิเคราะห์จากนิพจน์ทางคณิตศาสตร์ เรามาลองนิยามภาษาง่ายๆ ทางคณิตศาสตร์กัน และดูขั้นตอนการแยกวิเคราะห์กัน

ไวยากรณ์:

  1. องค์ประกอบที่ใช้สร้างสรรค์ของไวยากรณ์ภาษา ได้แก่ นิพจน์ คำศัพท์ และการทำงาน
  2. ภาษาของเราสามารถใช้นิพจน์จำนวนเท่าใดก็ได้
  3. นิพจน์คือ "คำศัพท์" ตามด้วย "operation" ตามด้วยคำศัพท์อื่น
  4. การดำเนินการคือโทเค็นเครื่องหมายบวกหรือโทเค็นเครื่องหมายลบ
  5. คำศัพท์คือโทเค็นจำนวนเต็มหรือนิพจน์

มาวิเคราะห์อินพุต 2 + 3 - 1 กัน

สตริงย่อยแรกที่ตรงกับกฎคือ 2: ตามกฎ #5 คือเป็นคำ การจับคู่รายการที่ 2 คือ 2 + 3: กรณีนี้ตรงกับกฎข้อที่ 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 ได้ทั้งหมด ดูคำจำกัดความอย่างเป็นทางการได้ที่บทความของ Wikipedia เกี่ยวกับไวยากรณ์ที่ไม่มีบริบท

ประเภทของโปรแกรมแยกวิเคราะห์

โปรแกรมแยกวิเคราะห์มี 2 ประเภท ได้แก่ โปรแกรมแยกวิเคราะห์จากบนลงล่าง และโปรแกรมแยกวิเคราะห์จากล่างขึ้นบน คำอธิบายที่เข้าใจง่ายคือโปรแกรมแยกวิเคราะห์จากบนลงล่างจะตรวจสอบโครงสร้างระดับสูงของไวยากรณ์ และพยายามหาการจับคู่กฎที่ตรงกัน โปรแกรมแยกวิเคราะห์ที่อยู่ด้านล่างจะเริ่มด้วยอินพุตและค่อยๆ เปลี่ยนรูปแบบเป็นกฎไวยากรณ์ โดยเริ่มจากกฎระดับล่างไปจนถึงกฎระดับสูง

ลองดูว่าโปรแกรมแยกวิเคราะห์ทั้งสองประเภทจะแยกวิเคราะห์ตัวอย่างของเราอย่างไร

โปรแกรมแยกวิเคราะห์จากบนลงล่างจะเริ่มต้นจากกฎระดับที่สูงกว่า โดยจะระบุ 2 + 3 เป็นนิพจน์ จากนั้นจะระบุ 2 + 3 - 1 เป็นนิพจน์ (กระบวนการระบุนิพจน์จะพัฒนาขึ้นโดยจับคู่กับกฎอื่นๆ แต่จุดเริ่มต้นคือกฎระดับสูงสุด)

โปรแกรมแยกวิเคราะห์จากล่างขึ้นบนจะสแกนอินพุตจนกว่ากฎจะจับคู่ และแทนที่อินพุตที่ตรงกันด้วยกฎ การดําเนินการนี้จะดำเนินไปจนกระทั่งอินพุตสิ้นสุดลง ระบบจะวางนิพจน์ที่ตรงกันบางส่วนในสแต็กของโปรแกรมแยกวิเคราะห์

ซ้อน อินพุต
2 + 3 - 1
term + 3 - 1
การดำเนินการข้อกำหนด 3 - 1
นิพจน์ - 1 รายการ
การดำเนินการนิพจน์ 1
นิพจน์ -

โปรแกรมแยกวิเคราะห์จากล่างขึ้นบนประเภทนี้เรียกว่าโปรแกรมแยกวิเคราะห์ลด Shift เนื่องจากอินพุตจะเลื่อนไปทางขวา (ลองนึกภาพตัวชี้ชี้ไปที่จุดเริ่มต้นของอินพุตและย้ายไปทางขวา) และค่อยๆ ลดลงเป็นกฎไวยากรณ์

การสร้างโปรแกรมแยกวิเคราะห์โดยอัตโนมัติ

มีเครื่องมือที่สามารถสร้างโปรแกรมแยกวิเคราะห์ได้ ให้คุณป้อนข้อมูลไวยากรณ์ในภาษาของคุณ ซึ่งได้แก่ คำศัพท์และกฎไวยากรณ์ จากนั้นโปรแกรมแยกวิเคราะห์ก็จะสร้างโปรแกรมแยกวิเคราะห์ที่ใช้งานได้ การสร้างโปรแกรมแยกวิเคราะห์ต้องอาศัยความเข้าใจในการแยกวิเคราะห์อย่างลึกซึ้ง และการสร้างโปรแกรมแยกวิเคราะห์ที่เพิ่มประสิทธิภาพด้วยตนเองนั้นก็ไม่ใช่เรื่องง่าย ดังนั้น โปรแกรมสร้างโปรแกรมแยกวิเคราะห์จึงอาจเป็นประโยชน์อย่างยิ่ง

WebKit ใช้โปรแกรมสร้างโปรแกรมแยกวิเคราะห์ที่รู้จักกันดี 2 โปรแกรม ได้แก่ Flex สำหรับสร้าง Lexer และ Bison เพื่อสร้างโปรแกรมแยกวิเคราะห์ (คุณอาจพบโปรแกรมแยกวิเคราะห์ชื่อ Lex และ Yacc) อินพุต Flex คือไฟล์ที่มีคำจำกัดความของนิพจน์ทั่วไปของโทเค็น อินพุตของ Bison คือกฎไวยากรณ์ของภาษาในรูปแบบ BNF

โปรแกรมแยกวิเคราะห์ HTML

หน้าที่ของโปรแกรมแยกวิเคราะห์ HTML คือการแยกวิเคราะห์มาร์กอัป HTML ลงในโครงสร้างการแยกวิเคราะห์

ไวยากรณ์ HTML

คำศัพท์และไวยากรณ์ของ HTML มีการกำหนดไว้ในข้อกำหนดจำเพาะที่สร้างโดยองค์กร W3C

ดังที่เราได้เห็นในการเกริ่นนำการแยกวิเคราะห์ สามารถกำหนดไวยากรณ์ไวยากรณ์อย่างเป็นทางการได้โดยใช้รูปแบบต่างๆ เช่น BNF

ขออภัย หัวข้อโปรแกรมแยกวิเคราะห์แบบเดิมทั้งหมดใช้ไม่ได้กับ HTML (ฉันไม่ได้อวดขึ้นมาเพื่อความสนุกเท่านั้น จะใช้ในการแยกวิเคราะห์ CSS และ JavaScript) HTML ไม่สามารถกำหนดได้โดยง่ายด้วยไวยากรณ์ที่ไม่ขึ้นอยู่กับบริบทที่โปรแกรมแยกวิเคราะห์ต้องการ

การกำหนด HTML - DTD (Document Type Definition) มีรูปแบบที่เป็นทางการ แต่ไม่ใช่ไวยากรณ์ที่ปราศจากบริบท

ซึ่งอาจดูแปลกๆ ในตอนแรก เพราะ HTML ค่อนข้างใกล้เคียงกับ XML โปรแกรมแยกวิเคราะห์ XML มีอยู่มากมาย HTML - XHTML มีรูปแบบ XML - แล้วความแตกต่างสำคัญอย่างไร

ความแตกต่างคือแนวทาง HTML จะมี "การให้อภัย" มากกว่า โดยจะช่วยให้คุณสามารถข้ามบางแท็ก (ซึ่งจะมีการเพิ่มโดยปริยาย) หรือบางครั้งก็ละเว้นแท็กเริ่มต้นหรือแท็กสิ้นสุด และอื่นๆ โดยรวมแล้วคือไวยากรณ์แบบ "นุ่มนวล" แทนที่จะเป็นไวยากรณ์ที่ยากและซับซ้อนของ XML

รายละเอียดที่ดูเหมือนจะเล็กน้อยนี้สร้างความแตกต่างให้โลกได้ กล่าวอีกนัยหนึ่งคือนี่คือเหตุผลหลักที่ทำให้ HTML ได้รับความนิยมมาก เพราะให้อภัยข้อผิดพลาดและทำให้ผู้เขียนเว็บนั้นทำให้ชีวิตเป็นเรื่องง่าย ในทางกลับกัน การเขียนไวยากรณ์อย่างเป็นทางการทำได้ยาก ดังนั้นสรุปแล้วโปรแกรมแยกวิเคราะห์ HTML ไม่สามารถแยกวิเคราะห์ได้ง่ายๆ ด้วยโปรแกรมแยกวิเคราะห์ทั่วไป เนื่องจากไวยากรณ์ไม่ได้ขึ้นอยู่กับบริบท โปรแกรมแยกวิเคราะห์ XML จะแยกวิเคราะห์ HTML ไม่ได้

HTML DTD

คำจำกัดความ HTML อยู่ในรูปแบบ DTD รูปแบบนี้ใช้เพื่อกำหนดภาษาของกลุ่ม SGML รูปแบบนี้มีคำจำกัดความสำหรับองค์ประกอบที่อนุญาตทั้งหมด แอตทริบิวต์ และลำดับชั้น ดังที่เราได้เห็นก่อนหน้านี้ HTML DTD ไม่ได้สร้างไวยากรณ์ที่ปราศจากบริบท

DTD มีอยู่ 2-3 รูปแบบ โหมดจำกัดจะเป็นไปตามข้อกำหนดเฉพาะ แต่โหมดอื่นๆ มีการรองรับมาร์กอัปที่เบราว์เซอร์ใช้ในอดีต วัตถุประสงค์คือความเข้ากันได้แบบย้อนหลังกับเนื้อหาเก่า DTD ปัจจุบันอยู่ที่ www.w3.org/TR/html4/strict.dtd

DOM

แผนผังเอาต์พุต ("แผนผังการแยกวิเคราะห์") เป็นต้นไม้ขององค์ประกอบ DOM และโหนดแอตทริบิวต์ DOM ย่อมาจาก Document Object Model โดยเป็นการนำเสนอวัตถุของเอกสาร HTML และอินเทอร์เฟซองค์ประกอบ HTML ไปยังโลกภายนอก เช่น JavaScript

ส่วนรากของต้นไม้คือออบเจ็กต์ "Document"

DOM มีความเชื่อมโยงกับมาร์กอัปเกือบหนึ่งต่อหนึ่ง เช่น

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

ระบบจะแปลมาร์กอัปนี้เป็นแผนผัง DOM ต่อไปนี้

แผนผัง DOM ของมาร์กอัปตัวอย่าง
ภาพที่ 8: แผนผัง DOM ของมาร์กอัปตัวอย่าง

องค์กร W3C จะระบุ DOM ด้วยเช่นกัน เช่นเดียวกับ HTML โปรดดู 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 อธิบายอัลกอริทึมการแยกวิเคราะห์ไว้อย่างละเอียด อัลกอริทึมประกอบด้วย 2 ขั้นตอน ได้แก่ การแปลงข้อมูลเป็นโทเค็นและการสร้างโครงสร้างต้นไม้

การแปลงข้อมูลเป็นโทเค็นคือการวิเคราะห์คำศัพท์ โดยแยกวิเคราะห์อินพุตออกเป็นโทเค็น ส่วนหนึ่งของโทเค็น HTML ได้แก่ แท็กเริ่มต้น แท็กปิดท้าย ชื่อแอตทริบิวต์ และค่าแอตทริบิวต์

เครื่องมือ Tokenizer จะจดจำโทเค็น มอบให้กับเครื่องมือสร้างต้นไม้ และใช้อักขระถัดไปในการจดจำโทเค็นถัดไป และเป็นเช่นนี้ไปเรื่อยๆ จนกระทั่งอินพุตสิ้นสุดลง

ขั้นตอนการแยกวิเคราะห์ HTML (นำมาจากข้อกำหนดของ HTML5)
ภาพที่ 9: ขั้นตอนการแยกวิเคราะห์ HTML (นำมาจากข้อกำหนดของ HTML5)

อัลกอริทึมการแปลงข้อมูลเป็นโทเค็น

เอาต์พุตของอัลกอริทึมคือโทเค็น HTML อัลกอริทึมจะแสดงเป็นเครื่องบ่งชี้สถานะ แต่ละรัฐจะใช้อักขระของสตรีมอินพุตอย่างน้อย 1 ตัว และอัปเดตสถานะถัดไปตามอักขระเหล่านั้น การตัดสินใจจะขึ้นอยู่กับสถานะการเปลี่ยนเป็นโทเค็นในปัจจุบันและสถานะการสร้างต้นไม้ ซึ่งหมายความว่าอักขระที่ใช้อักขระเดียวกันจะให้ผลลัพธ์ที่แตกต่างกันสำหรับสถานะถัดไปที่ถูกต้อง โดยขึ้นอยู่กับสถานะปัจจุบัน อัลกอริทึมนี้ซับซ้อนเกินกว่าจะอธิบายได้ทั้งหมด ดังนั้นมาดูตัวอย่างง่ายๆ ที่จะช่วยให้เราเข้าใจหลักการนี้ได้ดียิ่งขึ้น

ตัวอย่างเบื้องต้น - การทำโทเค็น HTML ต่อไปนี้เป็นโทเค็น

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

สถานะเริ่มต้นคือ "สถานะข้อมูล" เมื่อพบอักขระ < สถานะจะเปลี่ยนเป็น "สถานะเปิดแท็ก" การใช้อักขระ a-z ทำให้เกิดการสร้าง "โทเค็นของแท็กเริ่มต้น" สถานะจะเปลี่ยนเป็น "สถานะชื่อแท็ก" เราจะอยู่ในสถานะนี้จนกว่าอักขระ > จะหมด อักขระแต่ละตัวจะต่อท้ายชื่อโทเค็นใหม่ ในกรณีของเรา โทเค็นที่สร้างคือโทเค็น html

เมื่อถึงแท็ก > โทเค็นปัจจุบันจะเริ่มทำงาน และสถานะจะเปลี่ยนกลับไปเป็น "สถานะข้อมูล" ระบบจะจัดการแท็ก <body> ตามขั้นตอนเดียวกัน ตอนนี้มีการใช้แท็ก html และ body แล้ว ตอนนี้เรากลับมาที่ "สถานะข้อมูล" การใช้อักขระ H ของ Hello world จะทำให้มีการสร้างและส่งออกโทเค็นอักขระ ซึ่งจะดำเนินต่อไปจนกว่าจะถึงวันที่ < ของ </body> เราจะส่งโทเค็นอักขระ 1 ตัวสําหรับอักขระแต่ละตัวของ Hello world

ตอนนี้เรากลับมาที่ "สถานะเปิดแท็ก" แล้ว การใช้อินพุตถัดไป / จะทำให้ระบบสร้าง end tag token และเปลี่ยนเป็น "สถานะของชื่อแท็ก" อีกครั้งเราจะอยู่ในสถานะนี้จนกว่าจะไปถึง > จากนั้นระบบจะปล่อยโทเค็นแท็กใหม่ จากนั้นจะกลับไปที่ "สถานะข้อมูล" ระบบจะพิจารณาข้อมูลที่ป้อน </html> เหมือนเคสก่อนหน้า

การแปลงข้อมูลตัวอย่างเป็นโทเค็น
ภาพที่ 10: การแปลงข้อมูลตัวอย่างเป็นโทเค็น

อัลกอริทึมการสร้างต้นไม้

เมื่อสร้างโปรแกรมแยกวิเคราะห์ ระบบจะสร้างออบเจ็กต์เอกสารขึ้น ระหว่างระยะการสร้างต้นไม้ ระบบจะแก้ไขแผนผัง DOM ที่มีเอกสารในรากและจะเพิ่มองค์ประกอบเข้าไปในนั้น แต่ละโหนดที่ออกโดย Tokenizer จะได้รับการประมวลผลโดยตัวสร้างต้นไม้ สำหรับแต่ละโทเค็น ข้อกำหนดจะระบุองค์ประกอบ DOM ที่เกี่ยวข้องกับโทเค็นนั้น และจะสร้างขึ้นสำหรับโทเค็นนี้ ระบบจะเพิ่มองค์ประกอบลงในแผนผัง DOM และชุดองค์ประกอบที่เปิดอยู่ การซ้อนนี้ใช้เพื่อแก้ไขการซ้อนที่ไม่ตรงกันและแท็กที่ไม่ได้ปิด อัลกอริทึมยังมีคำอธิบายว่าเป็นแมชชีนประจำรัฐอีกด้วย สถานะนี้เรียกว่า "โหมดการแทรก"

ลองดูกระบวนการสร้างต้นไม้สำหรับอินพุตตัวอย่าง

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

อินพุตไปยังระยะการสร้างโครงสร้างต้นไม้คือลำดับโทเค็นจากขั้นตอนการแปลงข้อมูลเป็นโทเค็น โหมดแรกคือ "โหมดเริ่มต้น" การรับโทเค็น "html" จะทำให้เกิดการเปลี่ยนเป็นโหมด "ก่อน html" และการประมวลผลโทเค็นอีกครั้งในโหมดนั้น การดำเนินการนี้จะทำให้เกิดการสร้างองค์ประกอบ HTMLHtmlElement ซึ่งจะต่อท้ายออบเจ็กต์เอกสารระดับรูท

สถานะจะเปลี่ยนเป็น "before head" จากนั้นจะได้รับโทเค็น "body" ระบบจะสร้าง HTMLHeadElement โดยปริยาย แม้ว่าเราจะไม่มีโทเค็น "head" และระบบจะเพิ่มลงในโครงสร้าง

ตอนนี้เราจะไปที่โหมด "ในส่วนหัว" จากนั้นไปที่ "หลังส่วนหัว" โทเค็นเนื้อหาจะได้รับการประมวลผลอีกครั้ง โดยมีการสร้างและแทรก HTMLBodyElement แล้วโอนโหมดไปที่ "inbody"

ได้รับโทเค็นอักขระของสตริง "สวัสดีโลก" แล้ว รายการแรกจะทำให้เกิดการสร้างและการแทรกโหนด "ข้อความ" และอักขระอื่นๆ จะต่อท้ายโหนดนั้น

การรับโทเค็นสิ้นสุดส่วนเนื้อหาจะทำให้การโอนเข้าสู่โหมด "หลังเนื้อหา" ตอนนี้เราจะได้รับแท็กปิดท้าย HTML ซึ่งจะนำเราไปสู่โหมด "หลังหลังเนื้อหา" การรับโทเค็นสิ้นสุดของไฟล์จะทำให้การแยกวิเคราะห์สิ้นสุด

โครงสร้างต้นไม้ของ HTML ตัวอย่าง
ภาพที่ 11: โครงสร้างต้นไม้ของ HTML ตัวอย่าง

การดำเนินการเมื่อการแยกวิเคราะห์เสร็จสิ้น

ในขั้นตอนนี้ เบราว์เซอร์จะทำเครื่องหมายเอกสารเป็นแบบอินเทอร์แอกทีฟและเริ่มแยกวิเคราะห์สคริปต์ที่อยู่ในโหมด "เลื่อนเวลา" ซึ่งเป็นสคริปต์ที่ควรทำหลังจากแยกวิเคราะห์เอกสารแล้ว สถานะของเอกสารจะถูกตั้งค่าเป็น "เสร็จสมบูรณ์" และเหตุการณ์ "load" จะเริ่มทำงาน

ดูอัลกอริทึมเต็มรูปแบบสำหรับการแปลงเป็นโทเค็นและการสร้างโครงสร้างต้นไม้ได้ในข้อกำหนดของ HTML5

การยอมรับข้อผิดพลาดของเบราว์เซอร์

คุณจะไม่เห็นข้อผิดพลาด "ไวยากรณ์ไม่ถูกต้อง" ในหน้า HTML เบราว์เซอร์จะแก้ไขเนื้อหาที่ไม่ถูกต้องและดำเนินการต่อ

ลองดูตัวอย่าง HTML นี้

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

ฉันอาจมีการละเมิดกฎประมาณ 1 ล้านข้อ ("mytag" ไม่ใช่แท็กมาตรฐาน มีการซ้อนองค์ประกอบ "p" และ "div" ผิดรายการ และอื่นๆ) แต่เบราว์เซอร์ยังคงแสดงอย่างถูกต้องและไม่ร้องเรียน ดังนั้นโค้ดโปรแกรมแยกวิเคราะห์จำนวนมากจึงแก้ไขข้อผิดพลาดของผู้เขียน HTML

การจัดการข้อผิดพลาดนั้นค่อนข้างสม่ำเสมอในเบราว์เซอร์ แต่สิ่งที่น่าประหลาดใจนี้ยังไม่ใช่ส่วนหนึ่งของข้อกำหนด 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 จะเปลี่ยนลำดับชั้นเป็นตารางระดับเดียวกัน 2 ตารางดังนี้

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

โค้ด:

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

WebKit ใช้สแต็กสำหรับเนื้อหาองค์ประกอบปัจจุบัน ซึ่งจะแยกตารางด้านในออกจากกลุ่มตารางด้านนอก ตารางเหล่านี้จะเป็นกลุ่มข้างเคียง

องค์ประกอบของแบบฟอร์มที่ฝัง

ในกรณีที่ผู้ใช้ใส่แบบฟอร์มไว้ในแบบฟอร์มอื่น ระบบจะไม่สนใจแบบฟอร์มที่ 2

โค้ด:

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

จำแนวคิดการแยกวิเคราะห์ในบทนำได้ไหม CSS ต่างจาก HTML ตรงที่ไวยากรณ์ที่ไม่มีบริบทและสามารถแยกวิเคราะห์ได้โดยใช้โปรแกรมแยกวิเคราะห์ประเภทต่างๆ ที่อธิบายไว้ในบทนำ อันที่จริง ข้อกำหนด CSS กำหนดไวยากรณ์ CSS คำศัพท์และไวยากรณ์

ลองดูตัวอย่างต่อไปนี้

ไวยากรณ์คำศัพท์ (คำศัพท์) จะกำหนดด้วยนิพจน์ทั่วไปสำหรับแต่ละโทเค็น ดังนี้

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

"ident" ย่อมาจากตัวระบุ เช่น ชื่อคลาส "name" คือรหัสองค์ประกอบ (ที่อ้างอิงโดย "#")

ไวยากรณ์มีอธิบายอยู่ใน BNF

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

คำอธิบาย

ชุดกฎคือโครงสร้างดังนี้

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

div.error และ a.error เป็นตัวเลือก ส่วนภายในวงเล็บปีกกาประกอบด้วยกฎที่ชุดกฎนี้ใช้ โครงสร้างนี้มีคำจำกัดความอย่างเป็นทางการในคำจำกัดความนี้

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

ซึ่งหมายความว่าชุดกฎคือตัวเลือกหรือจำนวนตัวเลือก (ไม่บังคับ) ที่คั่นด้วยคอมมาและเว้นวรรค (S หมายถึงช่องว่าง) ชุดกฎมีวงเล็บปีกกาและมีการประกาศหรือประกาศจำนวนหนึ่งซึ่งคั่นด้วยเครื่องหมายเซมิโคลอน "การประกาศ" และ "ตัวเลือก" จะได้รับการนิยามไว้ในคำจำกัดความของ BNF ต่อไปนี้

โปรแกรมแยกวิเคราะห์ CSS ของ WebKit

WebKit ใช้โปรแกรมสร้างโปรแกรมแยกวิเคราะห์ Flex และ Bison เพื่อสร้างโปรแกรมแยกวิเคราะห์โดยอัตโนมัติจากไฟล์ไวยากรณ์ CSS อย่างที่คุณจำได้จากบทนำของโปรแกรมแยกวิเคราะห์ Bison จะสร้างโปรแกรมแยกวิเคราะห์แบบลดลงจากล่างขึ้นบน Firefox ใช้โปรแกรมแยกวิเคราะห์จากบนลงล่างที่เขียนด้วยตนเอง ในทั้ง 2 กรณี ไฟล์ CSS แต่ละไฟล์จะได้รับการแยกวิเคราะห์เป็นออบเจ็กต์ StyleSheet แต่ละออบเจ็กต์มีกฎ CSS ออบเจ็กต์กฎ CSS มีออบเจ็กต์ตัวเลือกและการประกาศ รวมถึงออบเจ็กต์อื่นๆ ที่เกี่ยวข้องกับไวยากรณ์ CSS

การแยกวิเคราะห์ CSS
รูปที่ 12: การแยกวิเคราะห์ CSS

ลำดับการประมวลผลสำหรับสคริปต์และสไตล์ชีต

สคริปต์

โมเดลของเว็บจะเป็นแบบซิงโครนัส ผู้เขียนคาดหวังให้สคริปต์ได้รับการแยกวิเคราะห์และดำเนินการทันทีเมื่อโปรแกรมแยกวิเคราะห์เข้าถึงแท็ก <script> การแยกวิเคราะห์เอกสารจะหยุดลงจนกว่าจะมีการเรียกใช้สคริปต์ หากสคริปต์เป็นแบบภายนอก จะต้องดึงทรัพยากรจากเครือข่ายก่อน ซึ่งจะทำแบบพร้อมกันและการแยกวิเคราะห์จนกว่าจะดึงข้อมูลทรัพยากร นี่เป็นต้นแบบมาหลายปีแล้ว และถูกระบุไว้ในข้อกำหนดเฉพาะ HTML4 และ 5 ด้วย ผู้เขียนสามารถเพิ่มแอตทริบิวต์ "เลื่อน" ลงในสคริปต์ ซึ่งในกรณีนี้จะไม่หยุดการแยกวิเคราะห์เอกสาร และจะดำเนินการหลังจากแยกวิเคราะห์เอกสารแล้ว HTML5 เพิ่มตัวเลือกในการทำเครื่องหมายสคริปต์เป็นแบบไม่พร้อมกันเพื่อให้มีการแยกวิเคราะห์และเรียกใช้โดยเทรดอื่น

การแยกวิเคราะห์แบบคาดเดา

ทั้ง WebKit และ Firefox ต่างก็ดำเนินการเพิ่มประสิทธิภาพนี้ ขณะเรียกใช้สคริปต์ เธรดอื่นจะแยกวิเคราะห์ส่วนที่เหลือของเอกสาร และค้นหาทรัพยากรอื่นๆ ที่ต้องโหลดจากเครือข่ายและโหลดทรัพยากรเหล่านั้น วิธีนี้จะช่วยให้โหลดทรัพยากรได้ขณะเชื่อมต่อแบบขนานและจะปรับปรุงความเร็วโดยรวม หมายเหตุ: โปรแกรมแยกวิเคราะห์แบบคาดเดาจะแยกวิเคราะห์การอ้างอิงไปยังทรัพยากรภายนอก เช่น สคริปต์ภายนอก สไตล์ชีต และรูปภาพเท่านั้น โดยไม่ได้แก้ไขแผนผัง DOM ซึ่งเหลืออยู่ในโปรแกรมแยกวิเคราะห์หลัก

สไตล์ชีต

ในทางกลับกัน สไตล์ชีตมีโมเดลที่ต่างกัน ตามหลักการแล้ว ดูเหมือนว่าเนื่องจากสไตล์ชีตไม่ได้เปลี่ยนแปลงโครงสร้าง DOM จึงไม่มีเหตุผลที่จะต้องรอและหยุดการแยกวิเคราะห์เอกสาร แต่ปัญหาจะเกิดจากสคริปต์ที่ขอข้อมูลรูปแบบในขั้นตอนการแยกวิเคราะห์เอกสาร หากยังไม่ได้โหลดและแยกวิเคราะห์สไตล์ สคริปต์จะได้รับคำตอบที่ไม่ถูกต้องและเห็นได้ชัดว่าสไตล์นี้ทำให้เกิดปัญหาอย่างมาก แม้จะเป็นกรณีส่วนใหญ่แต่ก็พบได้บ่อย Firefox จะบล็อกสคริปต์ทั้งหมดเมื่อมีสไตล์ชีตที่ยังโหลดและแยกวิเคราะห์อยู่ WebKit จะบล็อกสคริปต์เฉพาะเมื่อพยายามเข้าถึงคุณสมบัติของสไตล์บางอย่างซึ่งอาจได้รับผลกระทบจากสไตล์ชีตที่ยกเลิกการโหลด

แสดงผลการสร้างโครงสร้างต้นไม้

ขณะกำลังสร้างต้นไม้ DOM เบราว์เซอร์จะสร้างอีกต้นไม้หนึ่ง ซึ่งก็คือทรีการแสดงผล โครงสร้างต้นไม้นี้คือองค์ประกอบที่มองเห็นตามลำดับที่จะแสดง ซึ่งเป็นการนำเสนอภาพของเอกสาร วัตถุประสงค์ของต้นไม้นี้คือเพื่อให้สามารถทาสีเนื้อหาตามลำดับที่ถูกต้อง

Firefox เรียกองค์ประกอบในโครงสร้างการแสดงผลว่า "เฟรม" WebKit จะใช้คำว่าเรนเดอร์หรือออบเจ็กต์เรนเดอร์

โหมดแสดงภาพรู้วิธีจัดวางและระบายสีตัวเองและลูกๆ

คลาส RenderObject ของ WebKit ซึ่งเป็นคลาสพื้นฐานของโหมดแสดงภาพมีคำจำกัดความดังต่อไปนี้

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

ตัวแสดงผลแต่ละตัวจะแสดงพื้นที่สี่เหลี่ยมผืนผ้า ซึ่งปกติจะสอดคล้องกับช่อง CSS ของโหนด ดังที่อธิบายไว้โดยข้อกำหนด CSS2 ซึ่งรวมถึงข้อมูลทางเรขาคณิต เช่น ความกว้าง ความสูง และตำแหน่ง

ประเภทกล่องจะได้รับผลกระทบจากค่า "display" ของแอตทริบิวต์รูปแบบที่เกี่ยวข้องกับโหนด (ดูส่วนการคำนวณสไตล์) โค้ด 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" มีตัวแสดงผล 3 ตัว ได้แก่ ตัวหนึ่งสำหรับพื้นที่แสดงผล ตัวหนึ่งสำหรับช่องรายการแบบเลื่อนลง และอีกชุดสำหรับปุ่ม นอกจากนี้ เมื่อข้อความถูกแบ่งเป็นหลายบรรทัดเนื่องจากความกว้างไม่เพียงพอสำหรับหนึ่งบรรทัด บรรทัดใหม่จะถูกเพิ่มเป็นตัวแสดงผลเพิ่มเติม

อีกตัวอย่างหนึ่งของเครื่องมือแสดงผลหลายรายการคือ HTML ที่เสียหาย ตามข้อกำหนด CSS องค์ประกอบในบรรทัดต้องมีเฉพาะองค์ประกอบบล็อกหรือองค์ประกอบในบรรทัดเท่านั้น ในกรณีของเนื้อหาผสม ระบบจะสร้างตัวแสดงผลบล็อกที่ไม่ระบุตัวตนเพื่อรวมองค์ประกอบในบรรทัด

ออบเจ็กต์การแสดงผลบางรายการสอดคล้องกับโหนด DOM แต่ไม่ได้อยู่ในตำแหน่งเดียวกันในแผนผัง จุดลอยและองค์ประกอบที่ระบุตำแหน่งแบบตายตัวจะหลุดลอยไป วางไว้ในส่วนอื่นของต้นไม้ และจับคู่กับเฟรมจริง เฟรมตัวยึดตำแหน่งคือตำแหน่งที่ควรจะเป็น

แผนผังการแสดงผลและแผนผัง DOM ที่เกี่ยวข้อง
ภาพที่ 13: แผนผังการแสดงผลและแผนผัง DOM ที่เกี่ยวข้อง "วิวพอร์ต" คือค่าเริ่มต้นที่มีการบล็อก ใน WebKit จะเป็นออบเจ็กต์ "RenderView"

ขั้นตอนการสร้างต้นไม้

ใน Firefox งานนำเสนอจะได้รับการลงทะเบียนเป็น Listener การอัปเดต DOM งานนำเสนอมอบสิทธิ์การสร้างเฟรมให้กับ FrameConstructor และตัวสร้างจะกำหนดรูปแบบ (ดูการคำนวณรูปแบบ) และสร้างเฟรม

ใน WebKit กระบวนการแก้ไขรูปแบบและสร้างตัวแสดงผลเรียกว่า "ไฟล์แนบ" ทุกโหนด DOM จะมีเมธอด "attach" ไฟล์แนบเป็นแบบซิงโครนัส การแทรกโหนดไปยังแผนผัง DOM จะเรียกเมธอด "attach" โหนดใหม่

การประมวลผลแท็ก HTML และแท็กเนื้อหาจะทำให้เกิดการสร้างรากต้นไม้ในการแสดงผล ออบเจ็กต์การแสดงผลระดับรูทสอดคล้องกับสิ่งที่ข้อกำหนดของ CSS เรียกว่าบล็อก "ประกอบด้วย" ซึ่งก็คือบล็อกบนสุดที่มีบล็อกอื่นๆ ทั้งหมด โดยมีขนาดเป็นวิวพอร์ต ซึ่งก็คือขนาดพื้นที่แสดงหน้าต่างเบราว์เซอร์ Firefox เรียกว่า ViewPortFrame และ WebKit เรียกว่า RenderView นี่คือออบเจ็กต์แสดงผลที่เอกสารชี้ไป โครงสร้างส่วนที่เหลือจะสร้างขึ้นเป็นการแทรกโหนด DOM

ดูข้อมูลจำเพาะ CSS2 ในรูปแบบการประมวลผล

การคํานวณรูปแบบ

การสร้างแผนผังการแสดงผลจำเป็นต้องมีการคำนวณคุณสมบัติภาพของออบเจ็กต์การแสดงผลแต่ละรายการ ซึ่งทำได้โดยการคำนวณคุณสมบัติของรูปแบบของแต่ละองค์ประกอบ

สไตล์นี้ประกอบด้วยสไตล์ชีตของต้นทางต่างๆ องค์ประกอบของรูปแบบอินไลน์ และคุณสมบัติการมองเห็นใน HTML (เช่น คุณสมบัติ "bgcolor") ส่วนต่อมาได้รับการแปลเป็นคุณสมบัติของรูปแบบ CSS ที่ตรงกัน

จุดเริ่มต้นของสไตล์ชีตคือสไตล์ชีตเริ่มต้นของเบราว์เซอร์ สไตล์ชีตที่มีให้โดยผู้เขียนหน้าเว็บและสไตล์ชีตของผู้ใช้ - สไตล์ชีตเหล่านี้มาจากผู้ใช้เบราว์เซอร์ (เบราว์เซอร์จะช่วยให้คุณกําหนดสไตล์ที่ชื่นชอบได้ ตัวอย่างเช่น ใน Firefox คุณสามารถทำได้โดยวางสไตล์ชีตไว้ในโฟลเดอร์ "Firefox Profile")

การคำนวณรูปแบบจะทำให้เกิดปัญหาบางอย่างดังนี้

  1. ข้อมูลรูปแบบเป็นโครงสร้างที่ใหญ่มากและมีคุณสมบัติของรูปแบบจำนวนมาก ซึ่งอาจทำให้เกิดปัญหาหน่วยความจำได้
  2. การค้นหากฎการจับคู่สำหรับแต่ละองค์ประกอบอาจทำให้เกิดปัญหาด้านประสิทธิภาพหากไม่ได้เพิ่มประสิทธิภาพ การย้ายรายการกฎทั้งหมดสำหรับองค์ประกอบแต่ละรายการเพื่อค้นหารายการที่ตรงกันเป็นงานที่หนัก ตัวเลือกอาจมีโครงสร้างที่ซับซ้อนซึ่งอาจทำให้กระบวนการจับคู่เริ่มต้นในเส้นทางที่ดูแล้วมีแนวโน้มที่จะถูกพิสูจน์แล้วว่าเป็นวิธีที่ไร้ประโยชน์และจะต้องลองใช้เส้นทางอื่นแทน

    เช่น ตัวเลือกสารประกอบนี้

    div div div div{
    ...
    }
    

    หมายความว่ากฎจะมีผลกับ <div> ที่เป็นองค์ประกอบสืบทอดของ div 3 รายการ สมมติว่าคุณต้องการตรวจสอบว่ากฎมีผลกับองค์ประกอบ <div> ที่ระบุหรือไม่ คุณเลือกเส้นทางเฉพาะที่ขึ้นไปบนโครงสร้างเพื่อตรวจสอบ คุณอาจต้องข้ามผ่านโครงสร้างโหนดขึ้นเพื่อค้นหาว่ามี div เพียง 2 รายการเท่านั้นและกฎจะไม่มีผล จากนั้นคุณต้องลองใช้เส้นทางอื่นในแผนผัง

  3. การใช้กฎจะมีกฎการเรียงซ้อนที่ค่อนข้างซับซ้อนซึ่งจะกำหนดลำดับชั้นของกฎ

มาดูกันว่าเบราว์เซอร์พบปัญหาเหล่านี้อย่างไร

การแชร์ข้อมูลรูปแบบ

โหนด WebKit อ้างอิงออบเจ็กต์รูปแบบ (RenderStyle) โหนดสามารถแชร์ออบเจ็กต์เหล่านี้ได้ในบางเงื่อนไข โหนดดังกล่าวคือพี่น้องหรือพี่น้อง และ

  1. องค์ประกอบต้องอยู่ในสถานะเมาส์เดียวกัน (เช่น องค์ประกอบหนึ่งต้องไม่อยู่ใน :hover ในขณะที่อีกองค์ประกอบไม่อยู่)
  2. องค์ประกอบทั้งสองไม่ควรมีรหัส
  3. ชื่อแท็กควรตรงกัน
  4. แอตทริบิวต์คลาสควรตรงกัน
  5. ชุดแอตทริบิวต์ที่แมปต้องเหมือนกัน
  6. สถานะของลิงก์ต้องตรงกัน
  7. สถานะการโฟกัสต้องตรงกัน
  8. ทั้ง 2 องค์ประกอบไม่ควรได้รับผลกระทบจากตัวเลือกแอตทริบิวต์ โดยที่ได้รับผลกระทบหมายถึงมีการจับคู่ตัวเลือกที่ใช้ตัวเลือกแอตทริบิวต์ในตำแหน่งใดก็ได้ภายในตัวเลือกเลย
  9. ต้องไม่มีแอตทริบิวต์รูปแบบในบรรทัดในองค์ประกอบ
  10. ต้องไม่มีตัวเลือกระดับเดียวกันที่ใช้งานอยู่เลย WebCore จะสลับสวิตช์ส่วนกลางเมื่อพบตัวเลือกระดับข้างเคียง และปิดการใช้รูปแบบร่วมกันสำหรับเอกสารทั้งฉบับเมื่อเอกสารเหล่านั้นแสดงอยู่ ซึ่งรวมถึงตัวเลือก + และตัวเลือก เช่น :first-child และ :last-child

แผนผังกฎของ Firefox

Firefox มีแผนผังเพิ่มเติม 2 ต้นไม้เพื่อการคำนวณรูปแบบที่ง่ายขึ้น ได้แก่ แผนผังกฎและแผนผังบริบทของรูปแบบ WebKit ยังมีออบเจ็กต์รูปแบบอีกด้วยแต่ไม่ได้จัดเก็บไว้ในโครงสร้างต้นไม้ เช่น Styleเมนูตามบริบท แต่เฉพาะโหนด 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}

เพื่อทำให้สิ่งต่างๆ ง่ายขึ้น สมมติว่าเราต้องกรอกข้อมูลในโครงสร้างเพียง 2 แบบ คือ โครงสร้างสีและโครงสร้างขอบ โครงสร้างสีจะมีสมาชิกเพียงรายเดียวเท่านั้น ซึ่งก็คือสี โครงสร้างขอบประกอบด้วยทั้ง 4 ด้าน

โครงสร้างกฎที่ได้จะมีลักษณะดังนี้ (โหนดจะถูกทำเครื่องหมายด้วยชื่อโหนด ซึ่งก็คือจำนวนของกฎที่โหนดชี้ไป):

แผนผังกฎ
ภาพที่ 16: แผนผังกฎ

แผนผังบริบทจะมีลักษณะดังนี้ (ชื่อโหนด: โหนดกฎที่ชี้ไป):

แผนผังบริบท
ภาพที่ 17: แผนผังบริบท

สมมติว่าเราแยกวิเคราะห์ HTML และไปที่แท็ก <div> ที่สอง เราต้องการสร้างบริบทของรูปแบบสำหรับโหนดนี้และเติมโครงสร้างรูปแบบของโหนด

เราจะจับคู่กฎและพบว่ากฎการจับคู่สำหรับ <div> คือ 1, 2 และ 6 ซึ่งหมายความว่ามีเส้นทางที่มีอยู่ในโครงสร้างซึ่งองค์ประกอบของเราสามารถใช้ได้แล้ว และเราเพียงต้องเพิ่มโหนดอื่นให้กับโหนดดังกล่าวสำหรับกฎ 6 (โหนด F ในโครงสร้างกฎ)

เราจะสร้างบริบทของรูปแบบและใส่ไว้ในแผนผังบริบท บริบทรูปแบบใหม่จะชี้ไปยังโหนด F ในแผนผังกฎ

ตอนนี้เราต้องเติมโครงสร้างของรูปแบบ เราจะเริ่มด้วยการเติมโครงสร้างระยะขอบ เนื่องจากโหนดกฎสุดท้าย (F) ไม่ได้เพิ่มลงในโครงสร้างระยะขอบ เราจึงอาจขึ้นไปตามโครงสร้างที่แคชไว้จนกว่าจะพบโครงสร้างที่แคชไว้ซึ่งคำนวณในการแทรกโหนดก่อนหน้าและนำไปใช้ เราจะพบในโหนด B ซึ่งเป็นโหนดบนสุดที่ระบุกฎระยะขอบ

เรามีคำจำกัดความของโครงสร้างสี จึงไม่สามารถใช้โครงสร้างที่แคชไว้ได้ เนื่องจากสีมีแอตทริบิวต์ 1 รายการ เราไม่จำเป็นต้องขึ้นไปตามโครงสร้างเพื่อเติมแอตทริบิวต์อื่นๆ เราจะคำนวณค่าสิ้นสุด (แปลงสตริงเป็น RGB เป็นต้น) และแคชโครงสร้างที่คำนวณแล้วในโหนดนี้

การทำงานกับองค์ประกอบ <span> ที่ 2 นั้นง่ายยิ่งขึ้นอีก เราจะจับคู่กฎดังกล่าวและเริ่มสรุปว่ากฎนั้นชี้ไปยังกฎ G เช่นเดียวกับช่วงเวลาก่อนหน้า เนื่องจากเรามีข้างเคียงที่ชี้ไปยังโหนดเดียวกัน เราจึงแชร์บริบทของสไตล์ทั้งหมดได้และเพียงชี้ไปยังบริบทของสแปนก่อนหน้า

สำหรับโครงสร้างที่มีกฎที่รับช่วงมาจากระดับบนสุด การแคชจะทำในแผนผังบริบท (พร็อพเพอร์ตี้สีมีการรับค่ามาจริงๆ แต่ Firefox จะถือว่าพร็อพเพอร์ตี้รีเซ็ตและแคชไว้ในโครงสร้างกฎ)

ตัวอย่างเช่น หากเราเพิ่มกฎสำหรับแบบอักษรในย่อหน้า

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

จากนั้นองค์ประกอบย่อหน้าซึ่งเป็นองค์ประกอบย่อยของ div ในแผนผังบริบทอาจมีการใช้โครงสร้างแบบอักษรเดียวกันกับองค์ประกอบหลัก หากไม่ได้ระบุกฎแบบอักษรสำหรับย่อหน้า

ใน WebKit ที่ไม่มีโครงสร้างกฎ ระบบจะข้ามการประกาศที่ตรงกัน 4 ครั้ง เริ่มจากการใช้พร็อพเพอร์ตี้ที่มีลำดับความสำคัญสูงที่ไม่สำคัญก่อน (พร็อพเพอร์ตี้ที่ควรนำไปใช้ก่อนเนื่องจากพร็อพเพอร์ตี้อื่นๆ จะขึ้นอยู่กับพร็อพเพอร์ตี้ดังกล่าว เช่น การแสดงผล) จากนั้นตามด้วยพร็อพเพอร์ตี้ที่มีลำดับความสำคัญสูง ส่วนที่มีลำดับความสำคัญปกติไม่สำคัญ แล้วจึงใช้กฎที่มีลำดับความสำคัญปกติ ซึ่งหมายความว่าพร็อพเพอร์ตี้ที่ปรากฏหลายครั้งจะได้รับการแก้ไขตามลำดับการเรียงซ้อนที่ถูกต้อง ชัยชนะครั้งสุดท้าย

สรุปก็คือ การแชร์วัตถุลักษณะ (โครงสร้างทั้งหมดหรือบางส่วนภายใน) ช่วยแก้ปัญหาที่ 1 และ 3 ได้ โครงสร้างกฎของ Firefox ยังช่วยให้นำพร็อพเพอร์ตี้ไปใช้ตามลำดับที่ถูกต้องได้อีกด้วย

การควบคุมกฎเพื่อให้จับคู่กันได้ง่ายๆ

แหล่งที่มาของกฎสไตล์มีหลายแห่งดังนี้

  1. กฎ CSS ในสไตล์ชีตภายนอกหรือในองค์ประกอบของรูปแบบ css p {color: blue}
  2. แอตทริบิวต์ของรูปแบบแทรกในบรรทัด เช่น html <p style="color: blue" />
  3. แอตทริบิวต์ที่มองเห็นของ HTML (ซึ่งแมปกับกฎรูปแบบที่เกี่ยวข้อง) html <p bgcolor="blue" /> 2 รายการสุดท้ายจับคู่กับองค์ประกอบได้ง่ายเนื่องจากเขาเป็นเจ้าของแอตทริบิวต์ของสไตล์ และแอตทริบิวต์ HTML สามารถแมปโดยใช้องค์ประกอบเป็นคีย์ได้

ตามที่ได้แจ้งไว้ก่อนหน้านี้ในปัญหา #2 การจับคู่กฎ CSS อาจทำได้ยากขึ้น เพื่อแก้ความยาก กฎจะถูกปรับเปลี่ยนให้เข้าถึงได้ง่าย

หลังจากแยกวิเคราะห์สไตล์ชีตแล้ว ระบบจะเพิ่มกฎลงในการจับคู่แฮชหลายรายการตามตัวเลือก โดยมีแผนที่ตามรหัส ชื่อคลาส ตามชื่อแท็ก และแผนที่ทั่วไปสำหรับสิ่งต่างๆ ที่ไม่เข้ากับหมวดหมู่เหล่านั้น หากตัวเลือกเป็นรหัส ระบบจะเพิ่มกฎไปยังแมปรหัส หากเป็นคลาส ก็จะเพิ่มลงในแมปชั้นเรียน เป็นต้น

การปรับแต่งนี้ช่วยให้จับคู่กฎได้ง่ายขึ้นมาก คุณไม่จำเป็นต้องดูในการประกาศทุกครั้ง เนื่องจากเราสามารถดึงกฎที่เกี่ยวข้องสำหรับองค์ประกอบออกจากแผนที่ได้ การเพิ่มประสิทธิภาพนี้จะลบกฎกว่า 95% เพื่อไม่ให้มีการพิจารณากฎเหล่านั้นในกระบวนการจับคู่(4.1)

ลองดูตัวอย่างกฎของรูปแบบต่อไปนี้

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

ระบบจะแทรกกฎข้อแรกลงในการแมปชั้นเรียน รายการที่ 2 ลงในการแมปรหัส รายการที่ 3 ลงในการแมปแท็ก

สำหรับส่วนย่อย HTML ต่อไปนี้

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

ก่อนอื่นเราจะลองค้นหากฎสำหรับองค์ประกอบ p แมปคลาสจะมีคีย์ "error" ซึ่งพบกฎ "p.error" องค์ประกอบ div จะมีกฎที่เกี่ยวข้องในการจับคู่รหัส (คีย์คือรหัส) และแมปแท็ก ดังนั้นสิ่งเดียวที่เหลืออยู่คือการค้นหาว่ากฎใดซึ่งคีย์ที่ดึงมานั้นตรงกันจริงๆ

ตัวอย่างเช่น หากกฎสำหรับ div คือ

table div {margin: 5px}

แท็กจะยังคงดึงมาจากแมปแท็ก เนื่องจากคีย์เป็นตัวเลือกด้านขวาสุด แต่ไม่ตรงกับองค์ประกอบ div ของเราที่ไม่มีระดับบนของตาราง

ทั้ง WebKit และ Firefox จะจัดการเรื่องนี้

ลำดับการเรียงซ้อนของสไตล์ชีต

ออบเจ็กต์รูปแบบมีพร็อพเพอร์ตี้ที่สอดคล้องกับแอตทริบิวต์ที่มองเห็นทุกรายการ (แอตทริบิวต์ CSS ทั้งหมดแต่เป็นแบบทั่วไปมากกว่า) หากกฎที่ตรงกันไม่ได้กำหนดพร็อพเพอร์ตี้ ออบเจ็กต์รูปแบบองค์ประกอบระดับบนสุดจะรับค่าพร็อพเพอร์ตี้บางรายการได้ ส่วนที่พักอื่นๆ มีค่าเริ่มต้น

ปัญหาจะเริ่มเมื่อมีคําจํากัดความมากกว่า 1 รายการ มาดูลําดับการเรียงซ้อนกันเพื่อแก้ปัญหา

การประกาศสำหรับคุณสมบัติของสไตล์อาจปรากฏในสไตล์ชีตหลายรายการ และหลายครั้งในสไตล์ชีท ซึ่งหมายความว่าลำดับของการใช้กฎเป็นสิ่งที่สำคัญมาก ซึ่งเรียกว่าลำดับ "การเรียงซ้อน" ตามข้อกำหนด CSS2 ลำดับการเรียงซ้อนคือ (จากต่ำไปสูง)

  1. การประกาศเบราว์เซอร์
  2. การประกาศปกติของผู้ใช้
  3. การประกาศปกติของผู้เขียน
  4. เขียนประกาศสําคัญ
  5. ประกาศสำคัญของผู้ใช้

การประกาศของเบราว์เซอร์นั้นสำคัญน้อยที่สุด และผู้ใช้จะลบล้างผู้เขียนก็ต่อเมื่อการประกาศนั้นได้รับการทำเครื่องหมายว่าสำคัญเท่านั้น ระบบจะจัดเรียงประกาศที่มีลำดับเดียวกันตามความเฉพาะเจาะจง จากนั้นจึงระบุลำดับ ระบบจะแปลแอตทริบิวต์ภาพ HTML เป็นการประกาศ CSS ที่ตรงกัน ซึ่งถือเป็นกฎผู้เขียนที่มีลำดับความสำคัญต่ำ

ลักษณะเฉพาะ

ความจำเพาะของตัวเลือกจะกำหนดตามข้อกำหนด CSS2 ดังนี้

  1. นับ 1 หากการประกาศเป็นแอตทริบิวต์ "style" แทนที่จะเป็นกฎที่มีตัวเลือก หรือไม่ก็ 0 (= a)
  2. นับจำนวนแอตทริบิวต์รหัสในตัวเลือก (= b)
  3. นับจำนวนแอตทริบิวต์อื่นๆ และคลาส Pseudo ในตัวเลือก (= c)
  4. นับจำนวนชื่อองค์ประกอบและองค์ประกอบจำลองในตัวเลือก (= d)

การเชื่อมตัวเลข 4 ตัวเข้าด้วยกัน a-b-c-d (ในระบบตัวเลขที่มีฐานใหญ่) จะทำให้ได้ค่าความจำเพาะ

ฐานตัวเลขที่คุณจำเป็นต้องใช้จะกำหนดจากจำนวนสูงสุดที่คุณมีในหมวดหมู่ใดหมวดหมู่หนึ่ง

เช่น หาก a=14 จะใช้ฐานสิบหกได้ ในกรณีที่ไม่เกิดขึ้นบ่อยซึ่ง a=17 คุณจะต้องมีฐานตัวเลข 17 หลัก สถานการณ์ในภายหลังอาจเกิดขึ้นกับตัวเลือกอย่างนี้ html content 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 ใช้แฟล็กที่ทำเครื่องหมายหากมีการโหลดสไตล์ชีตระดับบนสุดทั้งหมด (รวมถึง @import) ถ้ารูปแบบยังโหลดไม่สมบูรณ์เมื่อแนบ ระบบจะใช้ตัวยึดตำแหน่งและทำเครื่องหมายไว้ในเอกสาร และจะมีการคำนวณสไตล์ใหม่เมื่อโหลดสไตล์ชีตแล้ว

เลย์เอาต์

เมื่อสร้างและเพิ่มไปยังแผนผังโหมดการแสดงผลแล้ว โหมดแสดงภาพจะไม่มีตำแหน่งและขนาด การคำนวณค่าเหล่านี้เรียกว่าการออกแบบหรือการจัดเรียงใหม่

HTML ใช้โมเดลเลย์เอาต์แบบโฟลว์ ซึ่งหมายความว่าส่วนใหญ่แล้วจะสามารถคำนวณเรขาคณิตได้บัตรผ่านเดียว โดยทั่วไปแล้ว องค์ประกอบที่อยู่ภายหลัง "ในโฟลว์" จะไม่มีผลต่อเรขาคณิตขององค์ประกอบที่อยู่ก่อนหน้า "ในโฟลว์" ดังนั้นเลย์เอาต์จึงสามารถเรียงจากซ้ายไปขวาและบนลงล่างผ่านเอกสารได้ แต่มีข้อยกเว้น เช่น ตาราง HTML อาจต้องส่งผ่านมากกว่า 1 ครั้ง

ระบบพิกัดจะสัมพัทธ์กับเฟรมราก ใช้พิกัดด้านบนและด้านซ้าย

เลย์เอาต์เป็นกระบวนการที่ต้องทำซ้ำ โดยจะเริ่มต้นที่ตัวแสดงผลรูทซึ่งสอดคล้องกับองค์ประกอบ <html> ของเอกสาร HTML เลย์เอาต์ยังคงวนซ้ำไปเรื่อยๆ ผ่านลำดับชั้นของเฟรมบางส่วนหรือทั้งหมด โดยการประมวลผลข้อมูลทางเรขาคณิตสำหรับโหมดแสดงภาพแต่ละโหมดที่ต้องใช้

ตำแหน่งของตัวแสดงผลระดับรูทคือ 0,0 และมิติข้อมูลคือวิวพอร์ต ซึ่งเป็นส่วนที่มองเห็นได้ของหน้าต่างเบราว์เซอร์

โหมดแสดงภาพทั้งหมดมีเมธอด "เลย์เอาต์" หรือ "การจัดเรียงใหม่" โดยโหมดแสดงภาพแต่ละโหมดจะเรียกเมธอดเลย์เอาต์ของหน่วยย่อยที่ต้องการเลย์เอาต์

ระบบบิตสกปรก

และเพื่อไม่ให้การจัดวางแบบเต็มสำหรับการเปลี่ยนแปลงเล็กๆ น้อยๆ ทุกครั้ง เบราว์เซอร์จะใช้ระบบ "บิตสกปรก" โหมดแสดงภาพที่มีการเปลี่ยนแปลงหรือเพิ่มจะทำเครื่องหมายตนเองและองค์ประกอบย่อยว่า "สกปรก": ต้องมีเลย์เอาต์

มีแฟล็ก 2 แบบ ได้แก่ "สกปรก" และ "เด็กๆ สกปรก" ซึ่งหมายความว่าแม้ตัวแสดงผลอาจใช้งานได้ดี แต่ก็มีเด็กอย่างน้อย 1 คนที่ต้องมีเลย์เอาต์

เลย์เอาต์ส่วนกลางและส่วนเพิ่ม

สามารถทริกเกอร์เลย์เอาต์ได้ทั่วทั้งโครงสร้างการแสดงผลทั้งหมด ซึ่งก็คือเลย์เอาต์แบบ "ส่วนกลาง" ซึ่งอาจเกิดจากสาเหตุต่อไปนี้

  1. การเปลี่ยนรูปแบบส่วนกลางที่มีผลต่อโหมดแสดงภาพทั้งหมด เช่น การเปลี่ยนขนาดแบบอักษร
  2. เนื่องจากหน้าจอถูกปรับขนาด

คุณสามารถใส่เลย์เอาต์เพิ่มได้ โดยจะจัดวางเฉพาะโหมดแสดงภาพที่สกปรกเท่านั้น (ซึ่งอาจทำให้เกิดความเสียหายบางส่วน ซึ่งต้องใช้เลย์เอาต์เพิ่มเติม)

ระบบจะทริกเกอร์เลย์เอาต์ที่เพิ่มขึ้น (ไม่พร้อมกัน) เมื่อโหมดแสดงภาพสกปรก เช่น เมื่อมีการผนวกโหมดแสดงภาพใหม่ต่อท้ายโครงสร้างการแสดงผลหลังจากเนื้อหาเพิ่มเติมมาจากเครือข่ายและได้รับการเพิ่มลงในแผนผัง DOM

เลย์เอาต์ส่วนเพิ่ม
รูปที่ 18: เลย์เอาต์ที่เพิ่มขึ้น - จัดวางเฉพาะโหมดแสดงภาพที่สกปรกและบุตรหลาน

เลย์เอาต์แบบไม่พร้อมกันและซิงโครนัส

เลย์เอาต์ส่วนที่เพิ่มขึ้นจะทำแบบไม่พร้อมกัน Firefox จัดคิว "คำสั่งจัดเรียง" เพื่อเพิ่มเลย์เอาต์และตัวจัดตารางเวลาจะทริกเกอร์การดำเนินการเป็นกลุ่มของคำสั่งเหล่านี้ นอกจากนี้ WebKit ยังมีตัวจับเวลาที่เรียกใช้เลย์เอาต์แบบเพิ่มขึ้นเรื่อยๆ กล่าวคือ แผนผังจะข้ามผ่านและโหมดแสดงภาพที่ "สกปรก" ถูกจัดวาง

สคริปต์ที่ขอข้อมูลรูปแบบ เช่น "offsetHeight" สามารถทริกเกอร์การออกแบบที่เพิ่มขึ้นแบบพร้อมกัน

โดยปกติแล้วเลย์เอาต์ส่วนกลางจะทริกเกอร์แบบพร้อมกัน

บางครั้งระบบจะทริกเกอร์เลย์เอาต์เป็นโค้ดเรียกกลับหลังจากเลย์เอาต์เริ่มต้น เนื่องจากแอตทริบิวต์บางรายการ เช่น ตําแหน่งการเลื่อนมีการเปลี่ยนแปลง

การเพิ่มประสิทธิภาพ

เมื่อมีการทริกเกอร์เลย์เอาต์ด้วย "การปรับขนาด" หรือการเปลี่ยนตำแหน่งโหมดแสดงภาพ(ไม่ใช่ขนาด) ระบบจะนำขนาดการแสดงผลมาจากแคชและไม่คำนวณใหม่...

ในบางกรณีจะมีการแก้ไขเฉพาะแผนผังย่อยและเลย์เอาต์ไม่ได้เริ่มต้นจากรูท ซึ่งเกิดขึ้นได้ในกรณีที่การเปลี่ยนแปลงเป็นการเปลี่ยนแปลงเฉพาะที่และไม่ส่งผลกระทบต่อสภาพแวดล้อม เช่น ข้อความที่แทรกลงในช่องข้อความ (มิฉะนั้นการกดแป้นพิมพ์ทุกครั้งจะทำให้เกิดเลย์เอาต์โดยเริ่มจากรูท)

กระบวนการของเลย์เอาต์

เลย์เอาต์มักมีรูปแบบต่อไปนี้

  1. ตัวแสดงผลระดับบนสุดจะกำหนดความกว้างของตัวเอง
  2. ผู้ปกครองมีลูกและ
    1. วางตัวแสดงผลย่อย (ตั้งค่า x และ y)
    2. เรียกใช้เลย์เอาต์ย่อยหากจำเป็น - การจัดวางอาจสกปรก หรือเรากำลังอยู่ในเลย์เอาต์ส่วนกลาง หรือด้วยเหตุผลอื่น ซึ่งจะคำนวณความสูงของบุตรหลาน
  3. ผู้เผยแพร่โฆษณาหลักใช้ความสูงสะสมของหน่วยย่อย รวมถึงความสูงของระยะขอบและระยะห่างจากขอบเพื่อตั้งค่าความสูงของตัวเอง ซึ่งหน่วยโฆษณาหลักของตัวแสดงผลระดับบนสุดจะใช้ค่านี้
  4. ตั้งค่าบิตสกปรกเป็น false

Firefox ใช้ออบเจ็กต์ "state" (nsHTMLReflowState) เป็นพารามิเตอร์ในเลย์เอาต์ (เรียกว่า "reflow") ส่วนหนึ่งของรัฐนี้รวมถึงความกว้างของครอบครัว

เอาต์พุตของเลย์เอาต์ของ Firefox คือออบเจ็กต์ "metry" (nsHTMLReflowMetrics) ซึ่งจะมีความสูงที่คำนวณได้ของโหมดแสดงภาพ

การคำนวณความกว้าง

ความกว้างของโหมดแสดงภาพจะคำนวณโดยใช้ความกว้างของบล็อกคอนเทนเนอร์ คุณสมบัติ "ความกว้าง" ของรูปแบบของโหมดแสดงภาพ ระยะขอบ และขอบ

ตัวอย่างเช่น ความกว้างของ div ต่อไปนี้

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

จะคำนวณโดย WebKit ดังต่อไปนี้(คลาส RenderBox วิธีการ calcWidth):

  • ความกว้างของคอนเทนเนอร์คือค่าสูงสุดของคอนเทนเนอร์ที่มีอยู่และ 0 availableWidth ในกรณีนี้คือ contentWidth ซึ่งมีวิธีคำนวณดังนี้
clientWidth() - paddingLeft() - paddingRight()

clientWidth และ clientHeight แสดงภายในของออบเจ็กต์ โดยไม่รวมเส้นขอบและแถบเลื่อน

  • ความกว้างขององค์ประกอบคือแอตทริบิวต์รูปแบบ "width" ซึ่งจะคำนวณเป็นค่าสัมบูรณ์โดยการคำนวณเปอร์เซ็นต์ของความกว้างของคอนเทนเนอร์

  • เพิ่มเส้นขอบแนวนอนและระยะห่างจากขอบแล้ว

ก่อนหน้านี้นี่ยังเป็นการคำนวณ "ความกว้างที่ต้องการ" ขณะนี้จะคำนวณความกว้างต่ำสุดและสูงสุด

หากความกว้างที่ต้องการมากกว่าความกว้างสูงสุด ระบบจะใช้ความกว้างสูงสุด หากค่าน้อยกว่าความกว้างขั้นต่ำ (หน่วยที่เล็กที่สุดที่แบ่งไม่ได้) ระบบจะใช้ความกว้างขั้นต่ำ

จะมีการแคชค่าในกรณีที่ต้องมีเลย์เอาต์ แต่ความกว้างจะไม่เปลี่ยนแปลง

การขึ้นบรรทัดใหม่

เมื่อโหมดแสดงภาพที่จุดกึ่งกลางของเลย์เอาต์ตัดสินใจว่าจะหยุดการทำงาน โหมดแสดงภาพจะหยุดและเผยแพร่ไปยังระดับบนสุดของเลย์เอาต์ที่จำเป็นต้องแยกภาพออก ระดับบนสุดจะสร้างเลย์เอาต์การแสดงผลและการเรียกใช้เพิ่มเติม

ภาพวาด

ในขั้นตอนการวาดภาพ ระบบจะข้ามผ่านต้นไม้แสดงผลและมีการเรียกใช้เมธอด "paint()" ของโหมดแสดงภาพเพื่อแสดงเนื้อหาบนหน้าจอ การลงสีใช้คอมโพเนนต์โครงสร้างพื้นฐานของ UI

ทั่วโลกและเพิ่มขึ้น

ภาพวาดก็ทำให้เกิดได้ทั่วโลกเช่นเดียวกับเลย์เอาต์ กล่าวคือ การทาสีต้นไม้ทั้งต้นหรือเพิ่มขึ้นทีละน้อย ในการวาดภาพส่วนเพิ่ม โหมดแสดงภาพบางส่วนจะเปลี่ยนไปในลักษณะที่ไม่มีผลต่อทั้งต้นไม้ โหมดแสดงภาพที่เปลี่ยนแปลงจะทำให้รูปสี่เหลี่ยมผืนผ้าบนหน้าจอไม่ถูกต้อง ซึ่งจะทำให้ระบบปฏิบัติการมองว่าเป็น "บริเวณที่สกปรก" และสร้างเหตุการณ์ "สี" ระบบปฏิบัติการทำงานอย่างชาญฉลาดและรวมหลายภูมิภาคเป็นหนึ่งเดียว ใน Chrome จะมีความซับซ้อนมากกว่าเนื่องจากตัวแสดงผลอยู่ในกระบวนการที่ต่างจากกระบวนการหลัก Chrome จะจำลองลักษณะการทำงานของระบบปฏิบัติการในระดับหนึ่ง งานนำเสนอจะคอยฟังเหตุการณ์เหล่านี้และมอบสิทธิ์ข้อความไปยังรูทการแสดงผล ต้นไม้จะข้ามผ่านจนกว่าจะถึงโหมดแสดงภาพที่เกี่ยวข้อง มันจะเปลี่ยนสีใหม่เอง (ซึ่งโดยทั่วไปจะเป็นลูกๆ ของมัน)

ลำดับภาพวาด

CSS2 กำหนดลำดับของกระบวนการทาสี จริงๆ แล้วนี่คือลำดับการจัดเรียงองค์ประกอบในบริบทการกองซ้อน ลำดับนี้มีผลต่อการวาดภาพเนื่องจากระบายสีจากด้านหลังถึงด้านหน้า ลำดับการเรียงซ้อนของตัวแสดงผลบล็อกคือ

  1. สีพื้นหลัง
  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 คำว่า Canvas จะหมายถึง "พื้นที่ที่แสดงผลโครงสร้างการจัดรูปแบบ" ซึ่งเป็นที่ที่เบราว์เซอร์ระบายสีเนื้อหา

ผืนผ้าใบไม่มีที่สิ้นสุดสำหรับแต่ละมิติของพื้นที่ แต่เบราว์เซอร์จะเลือกความกว้างเริ่มต้นตามขนาดของวิวพอร์ต

ตามที่ระบุใน www.w3.org/TR/CSS2/zindex.html ผืนผ้าใบจะโปร่งใสหากอยู่ภายในพื้นที่อื่น และในกรณีที่เบราว์เซอร์ไม่ได้กำหนดสีไว้

โมเดลกล่อง CSS

โมเดลกล่อง 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

รูปแบบการวางตำแหน่ง

มี 3 รูปแบบ ดังนี้

  1. ปกติ: วัตถุจะมีตำแหน่งตามตำแหน่งในเอกสาร ซึ่งหมายความว่าตำแหน่งในทรีการแสดงผลจะเหมือนกับที่ของต้นไม้ในแผนผัง DOM และจัดวางตามประเภทและขนาดของกล่อง
  2. ลอย: วัตถุจะวางมีลักษณะไหลแบบปกติก่อน แล้วจึงเคลื่อนที่ไปทางซ้ายหรือขวามากที่สุดเท่าที่จะทำได้
  3. สัมบูรณ์: ออบเจ็กต์ถูกวางในทรีการแสดงผลในตำแหน่งที่แตกต่างจากในทรี DOM

รูปแบบการกำหนดตำแหน่งจะกำหนดโดยพร็อพเพอร์ตี้ "ตำแหน่ง" และแอตทริบิวต์ "ทศนิยม"

  • แบบภาพนิ่งและสัมพัทธ์ทำให้เกิดการไหลตามปกติ
  • การกำหนดตำแหน่งแบบสัมบูรณ์และคงที่

สำหรับการจัดตำแหน่งคงที่ จะไม่มีการกำหนดตำแหน่งใดๆ และใช้การจัดตำแหน่งเริ่มต้น สำหรับรูปแบบอื่นๆ ผู้เขียนจะระบุตำแหน่งซึ่งได้แก่ ด้านบน ด้านล่าง ด้านซ้าย ด้านขวา

วิธีวางกล่องจะกำหนดโดยสิ่งต่อไปนี้

  • ประเภทกล่อง
  • ขนาดกล่อง
  • รูปแบบการวางตำแหน่ง
  • ข้อมูลภายนอก เช่น ขนาดรูปภาพและขนาดของหน้าจอ

ประเภทกล่อง

ช่องบล็อก: สร้างบล็อก - มีสี่เหลี่ยมผืนผ้าของตัวเองในหน้าต่างเบราว์เซอร์

กล่องบล็อก
รูปที่ 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: การจัดตำแหน่งคงที่

การนำเสนอแบบหลายชั้น

ซึ่งระบุโดยพร็อพเพอร์ตี้ CSS ของ z-index โดยแสดงถึงมิติที่ 3 ของช่อง นั่นคือ ตำแหน่งตาม "แกน z"

กล่องจะแบ่งออกเป็นชุดย่อย (เรียกว่า บริบทซ้อนกัน) ในแต่ละกอง องค์ประกอบย้อนกลับจะทาสีก่อน และองค์ประกอบข้างหน้าอยู่ด้านบนสุดใกล้กับผู้ใช้มากขึ้น ในกรณีที่ซ้อนทับกัน องค์ประกอบหลักจะซ่อนองค์ประกอบเดิม

กองจะเรียงลำดับตามพร็อพเพอร์ตี้ดัชนี z กล่องที่มีพร็อพเพอร์ตี้ "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 สีแดงนำหน้าสีเขียวในมาร์กอัป และควรแสดงก่อนในขั้นตอนปกติ แต่พร็อพเพอร์ตี้ของ z-index จะสูงกว่า ดังนั้นจึงอยู่ต่อจากในกลุ่มที่ยึดกล่องรากมากกว่า

แหล่งข้อมูล

  1. สถาปัตยกรรมเบราว์เซอร์

    1. Grosskurth, อลัน สถาปัตยกรรมอ้างอิงสำหรับเว็บเบราว์เซอร์ (pdf)
    2. Gupta, Vineet วิธีการทำงานของเบราว์เซอร์ - ตอนที่ 1 - สถาปัตยกรรม
  2. กำลังแยกวิเคราะห์

    1. Aho, Sethi, Ullman, ผู้เรียบเรียง: Principles, Techniques, and Tools (หรือ "มังกรหนังสือ"), Addison-Wesley, 1986
    2. Rick Jelliffe The Bold and the Beautiful: ฉบับร่างใหม่ 2 รายการสำหรับ HTML 5
  3. Firefox

    1. L. David Baron, HTML และ CSS ที่เร็วขึ้น: Layout Engine Internals สำหรับนักพัฒนาเว็บ
    2. L. David Baron, HTML และ CSS ที่เร็วขึ้น: Layout Engine Internals สำหรับนักพัฒนาเว็บ (วิดีโอการพูดคุยเรื่องเทคโนโลยีของ Google)
    3. L. David Baron เครื่องมือเลย์เอาต์ของ Mozilla
    4. L. David Baron, เอกสารระบบสไตล์ Mozilla
    5. Chris Waterson, หมายเหตุเกี่ยวกับ HTML Reflow
    6. Chris Waterson, ภาพรวมของตุ๊กแก
    7. Alexander Larsson, ช่วงชีวิตของคำขอ HTML HTTP
  4. WebKit

    1. David Hyatt, การติดตั้งใช้งาน CSS(ตอนที่ 1)
    2. David Hyatt, ภาพรวมของ WebCore
    3. David Hyatt, การแสดงภาพ WebCore
    4. David Hyatt, The FOUC Problem
  5. ข้อกำหนด W3C

    1. ข้อกำหนดเฉพาะสำหรับ HTML 4.01
    2. ข้อกำหนด W3C HTML5
    3. ข้อกำหนดเกี่ยวกับ Cascading Style Sheets ระดับที่ 2 ครั้งที่ 1 (CSS 2.1)
  6. วิธีการสร้างเบราว์เซอร์

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

คำแปล

หน้านี้ได้รับการแปลเป็นภาษาญี่ปุ่น 2 ครั้ง:

คุณดูคำแปลที่โฮสต์อยู่ภายนอกเป็นภาษาเกาหลีและตุรกีได้

ขอบคุณทุกคน