Cara kerja browser

Di balik layar browser web modern

Pengantar

Pengantar komprehensif tentang operasi internal WebKit dan Gecko ini adalah hasil dari banyak penelitian yang dilakukan oleh pengembang Israel, Tali Garsiel. Selama beberapa bertahun-tahun, dia meninjau semua data yang diterbitkan tentang internal {i>browser<i} dan menghabiskan banyak waktu membaca kode sumber {i>browser<i} web. Ia menulis:

Sebagai pengembang web, mempelajari bagian internal operasi browser membantu Anda membuat keputusan yang lebih baik dan mengetahui pembenaran di balik pengembangan praktik terbaik kami. Meskipun dokumen ini cukup panjang, sebaiknya Anda meluangkan waktu untuk mempelajarinya. Anda akan senang Anda melakukannya.

Paul Irish, Developer Relations Chrome

Pengantar

{i>Browser<i} internet adalah perangkat lunak yang paling banyak digunakan. Dalam pengantar ini, saya menjelaskan bagaimana keduanya bekerja di balik layar. Kita akan melihat apa yang terjadi saat Anda mengetik google.com di kolom URL hingga Anda melihat halaman Google di layar browser.

{i>Browser<i} yang akan kita bicarakan

Ada lima browser utama yang digunakan di desktop saat ini: Chrome, Internet Explorer, Firefox, Safari, dan Opera. Pada ponsel, browser utama adalah Browser Android, iPhone, Opera Mini dan Opera Mobile, Browser UC, browser Nokia S40/S60, dan Chrome, semuanya, kecuali browser Opera, didasarkan pada WebKit. Saya akan memberikan contoh dari browser {i>open source<i} Firefox dan Chrome, serta Safari (yang sebagian merupakan {i>open source<i}). Menurut statistik StatCounter (per Juni 2013), Chrome, Firefox dan Safari, menyumbang sekitar 71% dari penggunaan browser desktop global. Di perangkat seluler, penggunaan Browser Android, iPhone, dan Chrome mencakup sekitar 54%.

Fungsi utama browser

Fungsi utama browser adalah menyajikan resource web yang Anda pilih, dengan memintanya dari server dan menampilkannya di jendela browser. Resource ini biasanya berupa dokumen HTML, tetapi juga dapat berupa PDF, gambar, atau jenis konten lainnya. Lokasi resource ditetapkan oleh pengguna menggunakan URI (Uniform Resource Identifier).

Cara browser menafsirkan dan menampilkan file HTML ditentukan dalam spesifikasi HTML dan CSS. Spesifikasi ini dikelola oleh organisasi W3C (World Wide Web Consortium), yang merupakan organisasi standar untuk web. Selama bertahun-tahun, browser hanya mematuhi sebagian spesifikasi dan mengembangkan ekstensinya sendiri. Hal tersebut menyebabkan masalah kompatibilitas yang serius bagi penulis web. Saat ini, sebagian besar browser kurang lebih sesuai dengan spesifikasi.

Antarmuka pengguna browser memiliki banyak kesamaan satu sama lain. Elemen antarmuka pengguna yang umum adalah:

  1. Kolom URL untuk menyisipkan URI
  2. Tombol kembali dan maju
  3. Opsi bookmark
  4. Tombol muat ulang dan berhenti untuk memuat ulang atau menghentikan pemuatan dokumen saat ini
  5. Tombol Beranda yang membawa Anda ke halaman beranda

Anehnya, antarmuka pengguna browser itu tidak disebutkan dalam spesifikasi formal apa pun, itu hanya berasal dari praktik baik yang dibentuk selama bertahun-tahun pengalaman dan oleh browser yang meniru satu sama lain. Spesifikasi HTML5 tidak menentukan elemen UI yang harus dimiliki browser, tetapi mencantumkan beberapa elemen umum. Di antaranya adalah kolom URL, status bar, dan toolbar. Tentu saja ada fitur yang unik untuk browser tertentu seperti pengelola download Firefox.

Infrastruktur tingkat tinggi

Komponen utama browser adalah:

  1. Antarmuka pengguna: antarmuka ini mencakup kolom URL, tombol kembali/maju, menu bookmark, dll. Setiap bagian browser akan ditampilkan, kecuali jendela tempat Anda melihat halaman yang diminta.
  2. Mesin browser: tindakan marshal antara UI dan mesin rendering.
  3. Mesin rendering: bertanggung jawab untuk menampilkan konten yang diminta. Misalnya, jika konten yang diminta adalah HTML, mesin rendering akan mengurai HTML dan CSS, dan menampilkan konten yang diuraikan di layar.
  4. Networking: untuk panggilan jaringan seperti permintaan HTTP, yang menggunakan implementasi berbeda untuk platform berbeda di balik antarmuka yang tidak bergantung pada platform.
  5. Backend UI: digunakan untuk menggambar widget dasar seperti kotak kombinasi dan jendela. Backend ini mengekspos antarmuka generik yang tidak spesifik untuk platform. Di bawahnya menggunakan metode antarmuka pengguna sistem operasi.
  6. Penerjemah JavaScript. Digunakan untuk mengurai dan mengeksekusi kode JavaScript.
  7. Penyimpanan Data. Lapisan ini adalah lapisan persistensi. Browser mungkin perlu menyimpan semua jenis data secara lokal, seperti cookie. Browser juga mendukung mekanisme penyimpanan seperti localStorage, IndexedDB, WebSQL, dan FileSystem.
Komponen browser
Gambar 1: Komponen browser

Perlu diketahui bahwa browser seperti Chrome menjalankan beberapa instance mesin rendering: satu untuk setiap tab. Setiap tab berjalan dalam proses terpisah.

Mesin rendering

Tanggung jawab mesin rendering adalah... Rendering, yaitu menampilkan konten yang diminta pada layar browser.

Secara default, mesin rendering dapat menampilkan dokumen dan gambar HTML dan XML. Platform ini dapat menampilkan jenis data lain melalui plugin atau ekstensi; misalnya, menampilkan dokumen PDF menggunakan plugin penampil PDF. Akan tetapi, dalam bab ini kita akan berfokus pada kasus penggunaan utama: menampilkan HTML dan gambar yang diformat menggunakan CSS.

Browser yang berbeda menggunakan mesin render yang berbeda: Internet Explorer menggunakan Trident, Firefox menggunakan Gecko, Safari menggunakan WebKit. Chrome dan Opera (dari versi 15) menggunakan Blink, sebuah fork dari WebKit.

WebKit adalah mesin rendering open source yang dimulai sebagai mesin untuk platform Linux dan dimodifikasi oleh Apple untuk mendukung Mac dan Windows.

Alur utama

Mesin rendering akan mulai mendapatkan isi dokumen yang diminta dari lapisan jaringan. Hal ini biasanya akan dilakukan dalam potongan 8 kB.

Setelah itu, berikut ini adalah alur dasar dari mesin rendering:

Alur dasar mesin rendering
Gambar 2: Alur dasar mesin rendering

Mesin rendering akan mulai mengurai dokumen HTML dan mengonversi elemen menjadi node DOM pada hierarki yang disebut "hierarki konten". Mesin akan mengurai data gaya, baik dalam file CSS eksternal maupun dalam elemen gaya. Informasi gaya visual bersama dengan petunjuk visual di HTML akan digunakan untuk membuat hierarki lain: hierarki render.

Pohon render berisi persegi panjang dengan atribut visual seperti warna dan dimensi. Persegi panjang berada dalam urutan yang benar untuk ditampilkan di layar.

Setelah dibuat, hierarki render akan melalui "layout" {i>checkout<i}. Ini berarti memberi setiap {i>node<i} koordinat yang tepat di mana ia akan muncul pada layar. Tahap berikutnya adalah lukisan - hierarki render akan dilalui dan setiap node akan digambar menggunakan lapisan backend UI.

Penting untuk dipahami bahwa ini adalah proses bertahap. Untuk pengalaman pengguna yang lebih baik, mesin rendering akan mencoba menampilkan konten pada layar sesegera mungkin. Kode ini tidak akan menunggu hingga semua HTML diuraikan sebelum mulai membangun dan menata letak hierarki render. Bagian dari konten akan diuraikan dan ditampilkan, sementara proses berlanjut dengan konten lain yang terus berasal dari jaringan.

Contoh alur utama

Alur utama WebKit.
Gambar 3: Alur utama WebKit
Alur utama mesin rendering Gecko Mozilla.
Gambar 4: Alur utama mesin rendering Gecko Mozilla

Dari gambar 3 dan 4 Anda dapat melihat bahwa meskipun WebKit dan Gecko menggunakan terminologi yang sedikit berbeda, alurnya pada dasarnya sama.

Gecko menyebut hierarki elemen yang diformat secara visual sebagai "Frame tree". Setiap elemen adalah {i>frame<i}. WebKit menggunakan istilah "Render Tree" dan terdiri dari "Objek Render". WebKit menggunakan istilah "tata letak" untuk penempatan elemen, sedangkan Gecko menyebutnya "Reflow". "Lampiran" adalah istilah WebKit untuk menghubungkan simpul DOM dan informasi visual untuk membuat pohon render. Ada sedikit perbedaan non-semantik, yaitu bahwa Gecko memiliki lapisan tambahan antara HTML dan hierarki DOM. Hal ini disebut "content sink" dan merupakan factory untuk membuat elemen DOM. Kita akan membahas setiap bagian dari alurnya:

Penguraian - umum

Karena penguraian adalah proses yang sangat signifikan dalam mesin render, kita akan membahasnya sedikit lebih dalam. Mari kita mulai dengan sedikit pengantar tentang penguraian.

Mengurai dokumen berarti menerjemahkannya ke struktur yang dapat digunakan kode tersebut. Hasil penguraian biasanya berupa pohon node yang mewakili struktur dokumen. Ini disebut pohon penguraian atau pohon sintaksis.

Misalnya, mengurai ekspresi 2 + 3 - 1 dapat menampilkan hierarki ini:

Simpul pohon ekspresi matematika.
Gambar 5: node hierarki ekspresi matematika

Tata Bahasa

Penguraian didasarkan pada aturan sintaksis yang dipatuhi dokumen: bahasa atau format dokumen yang ditulis. Setiap format yang dapat Anda urai harus memiliki tata bahasa deterministik yang terdiri dari kosakata dan aturan sintaksis. Hal ini disebut tata bahasa bebas konteks. Bahasa manusia bukanlah bahasa itu dan, oleh karena itu, tidak dapat diurai dengan teknik penguraian konvensional.

Parser - Kombinasi Lexer

Penguraian dapat dipisahkan menjadi dua subproses: analisis leksikal dan analisis sintaksis.

Analisis leksikal adalah proses memecah input menjadi token. Token adalah kosakata bahasa: kumpulan elemen penyusun yang valid. Dalam bahasa manusia, kata ini akan terdiri dari semua kata yang muncul di kamus untuk bahasa tersebut.

Analisis sintaks adalah penerapan aturan sintaks bahasa.

Parser biasanya membagi pekerjaan di antara dua komponen: lexer (terkadang disebut tokenizer) yang bertanggung jawab untuk memecah input menjadi token yang valid, dan parser yang bertanggung jawab untuk menyusun hierarki penguraian dengan menganalisis struktur dokumen sesuai dengan aturan sintaksis bahasa.

Lexer tahu cara menghapus karakter yang tidak relevan seperti spasi putih dan baris baru.

Dari dokumen sumber untuk mengurai hierarki
Gambar 6: dari dokumen sumber untuk mengurai hierarki

Proses penguraian bersifat iteratif. Parser biasanya akan meminta token baru kepada lexer dan mencoba mencocokkan token tersebut dengan salah satu aturan sintaksis. Jika aturan cocok, node yang sesuai dengan token akan ditambahkan ke hierarki penguraian dan parser akan meminta token lain.

Jika tidak ada aturan yang cocok, parser akan menyimpan token secara internal, dan terus meminta token hingga aturan yang cocok dengan semua token yang disimpan secara internal ditemukan. Jika tidak ada aturan yang ditemukan, parser akan memunculkan pengecualian. Artinya, dokumen tersebut tidak valid dan berisi error sintaksis.

Terjemahan

Dalam banyak kasus, hierarki penguraian bukanlah produk akhir. Penguraian sering digunakan dalam penerjemahan: mengubah dokumen input ke format lain. Contohnya adalah kompilasi. Compiler yang mengompilasi kode sumber menjadi kode mesin terlebih dahulu akan mengurainya menjadi hierarki penguraian, lalu menerjemahkannya menjadi dokumen kode mesin.

Alur kompilasi
Gambar 7: alur kompilasi

Contoh penguraian

Pada gambar 5, kita membangun pohon penguraian dari ekspresi matematika. Mari kita coba tentukan bahasa matematika sederhana dan lihat proses penguraiannya.

Sintaksis:

  1. Elemen penyusun sintaksis bahasa adalah ekspresi, istilah, dan operasi.
  2. Bahasa kita dapat mencakup sejumlah ekspresi.
  3. Ekspresi didefinisikan sebagai "istilah" diikuti dengan "operasi" diikuti dengan istilah lain
  4. Operasi adalah token plus atau token minus
  5. Istilah adalah token bilangan bulat atau ekspresi

Mari kita analisis 2 + 3 - 1 input.

Substring pertama yang cocok dengan aturan adalah 2: menurut aturan #5, ini adalah istilah. Kecocokan kedua adalah 2 + 3: ini cocok dengan aturan ketiga: istilah diikuti dengan operasi yang diikuti dengan istilah lainnya. Hasil yang cocok berikutnya hanya akan ditekan di akhir input. 2 + 3 - 1 adalah ekspresi karena kita sudah tahu bahwa 2 + 3 adalah istilah, jadi kita memiliki istilah yang diikuti dengan operasi yang diikuti dengan istilah lainnya. 2 + + tidak akan cocok dengan aturan apa pun sehingga merupakan input yang tidak valid.

Definisi formal untuk kosakata dan sintaks

Kosakata biasanya dinyatakan oleh ekspresi reguler.

Misalnya bahasa kita akan ditentukan sebagai:

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

Seperti yang Anda lihat, bilangan bulat ditentukan oleh ekspresi reguler.

Sintaksis biasanya ditentukan dalam format yang disebut BNF. Bahasa kami akan ditentukan sebagai:

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

Kami mengatakan bahwa suatu bahasa dapat diuraikan oleh parser biasa jika tata bahasanya merupakan tata bahasa tanpa konteks. Definisi intuitif dari tata bahasa bebas konteks adalah tata bahasa yang dapat sepenuhnya dinyatakan dalam BNF. Untuk definisi formal, lihat Artikel Wikipedia tentang Tata bahasa bebas konteks

Jenis parser

Ada dua jenis parser: parser top-down dan parser bottom up. Penjelasan yang intuitif adalah parser top-down memeriksa struktur tingkat tinggi sintaksis dan mencoba menemukan kecocokan aturan. Parser bottom up dimulai dengan input dan secara bertahap mengubahnya menjadi aturan sintaksis, mulai dari aturan tingkat rendah hingga aturan tingkat tinggi terpenuhi.

Mari kita lihat bagaimana kedua jenis parser akan mengurai contoh kita.

Parser top-down akan dimulai dari aturan tingkat yang lebih tinggi: Parser akan mengidentifikasi 2 + 3 sebagai ekspresi. Alat ini kemudian mengidentifikasi 2 + 3 - 1 sebagai ekspresi (proses mengidentifikasi ekspresi berkembang, cocok dengan aturan lain, tetapi titik awalnya adalah aturan level tertinggi).

Parser bottom up akan memindai input hingga aturan cocok. Aturan ini kemudian akan mengganti input yang cocok dengan aturan. Ini akan berlanjut sampai akhir input. Ekspresi yang cocok sebagian ditempatkan di tumpukan parser.

Bertumpuk Input
2 + 3 - 1
istilah + 3 - 1
operasi berjangka 3 - 1
ekspresi - 1
operasi ekspresi 1
ekspresi -

Jenis parser bottom up ini disebut parser shift-reduce, karena input digeser ke kanan (bayangkan pointer yang menunjuk terlebih dahulu saat input dimulai dan bergerak ke kanan) dan secara bertahap dikurangi menjadi aturan sintaksis.

Membuat parser secara otomatis

Ada alat yang dapat menghasilkan parser. Anda memberi mereka tata bahasa bahasa Anda - kosakata dan aturan sintaksnya - dan mereka menghasilkan parser yang berfungsi dengan baik. Membuat parser memerlukan pemahaman mendalam tentang penguraian dan tidak mudah membuat parser yang dioptimalkan secara manual, sehingga generator parser dapat sangat berguna.

WebKit menggunakan dua generator parser yang terkenal: Flex untuk membuat lexer dan Bison untuk membuat parser (Anda mungkin menemuinya dengan nama Lex dan Yacc). Input Flex adalah file yang berisi definisi ekspresi reguler untuk token. Input Bison adalah aturan sintaksis bahasa dalam format BNF.

Parser HTML

Tugas parser HTML adalah menguraikan markup HTML ke dalam hierarki penguraian.

Tata bahasa HTML

Kosakata dan sintaks HTML ditentukan dalam spesifikasi yang dibuat oleh organisasi W3C.

Seperti yang telah kita lihat dalam pengantar penguraian, sintaks tata bahasa dapat didefinisikan secara formal menggunakan format seperti BNF.

Sayangnya semua topik parser konvensional tidak berlaku untuk HTML (saya tidak membicarakannya hanya untuk bersenang-senang - topik ini akan digunakan dalam mengurai CSS dan JavaScript). HTML tidak bisa dengan mudah didefinisikan dengan tata bahasa bebas konteks yang diperlukan oleh pengurai.

Ada format formal untuk menentukan HTML - DTD (Document Type Definition) - tetapi ini bukan tata bahasa bebas konteks.

Ini tampak aneh pada pandangan pertama; HTML agak mirip dengan XML. Ada banyak parser XML yang tersedia. Ada variasi XML HTML - {6/} - jadi apa perbedaan besarnya?

Perbedaannya adalah bahwa pendekatan HTML lebih "memaafkan": ini memungkinkan Anda menghilangkan tag tertentu (yang kemudian ditambahkan secara implisit), atau terkadang menghilangkan tag awal atau akhir, dan seterusnya. Secara keseluruhan, ini adalah "lembut" yang berbeda dari sintaks XML yang kaku dan menuntut.

Detail yang tampaknya kecil ini membuat perbedaan besar. Di satu sisi, inilah alasan utama mengapa HTML begitu populer: ini memaafkan kesalahan Anda dan memudahkan penulis web. Di sisi lain, penulisan tata bahasa formal sulit dilakukan. Jadi untuk meringkas, HTML tidak bisa diurai dengan mudah oleh parser konvensional, karena tata bahasanya tidak bebas konteks. HTML tidak dapat diuraikan oleh parser XML.

DTD HTML

Definisi HTML memiliki format DTD. Format ini digunakan untuk menentukan bahasa kelompok SGML. Format ini berisi definisi untuk semua elemen yang diizinkan, atribut, dan hierarkinya. Seperti yang kita lihat sebelumnya, DTD HTML tidak membentuk tata bahasa bebas konteks.

Ada beberapa variasi DTD. Mode ketat hanya sesuai dengan spesifikasi, tetapi mode lain berisi dukungan untuk markup yang digunakan oleh browser di masa lalu. Tujuannya adalah kompatibilitas mundur dengan konten lama. DTD ketat saat ini ada di sini: www.w3.org/TR/html4/strict.dtd

DOM

Hierarki output ("pohon penguraian") adalah hierarki elemen DOM dan node atribut. DOM adalah singkatan dari Document Object Model. Ini adalah presentasi objek dari dokumen HTML dan antarmuka elemen HTML ke dunia luar seperti JavaScript.

Akar pohon adalah "Dokumen" .

DOM memiliki hubungan yang hampir satu-ke-satu dengan markup. Contoh:

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

Markup ini akan diterjemahkan ke hierarki DOM berikut:

Hierarki DOM markup contoh
Gambar 8: Hierarki DOM markup contoh

Seperti HTML, DOM ditetapkan oleh organisasi W3C. Lihat www.w3.org/DOM/DOMTR. Ini adalah spesifikasi umum untuk memanipulasi dokumen. Modul spesifik menjelaskan elemen-elemen khusus HTML. Definisi HTML dapat ditemukan di sini: www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

Bila saya mengatakan bahwa pohon itu berisi simpul DOM, maksud saya adalah pohon itu dibuat dari elemen yang mengimplementasikan salah satu antarmuka DOM itu. Browser menggunakan penerapan konkret yang memiliki atribut lain yang digunakan oleh browser secara internal.

Algoritma penguraian

Seperti yang kita lihat di bagian sebelumnya, HTML tidak dapat diuraikan menggunakan parser top down atau bottom up biasa.

Alasannya adalah:

  1. Sifat bahasa yang memaafkan.
  2. Fakta bahwa browser memiliki toleransi kesalahan tradisional untuk mendukung kasus HTML tidak valid yang terkenal.
  3. Proses penguraian bersifat reentrant. Untuk bahasa lain, sumber tidak berubah selama penguraian, tetapi dalam HTML, kode dinamis (seperti elemen skrip yang berisi panggilan document.write()) dapat menambahkan token tambahan, sehingga proses penguraian benar-benar mengubah input.

Tidak dapat menggunakan teknik penguraian reguler, browser membuat parser kustom untuk menguraikan HTML.

Algoritma penguraian dijelaskan secara mendetail oleh spesifikasi HTML5. Algoritma terdiri dari dua tahap: tokenisasi dan konstruksi pohon.

Tokenisasi adalah analisis leksikal, yang menguraikan input menjadi token. Di antara token HTML adalah tag awal, tag akhir, nama atribut dan nilai atribut.

Tokenizer mengenali token, memberikannya ke konstruktor hierarki, dan menggunakan karakter berikutnya untuk mengenali token berikutnya, dan seterusnya hingga akhir input.

Alur penguraian HTML (diambil dari spesifikasi HTML5)
Gambar 9: Alur penguraian HTML (diambil dari spesifikasi HTML5)

Algoritma tokenisasi

Output algoritme adalah token HTML. Algoritma ini dinyatakan sebagai mesin status. Setiap status menggunakan satu atau beberapa karakter aliran input dan memperbarui status berikutnya sesuai dengan karakter tersebut. Keputusan ini dipengaruhi oleh status tokenisasi saat ini dan status konstruksi pohon. Hal ini berarti karakter yang sama yang digunakan akan memberikan hasil yang berbeda untuk status berikutnya yang benar, bergantung pada status saat ini. Algoritma terlalu rumit untuk dijelaskan secara lengkap, jadi mari kita lihat contoh sederhana yang akan membantu kita memahami prinsipnya.

Contoh dasar - membuat token HTML berikut:

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

Status awalnya adalah "Data state". Saat karakter < ditemukan, status diubah menjadi "Tag open state". Memakai karakter a-z menyebabkan pembuatan "Start tag token", statusnya diubah menjadi "Tag name state". Kita akan tetap dalam status ini sampai karakter > digunakan. Setiap karakter ditambahkan ke nama token baru. Dalam kasus kita, token yang dibuat adalah token html.

Saat tag > tercapai, token saat ini akan dimunculkan dan status berubah kembali ke "Status data". Tag <body> akan ditangani dengan langkah yang sama. Sejauh ini, tag html dan body telah dimunculkan. Kita sekarang kembali ke "Status data". Memakai karakter H dari Hello world akan menyebabkan pembuatan dan memunculkan token karakter, hal ini berlanjut hingga < dari </body> tercapai. Kita akan memunculkan token karakter untuk setiap karakter Hello world.

Kini kita kembali ke "Status terbuka tag". Memakai input / berikutnya akan menyebabkan pembuatan end tag token dan pemindahan ke "Status nama tag". Sekali lagi, kita akan tetap dalam status ini hingga mencapai >.Kemudian, token tag baru akan dimunculkan dan kita kembali ke "Status data". Input </html> akan diperlakukan seperti kasus sebelumnya.

Membuat token contoh input
Gambar 10: Membuat token contoh input

Algoritma konstruksi pohon

Saat parser dibuat, objek Dokumen akan dibuat. Selama tahap konstruksi hierarki, hierarki DOM yang memiliki Dokumen di root akan diubah dan elemen akan ditambahkan ke dalamnya. Setiap node yang dimunculkan oleh tokenizer akan diproses oleh konstruktor hierarki. Untuk setiap token, spesifikasi mendefinisikan elemen DOM mana yang relevan dengannya dan akan dibuat untuk token ini. Elemen ini ditambahkan ke hierarki DOM, dan juga tumpukan elemen terbuka. Stack ini digunakan untuk memperbaiki ketidakcocokan bertingkat dan tag yang tidak ditutup. Algoritma juga dijelaskan sebagai mesin status. Negara bagian tersebut disebut "mode penyisipan".

Mari kita lihat proses konstruksi pohon untuk contoh input:

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

Input ke tahap konstruksi pohon adalah urutan token dari tahap tokenisasi. Mode pertama adalah "mode awal". Menerima "html" akan menyebabkan pemindahan ke mode "sebelum html" dan pemrosesan ulang token pada mode tersebut. Ini akan menyebabkan pembuatan elemen HTMLHTMLElement, yang akan ditambahkan ke objek Document root.

Status akan diubah menjadi "before head". "Isi" token akan diterima. HTMLHeadElement akan dibuat secara implisit meskipun kita tidak memiliki "head" token tersebut dan akan ditambahkan ke dalam hierarki.

Sekarang kita beralih ke mode "in head", lalu ke "after head". Token isi diproses ulang, HTMLBodyElement dibuat dan disisipkan, dan mode ditransfer ke "in body".

Token karakter "Hello world" {i>string <i}telah diterima. Yang pertama akan menyebabkan pembuatan dan penyisipan "Text" {i>node<i} dan karakter lainnya akan ditambahkan ke {i>node<i} tersebut.

Penerimaan token akhir isi akan menyebabkan transfer ke mode "setelah isi". Sekarang kita akan menerima tag akhir html yang akan memindahkan kita ke mode "setelah isi". Menerima akhir token file akan mengakhiri penguraian.

Konstruksi pohon contoh HTML.
Gambar 11: konstruksi pohon dari contoh html

Tindakan ketika penguraian selesai

Pada tahap ini, browser akan menandai dokumen sebagai interaktif dan mulai mengurai skrip yang "ditangguhkan" mode yang harus dieksekusi setelah dokumen diuraikan. Status dokumen kemudian akan ditetapkan ke "complete" dan "beban" peristiwa akan diaktifkan.

Anda dapat melihat algoritma lengkap untuk tokenisasi dan konstruksi pohon di spesifikasi HTML5.

Browser toleransi error

Anda tidak pernah mendapatkan pesan "Sintaksis Tidak Valid" kesalahan pada laman HTML. Browser memperbaiki konten yang tidak valid dan melanjutkan.

Ambil contoh HTML ini:

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

Saya pasti telah melanggar sekitar satu juta aturan ("mytag" bukan tag standar, susunan elemen "p" dan "div" yang salah, dan banyak lagi), tetapi browser masih menampilkannya dengan benar dan tidak mengeluh. Banyak kode parser yang memperbaiki kesalahan penulis HTML.

Penanganan {i>error<i} cukup konsisten di {i>browser<i}, tetapi sangat menakjubkan hal ini belum menjadi bagian dari spesifikasi HTML. Seperti bookmark dan tombol kembali/maju, ini sesuatu yang berkembang di {i>browser<i} selama bertahun-tahun. Terdapat konstruksi HTML tidak valid yang diulang di banyak situs, dan browser mencoba memperbaikinya dengan cara yang sesuai dengan browser lainnya.

Spesifikasi HTML5 mendefinisikan beberapa persyaratan ini. (WebKit merangkumnya dengan baik dalam komentar di awal kelas parser HTML.)

Parser mengurai input dengan token ke dalam dokumen, sehingga membangun hierarki dokumen. Jika dokumennya tersusun dengan baik, Anda dapat langsung menguraikannya.

Sayangnya, kita harus menangani banyak dokumen HTML yang tidak diformat dengan baik, sehingga parser harus toleran terhadap error.

Kami harus mengatasi setidaknya kondisi error berikut:

  1. Elemen yang ditambahkan secara eksplisit dilarang di dalam beberapa tag luar. Dalam hal ini kita harus menutup semua tag hingga tag yang melarang elemen, dan menambahkannya setelahnya.
  2. Kami tidak diizinkan menambahkan elemen secara langsung. Bisa jadi orang yang menulis dokumen lupa beberapa tag di antaranya (atau tag di antaranya bersifat opsional). Hal ini dapat terjadi pada tag berikut: HTML HEAD BODY TBODY TR TD LI (apa saya lupa?).
  3. Kita ingin menambahkan elemen blok di dalam elemen inline. Tutup semua elemen inline hingga elemen blok berikutnya yang lebih tinggi.
  4. Jika hal tersebut tidak membantu, tutup elemen hingga kami diizinkan untuk menambahkan elemen - atau abaikan tag.

Mari kita lihat beberapa contoh toleransi error WebKit:

</br> bukan <br>

Beberapa situs menggunakan </br>, bukan <br>. Agar kompatibel dengan IE dan Firefox, WebKit memperlakukannya seperti <br>.

Kode:

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

Perhatikan bahwa penanganan error bersifat internal: error tidak akan ditampilkan kepada pengguna.

Meja menyimpang

Tabel menyimpang adalah tabel di dalam tabel lain, tetapi tidak di dalam sel tabel.

Contoh:

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

WebKit akan mengubah hierarki menjadi dua tabel bersaudara:

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

Kode:

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

WebKit menggunakan stack untuk konten elemen saat ini: ini akan memunculkan tabel dalam dari stack tabel luar. Tabel akan menjadi saudara.

Elemen formulir bertingkat

Jika pengguna memasukkan formulir ke dalam formulir lain, formulir kedua akan diabaikan.

Kode:

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

Hierarki tag yang terlalu dalam

Komentar tersebut dengan sendirinya.

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

Tag akhir html atau badan yang salah ditempatkan

Sekali lagi - komentar tersebut berbicara sendiri.

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

Jadi, berhati-hatilah pada penulis web - kecuali jika Anda ingin ditampilkan sebagai contoh dalam cuplikan kode toleransi error WebKit - tulislah HTML yang diformat dengan baik.

Penguraian CSS

Ingat konsep penguraian di pendahuluan? Tidak seperti HTML, CSS adalah tata bahasa bebas konteks dan dapat diurai menggunakan jenis parser yang dijelaskan dalam pendahuluan. Bahkan, spesifikasi CSS menentukan tata bahasa dan leksik CSS.

Mari kita lihat beberapa contohnya:

Tata bahasa leksis (kosakata) ditentukan oleh ekspresi reguler untuk setiap token:

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

&quot;ident&quot; adalah singkatan dari ID, seperti nama class. "nama" adalah id elemen (yang dirujuk dengan "#" )

Tata bahasa sintaks dijelaskan dalam 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*] ')' ]
  ;

Penjelasan:

Seperangkat aturan adalah struktur ini:

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

div.error dan a.error adalah pemilih. Bagian di dalam tanda kurung kurawal berisi aturan yang diterapkan oleh kumpulan aturan ini. Struktur ini didefinisikan secara formal dalam definisi ini:

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

Ini berarti kumpulan aturan adalah pemilih atau secara opsional sejumlah pemilih yang dipisahkan oleh koma dan spasi (S berarti ruang putih). Kumpulan aturan berisi tanda kurung kurawal dan di dalamnya terdapat deklarasi atau sejumlah deklarasi yang dipisahkan dengan titik koma. "deklarasi" dan "selector" akan didefinisikan dalam definisi BNF berikut.

Parser CSS WebKit

WebKit menggunakan generator parser Flex dan Bison untuk membuat parser secara otomatis dari file tata bahasa CSS. Seperti yang Anda ingat dari pengantar parser, Bison membuat parser shift-reduce bottom up. Firefox menggunakan parser {i>top down<i} yang ditulis secara manual. Dalam kedua kasus tersebut, setiap file CSS diuraikan menjadi objek StyleSheet. Setiap objek berisi aturan CSS. Objek aturan CSS berisi objek pemilih dan deklarasi serta objek lain yang sesuai dengan tata bahasa CSS.

Mengurai CSS.
Gambar 12: mengurai CSS

Urutan pemrosesan untuk skrip dan spreadsheet gaya

Skrip

Model web ini sinkron. Penulis mengharapkan skrip akan diuraikan dan dieksekusi segera saat parser mencapai tag <script>. Penguraian dokumen akan berhenti hingga skrip telah dieksekusi. Jika skrip bersifat eksternal, resource harus diambil terlebih dahulu dari jaringan - ini juga dilakukan secara sinkron, dan penguraian akan dihentikan hingga resource diambil. Model ini telah menjadi model selama bertahun-tahun dan ditentukan dalam spesifikasi HTML4 dan 5. Penulis dapat menambahkan "defer" ke skrip, dalam hal ini tidak akan menghentikan penguraian dokumen dan akan dieksekusi setelah dokumen diuraikan. HTML5 menambahkan opsi untuk menandai skrip sebagai asinkron sehingga akan diuraikan dan dieksekusi oleh utas yang berbeda.

Penguraian spekulatif

WebKit dan Firefox melakukan pengoptimalan ini. Saat menjalankan skrip, thread lain akan mengurai sisa dokumen dan mencari tahu resource lain apa yang perlu dimuat dari jaringan dan memuatnya. Dengan cara ini, resource dapat dimuat di koneksi paralel dan kecepatan keseluruhan akan ditingkatkan. Catatan: parser spekulatif hanya mengurai referensi ke resource eksternal seperti skrip eksternal, lembar gaya, dan gambar: parser ini tidak memodifikasi hierarki DOM - yang diserahkan ke parser utama.

Lembar gaya

Di sisi lain, lembar gaya memiliki model yang berbeda. Secara konseptual tampaknya karena lembar gaya tidak mengubah hierarki DOM, tidak ada alasan untuk menunggunya dan menghentikan penguraian dokumen. Namun, ada masalah pada skrip yang meminta informasi gaya selama tahap penguraian dokumen. Jika gaya belum dimuat dan diuraikan, skrip akan mendapatkan jawaban yang salah dan tampaknya hal ini menyebabkan banyak masalah. Tampaknya ini adalah {i>edge case<i}, tetapi cukup umum terjadi. Firefox memblokir semua skrip saat ada lembar gaya yang masih dimuat dan diuraikan. WebKit memblokir skrip hanya ketika mereka mencoba mengakses properti gaya tertentu yang mungkin terpengaruh oleh lembar gaya yang tidak dimuat.

Konstruksi hierarki render

Selagi hierarki DOM sedang dibuat, browser akan membentuk pohon lainnya, yakni pohon render. Pohon ini adalah elemen visual sesuai urutan tampilannya. {i>Metadata<i} adalah representasi visual dari dokumen. Tujuan dari pohon ini adalah untuk memungkinkan pengecatan konten dalam urutan yang benar.

Firefox menyebut elemen-elemen dalam pohon render sebagai "frames". WebKit menggunakan istilah perender atau objek render.

Perender tahu cara menata dan melukiskan dirinya sendiri dan turunannya.

Kelas RenderObject WebKit, kelas dasar perender, memiliki definisi berikut:

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
}

Setiap perender mewakili area persegi panjang yang biasanya sesuai dengan kotak CSS node, seperti yang dijelaskan oleh spesifikasi CSS2. Ini menyertakan informasi geometris seperti lebar, tinggi, dan posisi.

Jenis kotak dipengaruhi oleh "tampilan" atribut gaya yang relevan dengan node (lihat bagian komputasi gaya). Berikut ini adalah kode WebKit untuk memutuskan jenis perender yang harus dibuat untuk simpul DOM, sesuai dengan atribut tampilan:

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

Jenis elemen juga dipertimbangkan: misalnya, tabel dan kontrol formulir memiliki bingkai khusus.

Di WebKit, jika elemen ingin membuat perender khusus, elemen tersebut akan mengganti metode createRenderer(). Perender mengarah ke objek gaya yang berisi informasi non-geometris.

Relasi hierarki render dengan hierarki DOM

Perender sesuai dengan elemen DOM, namun relasinya bukan satu ke satu. Elemen DOM non-visual tidak akan disisipkan dalam hierarki render. Contohnya adalah "head" . Juga elemen yang nilai tampilannya ditetapkan ke "tidak ada" tidak akan muncul di hierarki (sedangkan elemen dengan visibilitas "tersembunyi" akan muncul di hierarki).

Ada elemen DOM yang sesuai dengan beberapa objek visual. Ini biasanya adalah elemen dengan struktur kompleks yang tidak dapat dijelaskan dengan persegi panjang tunggal. Misalnya, tombol "select" memiliki tiga perender: satu untuk area tampilan, satu untuk kotak daftar drop-down, dan satu lagi untuk tombol. Selain itu, jika teks dibagi menjadi beberapa baris karena lebarnya tidak cukup untuk satu baris, baris baru akan ditambahkan sebagai perender tambahan.

Contoh lain dari beberapa perender adalah HTML yang rusak. Menurut spesifikasi CSS, elemen inline harus berisi hanya elemen blok atau elemen inline saja. Untuk konten campuran, perender blok anonim akan dibuat untuk menggabungkan elemen inline.

Beberapa objek render sesuai dengan simpul DOM namun tidak pada tempat yang sama pada hierarki. Float dan elemen yang benar-benar diposisikan tidak mengalir, ditempatkan di bagian pohon yang berbeda, dan dipetakan ke bingkai sesungguhnya. Bingkai placeholder adalah tempat mereka seharusnya berada.

Pohon render dan hierarki DOM yang sesuai.
Gambar 13: Hierarki render dan hierarki DOM terkait. "Area pandang" adalah blok penampung awal. Di WebKit, ini akan menjadi "RenderView" objek

Alur konstruksi pohon

Di Firefox, presentasi didaftarkan sebagai pemroses untuk pembaruan DOM. Presentasi mendelegasikan pembuatan frame ke FrameConstructor dan konstruktor me-resolve gaya (lihat komputasi gaya) dan membuat frame.

Di WebKit, proses penyelesaian gaya dan pembuatan perender disebut "lampiran". Setiap node DOM memiliki "attach" . Lampiran bersifat sinkron, penyisipan node ke hierarki DOM memanggil node baru "attach" .

Pemrosesan pada tag html dan body akan menghasilkan konstruksi akar pohon render. Objek render root sesuai dengan apa yang disebut oleh spesifikasi CSS sebagai blok yang memuatnya: blok paling atas yang berisi semua blok lainnya. Dimensinya adalah area pandang: dimensi area tampilan jendela browser. Firefox menyebutnya ViewPortFrame dan WebKit menyebutnya RenderView. Ini adalah objek render yang ditunjuk dokumen. Hierarki lainnya dikonstruksi sebagai penyisipan node DOM.

Lihat spesifikasi CSS2 tentang model pemrosesan.

Komputasi gaya

Membangun hierarki render memerlukan penghitungan properti visual dari setiap objek render. Hal ini dilakukan dengan menghitung properti gaya dari setiap elemen.

Gaya mencakup lembar gaya dari berbagai asal, elemen gaya sebaris dan properti visual dalam HTML (seperti properti "bgcolor").Yang kemudian diterjemahkan ke properti gaya CSS yang cocok.

Asal-usul lembar gaya adalah lembar gaya default browser, lembar gaya yang disediakan oleh penulis halaman, dan lembar gaya pengguna - ini adalah lembar gaya yang disediakan oleh pengguna browser (browser memungkinkan Anda menentukan gaya favorit Anda. Di Firefox, misalnya, hal ini dilakukan dengan menempatkan lembar gaya di "Profil Firefox" ).

Komputasi gaya memunculkan beberapa kesulitan:

  1. Data gaya adalah konstruksi yang sangat besar, yang menyimpan banyak properti gaya, hal ini dapat menyebabkan masalah memori.
  2. Menemukan aturan yang cocok untuk setiap elemen dapat menyebabkan masalah performa jika tidak dioptimalkan. Menelusuri seluruh daftar aturan untuk setiap elemen untuk menemukan kecocokan adalah tugas yang berat. Pemilih dapat memiliki struktur kompleks yang dapat menyebabkan proses pencocokan dimulai pada jalur yang tampaknya menjanjikan yang terbukti sia-sia dan jalur lain harus dicoba.

    Misalnya - pemilih gabungan ini:

    div div div div{
    ...
    }
    

    Berarti, aturan tersebut berlaku untuk <div> yang merupakan turunan dari 3 div. Misalnya Anda ingin memeriksa apakah aturan berlaku untuk elemen <div> tertentu. Anda memilih jalur tertentu di atas pohon untuk diperiksa. Anda mungkin perlu melewati hierarki node ke atas hanya untuk mengetahui bahwa hanya ada dua div dan aturan ini tidak berlaku. Anda kemudian perlu mencoba jalur lain di hierarki.

  3. Menerapkan aturan melibatkan aturan menurun yang cukup kompleks yang mendefinisikan hierarki aturan.

Mari kita lihat bagaimana browser menghadapi masalah ini:

Membagikan data gaya

Node WebKit mereferensikan objek gaya (RenderStyle). Objek ini dapat dibagikan oleh node dalam beberapa kondisi. Node tersebut adalah saudara atau sepupu dan:

  1. Elemen harus dalam keadaan mouse yang sama (misalnya, yang satu tidak boleh :hover, sedangkan yang lainnya tidak)
  2. Tidak ada elemen yang harus memiliki id
  3. Nama tag harus cocok
  4. Atribut kelas harus cocok
  5. Kumpulan atribut yang dipetakan harus identik
  6. Status link harus cocok
  7. Status fokus harus sesuai
  8. Tidak ada elemen yang boleh dipengaruhi oleh pemilih atribut. Jika terpengaruh oleh pemilih tersebut akan didefinisikan memiliki kecocokan pemilih yang menggunakan pemilih atribut di posisi mana pun dalam pemilih.
  9. Tidak boleh ada atribut gaya inline pada elemen
  10. Tidak boleh ada pemilih saudara yang digunakan sama sekali. WebCore hanya melempar tombol global ketika pemilih seinduk ditemui dan menonaktifkan berbagi gaya untuk seluruh dokumen saat mereka ada. Ini termasuk pemilih + dan pemilih seperti :first-child dan :last-child.

Pohon aturan Firefox

Firefox memiliki dua pohon tambahan untuk memudahkan komputasi gaya: pohon aturan dan pohon konteks gaya. WebKit juga memiliki objek gaya, tetapi tidak disimpan di pohon seperti pohon konteks gaya, hanya simpul DOM yang mengarah ke gaya yang relevan.

Pohon konteks gaya Firefox.
Gambar 14: Hierarki konteks gaya Firefox.

Konteks gaya berisi nilai akhir. Nilai dihitung dengan menerapkan semua aturan yang cocok dalam urutan yang benar dan melakukan manipulasi yang mengubahnya dari nilai logis menjadi konkret. Misalnya, jika nilai logika adalah persentase layar, nilai tersebut akan dihitung dan diubah menjadi unit absolut. Ide hierarki aturan benar-benar cerdas. Fungsi ini memungkinkan pembagian nilai-nilai ini di antara node untuk menghindari komputasi nilai tersebut lagi. Cara ini juga menghemat ruang penyimpanan.

Semua aturan yang cocok akan disimpan dalam struktur. Node bawah pada jalur memiliki prioritas yang lebih tinggi. Hierarki berisi semua jalur untuk kecocokan aturan yang ditemukan. Menyimpan aturan dilakukan dengan lambat. Hierarki tidak dihitung di awal untuk setiap node, tetapi setiap kali gaya node perlu dihitung, jalur yang dihitung akan ditambahkan ke hierarki.

Idenya adalah melihat jalur hierarki sebagai kata-kata dalam leksikon. Katakanlah kita telah menghitung pohon aturan ini:

Hierarki aturan yang dikomputasi
Gambar 15: Hierarki aturan yang dikomputasi.

Misalkan kita perlu mencocokkan aturan untuk elemen lain dalam hierarki konten, dan menemukan aturan yang cocok (dalam urutan yang benar) adalah B-E-I. Kita sudah memiliki jalur ini di hierarki karena kita telah menghitung jalur A-B-E-I-L. Sekarang kita akan memiliki lebih sedikit pekerjaan yang harus dilakukan.

Mari kita lihat bagaimana pohon menyelamatkan kita bekerja.

Pembagian menjadi struct

Konteks gaya dibagi menjadi beberapa struct. Struktur tersebut berisi informasi gaya untuk kategori tertentu seperti batas atau warna. Semua properti dalam struct bersifat diwariskan atau tidak diwariskan. Properti turunan adalah properti yang diwarisi dari induknya, kecuali jika ditentukan oleh elemen. Properti yang tidak diwarisi (disebut properti "reset") menggunakan nilai default jika tidak ditentukan.

Hierarki membantu kita dengan meng-cache seluruh struct (berisi nilai akhir yang telah dihitung) di dalam hierarki. Idenya adalah jika node bawah tidak menyediakan definisi untuk struct, struct yang di-cache di node atas dapat digunakan.

Menghitung konteks gaya menggunakan hierarki aturan

Saat menghitung konteks gaya untuk elemen tertentu, pertama-tama kita menghitung jalur dalam hierarki aturan atau menggunakan yang sudah ada. Kemudian kita mulai menerapkan aturan di jalur tersebut untuk mengisi struct dalam konteks gaya baru. Kita mulai dari node bawah jalur - node dengan prioritas tertinggi (biasanya pemilih yang paling spesifik) dan melintasi pohon hingga struct kita penuh. Jika tidak ada spesifikasi untuk {i>struct <i}dalam simpul aturan itu, maka kita bisa mengoptimalkannya secara signifikan - kita akan menaiki pohon itu sampai kita menemukan sebuah simpul yang menentukannya sepenuhnya dan menunjuk ke sana - inilah pengoptimalan terbaiknya - seluruh struct akan dibagikan. Tindakan ini akan menghemat komputasi nilai akhir dan memori.

Jika kita menemukan definisi parsial, kita akan naik hierarkinya sampai struct terisi.

Jika kita tidak menemukan definisi apa pun untuk {i>struct<i}, maka, jika struct ini adalah "diwarisi" kita akan menunjuk ke struct induk dalam hierarki konteks. Dalam hal ini, kami juga berhasil membagikan struct. Jika ini adalah struct reset, maka nilai default akan digunakan.

Jika {i>node<i} yang paling spesifik menambahkan nilai, kita perlu melakukan beberapa perhitungan tambahan untuk mengubahnya menjadi nilai aktual. Kita kemudian meng-cache hasilnya di node pohon agar dapat digunakan oleh turunan.

Jika suatu elemen memiliki saudara atau saudara yang mengarah ke node pohon yang sama, seluruh konteks gaya dapat dibagikan di antara keduanya.

Mari kita lihat contohnya: Misalkan kita memiliki HTML ini

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

Dan aturan berikut:

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

Untuk menyederhanakan, misalkan kita hanya perlu mengisi dua struct: color struct dan margin struct. Struktur warna hanya berisi satu anggota: warna {i>Margin struct <i}berisi empat sisi.

Hierarki aturan yang dihasilkan akan terlihat seperti ini (node ditandai dengan nama node: nomor aturan yang ditunjuk oleh node):

Hierarki aturan
Gambar 16: Hierarki aturan

Hierarki konteks akan terlihat seperti ini (nama node: node aturan yang ditujunya):

Hierarki konteks.
Gambar 17: Hierarki konteks

Misalkan kita mengurai HTML dan sampai ke tag <div> kedua. Kita perlu membuat konteks gaya untuk node ini dan mengisi struct gayanya.

Kita akan mencocokkan aturan dan menemukan bahwa aturan yang cocok untuk <div> adalah 1, 2, dan 6. Ini berarti sudah ada jalur di hierarki yang dapat digunakan elemen kita dan kita hanya perlu menambahkan node lain untuk aturan 6 (node F dalam pohon aturan).

Kita akan membuat konteks gaya dan meletakkannya di pohon konteks. Konteks gaya baru akan mengarah ke node F dalam hierarki aturan.

Kita sekarang perlu mengisi gaya struct. Kita akan mulai dengan mengisi {i>margin struct<i}. Karena node aturan terakhir (F) tidak menambah margin struct, kita dapat menaikkan hierarki tersebut sampai menemukan struct cache yang dihitung pada penyisipan node sebelumnya dan menggunakannya. Kita akan menemukannya di node B, yang merupakan node paling atas yang menentukan aturan margin.

Kita memiliki definisi untuk color struct, jadi kita tidak dapat menggunakan struct yang di-cache. Karena warna memiliki satu atribut, kita tidak perlu naik hierarki untuk mengisi atribut lain. Kita akan menghitung nilai akhir (mengonversi string menjadi RGB, dll.) dan meng-cache struct yang dihitung pada node ini.

Pekerjaan pada elemen <span> kedua jauh lebih mudah. Kita akan mencocokkan aturan dan sampai pada kesimpulan bahwa aturan itu menunjuk aturan G, seperti span sebelumnya. Karena kita memiliki saudara yang mengarah ke node yang sama, kita bisa membagikan seluruh konteks gaya dan hanya menunjuk ke konteks span sebelumnya.

Untuk struktur yang berisi aturan yang diwarisi dari induk, penyimpanan dalam cache dilakukan pada hierarki konteks (properti warna sebenarnya diwarisi, tetapi Firefox memperlakukannya sebagai reset dan menyimpannya di cache pada hierarki aturan).

Misalnya, jika kita menambahkan aturan untuk {i>font<i} dalam sebuah paragraf:

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

Kemudian elemen paragraf, yang merupakan turunan dari div dalam hierarki konteks, dapat memiliki {i>font struct<i} yang sama dengan induknya. Hal ini berlaku jika tidak ada aturan font yang ditentukan untuk paragraf.

Dalam WebKit, yang tidak memiliki hierarki aturan, deklarasi yang cocok dilintasi empat kali. Properti prioritas tinggi yang tidak penting pertama diterapkan (properti yang harus diterapkan terlebih dahulu karena properti lain bergantung padanya, seperti display), lalu properti berprioritas tinggi yang penting, lalu prioritas normal tidak penting, kemudian aturan penting dengan prioritas normal. Ini berarti bahwa properti yang muncul beberapa kali akan diselesaikan sesuai dengan urutan yang benar. Terakhir, menang.

Jadi untuk meringkas: berbagi objek gaya (seluruhnya atau beberapa struct di dalamnya) dapat memecahkan masalah 1 dan 3. Hierarki aturan Firefox juga membantu menerapkan properti dalam urutan yang benar.

Memanipulasi aturan untuk pencocokan mudah

Ada beberapa sumber untuk aturan gaya:

  1. Aturan CSS, baik dalam lembar gaya eksternal atau dalam elemen gaya. css p {color: blue}
  2. Atribut gaya {i>inline<i} seperti html <p style="color: blue" />
  3. Atribut visual HTML (yang dipetakan ke aturan gaya yang relevan) html <p bgcolor="blue" /> Dua yang terakhir mudah dicocokkan dengan elemen karena ia memiliki atribut gaya dan atribut HTML dapat dipetakan menggunakan elemen sebagai kunci.

Seperti yang telah disebutkan sebelumnya dalam masalah #2, pencocokan aturan CSS bisa jadi lebih rumit. Untuk mengatasi kesulitannya, aturan dimanipulasi agar lebih mudah diakses.

Setelah mengurai lembar gaya, aturan akan ditambahkan ke salah satu dari beberapa peta hash, menurut pemilih. Ada peta menurut ID, nama kelas, nama tag, dan peta umum untuk apa pun yang tidak sesuai dengan kategori tersebut. Jika pemilih adalah ID, aturan akan ditambahkan ke peta ID. Jika berupa class, aturan akan ditambahkan ke peta class dll.

Manipulasi ini memudahkan pencocokan aturan. Tidak perlu melihat di setiap deklarasi: kita dapat mengekstrak aturan yang relevan untuk elemen dari peta. Pengoptimalan ini menghilangkan lebih dari 95% aturan, sehingga aturan tersebut bahkan tidak perlu dipertimbangkan selama proses pencocokan(4.1).

Mari kita lihat contoh aturan gaya berikut:

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

Aturan pertama akan disisipkan ke dalam peta class. Yang kedua masuk ke peta ID dan yang ketiga masuk ke peta tag.

Untuk fragmen HTML berikut;

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

Pertama-tama kita akan mencoba menemukan aturan untuk elemen p. Peta class akan berisi "error" tombol dengan aturan "p.error" ditemukan. Elemen div akan memiliki aturan yang relevan dalam peta ID (kuncinya adalah ID) dan peta tag. Jadi satu-satunya pekerjaan yang tersisa adalah mencari tahu aturan mana yang diekstrak oleh kunci yang benar-benar cocok.

Misalnya jika aturan untuk div adalah:

table div {margin: 5px}

Parameter ini akan tetap diekstrak dari peta tag, karena kuncinya adalah pemilih paling kanan, tetapi tidak akan cocok dengan elemen div, yang tidak memiliki ancestor tabel.

Baik WebKit dan Firefox melakukan manipulasi ini.

Urutan jenjang lembar gaya

Objek gaya memiliki properti yang sesuai dengan setiap atribut visual (semua atribut CSS, tetapi yang lebih umum). Jika properti tidak didefinisikan oleh salah satu aturan yang cocok, maka beberapa properti dapat diwarisi oleh objek gaya elemen induk. Properti lainnya memiliki nilai default.

Masalahnya dimulai ketika ada lebih dari satu definisi - inilah urutan menurun untuk memecahkan masalah tersebut.

Deklarasi untuk properti gaya bisa muncul di beberapa lembar gaya, dan beberapa kali di dalam lembar gaya. Artinya, urutan penerapan aturan tersebut sangat penting. Ini disebut "kaskade" pesanan. Menurut spesifikasi CSS2, urutannya adalah (dari rendah ke tinggi):

  1. Deklarasi browser
  2. Deklarasi normal pengguna
  3. Deklarasi normal penulis
  4. Pernyataan penting penulis
  5. Pernyataan penting pengguna

Deklarasi browser paling tidak penting dan pengguna mengganti penulis hanya jika deklarasi ditandai sebagai penting. Pernyataan dengan urutan yang sama akan diurutkan berdasarkan kekhususan, lalu urutan yang ditentukan. Atribut visual HTML diterjemahkan ke deklarasi CSS yang cocok . Aturan penulis diperlakukan sebagai aturan penulis dengan prioritas rendah.

Kekhususan

Kekhususan pemilih ditentukan oleh spesifikasi CSS2 sebagai berikut:

  1. count 1 jika deklarasi berasal dari 'style' daripada aturan dengan pemilih, 0 jika tidak (= a)
  2. menghitung jumlah atribut ID dalam pemilih (= b)
  3. menghitung jumlah atribut dan kelas semu lainnya dalam pemilih (= c)
  4. menghitung jumlah nama elemen dan elemen semu di pemilih (= d)

Menggabungkan empat angka a-b-c-d (dalam sistem bilangan dengan basis besar) akan memberikan kekhususan.

Basis angka yang perlu Anda gunakan ditentukan oleh jumlah tertinggi yang Anda miliki dalam salah satu kategori.

Misalnya, jika a=14, Anda dapat menggunakan basis heksadesimal. Dalam kasus yang jarang terjadi di mana a=17, Anda akan memerlukan basis angka 17 digit. Situasi selanjutnya dapat terjadi dengan pemilih seperti ini: html body div div p... (17 tag di pemilih Anda... sangat tidak mungkin).

Beberapa contohnya:

 *             {}  /* 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 */

Mengurutkan aturan

Setelah aturan dicocokkan, aturan tersebut akan diurutkan sesuai dengan aturan berurutan. WebKit menggunakan {i>bubble sort<i} untuk daftar kecil dan menggabungkan urutan untuk daftar besar. WebKit menerapkan pengurutan dengan mengganti operator > untuk aturan:

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

Proses bertahap

WebKit menggunakan tanda yang menandai apakah semua lembar gaya tingkat atas (termasuk @impor) telah dimuat. Jika gaya tidak dimuat sepenuhnya saat dipasang, place holder digunakan dan ditandai dalam dokumen, dan akan dihitung ulang setelah lembar gaya dimuat.

Tata Letak

Saat dibuat dan ditambahkan ke hierarki, perender tidak memiliki posisi dan ukuran. Menghitung nilai ini disebut layout atau reflow.

HTML menggunakan model tata letak berbasis alur, artinya sebagian besar waktu dapat dihitung geometri dalam satu penerusan. Elemen nanti "dalam alur" biasanya tidak memengaruhi geometri elemen yang sebelumnya "dalam alur", sehingga tata letak dapat dilanjutkan dari kiri ke kanan, dari atas ke bawah melalui dokumen. Ada pengecualian: misalnya, tabel HTML mungkin memerlukan lebih dari satu penerusan.

Sistem koordinat relatif terhadap frame root. Koordinat atas dan kiri digunakan.

Tata letak adalah proses rekursif. Proses ini dimulai di perender root, yang sesuai dengan elemen <html> pada dokumen HTML. Tata letak terus berlanjut secara rekursif melalui sebagian atau semua hierarki frame, menghitung informasi geometris untuk setiap perender yang memerlukannya.

Posisi perender root adalah 0,0 dan dimensinya adalah area pandang - bagian yang terlihat dari jendela browser.

Semua perender memiliki "layout" atau "mengubah posisi/geometri" , setiap perender memanggil metode tata letak turunannya yang memerlukan tata letak.

Sistem bit kotor

Agar tidak melakukan tata letak penuh pada setiap perubahan kecil, browser menggunakan "dirty bit" sistem file. Perender yang diubah atau ditambahkan menandai dirinya dan turunannya sebagai "kotor": membutuhkan tata letak.

Ada dua tanda: "kotor", dan "anak-anak kotor" Artinya, meskipun perender tidak bermasalah, perender memiliki setidaknya satu turunan yang memerlukan tata letak.

Tata letak global dan inkremental

Tata letak dapat dipicu di seluruh hierarki render - ini "global" tata letak. Hal ini dapat terjadi sebagai akibat dari:

  1. Perubahan gaya global yang memengaruhi semua perender, seperti perubahan ukuran font.
  2. Sebagai hasil dari ukuran layar yang diubah

Tata letak dapat bersifat inkremental, hanya perender kotor yang akan ditata (ini dapat menyebabkan beberapa kerusakan yang memerlukan tata letak tambahan).

Tata letak inkremental dipicu (secara asinkron) jika perender kotor. Misalnya, bila perender baru ditambahkan ke hierarki render setelah konten tambahan berasal dari jaringan dan ditambahkan ke hierarki DOM.

Tata letak inkremental.
Gambar 18: Tata letak inkremental - hanya perender yang kotor dan turunannya yang akan ditata

Tata letak Asinkron dan Sinkron

Tata letak inkremental dilakukan secara asinkron. Antrean Firefox "perintah ubah posisi/geometri" untuk tata letak inkremental dan penjadwal akan memicu eksekusi batch perintah ini. WebKit juga memiliki timer yang menjalankan tata letak inkremental - pohon ini dilewati dan "kotor" perender keluar tata letaknya.

Skrip yang meminta informasi gaya, seperti "offsetHeight" dapat memicu tata letak inkremental secara sinkron.

Tata letak global biasanya akan dipicu secara sinkron.

Terkadang tata letak dipicu sebagai callback setelah tata letak awal karena beberapa atribut, seperti posisi scroll berubah.

Pengoptimalan

Saat tata letak dipicu oleh perubahan ukuran atau perubahan posisi perender(dan bukan ukuran), ukuran render diambil dari cache dan tidak dihitung ulang...

Dalam beberapa kasus, hanya sub hierarki yang diubah dan tata letak tidak dimulai dari root. Hal ini dapat terjadi saat perubahan tersebut bersifat lokal dan tidak memengaruhi lingkungannya - seperti teks yang disisipkan ke dalam kolom teks (jika tidak, setiap penekanan tombol akan memicu tata letak yang dimulai dari root).

Proses {i>layout<i} (tata letak)

Tata letak biasanya memiliki pola berikut:

  1. Perender induk menentukan lebarnya sendiri.
  2. Orang tua lebih memperhatikan anak-anak dan:
    1. Tempatkan perender turunan (tetapkan x dan y-nya).
    2. Memanggil tata letak turunan jika diperlukan - jika kotor atau kita berada dalam tata letak global, atau karena alasan lain - yang akan menghitung tinggi turunan.
  3. Induk menggunakan tinggi akumulatif turunan serta tinggi margin dan padding untuk menetapkan tingginya sendiri - ini akan digunakan oleh induk perender induk.
  4. Menetapkan bit kotornya ke false (salah).

Firefox menggunakan "state" object(nsHTMLReflowState) sebagai parameter untuk tata letak (disebut "reflow"). Status antara lain mencakup lebar induk.

Output tata letak Firefox adalah "metrik" objek(nsHTMLReflowMetrics). Baris ini akan berisi tinggi yang dihitung perender.

Penghitungan lebar

Lebar perender dihitung menggunakan lebar blok penampung, "lebar" gaya perender properti, serta {i>margin<i} dan {i>border<i}.

Misalnya, lebar div berikut:

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

Akan dihitung oleh WebKit sebagai berikut(metode RenderBox class calcWidth):

  • Lebar penampung adalah maksimum penampung availableWidth dan 0. availableWidth dalam hal ini adalah contentWidth yang dihitung sebagai:
clientWidth() - paddingLeft() - paddingRight()

clientWidth dan clientHeight mewakili bagian dalam objek kecuali batas dan scrollbar.

  • Lebar elemen adalah "lebar" atribut gaya. Ini akan dihitung sebagai nilai absolut dengan menghitung persentase lebar container.

  • Batas horizontal dan padding kini telah ditambahkan.

Sejauh ini adalah perhitungan "lebar yang diinginkan". Sekarang lebar minimum dan maksimum akan dihitung.

Jika lebar yang diinginkan lebih besar dari lebar maksimum, lebar maksimum akan digunakan. Jika kurang dari lebar minimum (unit terkecil yang tidak dapat dipecah), lebar minimum akan digunakan.

Nilai di-cache jika tata letak diperlukan, tetapi lebarnya tidak berubah.

Pemutus Baris

Jika perender di tengah tata letak memutuskan bahwa perender perlu rusak, perender akan berhenti dan menyebar ke induk tata letak bahwa perender perlu rusak. Induk membuat perender tambahan dan memanggil tata letak pada perender tersebut.

Melukis

Pada tahap menggambar, pohon render dilalui dan "Paint()" perender dipanggil untuk menampilkan konten pada layar. Painting menggunakan komponen infrastruktur UI.

Global dan inkremental

Seperti tata letak, proses menggambar juga bisa bersifat global - seluruh pohon dicat - atau bertahap. Dalam penggambaran inkremental, beberapa perender berubah dengan cara yang tidak memengaruhi keseluruhan hierarki. Perender yang diubah membatalkan persegi panjangnya di layar. Hal ini menyebabkan OS melihatnya sebagai “area kotor” dan membuat "{i>cat<i}" peristiwa. OS melakukannya dengan cerdas dan menggabungkan beberapa region menjadi satu. Di Chrome, prosesnya lebih rumit karena perender berada dalam proses yang berbeda dengan proses utama. Chrome menyimulasikan perilaku OS sampai batas tertentu. Presentasi tersebut mendengarkan kejadian ini dan mendelegasikan pesan ke akar render. Hierarki akan dilalui hingga perender yang relevan tercapai. Class tersebut akan melakukan recolor dengan sendirinya (dan biasanya turunannya).

Urutan pengecatan

CSS2 menentukan urutan proses pengecatan. Ini sebenarnya adalah urutan elemen ditumpuk dalam konteks tumpukan. Urutan ini memengaruhi pengecatan karena tumpukan dicat dari belakang ke depan. Urutan tumpukan dari perender blok adalah:

  1. warna latar belakang
  2. gambar latar
  3. border
  4. children
  5. outline

Daftar tampilan Firefox

Firefox berjalan di atas pohon render dan membuat daftar tampilan untuk persegi panjang yang diwarnai. File ini berisi perender yang relevan untuk persegi panjang, dalam urutan pengecatan yang tepat (latar belakang perender, lalu batas, dll.).

Dengan begitu, pohon itu hanya perlu dilalui sekali untuk cat ulang, bukan beberapa kali - mengecat semua latar belakang, lalu semua gambar, lalu semua batas, dll.

Firefox mengoptimalkan proses dengan tidak menambahkan elemen yang akan disembunyikan, seperti elemen yang sepenuhnya berada di bawah elemen buram lainnya.

Penyimpanan persegi panjang WebKit

Sebelum menggambar ulang, WebKit menyimpan persegi panjang lama sebagai bitmap. Kemudian, fungsi ini hanya mengecat delta di antara persegi panjang baru dan lama.

Perubahan dinamis

Browser akan mencoba melakukan tindakan seminimal mungkin sebagai respons terhadap perubahan. Jadi, perubahan warna elemen hanya akan menyebabkan penggambaran ulang elemen. Perubahan pada posisi elemen akan menyebabkan tata letak dan penggambaran ulang elemen, turunannya, dan mungkin elemen yang setara. Menambahkan simpul DOM akan menyebabkan tata letak dan penggambaran ulang simpul. Perubahan besar, seperti memperbesar ukuran font "html" , akan menyebabkan pembatalan validasi cache, penataan ulang, dan penggambaran ulang seluruh pohon.

Thread mesin rendering

Mesin rendering thread tunggal. Hampir semuanya, kecuali operasi jaringan, terjadi di satu thread. Di Firefox dan Safari, ini adalah thread utama browser. Di Chrome, ini adalah thread utama proses tab.

Operasi jaringan dapat dilakukan oleh beberapa thread paralel. Jumlah koneksi paralel terbatas (biasanya 2 - 6 koneksi).

Loop peristiwa

Thread utama browser adalah loop peristiwa. Ini adalah loop tak terbatas yang membuat proses tetap berjalan. Fungsi ini menunggu peristiwa (seperti peristiwa layout dan paint) dan memprosesnya. Ini adalah kode Firefox untuk loop peristiwa utama:

while (!mExiting)
    NS_ProcessNextEvent(thread);

Model visual CSS2

Kanvas

Menurut spesifikasi CSS2, istilah kanvas menjelaskan "ruang tempat struktur pemformatan dirender": tempat browser melukiskan konten.

Kanvas tidak terbatas untuk setiap dimensi ruang, tetapi browser memilih lebar awal berdasarkan dimensi area pandang.

Menurut www.w3.org/TR/CSS2/zindex.html, kanvas akan transparan jika dimuat di dalam kanvas, dan akan diberi warna yang ditentukan oleh browser jika tidak.

Model CSS Box

Model kotak CSS menjelaskan kotak persegi panjang yang dihasilkan untuk elemen pada hierarki dokumen dan ditata sesuai dengan model pemformatan visual.

Setiap kotak memiliki area konten (mis. teks, gambar, dll.) dan area padding, batas, dan margin opsional di sekitarnya.

Model kotak CSS2
Gambar 19: Model kotak CSS2

Setiap node menghasilkan 0...n kotak tersebut.

Semua elemen memiliki "tampilan" properti yang menentukan jenis kotak yang akan dibuat.

Contoh:

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

Defaultnya adalah inline, tetapi style sheet browser dapat menetapkan default lainnya. Misalnya: tampilan default untuk "div" adalah blok.

Anda dapat menemukan contoh lembar gaya default di sini: www.w3.org/TR/CSS2/sample.html.

Skema positioning

Ada tiga skema:

  1. Normal: objek diposisikan sesuai dengan posisinya dalam dokumen. Ini berarti tempatnya di pohon render serupa dengan tempatnya di pohon DOM dan ditata menurut dimensi dan jenis kotaknya
  2. Float: objek pertama kali ditata seperti alur normal, lalu dipindahkan sejauh mungkin ke kiri atau ke kanan
  3. Absolut: objek diletakkan di hierarki render di tempat yang berbeda dengan di hierarki DOM

Skema penempatan ditetapkan oleh "posisi" dan "float" .

  • statis dan relatif menyebabkan aliran
  • mutlak dan tetap menyebabkan pemosisian absolut

Dalam pemosisian statis, tidak ada posisi yang ditentukan dan pemosisian default akan digunakan. Dalam skema lain, penulis menentukan posisi: atas, bawah, kiri, kanan.

Cara kotak itu ditata ditentukan oleh:

  • Jenis kotak
  • Dimensi kotak
  • Skema positioning
  • Informasi eksternal seperti ukuran gambar dan ukuran layar

Jenis kotak

Kotak blok: membentuk blok - memiliki kotak sendiri di jendela browser.

Kotak blok.
Gambar 20: Kotak blok

Kotak inline: tidak memiliki bloknya sendiri, tetapi berada di dalam blok yang memuatnya.

Kotak inline.
Gambar 21: Kotak inline

Blok diformat secara vertikal satu demi satu. Inline diformat secara horizontal.

Pemformatan blok dan Inline.
Gambar 22: Pemformatan blok dan inline

Kotak {i>inline<i} ditempatkan di dalam garis atau "kotak baris". Garis setidaknya setinggi kotak tertinggi namun bisa lebih tinggi, saat kotak disejajarkan dengan "dasar bawaan" - artinya bagian bawah elemen disejajarkan pada titik kotak lain, selain bagian bawah. Jika lebar container tidak cukup, inline akan ditempatkan pada beberapa baris. Inilah yang biasanya terjadi dalam sebuah paragraf.

Garis.
Gambar 23: Garis

Positioning

Relatif

Pemosisian relatif - diposisikan seperti biasa, lalu dipindahkan oleh delta yang diperlukan.

Pemosisian relatif.
Gambar 24: Pemosisian relatif

Float

Kotak {i>float<i} digeser ke kiri atau kanan suatu garis. Fitur yang menarik adalah kotak lain mengalir di sekelilingnya. HTML:

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

Akan terlihat seperti:

Mengambang.
Gambar 25: Float

Absolut dan tetap

Tata letak ditentukan dengan tepat terlepas dari alur normal. Elemen tersebut tidak berpartisipasi dalam alur normal. Dimensi akan relatif terhadap penampung. Jika diperbaiki, penampung adalah area pandang.

Pemosisian tetap.
Gambar 26: Pemosisian tetap

Representasi berlapis

Hal ini ditentukan oleh properti CSS indeks z. Bidang ini merepresentasikan dimensi ketiga kotak: posisinya di sepanjang "sumbu z".

Kotak tersebut dibagi menjadi beberapa tumpukan (disebut konteks tumpukan). Dalam setiap tumpukan, elemen mundur akan dicat terlebih dahulu dan elemen maju di atas, lebih dekat dengan pengguna. Jika terjadi tumpang tindih, elemen terpenting akan menyembunyikan elemen sebelumnya.

Tumpukan diurutkan sesuai dengan properti indeks z. Kotak dengan "z-index" membentuk tumpukan lokal. Area pandang memiliki stack luar.

Contoh:

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

Hasilnya adalah:

Pemosisian tetap.
Gambar 27: Pemosisian tetap

Meskipun div merah mendahului div berwarna hijau di markup, dan akan digambar sebelumnya dalam alur reguler, properti indeks z lebih tinggi, sehingga lebih maju dalam stack yang dipegang oleh kotak root.

Resource

  1. Arsitektur browser

    1. Grosskurth, Alan. Arsitektur Referensi untuk Browser Web (pdf)
    2. Gupta, Vineet. Cara Kerja Browser - Bagian 1 - Arsitektur
  2. Penguraian

    1. Aho, Sethi, Ullman, Penyusun: Prinsip, Teknik, dan Alat (alias "Buku Naga"), Addison-Wesley, 1986
    2. Rick Jelliffe. The Bold and the Beautiful: dua draf baru untuk HTML 5.
  3. Firefox

    1. L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers.
    2. L. David Baron, Faster HTML and CSS: Layout Engine Internals for Web Developers (video pembicaraan teknologi Google)
    3. L. David Baron, Layout Engine Mozilla
    4. L. David Baron, Dokumentasi Sistem Gaya Mozilla
    5. Chris Waterson, Catatan tentang Perubahan posisi/geometri HTML
    6. Chris Waterson, Ringkasan Gecko
    7. Alexander Larsson, The life of an HTML HTTP request
  4. WebKit

    1. David Hyatt, Menerapkan CSS(bagian 1)
    2. David Hyatt, Ringkasan WebCore
    3. David Hyatt, Rendering WebCore
    4. David Hyatt, Masalah FOUC
  5. Spesifikasi W3C

    1. Spesifikasi HTML 4.01
    2. Spesifikasi W3C HTML5
    3. Spesifikasi Cascading Style Sheets Level 2 Revisi 1 (CSS 2.1)
  6. Petunjuk build browser

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

Terjemahan

Halaman ini telah diterjemahkan ke dalam bahasa Jepang, dua kali:

Anda dapat melihat terjemahan Korea dan Turki.

Terima kasih semuanya!