Tarayıcılar nasıl çalışır?

Modern web tarayıcılarının perde arkası

Önsöz

WebKit ve Gecko'nun dahili işlemleriyle ilgili bu kapsamlı kılavuz, İsrailli geliştirici Tali Garsiel tarafından yapılan birçok araştırmanın sonucudur. Birkaç yıl boyunca tarayıcıların iç işleyişiyle ilgili yayınlanan tüm verileri inceledi ve web tarayıcılarının kaynak kodunu okumaya çok zaman ayırdı. Şöyle yazdı:

Web geliştiricisi olarak tarayıcı işlemlerinin iç yapısını öğrenmek, daha iyi kararlar almanıza ve geliştirme ile ilgili en iyi uygulamaların arkasındaki gerekçeleri anlamanıza yardımcı olur. Bu oldukça uzun bir doküman olsa da incelemek için biraz zaman ayırmanızı öneririz. Pişman olmazsınız.

Paul Irish, Chrome Geliştirici İlişkileri

Giriş

Web tarayıcıları en yaygın kullanılan yazılımlardır. Bu kılavuzda, bu modellerin arka planda nasıl çalıştığını açıklıyorum. Tarayıcı ekranında Google sayfasını görene kadar adres çubuğuna google.com yazdığınızda ne olacağını göreceğiz.

Bahsedeceğimiz tarayıcılar

Günümüzde masaüstünde kullanılan beş büyük tarayıcı vardır: Chrome, Internet Explorer, Firefox, Safari ve Opera. Mobil cihazlarda ana tarayıcılar Android Tarayıcı, iPhone, Opera Mini ve Opera Mobile, UC Tarayıcı, Nokia S40/S60 tarayıcıları ve Chrome'dur. Opera tarayıcıları hariç bunların tümü WebKit'i temel alır. Açık kaynak tarayıcılar Firefox ve Chrome ile kısmen açık kaynak olan Safari'den örnekler vereceğim. StatCounter istatistiklerine göre (Haziran 2013 itibarıyla) Chrome, Firefox ve Safari, dünya genelinde masaüstü tarayıcı kullanımının yaklaşık% 71'ini oluşturmaktadır. Mobil cihazlarda Android Tarayıcı, iPhone ve Chrome, kullanımın yaklaşık% 54'ünü oluşturur.

Tarayıcının ana işlevi

Tarayıcının ana işlevi, seçtiğiniz web kaynağını sunucudan isteyip tarayıcı penceresinde görüntüleyerek sunmaktır. Kaynak genellikle bir HTML belgesidir ancak PDF, resim veya başka bir içerik türü de olabilir. Kaynağın konumu, kullanıcı tarafından bir URI (Tek Tip Kaynak Tanımlayıcı) kullanılarak belirtilir.

Tarayıcının HTML dosyalarını yorumlama ve görüntüleme şekli, HTML ve CSS spesifikasyonlarında belirtilir. Bu spesifikasyonlar, web için standartlar kuruluşu olan W3C (World Wide Web Consortium) tarafından yönetilir. Yıllar boyunca tarayıcılar, spesifikasyonların yalnızca bir kısmına uydu ve kendi uzantılarını geliştirdi. Bu durum, web yazarları için ciddi uyumluluk sorunlarına neden oluyordu. Günümüzde çoğu tarayıcı, spesifikasyonlara az çok uymaktadır.

Tarayıcı kullanıcı arayüzlerinin birbiriyle birçok ortak noktası vardır. Yaygın kullanıcı arayüzü öğelerinden bazıları şunlardır:

  1. URI eklemek için adres çubuğu
  2. Geri ve ileri düğmeleri
  3. Yer işareti seçenekleri
  4. Mevcut dokümanların yüklenmesini yenilemek veya durdurmak için yenileme ve durdurma düğmeleri
  5. Sizi ana sayfanıza götüren ana sayfa düğmesi

İlginç bir şekilde, tarayıcının kullanıcı arayüzü herhangi bir resmi spesifikasyonda belirtilmemiştir. Bu arayüz, yılların deneyimiyle şekillenen ve tarayıcıların birbirini taklit etmesiyle oluşan iyi uygulamalardan ibarettir. HTML5 spesifikasyonu, bir tarayıcının sahip olması gereken kullanıcı arayüzü öğelerini tanımlamaz ancak bazı yaygın öğeleri listeler. Adres çubuğu, durum çubuğu ve araç çubuğu bunlardan bazılarıdır. Elbette Firefox'un indirme yöneticisi gibi belirli bir tarayıcıya özgü özellikler de vardır.

Üst düzey altyapı

Tarayıcının ana bileşenleri şunlardır:

  1. Kullanıcı arayüzü: Adres çubuğu, geri/ileri düğmesi, yer işareti menüsü vb. öğeleri içerir. Tarayıcı ekranının, istenen sayfayı gördüğünüz pencere dışındaki her bölümüdür.
  2. Tarayıcı motoru: Kullanıcı arayüzü ile oluşturma motoru arasındaki işlemleri yönetir.
  3. Oluşturma motoru: İstenen içeriği görüntülemekten sorumludur. Örneğin, istenen içerik HTML ise oluşturma motoru HTML ve CSS'yi ayrıştırır ve ayrıştırılan içeriği ekranda gösterir.
  4. Ağ oluşturma: HTTP istekleri gibi ağ çağrıları için platformdan bağımsız bir arayüzün arkasında farklı platformlar için farklı uygulamalar kullanma.
  5. Kullanıcı arayüzü arka ucu: Birleşik kutular ve pencereler gibi temel widget'ları çizmek için kullanılır. Bu arka uç, platforma özel olmayan genel bir arayüz sunar. Arka planda işletim sistemi kullanıcı arayüzü yöntemlerini kullanır.
  6. JavaScript yorumlayıcısı. JavaScript kodunu ayrıştırmak ve yürütmek için kullanılır.
  7. Veri depolama. Bu bir kalıcılık katmanıdır. Tarayıcının, çerezler gibi her türlü veriyi yerel olarak kaydetmesi gerekebilir. Tarayıcılar ayrıca localStorage, IndexedDB, WebSQL ve FileSystem gibi depolama mekanizmalarını da destekler.
Tarayıcı bileşenleri
Şekil 1: Tarayıcı bileşenleri

Chrome gibi tarayıcıların, her sekme için bir tane olmak üzere oluşturma motorunun birden fazla örneğini çalıştırdığını unutmayın. Her sekme ayrı bir işlemde çalışır.

Oluşturma motorları

Oluşturma motorunun sorumluluğu, istenen içeriklerin tarayıcı ekranında oluşturulması, yani gösterilmesidir.

Varsayılan olarak, oluşturma motoru HTML ve XML dokümanlarını ve resimleri görüntüleyebilir. Eklentiler veya uzantılar aracılığıyla başka veri türlerini de gösterebilir. Örneğin, PDF görüntüleyici eklentisi kullanarak PDF dokümanlarını gösterebilir. Ancak bu bölümde, ana kullanım alanına odaklanacağız: HTML ve CSS kullanılarak biçimlendirilmiş resimleri görüntüleme.

Farklı tarayıcılar farklı oluşturma motorları kullanır: Internet Explorer Trident'i, Firefox Gecko'yu, Safari ise WebKit'i kullanır. Chrome ve Opera (15. sürümden itibaren), WebKit'in bir çatallaması olan Blink'i kullanır.

WebKit, Linux platformu için bir motor olarak başlayan ve Apple tarafından Mac ve Windows'u destekleyecek şekilde değiştirilen açık kaynaklı bir oluşturma motorudur.

Ana akış

Oluşturma motoru, istenen belgenin içeriğini ağ katmanından almaya başlar. Bu işlem genellikle 8 kB'lık parçalar halinde yapılır.

Ardından, oluşturma motorunun temel akışı şu şekildedir:

Oluşturma motoru temel akışı
Şekil 2: Oluşturma motorunun temel akışı

Oluşturma motoru, HTML belgesini ayrıştırmaya başlar ve öğeleri "içerik ağacı" adı verilen bir ağaçtaki DOM düğümlerine dönüştürür. Motor, hem harici CSS dosyalarındaki hem de stil öğelerindeki stil verilerini ayrıştırır. HTML'deki görsel talimatlarla birlikte stil bilgileri, başka bir ağaç oluşturmak için kullanılır: oluşturma ağacı.

Oluşturma ağacı, renk ve boyutlar gibi görsel özelliklere sahip dikdörtgenler içerir. Dikdörtgenler, ekranda gösterilecek doğru sıradadır.

Oluşturma ağacı oluşturulduktan sonra "düzen" işleminden geçer. Bu, her düğüme ekranda görünmesi gereken tam koordinatları vermek anlamına gelir. Sonraki aşama boyama aşamasıdır. Bu aşamada, oluşturma ağacı taranır ve her düğüm, kullanıcı arayüzü arka uç katmanı kullanılarak boyanır.

Bunun kademeli bir süreç olduğunu anlamak önemlidir. Daha iyi bir kullanıcı deneyimi için oluşturma motoru, içerikleri ekranda mümkün olan en kısa sürede göstermeye çalışır. Oluşturma ağacını oluşturmaya ve düzenlemeye başlamadan önce tüm HTML'nin ayrıştırılmasını beklemez. İçeriğin bir kısmı ayrıştırılıp gösterilirken işlem, ağdan gelmeye devam eden içeriğin geri kalanıyla devam eder.

Ana akış örnekleri

WebKit ana akışı.
Şekil 3: WebKit ana akışı
Mozilla'nın Gecko oluşturma motoru ana akışı.
Şekil 4: Mozilla'nın Gecko oluşturma motoru ana akışı

Şekil 3 ve 4'ten, WebKit ve Gecko'nun biraz farklı terminoloji kullanmasına rağmen akışın temelde aynı olduğunu görebilirsiniz.

Gecko, görsel olarak biçimlendirilmiş öğelerin ağacına "çerçeve ağacı" adını verir. Her öğe bir karedir. WebKit, "Oluşturma Ağacı" terimini kullanır ve bu ağaç "Oluşturma Nesneleri"nden oluşur. WebKit, öğelerin yerleştirilmesi için "düzen" terimini kullanırken Gecko buna "yeniden akış" adını verir. "Ek", WebKit'in oluşturma ağacını oluşturmak için DOM düğümlerini ve görsel bilgileri bağlama terimidir. Anlamsal olmayan küçük bir fark, Gecko'nun HTML ile DOM ağacı arasında fazladan bir katmana sahip olmasıdır. "İçerik havuzu" olarak adlandırılır ve DOM öğeleri oluşturmak için kullanılan bir fabrikadır. Akışın her bir bölümünü ele alacağız:

Ayrıştırma - Genel

Ayrıştırma, oluşturma motorunda çok önemli bir işlem olduğundan bu konuyu biraz daha ayrıntılı olarak ele alacağız. Ayrıştırma hakkında kısa bir girişle başlayalım.

Bir dokümanı ayrıştırmak, dokümanı kodun kullanabileceği bir yapıya dönüştürmek anlamına gelir. Ayrıştırma işlemi sonucunda genellikle belgenin yapısını temsil eden bir düğüm ağacı oluşturulur. Buna ayrıştırma ağacı veya söz dizimi ağacı denir.

Örneğin, 2 + 3 - 1 ifadesi ayrıştırıldığında şu ağaç döndürülebilir:

Matematiksel ifade ağacı düğümü.
Şekil 5: Matematiksel ifade ağacı düğümü

Dilbilgisi

Ayrıştırma, belgenin uyduğu söz dizimi kurallarına (yazıldığı dil veya biçim) dayanır. Ayrıştırabileceğiniz her biçimin, kelime listesi ve söz dizimi kurallarından oluşan deterministik bir dil bilgisi olmalıdır. Buna bağlamdan bağımsız dil bilgisi denir. İnsan dilleri bu tür diller değildir ve bu nedenle geleneksel ayrıştırma teknikleriyle ayrıştırılamaz.

Ayrıştırıcı - sözcük analizcisi kombinasyonu

Ayrıştırma iki alt sürece ayrılabilir: sözcük analizi ve söz dizimi analizi.

Sözcüksel analiz, girişin jetonlara bölünmesi işlemidir. Jetonlar, dilin kelime dağarcığını, yani geçerli yapı taşlarının koleksiyonunu oluşturur. İnsan dilinde, o dilin sözlüğünde yer alan tüm kelimelerden oluşur.

Söz dizimi analizi, dilin söz dizimi kurallarının uygulanmasıdır.

Ayrıştırıcılar genellikle işi iki bileşen arasında böler: Girişi geçerli jetonlara bölmekten sorumlu olan lexer (bazen simgeleştirici olarak adlandırılır) ve dil söz dizimi kurallarına göre belge yapısını analiz ederek ayrıştırma ağacını oluşturmaktan sorumlu olan ayrıştırıcı.

Sözcük analizcisi, boşluklar ve satır sonları gibi alakasız karakterleri nasıl çıkaracağını bilir.

Kaynak dokümandan ayrıştırma ağaçlarına
Şekil 6: Kaynak dokümandan ayrıştırma ağaçlarına

Ayrıştırma işlemi yinelemelidir. Ayrıştırıcı genellikle sözcük analizcisinden yeni bir jeton ister ve jetonu söz dizimi kurallarından biriyle eşleştirmeye çalışır. Bir kural eşleşirse ayrıştırma ağacına jetona karşılık gelen bir düğüm eklenir ve ayrıştırıcı başka bir jeton ister.

Eşleşen bir kural yoksa ayrıştırıcı, jetonu dahili olarak depolar ve dahili olarak depolanan tüm jetonlarla eşleşen bir kural bulunana kadar jeton istemeye devam eder. Herhangi bir kural bulunamazsa ayrıştırıcı bir istisna oluşturur. Bu durum, dokümanın geçerli olmadığı ve söz dizimi hataları içerdiği anlamına gelir.

Çeviri

Çoğu durumda ayrıştırma ağacı nihai ürün değildir. Ayrıştırma, çeviride sıklıkla kullanılır: Giriş dokümanını başka bir biçime dönüştürme. Derleme buna bir örnektir. Kaynak kodunu makine koduna derleyen derleyici, önce kodu bir ayrıştırma ağacına ayrıştırır ve ardından ağacı bir makine kodu belgesine çevirir.

Derleme akışı
Şekil 7: Derleme akışı

Ayrıştırma örneği

Şekil 5'te matematiksel bir ifadeden ayrıştırma ağacı oluşturduk. Basit bir matematik dili tanımlamaya çalışalım ve ayrıştırma sürecini görelim.

Söz dizimi:

  1. Dil söz dizimi yapı taşları ifadeler, terimler ve işlemlerdir.
  2. Dilimizde dilediğiniz sayıda ifade bulunabilir.
  3. İfade, bir "terim" ve ardından bir "işlem" ve başka bir terim olarak tanımlanır.
  4. İşlem, artı veya eksi simgesidir.
  5. Terim, tam sayı jetonu veya ifadedir.

2 + 3 - 1 girişini analiz edelim.

Bir kuralla eşleşen ilk alt dize 2: 5 numaralı kurala göre bu bir terimdir. İkinci eşleşme 2 + 3: Bu, üçüncü kural olan "bir terim, ardından bir işlem ve ardından başka bir terim" ile eşleşir. Bir sonraki eşleşme yalnızca girişin sonunda gerçekleşir. 2 + 3 - 1 bir ifadedir çünkü 2 + 3'in bir terim olduğunu zaten biliyoruz. Dolayısıyla bir terim, ardından bir işlem ve ardından başka bir terim var. 2 + + hiçbir kuralla eşleşmeyeceğinden geçersiz bir giriştir.

Kelime ve söz dizimi için resmi tanımlar

Sözlük genellikle normal ifadelerle ifade edilir.

Örneğin, dilimiz şu şekilde tanımlanır:

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

Gördüğünüz gibi, tam sayılar normal ifadeyle tanımlanır.

Söz dizimi genellikle BNF adlı bir biçimde tanımlanır. Kullanacağımız dil şu şekilde tanımlanacak:

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

Dil bilgisi bağlamdan bağımsız bir dil bilgisi ise bir dilin normal ayrıştırıcılar tarafından ayrıştırılabileceğini söylemiştik. Bağlamdan bağımsız dil bilgisinin sezgisel tanımı, tamamen BNF ile ifade edilebilen bir dil bilgisidir. Resmi bir tanım için Wikipedia'nın bağlamdan bağımsız dil bilgisi hakkındaki makalesine bakın.

Ayrıştırıcı türleri

İki tür ayrıştırıcı vardır: yukarıdan aşağıya ayrıştırıcılar ve aşağıdan yukarıya ayrıştırıcılar. Sezgisel bir açıklama, yukarıdan aşağıya ayrıştırıcıların söz diziminin üst düzey yapısını incelemesi ve bir kural eşleşmesi bulmaya çalışmasıdır. Aşağıdan yukarıya ayrıştırıcılar, girişle başlar ve düşük seviyeli kurallardan başlayıp yüksek seviyeli kurallar karşılanana kadar girişi kademeli olarak söz dizimi kurallarına dönüştürür.

İki ayrıştırıcı türünün örneğimizi nasıl ayrıştıracağını görelim.

Yukarıdan aşağıya ayrıştırıcı, daha üst düzey kuraldan başlar: 2 + 3 ifadesini bir ifade olarak tanımlar. Ardından 2 + 3 - 1 ifadesini bir ifade olarak tanımlar (ifadeyi tanımlama süreci gelişir ve diğer kurallarla eşleşir ancak başlangıç noktası en yüksek düzey kuraldır).

Aşağıdan yukarıya ayrıştırıcı, bir kural eşleşene kadar girişi tarar. Ardından, eşleşen girişi kuralla değiştirir. Bu işlem, girişin sonuna kadar devam eder. Kısmen eşleşen ifade, ayrıştırıcının yığınına yerleştirilir.

Yığınla Giriş
2 + 3 - 1
terim + 3 - 1
terim işlemi 3 - 1
ifade - 1
ifade işlemi 1
ifade -

Bu tür aşağıdan yukarıya ayrıştırıcıya, giriş sağa kaydırıldığı (girişin başlangıcını gösteren ve sağa doğru hareket eden bir işaretçi düşünün) ve kademeli olarak söz dizimi kurallarına indirgendiği için kaydırma-indirgeme ayrıştırıcısı denir.

Ayrıştırıcıları otomatik olarak oluşturma

Ayrıştırıcı oluşturabilen araçlar vardır. Bu araçlara dilinizin dil bilgisini (kelime dağarcığı ve söz dizimi kuralları) girersiniz ve araçlar çalışan bir ayrıştırıcı oluşturur. Ayrıştırıcı oluşturmak için ayrıştırma konusunda derin bir anlayış gerekir ve optimize edilmiş bir ayrıştırıcıyı manuel olarak oluşturmak kolay değildir. Bu nedenle, ayrıştırıcı oluşturucular çok yararlı olabilir.

WebKit, iyi bilinen iki ayrıştırıcı oluşturucu kullanır: sözcük analizcisi oluşturmak için Flex ve ayrıştırıcı oluşturmak için Bison (bunlarla Lex ve Yacc adlarıyla karşılaşabilirsiniz). Flex girişi, jetonların normal ifade tanımlarını içeren bir dosyadır. Bison'un girişi, BNF biçimindeki dil söz dizimi kurallarıdır.

HTML ayrıştırıcı

HTML ayrıştırıcının görevi, HTML işaretlemesini bir ayrıştırma ağacına ayrıştırmaktır.

HTML dil bilgisi

HTML'nin kelime dağarcığı ve söz dizimi, W3C kuruluşu tarafından oluşturulan spesifikasyonlarda tanımlanır.

Ayrıştırmaya giriş bölümünde gördüğümüz gibi, dil bilgisi söz dizimi BNF gibi biçimler kullanılarak resmi olarak tanımlanabilir.

Maalesef geleneksel ayrıştırıcı konularının hiçbiri HTML için geçerli değildir (bunları sadece eğlence olsun diye gündeme getirmedim, CSS ve JavaScript ayrıştırmada kullanılacaklar). HTML, ayrıştırıcıların ihtiyaç duyduğu bağlamdan bağımsız bir dil bilgisiyle kolayca tanımlanamaz.

HTML'yi tanımlamak için resmi bir biçim (DTD - Belge Türü Tanımı) vardır ancak bu, bağlamdan bağımsız bir dil bilgisi değildir.

Bu ilk bakışta garip görünebilir. HTML, XML'e oldukça yakındır. Kullanabileceğiniz birçok XML ayrıştırıcısı vardır. HTML'nin XML varyasyonu olan XHTML de var. Peki aralarındaki büyük fark nedir?

Aradaki fark, HTML yaklaşımının daha "esnek" olmasıdır: Belirli etiketleri (daha sonra dolaylı olarak eklenir) veya bazen başlangıç ya da bitiş etiketlerini vb. atlamanıza olanak tanır. XML'in katı ve zorlu söz diziminin aksine, genel olarak "yumuşak" bir söz dizimine sahiptir.

Bu küçük ayrıntı, büyük bir fark yaratır. Bir yandan, HTML'nin bu kadar popüler olmasının ana nedeni budur: hatalarınızı affeder ve web yazarı için hayatı kolaylaştırır. Öte yandan, resmi bir dil bilgisi yazmayı zorlaştırır. Özetlemek gerekirse, HTML'nin dil bilgisi bağlamdan bağımsız olmadığı için geleneksel ayrıştırıcılar tarafından kolayca ayrıştırılamaz. HTML, XML ayrıştırıcılar tarafından ayrıştırılamaz.

HTML DTD

HTML tanımı, DTD biçimindedir. Bu biçim, SGML ailesinin dillerini tanımlamak için kullanılır. Biçim, izin verilen tüm öğelerin tanımlarını, özelliklerini ve hiyerarşisini içerir. Daha önce gördüğümüz gibi, HTML DTD'si bağlamdan bağımsız bir dil bilgisi oluşturmaz.

DTD'nin birkaç varyasyonu vardır. Yüksek düzey modu yalnızca spesifikasyonlara uygundur ancak diğer modlar, tarayıcıların geçmişte kullandığı işaretleme desteğini içerir. Amaç, eski içeriklerle geriye dönük uyumluluk sağlamaktır. Mevcut katı DTD'yi burada bulabilirsiniz: www.w3.org/TR/html4/strict.dtd

DOM

Çıkış ağacı ("ayrıştırma ağacı"), DOM öğesi ve özellik düğümlerinden oluşan bir ağaçtır. DOM, Belge Nesne Modeli'nin kısaltmasıdır. HTML belgesinin nesne sunumu ve HTML öğelerinin JavaScript gibi dış dünyaya arayüzüdür.

Ağacın kökü "Document" nesnesidir.

DOM, işaretlemeyle neredeyse bire bir ilişkiye sahiptir. Örneğin:

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

Bu işaretleme aşağıdaki DOM ağacına çevrilir:

Örnek işaretlemenin DOM ağacı
Şekil 8: Örnek işaretlemenin DOM ağacı

HTML gibi DOM da W3C kuruluşu tarafından belirtilir. www.w3.org/DOM/DOMTR adresini ziyaret edin. Belgeleri değiştirmek için kullanılan genel bir spesifikasyondur. Belirli bir modül, HTML'ye özgü öğeleri açıklar. HTML tanımlarını şu adreste bulabilirsiniz: www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

Ağacın DOM düğümleri içerdiğini söylediğimde, ağacın DOM arayüzlerinden birini uygulayan öğelerden oluşturulduğunu kastediyorum. Tarayıcılar, tarayıcı tarafından dahili olarak kullanılan diğer özelliklere sahip somut uygulamalar kullanır.

Ayrıştırma algoritması

Önceki bölümlerde gördüğümüz gibi, HTML normal yukarıdan aşağıya veya aşağıdan yukarıya ayrıştırıcılar kullanılarak ayrıştırılamaz.

Bunun nedenleri şunlardır:

  1. Dilin esnek yapısı.
  2. Tarayıcıların, geçersiz HTML'nin iyi bilinen durumlarını desteklemek için geleneksel hata toleransına sahip olması.
  3. Ayrıştırma işlemi yeniden girilebilir. Diğer dillerde kaynak, ayrıştırma sırasında değişmez ancak HTML'de dinamik kod (document.write() çağrıları içeren komut dosyası öğeleri gibi) ek jetonlar ekleyebilir. Bu nedenle, ayrıştırma işlemi girişi değiştirir.

Tarayıcılar, normal ayrıştırma tekniklerini kullanamadıkları için HTML'yi ayrıştırmak üzere özel ayrıştırıcılar oluşturur.

Ayrıştırma algoritması, HTML5 spesifikasyonunda ayrıntılı olarak açıklanmıştır. Algoritma iki aşamadan oluşur: simgeleştirme ve ağaç oluşturma.

Jetonlara ayırma, girişi jetonlara ayrıştıran sözcüksel analizdir. HTML jetonları arasında başlangıç etiketleri, bitiş etiketleri, özellik adları ve özellik değerleri bulunur.

Jetonlaştırıcı, jetonu tanır, ağaç oluşturucuya verir ve bir sonraki jetonu tanımak için bir sonraki karakteri kullanır. Bu işlem, girişin sonuna kadar devam eder.

HTML ayrıştırma akışı (HTML5 spesifikasyonundan alınmıştır)
Şekil 9: HTML ayrıştırma akışı (HTML5 spesifikasyonundan alınmıştır)

Tokenizasyon algoritması

Algoritmanın çıkışı bir HTML jetonudur. Algoritma, durum makinesi olarak ifade edilir. Her durum, giriş akışının bir veya daha fazla karakterini kullanır ve sonraki durumu bu karakterlere göre günceller. Bu karar, mevcut jetonlaştırma durumundan ve ağaç oluşturma durumundan etkilenir. Bu, aynı tüketilen karakterin mevcut duruma bağlı olarak doğru bir sonraki durum için farklı sonuçlar vereceği anlamına gelir. Algoritma, tam olarak açıklanamayacak kadar karmaşıktır. Bu nedenle, ilkeyi anlamamıza yardımcı olacak basit bir örneğe bakalım.

Temel örnek: Aşağıdaki HTML'yi token'lara ayırma:

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

İlk durum, "Veri durumu"dur. < karakteriyle karşılaşıldığında durum "Etiket açık durumu" olarak değişir. a-z karakterinin kullanılması, "Başlangıç etiketi jetonu" oluşturulmasına neden olur ve durum "Etiket adı durumu" olarak değiştirilir. > karakteri kullanılana kadar bu durumda kalırız. Her karakter yeni jeton adına eklenir. Bizim örneğimizde oluşturulan jeton bir html jetonudur.

> etiketine ulaşıldığında mevcut jeton yayınlanır ve durum tekrar "Veri durumu" olarak değişir. <body> etiketi de aynı adımlarla işlenir. Şu ana kadar html ve body etiketleri yayınlandı. Şimdi "Veri durumu" bölümüne geri dönelim. Hello world dizesinin H karakteri kullanıldığında bir karakter jetonu oluşturulur ve yayınlanır. Bu işlem, </body> dizesinin < karakterine ulaşılana kadar devam eder. Hello world ifadesinin her karakteri için bir karakter jetonu oluşturulur.

Şimdi "Etiket açık durumu"na geri döndük. Sonraki giriş / kullanıldığında bir end tag token oluşturulur ve "Etiket adı durumu"'na geçilir. > değerine ulaşana kadar bu durumda kalırız. Ardından yeni etiket jetonu yayınlanır ve "Veri durumu"'na geri döneriz. </html> girişi, önceki örnekte olduğu gibi değerlendirilir.

Örnek girişi token'lara ayırma
Şekil 10: Örnek girişi parçalara ayırma

Ağaç oluşturma algoritması

Ayrıştırıcı oluşturulduğunda Document nesnesi de oluşturulur. Ağaç oluşturma aşamasında, kökünde Document bulunan DOM ağacı değiştirilir ve ağaca öğeler eklenir. Simgeleştirici tarafından oluşturulan her düğüm, ağaç oluşturucu tarafından işlenir. Spesifikasyon, her jeton için hangi DOM öğesinin alakalı olduğunu ve bu jeton için oluşturulacağını tanımlar. Öğe, DOM ağacına ve açık öğeler yığınına eklenir. Bu yığın, iç içe yerleştirme uyuşmazlıklarını ve kapatılmamış etiketleri düzeltmek için kullanılır. Algoritma, durum makinesi olarak da tanımlanır. Bu durumlara "ekleme modları" denir.

Örnek giriş için ağaç oluşturma sürecine bakalım:

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

Ağaç oluşturma aşamasının girişi, jetonlara ayırma aşamasından gelen bir jeton dizisidir. İlk mod, "başlangıç modu"dur. "html" jetonunun alınması, "before html" moduna geçilmesine ve jetonun bu modda yeniden işlenmesine neden olur. Bu işlem, kök Document nesnesine eklenecek HTMLHtmlElement öğesinin oluşturulmasına neden olur.

Durum "başlıktan önce" olarak değiştirilir. Ardından "body" jetonu alınır. "head" jetonumuz olmasa da dolaylı olarak bir HTMLHeadElement oluşturulur ve ağaca eklenir.

Şimdi "in head" moduna, ardından "after head" moduna geçiyoruz. Gövde jetonu yeniden işlenir, bir HTMLBodyElement oluşturulup eklenir ve mod "gövdede" olarak değiştirilir.

"Hello world" dizesinin karakter jetonları alındı. İlk karakter bir "Metin" düğümünün oluşturulmasına ve eklenmesine neden olur, diğer karakterler ise bu düğüme eklenir.

Gövde bitiş jetonunun alınması, "gövdeden sonra" moduna geçişe neden olur. Şimdi, bizi "after after body" moduna geçirecek html bitiş etiketini alacağız. Dosya sonu jetonunun alınması, ayrıştırma işlemini sonlandırır.

Örnek HTML'nin ağaç yapısı.
Şekil 11: Örnek HTML'nin ağaç yapısı

Ayrıştırma tamamlandığında yapılacak işlemler

Bu aşamada tarayıcı, dokümanı etkileşimli olarak işaretler ve "ertelenmiş" modda olan komut dosyalarını ayrıştırmaya başlar. Bu komut dosyaları, doküman ayrıştırıldıktan sonra yürütülmelidir. Ardından, doküman durumu "tamamlandı" olarak ayarlanır ve bir "yükleme" etkinliği tetiklenir.

HTML5 spesifikasyonunda, simgeleştirme ve ağaç oluşturma için kullanılan algoritmaların tamamını görebilirsiniz.

Tarayıcıların hata toleransı

HTML sayfasında hiçbir zaman "Geçersiz Söz Dizimi" hatası almazsınız. Tarayıcılar geçersiz içerikleri düzeltir ve devam eder.

Örneğin, şu HTML'yi ele alalım:

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

Yaklaşık bir milyon kuralı ihlal etmiş olmalıyım ("mytag" standart bir etiket değil, "p" ve "div" öğelerinin yanlış iç içe yerleştirilmesi ve daha fazlası) ancak tarayıcı yine de doğru şekilde gösteriyor ve şikayet etmiyor. Bu nedenle, ayrıştırıcı kodunun büyük bir kısmı HTML yazarının hatalarını düzeltir.

Hata işleme, tarayıcılarda oldukça tutarlıdır ancak şaşırtıcı bir şekilde HTML spesifikasyonlarının bir parçası olmamıştır. Yer işareti ekleme ve geri/ileri düğmeleri gibi, bu da tarayıcılarda yıllar içinde geliştirilen bir özelliktir. Birçok sitede tekrarlanan, bilinen geçersiz HTML yapıları vardır ve tarayıcılar bunları diğer tarayıcılarla uyumlu bir şekilde düzeltmeye çalışır.

HTML5 spesifikasyonu bu koşullardan bazılarını tanımlar. (WebKit, HTML ayrıştırıcı sınıfının başındaki yorumda bunu güzel bir şekilde özetler.)

Ayrıştırıcı, jetonlara ayrılmış girişi dokümana ayrıştırarak doküman ağacını oluşturur. Doküman doğru biçimlendirilmişse ayrıştırma işlemi kolaydır.

Maalesef düzgün biçimlendirilmemiş birçok HTML belgesini işlememiz gerektiğinden ayrıştırıcının hatalara karşı toleranslı olması gerekir.

En azından aşağıdaki hata koşullarını ele almamız gerekir:

  1. Eklenen öğe, bazı dış etiketlerin içinde açıkça yasaklanmıştır. Bu durumda, öğeyi yasaklayan etikete kadar tüm etiketleri kapatıp ardından öğeyi eklememiz gerekir.
  2. Öğeyi doğrudan eklememize izin verilmez. Belgeyi yazan kişi, aradaki bir etiketi unutmuş olabilir (veya aradaki etiket isteğe bağlıdır). Bu durum şu etiketlerde geçerli olabilir: HTML HEAD BODY TBODY TR TD LI (unuttuğum var mı?).
  3. Satır içi öğenin içine bir blok öğesi eklemek istiyoruz. Bir sonraki daha yüksek blok öğesine kadar tüm satır içi öğeleri kapatın.
  4. Bu işe yaramazsa öğeyi eklememize izin verilene kadar öğeleri kapatın veya etiketi yoksayın.

WebKit hata toleransıyla ilgili bazı örneklere bakalım:

<br> yerine </br>

Bazı siteler <br> yerine </br> kullanır. WebKit, IE ve Firefox ile uyumlu olması için bunu <br> olarak değerlendirir.

Kod:

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

Hata işleme dahili olarak gerçekleşir ve kullanıcıya gösterilmez.

Başıboş bir masa

Başıboş tablo, başka bir tablonun içinde olan ancak tablo hücresinin içinde olmayan bir tablodur.

Örneğin:

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

WebKit, hiyerarşiyi iki kardeş tabloya dönüştürür:

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

Kod:

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

WebKit, geçerli öğe içerikleri için bir yığın kullanır: İç tabloyu dış tablo yığınından çıkarır. Tablolar artık kardeş olacak.

İç içe yerleştirilmiş form öğeleri

Kullanıcı bir formu başka bir formun içine yerleştirirse ikinci form yoksayılır.

Kod:

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

Çok derin bir etiket hiyerarşisi

Yorum her şeyi açıklıyor.

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

Yanlış yerleştirilmiş html veya body bitiş etiketleri

Yorum zaten her şeyi açıklıyor.

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

Bu nedenle, web yazarlarının dikkatli olması gerekir. WebKit hata toleransı kod snippet'inde örnek olarak görünmek istemiyorsanız iyi biçimlendirilmiş HTML yazın.

CSS ayrıştırma

Giriş bölümündeki ayrıştırma kavramlarını hatırlıyor musunuz? HTML'nin aksine CSS, bağlamdan bağımsız bir dilbilgisine sahiptir ve girişte açıklanan ayrıştırıcı türleri kullanılarak ayrıştırılabilir. Aslında CSS spesifikasyonu, CSS sözcüksel ve söz dizimi dil bilgisini tanımlar.

Bazı örneklere göz atalım:

Sözcüksel dil bilgisi (kelime dağarcığı), her jeton için normal ifadelerle tanımlanır:

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", sınıf adı gibi bir tanımlayıcının kısaltmasıdır. "name", bir öğe kimliğidir ("#" ile belirtilir)

Söz dizimi dil bilgisi BNF'de açıklanmıştır.

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*] ')' ]
  ;

Açıklama:

Kural grubu şu yapıya sahiptir:

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

div.error ve a.error seçicidir. Süslü parantezlerin içindeki kısım, bu kural grubu tarafından uygulanan kuralları içerir. Bu yapı, şu tanımda resmi olarak açıklanmıştır:

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

Bu, kural kümesinin bir seçici veya isteğe bağlı olarak virgül ve boşluklarla ayrılmış bir dizi seçici olduğu anlamına gelir (S, boşluk anlamına gelir). Kural kümesi, süslü parantezler ve bunların içinde bir bildirim veya isteğe bağlı olarak noktalı virgülle ayrılmış bir dizi bildirim içerir. "declaration" ve "selector" aşağıdaki BNF tanımlarında tanımlanacaktır.

WebKit CSS ayrıştırıcısı

WebKit, CSS dil bilgisi dosyalarından otomatik olarak ayrıştırıcılar oluşturmak için Flex ve Bison ayrıştırıcı oluşturucularını kullanır. Ayrıştırıcıya giriş bölümünden hatırlayacağınız üzere Bison, aşağıdan yukarıya bir kaydırma-azaltma ayrıştırıcısı oluşturur. Firefox, manuel olarak yazılmış bir yukarıdan aşağı ayrıştırıcı kullanır. Her iki durumda da her CSS dosyası bir StyleSheet nesnesine ayrıştırılır. Her nesne CSS kuralları içerir. CSS kuralı nesneleri, seçici ve bildirim nesneleri ile CSS dil bilgisine karşılık gelen diğer nesneleri içerir.

CSS ayrıştırılıyor.
12. şekil: CSS'yi ayrıştırma

Komut dosyaları ve stil sayfaları için işleme sırası

Komut Dosyaları

Web modeli eşzamanlıdır. Yazarlar, ayrıştırıcı bir <script> etiketine ulaştığında komut dosyalarının hemen ayrıştırılıp yürütülmesini bekler. Komut dosyası yürütülene kadar belgenin ayrıştırılması durdurulur. Komut dosyası hariciyse kaynağın önce ağdan getirilmesi gerekir. Bu işlem de eşzamanlı olarak yapılır ve kaynak getirilene kadar ayrıştırma durdurulur. Bu, uzun yıllar boyunca kullanılan modeldi ve HTML4 ile 5 spesifikasyonlarında da belirtilmiştir. Yazarlar, bir komut dosyasına "defer" özelliğini ekleyebilir. Bu durumda, komut dosyası doküman ayrıştırmayı durdurmaz ve doküman ayrıştırıldıktan sonra yürütülür. HTML5, komut dosyasını eşzamansız olarak işaretleme seçeneği ekler. Böylece komut dosyası farklı bir iş parçacığı tarafından ayrıştırılır ve yürütülür.

Tahmine dayalı ayrıştırma

Hem WebKit hem de Firefox bu optimizasyonu yapar. Komut dosyaları yürütülürken başka bir iş parçacığı, dokümanın geri kalanını ayrıştırır ve ağdan yüklenmesi gereken diğer kaynakları bulup yükler. Bu sayede kaynaklar paralel bağlantılar üzerinden yüklenebilir ve genel hız artırılır. Not: Tahmini ayrıştırıcı yalnızca harici komut dosyaları, stil sayfaları ve resimler gibi harici kaynaklara yapılan referansları ayrıştırır. DOM ağacını değiştirmez. Bu işlem ana ayrıştırıcıya bırakılır.

Stil sayfaları

Stil sayfalarının ise farklı bir modeli vardır. Stil sayfaları DOM ağacını değiştirmediğinden, bunları beklemek ve doküman ayrıştırmayı durdurmak için bir neden yok gibi görünüyor. Ancak komut dosyalarının, doküman ayrıştırma aşamasında stil bilgisi istemesiyle ilgili bir sorun vardır. Stil henüz yüklenip ayrıştırılmadıysa komut dosyası yanlış yanıtlar alır ve bu durum birçok soruna neden olur. Uç bir durum gibi görünse de oldukça yaygındır. Firefox, hâlâ yüklenmekte ve ayrıştırılmakta olan bir stil sayfası olduğunda tüm komut dosyalarını engeller. WebKit, komut dosyalarını yalnızca yüklenmemiş stil sayfalarından etkilenebilecek belirli stil özelliklerine erişmeye çalıştıklarında engeller.

Oluşturma ağacı oluşturma

DOM ağacı oluşturulurken tarayıcı, oluşturma ağacı adında başka bir ağaç oluşturur. Bu ağaç, görsel öğelerin gösterilecekleri sırayı gösterir. Belgenin görsel temsilidir. Bu ağacın amacı, içeriklerin doğru sırayla boyanmasını sağlamaktır.

Firefox, oluşturma ağacındaki öğeleri "çerçeveler" olarak adlandırır. WebKit, oluşturucu veya oluşturma nesnesi terimini kullanır.

Oluşturucu, kendisini ve alt öğelerini nasıl düzenleyeceğini ve boyayacağını bilir.

WebKit'in oluşturucuların temel sınıfı olan RenderObject sınıfı aşağıdaki tanıma sahiptir:

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
}

Her oluşturucu, CSS2 spesifikasyonunda açıklandığı gibi genellikle bir düğümün CSS kutusuna karşılık gelen dikdörtgen bir alanı temsil eder. Genişlik, yükseklik ve konum gibi geometrik bilgileri içerir.

Kutu türü, düğümle alakalı stil özelliğinin "display" değerinden etkilenir (stil hesaplama bölümüne bakın). Aşağıda, görüntüleme özelliğine göre bir DOM düğümü için ne tür bir oluşturucunun oluşturulması gerektiğine karar veren WebKit kodu verilmiştir:

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

Öğe türü de dikkate alınır: Örneğin, form kontrolleri ve tabloların özel çerçeveleri vardır.

WebKit'te bir öğe özel bir oluşturucu oluşturmak isterse createRenderer() yöntemini geçersiz kılar. Oluşturucular, geometrik olmayan bilgiler içeren stil nesnelerini işaret eder.

Oluşturma ağacının DOM ağacıyla ilişkisi

Oluşturucular DOM öğelerine karşılık gelir ancak ilişki bire bir değildir. Görsel olmayan DOM öğeleri, oluşturma ağacına eklenmez. "head" öğesi buna bir örnektir. Ayrıca, görüntüleme değeri "none" olarak atanan öğeler ağaçta görünmez ("hidden" görünürlüğe sahip öğeler ağaçta görünür).

Çeşitli görsel nesnelere karşılık gelen DOM öğeleri vardır. Bunlar genellikle tek bir dikdörtgenle tanımlanamayan karmaşık yapıya sahip öğelerdir. Örneğin, "select" öğesinin üç oluşturucusu vardır: biri görüntüleme alanı için, biri açılır liste kutusu için ve biri de düğme için. Ayrıca, genişlik bir satır için yeterli olmadığından metin birden fazla satıra bölündüğünde yeni satırlar ek oluşturucu olarak eklenir.

Birden fazla oluşturucuya bir başka örnek de bozuk HTML'dir. CSS spesifikasyonuna göre, satır içi öğeler yalnızca blok öğeleri veya yalnızca satır içi öğeleri içermelidir. Karma içerik söz konusu olduğunda, satır içi öğeleri sarmalamak için anonim blok oluşturucular oluşturulur.

Bazı oluşturma nesneleri bir DOM düğümüne karşılık gelir ancak ağaçta aynı yerde bulunmaz. Kayan ve mutlak konumlandırılmış öğeler akışın dışındadır, ağacın farklı bir bölümüne yerleştirilir ve gerçek çerçeveyle eşlenir. Yer tutucu çerçeve, bu öğelerin bulunması gereken yerdir.

Oluşturma ağacı ve karşılık gelen DOM ağacı.
Şekil 13: Oluşturma ağacı ve karşılık gelen DOM ağacı. "Görüntü alanı", ilk kapsayıcı bloktur. WebKit'te bu, "RenderView" nesnesi olacaktır

Ağaç oluşturma akışı

Firefox'ta sunu, DOM güncellemeleri için dinleyici olarak kaydedilir. Sunu, çerçeve oluşturma işlemini FrameConstructor öğesine devreder ve kurucu, stili çözümler (stil hesaplama bölümüne bakın) ve bir çerçeve oluşturur.

WebKit'te, stilin çözümlenmesi ve oluşturucunun oluşturulması işlemine "ekleme" adı verilir. Her DOM düğümünün bir "attach" yöntemi vardır. Ek senkronize bir işlemdir. DOM ağacına düğüm ekleme işlemi, yeni düğümün "attach" yöntemini çağırır.

HTML ve gövde etiketlerinin işlenmesi, oluşturma ağacı kökünün oluşturulmasıyla sonuçlanır. Kök oluşturma nesnesi, CSS spesifikasyonunun kapsayıcı blok olarak adlandırdığı şeye karşılık gelir: diğer tüm blokları içeren en üstteki blok. Boyutları, görüntü alanıdır (tarayıcı penceresi görüntüleme alanı boyutları). Firefox bunu ViewPortFrame, WebKit ise RenderView olarak adlandırır. Bu, belgenin işaret ettiği oluşturma nesnesidir. Ağacın geri kalanı, DOM düğümleri eklenerek oluşturulur.

İşleme modeliyle ilgili CSS2 spesifikasyonuna bakın.

Stil hesaplama

Oluşturma ağacını oluşturmak için her oluşturma nesnesinin görsel özelliklerinin hesaplanması gerekir. Bu işlem, her öğenin stil özellikleri hesaplanarak yapılır.

Stil, çeşitli kaynaklardan stil sayfaları, satır içi stil öğeleri ve HTML'deki görsel özellikleri (ör. "bgcolor" özelliği) içerir. İkincisi, eşleşen CSS stil özelliklerine çevrilir.

Stil sayfalarının kaynakları, tarayıcının varsayılan stil sayfaları, sayfa yazarı tarafından sağlanan stil sayfaları ve kullanıcı stil sayfalarıdır. Bunlar, tarayıcı kullanıcısı tarafından sağlanan stil sayfalarıdır (tarayıcılar, favori stillerinizi tanımlamanıza olanak tanır). Örneğin, Firefox'ta bu işlem, "Firefox Profili" klasörüne bir stil sayfası yerleştirilerek yapılır.

Stil hesaplaması birkaç zorluk getirir:

  1. Stil verileri, çok sayıda stil özelliğini barındıran çok büyük bir yapıdır. Bu durum, bellek sorunlarına neden olabilir.
  2. Her öğe için eşleşen kuralları bulmak, optimize edilmediği takdirde performans sorunlarına neden olabilir. Eşleşmeleri bulmak için her öğe için tüm kural listesini incelemek zor bir iştir. Seçiciler, eşleştirme işleminin umut verici görünen ancak sonuçsuz kalan bir yolda başlamasına ve başka bir yolun denenmesine neden olabilecek karmaşık bir yapıya sahip olabilir.

    Örneğin, şu bileşik seçici:

    div div div div{
    ...
    }
    

    Kuralların, 3 div'in alt öğesi olan bir <div> için geçerli olduğu anlamına gelir. Kuralın belirli bir <div> öğesi için geçerli olup olmadığını kontrol etmek istediğinizi varsayalım. Kontrol etmek için ağaçta belirli bir yol seçersiniz. Yalnızca iki div olduğunu ve kuralın geçerli olmadığını öğrenmek için düğüm ağacını yukarı doğru incelemeniz gerekebilir. Ardından ağaçtaki diğer yolları denemeniz gerekir.

  3. Kuralların uygulanması, kuralların hiyerarşisini tanımlayan oldukça karmaşık basamaklı kuralları içerir.

Tarayıcıların bu sorunlarla nasıl karşılaştığına bakalım:

Stil verilerini paylaşma

WebKit düğümleri, stil nesnelerine (RenderStyle) referans verir. Bu nesneler bazı durumlarda düğümler tarafından paylaşılabilir. Düğümler kardeş veya kuzendir ve:

  1. Öğeler aynı fare durumunda olmalıdır (ör.biri :hover durumundayken diğeri olamaz).
  2. Hiçbir öğenin kimliği olmamalıdır.
  3. Etiket adları eşleşmelidir.
  4. Sınıf özellikleri eşleşmelidir.
  5. Eşlenen özellikler aynı olmalıdır.
  6. Bağlantı durumları eşleşmelidir
  7. Odaklanmış durumlar eşleşmelidir
  8. Hiçbir öğe, özellik seçicilerden etkilenmemelidir. Etkilenme, seçicinin herhangi bir konumunda özellik seçici kullanan herhangi bir seçici eşleşmesinin olması olarak tanımlanır.
  9. Öğelerde satır içi stil özelliği bulunmamalıdır.
  10. Hiçbir kardeş seçici kullanılmamalıdır. WebCore, herhangi bir kardeş seçiciyle karşılaşıldığında genel bir anahtar oluşturur ve bunlar mevcut olduğunda tüm doküman için stil paylaşımını devre dışı bırakır. Buna + seçici ve :first-child ile :last-child gibi seçiciler dahildir.

Firefox kural ağacı

Firefox'ta, stil hesaplamasını kolaylaştırmak için iki ek ağaç bulunur: kural ağacı ve stil bağlam ağacı. WebKit'te de stil nesneleri vardır ancak bunlar stil bağlamı ağacı gibi bir ağaçta depolanmaz. Yalnızca DOM düğümü, ilgili stiline işaret eder.

Firefox stili bağlam ağacı.
Şekil 14: Firefox stil bağlam ağacı.

Stil bağlamları bitiş değerleri içerir. Değerler, tüm eşleşen kuralların doğru sırada uygulanması ve mantıksal değerleri somut değerlere dönüştüren işlemlerin gerçekleştirilmesiyle hesaplanır. Örneğin, mantıksal değer ekranın bir yüzdesiyse hesaplanır ve mutlak birimlere dönüştürülür. Kural ağacı fikri gerçekten çok akıllıca. Bu değerlerin düğümler arasında paylaşılmasını sağlayarak tekrar hesaplanmalarını önler. Bu sayede yerden de tasarruf edersiniz.

Eşleşen tüm kurallar bir ağaçta depolanır. Bir yoldaki alt düğümler daha yüksek önceliğe sahiptir. Ağaç, bulunan kural eşleşmelerinin tüm yollarını içerir. Kurallar, gerektiğinde depolanır. Ağaç, başlangıçta her düğüm için hesaplanmaz ancak bir düğüm stilinin hesaplanması gerektiğinde hesaplanan yollar ağaca eklenir.

Buradaki amaç, ağaç yollarını bir sözlükteki kelimeler olarak görmektir. Bu kural ağacını daha önce hesapladığımızı varsayalım:

Hesaplanmış kural ağacı
Şekil 15: Hesaplanmış kural ağacı.

İçerik ağacındaki başka bir öğe için kuralları eşleştirmemiz gerektiğini ve eşleşen kuralların (doğru sırada) B-E-I olduğunu varsayalım. A-B-E-I-L yolunu daha önce hesapladığımız için bu yol ağaçta zaten mevcut. Artık daha az işimiz olacak.

Ağacın işimizi nasıl kolaylaştırdığına bakalım.

Yapılara bölme

Stil bağlamları yapılara ayrılır. Bu yapılar, kenarlık veya renk gibi belirli bir kategori için stil bilgileri içerir. Bir yapıdaki tüm özellikler devralınır veya devralınmaz. Devralınan özellikler, öğe tarafından tanımlanmadığı sürece üst öğesinden devralınan özelliklerdir. Devralınmayan özellikler ("sıfırlama" özellikleri olarak adlandırılır), tanımlanmamışsa varsayılan değerleri kullanır.

Ağaç, ağaçtaki tüm yapıları (hesaplanan nihai değerleri içeren) önbelleğe alarak bize yardımcı olur. Buradaki amaç, alt düğüm bir yapı için tanım sağlamadıysa üst düğümdeki önbelleğe alınmış bir yapının kullanılabilmesidir.

Kural ağacını kullanarak stil bağlamlarını hesaplama

Belirli bir öğenin stil bağlamını hesaplarken önce kural ağacında bir yol hesaplar veya mevcut bir yolu kullanırız. Ardından, yeni stil bağlamımızdaki yapıları doldurmak için yoldaki kuralları uygulamaya başlarız. Yolun en alt düğümünden (en yüksek önceliğe sahip olan, genellikle en spesifik seçici) başlar ve yapımız dolana kadar ağacı yukarı doğru tararız. Bu kural düğümünde yapı için bir spesifikasyon yoksa büyük ölçüde optimizasyon yapabiliriz. Ağaçta yukarı doğru ilerleyerek yapıyı tam olarak belirten bir düğüm bulur ve bu düğümü işaret ederiz. Bu, en iyi optimizasyondur. Yapının tamamı paylaşılır. Bu sayede, bitiş değerlerinin hesaplanması ve bellekten tasarruf edilir.

Kısmi tanımlar bulursak yapı doldurulana kadar ağaçta yukarı doğru ilerleriz.

Yapımız için herhangi bir tanım bulamazsak ve yapı "devralınan" bir türse bağlam ağacında üst öğemizin yapısını işaret ederiz. Bu durumda yapıları paylaşmayı da başardık. Sıfırlama yapısıysa varsayılan değerler kullanılır.

En spesifik düğüm değer ekliyorsa bu değerleri gerçek değerlere dönüştürmek için bazı ek hesaplamalar yapmamız gerekir. Ardından, sonucu ağaç düğümünde önbelleğe alırız. Böylece alt öğeler tarafından kullanılabilir.

Bir öğenin aynı ağaç düğümüne işaret eden bir eşi veya kardeşi varsa tüm stil bağlamı aralarında paylaşılabilir.

Bir örneğe bakalım: Şu HTML'ye sahip olduğumuzu varsayalım

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

Ayrıca aşağıdaki kurallar geçerlidir:

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

İşleri basitleştirmek için yalnızca iki yapıyı doldurmamız gerektiğini varsayalım: renk yapısı ve kenar boşluğu yapısı. Renk yapısı yalnızca bir üye içerir: renk Kenar boşluğu yapısı dört tarafı içerir.

Sonuçta elde edilen kural ağacı aşağıdaki gibi görünür (düğümler, düğüm adıyla işaretlenir: işaret ettikleri kuralın numarası):

Kural ağacı
Şekil 16: Kural ağacı

Bağlam ağacı şu şekilde görünür (düğüm adı: işaret ettikleri kural düğümü):

Bağlam ağacı.
Şekil 17: Bağlam ağacı

HTML'yi ayrıştırdığımızı ve ikinci <div> etiketine ulaştığımızı varsayalım. Bu düğüm için bir stil bağlamı oluşturmamız ve stil yapılarını doldurmamız gerekiyor.

Kuralları eşleştiririz ve <div> için eşleşen kuralların 1, 2 ve 6 olduğunu görürüz. Bu, ağaçta öğemizin kullanabileceği bir yolun zaten mevcut olduğu ve 6. kural için bu yola başka bir düğüm (kural ağacındaki F düğümü) eklememiz gerektiği anlamına gelir.

Bir stil bağlamı oluşturup bunu bağlam ağacına yerleştiririz. Yeni stil bağlamı, kural ağacındaki F düğümünü işaret eder.

Şimdi stil yapılarını doldurmamız gerekiyor. Öncelikle margin yapısını dolduracağız. Son kural düğümü (F) kenar boşluğu yapısına eklenmediğinden, önceki bir düğüm ekleme işleminde hesaplanan önbelleğe alınmış bir yapı bulup kullanana kadar ağaçta yukarı çıkabiliriz. Bu kuralı, kâr marjı kurallarını belirten en üstteki düğüm olan B düğümünde buluruz.

Renk yapısı için bir tanımımız var. Bu nedenle, önbelleğe alınmış bir yapı kullanamayız. Renk özelliğinin tek bir alt özelliği olduğundan diğer özellikleri doldurmak için ağaçta yukarı çıkmamız gerekmez. Bitiş değerini hesaplar (dizeyi RGB'ye dönüştürür vb.) ve hesaplanan yapıyı bu düğümde önbelleğe alırız.

İkinci <span> öğesiyle çalışmak daha da kolaydır. Kuralları eşleştirir ve önceki aralıkta olduğu gibi G kuralına işaret ettiği sonucuna varırız. Aynı düğüme işaret eden kardeş öğelerimiz olduğundan, tüm stil bağlamını paylaşabilir ve yalnızca önceki span'in bağlamına işaret edebiliriz.

Üst öğeden devralınan kuralları içeren yapılar için önbelleğe alma işlemi bağlam ağacında yapılır (renk özelliği aslında devralınır ancak Firefox bunu sıfırlanmış olarak kabul eder ve kural ağacında önbelleğe alır).

Örneğin, bir paragraftaki yazı tipleri için kurallar eklediysek:

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

Ardından, bağlam ağacındaki div'in alt öğesi olan paragraf öğesi, üst öğesiyle aynı yazı tipi yapısını paylaşabilir. Bu, paragraf için yazı tipi kuralı belirtilmediğinde geçerlidir.

Kural ağacı olmayan WebKit'te, eşleşen bildirimler dört kez taranır. Önce önemli olmayan yüksek öncelikli özellikler (diğerleri bunlara bağlı olduğu için önce uygulanması gereken özellikler, örneğin display), ardından yüksek öncelikli önemli, sonra normal öncelikli önemli olmayan ve son olarak normal öncelikli önemli kurallar uygulanır. Bu, birden çok kez görünen özelliklerin doğru basamak sırasına göre çözümleneceği anlamına gelir. Sonuncusu kazanır.

Özetlemek gerekirse: Stil nesnelerini (tamamen veya içindeki yapıların bir kısmını) paylaşmak 1. ve 3. sorunları çözer. Firefox kural ağacı, özelliklerin doğru sırada uygulanmasına da yardımcı olur.

Kolay bir eşleşme için kuralları manipüle etme

Stil kuralları için çeşitli kaynaklar vardır:

  1. Harici stil sayfalarında veya stil öğelerinde CSS kuralları. css p {color: blue}
  2. Satır içi stil özellikleri (ör. html <p style="color: blue" />)
  3. HTML görsel özellikleri (ilgili stil kurallarıyla eşlenir) html <p bgcolor="blue" /> Son ikisi, stil özelliklerine sahip olduğu için öğeyle kolayca eşleştirilir ve HTML özellikleri, öğe anahtar olarak kullanılarak eşlenebilir.

2. sorunda daha önce belirtildiği gibi, CSS kuralı eşleştirme daha zor olabilir. Bu zorluğu çözmek için kurallar, daha kolay erişim sağlayacak şekilde değiştirilir.

Stil sayfası ayrıştırıldıktan sonra kurallar, seçiciye göre birkaç karma haritadan birine eklenir. Kimliğe, sınıf adına, etiket adına göre eşlemeler ve bu kategorilere uymayan her şey için genel bir eşleme vardır. Seçici bir kimlikse kural kimlik eşlemesine, sınıfsa sınıf eşlemesine eklenir.

Bu değişiklik, kuralların eşleştirilmesini çok daha kolay hale getirir. Her bildirime bakmaya gerek yoktur: Bir öğeyle ilgili kuralları haritalardan çıkarabiliriz. Bu optimizasyon, kuralların% 95'inden fazlasını ortadan kaldırır. Böylece, eşleştirme işlemi sırasında bu kuralların dikkate alınması bile gerekmez(4.1).

Örneğin, aşağıdaki stil kurallarına bakalım:

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

İlk kural, sınıf haritasına eklenir. İkincisi kimlik eşlemesine, üçüncüsü ise etiket eşlemesine eklenir.

Aşağıdaki HTML parçası için;

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

Öncelikle p öğesi için kuralları bulmaya çalışırız. Sınıf eşlemesinde, "p.error" kuralının bulunduğu bir "error" anahtarı yer alır. div öğesi, kimlik eşlemesinde (anahtar, kimliktir) ve etiket eşlemesinde alakalı kurallara sahip olur. Bu nedenle, geriye kalan tek iş, anahtarlar tarafından çıkarılan kurallardan hangilerinin gerçekten eşleştiğini bulmaktır.

Örneğin, div kuralı şu şekildeyse:

table div {margin: 5px}

Anahtar en sağdaki seçici olduğu için etiket haritasından çıkarılmaya devam eder ancak tablo üst öğesi olmayan div öğemizle eşleşmez.

Hem WebKit hem de Firefox bu işlemi yapar.

Stil sayfası basamak sırası

Stil nesnesi, her görsel özelliğe (tüm CSS özellikleri ancak daha genel) karşılık gelen özelliklere sahiptir. Özellik, eşleşen kurallardan herhangi biri tarafından tanımlanmamışsa bazı özellikler üst öğe stil nesnesi tarafından devralınabilir. Diğer özelliklerin varsayılan değerleri vardır.

Birden fazla tanım olduğunda sorun başlar. Bu sorunu çözmek için basamaklı sıralama kullanılır.

Bir stil özelliği için beyan, birden fazla stil sayfasında ve bir stil sayfasının içinde birkaç kez görünebilir. Bu nedenle, kuralların uygulanma sırası çok önemlidir. Buna "basamaklı" sıra denir. CSS2 spesifikasyonuna göre basamaklı sıralama şu şekildedir (düşükten yükseğe):

  1. Tarayıcı bildirimleri
  2. Kullanıcı normal beyanları
  3. Yazarın normal beyanları
  4. Yazarın önemli beyanları
  5. Kullanıcı için önemli beyanlar

Tarayıcı bildirimleri en az öneme sahiptir ve kullanıcı, yalnızca bildirim önemli olarak işaretlenmişse yazarın bildirimini geçersiz kılar. Aynı sıraya sahip bildirimler, özgüllüğe ve ardından belirtildikleri sıraya göre sıralanır. HTML görsel özellikleri, eşleşen CSS bildirimlerine çevrilir . Bu kurallar, düşük öncelikli yazar kuralları olarak değerlendirilir.

Belirginlik

Seçici özgüllüğü, CSS2 spesifikasyonu tarafından aşağıdaki şekilde tanımlanır:

  1. Bildirim, seçici içeren bir kural yerine "style" özelliği ise 1, aksi takdirde 0 değerini alır (= a)
  2. Seçicideki kimlik özelliklerinin sayısını hesaplayın (= b)
  3. Seçicideki diğer özelliklerin ve sözde sınıfların sayısını sayın (= c)
  4. Seçicideki öğe adlarının ve sözde öğelerin sayısını sayın (= d)

Dört sayının (a-b-c-d) birleştirilmesi (büyük tabanlı bir sayı sisteminde) özgüllüğü verir.

Kullanmanız gereken sayı tabanı, kategorilerden birinde sahip olduğunuz en yüksek sayıya göre belirlenir.

Örneğin, a=14 ise onaltılık tabanı kullanabilirsiniz. a=17 olduğu nadir durumlarda 17 haneli bir sayı tabanına ihtiyacınız olur. İkinci durum, şu şekilde bir seçiciyle gerçekleşebilir: html body div div p… (Seçicinizde 17 etiket var… pek olası değil).

Bazı örnekler:

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

Kuralları sıralama

Kurallar eşleştirildikten sonra basamaklı kurallara göre sıralanır. WebKit, küçük listeler için kabarcık sıralaması, büyük listeler için birleştirmeli sıralama kullanır. WebKit, kurallar için > operatörünü geçersiz kılarak sıralama uygular:

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

Kademeli süreç

WebKit, tüm üst düzey stil sayfalarının (@imports dahil) yüklenip yüklenmediğini işaretleyen bir işaret kullanır. Stil, ekleme sırasında tam olarak yüklenmezse yer tutucular kullanılır ve dokümanda işaretlenir. Stil sayfaları yüklendikten sonra yeniden hesaplanır.

Düzen

Oluşturulup ağaca eklendiğinde oluşturucunun konumu ve boyutu yoktur. Bu değerlerin hesaplanmasına düzen veya yeniden akış adı verilir.

HTML, akış tabanlı bir düzen modeli kullanır. Bu, çoğu zaman geometrinin tek bir geçişte hesaplanabileceği anlamına gelir. "Akışta" daha sonra gelen öğeler genellikle "akışta" daha önce gelen öğelerin geometrisini etkilemez. Bu nedenle, düzen soldan sağa, yukarıdan aşağıya doğru ilerleyebilir. İstisnalar vardır: Örneğin, HTML tabloları birden fazla geçiş gerektirebilir.

Koordinat sistemi, kök çerçeveye göredir. Üst ve sol koordinatlar kullanılır.

Düzen, özyinelemeli bir süreçtir. HTML belgesinin <html> öğesine karşılık gelen kök oluşturucuda başlar. Düzen, kare hiyerarşisinin bir kısmı veya tamamı boyunca yinelemeli olarak devam eder ve her oluşturucu için gereken geometrik bilgileri hesaplar.

Kök oluşturucunun konumu 0,0'dır ve boyutları görüntü alanıdır (tarayıcı penceresinin görünür kısmı).

Tüm oluşturucuların bir "düzen" veya "yeniden akış" yöntemi vardır. Her oluşturucu, düzen gerektiren alt öğelerinin düzen yöntemini çağırır.

Kirli bit sistemi

Tarayıcılar, her küçük değişiklik için tam bir düzen oluşturmamak amacıyla "dirty bit" sistemi kullanır. Değiştirilen veya eklenen bir oluşturucu, kendisini ve alt öğelerini "kirli" (düzen gerektiriyor) olarak işaretler.

İki işaret vardır: "dirty" ve "children are dirty". Bu işaretler, oluşturucunun kendisi sorunsuz olsa bile düzen gerektiren en az bir alt öğesi olduğu anlamına gelir.

Genel ve artımlı düzen

Düzen, tüm oluşturma ağacında tetiklenebilir. Bu, "genel" düzendir. Bu durum, aşağıdakilerin sonucunda ortaya çıkabilir:

  1. Yazı tipi boyutu değişikliği gibi tüm oluşturucuları etkileyen genel bir stil değişikliği.
  2. Ekranın yeniden boyutlandırılması nedeniyle

Düzen artımlı olabilir, yalnızca kirli oluşturucular düzenlenir (bu, ek düzenler gerektirecek bazı hasarlara neden olabilir).

Oluşturucular kirli olduğunda artımlı düzen tetiklenir (eşzamansız olarak). Örneğin, ağdan ek içerik gelip DOM ağacına eklendikten sonra oluşturma ağacına yeni oluşturucular eklendiğinde.

Artımlı düzen.
Şekil 18: Artımlı düzen: Yalnızca kirli oluşturucular ve alt öğeleri düzenlenir

Eşzamansız ve eşzamanlı düzen

Artımlı düzen, eşzamansız olarak yapılır. Firefox, artımlı düzenler için "yeniden akış komutlarını" sıraya alır ve bir planlayıcı, bu komutların toplu olarak yürütülmesini tetikler. WebKit'te ayrıca artımlı bir düzeni yürüten bir zamanlayıcı da vardır. Ağaç taranır ve "kirli" oluşturucular düzenlenir.

"offsetHeight" gibi stil bilgisi isteyen komut dosyaları, artımlı düzeni eşzamanlı olarak tetikleyebilir.

Global düzen genellikle eşzamanlı olarak tetiklenir.

Bazen kaydırma konumu gibi bazı özellikler değiştiği için düzen, ilk düzenden sonra geri çağırma olarak tetiklenir.

Optimizasyonlar

Bir düzen, "yeniden boyutlandırma" veya oluşturucu konumundaki(boyut değil) bir değişiklik tarafından tetiklendiğinde, oluşturma boyutları bir önbellekten alınır ve yeniden hesaplanmaz…

Bazı durumlarda yalnızca bir alt ağaç değiştirilir ve düzen kökten başlamaz. Bu durum, değişikliğin yerel olduğu ve çevresini etkilemediği durumlarda (ör. metin alanlarına eklenen metinler) gerçekleşebilir. Aksi takdirde, her tuş vuruşu kökten başlayan bir düzeni tetikler.

Düzen süreci

Düzen genellikle aşağıdaki kalıba sahiptir:

  1. Üst oluşturucu kendi genişliğini belirler.
  2. Ebeveyn, çocukların yanına gider ve:
    1. Alt oluşturucuyu yerleştirir (x ve y değerlerini ayarlar).
    2. Gerekirse alt düzeni çağırır (kirliyse veya global bir düzendeysek ya da başka bir nedenle) ve alt öğenin yüksekliğini hesaplar.
  3. Üst öğe, kendi yüksekliğini ayarlamak için alt öğelerin toplam yüksekliklerini ve kenar boşlukları ile dolguların yüksekliklerini kullanır. Bu, üst oluşturucunun üst öğesi tarafından kullanılır.
  4. Değişiklik bitini "false" (yanlış) olarak ayarlar.

Firefox, düzen için parametre olarak bir "durum" nesnesi(nsHTMLReflowState) kullanır ("yeniden akış" olarak adlandırılır). Diğerlerinin yanı sıra, durum üst öğenin genişliğini içerir.

Firefox düzeninin çıkışı, bir "metrikler" nesnesidir(nsHTMLReflowMetrics). Oluşturucu tarafından hesaplanan yüksekliği içerir.

Genişlik hesaplaması

Oluşturucunun genişliği; kapsayıcı bloğun genişliği, oluşturucunun stil "width" özelliği, kenar boşlukları ve kenarlıklar kullanılarak hesaplanır.

Örneğin, aşağıdaki div'in genişliği:

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

WebKit tarafından şu şekilde hesaplanır(class RenderBox method calcWidth):

  • Kapsayıcı genişliği, kapsayıcıların availableWidth ve 0 değerlerinin maksimumudur. Bu durumda availableWidth, contentWidth'tir ve şu şekilde hesaplanır:
clientWidth() - paddingLeft() - paddingRight()

clientWidth ve clientHeight, kenarlık ve kaydırma çubuğu hariç bir nesnenin içini temsil eder.

  • Öğelerin genişliği, "width" stil özelliğidir. Kapsayıcı genişliğinin yüzdesi hesaplanarak mutlak değer olarak hesaplanır.

  • Yatay kenarlıklar ve dolgular eklendi.

Şimdiye kadar "tercih edilen genişlik" hesaplamasını ele aldık. Şimdi minimum ve maksimum genişlikler hesaplanacak.

Tercih edilen genişlik maksimum genişlikten büyükse maksimum genişlik kullanılır. Minimum genişlikten (bölünemeyen en küçük birim) daha küçükse minimum genişlik kullanılır.

Değerler, bir düzene ihtiyaç duyulması ihtimaline karşı önbelleğe alınır ancak genişlik değişmez.

Satır sonu

Düzenin ortasındaki bir oluşturucu, bölünmesi gerektiğine karar verdiğinde durur ve düzenin üst öğesine bölünmesi gerektiğini bildirir. Üst öğe, ek oluşturucuları oluşturur ve bunlarda düzeni çağırır.

Resim

Boyama aşamasında, oluşturma ağacı taranır ve içeriği ekranda görüntülemek için oluşturucunun "paint()" yöntemi çağrılır. Boyama, kullanıcı arayüzü altyapı bileşenini kullanır.

Genel ve artımlı

Düzen gibi boyama da genel (tüm ağaç boyanır) veya artımlı olabilir. Artımlı boyamada, oluşturuculardan bazıları tüm ağacı etkilemeyecek şekilde değişir. Değiştirilen oluşturucu, ekrandaki dikdörtgenini geçersiz kılar. Bu, OS'nin bunu "kirli bölge" olarak görmesine ve bir "boyama" etkinliği oluşturmasına neden olur. İşletim sistemi bunu akıllıca yapar ve birkaç bölgeyi tek bir bölgede birleştirir. Chrome'da oluşturucu, ana işlemden farklı bir işlemde olduğu için bu durum daha karmaşıktır. Chrome, işletim sisteminin davranışını bir dereceye kadar simüle eder. Sunu bu etkinlikleri dinler ve mesajı oluşturma köküne iletir. İlgili oluşturucuya ulaşılana kadar ağaçta gezinilir. Kendisini (ve genellikle alt öğelerini) yeniden boyar.

Boyama sırası

CSS2, boyama işleminin sırasını tanımlar. Bu, aslında öğelerin yığınlama bağlamlarında yığınlanma sırasıdır. Yığınlar arkadan öne doğru boyandığı için bu sıra boyamayı etkiler. Blok oluşturucunun yığınlama sırası şöyledir:

  1. arka plan rengi
  2. arka plan resmi
  3. border
  4. çocuklar
  5. outline

Firefox görüntüleme listesi

Firefox, oluşturma ağacını inceler ve boyalı dikdörtgen için bir görüntüleme listesi oluşturur. Dikdörtgenle alakalı oluşturucuları doğru boyama sırasıyla (oluşturucuların arka planları, ardından kenarlıklar vb.) içerir.

Bu sayede, yeniden boyama için ağacın birkaç kez (tüm arka planları, ardından tüm resimleri, ardından tüm kenarlıkları vb. boyama) değil, yalnızca bir kez taranması gerekir.

Firefox, gizlenecek öğeleri (ör. tamamen diğer opak öğelerin altında kalan öğeler) eklemeyerek süreci optimize eder.

WebKit dikdörtgen depolama alanı

WebKit, yeniden boyamadan önce eski dikdörtgeni bit eşlem olarak kaydeder. Ardından, yalnızca yeni ve eski dikdörtgenler arasındaki farkı boyar.

Dinamik değişiklikler

Tarayıcılar, bir değişikliğe yanıt olarak mümkün olan en az işlemi yapmaya çalışır. Bu nedenle, bir öğenin renginde yapılan değişiklikler yalnızca öğenin yeniden boyanmasına neden olur. Öğe konumunda yapılan değişiklikler, öğenin, alt öğelerinin ve muhtemelen kardeşlerinin düzenini ve yeniden boyanmasını etkiler. DOM düğümü eklemek, düğümün düzenlenmesine ve yeniden boyanmasına neden olur. "html" öğesinin yazı tipi boyutunu artırmak gibi büyük değişiklikler, önbelleklerin geçersiz kılınmasına, tüm ağacın yeniden düzenlenmesine ve yeniden boyanmasına neden olur.

Oluşturma motorunun iş parçacıkları

Oluşturma motoru tek iş parçacıklıdır. Ağ işlemleri hariç neredeyse her şey tek bir iş parçacığında gerçekleşir. Firefox ve Safari'de bu, tarayıcının ana iş parçacığıdır. Chrome'da bu, sekme işleminin ana iş parçacığıdır.

Ağ işlemleri, birkaç paralel iş parçacığı tarafından gerçekleştirilebilir. Paralel bağlantı sayısı sınırlıdır (genellikle 2-6 bağlantı).

Etkinlik döngüsü

Tarayıcının ana iş parçacığı bir etkinlik döngüsüdür. Süreci canlı tutan sonsuz bir döngüdür. Etkinlikleri (düzen ve boyama etkinlikleri gibi) bekler ve işler. Bu, ana etkinlik döngüsü için Firefox kodudur:

while (!mExiting)
    NS_ProcessNextEvent(thread);

CSS2 görsel modeli

Tuval

CSS2 spesifikasyonuna göre, tuval terimi "biçimlendirme yapısının oluşturulduğu alanı" (tarayıcının içeriği boyadığı yer) tanımlar.

Tuval, alanın her boyutu için sonsuzdur ancak tarayıcılar, görüntü alanının boyutlarına göre bir başlangıç genişliği seçer.

www.w3.org/TR/CSS2/zindex.html adresine göre, tuval başka bir tuvalin içindeyse şeffaftır, değilse tarayıcı tarafından tanımlanan bir renge sahiptir.

CSS kutu modeli

CSS kutu modeli, doküman ağacındaki öğeler için oluşturulan ve görsel biçimlendirme modeline göre düzenlenen dikdörtgen kutuları tanımlar.

Her kutunun bir içerik alanı (ör. metin, resim vb.) ve isteğe bağlı çevreleyen dolgu, kenarlık ve kenar boşluğu alanları vardır.

CSS2 kutu modeli
Şekil 19: CSS2 kutu modeli

Her düğüm, 0…n sayıda bu tür kutu oluşturur.

Tüm öğelerin, oluşturulacak kutunun türünü belirleyen bir "display" özelliği vardır.

Örnekler:

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

Varsayılan değer satır içidir ancak tarayıcı stil sayfası başka varsayılan değerler ayarlayabilir. Örneğin: "div" öğesinin varsayılan görünümü bloktur.

Varsayılan stil sayfası örneğini www.w3.org/TR/CSS2/sample.html adresinde bulabilirsiniz.

Konumlandırma şeması

Üç şema vardır:

  1. Normal: Nesne, dokümandaki yerine göre konumlandırılır. Bu, oluşturma ağacındaki yerinin DOM ağacındaki yeri gibi olduğu ve kutu türüne ve boyutlarına göre düzenlendiği anlamına gelir.
  2. Float: Nesne önce normal akış gibi düzenlenir, ardından mümkün olduğunca sola veya sağa taşınır.
  3. Mutlak: Nesne, oluşturma ağacında DOM ağacından farklı bir yere yerleştirilir.

Konumlandırma şeması, "position" özelliği ve "float" özelliği tarafından ayarlanır.

  • statik ve göreli normal bir akışa neden olur
  • Mutlak ve sabit, mutlak konumlandırmaya neden olur

Statik konumlandırmada herhangi bir konum tanımlanmaz ve varsayılan konumlandırma kullanılır. Diğer şemalarda yazar konumu belirtir: üst, alt, sol, sağ.

Kutunun düzeni şunlara göre belirlenir:

  • Kutu türü
  • Kutu boyutları
  • Konumlandırma şeması
  • Resim boyutu ve ekran boyutu gibi harici bilgiler

Kutu türleri

Blok kutusu: Blok oluşturur ve tarayıcı penceresinde kendi dikdörtgeni vardır.

Engelleme kutusu.
Şekil 20: Blok kutusu

Satır içi kutu: Kendi bloğu yoktur ancak kapsayıcı bir bloğun içindedir.

Satır içi kutular.
Şekil 21: Satır içi kutular

Bloklar dikey olarak art arda biçimlendirilir. Satır içi öğeler yatay olarak biçimlendirilir.

Blok ve satır içi biçimlendirme.
Şekil 22: Blok ve satır içi biçimlendirme

Satır içi kutular, satırların veya "satır kutularının" içine yerleştirilir. Satırlar en az en uzun kutu kadar uzundur ancak kutular "referans" olarak hizalandığında daha uzun olabilir. Bu, bir öğenin alt kısmının başka bir kutunun alt kısmı dışında bir noktada hizalandığı anlamına gelir. Kapsayıcı genişliği yeterli değilse satır içi öğeler birkaç satıra yerleştirilir. Genellikle bir paragrafta bu durum ortaya çıkar.

Çizgiler.
Şekil 23: Çizgiler

Konumlandırma

Akraba

Göreceli konumlandırma: Her zamanki gibi konumlandırılır ve ardından gerekli delta kadar taşınır.

Göreli konumlandırma.
Şekil 24: Göreceli konumlandırma

Kayanlar

Kayan kutu, bir satırın soluna veya sağına kaydırılır. İlginç olan, diğer kutuların bu kutunun etrafında akmasıdır. HTML:

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

Şöyle görünür:

Float.
25. Şekil: Float

Mutlak ve sabit

Düzen, normal akıştan bağımsız olarak tam olarak tanımlanır. Öğe, normal akışa katılmaz. Boyutlar kapsayıcıya göredir. Sabit konumlandırmada kapsayıcı, görüntü alanıdır.

Sabit konumlandırma.
Şekil 26: Sabit konumlandırma

Katmanlı gösterim

Bu, z-index CSS özelliğiyle belirtilir. Kutunun üçüncü boyutunu temsil eder: "z ekseni" boyunca konumu.

Kutular, yığınlara (yığınlama bağlamları olarak adlandırılır) bölünür. Her yığında, önce arka öğeler, ardından üstte ve kullanıcıya daha yakın olan ön öğeler boyanır. Çakışma durumunda en öndeki öğe, önceki öğeyi gizler.

Yığınlar, z-endeksi özelliğine göre sıralanır. "z-index" özelliğine sahip kutular yerel bir yığın oluşturur. Görüntü alanında dış yığın bulunur.

Örnek:

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

Sonuç şu şekilde olacaktır:

Sabit konumlandırma.
Şekil 27: Sabit konumlandırma

İşaretlemede kırmızı div, yeşil div'den önce gelse ve normal akışta daha önce boyanmış olsa da z-endeksi özelliği daha yüksek olduğundan kök kutunun tuttuğu yığında daha öndedir.

Kaynaklar

  1. Tarayıcı mimarisi

    1. Grosskurth, Alan. Web Tarayıcıları İçin Referans Mimari (pdf)
    2. Gupta, Vineet. Tarayıcıların İşleyiş Şekli - 1. Bölüm - Mimari
  2. Ayrıştırma

    1. Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools ("Dragon book" olarak da bilinir), Addison-Wesley, 1986
    2. Rick Jelliffe. The Bold and the Beautiful: HTML 5 için iki yeni taslak.
  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 (Google tech talk video)
    3. L. David Baron, Mozilla's Layout Engine
    4. L. David Baron, Mozilla Style System Documentation
    5. Chris Waterson, Notes on HTML Reflow (HTML Yeniden Akışı Hakkında Notlar)
    6. Chris Waterson, Gecko Overview
    7. Alexander Larsson, The life of an HTML HTTP request
  4. WebKit

    1. David Hyatt, Implementing CSS(part 1)
    2. David Hyatt, An Overview of WebCore
    3. David Hyatt, WebCore Oluşturma
    4. David Hyatt, The FOUC Problem
  5. W3C Spesifikasyonları

    1. HTML 4.01 Spesifikasyonu
    2. W3C HTML5 Spesifikasyonu
    3. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification
  6. Tarayıcı derleme talimatları

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

Çeviriler

Bu sayfa iki kez Japoncaya çevrilmiştir:

Korece ve Türkçe dillerindeki harici olarak barındırılan çevirileri görüntüleyebilirsiniz.

Herkese teşekkürler.