ब्राउज़र कैसे काम करते हैं

मॉडर्न वेब ब्राउज़र की अनसुनी बातें

भूमिका

WebKit और Gecko के अंदरूनी कामकाज के बारे में विस्तार से जानकारी देने वाली यह जानकारी, इज़रायल के डेवलपर ताली गार्सिएल की रिसर्च के आधार पर तैयार की गई है. कुछ सालों तक, उन्होंने ब्राउज़र के इंटर्नल के बारे में पब्लिश किए गए सारे डेटा की समीक्षा की और वेब ब्राउज़र के सोर्स कोड को पढ़ने में काफ़ी समय बिताया. उन्होंने लिखा:

वेब डेवलपर के तौर पर, ब्राउज़र की अंदरूनी कार्रवाइयों की जानकारी पाने से, आपको बेहतर फ़ैसले लेने में मदद मिलती है. साथ ही, आपको यह भी पता चलता है कि डेवलपर बनने के सबसे सही तरीके क्या हैं. हालांकि, यह दस्तावेज़ काफ़ी लंबा है, फिर भी हमारा सुझाव है कि आप इस पर ध्यान दें. आपको बहुत खुशी होगी.

पॉल आयरिश, Chrome डेवलपर रिलेशंस

शुरुआती जानकारी

वेब ब्राउज़र सबसे ज़्यादा इस्तेमाल किए जाने वाले सॉफ़्टवेयर हैं. इस प्राइमर में, मैंने बताया है कि वे पर्दे के पीछे कैसे काम करती हैं. जब तक आपको ब्राउज़र स्क्रीन पर Google पेज नहीं दिखता, तब तक हम यह देखेंगे कि पता बार में google.com टाइप करने पर क्या होता है.

ऐसे ब्राउज़र जिन पर हम बात करेंगे

आज-कल डेस्कटॉप पर पांच मुख्य ब्राउज़र इस्तेमाल किए जाते हैं: Chrome, Internet Explorer, Firefox, Safari, और Opera. मोबाइल पर, मुख्य ब्राउज़र Android ब्राउज़र, iPhone, Opera Mini और Opera Mobile, UC ब्राउज़र, Nokia S40/S60 ब्राउज़र और Chrome हैं. इनमें से सभी Opera ब्राउज़र के अलावा, WebKit पर आधारित हैं. मैं ओपन सोर्स ब्राउज़र Firefox और Chrome, और Safari (जो कि आंशिक रूप से ओपन सोर्स है) से उदाहरण दूंगा. StatCounter के आंकड़ों के अनुसार (जून 2013 तक) वैश्विक डेस्कटॉप ब्राउज़र के इस्तेमाल का करीब 71% हिस्सा Chrome, Firefox और Safari का है. मोबाइल पर, Android ब्राउज़र, iPhone, और Chrome का 54% इस्तेमाल किया जाता है.

ब्राउज़र की मुख्य फ़ंक्शन

ब्राउज़र का मुख्य काम, आपके चुने गए वेब रिसॉर्स को सर्वर से अनुरोध करके और उसे ब्राउज़र विंडो में दिखाकर लोगों को दिखाना होता है. आम तौर पर, यह रिसॉर्स एक एचटीएमएल दस्तावेज़ होता है. हालांकि, यह PDF, इमेज या किसी दूसरी तरह का कॉन्टेंट भी हो सकता है. संसाधन की जगह को उपयोगकर्ता, यूआरआई (यूनिफ़ॉर्म रिसॉर्स आइडेंटिफ़ायर) का इस्तेमाल करके तय करता है.

ब्राउज़र, एचटीएमएल फ़ाइलों को जिस तरह से देखता और दिखाता है उसके बारे में एचटीएमएल और सीएसएस की खास बातों में बताया गया है. इन स्पेसिफ़िकेशन का रखरखाव, W3C (वर्ल्ड वाइड वेब कंसोर्टियम) संगठन करता है. W3C (वर्ल्ड वाइड वेब कंसोर्टियम) संगठन, वेब के लिए स्टैंडर्ड संगठन है. सालों तक ब्राउज़र ने सिर्फ़ खास निर्देशों का पालन किया और अपने एक्सटेंशन डेवलप किए. इससे वेब लेखकों के साथ काम करने में गंभीर समस्याएं आ रही थीं. आज-कल ज़्यादातर ब्राउज़र, निर्देशों का कुछ हद तक पालन करते हैं.

कई ब्राउज़र यूज़र इंटरफ़ेस, एक-दूसरे से काफ़ी मिलते-जुलते हैं. सामान्य यूज़र इंटरफ़ेस एलिमेंट में ये एलिमेंट शामिल हैं:

  1. यूआरआई शामिल करने के लिए पता बार
  2. 'वापस जाएं' और 'आगे बढ़ें' बटन
  3. बुकमार्क करने के विकल्प
  4. मौजूदा दस्तावेज़ों को लोड होने से रोकने या रीफ़्रेश करने के लिए, रीफ़्रेश और बंद करने वाले बटन
  5. होम बटन, जो आपको होम पेज पर ले जाता है

हैरानी की बात है कि ब्राउज़र के यूज़र इंटरफ़ेस का किसी भी औपचारिक तरीके में ज़िक्र नहीं किया गया है. यह सिर्फ़ उन अच्छे तरीकों की वजह से आता है जो सालों के अनुभव और ब्राउज़र के एक-दूसरे की नकल करते हैं. HTML5 ब्यौरा, ब्राउज़र के लिए ज़रूरी यूज़र इंटरफ़ेस (यूआई) एलिमेंट तय नहीं करता. हालांकि, इसमें कुछ सामान्य एलिमेंट की सूची होती है. इनमें पता बार, स्टेटस बार, और टूल बार शामिल हैं. इसमें Firefox के डाउनलोड मैनेजर जैसे किसी खास ब्राउज़र की खास विशेषताएं होती हैं.

बेहतर सुविधाएं

ब्राउज़र के मुख्य कॉम्पोनेंट:

  1. यूज़र इंटरफ़ेस: इसमें पता बार, बैक-फ़ॉरवर्ड बटन, बुकमार्किंग मेन्यू वगैरह शामिल हैं. ब्राउज़र का हर हिस्सा, उस विंडो को छोड़कर दिखता है जहां आपको अनुरोध किया गया पेज दिखता है.
  2. ब्राउज़र इंजन: यूज़र इंटरफ़ेस (यूआई) और रेंडरिंग इंजन के बीच मार्शल ऐक्शन.
  3. रेंडरिंग इंजन: यह अनुरोध किए गए कॉन्टेंट को दिखाने के लिए ज़िम्मेदार है. उदाहरण के लिए, अगर अनुरोध किया गया कॉन्टेंट एचटीएमएल है, तो रेंडरिंग इंजन एचटीएमएल और सीएसएस को पार्स करता है और पार्स किया गया कॉन्टेंट स्क्रीन पर दिखाता है.
  4. नेटवर्किंग: इसमें अलग-अलग प्लैटफ़ॉर्म के लिए अलग-अलग तरीकों का इस्तेमाल करके, एचटीटीपी अनुरोध जैसे नेटवर्क कॉल के लिए प्लैटफ़ॉर्म-इंडिपेंडेंट इंटरफ़ेस का इस्तेमाल किया जाता है.
  5. यूज़र इंटरफ़ेस (यूआई) बैकएंड: इसका इस्तेमाल, कॉम्बो बॉक्स और विंडो जैसे बेसिक विजेट बनाने के लिए किया जाता है. यह बैकएंड एक सामान्य इंटरफ़ेस दिखाता है, जो प्लैटफ़ॉर्म के हिसाब से नहीं है. इसके नीचे, ऑपरेटिंग सिस्टम के यूज़र इंटरफ़ेस के तरीकों का इस्तेमाल किया गया है.
  6. JavaScript की मदद से अनुवाद करने वाला टूल. इसका इस्तेमाल JavaScript कोड को पार्स और चलाने के लिए किया जाता है.
  7. डेटा स्टोरेज. यह एक परसिस्टेंस लेयर है. ब्राउज़र को कुकी जैसा सभी तरह का डेटा स्थानीय तौर पर सेव करने की ज़रूरत पड़ सकती है. ब्राउज़र, localStorage, IndexedDB, WebSQL, और FileSystem जैसे स्टोरेज सिस्टम के साथ भी काम करते हैं.
ब्राउज़र कॉम्पोनेंट
पहली इमेज: ब्राउज़र के कॉम्पोनेंट

इस बात पर ध्यान देना ज़रूरी है कि Chrome जैसे ब्राउज़र, रेंडरिंग इंजन के एक से ज़्यादा इंस्टेंस चलाते हैं: हर टैब के लिए एक. हर टैब अलग प्रोसेस में चलता है.

रेंडरिंग इंजन

रेंडरिंग इंजन की ज़िम्मेदारी अच्छी तरह से है... रेंडरिंग, जिसका मतलब है कि अनुरोध किए गए कॉन्टेंट को ब्राउज़र की स्क्रीन पर दिखाना.

डिफ़ॉल्ट रूप से रेंडरिंग इंजन, एचटीएमएल और एक्सएमएल दस्तावेज़ और इमेज दिखा सकता है. यह प्लग-इन या एक्सटेंशन के ज़रिए दूसरी तरह का डेटा दिखा सकता है; उदाहरण के लिए, PDF व्यूअर प्लग-इन का इस्तेमाल करके PDF दस्तावेज़ दिखाना. हालांकि, इस चैप्टर में हम इस्तेमाल के मुख्य उदाहरण पर फ़ोकस करेंगे: सीएसएस का इस्तेमाल करके फ़ॉर्मैट किए गए एचटीएमएल और इमेज दिखाना.

अलग-अलग ब्राउज़र अलग-अलग रेंडरिंग इंजन का इस्तेमाल करते हैं: Internet Explorer में ट्राईडेंट का इस्तेमाल होता है, Firefox Gecko का इस्तेमाल करता है, Safari WebKit का इस्तेमाल करता है. Chrome और Opera (वर्शन 15 से) Blink का उपयोग करते हैं, जो WebKit का एक फ़ोर्क है.

WebKit एक ओपन सोर्स रेंडरिंग इंजन है, जो Linux प्लैटफ़ॉर्म के इंजन के तौर पर शुरू हुआ था. इसे Apple ने Mac और Windows के साथ काम करने के लिए बदला था.

मेन फ़्लो

रेंडरिंग इंजन को नेटवर्किंग लेयर से, अनुरोध किए गए दस्तावेज़ का कॉन्टेंट मिलने लगेगा. आम तौर पर, यह प्रक्रिया 8 केबी हिस्सों में की जाती है.

इसके बाद, यह रेंडरिंग इंजन का बुनियादी फ़्लो है:

इंजन का बेसिक फ़्लो रेंडर हो रहा है
इमेज 2: इंजन का बेसिक फ़्लो रेंडर करना

रेंडरिंग इंजन, एचटीएमएल दस्तावेज़ को पार्स करना शुरू कर देगा और एलिमेंट को "कॉन्टेंट ट्री" नाम के ट्री के DOM नोड में बदल देगा. इंजन, बाहरी सीएसएस फ़ाइलों और स्टाइल एलिमेंट, दोनों में स्टाइल डेटा को पार्स करेगा. एचटीएमएल में विज़ुअल निर्देशों के साथ जानकारी को स्टाइल करने पर, एक और ट्री बनाया जाएगा: रेंडर ट्री.

रेंडर ट्री में, ऐसे रेक्टैंगल होते हैं जिनमें विज़ुअल एट्रिब्यूट, जैसे कि रंग और डाइमेंशन होते हैं. स्क्रीन पर दिखाए जाने के लिए, रेक्टैंगल सही क्रम में हैं.

रेंडर ट्री बनाने के बाद, उसे "लेआउट" प्रोसेस से गुज़रना होता है. इसका मतलब है कि हर नोड को सटीक निर्देशांक दिया जाना चाहिए, जहां उसे स्क्रीन पर दिखना चाहिए. अगला चरण पेंटिंग है - रेंडर ट्री को ट्रेस किया जाएगा और हर नोड को यूज़र इंटरफ़ेस (यूआई) बैकएंड लेयर का इस्तेमाल करके पेंट किया जाएगा.

यह समझना ज़रूरी है कि यह प्रोसेस धीरे-धीरे होती है. बेहतर उपयोगकर्ता अनुभव के लिए, रेंडरिंग इंजन, स्क्रीन पर मौजूद कॉन्टेंट को जल्द से जल्द दिखाने की कोशिश करेगा. रेंडर ट्री बनाना और उसका लेआउट बनाना शुरू करने से पहले, यह तब तक इंतज़ार नहीं करेगा, जब तक कि सभी एचटीएमएल पार्स नहीं हो जाते. कॉन्टेंट के कुछ हिस्सों को पार्स करके दिखाया जाएगा. हालांकि, नेटवर्क से लगातार आने वाले कॉन्टेंट के लिए यह प्रोसेस जारी रहेगी.

मुख्य फ़्लो के उदाहरण

WebKit का मुख्य फ़्लो.
इमेज 3: WebKit का मुख्य फ़्लो
Mozilla के Gecko रेंडरिंग इंजन का मुख्य फ़्लो.
इमेज 4: Mozilla का Gecko रेंडरिंग इंजन का मुख्य फ़्लो

इमेज 3 और 4 से यह देखा जा सकता है कि भले ही WebKit और Gecko में थोड़ी अलग शब्दावली का इस्तेमाल किया गया है, लेकिन फ़्लो मूल रूप से एक जैसा है.

Gecko, विज़ुअल तौर पर फ़ॉर्मैट किए गए एलिमेंट के ट्री को "फ़्रेम ट्री" कहता है. हर एलिमेंट एक फ़्रेम होता है. WebKit "रेंडर ट्री" शब्द का इस्तेमाल करता है और इसमें "रेंडर ऑब्जेक्ट" शामिल हैं. WebKit, एलिमेंट को जोड़ने के लिए "लेआउट" इस्तेमाल करता है, जबकि गैको इसे "रीफ़्लो" कहते हैं. "अटैचमेंट", WebKit का इस्तेमाल है. इसमें रेंडर ट्री बनाने के लिए, डीओएम नोड और विज़ुअल जानकारी को कनेक्ट किया जाता है. मामूली अंतर यह है कि Gecko में एचटीएमएल और डीओएम ट्री के बीच एक अतिरिक्त लेयर होती है. इसे "कॉन्टेंट सिंक" कहा जाता है. साथ ही, यह डीओएम एलिमेंट बनाने की फ़ैक्ट्री है. हम फ़्लो के हर हिस्से पर बात करेंगे:

पार्स करना - सामान्य

रेंडरिंग इंजन में पार्स करने की एक बहुत ही अहम प्रोसेस होती है. इसलिए, हम इस पर थोड़ा और गहराई से बात करेंगे. आइए पार्स करने के बारे में थोड़ी जानकारी से शुरुआत करते हैं.

किसी दस्तावेज़ को पार्स करने का मतलब है उसे ऐसे स्ट्रक्चर में अनुवाद करना जिसका इस्तेमाल कोड कर सके. पार्स करने का नतीजा आम तौर पर नोड का एक ट्री होता है, जो दस्तावेज़ का स्ट्रक्चर दिखाता है. इसे पार्स ट्री या सिंटैक्स ट्री कहा जाता है.

उदाहरण के लिए, एक्सप्रेशन 2 + 3 - 1 को पार्स करने पर, यह ट्री दिख सकता है:

मैथमैटिकल एक्सप्रेशन ट्री नोड.
इमेज 5: मैथमैटिकल एक्सप्रेशन ट्री नोड

Grammar

पार्स करने की प्रोसेस, उस सिंटैक्स नियमों के आधार पर होती है जिसका पालन दस्तावेज़ करता है. जैसे, वह भाषा या फ़ॉर्मैट जिसमें इसे लिखा गया था. पार्स किए जा सकने वाले हर फ़ॉर्मैट में शब्दावली और सिंटैक्स के नियमों वाला तय व्याकरण होना चाहिए. इसे कॉन्टेक्स्ट फ़्री व्याकरण कहा जाता है. इंसानों की भाषाएं ऐसी नहीं हैं, इसलिए इन्हें पारंपरिक पार्स करने की तकनीकों के ज़रिए पार्स नहीं किया जा सकता.

पार्सर - लेक्सर कॉम्बिनेशन

पार्स करने की प्रोसेस को दो सब प्रोसेस में बांटा जा सकता है: लेक्सिकल विश्लेषण और सिंटैक्स विश्लेषण.

लेक्सिकल विश्लेषण, इनपुट को टोकन में बांटने की प्रोसेस है. टोकन, भाषा की शब्दावली है: मान्य बिल्डिंग ब्लॉक का कलेक्शन. मानव भाषा में, इसमें उस भाषा के शब्दकोश में मिलने वाले सभी शब्द शामिल होंगे.

भाषा के सिंटैक्स के नियमों को लागू करने को सिंटैक्स का विश्लेषण कहते हैं.

आम तौर पर, पार्सर दो कॉम्पोनेंट में काम को बांटते हैं: lexer (इसे टोकनाइज़र भी कहा जाता है) जो इनपुट को मान्य टोकन में तोड़ने के लिए ज़िम्मेदार होता है. वहीं, पार्स, जो लैंग्वेज सिंटैक्स नियमों के हिसाब से दस्तावेज़ के स्ट्रक्चर का विश्लेषण करके, पार्स ट्री बनाने के लिए ज़िम्मेदार है.

lexer को पता है कि ग़ैर-ज़रूरी वर्णों को कैसे हटाया जाए, जैसे कि खाली जगहें और लाइन ब्रेक.

सोर्स दस्तावेज़ से पार्स ट्री तक
इमेज 6: सोर्स दस्तावेज़ से लेकर ट्री पार्स करने तक

पार्स करने की प्रोसेस बार-बार होती है. आम तौर पर, पार्सर किसी नए टोकन के लिए लेक्सर से अनुरोध करता है. साथ ही, टोकन को सिंटैक्स के किसी एक नियम से मैच कराने की कोशिश करता है. अगर कोई नियम मेल खाता है, तो टोकन से जुड़ा कोई नोड पार्स ट्री में जोड़ दिया जाएगा और पार्सर, दूसरे टोकन की मांग करेगा.

अगर कोई नियम मेल नहीं खाता, तो पार्सर टोकन को अंदरूनी तौर पर सेव करेगा और तब तक टोकन मांगता रहेगा, जब तक कि अंदरूनी तौर पर सेव किए गए सभी टोकन से मेल खाने वाला नियम नहीं मिलता. अगर कोई नियम नहीं मिलता है, तो पार्सर एक अपवाद दिखाएगा. इसका मतलब है कि दस्तावेज़ मान्य नहीं था और उसमें सिंटैक्स की गड़बड़ियां थीं.

अनुवाद

कई मामलों में, पार्स ट्री फ़ाइनल प्रॉडक्ट नहीं होता है. अनुवाद के लिए, पार्स करने की सुविधा का इस्तेमाल अक्सर किया जाता है: इनपुट दस्तावेज़ को किसी दूसरे फ़ॉर्मैट में बदलना. उदाहरण के लिए, कंपाइलेशन. सोर्स कोड को मशीन कोड में कंपाइल करने वाला कंपाइलर, पहले इसे पार्स ट्री में पार्स करता है और फिर ट्री को मशीन कोड दस्तावेज़ में बदल देता है.

कंपाइलेशन फ़्लो
सातवीं इमेज: कंपाइलेशन फ़्लो

पार्स करने का उदाहरण

इमेज 5 में हमने गणित के व्यंजक का इस्तेमाल करके एक पार्स ट्री बनाया है. आइए, गणित की एक आसान भाषा तय करने और पार्स करने की प्रक्रिया देखते हैं.

सिंटैक्स:

  1. लैंग्वेज सिंटैक्स बिल्डिंग ब्लॉक में एक्सप्रेशन, टर्म, और ऑपरेशन शामिल होते हैं.
  2. हमारी भाषा में कितने भी एक्सप्रेशन मौजूद हो सकते हैं.
  3. एक्सप्रेशन को एक "टर्म" के तौर पर परिभाषित किया जाता है. इसके बाद "ऑपरेशन" और दूसरा शब्द होता है
  4. ऑपरेशन एक प्लस टोकन या माइनस टोकन होता है
  5. शब्द एक पूर्णांक टोकन या एक्सप्रेशन होता है

आइए, 2 + 3 - 1 इनपुट का विश्लेषण करते हैं.

किसी नियम से मेल खाने वाली पहली सबस्ट्रिंग 2 है: नियम #5 के मुताबिक यह एक शब्द है. दूसरा मैच 2 + 3 है: यह तीसरे नियम से मेल खाता है: कोई शब्द, जिसके बाद कोई कार्रवाई होती है और उसके बाद कोई दूसरा शब्द. अगला मैच सिर्फ़ इनपुट के आखिर में हिट होगा. 2 + 3 - 1 एक एक्सप्रेशन है, क्योंकि हम पहले से ही जानते हैं कि 2 + 3 एक शब्द है. इसलिए, हमारा टर्म है और उसके बाद कोई दूसरा टर्म है. 2 + + किसी भी नियम से मैच नहीं करेगा. इसलिए, यह इनपुट अमान्य है.

शब्दावली और वाक्य की बनावट के लिए औपचारिक परिभाषाएं

शब्द ज्ञान को आम तौर पर रेगुलर एक्सप्रेशन से व्यक्त किया जाता है.

उदाहरण के लिए, हमारी भाषा इस तरह परिभाषित की जाएगी:

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

जैसा कि आपको दिख रहा है, पूर्णांकों को रेगुलर एक्सप्रेशन से तय किया जाता है.

आम तौर पर, सिंटैक्स को BNF फ़ॉर्मैट में दिखाया जाता है. हमारी भाषा इस तरह परिभाषित की जाएगी:

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

हमने कहा कि किसी भाषा को सामान्य पार्सर तब ही पार्स कर सकते हैं, जब उसका व्याकरण कॉन्टेक्स्ट फ़्री व्याकरण हो. कॉन्टेक्स्ट फ़्री व्याकरण की सहज परिभाषा एक ऐसा व्याकरण है जिसे पूरी तरह से बीएनएफ़ में ज़ाहिर किया जा सकता है. औपचारिक परिभाषा के लिए, कॉन्टेक्स्ट के बिना व्याकरण के बारे में Wikipedia का लेख देखें

पार्सर के टाइप

पार्सर दो तरह के होते हैं: टॉप डाउन पार्सर और बॉटम अप पार्सर. इसकी आसान जानकारी यह है कि टॉप डाउन पार्सर, सिंटैक्स के हाई लेवल स्ट्रक्चर की जांच करते हैं और किसी नियम का मिलान ढूंढने की कोशिश करते हैं. बॉटम अप पार्सर, इनपुट से शुरू होता है और धीरे-धीरे इसे सिंटैक्स के नियमों में बदलता है. इसमें निचले लेवल के नियमों से लेकर हाई लेवल के नियम पूरे होने तक, ये शामिल होते हैं.

आइए, देखते हैं कि दोनों तरह के पार्सर, हमारे उदाहरण को कैसे पार्स करेंगे.

टॉप डाउन पार्सर, ऊंचे लेवल के नियम से शुरू होगा: यह 2 + 3 की पहचान एक्सप्रेशन के तौर पर करेगा. इसके बाद यह 2 + 3 - 1 की पहचान एक्सप्रेशन के तौर पर करेगा (एक्सप्रेशन की पहचान करने की प्रोसेस आगे बढ़ती है और अन्य नियमों से मैच होती है, लेकिन शुरुआत की जगह सबसे ऊंचे लेवल का नियम है).

बॉटम अप पार्सर, इनपुट को तब तक स्कैन करेगा, जब तक कोई नियम मेल नहीं खाता. इसके बाद, यह मैच करने वाले इनपुट को नियम से बदल देगा. यह कार्रवाई, इनपुट के आखिर तक जारी रहेगी. आंशिक रूप से मैच होने वाला एक्सप्रेशन, पार्सर के स्टैक पर रखा जाता है.

स्टैक इनपुट
2 + 3 - 1
term + 3 से 1
कीवर्ड ऑपरेशन 3 से 1
एक्सप्रेशन - 1
एक्सप्रेशन ऑपरेशन 1
एक्सप्रेशन -

इस तरह के बॉटम अप पार्सर को शिफ़्ट-रिड्यूस पार्सर कहा जाता है. ऐसा इसलिए होता है, क्योंकि इनपुट को दाईं ओर शिफ़्ट किया जाता है (इनपुट के शुरू होने और दाईं ओर जाने वाले पॉइंटर की कल्पना करें) और उसे धीरे-धीरे सिंटैक्स नियमों के हिसाब से कम किया जाता है.

अपने-आप पार्सर जनरेट हो रहे हैं

ऐसे टूल उपलब्ध हैं जिनसे पार्सर जनरेट किया जा सकता है. आप उन्हें अपनी भाषा का व्याकरण फ़ीड करें - उसकी शब्दावली और वाक्य रचना के नियम - और वे एक काम कर रहा पार्सर तैयार करते हैं. पार्सर बनाने के लिए, पार्स करने की अच्छी समझ होनी ज़रूरी है. साथ ही, मैन्युअल तरीके से ऑप्टिमाइज़ किया गया पार्सर बनाना आसान नहीं है. इसलिए, पार्सर बनाने वाले टूल बहुत मददगार साबित हो सकते हैं.

WebKit दो जाने-माने पार्सर जनरेटर का इस्तेमाल करता है: Flex lexer बनाने के लिए और बाइसन पार्सर बनाने के लिए (हो सकता है कि आप इनमें Lex और Yacc नाम का इस्तेमाल करते हों). फ़्लेक्स इनपुट एक ऐसी फ़ाइल है जिसमें टोकन की रेगुलर एक्सप्रेशन परिभाषाएं होती हैं. बायसन का इनपुट, बीएनएफ़ फ़ॉर्मैट में भाषा के सिंटैक्स के नियम है.

एचटीएमएल पार्सर

एचटीएमएल पार्सर का काम, एचटीएमएल मार्कअप को पार्स ट्री में पार्स करना है.

एचटीएमएल व्याकरण

HTML की शब्दावली और सिंटैक्स, W3C संगठन के बनाए निर्देशों के हिसाब से तय किए गए हैं.

जैसा कि हमने पार्स करने के परिचय में देखा है, बीएनएफ़ जैसे फ़ॉर्मैट का इस्तेमाल करके व्याकरण सिंटैक्स को औपचारिक रूप से तय किया जा सकता है.

माफ़ करें, सभी पारंपरिक पार्सर विषय एचटीएमएल पर लागू नहीं होते हैं (मैंने उन्हें सिर्फ़ मनोरंजन के मकसद से नहीं लाया है - उनका इस्तेमाल सीएसएस और JavaScript को पार्स करने में किया जाएगा). एचटीएमएल को ऐसे कॉन्टेक्स्ट फ़्री व्याकरण से आसानी से तय नहीं किया जा सकता जिसमें पार्सर की ज़रूरत होती है.

एचटीएमएल - DTD (दस्तावेज़ टाइप की परिभाषा) को तय करने के लिए एक औपचारिक फ़ॉर्मैट है - लेकिन यह कॉन्टेक्स्ट-फ़्री व्याकरण नहीं है.

पहली नज़र में यह अजीब लगता है; एचटीएमएल, एक्सएमएल के काफ़ी करीब है. कई एक्सएमएल पार्सर उपलब्ध हैं. एचटीएमएल - एक्सएचटीएमएल का एक्सएमएल वैरिएशन है - इसलिए इन दोनों में बड़ा अंतर क्या है?

इसमें अंतर यह है कि एचटीएमएल का अप्रोच "माफ़ करने वाला" है. इससे, कुछ टैग को हटाया जा सकता है (जो बाद में इंप्लिसिट तरीके से जोड़े जाते हैं) या कभी-कभी शुरुआती या आखिरी टैग वगैरह को छोड़ देते हैं. कुल मिलाकर, यह एक "सॉफ़्ट" सिंटैक्स है, जो एक्सएमएल की कठोर और मांग वाले सिंटैक्स के उलट है.

दिखने में इस छोटी सी बारीकियों से, पर्यावरण पर सबसे ज़्यादा असर पड़ता है. एचटीएमएल के बहुत लोकप्रिय होने की यह मुख्य वजह है: यह आपकी गलतियों को माफ़ कर देता है और वेब लेखक के लिए ज़िंदगी आसान बनाता है. वहीं दूसरी ओर, औपचारिक व्याकरण लिखना मुश्किल हो जाता है. इसलिए संक्षेप में, HTML को पारंपरिक पार्सर से आसानी से पार्स नहीं किया जा सकता, क्योंकि इसका व्याकरण संदर्भ-रहित नहीं है. एचटीएमएल को एक्सएमएल पार्सर से पार्स नहीं किया जा सकता.

एचटीएमएल डीटीडी

एचटीएमएल की परिभाषा, DTD फ़ॉर्मैट में होती है. इस फ़ॉर्मैट का इस्तेमाल, SGML फ़ैमिली की भाषा तय करने के लिए किया जाता है. फ़ॉर्मैट में सभी स्वीकार किए गए एलिमेंट, उनके एट्रिब्यूट, और हैरारकी की परिभाषाएं शामिल होती हैं. जैसा कि हमने पहले देखा, एचटीएमएल DTD एक कॉन्टेक्स्ट फ़्री व्याकरण नहीं है.

DTD में कुछ वैरिएशन हैं. स्ट्रिक्ट मोड पूरी तरह से निर्देशों के मुताबिक होता है, लेकिन अन्य मोड में उन मार्कअप के साथ काम करता है जिनका इस्तेमाल पहले ब्राउज़र किया जा रहा था. इसका मकसद पुराने कॉन्टेंट का इस्तेमाल करना है. मौजूदा सख्त DTD यहां है: www.w3.org/TR/html4/strict.dtd

डीओएम

आउटपुट ट्री ("पार्स ट्री") डीओएम एलिमेंट और एट्रिब्यूट नोड का ट्री है. DOM, डॉक्यूमेंट ऑब्जेक्ट मॉडल के लिए छोटा है. यह JavaScript जैसी बाहरी दुनिया के एचटीएमएल दस्तावेज़ और एचटीएमएल एलिमेंट का इंटरफ़ेस होता है.

ट्री का रूट "दस्तावेज़" ऑब्जेक्ट है.

DOM में मार्कअप के साथ करीब-करीब वन-टू-वन संबंध होता है. उदाहरण के लिए:

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

इस मार्कअप का अनुवाद, नीचे दिए गए डीओएम ट्री में किया जाएगा:

मार्कअप के उदाहरण का डीओएम ट्री
इमेज 8: उदाहरण मार्कअप का DOM ट्री

एचटीएमएल की तरह, DOM को W3C संगठन तय करता है. www.w3.org/DOM/DOMTR देखें. यह दस्तावेज़ों में हेर-फेर करने के लिए एक सामान्य स्पेसिफ़िकेशन है. कोई खास मॉड्यूल, एचटीएमएल से जुड़े एलिमेंट के बारे में बताता है. एचटीएमएल की परिभाषाएं यहां देखी जा सकती हैं: www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.

जब मैं कहता हूं कि ट्री में DOM नोड हैं, तो मेरा मतलब है कि ट्री उन एलिमेंट से बना है जो किसी एक DOM इंटरफ़ेस को लागू करते हैं. ब्राउज़र, ऐसे कंक्रीट इंप्लीमेंटेशन का इस्तेमाल करते हैं जिनमें ब्राउज़र, अंदरूनी तौर पर दूसरे एट्रिब्यूट का इस्तेमाल करता है.

पार्स करने का एल्गोरिदम

जैसा कि हमने पिछले सेक्शन में देखा है, एचटीएमएल को टॉप डाउन या बॉटम अप पार्सर का इस्तेमाल करके पार्स नहीं किया जा सकता.

इसकी वजहें यहां दी गई हैं:

  1. भाषा का प्रतिकूल स्वभाव.
  2. यह तथ्य कि अमान्य एचटीएमएल के जाने-माने मामलों के साथ काम करने के मामले में, ब्राउज़र में किसी भी तरह की गड़बड़ी होने की पारंपरिक आशंका होती है.
  3. पार्स करने की प्रोसेस फिर से भेजी गई है. दूसरी भाषाओं के लिए, सोर्स पार्स करने के दौरान नहीं बदलता, लेकिन एचटीएमएल में डाइनैमिक कोड (जैसे, document.write() कॉल वाले स्क्रिप्ट एलिमेंट) अतिरिक्त टोकन जोड़ सकता है. इसलिए, पार्स करने की प्रोसेस असल में इनपुट में बदलाव करती है.

सामान्य पार्स करने की तकनीकों का इस्तेमाल नहीं किया जा सका, इसलिए ब्राउज़र, एचटीएमएल को पार्स करने के लिए कस्टम पार्सर बनाते हैं.

पार्सिंग एल्गोरिदम के बारे में HTML5 की खास बातों में पूरी जानकारी दी गई है. इस एल्गोरिदम में दो चरण होते हैं: टोकनाइज़ेशन और ट्री बनाना.

टोकनाइज़ेशन, इनपुट को टोकन में पार्स करने वाला लेक्सिकल विश्लेषण है. एचटीएमएल टोकन में, शुरुआती टैग, आखिरी टैग, एट्रिब्यूट के नाम, और एट्रिब्यूट की वैल्यू शामिल होती हैं.

टोकनाइज़र, टोकन की पहचान करता है और उसे ट्री कंस्ट्रक्टर को देता है. साथ ही, अगले टोकन की पहचान करने के लिए, अगले वर्ण का इस्तेमाल करता है. इसी तरह, इनपुट के आखिर तक अगले वर्ण का इस्तेमाल भी करता है.

एचटीएमएल पार्स करने का फ़्लो (HTML5 स्पेसिफ़िकेशन से लिया गया)
इमेज 9: एचटीएमएल पार्स करने का फ़्लो (HTML5 स्पेसिफ़िकेशन से लिया गया)

टोकनाइज़ेशन एल्गोरिदम

एल्गोरिदम का आउटपुट, एचटीएमएल टोकन होता है. एल्गोरिदम को स्टेट मशीन के तौर पर दिखाया जाता है. हर राज्य, इनपुट स्ट्रीम के एक या उससे ज़्यादा वर्णों का इस्तेमाल करता है और उन वर्णों के हिसाब से अगली स्थिति अपडेट करता है. टोकनाइज़ेशन की मौजूदा स्थिति और पेड़ बनाने की स्थिति से, इस फ़ैसले पर असर पड़ता है. इसका मतलब है कि इस्तेमाल किया गया एक ही वर्ण, मौजूदा स्थिति के आधार पर सही अगली स्थिति के लिए अलग-अलग नतीजे देगा. एल्गोरिदम बहुत जटिल है. इसलिए, उसके बारे में पूरी जानकारी नहीं दी जा सकती. आइए, हम एक आसान उदाहरण देखते हैं, जिससे हमें सिद्धांत को समझने में मदद मिल सकती है.

सामान्य उदाहरण - नीचे दिए गए एचटीएमएल को टोकन बनाना:

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

शुरुआती स्थिति "डेटा की स्थिति" होती है. < वर्ण मिलने पर, स्टेटस बदलकर "टैग खुला है" हो जाता है. a-z वर्ण का इस्तेमाल करने से "स्टार्ट टैग टोकन" बनता है. इसका स्टेटस बदलकर, "टैग के नाम की स्थिति" हो जाता है. हम इस स्थिति में तब तक बने रहते हैं, जब तक > वर्ण का इस्तेमाल नहीं हो जाता. हर वर्ण को नए टोकन के नाम के साथ जोड़ दिया जाता है. हमारे मामले में, बनाया गया टोकन html टोकन है.

जब > टैग मिल जाता है, तो मौजूदा टोकन जनरेट होता है और इसका स्टेटस वापस "डेटा स्टेट" में बदल जाता है. <body> टैग के लिए भी यही तरीका अपनाया जाएगा. अब तक html और body टैग उत्सर्जित किए गए थे. अब हम "डेटा की स्थिति" पर वापस आ गए हैं. Hello world के H वर्ण का इस्तेमाल करने से, कोई वर्ण टोकन बनाने और उसे छोड़ने पर, यह तब तक जारी रहता है, जब तक </body> के < तक नहीं पहुंचा जाता. हम Hello world के हर वर्ण के लिए एक कैरेक्टर टोकन छोड़ेंगे.

अब हम "टैग खुला है" पर वापस आ गए हैं. अगले इनपुट / का इस्तेमाल करने पर, end tag token बन जाएगा और "टैग के नाम की स्थिति" पर चला जाएगा. फिर से हम इस स्थिति में तब तक बने रहते हैं, जब तक हम > तक नहीं पहुंच जाते.इसके बाद, नया टैग टोकन भेजा जाएगा और हम वापस "डेटा की स्थिति" पर वापस चले जाएंगे. </html> इनपुट का इस्तेमाल, पिछले केस की तरह किया जाएगा.

उदाहरण के तौर पर दिए गए इनपुट को टोकन बनाना
इमेज 10: उदाहरण के इनपुट को टोकन करना

पेड़ बनाने का एल्गोरिदम

पार्सर बनाए जाने पर दस्तावेज़ ऑब्जेक्ट बन जाता है. ट्री बनाने के दौरान, उस DOM ट्री में बदलाव किया जाएगा जिसके रूट में दस्तावेज़ मौजूद होगा. साथ ही, उसमें एलिमेंट जोड़ दिए जाएंगे. टोकनाइज़र से उत्सर्जित होने वाला हर नोड, ट्री कंस्ट्रक्टर के ज़रिए प्रोसेस किया जाएगा. हर टोकन के लिए स्पेसिफ़िकेशन यह तय करता है कि कौनसा DOM एलिमेंट इसके काम का है और इस टोकन के लिए बनाया जाएगा. एलिमेंट को डीओएम ट्री के साथ-साथ ओपन एलिमेंट के स्टैक में भी जोड़ा जाता है. इस स्टैक का इस्तेमाल, नेस्टिंग के मेल न खाने और जो टैग बंद नहीं किए हैं उन्हें ठीक करने के लिए किया जाता है. एल्गोरिदम को एक स्टेट मशीन भी बताया गया है. इन स्टेट को "इंसर्शन मोड" कहा जाता है.

आइए, उदाहरण के तौर पर दिए गए इनपुट के लिए, ट्री बनाने की प्रोसेस देखते हैं:

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

ट्री बनाने के चरण का इनपुट, टोकनाइज़ेशन स्टेज से मिलने वाले टोकन का एक क्रम होता है. पहला मोड "शुरुआती मोड" है. "html" टोकन मिलने पर, उसे "html से पहले" मोड में ले जाया जाएगा और उस मोड में टोकन को फिर से प्रोसेस किया जाएगा. इससे HTMLएचटीएमएल एलिमेंट बन जाएगा, जिसे रूट दस्तावेज़ ऑब्जेक्ट में जोड़ा जाएगा.

राज्य का नाम बदलकर, "head" हो जाएगा. इसके बाद, "body" टोकन मिलता है. हमारे पास "head" टोकन नहीं है और इसे ट्री में जोड़ दिया जाएगा. इसके बावजूद, HTMLहेड एलिमेंट को इंप्लिसिट तरीके से बनाया जाएगा.

अब हम "इन हेड" मोड में और फिर "बाद में" मोड में हैं. बॉडी टोकन को फिर से प्रोसेस किया जाता है. साथ ही, एक HTMLBodyElement बनाया जाता है और उसे डाला जाता है. साथ ही, मोड को "in body" में ट्रांसफ़र किया जाता है.

"नमस्ते दुनिया" स्ट्रिंग के वर्ण टोकन अब मिल गए हैं. पहले कोड से "टेक्स्ट" नोड बनाया और शामिल किया जाएगा और बाकी वर्ण उस नोड में जोड़ दिए जाएंगे.

बॉडी एंड टोकन मिलने से "बाद के बॉडी" मोड में ट्रांसफ़र हो जाएगा. अब हमें एचटीएमएल एंड टैग मिलेगा, जो हमें "Body के बाद" मोड में ले जाएगा. फ़ाइल के आखिर में टोकन मिलने पर, पार्स करने की प्रोसेस खत्म हो जाएगी.

एचटीएमएल के उदाहरण का ट्री बनाना.
इमेज 11: एचटीएमएल के उदाहरण का पेड़ बनाना

पार्स करने के पूरा होने पर की जाने वाली कार्रवाइयां

इस चरण में ब्राउज़र, दस्तावेज़ को इंटरैक्टिव के तौर पर मार्क करेगा और उन स्क्रिप्ट को पार्स करना शुरू कर देगा जो "स्थगित" मोड में हैं: वे स्क्रिप्ट जिन्हें दस्तावेज़ पार्स होने के बाद एक्ज़ीक्यूट किया जाना चाहिए. इसके बाद, दस्तावेज़ की स्थिति "पूरा हो गया" पर सेट हो जाएगी और "लोड" इवेंट सक्रिय हो जाएगा.

HTML5 की खास बातों में, टोकनाइज़ेशन और ट्री बनाने के लिए पूरे एल्गोरिदम देखे जा सकते हैं.

ब्राउज़र की गड़बड़ी सहिष्णुता

आपको किसी एचटीएमएल पेज पर "अमान्य सिंटैक्स" गड़बड़ी कभी नहीं मिलती. ब्राउज़र किसी भी अमान्य कॉन्टेंट को ठीक करते हैं और आगे बढ़ जाते हैं.

उदाहरण के लिए, इस एचटीएमएल को देखें:

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

मैंने करीब दस लाख नियमों का उल्लंघन किया होगा ("mytag" कोई स्टैंडर्ड टैग नहीं है, "p" और "div" एलिमेंट की गलत नेस्टिंग वगैरह) लेकिन ब्राउज़र अब भी इसे सही तरीके से दिखाता है और शिकायत नहीं करता. इसलिए, कई पार्सर कोड से एचटीएमएल लेखक की गलतियां ठीक की जा रही हैं.

ब्राउज़र में गड़बड़ियों को ठीक करना काफ़ी हद तक एक जैसा है, लेकिन यह एचटीएमएल की खास जानकारी का हिस्सा नहीं है. बुकमार्किंग और बैक/फ़ॉरवर्ड बटन की तरह ही, यह कुछ ऐसा है जो पिछले कई सालों में ब्राउज़र में डेवलप किया गया है. कई साइटों पर पहचाने गए अमान्य एचटीएमएल कंस्ट्रक्शन दोहराए जाते हैं और ब्राउज़र उन्हें अन्य ब्राउज़र के मुताबिक सही तरीके से ठीक करने की कोशिश करते हैं.

इनमें से कुछ ज़रूरी शर्तें, HTML5 की खास बातों में तय की गई हैं. (WebKit इसे एचटीएमएल पार्सर क्लास की शुरुआत में टिप्पणी में अच्छी तरह से बताता है.)

पार्सर, दस्तावेज़ में टोकन के तौर पर मौजूद इनपुट को पार्स करता है, जिससे दस्तावेज़ की संरचना बनती है. अगर दस्तावेज़ सही से बनाया गया है, तो इसे आसानी से पार्स किया जा सकता है.

माफ़ करें, हमें कई ऐसे एचटीएमएल दस्तावेज़ों को हैंडल करना पड़ता है जो सही तरीके से नहीं बने हैं. इसलिए, पार्सर को गड़बड़ियों का सामना करना पड़ सकता है.

हमें कम से कम नीचे दी गई गड़बड़ी की स्थितियों का ध्यान रखना होगा:

  1. जोड़े जा रहे एलिमेंट को कुछ बाहरी टैग में साफ़ तौर पर अनुमति नहीं है. ऐसे मामले में, हमें उस टैग तक के सभी टैग बंद कर देने चाहिए जिसमें एलिमेंट का इस्तेमाल करने की अनुमति नहीं है और बाद में टैग जोड़ना चाहिए.
  2. हमें एलिमेंट को सीधे जोड़ने की अनुमति नहीं है. ऐसा हो सकता है कि दस्तावेज़ लिखने वाला व्यक्ति बीच में कुछ टैग भूल गया हो या उसके बीच का टैग वैकल्पिक हो. ऐसा इन टैग के मामले में भी हो सकता है: HTML head BODY TBODY TR TD LI (क्या मैंने कुछ भूला है?).
  3. हम किसी इनलाइन एलिमेंट में ब्लॉक एलिमेंट जोड़ना चाहते हैं. अगले उच्च ब्लॉक एलिमेंट तक सभी इनलाइन एलिमेंट बंद करें.
  4. अगर इससे मदद नहीं मिलती, तो एलिमेंट को तब तक बंद रखें, जब तक हम उन्हें एलिमेंट जोड़ने की अनुमति न दें या टैग को अनदेखा करें.

WebKit गड़बड़ी सहन करने की क्षमता के कुछ उदाहरण देखते हैं:

<br> के बजाय </br>

कुछ साइटें <br> के बजाय </br> का इस्तेमाल करती हैं. IE और Firefox के साथ काम करने के लिए, WebKit इसके साथ <br> की तरह व्यवहार करता है.

कोड:

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

ध्यान दें कि गड़बड़ी को ठीक करने का तरीका अंदरूनी होता है: इसे उपयोगकर्ता को नहीं दिखाया जाएगा.

एक खाली टेबल

स्ट्रेई टेबल, किसी दूसरी टेबल के अंदर मौजूद टेबल होती है. हालांकि, यह किसी टेबल सेल के अंदर नहीं होती.

उदाहरण के लिए:

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

WebKit, क्रम को दो सिबलिंग टेबल में बदल देगा:

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

कोड:

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

WebKit मौजूदा एलिमेंट कॉन्टेंट के लिए एक स्टैक का इस्तेमाल करता है: यह इनर टेबल को आउटर टेबल स्टैक से बाहर पॉप कर देगा. टेबल अब सिबलिंग होंगी.

नेस्ट किए गए फ़ॉर्म एलिमेंट

अगर उपयोगकर्ता किसी फ़ॉर्म को किसी दूसरे फ़ॉर्म में डालता है, तो दूसरे फ़ॉर्म को अनदेखा कर दिया जाता है.

कोड:

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

टैग की हैरारकी बहुत गहरी है

टिप्पणी अपनी कहानी खुद बता रही है.

bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)
{

unsigned i = 0;
for (HTMLStackElem* curr = m_blockStack;
         i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;
     curr = curr->next, i++) { }
return i != cMaxRedundantTagDepth;
}

गलत जगह पर रखे गए एचटीएमएल या मुख्य हिस्से वाले टैग

ध्यान दें - टिप्पणी अपनी कहानी खुद बताती है.

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

इसलिए वेब लेखक सावधान रहें - जब तक कि आप WebKit गड़बड़ी सहिष्णुता कोड स्निपेट में उदाहरण के रूप में प्रदर्शित नहीं होना चाहते - अच्छी तरह से बनाया गया HTML लिखें.

सीएसएस पार्स करना

क्या आपको परिचय में बताए गए पार्स करने के कॉन्सेप्ट याद हैं? एचटीएमएल से उलट, सीएसएस एक कॉन्टेक्स्ट फ़्री व्याकरण है. इसे परिचय में बताए गए अलग-अलग तरह के पार्सर का इस्तेमाल करके पार्स किया जा सकता है. असल में, सीएसएस स्पेसिफ़िकेशन में, सीएसएस लेक्सिकल और सिंटैक्स के व्याकरण के बारे में बताया गया है.

आइए कुछ उदाहरण देखते हैं:

हर टोकन के लिए, लेक्सिकल व्याकरण (शब्दावली) को रेगुलर एक्सप्रेशन से तय किया जाता है:

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", आइडेंटिफ़ायर का छोटा नाम है, जैसे कि क्लास का नाम. "नाम" एक एलिमेंट आईडी है (इसे "#" से रेफ़र किया जाता है)

सिंटैक्स व्याकरण के बारे में बीएनएफ़ में बताया गया है.

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

इसका मतलब है:

रूलसेट ऐसा स्ट्रक्चर होता है:

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

div.error और a.error सिलेक्टर हैं. कर्ली ब्रेसेस के अंदर के हिस्से में वे नियम होते हैं जो इस रूलसेट के ज़रिए लागू होते हैं. इस परिभाषा में, इस स्ट्रक्चर को औपचारिक तौर पर बताया गया है:

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

इसका मतलब है कि रूलसेट, सिलेक्टर है या विकल्प के तौर पर कई सिलेक्टर है जो कॉमा और स्पेस से अलग किए गए हैं (S का मतलब है व्हाइट स्पेस). नियमों के सेट में कर्ली ब्रैकेट होते हैं. साथ ही, उनके अंदर एक डिक्लेरेशन या वैकल्पिक तौर पर सेमीकोलन से अलग किए गए कई एलान होते हैं. "डिक्लेरेशन" और "सिलेक्टर" के बारे में बीएनएफ़ की इन परिभाषाओं में बताया जाएगा.

WebKit CSS पार्सर

WebKit, सीएसएस की व्याकरण फ़ाइलों से अपने-आप पार्सर बनाने के लिए, Flex और Bison पार्सर जनरेटर का इस्तेमाल करता है. जैसा कि आपको पार्सर के परिचय से याद है, बायसन एक बॉटम अप शिफ़्ट-रिड्यूस पार्सर बनाता है. Firefox मैन्युअल तौर पर लिखे गए टॉप डाउन पार्सर का इस्तेमाल करता है. दोनों ही मामलों में, हर सीएसएस फ़ाइल को StyleSheet ऑब्जेक्ट में पार्स किया जाता है. हर ऑब्जेक्ट में सीएसएस के नियम होते हैं. सीएसएस नियम से जुड़े ऑब्जेक्ट में सिलेक्टर और डिक्लेरेशन ऑब्जेक्ट के साथ-साथ सीएसएस व्याकरण से जुड़े अन्य ऑब्जेक्ट होते हैं.

सीएसएस को पार्स किया जा रहा है.
इमेज 12: सीएसएस को पार्स करना

स्क्रिप्ट और स्टाइल शीट के लिए प्रोसेसिंग ऑर्डर

स्क्रिप्ट

वेब का मॉडल सिंक्रोनस होता है. लेखक उम्मीद करते हैं कि जब पार्सर <script> टैग पर पहुंच जाए, तब स्क्रिप्ट को पार्स करके तुरंत लागू कर दिया जाए. दस्तावेज़ की पार्सिंग तब तक रुक जाती है, जब तक स्क्रिप्ट लागू नहीं हो जाती. अगर स्क्रिप्ट बाहरी है, तो सबसे पहले संसाधन को नेटवर्क से फ़ेच किया जाना चाहिए. यह प्रोसेस सिंक्रोनस तरीके से भी की जाती है. साथ ही, संसाधन को फ़ेच करने तक पार्स करना रुक जाता है. यह कई सालों तक काम करने वाला मॉडल था. इसकी जानकारी भी HTML4 और 5 में दी गई है. लेखक किसी स्क्रिप्ट में "defer" एट्रिब्यूट जोड़ सकते हैं, इस स्थिति में यह दस्तावेज़ पार्स करना नहीं रुकेगा और दस्तावेज़ के पार्स होने के बाद काम करेगा. HTML5 स्क्रिप्ट को एसिंक्रोनस के तौर पर मार्क करने का विकल्प जोड़ता है. इससे इसे किसी दूसरे थ्रेड से पार्स और एक्ज़ीक्यूट किया जाएगा.

अनुमान के हिसाब से पार्स करना

WebKit और Firefox दोनों ही यह ऑप्टिमाइज़ेशन करते हैं. स्क्रिप्ट को एक्ज़ीक्यूट करते समय, दूसरा थ्रेड बाकी दस्तावेज़ को पार्स करता है. साथ ही, यह पता लगाता है कि नेटवर्क से कौनसे दूसरे रिसॉर्स लोड करने की ज़रूरत है और उन्हें लोड करता है. इस तरह, समानांतर कनेक्शन पर संसाधन लोड हो सकते हैं और कुल स्पीड बेहतर हो जाती है. ध्यान दें: अनुमान वाला पार्सर, सिर्फ़ बाहरी संसाधनों के रेफ़रंस पार्स करता है, जैसे कि बाहरी स्क्रिप्ट, स्टाइल शीट, और इमेज: यह मुख्य पार्सर के लिए छोड़ दिए गए डीओएम ट्री में बदलाव नहीं करता.

स्टाइल शीट

वहीं दूसरी ओर, स्टाइल शीट में अलग मॉडल है. सैद्धान्तिक तौर पर ऐसा लगता है कि स्टाइल शीट से डीओएम ट्री नहीं बदलता, इसलिए उनके लिए इंतज़ार करने और दस्तावेज़ को पार्स करने की ज़रूरत नहीं होती. हालांकि, दस्तावेज़ पार्स करने के दौरान स्क्रिप्ट की शैली की जानकारी मांगने वाली स्क्रिप्ट में एक समस्या है. अगर स्टाइल अभी तक लोड और पार्स नहीं हुई है, तो स्क्रिप्ट को गलत जवाब मिलेंगे और इसकी वजह से कई समस्याएं हो सकती हैं. यह एक खास मामला लगता है, लेकिन आम बात है. अगर कोई स्टाइल शीट अभी लोड और पार्स हो रही है, तो Firefox सभी स्क्रिप्ट को ब्लॉक कर देता है. WebKit, स्क्रिप्ट को सिर्फ़ तब ब्लॉक करता है, जब वे कुछ स्टाइल प्रॉपर्टी को ऐक्सेस करने की कोशिश करते हैं. इन स्टाइल शीट पर, अनलोड की गई स्टाइल शीट का असर पड़ सकता है.

रेंडर होने वाले पेड़ बनाना

डीओएम ट्री बनाने के दौरान ब्राउज़र, एक दूसरा ट्री यानी रेंडर ट्री बनाता है. इस ट्री में विज़ुअल एलिमेंट उसी क्रम में दिखते हैं जिस क्रम में वे दिखते हैं. यह दस्तावेज़ को विज़ुअल के रूप में दिखाता है. इस ट्री का मकसद, कॉन्टेंट को सही क्रम में पेंट करना है.

Firefox रेंडर ट्री में एलिमेंट को "फ़्रेम" कॉल करता है. WebKit, रेंडरर या रेंडर ऑब्जेक्ट शब्द का इस्तेमाल करता है.

इमेज बनाने वाला व्यक्ति, खुद को और अपने बच्चों के लिए विज्ञापन दिखाना जानता है.

WebKit की RenderObject क्लास, रेंडरर की बेस क्लास में, नीचे दी गई परिभाषा होती है:

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
}

हर रेंडरर एक आयताकार क्षेत्र होता है, जो आम तौर पर नोड के सीएसएस बॉक्स से जुड़ा होता है, जैसा कि CSS2 स्पेसिफ़िकेशन में बताया गया है. इसमें चौड़ाई, ऊंचाई, और स्थिति जैसी ज्यामितीय जानकारी शामिल होती है.

बॉक्स टाइप पर, उस स्टाइल एट्रिब्यूट की "डिसप्ले" वैल्यू का असर पड़ता है जो नोड के हिसाब से सही है. स्टाइल कंप्यूटेशन सेक्शन देखें. यहां WebKit कोड दिया गया है, जिससे तय होता है कि डिसप्ले एट्रिब्यूट के हिसाब से, डीओएम नोड के लिए किस तरह का रेंडरर बनाया जाना चाहिए:

RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
    Document* doc = node->document();
    RenderArena* arena = doc->renderArena();
    ...
    RenderObject* o = 0;

    switch (style->display()) {
        case NONE:
            break;
        case INLINE:
            o = new (arena) RenderInline(node);
            break;
        case BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case INLINE_BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case LIST_ITEM:
            o = new (arena) RenderListItem(node);
            break;
       ...
    }

    return o;
}

एलिमेंट टाइप को भी ध्यान में रखा जाता है: उदाहरण के लिए, फ़ॉर्म कंट्रोल और टेबल में खास फ़्रेम होते हैं.

WebKit में अगर कोई एलिमेंट एक खास रेंडरर बनाना चाहता है, तो वह createRenderer() तरीके को बदल देगा. रेंडरर, उन चीज़ों की स्टाइल बताने वाले ऑब्जेक्ट की ओर इशारा करते हैं जिनमें ज्यामितीय जानकारी होती है.

रेंडर ट्री का डीओएम ट्री से संबंध

रेंडरर, डीओएम एलिमेंट से जुड़े होते हैं, लेकिन एक-दूसरे का संबंध नहीं होता. रेंडर ट्री में, बिना विज़ुअल वाले डीओएम एलिमेंट शामिल नहीं किए जाएंगे. इसका एक उदाहरण "head" एलिमेंट है. साथ ही, जिन तत्वों का प्रदर्शन मान "कोई भी नहीं" को असाइन किया गया था वे ट्री में दिखाई नहीं देंगे (जबकि "छिपी हुई" दृश्यता वाले तत्व ट्री में दिखाई देंगे).

कई विज़ुअल ऑब्जेक्ट से जुड़े डीओएम एलिमेंट होते हैं. आम तौर पर, ये जटिल स्ट्रक्चर वाले एलिमेंट होते हैं. इनके बारे में किसी एक रेक्टैंगल से नहीं बताया जा सकता. उदाहरण के लिए, "चुनें" एलिमेंट में तीन रेंडरर होते हैं: एक डिसप्ले एरिया के लिए, एक ड्रॉप डाउन सूची बॉक्स के लिए और दूसरा बटन के लिए. साथ ही, अगर टेक्स्ट को एक से ज़्यादा लाइनों में बांटा जाता है, क्योंकि एक लाइन के लिए चौड़ाई काफ़ी नहीं है, तो नई लाइनें ज़्यादा रेंडरर के तौर पर जोड़ दी जाएंगी.

काम न करने वाला एचटीएमएल कोड, एक से ज़्यादा रेंडरर का एक अन्य उदाहरण है. सीएसएस के स्पेसिफ़िकेशन के मुताबिक, किसी इनलाइन एलिमेंट में या तो सिर्फ़ ब्लॉक एलिमेंट शामिल होने चाहिए या सिर्फ़ इनलाइन एलिमेंट. अगर मिले-जुले कॉन्टेंट के मामले में, इनलाइन एलिमेंट को रैप करने के लिए, पहचान छिपाने वाले ब्लॉक रेंडरर बनाए जाएंगे.

कुछ रेंडर ऑब्जेक्ट, DOM नोड से मेल खाते हैं, लेकिन ट्री में उसी जगह पर नहीं होते. फ़्लोट और पूरी तरह से पोज़िशन किए गए एलिमेंट, फ़्लो से बाहर हैं, उन्हें पेड़ के अलग हिस्से में रखा गया है और उन्हें असली फ़्रेम पर मैप किया गया है. प्लेसहोल्डर फ़्रेम वह जगह है जहां उन्हें होना चाहिए था.

रेंडर ट्री और उससे जुड़ा डीओएम ट्री.
इमेज 13: रेंडर ट्री और उससे जुड़ा डीओएम ट्री. "व्यूपोर्ट" वह शुरुआती ब्लॉक है जिसमें शामिल है. WebKit में यह "RenderView" ऑब्जेक्ट होगा

ट्री बनाने का फ़्लो

Firefox में, प्रज़ेंटेशन को DOM अपडेट के लिए लिसनर के तौर पर रजिस्टर किया गया है. प्रज़ेंटेशन, फ़्रेम बनाने की प्रक्रिया को FrameConstructor को सौंपता है और कंस्ट्रक्टर, स्टाइल (स्टाइल कंप्यूटेशन देखें) को रिज़ॉल्व करता है और फ़्रेम बनाता है.

WebKit में स्टाइल को ठीक करने और रेंडरर बनाने की प्रोसेस को "अटैचमेंट" कहा जाता है. हर डीओएम नोड में "अटैचमेंट" का एक तरीका होता है. अटैचमेंट सिंक्रोनस है, जबकि डीओएम ट्री में नोड इंसर्शन की सुविधा नए नोड को "अटैचमेंट" करने के तरीके को कॉल करती है.

एचटीएमएल और बॉडी टैग को प्रोसेस करने से रेंडर ट्री रूट बन जाता है. रूट रेंडर ऑब्जेक्ट वही होता है जिसे सीएसएस स्पेसिफ़िकेशन, इसमें शामिल ब्लॉक को कहते हैं: सबसे ऊपर वाला वह ब्लॉक जिसमें बाकी सभी ब्लॉक शामिल होते हैं. इसके डाइमेंशन, व्यूपोर्ट हैं: ब्राउज़र विंडो के डिसप्ले एरिया के डाइमेंशन. Firefox इसे ViewPortFrame कहता है और WebKit इसे RenderView कहता है. यह वह रेंडर ऑब्जेक्ट है जिसकी ओर दस्तावेज़ ले जाता है. बाकी ट्री को डीओएम नोड इंसर्शन के तौर पर बनाया गया है.

प्रोसेसिंग मॉडल के लिए CSS2 की खास जानकारी देखें.

स्टाइल कंप्यूटेशन

रेंडर ट्री बनाने के लिए, हर रेंडर ऑब्जेक्ट की विज़ुअल प्रॉपर्टी की गिनती करनी होती है. ऐसा करने के लिए, हर एलिमेंट की स्टाइल प्रॉपर्टी का हिसाब लगाया जाता है.

स्टाइल में, एचटीएमएल में अलग-अलग ऑरिजिन की स्टाइल शीट, इनलाइन स्टाइल एलिमेंट, और विज़ुअल प्रॉपर्टी (जैसे, "bgcolor" प्रॉपर्टी) शामिल होती हैं. बाद वाले वर्शन का अनुवाद, सीएसएस स्टाइल प्रॉपर्टी के हिसाब से किया जाता है.

स्टाइल शीट के ऑरिजिन, ब्राउज़र की डिफ़ॉल्ट स्टाइल शीट, पेज के लेखक की ओर से उपलब्ध कराई गई स्टाइल शीट, और उपयोगकर्ता की स्टाइल शीट होती हैं - ये ब्राउज़र के उपयोगकर्ता की ओर से उपलब्ध कराई गई स्टाइल शीट होती हैं (ब्राउज़र की मदद से अपनी पसंदीदा स्टाइल तय की जा सकती है. उदाहरण के लिए, Firefox में ऐसा करने के लिए "Firefox Profile" फ़ोल्डर में एक स्टाइल शीट डाली जाती है.

स्टाइल कंप्यूटेशन की प्रक्रिया में कुछ समस्याएं आती हैं:

  1. स्टाइल डेटा बहुत बड़ा कंस्ट्रक्शन है, जिसमें कई स्टाइल प्रॉपर्टी होती हैं. इससे मेमोरी से जुड़ी समस्याएं हो सकती हैं.
  2. हर एलिमेंट के लिए मैच करने वाले नियम ढूंढने पर, उसे ऑप्टिमाइज़ न करने पर परफ़ॉर्मेंस से जुड़ी समस्याएं हो सकती हैं. मैच ढूंढने के लिए, हर एलिमेंट के लिए नियम की पूरी सूची को ट्रैवर्स करना मुश्किल काम है. सिलेक्टर की बनावट जटिल हो सकती है. इस वजह से, मैच करने की प्रोसेस ऐसे पाथ पर शुरू हो सकती है जो व्यावहारिक साबित होता है और किसी दूसरे पाथ पर काम करना पड़ता है.

    उदाहरण के लिए - यह कंपाउंड सिलेक्टर:

    div div div div{
    ...
    }
    

    इसका मतलब है कि नियम ऐसे <div> पर लागू होते हैं जो तीन divs का वंशज है. मान लें कि आपको यह देखना है कि दिए गए <div> एलिमेंट पर नियम लागू होता है या नहीं. आप जांच के लिए ट्री का कोई विशेष पथ चुनते हैं. यह पता लगाने के लिए कि सिर्फ़ दो डिव हैं और नियम लागू नहीं होता, आपको नोड ट्री को ऊपर करके देखना पड़ सकता है. इसके बाद आपको ट्री में अन्य पाथ आज़माने होंगे.

  3. नियमों को लागू करने के लिए बहुत जटिल कैस्केड नियम होते हैं, जो नियमों का क्रम तय करते हैं.

आइए देखते हैं कि ब्राउज़र इन समस्याओं का सामना कैसे करते हैं:

स्टाइल डेटा शेयर करना

WebKit नोड, शैली ऑब्जेक्ट (RenderStyle) को रेफ़र करता है. कुछ स्थितियों में ये ऑब्जेक्ट, नोड की मदद से शेयर किए जा सकते हैं. नोड, भाई-बहन या कज़न होते हैं और:

  1. एलिमेंट का माउस की स्थिति एक ही होना चाहिए (जैसे कि एक को :hover में नहीं किया जा सकता, जबकि दूसरा नहीं हो)
  2. किसी भी एलिमेंट का आईडी नहीं होना चाहिए
  3. टैग के नाम मेल खाने चाहिए
  4. क्लास के एट्रिब्यूट मेल खाने चाहिए
  5. मैप किए गए एट्रिब्यूट का सेट एक जैसा होना चाहिए
  6. लिंक की स्थिति मेल खानी चाहिए
  7. फ़ोकस स्टेट का मिलान होना चाहिए
  8. किसी भी एलिमेंट पर एट्रिब्यूट सिलेक्टर का असर नहीं होना चाहिए, जहां पर असर का मतलब ऐसे सिलेक्टर के मिलान के तौर पर है जो सिलेक्टर में किसी भी पोज़िशन में एट्रिब्यूट सिलेक्टर का इस्तेमाल करता है
  9. एलिमेंट पर कोई इनलाइन स्टाइल एट्रिब्यूट नहीं होना चाहिए
  10. इसमें कोई भी सिबलिंग सिलेक्टर का इस्तेमाल नहीं होना चाहिए. सिबलिंग सिलेक्टर के मिलने पर WebCore, ग्लोबल स्विच करता है. साथ ही, पूरे दस्तावेज़ के मौजूद होने पर, स्टाइल शेयर करने की सुविधा बंद कर देता है. इसमें + सिलेक्टर और सिलेक्टर भी शामिल हैं, जैसे कि :first-child और :last-child.

Firefox रूल ट्री

आसानी से स्टाइल कंप्यूटेशन के लिए, Firefox में दो अतिरिक्त ट्री होते हैं: रूल ट्री और स्टाइल कॉन्टेक्स्ट ट्री. WebKit में स्टाइल ऑब्जेक्ट भी होते हैं, लेकिन उन्हें स्टाइल कॉन्टेक्स्ट ट्री की तरह ट्री में स्टोर नहीं किया जाता है. सिर्फ़ डीओएम नोड, उससे जुड़ी स्टाइल की ओर इशारा करता है.

Firefox स्टाइल का कॉन्टेक्स्ट ट्री.
इमेज 14: फ़ायरफ़ॉक्स स्टाइल कॉन्टेक्स्ट ट्री.

स्टाइल कॉन्टेक्स्ट में आखिरी वैल्यू होती हैं. वैल्यू का हिसाब सही क्रम में मैच करने वाले सभी नियमों को लागू करके और ऐसे बदलाव करके लगाया जाता है जो उन्हें लॉजिकल से कंक्रीट वैल्यू में बदल देते हैं. उदाहरण के लिए, अगर लॉजिकल वैल्यू, स्क्रीन का कोई प्रतिशत है, तो उसे कैलकुलेट किया जाएगा और उसे ऐब्सलूट इकाइयों में बदल दिया जाएगा. रूल ट्री का आइडिया काफ़ी चालाक है. यह इन वैल्यू को नोड के बीच शेयर करने की सुविधा देता है, ताकि फिर से उनकी गणना नहीं की जा सके. इससे स्टोरेज भी बचता है.

मेल खाने वाले सभी नियम, ट्री में सेव किए जाते हैं. पाथ में नीचे के नोड की प्राथमिकता ज़्यादा होती है. ट्री में, नियम से मिलते-जुलते सभी पाथ शामिल हैं. नियमों को धीरे-धीरे स्टोर किया जाता है. हर नोड की शुरुआत में ट्री की गिनती नहीं की जाती. हालांकि, जब भी किसी नोड स्टाइल की गणना की ज़रूरत होती है, तो कंप्यूट किए गए पाथ को ट्री में जोड़ दिया जाता है.

आइडिया यह है कि ट्री पाथ को शब्दकोश में शब्दों के तौर पर देखा जाए. मान लें कि हमने पहले ही इस नियम ट्री की गणना कर ली है:

कंप्यूटेड रूल ट्री
इमेज 15: कंप्यूटेड रूल ट्री.

मान लें कि हमें कॉन्टेंट ट्री में किसी अन्य एलिमेंट के लिए नियमों का मिलान करने की ज़रूरत है और इससे पता चलता है कि मेल खाने वाले नियम (सही क्रम में) बी-ई-आई हैं. हमारे पास ट्री में यह पाथ पहले से मौजूद है, क्योंकि हम पाथ A-B-E-I-L का कंप्यूट पहले ही कर चुके हैं. अब हमारे पास करने के लिए कम काम होगा.

आइए देखते हैं कि पेड़ हमारे काम को कैसे बचाते हैं.

निर्देशों में बांटें

शैली के कॉन्टेक्स्ट को निर्देशों में बांटा गया है. इन निर्देशों में, बॉर्डर या रंग जैसी खास कैटगरी के लिए स्टाइल की जानकारी होती है. किसी स्ट्रक्चर में सभी प्रॉपर्टी इनहेरिट की जाती हैं या इनहेरिट नहीं की जाती हैं. इनहेरिट की गई प्रॉपर्टी ऐसी प्रॉपर्टी होती हैं जिन्हें एलिमेंट से तय नहीं किया जाता है. हालांकि, ये प्रॉपर्टी अपने पैरंट से इनहेरिट की जाती हैं. इनहेरिट नहीं की गई प्रॉपर्टी (जिन्हें "रीसेट" प्रॉपर्टी कहा जाता है) तय न होने पर, डिफ़ॉल्ट वैल्यू का इस्तेमाल करती हैं.

ट्री, ट्री में सभी निर्देशों को कैश मेमोरी में सेव करने में हमारी मदद करता है, जिसमें कंप्यूट किए गए आखिरी मान शामिल होते हैं. विचार यह है कि अगर बॉटम नोड किसी स्ट्रक्चर के लिए परिभाषा नहीं देता है, तो ऊपरी नोड में कैश मेमोरी में सेव किए गए स्ट्रक्चर का इस्तेमाल किया जा सकता है.

रूल ट्री का इस्तेमाल करके, स्टाइल के कॉन्टेक्स्ट की गणना करना

किसी खास एलिमेंट के लिए स्टाइल के कॉन्टेक्स्ट की गणना करते समय, हम पहले नियम ट्री में पाथ का कंप्यूट करते हैं या पहले से मौजूद किसी एलिमेंट का इस्तेमाल करते हैं. इसके बाद, हम नए स्टाइल वाले कॉन्टेक्स्ट में निर्देशों को भरने के लिए, पाथ में नियमों को लागू करना शुरू करते हैं. हम पाथ के निचले नोड से शुरुआत करते हैं - सबसे ज़्यादा प्राथमिकता वाला नोड (आम तौर पर, सबसे सटीक सिलेक्टर) और हमारा स्ट्रक्चर भर जाने तक ट्री को ऊपर रखते हैं. अगर उस नियम नोड में निर्देश के लिए कोई विशेष विवरण नहीं है, तो हम पूरी तरह से ऑप्टिमाइज़ कर सकते हैं - हम ट्री को तब तक आगे बढ़ा सकते हैं जब तक कि हमें वह नोड नहीं मिल जाता जिसमें इसे पूरी तरह से तय किया गया है और उस पर पॉइंट किया गया है - यह सबसे अच्छा ऑप्टिमाइज़ेशन है - पूरे स्ट्रक्चर को शेयर किया गया है. यह आखिरी वैल्यू और मेमोरी की गणना को सेव करता है.

अगर हमें आंशिक परिभाषाएं मिलती हैं, तो हम तब तक आगे बढ़ते रहते हैं, जब तक संरचना नहीं भर जाती.

अगर हमें अपने स्ट्रक्चर की कोई परिभाषा नहीं मिलती है, तो अगर स्ट्रक्चर "इनहेरिट" किया गया है, तो हम कॉन्टेक्स्ट ट्री में अपने पैरंट के स्ट्रक्चर की ओर इशारा करते हैं. इस मामले में, हम निर्देश भी शेयर कर पाए. अगर यह रीसेट करने का स्ट्रक्चर है, तो डिफ़ॉल्ट वैल्यू का इस्तेमाल किया जाएगा.

अगर सबसे सटीक नोड में वैल्यू नहीं जोड़ी जाती हैं, तो हमें उसे असल वैल्यू में बदलने के लिए कुछ और कैलकुलेशन करनी होंगी. इसके बाद, हम नतीजे को ट्री नोड में कैश मेमोरी में सेव करते हैं, ताकि बच्चे उसका इस्तेमाल कर सकें.

अगर किसी एलिमेंट का भाई-बहन या भाई एक ही ट्री नोड की ओर इशारा करता है, तो उनके बीच पूरी स्टाइल का संदर्भ शेयर किया जा सकता है.

आइए एक उदाहरण देखते हैं: मान लें कि हमारे पास यह एचटीएमएल है

<html>
  <body>
    <div class="err" id="div1">
      <p>
        this is a <span class="big"> big error </span>
        this is also a
        <span class="big"> very  big  error</span> error
      </p>
    </div>
    <div class="err" id="div2">another error</div>
  </body>
</html>

ये नियम भी हैं:

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

चीज़ों को आसान बनाने के लिए, हमें सिर्फ़ दो निर्देशों को भरने की ज़रूरत है: कलर स्ट्रक्चर और मार्जिन स्ट्रक्चर. कलर स्ट्रक्चर में सिर्फ़ एक सदस्य होता है: रंग मार्जिन स्ट्रक्चर में चारों तरफ़ से हिस्सा होता है.

नतीजे वाला रूल ट्री ऐसा दिखेगा (नोड को नोड के नाम से मार्क किया जाता है: वे नियम की वह संख्या दिखाते हैं):

रूल ट्री
इमेज 16: रूल ट्री

कॉन्टेक्स्ट ट्री कुछ ऐसा दिखेगा (नोड नाम: नियम नोड, जिसकी ओर इशारा करता है):

कॉन्टेक्स्ट ट्री.
इमेज 17: कॉन्टेक्स्ट ट्री

मान लें कि हम एचटीएमएल को पार्स करके दूसरे <div> टैग पर पहुंचते हैं. हमें इस नोड के लिए स्टाइल कॉन्टेक्स्ट बनाना होगा और इसके स्टाइल स्ट्रक्ट भरने होंगे.

हम इन नियमों का मिलान करके यह पता लगाएंगे कि <div> से मेल खाने वाले नियम 1, 2, और 6 हैं. इसका मतलब है कि ट्री में पहले से ही एक पाथ मौजूद है जिसका इस्तेमाल हमारा एलिमेंट कर सकता है. हमें नियम 6 (नियम ट्री में नोड F) के लिए, बस इसमें एक और नोड जोड़ना है.

हम स्टाइल कॉन्टेक्स्ट बनाएंगे और उसे कॉन्टेक्स्ट ट्री में शामिल करेंगे. नई शैली का कॉन्टेक्स्ट, नियम ट्री में नोड F को पॉइंट करेगा.

अब हमें स्टाइल से जुड़े निर्देशों को पूरा करना होगा. हम मार्जिन के स्ट्रक्चर को भरने से शुरुआत करेंगे. आखिरी नियम नोड (F) को मार्जिन स्ट्रक्चर में नहीं जोड़ा जाता. इसलिए, हम ट्री पर तब तक आगे बढ़ सकते हैं, जब तक कि हमें पिछली नोड इंसर्शन में कंप्यूट किया गया कैश किया गया स्ट्रक्चर नहीं मिल जाता और हम उसका इस्तेमाल करते हैं. हमें इसे नोड B पर मिलेगा, जो सबसे ऊपर वाला नोड है जिसने मार्जिन नियमों के बारे में बताया है.

हमारे पास कलर स्ट्रक्चर की एक परिभाषा है, इसलिए हम कैश मेमोरी में सेव किए गए स्ट्रक्चर का इस्तेमाल नहीं कर सकते. रंग की एक विशेषता है, इसलिए हमें दूसरी विशेषताओं को भरने के लिए ऊपर जाने की ज़रूरत नहीं है. हम आखिरी वैल्यू (स्ट्रिंग को आरजीबी वगैरह में बदलें) का हिसाब लगाएंगे और इस नोड पर कंप्यूट किए गए स्ट्रक्चर को कैश मेमोरी में सेव करेंगे.

दूसरे <span> एलिमेंट पर काम करना और भी आसान है. हम नियमों का मिलान करेंगे और इस नतीजे पर पहुंचेंगे कि यह नियम G की ओर इशारा करता है, जैसे कि पिछली अवधि. हमारे भाई-बहन एक ही नोड की ओर इशारा करते हैं. इसलिए, हम स्टाइल का पूरा कॉन्टेक्स्ट शेयर कर सकते हैं और सिर्फ़ पिछले स्पैन के कॉन्टेक्स्ट की जानकारी दे सकते हैं.

जिन निर्देशों में पैरंट से इनहेरिट किए गए नियम शामिल होते हैं, उन्हें कॉन्टेक्स्ट ट्री पर कैश मेमोरी में सेव किया जाता है (असल में कलर प्रॉपर्टी इनहेरिट की जाती है. हालांकि, Firefox इसे रीसेट के तौर पर देखता है और नियम ट्री पर कैश मेमोरी में सेव करता है).

उदाहरण के लिए, अगर हमने पैराग्राफ़ में फ़ॉन्ट के लिए नियम जोड़े हैं, तो:

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

फिर पैराग्राफ़ एलिमेंट, जो कि कॉन्टेक्स्ट ट्री में div का चाइल्ड है, उसके पैरंट जैसा ही फ़ॉन्ट स्ट्रक्चर शेयर कर सकता है. ऐसा तब होता है, जब पैराग्राफ़ के लिए कोई फ़ॉन्ट नियम तय नहीं किया गया था.

WebKit में, जिसके पास रूल ट्री नहीं होता, उससे मेल खाने वाले एलानों को चार बार बदला जाता है. पहली गैर-ज़रूरी, ज़्यादा प्राथमिकता वाली प्रॉपर्टी लागू की जाती हैं (ऐसी प्रॉपर्टी जिन्हें पहले लागू किया जाना चाहिए, क्योंकि बाकी प्रॉपर्टी उन पर निर्भर करती हैं, जैसे कि डिसप्ले), फिर ज़्यादा प्राथमिकता और फिर सामान्य प्राथमिकता और गैर-ज़रूरी नियम, और फिर सामान्य प्राथमिकता वाले ज़रूरी नियम लागू किए जाते हैं. इसका मतलब है कि कई बार दिखने वाली प्रॉपर्टी को कैस्केड के सही क्रम के हिसाब से हल कर दिया जाएगा. आखिरी जीत.

इसलिए, कम शब्दों में कहें: स्टाइल ऑब्जेक्ट (पूरी तरह से या उनके अंदर कुछ स्ट्रक्चर) को शेयर करने से, पहली और तीसरी लाइन हल हो जाती है. Firefox नियम ट्री, प्रॉपर्टी को सही क्रम में लागू करने में भी मदद करता है.

आसान मैच के लिए नियमों में हेर-फेर करना

स्टाइल के नियमों के कई सोर्स होते हैं:

  1. सीएसएस नियम, बाहरी स्टाइल शीट या स्टाइल एलिमेंट में. css p {color: blue}
  2. इनलाइन स्टाइल एट्रिब्यूट, जैसे कि html <p style="color: blue" />
  3. एचटीएमएल विज़ुअल एट्रिब्यूट (जो काम के स्टाइल नियमों के साथ मैप किए जाते हैं) html <p bgcolor="blue" /> आखिरी दो एलिमेंट, एलिमेंट के साथ आसानी से मैच हो जाते हैं. ऐसा इसलिए होता है, क्योंकि उनके पास स्टाइल एट्रिब्यूट हैं और एचटीएमएल एट्रिब्यूट को एलिमेंट का इस्तेमाल करके कुंजी के तौर पर मैप किया जा सकता है.

जैसा कि समस्या #2 में पहले बताया गया था, सीएसएस के नियम को मैच करना मुश्किल हो सकता है. समस्या को हल करने के लिए, नियमों में बदलाव किया जाता है, ताकि उन्हें आसानी से ऐक्सेस किया जा सके.

स्टाइल शीट को पार्स करने के बाद, सिलेक्टर के मुताबिक नियमों को कई हैश मैप में से किसी एक में जोड़ दिया जाता है. यहां आईडी के हिसाब से, क्लास के नाम, टैग के नाम, और सामान्य मैप के हिसाब से मैप होते हैं. ये वे मैप होते हैं जो कैटगरी में फ़िट नहीं होते हैं. अगर सिलेक्टर कोई आईडी है, तो नियम को आईडी मैप में जोड़ दिया जाएगा. अगर यह क्लास है, तो इसे क्लास मैप वगैरह में जोड़ दिया जाएगा.

इस बदलाव की मदद से, नियमों को आसानी से मैच किया जा सकता है. हर एलान को देखने की ज़रूरत नहीं है: हम मैप से किसी एलिमेंट के लिए काम के नियमों को एक्सट्रैक्ट कर सकते हैं. यह ऑप्टिमाइज़ेशन 95+% नियमों को हटा देता है, ताकि मैच करने की प्रोसेस(4.1) के दौरान उन पर ध्यान देने की ज़रूरत न पड़े.

आइए, स्टाइल के इन नियमों का उदाहरण देखते हैं:

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

पहला नियम क्लास के मैप में डाला जाएगा. आईडी मैप में दूसरा और टैग मैप में तीसरा.

इस एचटीएमएल फ़्रैगमेंट के लिए;

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

हम सबसे पहले p एलिमेंट के लिए नियम ढूंढने की कोशिश करेंगे. क्लास मैप में एक "गड़बड़ी" कुंजी होगी जिसके तहत "p.error" नियम मिलेगा. div तत्व के आईडी मैप (कुंजी आईडी है) और टैग मैप में प्रासंगिक नियम होंगे. इसलिए, सिर्फ़ यह पता लगाना बाकी है कि कुंजियों से निकाले गए कौनसे नियम वाकई मेल खाते हैं.

उदाहरण के लिए, अगर div का नियम यह था:

table div {margin: 5px}

इसे अब भी टैग मैप से निकाला जाएगा, क्योंकि कुंजी सबसे दाईं ओर मौजूद सिलेक्टर है, लेकिन यह हमारे उस div एलिमेंट से मैच नहीं करेगी जिसके पास टेबल का कोई एंसेस्टर नहीं है.

WebKit और Firefox दोनों यह हेर-फेर करते हैं.

स्टाइल शीट कैस्केड ऑर्डर

स्टाइल ऑब्जेक्ट में हर विज़ुअल एट्रिब्यूट के हिसाब से प्रॉपर्टी होती हैं. जैसे, सभी सीएसएस एट्रिब्यूट, लेकिन बहुत सामान्य एट्रिब्यूट. अगर प्रॉपर्टी, मैच होने वाले किसी भी नियम के मुताबिक नहीं है, तो पैरंट एलिमेंट स्टाइल ऑब्जेक्ट से कुछ प्रॉपर्टी इनहेरिट की जा सकती हैं. अन्य प्रॉपर्टी की डिफ़ॉल्ट वैल्यू होती हैं.

समस्या की शुरुआत तब होती है, जब एक से ज़्यादा परिभाषाएं होती हैं - इस समस्या को हल करने के लिए कैस्केड ऑर्डर दिया गया है.

स्टाइल प्रॉपर्टी के लिए एलान, कई स्टाइल शीट में दिख सकता है और स्टाइल शीट में कई बार दिख सकता है. इसका मतलब है कि नियमों को लागू करने का क्रम बहुत ज़रूरी है. इसे "कैस्केड" ऑर्डर कहा जाता है. CSS2 के स्पेसिफ़िकेशन के मुताबिक, कैस्केड का क्रम (कम से ज़्यादा) है:

  1. ब्राउज़र से जुड़े एलान
  2. उपयोगकर्ता के सामान्य एलान
  3. लेखक की सामान्य जानकारी
  4. लेखक की अहम जानकारी
  5. उपयोगकर्ता के लिए ज़रूरी एलान

ब्राउज़र के एलान सबसे कम अहम होते हैं. साथ ही, एलान को ज़रूरी के तौर पर मार्क किए जाने पर ही उपयोगकर्ता, लेखक को ओवरराइड करता है. एक ही क्रम वाले एलानों को खासियत के हिसाब से क्रम में लगाया जाएगा. इसके बाद, उन्हें तय किए गए क्रम के हिसाब से क्रम में लगाया जाएगा. एचटीएमएल विज़ुअल एट्रिब्यूट का अनुवाद, मिलते-जुलते सीएसएस एलानों में किया जाता है . उन्हें कम प्राथमिकता वाला लेखक नियम माना जाता है.

खासियत

सिलेक्टर की खास जानकारी को CSS2 के स्पेसिफ़िकेशन में इस तरह से तय किया जाता है:

  1. गिनती 1 करें, अगर यह एलान, सिलेक्टर वाले नियम के बजाय 'स्टाइल' एट्रिब्यूट है, तो 0 न होने पर (= a)
  2. सिलेक्टर में आईडी एट्रिब्यूट की संख्या (= b) गिनें
  3. सिलेक्टर (= c) में अन्य एट्रिब्यूट और सूडो-क्लास की संख्या गिनें
  4. सिलेक्टर (= d) में, एलिमेंट के नाम और छद्म-एलिमेंट की संख्या गिनें

चार संख्याओं a-b-c-d (बड़े आधार वाले संख्या प्रणाली में) को जोड़ने पर विशिष्टता मिलती है.

किसी एक कैटगरी में आपको मिली सबसे ज़्यादा संख्या से तय होता है कि आपको किस नंबर बेस का इस्तेमाल करना है.

उदाहरण के लिए, अगर a=14 है, तो हेक्साडेसिमल आधार का इस्तेमाल किया जा सकता है. ऐसी स्थिति में जहां a=17 है, आपको 17 अंकों की संख्या के आधार की ज़रूरत होगी. बाद की स्थिति इस तरह के एक सिलेक्टर के साथ हो सकती है: html body div div p... (आपके सिलेक्टर में 17 टैग... बहुत कम संभावना है).

कुछ उदाहरण:

 *             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
 li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
 li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
 ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
 ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
 h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
 ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
 li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
 #x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
 style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

नियमों को क्रम से लगाना

नियमों का मिलान करने के बाद, उन्हें कैस्केड के नियमों के हिसाब से क्रम में लगाया जाता है. WebKit, छोटी सूचियों के लिए बबल क्रम और बड़ी सूचियों के लिए मर्ज सॉर्ट का इस्तेमाल करता है. WebKit, नियमों के लिए > ऑपरेटर को ओवरराइड करके, क्रम से लगाने की सुविधा लागू करता है:

static bool operator >(CSSRuleData& r1, CSSRuleData& r2)
{
    int spec1 = r1.selector()->specificity();
    int spec2 = r2.selector()->specificity();
    return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;
}

धीरे-धीरे होने वाली प्रोसेस

WebKit ऐसे फ़्लैग का इस्तेमाल करता है जो यह मार्क करता है कि सभी टॉप लेवल स्टाइल शीट (इसमें @Imports भी शामिल है) लोड किए गए हैं या नहीं. अगर अटैच करते समय स्टाइल पूरी तरह से लोड नहीं होता है, तो प्लेस होल्डर का इस्तेमाल किया जाता है और उसे दस्तावेज़ में मार्क किया जाता है. स्टाइल शीट लोड होने के बाद, उनका फिर से हिसाब लगाया जाता है.

लेआउट

जब रेंडरर बनाया जाता है और उसे ट्री में जोड़ा जाता है, तो उसकी कोई पोज़िशन और साइज़ नहीं होता. इन वैल्यू को कैलकुलेट करना, लेआउट या रीफ़्लो कहा जाता है.

एचटीएमएल, फ़्लो आधारित लेआउट मॉडल का इस्तेमाल करता है. इसका मतलब है कि ज़्यादातर मामलों में, एक ही पास में ज्यामिति की गिनती की जा सकती है. "फ़्लो में" बाद के एलिमेंट आम तौर पर उन एलिमेंट की ज्यामिति पर असर नहीं डालते जो पहले "फ़्लो में" हैं. इसलिए, लेआउट दस्तावेज़ में बाईं से दाईं ओर, ऊपर से नीचे तक जा सकता है. इसके कुछ अपवाद हैं: उदाहरण के लिए, एचटीएमएल टेबल के लिए एक से ज़्यादा पास की ज़रूरत हो सकती है.

निर्देशांक सिस्टम, रूट फ़्रेम के मुताबिक होता है. शीर्ष और बाएं निर्देशांकों का उपयोग किया जाता है.

लेआउट बार-बार इस्तेमाल होने वाली प्रोसेस है. यह रूट रेंडरर पर शुरू होता है, जो एचटीएमएल दस्तावेज़ के <html> एलिमेंट से मेल खाता है. लेआउट, फ़्रेम के कुछ या पूरे क्रम में बार-बार चलता रहता है. इससे हर रेंडरर के लिए ज्यामितीय जानकारी की गणना की जाती है, जिसे इसकी ज़रूरत होती है.

रूट रेंडरर की जगह 0,0 है और इसके डाइमेंशन, व्यूपोर्ट हैं - यह ब्राउज़र विंडो का दिखने वाला हिस्सा होता है.

सभी रेंडरर में एक "लेआउट" या "रीफ़्लो" तरीका होता है. हर रेंडरर अपने चाइल्ड के उस लेआउट तरीके को शुरू करता है जिसे लेआउट की ज़रूरत होती है.

डर्टी बिट सिस्टम

हर छोटे बदलाव के लिए पूरा लेआउट इस्तेमाल न करने के लिए, ब्राउज़र एक "डर्टी बिट" सिस्टम का इस्तेमाल करते हैं. जिस रेंडरर में बदलाव किया जाता है या जोड़ा जाता है वह खुद और उसके चाइल्ड को "गंदा" के तौर पर मार्क करता है: लेआउट की ज़रूरत होती है.

इसमें दो फ़्लैग हैं: "गंदा है" और "बच्चे गंदे हैं". इसका मतलब है कि भले ही रेंडरर ठीक हो, लेकिन इसमें कम से कम एक ऐसा चाइल्ड है जिसके लिए लेआउट की ज़रूरत होती है.

ग्लोबल और इंक्रीमेंटल लेआउट

लेआउट को पूरे रेंडर ट्री पर ट्रिगर किया जा सकता है - यह "ग्लोबल" लेआउट है. ऐसा इन वजहों से हो सकता है:

  1. ग्लोबल स्टाइल में होने वाला बदलाव, जो सभी रेंडरर पर असर डालता है. जैसे, फ़ॉन्ट के साइज़ में बदलाव.
  2. स्क्रीन का साइज़ बदलने की वजह से

लेआउट बढ़ाया जा सकता है, सिर्फ़ गंदे रेंडरर बिछाए जाएंगे (इससे कुछ नुकसान हो सकता है, जिसके लिए अतिरिक्त लेआउट की ज़रूरत होगी).

रेंडरर के गंदे होने पर, इंक्रीमेंटल लेआउट (एसिंक्रोनस तरीके से) ट्रिगर होता है. उदाहरण के लिए, जब नेटवर्क से ज़्यादा कॉन्टेंट आने और डीओएम ट्री में जोड़ दिए जाने के बाद, रेंडर ट्री में नए रेंडरर जोड़े जाते हैं.

इंक्रीमेंटल लेआउट.
इमेज 18: इंक्रीमेंटल लेआउट - सिर्फ़ गंदे रेंडरर और उनके बच्चों को दिखाया जाता है

एसिंक्रोनस और सिंक्रोनस लेआउट

इंक्रीमेंटल लेआउट एसिंक्रोनस तरीके से किया जाता है. Firefox इंक्रीमेंटल लेआउट के लिए "रीफ़्लो कमांड" दिखाता है और एक शेड्यूलर इन कमांड के बैच को एक्ज़ीक्यूशन को ट्रिगर करता है. WebKit में एक टाइमर भी होता है जो बढ़ते हुए लेआउट को चलाता है - ट्री को ट्रैवर्स किया जाता है और "गंदा" रेंडरर लेआउट आउट होता है.

शैली की जानकारी मांगने वाली स्क्रिप्ट, जैसे कि "offsetHeight", क्रम के हिसाब से लेआउट को ट्रिगर कर सकते हैं.

ग्लोबल लेआउट आम तौर पर सिंक्रोनस रूप से ट्रिगर होता है.

कभी-कभी शुरुआती लेआउट के बाद, लेआउट को कॉलबैक के तौर पर ट्रिगर किया जाता है. ऐसा इसलिए होता है, क्योंकि स्क्रोल करने की पोज़िशन जैसे कुछ एट्रिब्यूट बदल जाते हैं.

अनुकूलन

जब किसी लेआउट को "साइज़ बदलें" या रेंडरर की पोज़िशन(साइज़ नहीं) में बदलाव करने से ट्रिगर होता है, तो रेंडर के साइज़ को कैश मेमोरी से लिया जाता है. उसका फिर से हिसाब नहीं लगाया जाता...

कुछ मामलों में, सिर्फ़ सब ट्री में बदलाव किया जाता है और लेआउट रूट से शुरू नहीं होता. ऐसा उन मामलों में हो सकता है जहां बदलाव लोकल हो और उसके आस-पास की चीज़ों पर कोई असर न पड़ता हो. जैसे, टेक्स्ट फ़ील्ड में डाला गया टेक्स्ट. ऐसा न करने पर, हर कीस्ट्रोक, रूट से शुरू होने वाला लेआउट ट्रिगर करेगा.

लेआउट प्रोसेस

लेआउट में आम तौर पर यह पैटर्न होता है:

  1. पैरंट रेंडरर, डिवाइस की चौड़ाई अपने हिसाब से तय करता है.
  2. माता-पिता, बच्चों के ऊपर ध्यान देते हैं और:
    1. चाइल्ड रेंडरर रखें (इसके x और y को सेट करता है).
    2. ज़रूरत पड़ने पर चाइल्ड लेआउट को कॉल किया जाता है - वे गंदे होते हैं या हम ग्लोबल लेआउट में होते हैं, या किसी दूसरी वजह से - जिससे बच्चे की ऊंचाई का हिसाब लगाया जाता है.
  3. पैरंट अपनी ऊंचाई सेट करने के लिए, बच्चों की कुल ऊंचाई, मार्जिन की ऊंचाई, और पैडिंग का इस्तेमाल करता है. इसका इस्तेमाल पैरंट रेंडरर का पैरंट करेगा.
  4. इसके धूल भरे बिट को गलत पर सेट करता है.

Firefox, लेआउट के पैरामीटर के रूप में "state" ऑब्जेक्ट(nsHTMLReflowState) का इस्तेमाल करता है (इसे "reflow" कहा जाता है). राज्य में माता-पिता की चौड़ाई भी शामिल है.

Firefox लेआउट का आउटपुट "मेट्रिक" ऑब्जेक्ट(nsHTMLReflowMetrics) है. इसमें रेंडरर की कंप्यूट की गई ऊंचाई शामिल होगी.

विड्थ कैलकुलेशन

रेंडरर की चौड़ाई का हिसाब, कंटेनर ब्लॉक की चौड़ाई, रेंडर करने वाले की स्टाइल "width" प्रॉपर्टी, मार्जिन, और बॉर्डर का इस्तेमाल करके लगाया जाता है.

उदाहरण के लिए, नीचे दिए गए div की चौड़ाई:

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

WebKit के ज़रिए इस तरह से गणना की जाएगी(क्लास 'रेंडरबॉक्स' मेथड कैल्कविड्थ):

  • कंटेनर की चौड़ाई, उपलब्ध कंटेनर की ज़्यादा से ज़्यादा चौड़ाई और 0 है. इस मामले में उपलब्ध चौड़ाई का मतलब कॉन्टेंट की चौड़ाई है, जिसका हिसाब इस तरह से लगाया जाता है:
clientWidth() - paddingLeft() - paddingRight()

क्लाइंट-विड्थ और ClientHeight, बॉर्डर और स्क्रोलबार को छोड़कर, ऑब्जेक्ट के अंदरूनी हिस्से को दिखाती हैं.

  • एलिमेंट की चौड़ाई, "width" स्टाइल एट्रिब्यूट है. इसकी गिनती निरपेक्ष मान के रूप में की जाएगी. इसके लिए, कंटेनर की चौड़ाई के प्रतिशत का हिसाब लगाया जाएगा.

  • हॉरिज़ॉन्टल बॉर्डर और पैडिंग (जगह) जोड़ दी गई हैं.

अब तक यह "पसंदीदा चौड़ाई" का कैलकुलेशन था. अब कम से कम और ज़्यादा से ज़्यादा चौड़ाई का हिसाब लगाया जाएगा.

अगर पसंदीदा चौड़ाई, ज़्यादा से ज़्यादा चौड़ाई से ज़्यादा है, तो ज़्यादा से ज़्यादा चौड़ाई का इस्तेमाल किया जाता है. अगर यह चौड़ाई (सबसे छोटी इकाई) से कम है, तो कम से कम चौड़ाई का इस्तेमाल किया जाता है.

लेआउट की ज़रूरत पड़ने पर इन वैल्यू को कैश मेमोरी में सेव किया जाता है. हालांकि, इनकी चौड़ाई में कोई बदलाव नहीं होता.

लाइन ब्रेकिंग

जब किसी लेआउट के बीच में मौजूद रेंडरर यह तय करता है कि उसे हटाना है, तो रेंडरर रुक जाता है. इसके बाद, लेआउट के पैरंट को यह मैसेज दिखता है कि इसे ठीक करना ज़रूरी है. पैरंट उन पर ज़्यादा रेंडरर और कॉल लेआउट बनाता है.

पेंटिंग

पेंटिंग के चरण में, रेंडर ट्री को ट्रैवर्स किया जाता है और स्क्रीन पर कॉन्टेंट दिखाने के लिए, रेंडर करने वाले के " Paint()" तरीके को कॉल किया जाता है. पेंटिंग, यूज़र इंटरफ़ेस (यूआई) इंफ़्रास्ट्रक्चर कॉम्पोनेंट का इस्तेमाल करती है.

ग्लोबल और इंक्रीमेंटल

लेआउट की तरह, पेंटिंग भी ग्लोबल हो सकती है - पूरे पेड़ को पेंट किया गया हो या इंंक्रीमेंटल हो. इंंक्रीमेंटल पेंटिंग में, रेंडरर में कुछ ऐसे बदलाव होते हैं जिनसे पूरे पेड़ पर कोई असर नहीं पड़ता. बदला गया रेंडरर, स्क्रीन पर अपने रेक्टैंगल को अमान्य कर देता है. इससे ओएस को यह "गंदा इलाका" के तौर पर दिखता है और "पेंट" इवेंट जनरेट करता है. ओएस इस काम को बड़ी चालाकी से करता है और कई इलाकों को एक साथ मिला देता है. Chrome में यह बहुत जटिल है, क्योंकि रेंडरर किसी दूसरी प्रोसेस में होता है और मुख्य प्रोसेस में. Chrome कुछ हद तक, ओएस के काम करने के तरीके को सिम्युलेट करता है. प्रज़ेंटेशन इन इवेंट को सुनता है और मैसेज को रेंडर रूट को सौंपता है. सही रेंडरर तक पहुंचने तक, ट्री को ट्रेवर्स किया जाता है. वह खुद को (और आम तौर पर अपने बच्चों) को फिर से पेंट करेगा.

पेंटिंग का ऑर्डर

CSS2, पेंटिंग की प्रोसेस का क्रम तय करता है. यह असल में वही क्रम होता है जिसमें एलिमेंट को स्टैकिंग कॉन्टेक्स्ट में स्टैक किया जाता है. इस क्रम से पेंटिंग पर असर पड़ता है, क्योंकि स्टैक को पीछे से सामने की ओर पेंट किया जाता है. ब्लॉक रेंडरर का स्टैकिंग क्रम यह होता है:

  1. बैकग्राउंड का रंग
  2. बैकग्राउंड इमेज
  3. border
  4. बच्चे
  5. outline

फ़ायरफ़ॉक्स डिसप्ले सूची

Firefox रेंडर ट्री के ऊपर जाता है और पेंट किए गए आयताकार के लिए एक डिसप्ले सूची बनाता है. इसमें आयताकार रेंडरर होता है, जो सही पेंटिंग क्रम में होता है. जैसे, रेंडरर के बैकग्राउंड, फिर बॉर्डर वगैरह.

इस तरह, दोबारा पेंट करने के लिए, पेड़ को कई बार घुमाने के बजाय, सिर्फ़ एक बार पार करना होता है. जैसे, हर बैकग्राउंड, फिर सभी इमेज, और फिर बॉर्डर वगैरह को पेंट करना.

Firefox छिपाए जाने वाले एलिमेंट को जोड़कर, प्रोसेस को ऑप्टिमाइज़ करता है, जैसे कि अन्य ओपेक एलिमेंट के नीचे एलिमेंट.

WebKit रेक्टैंगल स्टोरेज

फिर से पेंट करने से पहले, WebKit पुराने रेक्टैंगल को बिट मैप के तौर पर सेव करता है. इसके बाद, यह नए और पुराने आयतों के बीच के अंतर को ही दिखाता है.

डाइनैमिक बदलाव

ब्राउज़र, बदलाव की वजह से कम से कम कार्रवाइयां करने की कोशिश करते हैं. इसलिए, एलिमेंट के रंग में बदलाव करने से, एलिमेंट को सिर्फ़ फिर से पेंट किया जाएगा. एलिमेंट की पोज़िशन बदलने से एलिमेंट, उसके चिल्ड्रन, और शायद भाई-बहनों का लेआउट और रीपेंट हो जाएगा. DOM नोड जोड़ने से नोड का लेआउट और रीपेंट हो जाएगा. "html" एलिमेंट का फ़ॉन्ट साइज़ बढ़ाने जैसे बड़े बदलाव, कैश मेमोरी को अमान्य कर देंगे. साथ ही, यह पूरे ट्री को फिर से लेआउट और फिर से पेंट करेंगे.

रेंडरिंग इंजन के थ्रेड

रेंडरिंग इंजन, सिंगल थ्रेड वाला होता है. नेटवर्क ऑपरेशन को छोड़कर, करीब-करीब सब कुछ एक ही थ्रेड में होता है. Firefox और Safari में यह ब्राउज़र की मुख्य थ्रेड होती है. Chrome में यह टैब प्रोसेस की मुख्य थ्रेड है.

नेटवर्क ऑपरेशन कई समानांतर थ्रेड से किए जा सकते हैं. पैरलल कनेक्शन की संख्या सीमित होती है. आम तौर पर, दो से छह कनेक्शन होते हैं.

इवेंट लूप

ब्राउज़र की मुख्य थ्रेड एक इवेंट लूप है. यह कभी न खत्म होने वाला लूप है, जो प्रोसेस को चालू रखता है. यह इवेंट (जैसे कि लेआउट और पेंट इवेंट) का इंतज़ार करता है और उन्हें प्रोसेस करता है. यह मुख्य इवेंट लूप के लिए Firefox कोड है:

while (!mExiting)
    NS_ProcessNextEvent(thread);

CSS2 विज़ुअल मॉडल

कैनवस

CSS2 स्पेसिफ़िकेशन के मुताबिक, कैनवस शब्द से यह पता चलता है कि "वह स्पेस जहां फ़ॉर्मैटिंग का स्ट्रक्चर रेंडर किया जाता है": जहां ब्राउज़र कॉन्टेंट को पेंट करता है.

कैनवस के हर डाइमेंशन के लिए, कैनवस की कोई सीमा नहीं होती. हालांकि, ब्राउज़र, व्यूपोर्ट के डाइमेंशन के आधार पर शुरुआती चौड़ाई चुनते हैं.

www.w3.org/TR/CSS2/zindex.html के मुताबिक, अगर कैनवस किसी दूसरे में रखा जाता है, तो वह पारदर्शी होता है. अगर कैनवस मौजूद नहीं है, तो ब्राउज़र के लिए तय किया गया रंग दिया जाता है.

सीएसएस बॉक्स का मॉडल

सीएसएस बॉक्स मॉडल, रेक्टैंग्युलर बॉक्स के बारे में जानकारी देता है. ये बॉक्स, दस्तावेज़ ट्री में एलिमेंट के लिए जनरेट होते हैं और विज़ुअल फ़ॉर्मैटिंग मॉडल के हिसाब से बनाए जाते हैं.

हर बॉक्स में, कॉन्टेंट एरिया (जैसे, टेक्स्ट, इमेज वगैरह) और आस-पास की पैडिंग, बॉर्डर, और मार्जिन एरिया वैकल्पिक होते हैं.

CSS2 बॉक्स मॉडल
इमेज 19: CSS2 बॉक्स मॉडल

हर नोड, ऐसे बॉक्स की संख्या 0...n जनरेट करता है.

सभी एलिमेंट में "display" प्रॉपर्टी होती है, जिससे यह तय होता है कि किस तरह का बॉक्स जनरेट होगा.

उदाहरण:

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

डिफ़ॉल्ट इनलाइन सेट होता है, लेकिन ब्राउज़र स्टाइल शीट अन्य डिफ़ॉल्ट सेट कर सकती है. उदाहरण के लिए: "div" एलिमेंट का डिफ़ॉल्ट डिसप्ले ब्लॉक है.

डिफ़ॉल्ट स्टाइल शीट का उदाहरण यहां देखा जा सकता है: www.w3.org/TR/CSS2/sample.html.

पोज़िशनिंग स्कीम

तीन स्कीम होती हैं:

  1. सामान्य: ऑब्जेक्ट को दस्तावेज़ में उसकी जगह के हिसाब से रखा जाता है. इसका मतलब है कि रेंडर ट्री में इसकी जगह, डीओएम ट्री में इसकी जगह जैसी है. साथ ही, इसे बॉक्स टाइप और डाइमेंशन के हिसाब से तय किया गया है
  2. फ़्लोट: ऑब्जेक्ट को पहले सामान्य फ़्लो की तरह बिछाया जाता है. इसके बाद, उसे जितना हो सके उतना बाईं या दाईं ओर ले जाया जाता है
  3. सटीक: ऑब्जेक्ट को रेंडर ट्री में, डीओएम ट्री से अलग जगह पर रखा जाता है

पोज़िशनिंग स्कीम, "रैंक" प्रॉपर्टी और "फ़्लोट" एट्रिब्यूट से सेट की जाती है.

  • इसकी वजह से स्थिर और रिलेटिव होता है
  • ऐब्सलूट और फ़िक्स वजह के हिसाब से ऐब्सलूट पोज़िशनिंग

स्टैटिक पोज़िशनिंग में, कोई पोज़िशन तय नहीं की जाती और डिफ़ॉल्ट पोज़िशन का इस्तेमाल किया जाता है. अन्य स्कीम में, लेखक ने जगह की जानकारी दी है: टॉप, बॉटम, लेफ़्ट, राइट.

बॉक्स को बनाने का तरीका नीचे दी गई बातों से तय होता है:

  • बॉक्स टाइप
  • बॉक्स के डाइमेंशन
  • पोज़िशनिंग स्कीम
  • बाहरी जानकारी, जैसे कि इमेज का साइज़ और स्क्रीन का साइज़

बॉक्स के टाइप

ब्लॉक बॉक्स: ब्लॉक फ़ॉर्म - ब्राउज़र विंडो में इसका अपना रेक्टैंगल होता है.

ब्लॉक बॉक्स.
इमेज 20: ब्लॉक बॉक्स

इनलाइन बॉक्स: इसका अपना ब्लॉक नहीं होता, लेकिन यह मौजूदा ब्लॉक के अंदर होता है.

इनलाइन बॉक्स.
इमेज 21: इनलाइन बॉक्स

ब्लॉक को, एक के बाद एक वर्टिकल फ़ॉर्मैट में रखा जाता है. इनलाइन को हॉरिज़ॉन्टल फ़ॉर्मैट में रखा जाता है.

ब्लॉक और इनलाइन फ़ॉर्मैटिंग.
इमेज 22: ब्लॉक और इनलाइन फ़ॉर्मैटिंग

इनलाइन बॉक्स को लाइनों या "लाइन बॉक्स" के अंदर रखा जाता है. ये लाइनें कम से कम सबसे लंबे बॉक्स जितनी ही लंबी होती हैं. हालांकि, ये लंबे बॉक्स के बराबर भी हो सकती हैं. ऐसा तब होता है, जब बॉक्स "बेसलाइन" पर अलाइन हों. इसका मतलब है कि एलिमेंट के निचले हिस्से को, नीचे वाले किसी और बॉक्स के पॉइंट पर अलाइन किया गया है. अगर कंटेनर की चौड़ाई ज़रूरत के मुताबिक नहीं है, तो इनलाइन को कई लाइनों पर डाल दिया जाएगा. आम तौर पर, ऐसा किसी पैराग्राफ़ में होता है.

लाइनें.
इमेज 23: लाइनें

स्थिति

संबंधी

रिलेटिव पोज़िशनिंग - सामान्य पोज़िशन में रखें और फिर ज़रूरी डेल्टा की मदद से पोज़िशन में बदलाव करें.

रिलेटिव पोज़िशनिंग.
इमेज 24: रिलेटिव पोज़िशनिंग

फ़्लोट

फ़्लोट बॉक्स को लाइन के बाईं या दाईं ओर शिफ़्ट किया जाता है. दिलचस्प सुविधा यह है कि इसके आस-पास अन्य बॉक्स दिखते हैं. एचटीएमएल:

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

यह ऐसा दिखेगा:

फ़्लोट.
इमेज 25: फ़्लोट

ऐब्सलूट और फ़िक्स

लेआउट को सामान्य फ़्लो पर ध्यान दिए बिना पूरी तरह से परिभाषित किया गया है. एलिमेंट, सामान्य फ़्लो में हिस्सा नहीं लेता. डाइमेंशन, कंटेनर से मिलते-जुलते होते हैं. स्थिर यानी कंटेनर, व्यूपोर्ट है.

जगह के बारे में तय किया गया समय.
इमेज 26: जगह तय करना

लेयर वाला वर्शन

यह z-index सीएसएस प्रॉपर्टी से तय होता है. यह बॉक्स का तीसरा डाइमेंशन दिखाता है: "z ऐक्सिस" पर इसकी पोज़िशन.

बॉक्स को स्टैक में बांटा गया है. इन्हें स्टैकिंग कॉन्टेक्स्ट कहा जाता है. हर स्टैक में, पिछले एलिमेंट को पहले पेंट किया जाएगा और आगे के एलिमेंट को सबसे ऊपर, उपयोगकर्ता के करीब रखा जाएगा. ओवरलैप होने पर, सबसे अहम एलिमेंट पुराने एलिमेंट को छिपा देगा.

स्टैक को z-index प्रॉपर्टी के हिसाब से क्रम में लगाया जाता है. "z-index" प्रॉपर्टी वाले बॉक्स, लोकल स्टैक बनाते हैं. व्यूपोर्ट में बाहरी स्टैक होता है.

उदाहरण:

<style type="text/css">
  div {
    position: absolute;
    left: 2in;
    top: 2in;
  }
</style>

<p>
  <div
    style="z-index: 3;background-color:red; width: 1in; height: 1in; ">
  </div>
  <div
    style="z-index: 1;background-color:green;width: 2in; height: 2in;">
  </div>
</p>

इससे यह होगा:

जगह के बारे में तय किया गया समय.
इमेज 27: जगह तय करना

हालांकि, लाल रंग का डीव मार्कअप में हरे रंग से पहले आता है और उसे नियमित फ़्लो में पहले ही पेंट किया जाता था, लेकिन z-इंडेक्स प्रॉपर्टी ऊंची है, इसलिए वह रूट बॉक्स में रखे स्टैक में ज़्यादा आगे है.

रिसॉर्स

  1. ब्राउज़र का आर्किटेक्चर

    1. ग्रॉसकुर्थ, एलन. वेब ब्राउज़र के लिए रेफ़रंस आर्किटेक्चर (pdf)
    2. गुप्ता, विनीत. ब्राउज़र कैसे काम करते हैं - पार्ट - आर्किटेक्चर
  2. पार्स करना

    1. आहो, सेठी, उल्मैन, कंपाइलर्स: प्रिंसिपल्स, टेक्नीक्स ऐंड टूल्स (यानी "ड्रैगन बुक"), एडिसन-वेस्ली, 1986
    2. रिक जेलीफ़. द बोल्ड ऐंड द ब्यूटीफ़ुल: HTML 5 के लिए दो नए ड्राफ़्ट.
  3. Firefox

    1. एल॰ डेविड बैरन, ज़्यादा तेज़ एचटीएमएल और सीएसएस: वेब डेवलपर के लिए लेआउट इंजन इंटरनल.
    2. एल॰ डेविड बैरन, ज़्यादा तेज़ एचटीएमएल और सीएसएस: वेब डेवलपर के लिए लेआउट इंजन इंटरनल (Google tech टॉक वीडियो)
    3. एल॰ डेविड बैरन, Mozilla का Layout Engine
    4. एल॰ डेविड बैरन, Mozilla स्टाइल सिस्टम डॉक्यूमेंटेशन
    5. क्रिस वॉटरसन, Notes on HTML Reflow
    6. क्रिस वॉटरसन, गेको की खास जानकारी
    7. ऐलेक्ज़ेंडर लार्सन, एचटीएमएल एचटीटीपी अनुरोध की समयसीमा
  4. WebKit

    1. डेविड हयात, सीएसएस लागू करना(भाग 1)
    2. डेविड हयात, AnOverview of WebCore
    3. डेविड हयात, WebCore रेंडरिंग
    4. डेविड हयात, The FOUC Challenge
  5. W3C की खास बातें

    1. एचटीएमएल 4.01 की खास बातें
    2. W3C HTML5 का स्पेसिफ़िकेशन
    3. कैस्केडिंग स्टाइल शीट लेवल 2 रिविज़न 1 (सीएसएस 2.1) की खास बातें
  6. ब्राउज़र बनाने के निर्देश

    1. Firefox. https://developer.mozilla.org/Build_Documentation
    2. वेबकिट. http://webkit.org/building/build.html

अनुवाद

इस पेज का दो बार जैपनीज़ में अनुवाद किया गया है:

बाहर से होस्ट किए गए, कोरियाई और टर्किश के अनुवाद देखे जा सकते हैं.

सभी को धन्यवाद!