Modern web tarayıcıların perde arkası
Önsöz
WebKit ve Gecko'nun dahili işlemleriyle ilgili bu kapsamlı giriş makalesi, İsrailli geliştirici Tali Garsiel tarafından yapılan kapsamlı bir araştırmanın sonucudur. Birkaç yıl boyunca, tarayıcıların dahili öğeleriyle ilgili yayınlanan tüm verileri inceledi ve web tarayıcısı kaynak kodunu okumak için çok zaman harcadı. Şunu yazdı:
Bir web geliştiricisi olarak, tarayıcı işlemlerinin dahili işleyişini öğrenmek, daha iyi kararlar almanıza ve geliştirme en iyi uygulamalarının arkasındaki gerekçeleri öğrenmenize yardımcı olur. Bu doküman oldukça uzun olsa da biraz zaman ayırıp incelemenizi öneririz. Pişman olmazsınız.
Paul Ireland, Chrome Geliştirici İlişkileri
Giriş
Web tarayıcıları en yaygın kullanılan yazılımlardır. Bu ön belgede, sahne arkasında
nasıl çalıştıkları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.
Konuşacağımız tarayıcılar
Günümüzde masaüstünde kullanılan başlıca beş tarayıcı vardır: Chrome, Internet Explorer, Firefox, Safari ve Opera. Mobil cihazlarda kullanılan başlıca 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ç tüm bu tarayıcılar WebKit'e dayanır. Açık kaynaklı Firefox ve Chrome tarayıcılarından ve kısmen açık kaynaklı Safari'den örnekler vereceğim. StatCounter istatistiklerine (Haziran 2013 itibarıyla) göre Chrome, Firefox ve Safari, dünya genelindeki masaüstü tarayıcı kullanımının yaklaşık% 71'ini oluşturuyor. Mobil cihazlarda Android Tarayıcı, iPhone ve Chrome kullanımın yaklaşık% 54'ünü oluşturur.
Tarayıcının ana işlevi
Bir tarayıcının ana işlevi, seçtiğiniz web kaynağını sunucudan isteyip tarayıcı penceresinde göstermektir. 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 URI (Tekdüzen Kaynak Tanımlayıcısı) 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'in standartlar kuruluşu olan W3C (World Wide Web Consortium) tarafından yönetilir. Tarayıcılar yıllarca spesifikasyonların yalnızca bir kısmını karşıladı ve kendi uzantılarını geliştirdi. Bu durum, web yazarları için ciddi uyumluluk sorunlarına neden oldu. Bugün, tarayıcıların çoğu spesifikasyonlara hemen uymaktadır.
Tarayıcı kullanıcı arayüzlerinin birbirinden çok ortak noktası vardır. Yaygın kullanıcı arayüzü öğeleri arasında şunlar yer alır:
- URI eklemek için adres çubuğu
- Geri ve ileri düğmeleri
- Yer işareti seçenekleri
- Mevcut dokümanların yenilenmesi veya yüklemesinin durdurulması için yenile ve durdur düğmeleri
- Ana sayfanıza götüren ana sayfa düğmesi
Tuhaf bir şekilde, tarayıcının kullanıcı arayüzü herhangi bir resmi spesifikasyonda belirtilmemiştir. Bu arayüz, yıllar süren deneyimler ve tarayıcıların birbirini taklit etmesi sonucunda ortaya çıkan iyi uygulamalardan oluşur. HTML5 spesifikasyonu bir tarayıcıda bulunması gereken kullanıcı arayüzü öğelerini tanımlamaz, ancak bazı ortak öğeleri listeler. Bunların arasında adres çubuğu, durum çubuğu ve araç çubuğu bulunur. 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:
- Kullanıcı arayüzü: Buna adres çubuğu, geri/ileri düğmesi, yer işareti ekleme menüsü vb. dahildir. İstenen sayfayı gördüğünüz pencere hariç, tarayıcının her bölümü görüntülenir.
- Tarayıcı motoru: Kullanıcı arayüzü ile oluşturma motoru arasındaki işlemleri sıralar.
- Oluşturma motoru: İstenen içeriği göstermekten 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.
- Ağ: Platformdan bağımsız bir arayüzün arkasında farklı platformlar için farklı uygulamalar kullanılarak HTTP istekleri gibi ağ çağrıları için.
- 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 özgü olmayan genel bir arayüz sunar. Alt katmanlarda işletim sistemi kullanıcı arayüzü yöntemleri kullanılır.
- JavaScript çevirmeni. JavaScript kodunu ayrıştırmak ve yürütmek için kullanılır.
- 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, localStorage, IndexedDB, WebSQL ve FileSystem gibi depolama mekanizmalarını da destekler.
Chrome gibi tarayıcıların, her sekme için bir tane olmak üzere oluşturma motorunun birden çok örneğini çalıştırdığını unutmamak önemlidir. Her sekme ayrı bir işlemde çalışır.
Oluşturma motorları
Oluşturma motorunun sorumluluğu... Oluşturma, istenen içeriğin tarayıcı ekranında görüntülenmesidir.
Oluşturma motoru varsayılan olarak HTML ve XML dokümanlarını ve resimlerini görüntüleyebilir. Eklentiler veya uzantılar aracılığıyla diğer veri türlerini de görüntüleyebilir (örneğin, bir PDF görüntüleyici eklentisi kullanarak PDF dokümanlarını görüntüleyebilir). Ancak bu bölümde ana kullanım alanına odaklanacağız: HTML'yi ve CSS kullanılarak biçimlendirilmiş resimleri görüntüleme.
Farklı tarayıcılar farklı oluşturma motorları kullanır: Internet Explorer Trident, Firefox Gecko, Safari WebKit kullanır. Chrome ve Opera (15 sürümünden itibaren), WebKit'in bir çatalı olan Blink'i kullanır.
WebKit, Linux platformu için bir motor olarak başlayan ve Apple tarafından Mac ile Windows'u desteklemek için değiştirilen açık kaynak bir oluşturma motorudur.
Ana akış
Oluşturma motoru, istenen dokümanın içeriğini ağ katmanından almaya başlar. Bu işlem genellikle 8 KB'lık parçalar halinde yapılır.
Bundan sonra, oluşturma motorunun temel akışı şu şekildedir:
Oluşturma motoru, HTML dokümanını ayrıştırmaya başlar ve öğeleri "içerik ağacı" adı verilen bir ağaçta 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örüntülenecek doğru sırada olmalıdır.
Oluşturma ağacı oluşturulduktan sonra "düzenleme" işlemine girer. Bu, her bir düğüme ekranda görünmesi gereken yerin tam koordinatlarını vermek anlamına gelir. Sonraki aşama boyama işlemidir. Oluşturma ağacı taranacak ve her düğüm, kullanıcı arayüzü arka uç katmanı kullanılarak boyanacaktır.
Bunun kademeli bir süreç olduğunu bilmeniz önemlidir. Oluşturma motoru, daha iyi bir kullanıcı deneyimi sağlamak için içerikleri mümkün olan en kısa sürede ekranda görüntülemeye çalışır. Oluşturma ağacını oluşturmaya ve biçimlendirmeye başlamadan önce tüm HTML'nin ayrıştırılmasını beklemez. İçeriğin bazı bölümleri ayrıştırılıp gösterilirken işlem, ağdan gelen diğer içeriklerle devam eder.
Ana akış örnekleri
3. ve 4. resimlerde, WebKit ve Gecko'nun biraz farklı terminolojiler kullanmasına rağmen akışın temelde aynı olduğunu görebilirsiniz.
Geko, görsel olarak biçimlendirilmiş öğeler ağacına "Çerçeve ağacı" adını verir. Her öğe bir çerçevedir. WebKit, "Oluşturma Ağacı" terimini kullanır ve "Oluşturma Nesneleri"nden oluşur. WebKit, öğelerin yerleştirilmesi için "düzenleme" terimini kullanırken Gecko bunu "yeniden akış" olarak adlandırır. "Ek", WebKit'in DOM düğümleri ile oluşturma ağacını oluşturmak için görsel bilgileri birbirine bağlamak için kullandığı terimdir. Semantik olmayan küçük bir fark, Gecko'nun HTML ile DOM ağacı arasında ek bir katmana sahip olmasıdır. "İçerik havuzu" olarak adlandırılan bu öğe, DOM öğeleri oluşturmaya yönelik bir fabrikadır. Akışın her bir bölümü hakkında konuşacağız:
Ayrıştırma - genel
Ayrıştırma, oluşturma motoru içinde çok önemli bir işlem olduğundan bu konuyu biraz daha ayrıntılı bir şekilde 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 çevirmektir. Ayrıştırmanın sonucu genellikle dokümanın yapısını temsil eden bir düğüm ağacıdır. 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:
Dilbilgisi
Ayrıştırma, dokümanın uyduğu söz dizimi kurallarına, yani yazıldığı dil veya biçime dayanır. Ayrıştırabileceğiniz her biçimin, kelime bilgisi ve söz dizimi kurallarından oluşan deterministik dil bilgisi olması gerekir. Bağlamsal dil bilgisi olarak adlandırılır. İnsan dilleri bu tür diller değildir ve bu nedenle, geleneksel ayrıştırma teknikleriyle ayrıştırılamaz.
Ayrıştırıcı - Lexer kombinasyonu
Ayrıştırma iki alt sürece ayrılabilir: sözcüksel analiz ve söz dizimi analizi.
Sözcük analizi, girişin jetonlara bölünmesi işlemidir. Jetonlar, geçerli yapı taşlarının bir araya getirilmesiyle oluşan dil kelime hazinesidir. Bu dil için sözlükte yer alan tüm kelimelerden oluşur.
Söz dizimi analizi, dil 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ş karakter dizisini geçerli jetonlara bölme sorumlusu olan token ayırıcı (bazen jeton dizilici olarak da adlandırılır) ve belge yapısını dil söz dizimi kurallarına göre analiz ederek ayrıştırma ağacını oluşturmaktan sorumlu olan ayrıştırıcı.
Lexer, boşluklar ve satır sonları gibi alakasız karakterleri nasıl çıkaracağını biliyor.
Ayrıştırma işlemi yinelemeli bir süreçtir. Ayrıştırıcı genellikle dize ayrıştırıcıdan yeni bir jeton ister ve jetonu söz dizimi kurallarından biriyle eşleştirmeye çalışır. Bir kural eşleşirse jetona karşılık gelen bir düğüm ayrıştırma ağacına eklenir ve ayrıştırıcı başka bir jeton ister.
Hiçbir kural eşleşmezse 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 bulunmazsa 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ş belgesini başka bir biçime dönüştürür. Buna örnek olarak derleme verilebilir. Kaynak kodunu makine koduna derleyen derleyici, önce kodu bir ayrıştırma ağacına ayırır, ardından ağacı bir makine kodu belgesine çevirir.
Ayrıştırma örneği
5. resimde, matematiksel bir ifadeden bir ayrıştırma ağacı oluşturduk. Basit bir matematiksel dil tanımlamayı deneyelim ve ayrıştırma işlemini görelim.
Söz dizimi:
- Dil söz dizimi yapı taşları ifadeler, terimler ve işlemlerdir.
- Dilimiz sınırsız sayıda ifade içerebilir.
- Bir ifade, "terim", ardından "işlem" ve ardından başka bir terim olarak tanımlanır.
- İşlem, artı veya eksi jetondur.
- Terim, tam sayı jetonu veya ifadedir
2 + 3 - 1
girişini analiz edelim.
Bir kuralla eşleşen ilk alt dize 2
'tür: 5. kurala göre bu bir terimdir.
İkinci eşleme 2 + 3
: Bu, üçüncü kuralla eşleşir: Bir terimin ardından bir işlem ve ardından başka bir terim.
Sonraki eşleme yalnızca girişin sonunda gerçekleşir.
2 + 3
bir terim olduğu için 2 + 3 - 1
bir ifadedir. Dolayısıyla, bir terimin ardından bir işlem ve ardından başka bir terimimiz var.
2 + +
hiçbir kuralla eşleşmediği için geçersiz bir giriş.
Sözlük ve söz dizimi için resmi tanımlar
Kelime dağarcığı 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 adı verilen bir biçimde tanımlanır. Dilimiz şu şekilde tanımlanacaktır:
expression := term operation term
operation := PLUS | MINUS
term := INTEGER | expression
Bir dilin söz dizimi bağlamsız söz dizimiyse normal ayrıştırıcılar tarafından ayrıştırılabileceğini söylemiştik. Bağlamsız söz dizimi, tamamen BNF ile ifade edilebilen bir söz dizimidir. Resmi bir tanım için Wikipedia'nın Bağlamdan Bağımsız Dil Bilgisi makalesine bakın.
Ayrıştırıcı türleri
İki tür ayrıştırıcı vardır: yukarıdan aşağı ayrıştırıcılar ve aşağıdan yukarı ayrıştırıcılar. Üstten aşağı ayrıştırıcıların, söz dizisinin üst düzey yapısını inceleyip kural eşleşmesi bulmaya çalıştıklarını sezgisel bir şekilde açıklayabiliriz. Aşağıdan yukarıya doğru ayrıştırıcılar, girişle başlar ve girişi düşük düzeyli kurallardan başlayarak yüksek düzeyli kurallar karşılanana kadar yavaş yavaş söz dizimi kurallarına dönüştürür.
İki tür ayrıştırıcının örneğimizi nasıl ayrıştırdığını görelim.
Yukarıdan aşağıya ayrıştırıcı daha üst düzey kuraldan başlar: 2 + 3
öğesini bir ifade olarak tanımlar. Ardından 2 + 3 - 1
ifadesi tanımlanır (ifadeyi tanımlama süreci, diğer kurallarla eşleşerek gelişir ancak başlangıç noktası en üst düzey kuraldır).
Aşağıdan yukarıya doğru 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.
Giriş sağa kaydırılıp (ilk olarak girişin başlangıcını işaret eden ve sağa doğru hareket eden bir işaretçi düşünün) yavaş yavaş söz dizimi kurallarına indirgendiği için bu tür bir aşağıdan yukarıya ayrıştırıcıya kaydırma-azaltma ayrıştırıcı denir.
Ayrıştırıcıları otomatik olarak oluşturma
Ayrıştırıcı oluşturabilen araçlar vardır. Onlara dilinizin dil bilgisini (kelime hazinesini ve söz dizimi kurallarını) sağlarsınız ve çalışan bir ayrıştırıcı oluştururlar. Ayrıştırıcı oluşturmak için ayrıştırma hakkında derin bir bilgi sahibi olmanız gerekir. Ayrıca, 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: Lexer oluşturmak için Flex, ayrıştırıcı oluşturmak için Bison (Lex ve Yacc adlı adlarla karşılaşabilirsiniz). Esnek giriş, 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 işi, HTML işaretlemesini ayrıştırma ağacına dönüştürmektir.
HTML dil bilgisi
HTML sözlüğü ve söz dizimi, W3C kuruluşu tarafından oluşturulan spesifikasyonlarda tanımlanmıştır.
Ayrıştırma girişinde 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 tümü HTML için geçerli değildir (Bunları sadece eğlence için gündeme getirmedim; CSS ve JavaScript'i ayrıştırırken kullanılacaklardır). HTML, ayrıştırıcıların ihtiyaç duyduğu bağlamsız bir dil bilgisiyle kolayca tanımlanamaz.
HTML'yi tanımlamak için resmi bir biçim (DTD - Document Type Definition) vardır ancak bu biçim bağlamdan bağımsız bir dil bilgisi değildir.
Bu durum ilk bakışta garip görünebilir; HTML, XML'e oldukça yakındır. Birçok XML ayrıştırıcı mevcuttur. HTML'nin XML varyantı olan XHTML'nin ne gibi avantajları vardır?
Aralarındaki fark, HTML yaklaşımının daha "rahatlayıcı" olmasıdır: Belirli etiketleri atlamanıza (daha sonra dolaylı olarak eklenir) veya bazen başlangıç ya da bitiş etiketlerini atlamanıza olanak tanır ve bu böyle devam eder. Genel olarak, XML'in katı ve talepkar söz dizimine kıyasla "yumuşak" bir söz dizimidir.
Küçük görünen bu ayrıntı, büyük bir fark yaratabilir. HTML'nin bu kadar popüler olmasının başlıca nedeni de budur: Hatalarınızı bağışlar ve web yazarı için hayatı kolaylaştırır. Diğer yandan, biçimsel bir dil bilgisi yazmanızı zorlaştırır. Özetlemek gerekirse, dil bilgisi bağlamdan bağımsız olmadığı için HTML, 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 de gördüğümüz gibi, HTML DTD 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 geçmişte tarayıcılar tarafından kullanılan işaretleme için destek içerir. Amaç eski içeriklerle geriye dönük uyumluluk sağlamaktır. Mevcut katı DTD şu adrestedir: 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 dış dünyaya (JavaScript gibi) arayüzüdür.
Ağacın kökü "Document" nesnesidir.
DOM, işaretlemeyle neredeyse bire bir ilişkilidir. Ö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 dönüştürülür:
HTML gibi DOM da W3C kuruluşu tarafından belirtilir. www.w3.org/DOM/DOMTR sayfasına göz atın. Belgelerin değiştirilmesine ilişkin genel bir spesifikasyondur. Belirli bir modül, HTML'ye özgü öğeleri açıklar. HTML tanımlarını www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html adresinde bulabilirsiniz.
Ağacın DOM düğümleri içerdiğini söylediğimde, ağacın DOM arayüzlerinden birini uygulayan öğelerden oluştuğunu kastediyorum. Tarayıcılar, tarayıcı tarafından dahili olarak kullanılan başka ö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ğı veya aşağıdan yukarı ayrıştırıcılar kullanılarak ayrıştırılamaz.
Bunun nedenleri şunlardır:
- Dilin bağışlayıcı yapısı.
- Tarayıcıların, bilinen geçersiz HTML durumlarını desteklemek için geleneksel hata toleransına sahip olması.
- Ayrıştırma işlemi yeniden girilebilir. Diğer diller için 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 aslında girişi değiştirir.
Normal ayrıştırma tekniklerini kullanamayan tarayıcılar, HTML'yi ayrıştırmak için özel ayrıştırıcılar oluşturur.
Ayrıştırma algoritması, HTML5 spesifikasyonunda ayrıntılı olarak açıklanmaktadır. Algoritma iki aşamadan oluşur: jeton oluşturma ve ağaç oluşturma.
Örneklendirme, girişi örnekçelere ayıran söz dizimi analizidir. HTML jetonları arasında başlangıç etiketleri, bitiş etiketleri, özellik adları ve özellik değerleri bulunur.
Jeton oluşturucu jetonu tanır, ağaç oluşturucuya verir, bir sonraki jetonu tanımak için sonraki karakteri tüketir ve girişin sonuna kadar bu şekilde devam eder.
Jeton oluşturma algoritması
Algoritmanın sonucu bir HTML jetonudur. Algoritma, durum makinesi olarak ifade edilir. Her durum, giriş akışındaki bir veya daha fazla karakteri tüketir ve bir sonraki durumu bu karakterlere göre günceller. Karar, mevcut jeton oluşturma durumu 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 örnekle başlayalım.
Temel örnek - aşağıdaki HTML'yi tokenleştirme:
<html>
<body>
Hello world
</body>
</html>
Başlangıç durumu "Veri durumu"dur.
<
karakteriyle karşılaşıldığında durum "Etiket açık durumu" olarak değiştirilir.
Bir a-z
karakterinin kullanılması "Başlangıç etiketi jetonu" oluşturulmasına neden olur ve durum "Etiket adı durumu" olarak değiştirilir.
>
karakteri tüketilene kadar bu durumda kalırız. Her karakter yeni jeton adına eklenir. Örneğimizde, oluşturulan jeton bir html
jetonudur.
>
etiketine ulaşıldığında mevcut jeton yayınlanır ve durum "Veri durumu" olarak geri döner.
<body>
etiketi de aynı adımlarla işlenir.
Şu ana kadar html
ve body
etiketleri yayınlandı. Şimdi "Veri durumu" sayfasına geri döndük.
Hello world
'un H
karakteri tüketildiğinde bir karakter jetonu oluşturulur ve yayınlanır. Bu işlem, </body>
'ın <
karakterine ulaşılana kadar devam eder. Hello world
öğesinin her karakteri için bir karakter jetonu yayınlayacağız.
Şimdi "Etiket açık durumu" bölümüne geri döndük.
Sonraki giriş /
tüketildiğinde bir end tag token
oluşturulur ve "Etiket adı durumu"'na geçilir. >
değerine ulaşana kadar yine bu durumda kalırız.Ardından yeni etiket jetonu yayınlanır ve "Veri durumu" bölümüne geri döneriz.
</html>
girişi, önceki örnekte olduğu gibi değerlendirilir.
Ağaç yapım algoritması
Ayrıştırıcı oluşturulduğunda Doküman nesnesi oluşturulur. Ağaç oluşturma aşamasında, kökünde Doküman bulunan DOM ağacı değiştirilir ve öğeler eklenir. Ayrıştırıcı tarafından oluşturulan her düğüm, ağaç oluşturucu tarafından işlenir. Spesifikasyon, her jeton için hangi DOM öğesinin onunla alakalı olduğunu ve bu jeton için oluşturulacağını tanımlar. Öğe, DOM ağacına ve açık öğe 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 açıklanır. Bu durumlara "ekleme modları" denir.
Örnek giriş için ağaç oluşturma sürecine göz atalım:
<html>
<body>
Hello world
</body>
</html>
Ağaç oluşturma aşamasına girilen giriş, parçalara ayırma aşamasından gelen bir parça dizisidir. İlk mod "başlangıç modu"dur. "html" jetonunun alınması, "html'den önce" 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, "beforehead" (başlıktan önce) olarak değiştirilir. Ardından "body" jetonu alınır. "head" jetonumuz olmasa da bir HTMLHeadElement örtük olarak oluşturulur ve ağaca eklenir.
Şimdi "kafa içinde" moduna, ardından "kafadan sonra" moduna geçiyoruz. Gövde jetonu yeniden işlenir, bir HTMLBodyElement oluşturulur ve eklenir ve mod "in body" olarak aktarılır.
"Merhaba dünya" dizesinin karakter jetonları artık alınmıştır. İlki, bir "Metin" düğümünün oluşturulmasına ve eklenmesine neden olur ve diğer karakterler o düğüme eklenir.
Gövde sonu jetonunun alınması, "gövden sonra" moduna geçişe neden olur. Ardından, bizi "body'dan sonra" moduna geçirecek html bitiş etiketini alırız. Dosya sonu jetonu alındığında ayrıştırma işlemi sonlandırılır.
Ayrıştırma işlemi tamamlandığında yapılacak işlemler
Bu aşamada tarayıcı, dokümanı etkileşimli olarak işaretler ve "ertelenen" modda olan komut dosyalarını ayrıştırmaya başlar: doküman ayrıştırıldıktan sonra yürütülmesi gerekenler. Ardından belge durumu "tamamlandı" olarak ayarlanır ve bir "yükleme" etkinliği tetiklenir.
HTML5 spesifikasyonunda jeton oluşturma ve ağaç oluşturma algoritmalarının tamamını görebilirsiniz.
Tarayıcıların hata toleransı
HTML sayfalarında hiçbir zaman "Geçersiz Söz Dizimi" hatası almazsınız. Tarayıcılar geçersiz içeriği düzeltir ve bu şekilde devam eder.
Aşağıdaki HTML'yi örnek olarak 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ış 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ı hatalarını düzeltiyor.
Hata işleme, tarayıcılarda oldukça tutarlı bir şekilde uygulanır ancak şaşırtıcı bir şekilde HTML spesifikasyonlarının bir parçası değildir. Yer işareti ve geri/ileri düğmeleri gibi, bu da tarayıcılarda yıllar içinde geliştirilmiş bir özelliktir. Birçok sitede tekrarlanan geçersiz HTML yapıları olduğu bilinmektedir ve tarayıcılar bunları diğer tarayıcılarla uyumlu bir şekilde düzeltmeye çalışır.
HTML5 spesifikasyonu bu gereksinimlerden bazılarını tanımlamaktadır. (WebKit, bunu HTML ayrıştırıcı sınıfının başındaki yorumda güzel bir şekilde özetler.)
Ayrıştırıcı, dokümana ayrıştırılmış girişi ayrıştırarak doküman ağacını oluşturur. Doküman iyi biçimlendirilmişse ayrıştırması kolaydır.
Maalesef düzgün biçimlendirilmemiş birçok HTML dokümanını işlemek zorundayız, bu nedenle ayrıştırıcının hatalar konusunda toleranslı olması gerekir.
En azından aşağıdaki hata koşullarını ele almamız gerekir:
- Eklenen öğe bazı dış etiketlerin içinde açıkça yasaklanmıştır. Bu durumda, öğeyi yasaklayana kadar tüm etiketleri kapatmalı ve ardından öğeyi eklemeliyiz.
- Öğeyi doğrudan eklememize izin verilmez. Belgeyi yazan kişinin aradaki etiketleri unutmuş olması (veya aradaki etiketin isteğe bağlı olması) mümkün olabilir. Bu durum, şu etiketlerde geçerli olabilir: HTML HEAD BODY TBODY TR TD LI (bunu unuttum?)
- Bir satır içi öğenin içine blok öğesi eklemek istiyoruz. Bir sonraki yüksek blok öğesine kadar tüm satır içi öğeleri kapatın.
- Bu işe yaramazsa, öğeyi eklememize izin verilene kadar öğeleri kapatın veya etiketi yoksayın.
Şimdi bazı WebKit hata toleransı örneklerine göz atalım:
<br>
yerine </br>
Bazı siteler <br>
yerine </br>
kullanır. WebKit, IE ve Firefox ile uyumlu olması için bunu <br>
gibi işler.
Kod:
if (t->isCloseTag(brTag) && m_document->inCompatMode()) {
reportError(MalformedBRError);
t->beginTag = true;
}
Hata işlemenin dahili olduğunu unutmayın: kullanıcıya gösterilmez.
Başıboş bir tablo
Başıboş tablo, başka bir tablonun içinde bulunan ancak tablo hücresinin içinde bulunmayan 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ş tabloyla değiştirir:
<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, mevcut öğe içerikleri için bir yığın kullanır: İç tabloyu dış tablo yığınından çıkarır. Tablolar artık kardeş olur.
İç içe yerleştirilmiş form öğeleri
Kullanıcı bir formu başka bir formun içine yerleştirirse ikinci form yok sayı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 gövde bitiş etiketleri
Yorumlar her şeyi açıklıyor.
if (t->tagName == htmlTag || t->tagName == bodyTag )
return;
Bu nedenle, web yazarları WebKit hata toleransı kod snippet'inde örnek olarak görünmek istemiyorlarsa iyi biçimlendirilmiş HTML yazmalıdır.
CSS ayrıştırma
Girişteki ayrıştırma kavramlarını hatırlıyor musunuz? HTML'den farklı olarak CSS, bağlamdan bağımsız bir dil bilgisidir ve girişte açıklanan ayrıştırıcı türleri kullanılarak ayrıştırılabilir. Aslında CSS spesifikasyonu, CSS'nin sözlük ve söz dizimi dil bilgisini tanımlar.
Birkaç örneğe göz atalım:
Sözlüksel dil bilgisi (sözlükler), her bir belirteç 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 tanımlayıcının kısaltmasıdır. "ad", bir öğe kimliğidir ("#" ile belirtilir).
Söz dizimi dil bilgisi BNF'de açıklanı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 kümesi şu yapıdır:
div.error, a.error {
color:red;
font-weight:bold;
}
div.error
ve a.error
seçicilerdir. Küme ayraçlarının içindeki kısım, bu kural grubu tarafından uygulanan kuralları içerir.
Bu yapı, bu tanımda resmi olarak tanımlanmış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 (Boşluk karakteri S ile gösterilir) olduğu anlamına gelir. Kural kümesi, köşeli parantez içerir ve bunların içinde bir bildirim veya isteğe bağlı olarak noktalı virgülle ayrılmış birkaç bildirim bulunur. "declaration" ve "selector", aşağıdaki BNF tanımlarında açıklanacaktı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. Ayıklama aracı girişinde de belirtildiği gibi Bison, aşağıdan yukarıya doğru kaydırma azaltma ayrıştırıcısı oluşturur. Firefox, manuel olarak yazılan yukarıdan aşağıya bir 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, CSS diline karşılık gelen seçici ve bildirim nesneleri ile diğer nesneleri içerir.
Komut dosyaları ve stil sayfaları için işleme sırası
Komut Dosyaları
Web'in modeli senkronizedir. Yazarlar, ayrıştırıcı bir <script>
etiketine ulaştığında komut dosyalarının anında ayrıştırılmasını ve yürütülmesini bekler.
Komut dosyası çalıştırılana kadar belgenin ayrıştırılması duraklatılır.
Komut dosyası hariciyse kaynak önce ağdan getirilmelidir. Bu işlem de senkronize olarak yapılır ve kaynak getirilene kadar ayrıştırma duraklatılır.
Bu model uzun yıllar boyunca kullanıldı ve HTML4 ile 5 spesifikasyonlarında da belirtilmiştir.
Yazarlar bir komut dosyasına "defer" özelliğini ekleyebilir. Bu durumda, doküman ayrıştırmayı durdurmaz ve doküman ayrıştırıldıktan sonra yürütülür. HTML5, komut dosyasını farklı bir iş parçacığı tarafından ayrıştırılıp yürütülmesi için ayarlamak üzere komut dosyasını asenkron olarak işaretleme seçeneği ekler.
Spekülatif 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 hangi diğer kaynakların yüklenmesi gerektiğini bulup yükler. Bu sayede, kaynaklar paralel bağlantılara yüklenebilir ve genel hız artırılabilir. 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 farklı bir modeli vardır. Kavramsal olarak, stil sayfaları DOM ağacını değiştirmediği için bunları beklemek ve belge ayrıştırmasını durdurmak için bir neden yoktur. Bununla birlikte, belge ayrıştırma aşamasında stil bilgilerini isteyen komut dosyalarıyla ilgili bir sorun vardır. Stil henüz yüklenmemiş ve ayrıştırılmamışsa komut dosyası yanlış yanıtlar alır ve bu da birçok soruna neden olur. Bu durum uç bir durum gibi görünse de oldukça yaygındır. Firefox, yüklenmeye ve ayrıştırılmaya devam eden 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.
Ağaç yapımı oluşturma
DOM ağacı oluşturulurken tarayıcı başka bir ağaç olan oluşturma ağacını da oluşturur. Bu ağaç, görsel öğelerin gösterilecekleri sırayla gösterilir. 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 yerleştirmeyi ve boyamayı iyi biliyor.
Oluşturucuların temel sınıfı olan WebKit'in 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, genellikle CSS2 spesifikasyonunda açıklandığı gibi bir düğümün CSS kutusuna karşılık gelen dikdörtgen bir alanı temsil eder. Bu alan, 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 hesaplaması bölümüne bakın). Görüntü özelliğine göre bir DOM düğümü için ne tür oluşturucunun oluşturulması gerektiğine karar vermeye yönelik WebKit kodunu aşağıda bulabilirsiniz:
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 özel çerçevelere sahiptir.
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. Örneğin, "head" öğesi verilebilir. Ayrıca, görüntüleme değeri "yok" olarak atanan öğeler de ağaçta görünmez (ancak "gizli" görünürlük değerine sahip öğeler ağaçta görünür).
Birkaç görsel nesneye karşılık gelen DOM öğeleri vardır. Bunlar genellikle tek bir dikdörtgenle açıklanamayan karmaşık yapıya sahip öğelerdir. Örneğin, "select" öğesinin üç oluşturucusu vardır: biri görüntüleme alanı, biri açılır liste kutusu ve biri düğme içindir. 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şturucular olarak eklenir.
Birden fazla oluşturma aracına örnek olarak bozuk HTML verilebilir. CSS spesifikasyonuna göre satır içi öğeler yalnızca blok öğeler veya yalnızca satır içi öğeler içermelidir. Karışık 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 değildir. Yüzen ve mutlak konumlandırılmış öğeler akış 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, öğelerin olması gereken yerdir.
Ağacın oluşturulma akışı
Firefox'ta sunu, DOM güncellemeleri için dinleyici olarak kaydedilir.
Sunum, çerçeve oluşturma işlemini FrameConstructor
'e delege eder ve kurucu, stili çözer (stil hesaplaması bölümüne bakın) ve bir çerçeve oluşturur.
WebKit'te stili çözme ve bir oluşturma işlemine "ekleme" adı verilir. Her DOM düğümünün bir "attach" yöntemi vardır. Ek eşzamanlıdır. 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 spesifikasyonunda kapsayıcı blok olarak adlandırılan öğeye karşılık gelir: diğer tüm blokları içeren en üst blok. Boyutları görüntü alanıdır: tarayıcı penceresi görüntüleme alanı boyutları.
Firefox'ta ViewPortFrame
, WebKit'te ise RenderView
olarak adlandırılır.
Bu, belgenin işaret ettiği oluşturma nesnesidir.
Ağacın geri kalanı, DOM düğümü ekleme olarak oluşturulur.
İşleme modelindeki 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 bir öğenin stil özellikleri hesaplanarak yapılır.
Stil, HTML'de çeşitli kaynaklı stil sayfaları, satır içi stil öğeleri ve görsel özellikler ("bgcolor" özelliği gibi) içerir. Daha sonra, eşleşen CSS stili özelliklerine dönüştürülür.
Stil sayfalarının kaynağı; tarayıcının varsayılan stil sayfaları, sayfanın 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 sağlar). Örneğin, Firefox'ta bu işlem "Firefox Profili" klasörüne bir stil sayfası yerleştirilerek yapılır.
Stil hesaplaması birkaç zorluk ortaya çıkarır:
- 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.
Her öğe için eşleşen kuralları bulmak, öğe optimize edilmemişse performans sorunlarına neden olabilir. Eşleşmeleri bulmak için her bir öğeye ait kural listesinin tamamını taşımak ciddi bir iştir. Seçiciler karmaşık bir yapıya sahip olabilir. Bu durum, eşleştirme sürecinin ümit verici görünen bir yolda başlamasına neden olabilir. Ancak bu yol işe yaramaz ve başka bir yol denenmesi gerekir.
Örneğin, şu birleşik seçici:
div div div div{ ... }
Bu, 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 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.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 başa çıktığına bakalım:
Stil verisi paylaşılıyor
WebKit düğümleri stil nesnelerine (RenderStyle) referans verir. Bu nesneler, bazı koşullarda düğümler tarafından paylaşılabilir. Düğümler kardeş veya kuzendir ve:
- Öğeler aynı fare durumunda olmalıdır (ör. biri :hover durumundayken diğeri bu durumda olamaz)
- Hiçbir öğenin kimliği olmamalıdır.
- Etiket adları eşleşmelidir
- Sınıf özellikleri eşleşmelidir
- Eşlenen özellikler grubu aynı olmalıdır
- Bağlantı durumları eşleşmelidir
- Odak durumları eşleşmelidir
- Hiçbir öğe özellik seçicilerden etkilenmemelidir. Etkilenen, seçici içinde herhangi bir konumda özellik seçici kullanan herhangi bir seçici eşleşmesine sahip olmak olarak tanımlanır.
- Öğelerde satır içi stil özelliği bulunmamalıdır.
- Kullanılan eşdüzey seçici bulunmamalıdır. WebCore, herhangi bir kardeş seçiciyle karşılaşıldığında genel bir anahtar atar ve mevcut olduğunda stil paylaşımını belgenin tamamı için devre dışı bırakır. Buna, + seçici ile :first-child ve :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 stili gösterir.
Stil bağlamları bitiş değerleri içerir. Değerler, tüm eşleşme kurallarının doğru sırada uygulanması ve mantıksal değerlerden somut değerlere dönüştüren işlemler gerçekleştirerek 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 akıllıca. Bu değerleri tekrar hesaplamamak için düğümler arasında paylaşılmasını sağlar. Bu işlem, alandan da tasarruf etmenizi sağlar.
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, tembel bir şekilde depolanır. Ağaç, her düğüm için başlangıçta 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:
İçerik ağacındaki başka bir öğeyle ilgili 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 zaten hesapladığımız için bu yol ağaçta zaten mevcuttur. Artık daha az işimiz olacak.
Ağacın çalışmamızı nasıl kurtardığını görelim.
Yapılara bölme
Stil bağlamları yapılara ayrılır. Bu yapıların her biri, kenar veya renk gibi belirli bir kategoriye ait stil bilgilerini içerir. Bir yapıdaki tüm özellikler devralınır veya devralınmaz. Devralınan özellikler, öğe tarafından tanımlanmayan ve ü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ç, yapıların tamamını (hesaplanmış bitiş değerlerini içeren) ağaçta önbelleğe alarak bize yardımcı olur. Buradaki amaç, alt düğüm bir yapı için bir 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 hesaplanır veya mevcut bir yol kullanılır. Ardından, yapıları yeni stil bağlamımızda doldurmak için yoldaki kuralları uygulamaya başlarız. Yolun en alt düğümünden (en yüksek önceliğe sahip olandan, genellikle en spesifik seçiciden) başlar ve yapımız dolana kadar ağacı tararız. Söz konusu kural düğümünde yapı için bir spesifikasyon yoksa büyük ölçüde optimizasyon yapabiliriz. Yapıyı tam olarak belirten ve işaret eden bir düğüm bulana kadar ağaçta yukarı doğru gideriz. Bu, en iyi optimizasyondur. Yapının tamamı paylaşılır. Bu sayede son değerlerin hesaplanmasına ve belleğe gerek kalmaz.
Kısmi tanımlar bulursak yapı doldurulana kadar ağaçta yukarı doğru gideriz.
struct'ımızın bir tanımını bulamazsak struct'ın "devralınan" tür bir tür olması durumunda bağlam ağacında üst öğemizin struct'ını belirtiriz. Bu örnekte, struct'ları da başarıyla paylaştık. Sıfırlanan bir yapıysa varsayılan değerler kullanılır.
En spesifik düğüm değer ekliyorsa bunu gerçek değerlere dönüştürmek için bazı ek hesaplamalar yapmamız gerekir. Ardından, sonucu çocukların kullanabilmesi için ağaç düğümünde önbelleğe alırız.
Bir öğenin aynı ağaç düğümünü işaret eden bir kardeşi veya abisi varsa stil bağlamının tamamı bunlar arasında paylaşılabilir.
Bir örneğe göz atalı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 kurallara uyulmalıdır:
div {margin: 5px; color:black}
.err {color:red}
.big {margin-top:3px}
div span {margin-bottom:4px}
#div1 {color:blue}
#div2 {color:green}
İşlemi basitleştirmek için yalnızca iki struct doldurmamız gerektiğini varsayalım: renk struct'u ve kenar boşluğu struct'u. Renk yapısı yalnızca bir üye içerir: renk. Kenarlık yapısı dört tarafı içerir.
Sonuçta ortaya çıkan 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ı):
Bağlam ağacı şöyle görünür (düğüm adı: işaret ettikleri kural düğümü):
HTML'yi ayrıştırıp 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 gerekir.
Kuralları eşleştirecek ve <div>
için eşleşme kurallarının 1, 2 ve 6 olduğunu belirleyeceğiz.
Bu, ağaçta öğemizin kullanabileceği mevcut bir yol olduğu anlamına gelir. 6. kural için bu yola başka bir düğüm (kural ağacındaki F düğümü) eklememiz yeterlidir.
Bir stil bağlamı oluşturup bağlam ağacına ekleyeceğiz. Yeni stil bağlamı, kural ağacındaki F düğümüne işaret eder.
Şimdi stil yapılarını doldurmamız gerekiyor. Öncelikle margin yapısını doldurarak başlayacağız. Son kural düğümü (F), kenar boşluğu yapısını artırmadığından, önceki bir düğüm eklemede hesaplanan önbelleğe alınmış bir yapı bulana kadar ağaçta yukarı gidebilir ve bu yapıyı kullanabiliriz. Bu değeri, kenar boşluğu kurallarını belirten en üstteki B düğümünde buluruz.
color struct'ın bir tanımımız olduğundan önbelleğe alınmış bir struct'ı kullanamayız. Rengin bir özelliği olduğundan diğer özellikleri doldurmak için ağacın yukarısına çı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>
öğesi üzerinde çalışmak daha da kolaydır. Kuralları eşleştiririz ve önceki aralığın yaptığı gibi G kuralını işaret ettiği sonucuna varırız.
Aynı düğümü işaret eden kardeş öğelerimiz olduğundan stil bağlamının tamamını paylaşabilir ve yalnızca önceki aralığın bağlamını işaret edebiliriz.
Üst öğeden devralınan kuralları içeren struct'larda, önbelleğe alma işlemi içerik ağacında yapılır (renk özelliği aslında devralınır, ancak Firefox bunu sıfırlanmış olarak değerlendirir ve kural ağacında önbelleğe alır).
Örneğin, bir paragrafta yazı tipleriyle ilgili kurallar eklediysek:
p {font-family: Verdana; font size: 10px; font-weight: bold}
Bu durumda, bağlam ağacında div öğesinin alt öğesi olan paragraf öğesi, üst öğesiyle aynı yazı tipi struct'ını paylaşmış olabilir. Bu durum, paragraf için yazı tipi kuralı belirtilmediyse geçerlidir.
Kural ağacı olmayan WebKit'te eşleşen bildirimler dört kez taranır. Öncelikle önemli olmayan yüksek öncelikli mülkler (görüntülü reklam gibi diğer mülkler bunlara bağlı olduğu için önce uygulanması gereken mülkler), ardından önemli olan yüksek öncelikli mülkler, ardından normal öncelikli önemli olmayan mülkler ve ardından 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. Sonuncu 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ı değiştirme
Stil kuralları için birkaç kaynak vardır:
- Harici stil sayfalarında veya stil öğelerinde CSS kuralları.
css p {color: blue}
html <p style="color: blue" />
gibi satır içi stil özellikleri- HTML görsel özellikleri (ilgili stil kurallarıyla eşlenir)
html <p bgcolor="blue" />
Son ikisi stil özelliklerine sahip olduğundan öğeyle kolayca eşleştirilebilir. HTML özellikleri ise anahtar olarak öğe kullanılarak eşlenebilir.
Daha önce 2. sorunda belirtildiği gibi, CSS kuralı eşleştirmesi daha karmaşık olabilir. Bu zorluğu çözmek için kurallar, daha kolay erişim için değiştirilir.
Stil sayfası ayrıştırıldıktan sonra kurallar, seçiciye göre çeşitli karma haritalarından birine eklenir. Kimlik, sınıf adı ve etiket adına göre haritalar ve bu kategorilere uymayan her şey için genel bir harita bulunur. Seçici bir kimlikse kural, kimlik haritasına, sınıfsa sınıf haritasına eklenir.
Bu değiştirme, kuralları eşleştirmeyi çok daha kolay hale getirir. Her beyanı incelemeniz gerekmez. Bir öğeyle ilgili alakalı kuralları haritalardan ayıklayabiliriz. Bu optimizasyon, kuralların %95'inden fazlasını ortadan kaldırır. Böylece, eşleştirme işlemi sırasında (4.1) kuralların dikkate alınmasına bile gerek kalmaz.
Ö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 haritasına, üçüncüsü ise etiket haritasına 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ışacağız. Sınıf haritası, "p.error" kuralının bulunduğu bir "error" anahtarı içerir. div öğesi, kimlik haritasında (anahtar kimliktir) ve etiket haritasında ilgili kurallara sahiptir. Geriye kalan tek iş, anahtarlardan ayıklanan kurallardan hangilerinin gerçekten eşleştiğini bulmaktır.
Örneğin, div için kural şu şekildeyse:
table div {margin: 5px}
Anahtar en sağdaki seçici olduğundan etiket haritasından yine ayıklanır ancak tablo üst öğesi olmayan div öğemizle eşleşmez.
Hem WebKit hem de Firefox bu manipülasyonu yapar.
Stil sayfası basamaklı sıralaması
Stil nesnesi, her görsel özelliğe karşılık gelen özelliklere (tüm CSS özellikleri ancak daha genel) sahiptir. Mülk, eşleşen kurallardan hiçbiriyle tanımlanmamışsa bazı özellikler üst öğe stili nesnesi tarafından devralınabilir. Diğer özelliklerin varsayılan değerleri vardır.
Sorun, birden fazla tanım olduğunda başlar. Burada sorunu çözmek için basamak sırası gelir.
Bir stil mülkünün beyanı, birkaç stil sayfasında ve bir stil sayfasında birkaç kez görünebilir. Bu, kuralların uygulanma sırasının çok önemli olduğu anlamına gelir. Buna "basamak" sırası denir. CSS2 spesifikasyonuna göre basamak sırası (düşükten yükseğe):
- Tarayıcı bildirimleri
- Kullanıcı normal bildirimleri
- Yazar normal beyanları
- Yazarla ilgili önemli beyanlar
- Kullanıcılarla ilgili önemli beyanlar
Tarayıcı beyanları en az önemlidir ve kullanıcı, yalnızca beyan önemli olarak işaretlenmişse yazarı geçersiz kılar. Aynı sıraya sahip bildirimler spesifikliğe ve ardından belirtildikleri sıraya göre sıralanır. HTML görsel özellikleri, eşleşen CSS bildirimlerine dönüştürülür . Bunlar, düşük öncelikli yazar kuralları olarak değerlendirilir.
Belirginlik
Seçici netliği CSS2 spesifikasyonu tarafından şu şekilde tanımlanır:
- Kaynaktaki beyan bir seçici içeren kural yerine "stil" özelliğiyse 1, aksi takdirde 0 değerini alır (= a)
- Seçicideki kimlik özelliklerinin sayısını sayın (= b).
- seçicideki diğer özelliklerin ve sözde sınıfların sayısını say (= c)
- Seçicideki öğe adlarının ve sözde öğelerin sayısını sayma (= d)
a-b-c-d dört sayısının birleştirilmesi (büyük tabanlı bir sayı sisteminde) özgünlüğü sağlar.
Kullanmanız gereken sayı tabanı, kategorilerden birinde sahip olduğunuz en yüksek sayıya göre belirlenir.
Örneğin, a=14 ise on altılık taban kullanabilirsiniz. a=17 olması ihtimali düşük olsa da 17 haneli bir sayı tabanına ihtiyacınız olacaktır. İkinci durum, aşağıdaki gibi bir seçici kullanıldığında ortaya çıkabilir: html body div div p… (Seçicinizde 17 etiket var… Bu olasılık çok düşüktür.)
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 basamak kuralına göre sıralanır.
WebKit, küçük listeler için kabarcık sıralama, büyük listeler için ise birleştirme sıralama kullanır.
WebKit, kurallar için >
operatörünü geçersiz kılarak sıralamayı 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üklendiğini gösteren bir işaret kullanır. Stil eklenirken 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şturulan ve ağaca eklenen oluşturma aracının konumu ve boyutu yoktur. Bu değerlerin hesaplanmasına sayfa düzeni veya yeniden akış adı verilir.
HTML, akışa dayalı 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, doküman boyunca düzen soldan sağa, yukarıdan aşağıya doğru ilerleyebilir. Bununla birlikte, istisnalar da vardır. Örneğin, HTML tabloları için birden fazla geçiş gerekebilir.
Koordinat sistemi, kök çerçeveye göredir. Üst ve sol koordinatlar kullanılır.
Düzenleme, yinelemeli bir süreçtir. HTML dokümanının <html>
öğesine karşılık gelen kök oluşturucuda başlar. Düzenleme, çerçeve hiyerarşisinin bir kısmında veya tamamında yinelemeli olarak devam eder ve bunu gerektiren her oluşturma aracı için geometrik bilgileri hesaplar.
Kök oluşturma aracının konumu 0,0'dır ve boyutları, tarayıcı penceresinin görünür kısmı olan görüntü alanıdır.
Tüm oluşturucuların bir "düzen" veya "yeniden düzenleme" yöntemi vardır. Her oluşturucu, düzene ihtiyacı olan alt öğelerinin düzen yöntemini çağırır.
Kirli bit sistemi
Tarayıcılar, her küçük değişiklikte tam bir düzen kurmamak için "kirli bit" sistemi kullanır. Değiştirilen veya eklenen bir oluşturucu, kendisini ve alt öğelerini "kirli" olarak işaretler: düzene ihtiyaç vardır.
İki işaret vardır: "kirli" ve "alt öğeler kirli". Bu işaretler, oluşturucunun kendisi iyi olsa bile düzene ihtiyaç duyan en az bir alt öğesinin olduğu anlamına gelir.
Global ve artımlı düzen
Düzen, oluşturma ağacının tamamında tetiklenebilir. Bu "evrensel" düzendir. Bu durum aşağıdakilerden kaynaklanabilir:
- Yazı tipi boyutu değişikliği gibi tüm oluşturma araçlarını etkileyen genel bir stil değişikliği.
- Ekranın yeniden boyutlandırılması
Düzen artımlı olabilir, yalnızca kirli oluşturucular yerleştirilir (bu durum ek düzen gerektirecek bazı hasarlara neden olabilir).
Oluşturucular kirli olduğunda artımlı düzen tetiklenir (asynchronize olarak). Örneğin, ağdan ek içerik gelip DOM ağacına eklendikten sonra yeni oluşturma araçları oluşturma ağacına eklendiğinde.
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, artımlı bir düzeni yürüten bir zamanlayıcı da vardır. Ağda gezinilir ve "kirli" oluşturma araçları düzenlenir.
"offsetHeight" gibi stil bilgileri isteyen komut dosyaları, artımlı düzeni eşzamanlı olarak tetikleyebilir.
Genel düzen genellikle senkronize olarak tetiklenir.
Düzen, bazen kaydırma konumu gibi bazı özelliklerin değişmesi nedeniyle bir ilk düzenden sonra geri çağırma olarak tetiklenir.
Optimizasyonlar
Bir düzen "yeniden boyutlandırma" veya oluşturma aracının konumunda(boyut değil) bir değişiklik nedeniyle 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 etrafındakileri etkilemediği durumlarda (ör. metin alanlarına eklenen metin) ortaya çıkabilir. Aksi takdirde her tuş vuruşu, kökten başlayan bir düzeni tetikler.
Düzenleme süreci
Düzen genellikle aşağıdaki kalıba sahiptir:
- Üst oluşturucu kendi genişliğini belirler.
- Ebeveyn, çocukları kontrol eder ve:
- Alt oluşturucuyu yerleştirin (x ve y değerlerini ayarlar).
- Gerekirse alt öğe düzenini çağırır (öğeler kirliyse veya küresel bir düzendeysek ya da başka bir nedenle). Bu düzen, alt öğenin yüksekliğini hesaplar.
- Üst öğe, kendi yüksekliğini ayarlamak için alt öğelerin kümülatif yüksekliklerini ve kenar boşluklarının ve dolguların yüksekliklerini kullanır. Bu değer, üst öğe oluşturma aracının üst öğesi tarafından kullanılır.
- Kirli bitini false (yanlış) olarak ayarlar.
Firefox, sayfa düzeni için parametre olarak bir "durum" nesnesi(nsHTMLReflowState) kullanır ("yeniden akış" olarak adlandırılır). Durum, diğerlerinin yanı sıra ebeveyn genişliğini içerir.
Firefox düzeninin çıktısı bir "metrics" nesnesidir(nsHTMLReflowMetrics). Oluşturucunun hesapladığı yüksekliği içerir.
Genişlik hesaplama
Oluşturucunun genişliği, kapsayıcı bloğunun genişliği, oluşturucunun stil "genişlik" ö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 aşağıdaki gibi hesaplanır(RenderBox sınıfı calcWidth yöntemi):
- Kapsayıcı genişliği, mevcut kapsayıcıların maksimum genişliği ve 0'dır. Bu durumda availableWidth, contentWidth değeridir ve şu şekilde hesaplanır:
clientWidth() - paddingLeft() - paddingRight()
clientwidth ve clientHeight, kenarlık ve kaydırma çubuğu hariç bir nesnenin iç kısmını temsil eder.
Öğenin genişliği, "width" stil özelliğidir. Kapsayıcı genişliğinin yüzdesi hesaplanarak mutlak bir değer olarak hesaplanır.
Yatay kenarlıklar ve dolgular eklendi.
Şimdiye kadar bu "tercih edilen genişlik" hesaplaması yapılıyordu. Ardından minimum ve maksimum genişlikler hesaplanır.
Tercih edilen genişlik maksimum genişlikten büyükse maksimum genişlik kullanılır. Minimum genişlikten (en küçük bölünemez birim) azsa minimum genişlik kullanılır.
Bir düzene ihtiyaç olması ihtimaline karşı değerler önbelleğe alınır, ancak genişlik değişmez.
Satır sonu
Bir düzenin ortasında bulunan 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 ekranda içerik 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.
Küresel ve artımlı
Düzenleme gibi boyama da genel olabilir (ağacın tamamı boyanır) veya artımlı olabilir. Artımlı boyamada, bazı oluşturma araçları ağacın tamamını etkilemeyecek şekilde değişir. Değiştirilen oluşturucu, ekrandaki dikdörtgeni geçersiz kılar. Bu, işletim sisteminin bu alanı "kirli bölge" olarak görmesine ve bir "boya" etkinliği oluşturmasına neden olur. İşletim sistemi bunu akıllıca yapar ve birkaç bölgeyi tek bir bölge altında birleştirir. Oluşturucu, ana işlemden farklı bir işlemde olduğundan Chrome'da bu daha karmaşıktır. Chrome, işletim sistemi davranışını bir dereceye kadar simüle eder. Sunum bu etkinlikleri dinler ve mesajı, oluşturma köküne aktarır. İlgili oluşturma aracına ulaşana kadar ağaç taranacaktır. Kendini (ve genellikle alt öğelerini) yeniden boyar.
Boyama sırası
CSS2, boyama sürecinin sırasını tanımlar. Bu, öğelerin yığma bağlamlarında yığılma sırasıdır. Gruplar arkadan öne doğru boyandığından bu sıra boyamayı etkiler. Bir blok oluşturma aracının yığılma sırası şu şekildedir:
- arka plan rengi
- arka plan resmi
- border
- çocuklar
- outline
Firefox görüntüleme listesi
Firefox, oluşturma ağacının üzerinden geçer ve boyanmış dikdörtgen için bir görüntüleme listesi oluşturur. Dikdörtgenle ilgili oluşturucuları doğru tablo sıralamasında (oluşturucuların arka planları, kenarlıklar vb.) içerir.
Bu sayede, ağacın yeniden boyanması için birkaç kez değil, yalnızca bir kez dolaşılması gerekir. Önce tüm arka planlar, ardından tüm resimler, ardından tüm kenarlıklar vb. boyanır.
Firefox, tamamen diğer opak öğelerin altında olan öğeler gibi gizlenecek öğeler eklemeyerek süreci optimize eder.
WebKit dikdörtgen depolama alanı
WebKit, yeniden boyamadan önce eski dikdörtgeni bitmap 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 yeniden boyanmasına neden olur. Öğe konumunda yapılan değişiklikler; öğenin, alt öğelerinin ve muhtemelen kardeşlerin düzenine ve yeniden boyanmasına neden olur. DOM düğümü eklemek, düğümün düzenlenmesine ve yeniden boyanmasına neden olur. "html" öğesinin yazı tipi boyutunu artırmak gibi önemli değişiklikler, önbellekleri geçersiz kılar, ağacın tamamını yeniden düzenler ve yeniden boyar.
Oluşturma motorunun iş parçacıkları
Oluşturma motoru tek iş parçacıklıdır. Ağ işlemleri dışında 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 mesaj dizisidir.
Ağ işlemleri, birkaç paralel iş parçacığı kullanılarak gerçekleştirilebilir. Paralel bağlantıların 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ürecin devam etmesini sağlayan sonsuz bir döngüdür. Etkinlikleri (düzen ve boyama etkinlikleri gibi) bekler ve işler. Aşağıda, ana etkinlik döngüsü için Firefox kodu verilmiştir:
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ı", yani tarayıcının içeriği boyadığı yeri tanımlar.
Zemin, alanın her boyutu için sınırsızdır 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, kanvas başka bir öğenin içindeyse şeffaftır, aksi takdirde tarayıcı tarafından tanımlanan bir renge sahip olur.
CSS Box modeli
CSS kutu modeli, belge 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ı olarak çevresindeki dolgu, kenarlık ve kenar boşluğu alanları vardır.
Her düğüm 0 ila n tane böyle 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, satır içidir ancak tarayıcı stil sayfası başka varsayılanlar ayarlayabilir. Örneğin, "div" öğesi için varsayılan ekran bloktur.
Varsayılan stil sayfası örneğini şu adreste bulabilirsiniz: www.w3.org/TR/CSS2/sample.html.
Konumlandırma şeması
Üç şema vardır:
- Normal: Nesne dokümandaki konumuna göre konumlandırılır. Bu, onun 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
- Yüzen: Nesne önce normal akış gibi düzenlenir, ardından mümkün olduğunca sola veya sağa taşınır.
- Mutlak: Nesne, oluşturma ağacında DOM ağacından farklı bir yere yerleştirilmiştir
Konumlandırma şeması, "position" mülkü ve "float" özelliği tarafından belirlenir.
- statik ve göreceli olması normal akışa neden olur
- mutlak ve sabit neden mutlak konumlandırma
Statik konumlandırmada konum tanımlanmaz ve varsayılan konumlandırma kullanılır. Diğer şemalarda, yazar konumu belirtir: üst, alt, sol, sağ.
Kutunun düzeni şu faktörlere göre belirlenir:
- Kutu türü
- Kutu boyutları
- Konumlandırma şeması
- Resim boyutu ve ekran boyutu gibi harici bilgiler
Kutu türleri
Blok kutusu: Bir blok oluşturur. Tarayıcı penceresinde kendi dikdörtgeni vardır.
Satır içi kutu: Kendi bloğuna sahip değildir ancak kapsayıcı bir bloğun içindedir.
Bloklar, arka arkaya dikey olarak biçimlendirilir. Satır içi satırlar yatay olarak biçimlendirilir.
Satır içi kutular, satırların veya "satır kutularının" içine yerleştirilir. Çizgiler en az en yüksek kutunun yüksekliğindedir ancak kutular "referans değeri"yle hizalandığında daha yüksek olabilir. Bu, bir öğenin alt kısmının, alttan başka bir kutunun bir noktasında hizalandığı anlamına gelir. Kapsayıcı genişliği yeterli değilse satır içi öğeler birkaç satıra yerleştirilir. Bu genellikle bir paragrafta olur.
Konumlandırma
Akraba
Göreli konumlandırma: Normal şekilde konumlandırılır ve ardından gereken delta değeri kadar hareket ettirilir.
Kayanlar
Yüzen kutu, bir satırın soluna veya sağına kaydırılır. İlginç bir özellik de diğer kutuların 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ünecek:
Mutlak ve sabit
Düzen, normal akıştan bağımsız olarak tam olarak tanımlanır. Öğe normal akışa dahil değildir. Boyutlar kapsayıcıya bağlıdır. Sabit durumda, kapsayıcı görüntü alanıdır.
Katmanlı temsil
Bu, z-index CSS mülkü tarafından belirtilir. Kutunun üçüncü boyutunu (yani "z ekseni"ndeki konumunu) temsil eder.
Kutular, yığınlara (yığın bağlamı olarak adlandırılır) ayrılır. Her yığında önce arkadaki öğeler, kullanıcıya daha yakın olacak şekilde en üstte olan ileri öğeler boyanır. Örtüşme olması durumunda en üstteki öğe, önceki öğeyi gizler.
Gruplar, z-endeksi özelliğine göre sıralanır. "z-index" özelliğine sahip kutular yerel bir grup 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 olur:
Kırmızı div, işaretlemede yeşil div'den önce gelir ve normal akışta daha önce boyanırdı. Ancak z-endeksi özelliği daha yüksek olduğundan kök kutu tarafından tutulan yığınta daha ileridedir.
Kaynaklar
Tarayıcı mimarisi
- Grosskurth, Ahmet. Web Tarayıcıları İçin Referans Mimari (pdf)
- Gupta, Vineet. Tarayıcılar Nasıl Çalışır? - Bölüm 1 - Mimari
Ayrıştırma
- Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools ("Dragon book" olarak da bilinir), Addison-Wesley, 1986
- Rick Jelliffe. The Bold and the Beautiful: HTML 5 için iki yeni taslak.
Firefox
- L. David Baron, Daha hızlı HTML ve CSS: Web Geliştiricileri için Layout Engine Dahilileri.
- L. David Baron, Daha Hızlı HTML ve CSS: Web Geliştiricileri İçin Düzenleme Motoru İçeriği (Google teknik konuşması videosu)
- L. David Baron, Mozilla Layout Engine
- L. David Baron, Mozilla Stilinde Sistem Dokümanları
- Chris Waterson, Notes on HTML Reflow
- Chris Waterson, Gecko'ya Genel Bakış
- Alexander Larsson, HTML HTTP isteğinin ömrü
WebKit
- David Hyatt, CSS'yi Uygulama(1. bölüm)
- David Hyatt, WebCore'a Genel Bakış
- David Hyatt, WebCore Oluşturma
- David Hyatt, The FOUC Problem
W3C Özellikleri
Tarayıcılar için derleme talimatları
Çeviriler
Bu sayfa iki kez Japoncaya çevrildi:
- How Browsers Work - Behind the Scenes of Modern Web Browsers (ja) @kosei tarafından
- ブラウザってどうやって動いてるの?(モダンWEBブラウザシーンの裏側 @ikeike443 ve @kiyoto01 tarafından.
Korece ve Türkçe dillerinde harici olarak barındırılan çevirileri görüntüleyebilirsiniz.
Hepinize teşekkürler.