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

आधुनिक वेब ब्राउज़र के पर्दे के पीछे की कहानी

प्रस्तावना

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

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

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

परिचय

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

हम इन ब्राउज़र के बारे में बात करेंगे

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

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

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

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

ब्राउज़र के यूज़र इंटरफ़ेस में कई चीज़ें एक जैसी होती हैं. यूज़र इंटरफ़ेस (यूआई) के सामान्य एलिमेंट में ये शामिल हैं:

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

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

हाई-लेवल इन्फ़्रास्ट्रक्चर

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

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

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

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

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

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

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

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

मुख्य फ़्लो

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

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

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

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

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

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

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

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

WebKit का मुख्य फ़्लो.
तीसरी इमेज: WebKit का मुख्य फ़्लो
Mozilla के Gecko रेंडरिंग इंजन का मुख्य फ़्लो.
चौथी इमेज: Mozilla के Gecko रेंडरिंग इंजन का मुख्य फ़्लो

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

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

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

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

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

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

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

व्याकरण

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

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

पार्सिंग को दो सब-प्रोसेस में बांटा जा सकता है: लेक्सिकल एनालिसिस और सिंटैक्स एनालिसिस.

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

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

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

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

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

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

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

अनुवाद

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

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

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

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

सिंटैक्स:

  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

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

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

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

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

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

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

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

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

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

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

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

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

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

एचटीएमएल ग्रामर

एचटीएमएल की शब्दावली और सिंटैक्स को W3C संगठन की ओर से बनाई गई खास जानकारी में बताया गया है.

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

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

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

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

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

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

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

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

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

DOM

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

ट्री का रूट, "Document" ऑब्जेक्ट होता है.

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

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

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

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

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

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

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

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

इसकी ये वजहें हैं:

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

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

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

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

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

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

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

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

सामान्य उदाहरण - इस एचटीएमएल को टोकनाइज़ करना:

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

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

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

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

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

ट्री कंस्ट्रक्शन एल्गोरिदम

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

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

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

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

स्टेट को बदलकर "before head" कर दिया जाएगा. इसके बाद, "बॉडी" टोकन मिलता है. एक HTMLHeadElement अपने-आप बन जाएगा. भले ही, हमारे पास "head" टोकन न हो. साथ ही, इसे ट्री में जोड़ दिया जाएगा.

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

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

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

उदाहरण के तौर पर दिए गए एचटीएमएल का ट्री कंस्ट्रक्शन.
इमेज 11: उदाहरण के तौर पर दिए गए एचटीएमएल का ट्री कंस्ट्रक्शन

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

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

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 गड़बड़ी टॉलरेंस कोड स्निपेट में उदाहरण के तौर पर न दिखना हो - सही तरीके से एचटीएमएल लिखें.

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

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

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

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

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

"ident", आइडेंटिफ़ायर का छोटा नाम है. जैसे, क्लास का नाम. "name" एक एलिमेंट आईडी है (इसे "#" से रेफ़र किया जाता है)

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

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 का मतलब व्हाइट स्पेस है. नियमों के सेट में कर्ली ब्रैकेट होते हैं. इनके अंदर एक एलान होता है या फिर सेमीकोलन से अलग किए गए कई एलान होते हैं. "declaration" और "selector" को यहां दी गई बीएनएफ़ परिभाषाओं में बताया जाएगा.

WebKit CSS parser

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

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

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

स्क्रिप्ट

वेब का मॉडल सिंक्रोनस है. लेखकों को उम्मीद होती है कि जब पार्सर <script> टैग पर पहुंचेगा, तब स्क्रिप्ट को तुरंत पार्स और लागू किया जाएगा. स्क्रिप्ट के लागू होने तक, दस्तावेज़ को पार्स करने की प्रोसेस रुक जाती है. अगर स्क्रिप्ट बाहरी है, तो संसाधन को पहले नेटवर्क से फ़ेच करना होगा. यह काम भी सिंक्रोनस तरीके से किया जाता है. साथ ही, संसाधन फ़ेच होने तक पार्स करने की प्रोसेस रुक जाती है. यह मॉडल कई सालों तक इस्तेमाल किया गया. साथ ही, इसे एचटीएमएल4 और 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
}

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

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

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

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

    return o;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    div div div div{
    ...
    }
    

    इसका मतलब है कि नियम उस <div> पर लागू होते हैं जो तीन div का डिसेंडेंट है. मान लें कि आपको यह देखना है कि नियम, दिए गए <div> एलिमेंट पर लागू होता है या नहीं. जांच करने के लिए, ट्री में कोई पाथ चुना जाता है. आपको नोड ट्री को ऊपर की ओर ट्रैवर्स करना पड़ सकता है, ताकि यह पता चल सके कि सिर्फ़ दो 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: Firefox स्टाइल कॉन्टेक्स्ट ट्री.

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

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

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

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

मान लें कि हमें कॉन्टेंट ट्री में किसी दूसरे एलिमेंट के लिए नियमों का मिलान करना है. साथ ही, हमें पता चलता है कि मैच होने वाले नियम (सही क्रम में) B-E-I हैं. यह पाथ ट्री में पहले से मौजूद है, क्योंकि हमने पहले ही 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 एलिमेंट के लिए नियम ढूंढने की कोशिश करेंगे. क्लास मैप में "error" कुंजी होगी. इसमें "p.error" का नियम मौजूद होगा. div एलिमेंट में, आईडी मैप (कुंजी, आईडी है) और टैग मैप में काम के नियम होंगे. इसलिए, अब सिर्फ़ यह पता लगाना बाकी है कि कुंजियों से निकाले गए नियमों में से कौनसे नियम वाकई मैच करते हैं.

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

table div {margin: 5px}

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

WebKit और Firefox, दोनों में यह बदलाव किया जाता है.

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

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

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

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

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

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

खासियत

सीएसएस2 की खास जानकारी में, सिलेक्टर की खास जानकारी इस तरह दी गई है:

  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 होती है और इसके डाइमेंशन व्यूपोर्ट होते हैं. व्यूपोर्ट, ब्राउज़र विंडो का वह हिस्सा होता है जो दिखता है.

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

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

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

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

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

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

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

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

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

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

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

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

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

आम तौर पर, ग्लोबल लेआउट एक साथ ट्रिगर होगा.

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

ऑप्टिमाइज़ेशन

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

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

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

आम तौर पर, लेआउट का पैटर्न ऐसा होता है:

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

Firefox, लेआउट के लिए पैरामीटर के तौर पर "स्टेट" ऑब्जेक्ट (nsHTMLReflowState) का इस्तेमाल करता है. इसे "रीफ़्लो" कहा जाता है. अन्य चीज़ों के अलावा, इस स्थिति में पैरंट की चौड़ाई भी शामिल होती है.

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

चौड़ाई का कैलकुलेशन

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

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

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

WebKit, इसे इस तरह कैलकुलेट करेगा(क्लास RenderBox मेथड calcWidth):

  • कंटेनर की चौड़ाई, कंटेनर की availableWidth और 0 में से जो भी ज़्यादा हो वह होती है. इस मामले में, availableWidth ही contentWidth है. इसे इस तरह कैलकुलेट किया जाता है:
clientWidth() - paddingLeft() - paddingRight()

clientWidth और clientHeight, किसी ऑब्जेक्ट के अंदरूनी हिस्से को दिखाते हैं. इसमें बॉर्डर और स्क्रोलबार शामिल नहीं होते.

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

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

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

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

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

लाइन ब्रेक करना

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

पेंटिंग

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

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

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

पेंटिंग का क्रम

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

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

Firefox की डिसप्ले लिस्ट

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

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

Firefox, उन एलिमेंट को न जोड़कर प्रोसेस को ऑप्टिमाइज़ करता है जो छिपे रहेंगे. जैसे, पूरी तरह से अपारदर्शी एलिमेंट के नीचे मौजूद एलिमेंट.

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

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

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

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

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

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

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

इवेंट लूप

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

while (!mExiting)
    NS_ProcessNextEvent(thread);

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

कैनवस

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

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

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. ऐब्सलूट: ऑब्जेक्ट को रेंडर ट्री में, डीओएम ट्री से अलग जगह पर रखा जाता है

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

  • static और relative, सामान्य फ़्लो बनाते हैं
  • absolute and fixed cause absolute positioning

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

बॉक्स का लेआउट इन बातों से तय होता है:

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

बॉक्स के टाइप

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

ब्लॉक बॉक्स.
इमेज 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>

यह कुछ ऐसा दिखेगा:

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

एब्सोलूट और फ़िक्स्ड

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

फ़िक्स्ड पोज़िशनिंग.
इमेज 26: फ़िक्स्ड पोज़िशनिंग

लेयर के हिसाब से जानकारी दिखाना

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

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

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

उदाहरण:

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

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

इससे यह नतीजा मिलेगा:

फ़िक्स्ड पोज़िशनिंग.
इमेज 27: तय की गई पोज़िशनिंग

मार्कअप में लाल div, हरे div से पहले आता है और सामान्य फ़्लो में पहले पेंट किया जाता. हालांकि, z-index प्रॉपर्टी ज़्यादा होने की वजह से, यह रूट बॉक्स के स्टैक में आगे है.

संसाधन

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

    1. Grosskurth, Alan. A Reference Architecture for Web Browsers (pdf)
    2. गुप्ता, विनीत. ब्राउज़र कैसे काम करते हैं - पहला भाग - आर्किटेक्चर
  2. पार्सिंग

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

    1. एल॰ डेविड बैरन, Faster HTML and CSS: Layout Engine Internals for Web Developers.
    2. एल॰ डेविड बैरन, Faster HTML and CSS: Layout Engine Internals for Web Developers (Google tech talk video)
    3. एल॰ डेविड बैरन, Mozilla का लेआउट इंजन
    4. एल॰ डेविड बैरन, Mozilla Style System Documentation
    5. क्रिस वॉटरसन, Notes on HTML Reflow
    6. Gecko Overview, क्रिस वॉटरसन
    7. Alexander Larsson, The life of an HTML HTTP request
  4. WebKit

    1. डेविड हयात, Implementing CSS(part 1)
    2. डेविड हयात, वेबकोर की खास जानकारी
    3. डेविड हयात, WebCore रेंडरिंग
    4. डेविड हयात, The FOUC Problem
  5. W3C की खास बातें

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

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

अनुवाद

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

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

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