आधुनिक वेब ब्राउज़र के पर्दे के पीछे
भूमिका
WebKit और Gecko के अंदरूनी कामकाज के बारे में पूरी जानकारी देने वाली यह जानकारी इज़रायली डेवलपर ताली गार्सिएल की रिसर्च से मिले नतीजे. कुछ ज़्यादा सालों तक, उसने ब्राउज़र के इंटरनल वर्शन के बारे में पब्लिश किए गए सारे डेटा की समीक्षा की और वेब ब्राउज़र के सोर्स कोड को पढ़ने में बहुत ज़्यादा समय लगता है. उन्होंने लिखा:
वेब डेवलपर के तौर पर, ब्राउज़र की कार्रवाइयों के बारे में जानना इससे आपको बेहतर फ़ैसले लेने में मदद मिलती है. साथ ही, आपको डेवलपमेंट के पीछे की वजहें भी पता चलती हैं सबसे सही तरीके के बारे में जानें. यह दस्तावेज़ काफ़ी लंबा है, लेकिन हमारा सुझाव है कि चीज़ें समझने में कुछ समय बीत जाता है. आपको खुशी होगी.
पॉल आइरिश, Chrome Developer Relations
परिचय
वेब ब्राउज़र सबसे ज़्यादा इस्तेमाल किए जाने वाले सॉफ़्टवेयर हैं. इस प्राइमर में, मैंने बताया है कि
पर्दे के पीछे रहकर काम किया जाता है. हम देखेंगे कि google.com
टाइप करने पर क्या होता है
को तब तक दबाएं, जब तक आपको ब्राउज़र स्क्रीन पर Google पेज न दिखाई दे.
वे ब्राउज़र जिन पर हम बात करेंगे
फ़िलहाल डेस्कटॉप पर पांच मुख्य ब्राउज़र का इस्तेमाल किया जाता है: 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 (वर्ल्ड वाइड वेब कंसोर्टियम) संगठन करता है, जो वेब के लिए मानक संगठन है. वर्षों तक ब्राउज़र विनिर्देशों का पालन करते थे और अपने एक्सटेंशन विकसित करते थे. इससे वेब लेखकों के साथ काम करने से जुड़ी गंभीर समस्याएं पैदा हुई थीं. आजकल ज़्यादातर ब्राउज़र विनिर्देशों का पालन करते हैं या फिर कम.
ब्राउज़र के यूज़र इंटरफ़ेस में कई चीज़ें एक जैसी हैं. यूज़र इंटरफ़ेस में ये एलिमेंट होते हैं:
- यूआरआई डालने के लिए पता बार
- 'वापस जाएं' और 'आगे जाएं' बटन
- बुकमार्क करने के विकल्प
- मौजूदा दस्तावेज़ों को लोड होने से रोकने या रीफ़्रेश करने के लिए, रीफ़्रेश और बंद करने वाले बटन
- होम बटन, जो आपको अपने होम पेज पर ले जाता है
हैरान करने वाली बात यह है कि इस ब्राउज़र का यूज़र इंटरफ़ेस किसी भी औपचारिक जानकारी में नहीं बताया गया है. यह सिर्फ़ पिछले कई सालों के अनुभव के आधार पर तैयार किए गए अच्छे तरीकों और ब्राउज़र की एक-दूसरे की नकल करने से आता है. HTML5 स्पेसिफ़िकेशन में ऐसे यूज़र इंटरफ़ेस (यूआई) एलिमेंट तय नहीं किए गए हैं जो ब्राउज़र में होने चाहिए. हालांकि, इसमें कुछ सामान्य एलिमेंट शामिल किए गए हैं. इनमें पता बार, स्टेटस बार, और टूल बार शामिल होते हैं. निश्चित रूप से, Firefox के डाउनलोड मैनेजर जैसे किसी विशेष ब्राउज़र में विशिष्ट सुविधाएँ होती हैं.
बेहतरीन इन्फ़्रास्ट्रक्चर
ब्राउज़र के मुख्य कॉम्पोनेंट ये हैं:
- यूज़र इंटरफ़ेस: इसमें पता बार, बैक/फ़ॉरवर्ड बटन, बुकमार्क करने वाला मेन्यू वगैरह शामिल होते हैं. इस सेटिंग में, उस विंडो को छोड़कर ब्राउज़र का हर हिस्सा दिखता है जहां आपको अनुरोध किया गया पेज दिखता है.
- ब्राउज़र इंजन: यह यूज़र इंटरफ़ेस (यूआई) और रेंडरिंग इंजन के बीच, मार्शल आर्ट का इस्तेमाल करता है.
- रेंडरिंग इंजन: अनुरोध किए गए कॉन्टेंट को दिखाने की ज़िम्मेदारी. उदाहरण के लिए, अगर अनुरोध किया गया कॉन्टेंट एचटीएमएल है, तो रेंडरिंग इंजन, एचटीएमएल और सीएसएस को पार्स करता है और पार्स किया गया कॉन्टेंट स्क्रीन पर दिखाता है.
- नेटवर्किंग: इसका इस्तेमाल, नेटवर्क कॉल के लिए किया जाता है. जैसे, एचटीटीपी अनुरोध. इसके लिए, प्लैटफ़ॉर्म-इंडिपेंडेंट इंटरफ़ेस के पीछे अलग-अलग प्लैटफ़ॉर्म के लिए अलग-अलग तरीकों का इस्तेमाल किया जाता है.
- यूज़र इंटरफ़ेस (यूआई) बैकएंड: इसका इस्तेमाल कॉम्बो बॉक्स और विंडो जैसे बेसिक विजेट बनाने के लिए किया जाता है. यह बैकएंड एक ऐसा सामान्य इंटरफ़ेस दिखाता है जो प्लैटफ़ॉर्म के हिसाब से नहीं होता. इसके नीचे, यह ऑपरेटिंग सिस्टम के यूज़र इंटरफ़ेस के तरीकों का इस्तेमाल करता है.
- JavaScript का अनुवादक. इसका इस्तेमाल JavaScript कोड को पार्स और एक्ज़ीक्यूट करने के लिए किया जाता है.
- डेटा स्टोरेज. यह एक परसिस्टेंस लेयर है. ब्राउज़र को सभी तरह के डेटा, जैसे कि कुकी को स्थानीय तौर पर सेव करना पड़ सकता है. ब्राउज़र, localStorage, IndexedDB, WebSQL, और FileSystem जैसी स्टोरेज की सुविधाओं के साथ भी काम करते हैं.
यह ध्यान रखना ज़रूरी है कि Chrome जैसे ब्राउज़र में रेंडरिंग इंजन के कई इंस्टेंस चलते हैं: हर टैब के लिए एक इंस्टेंस. हर टैब एक अलग प्रोसेस में चलता है.
रेंडर करने वाले इंजन
रेंडरिंग इंजन की ज़िम्मेदारी अच्छी तरह से है... रेंडर करना, यानी ब्राउज़र की स्क्रीन पर अनुरोध किए गए कॉन्टेंट को दिखाना.
डिफ़ॉल्ट रूप से, रेंडरिंग इंजन एचटीएमएल और एक्सएमएल दस्तावेज़ और इमेज दिखा सकता है. यह प्लग-इन या एक्सटेंशन के ज़रिए, दूसरी तरह का डेटा दिखा सकता है; उदाहरण के लिए, PDF व्यूअर प्लग-इन का इस्तेमाल करके PDF दस्तावेज़ दिखाना. हालांकि, इस चैप्टर में हम इस्तेमाल के मुख्य उदाहरण पर फ़ोकस करेंगे: सीएसएस का इस्तेमाल करके फ़ॉर्मैट किए गए एचटीएमएल और इमेज को दिखाना.
अलग-अलग ब्राउज़र में अलग-अलग रेंडरिंग इंजन का इस्तेमाल होता है: Internet Explorer में ट्राइडेंट, Firefox, Gecko का, और Safari में WebKit का इस्तेमाल करता है. Chrome और Opera (वर्शन 15 से), Blink का इस्तेमाल करते हैं, जो WebKit का एक फ़ोर्क है.
WebKit एक ओपन सोर्स रेंडरिंग इंजन है, जिसकी शुरुआत Linux प्लैटफ़ॉर्म के लिए इंजन के तौर पर हुई थी. Mac और Windows पर काम करने के लिए, Apple ने इसमें बदलाव किया था.
मेन फ़्लो
रेंडरिंग इंजन को, अनुरोध किए गए दस्तावेज़ का कॉन्टेंट मिलना शुरू हो जाएगा से कर सकती है. यह काम आम तौर पर 8kB के हिस्से में होता है.
इसके बाद, यह रेंडरिंग इंजन का बुनियादी फ़्लो है:
रेंडरिंग इंजन, एचटीएमएल दस्तावेज़ को पार्स करना शुरू कर देगा. साथ ही, एलिमेंट को "कॉन्टेंट ट्री" नाम के ट्री में DOM नोड में बदल देगा. इंजन, बाहरी सीएसएस फ़ाइलों और स्टाइल एलिमेंट, दोनों में स्टाइल डेटा को पार्स करेगा. एचटीएमएल में विज़ुअल निर्देशों के साथ जानकारी को स्टाइल करने का तरीका, दूसरा ट्री बनाने के लिए इस्तेमाल किया जाएगा: रेंडर ट्री.
रेंडर ट्री में रंग और डाइमेंशन जैसे विज़ुअल एट्रिब्यूट वाले रेक्टैंगल होते हैं. रेक्टैंगल, स्क्रीन पर सही क्रम में दिखाए जा रहे हैं.
रेंडर ट्री बनाने के बाद, यह "लेआउट" से होकर गुज़रता है प्रोसेस. इसका मतलब है कि हर नोड को स्क्रीन पर दिखने वाले निर्देशांकों की सटीक जानकारी देना. अगला चरण पेंट करना है - रेंडर ट्री को दिखाया जाएगा और हर नोड को यूज़र इंटरफ़ेस (यूआई) बैकएंड लेयर का इस्तेमाल करके पेंट किया जाएगा.
यह समझना ज़रूरी है कि यह प्रोसेस धीरे-धीरे होती है. बेहतर उपयोगकर्ता अनुभव के लिए, रेंडरिंग इंजन जितनी जल्दी हो सके स्क्रीन पर कॉन्टेंट दिखाने की कोशिश करेगा. रेंडर ट्री बनाने और उसका लेआउट बनाने से पहले, सभी एचटीएमएल को पार्स कर लेने तक इंतज़ार नहीं किया जा सकता. कॉन्टेंट के कुछ हिस्सों को पार्स किया जाएगा और दिखाया जाएगा. हालांकि, यह प्रोसेस, नेटवर्क से बार-बार आने वाले बाकी कॉन्टेंट के साथ भी जारी रहेगी.
मेन फ़्लो के उदाहरण
तीसरी और चौथी इमेज में देखा जा सकता है कि WebKit और Gecko में थोड़ी अलग शब्दावली का इस्तेमाल किया जाता है, लेकिन इनका फ़्लो बुनियादी तौर पर एक जैसा ही है.
गेको, विज़ुअल तौर पर फ़ॉर्मैट किए गए एलिमेंट के ट्री को "फ़्रेम ट्री" कहता है. हर एलिमेंट एक फ़्रेम होता है. WebKit, "रेंडर ट्री" शब्द का उपयोग करता है और इसमें "रेंडर ऑब्जेक्ट" शामिल होता है. WebKit, "लेआउट" शब्द का इस्तेमाल करता है एलिमेंट को सही जगह पर रखने के मामले में, गेको इसे "रीफ़्लो" कहते हैं. "अटैचमेंट" रेंडर ट्री बनाने के लिए, डीओएम नोड और विज़ुअल जानकारी को कनेक्ट करने के लिए WebKit का इस्तेमाल किया जाता है. बिना मतलब वाला छोटा सा अंतर यह है कि गेको के एचटीएमएल और डीओएम ट्री के बीच एक अतिरिक्त लेयर होती है. इसे "कॉन्टेंट सिंक" कहते हैं साथ ही, यह डीओएम एलिमेंट बनाने का फ़ैक्ट्री है. हम फ़्लो के हर हिस्से के बारे में बात करेंगे:
पार्स करना - सामान्य
रेंडरिंग इंजन में पार्स करना एक बहुत अहम प्रोसेस है. इसलिए, हम इस पर थोड़ा और गहराई से बात करेंगे. आइए, पार्स करने के बारे में कम शब्दों में जानकारी देते हैं.
किसी दस्तावेज़ को पार्स करने का मतलब है कि उसे किसी ऐसे स्ट्रक्चर में अनुवाद करना जिसे कोड इस्तेमाल कर सके. पार्स करने का नतीजा आम तौर पर नोड का एक ट्री होता है जो दस्तावेज़ की बनावट को दिखाता है. इसे पार्स ट्री या सिंटैक्स ट्री कहा जाता है.
उदाहरण के लिए, 2 + 3 - 1
एक्सप्रेशन को पार्स करने से यह ट्री वापस आ सकता है:
व्याकरण
पार्स करने की प्रक्रिया, दस्तावेज़ के लिए इस्तेमाल होने वाले सिंटैक्स के नियमों पर आधारित होती है. जैसे, उस भाषा या फ़ॉर्मैट में जिसे दस्तावेज़ में लिखा गया था. जिस फ़ॉर्मैट को पार्स किया जा सकता है उसमें शब्दावली और सिंटैक्स के नियम शामिल होने चाहिए. इसे कहा जाता है कॉन्टेक्स्ट के बिना व्याकरण का इस्तेमाल करना. इंसान की भाषाएं ऐसी भाषाएं नहीं हैं, इसलिए पारंपरिक पार्स करने की तकनीकों की मदद से इसे पार्स नहीं किया जा सकता.
पार्सर - लेक्सर कॉम्बिनेशन
पार्स करने की प्रक्रिया को दो सब-प्रोसेस में बांटा जा सकता है: लेक्सिकल विश्लेषण और सिंटैक्स विश्लेषण.
लेक्सिकल ऐनलिसिस, इनपुट को टोकन में बांटने की प्रोसेस है. टोकन, भाषा से जुड़े शब्द हैं: मान्य बिल्डिंग ब्लॉक का कलेक्शन. मानवीय भाषा में, इसमें उस भाषा के शब्दकोश में दिखने वाले सभी शब्द शामिल होंगे.
सिंटैक्स विश्लेषण का मतलब है, लैंग्वेज सिंटैक्स के नियमों को लागू करना.
आम तौर पर, पार्सर डेटा को दो कॉम्पोनेंट में बांटते हैं: lexer (इसे कभी-कभी टोकनाइज़र भी कहा जाता है) जो इनपुट को मान्य टोकन में बांटने की ज़िम्मेदारी होती है. वहीं, पार्सर, लैंग्वेज सिंटैक्स के नियमों के मुताबिक दस्तावेज़ के स्ट्रक्चर का विश्लेषण करके, पार्स ट्री बनाने का काम करता है.
लेक्सर को पता है कि खाली जगह और लाइन ब्रेक जैसे गै़र-ज़रूरी वर्णों को कैसे हटाना है.
पार्स करने की प्रोसेस बार-बार की जाती है. आम तौर पर, पार्सर लेक्सर से नया टोकन मांगेगा और टोकन का मिलान सिंटैक्स के किसी एक नियम से करने की कोशिश करेगा. अगर किसी नियम का मिलान होता है, तो टोकन से संबंधित नोड को पार्स ट्री में जोड़ दिया जाएगा और पार्सर किसी दूसरे टोकन की मांग करेगा.
अगर कोई नियम मैच नहीं करता है, तो पार्सर अंदरूनी तौर पर टोकन को सेव करेगा और तब तक टोकन के लिए पूछता रहेगा, जब तक कि अंदरूनी तौर पर स्टोर किए गए सभी टोकन से मेल खाने वाला कोई नियम नहीं मिल जाता. अगर कोई नियम नहीं मिलता है, तो पार्सर अपवाद डाल सकता है. इसका मतलब है कि दस्तावेज़ मान्य नहीं था और उसमें सिंटैक्स की गड़बड़ियां थीं.
अनुवाद
कई मामलों में पार्स ट्री फ़ाइनल प्रॉडक्ट नहीं होता. पार्सिंग का इस्तेमाल अक्सर अनुवाद में किया जाता है: इनपुट दस्तावेज़ को किसी दूसरे फ़ॉर्मैट में बदलना. कंपाइलेशन का एक उदाहरण है. सोर्स कोड को मशीन कोड में कंपाइल करने वाला कंपाइलर सबसे पहले उसे पार्स ट्री में पार्स करता है. इसके बाद, ट्री को मशीन कोड दस्तावेज़ में बदल देता है.
पार्स करने का उदाहरण
पांचवीं इमेज में हमने गणित के एक्सप्रेशन की मदद से एक पार्स ट्री बनाया है. आइए, गणित के हिसाब से आसान भाषा का इस्तेमाल करते हैं और पार्स करने की प्रोसेस देखते हैं.
सिंटैक्स:
- लैंग्वेज सिंटैक्स बिल्डिंग ब्लॉक में एक्सप्रेशन, टर्म, और ऑपरेशन होते हैं.
- हमारी भाषा में कितने भी हाव-भाव हो सकते हैं.
- एक्सप्रेशन को "शब्द" के तौर पर परिभाषित किया जाता है उसके बाद "कार्रवाई" इसके बाद दूसरा शब्द
- ऑपरेशन प्लस टोकन या माइनस टोकन होता है
- टर्म एक इंटीजर टोकन या एक्सप्रेशन होता है
आइए, इनपुट 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
की पहचान एक एक्सप्रेशन के रूप में करेगा (एक्सप्रेशन की पहचान करने की प्रोसेस बढ़ती है और अन्य नियमों का मिलान किया जाता है, लेकिन शुरुआती पॉइंट सबसे ऊपर का नियम होता है).
बॉटम अप पार्सर, नियम के मैच होने तक इनपुट को स्कैन करेगा. इसके बाद, यह मैच करने वाले इनपुट को नियम से बदल देगा. यह इनपुट के आखिर तक चलता रहेगा. आंशिक रूप से मैच होने वाला एक्सप्रेशन, पार्सर के स्टैक पर रखा जाता है.
इस तरह के बॉटम अप पार्सर को shift-रिड्यूस पार्सर कहा जाता है, क्योंकि इनपुट को दाईं ओर शिफ़्ट कर दिया जाता है (मान लें कि कोई पॉइंटर पहले इनपुट स्टार्ट की ओर जाकर दाईं ओर ले जाता हो) और धीरे-धीरे इसे सिंटैक्स के नियमों तक सीमित कर दिया जाता है.
पार्सर अपने-आप जनरेट होना
कुछ टूल से पार्सर जनरेट किया जा सकता है. आप उन्हें अपनी भाषा का व्याकरण - उसकी शब्दावली और वाक्य संरचना - फ़ीड करें और वे एक काम करने वाला पार्सर जनरेट करें. पार्सर बनाने के लिए, पार्सर की अच्छी समझ होना ज़रूरी है. साथ ही, खुद से ऑप्टिमाइज़ किया गया पार्सर बनाना आसान नहीं है. इसलिए, पार्सर जनरेटर बहुत काम आ सकते हैं.
WebKit दो जाने-माने पार्सर जनरेटर का इस्तेमाल करता है: लेक्सर बनाने के लिए Flex और पार्सर बनाने के लिए Bison (आप इनमें Lex और Yacc जैसे नाम चला सकते हैं). Flex इनपुट ऐसी फ़ाइल है जिसमें टोकन के रेगुलर एक्सप्रेशन की परिभाषाएं होती हैं. बाइसन का इनपुट, बीएनएफ़ फ़ॉर्मैट में भाषा के सिंटैक्स के नियम है.
एचटीएमएल पार्सर
एचटीएमएल पार्सर का काम एचटीएमएल मार्कअप को पार्स ट्री में पार्स करना होता है.
एचटीएमएल व्याकरण
एचटीएमएल का शब्दावली और सिंटैक्स, W3C संगठन की ओर से तैयार किए गए स्पेसिफ़िकेशन में बताया गया है.
जैसा कि हमने पार्स करने की शुरुआत में देखा है, व्याकरण सिंटैक्स को बीएनएफ़ जैसे फ़ॉर्मैट का इस्तेमाल करके औपचारिक तौर पर तय किया जा सकता है.
माफ़ करें, सभी कंवेंशनल पार्सर विषय, एचटीएमएल पर लागू नहीं होते हैं (मैंने इन्हें सिर्फ़ मनोरंजन के लिए नहीं लाया था - इनका इस्तेमाल सीएसएस और JavaScript को पार्स करने में किया जाएगा). एचटीएमएल को बिना कॉन्टेक्स्ट के उस व्याकरण के ज़रिए आसानी से परिभाषित नहीं किया जा सकता जिसकी पार्सर को ज़रूरत होती है.
HTML परिभाषित करने के लिए औपचारिक प्रारूप है - DTD (दस्तावेज़ प्रकार परिभाषा) - लेकिन यह संदर्भ-मुक्त व्याकरण नहीं है.
पहली बार में यह अजीब लगता है; एचटीएमएल, एक्सएमएल के काफ़ी करीब है. बहुत सारे एक्सएमएल पार्सर उपलब्ध हैं. एचटीएमएल - एक्सटर्नल में एक्सएमएल वैरिएशन है - तो इनमें बड़ा अंतर क्या है?
इसमें अंतर यह है कि एचटीएमएल वाला तरीका ज़्यादा "माफ़ करना" होता है: इसकी मदद से, कुछ टैग हटाए जा सकते हैं, जैसे कि बाद में उन्हें सीधे तौर पर जोड़ दिया जाता है. इसके अलावा, कभी-कभी स्टार्ट या एंड टैग को छोड़ दिया जाता है. इसी तरह अन्य टैग लागू हो जाते हैं. कुल मिलाकर यह "सॉफ़्ट" है का इस्तेमाल किया जा सकता है.
देखने में छोटी सी बात लगने से, दुनिया अलग-अलग हो जाती है. एक ओर यह इसका मुख्य कारण है कि HTML इतना लोकप्रिय क्यों है: यह आपकी गलतियों को क्षमा कर देता है और वेब लेखक के लिए जीवन को आसान बनाता है. दूसरी ओर, औपचारिक व्याकरण लिखना मुश्किल हो जाता है. इसलिए, संक्षेप में बताएं कि एचटीएमएल को कंवेंशनल पार्सर के ज़रिए आसानी से पार्स नहीं किया जा सकता, क्योंकि इसका व्याकरण बिना कॉन्टेक्स्ट के नहीं है. एचटीएमएल को एक्सएमएल पार्सर से पार्स नहीं किया जा सकता.
एचटीएमएल डीटीडी
एचटीएमएल की परिभाषा DTD फ़ॉर्मैट में होती है. इस फ़ॉर्मैट का इस्तेमाल, SGML परिवार की भाषाएं तय करने के लिए किया जाता है. फ़ॉर्मैट में, अनुमति वाले सभी एलिमेंट, उनकी एट्रिब्यूट, और हैरारकी की जानकारी शामिल होती है. जैसा कि हमने पहले देखा, HTML DTD संदर्भ मुक्त व्याकरण नहीं बनाता.
डीटीडी के कुछ वैरिएशन हैं. सख्त मोड सिर्फ़ निर्देशों के मुताबिक है, लेकिन दूसरे मोड में भी पहले ब्राउज़र इस्तेमाल किए गए मार्कअप का इस्तेमाल किया जा सकता है. इसकी वजह यह है कि पुराने वीडियो के साथ काम करने की सुविधा, पुराने सिस्टम के साथ काम करती है. मौजूदा सख्त DTD यहां है: www.w3.org/TR/html4/strict.dtd
डीओएम
आउटपुट ट्री ("पार्स ट्री") डीओएम एलिमेंट और एट्रिब्यूट नोड का एक ट्री है. डॉक्यूमेंट ऑब्जेक्ट मॉडल के लिए डीओएम छोटा होता है. यह HTML दस्तावेज़ की ऑब्जेक्ट प्रज़ेंटेशन और बाहर की दुनिया के लिए एचटीएमएल एलिमेंट का इंटरफ़ेस है, जैसे कि JavaScript.
ट्री का रूट "दस्तावेज़" है ऑब्जेक्ट है.
मार्कअप से डीओएम का करीब-करीब वन-टू-वन संबंध होता है. उदाहरण के लिए:
<html>
<body>
<p>
Hello World
</p>
<div> <img src="example.png"/></div>
</body>
</html>
इस मार्कअप का अनुवाद नीचे दिए गए डीओएम ट्री में किया जाएगा:
एचटीएमएल की तरह ही, DOM को W3C संगठन भी तय करता है. www.w3.org/DOM/DOMTR देखें. यह दस्तावेज़ों में हेर-फेर करने के लिए एक सामान्य नियम है. एक खास मॉड्यूल, एचटीएमएल से जुड़े एलिमेंट के बारे में बताता है. एचटीएमएल की परिभाषाएं यहां दी गई हैं: www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html.
जब मेरे कहने का मतलब है कि ट्री में DOM नोड हैं, तो मेरा मतलब है कि ट्री ऐसे एलिमेंट से बना है जो किसी एक DOM इंटरफ़ेस को लागू करते हैं. ब्राउज़र, इंटिग्रिटी प्रोटेक्शन की सुविधा का इस्तेमाल करते हैं. इसमें ऐसे अन्य एट्रिब्यूट भी होते हैं जिनका इस्तेमाल ब्राउज़र, अंदरूनी तौर पर करता है.
पार्स करने का एल्गोरिदम
जैसा कि हमने पिछले सेक्शन में देखा था, एचटीएमएल को सामान्य टॉप डाउन या बॉटम अप पार्सर का इस्तेमाल करके पार्स नहीं किया जा सकता.
इसकी वजहें ये हैं:
- भाषा की माफ़ी.
- यह तथ्य कि ब्राउज़र में अमान्य HTML के जाने-पहचाने मामलों में सहायता करने के लिए पारंपरिक गड़बड़ी सहनशीलता होती है.
- पार्स करने की प्रोसेस फिर से शामिल की गई है. दूसरी भाषाओं के लिए, पार्स करने के दौरान सोर्स नहीं बदलता. हालांकि, एचटीएमएल में डाइनैमिक कोड (जैसे कि
document.write()
कॉल वाले स्क्रिप्ट एलिमेंट) में ज़्यादा टोकन जुड़ सकते हैं. इसलिए, पार्स करने की प्रोसेस, इनपुट में बदलाव कर देती है.
सामान्य पार्स करने की तकनीकों का इस्तेमाल नहीं किया जा सका. ब्राउज़र, एचटीएमएल को पार्स करने के लिए कस्टम पार्सर बनाते हैं.
पार्सिंग एल्गोरिदम के बारे में HTML5 स्पेसिफ़िकेशन में बताया गया है. एल्गोरिदम के दो चरण होते हैं: टोकनाइज़ेशन और ट्री बनाना.
टोकनाइज़ेशन एक लेक्सिकल विश्लेषण है, जो इनपुट को टोकन में पार्स करता है. एचटीएमएल टोकन में स्टार्ट टैग, एंड टैग, एट्रिब्यूट के नाम, और एट्रिब्यूट की वैल्यू शामिल होती हैं.
टोकनाइज़र, टोकन की पहचान करता है और उसे ट्री कंस्ट्रक्टर को देता है. साथ ही, अगले टोकन की पहचान करने के लिए, अगले वर्ण का इस्तेमाल करता है. ऐसा इनपुट के आखिर तक होता है.
टोकनाइज़ेशन का एल्गोरिदम
एल्गोरिदम का आउटपुट एक एचटीएमएल टोकन होता है. इस एल्गोरिदम को एक स्टेट मशीन के तौर पर दिखाया जाता है. हर राज्य, इनपुट स्ट्रीम के एक या उससे ज़्यादा वर्णों का इस्तेमाल करता है और उन वर्णों के हिसाब से अगली स्थिति को अपडेट करता है. इस फ़ैसले पर, टोकन की मौजूदा स्थिति और ट्री कंस्ट्रक्शन की स्थिति का असर पड़ता है. इसका मतलब है कि इस्तेमाल किया गया एक ही वर्ण, मौजूदा स्थिति के आधार पर सही अगली स्थिति के लिए अलग-अलग नतीजे देगा. पूरी तरह से समझाने के लिए एल्गोरिदम बहुत जटिल है, इसलिए आइए एक आसान उदाहरण देखते हैं. इससे हमें सिद्धांत को समझने में मदद मिलेगी.
बुनियादी उदाहरण - इस एचटीएमएल को टोकन देना:
<html>
<body>
Hello world
</body>
</html>
शुरुआती स्थिति "डेटा की स्थिति" होती है.
जब <
वर्ण मिलता है, तो स्टेटस को "टैग के खुले होने की स्थिति" में बदल दिया जाता है.
a-z
वर्ण का इस्तेमाल करने से "टैग टोकन शुरू करें" बन जाता है. इस स्थिति को बदलकर "टैग के नाम की स्थिति" हो जाता है.
>
वर्ण खत्म होने तक, हम इस स्थिति में बने रहते हैं. हर वर्ण को नए टोकन के नाम में जोड़ा जाता है. हमारे मामले में, बनाया गया टोकन html
टोकन है.
जब >
टैग तक पहुंचा जाता है, तो मौजूदा टोकन ट्रिगर हो जाता है. इसके बाद, स्टेटस वापस "डेटा की स्थिति" में बदल जाता है.
<body>
टैग के लिए भी यही तरीका इस्तेमाल किया जाएगा.
अब तक html
और body
टैग माइग्रेट हो चुके हैं. अब हम "डेटा की स्थिति" पर वापस आ गए हैं.
Hello world
के H
वर्ण का इस्तेमाल करने से कैरेक्टर टोकन बनाने और उसे निकालने की प्रोसेस तब तक जारी रहेगी, जब तक </body>
की <
पूरी नहीं हो जाती. हम Hello world
के हर वर्ण के लिए एक वर्ण टोकन छोड़ेंगे.
अब हम वापस "टैग चालू स्थिति" पर आ गए हैं.
अगले इनपुट /
का इस्तेमाल करने से, end tag token
बन जाएगा. साथ ही, "टैग के नाम की स्थिति" पर चला जाएगा. जब तक हम >
तक नहीं पहुंच जाते, तब तक हम इसी स्थिति में बने रहते हैं.इसके बाद, नया टैग टोकन बाहर आ जाएगा और हम वापस "डेटा की स्थिति" पर चले जाएंगे.
</html>
इनपुट को पिछले केस की तरह माना जाएगा.
पेड़ बनाने का एल्गोरिदम
पार्सर बनाए जाने पर, दस्तावेज़ ऑब्जेक्ट बन जाता है. ट्री बनाते समय, दस्तावेज़ के रूट में मौजूद डीओएम ट्री में बदलाव किया जाएगा और एलिमेंट जोड़े जाएंगे. टोकनाइज़र से निकलने वाले हर नोड को ट्री कंस्ट्रक्टर प्रोसेस करेगा. हर टोकन के लिए स्पेसिफ़िकेशन तय करता है कि कौनसा DOM एलिमेंट उसके लिए काम का है और उसे इस टोकन के लिए बनाया जाएगा. एलिमेंट को डीओएम ट्री में जोड़ा गया है. साथ ही, खुले एलिमेंट के स्टैक में भी जोड़ा गया है. इस स्टैक का इस्तेमाल, नेस्टिंग के मेल न खाने और ऐसे टैग को ठीक करने के लिए किया जाता है जो बंद नहीं हैं. इस एल्गोरिदम को एक स्टेट मशीन भी कहा जाता है. राज्यों को "इंसर्शन मोड" कहा जाता है.
इनपुट के लिए, आइए ट्री बनाने की प्रोसेस देखते हैं:
<html>
<body>
Hello world
</body>
</html>
ट्री कंस्ट्रक्शन स्टेज का इनपुट, टोकनाइज़ेशन स्टेज के टोकन का एक क्रम होता है. पहला मोड "शुरुआती मोड" है. "html" मिल रहा है टोकन की वजह से "html से पहले" मोड में चला जाएगा और उस मोड में टोकन को दोबारा प्रोसेस किया जा सकेगा. इससे HTMLhtmlElement एलिमेंट बनेगा, जिसे रूट Document ऑब्जेक्ट में जोड़ दिया जाएगा.
स्थिति "पहले हेड" में बदल जाएगी. "मुख्य हिस्सा" इसके बाद, टोकन मिलता है. एक HTML HeadElement मौजूद है, जो साफ़ तौर पर बनाया जाएगा. हालांकि, हमारे पास "head" नहीं है टोकन बनाया जाएगा और इसे ट्री में जोड़ दिया जाएगा.
अब हम "inhead" मोड पर और फिर "Afterhead" पर चले जाएंगे. बॉडी टोकन को दोबारा प्रोसेस किया जाता है, एक HTMLBodyElement बनाया जाता है और उसे शामिल किया जाता है, और मोड को "in body" में ट्रांसफ़र किया जाता है.
"नमस्ते वर्ल्ड" के कैरेक्टर टोकन स्ट्रिंग अब मिल गई है. पहले मॉड्यूल में, "टेक्स्ट" बनाया और शामिल किया जाएगा नोड में शामिल कर सकते हैं और अन्य वर्ण उस नोड में जोड़ दिए जाएंगे.
बॉडी एंड टोकन मिलने की वजह से " उपयोगकर्ता के बाद" मोड में ट्रांसफ़र हो जाएगा. अब हमें html एंड टैग मिलेगा, जिससे हम " after body" मोड में चले जाएंगे. फ़ाइल टोकन खत्म होने पर पार्स करने की प्रोसेस खत्म हो जाएगी.
पार्स करने की प्रोसेस पूरी होने के बाद की जाने वाली कार्रवाइयां
इस चरण में ब्राउज़र, दस्तावेज़ को इंटरैक्टिव के तौर पर मार्क करेगा और "स्थगित" वाली स्क्रिप्ट को पार्स करना शुरू करेगा मोड: वे जिन्हें दस्तावेज़ पार्स होने के बाद एक्ज़ीक्यूट किया जाना चाहिए. इसके बाद, दस्तावेज़ की स्थिति "पूरा हो गया" पर सेट हो जाएगी और "लोड" होने चाहिए इवेंट ट्रिगर होगा.
HTML5 स्पेसिफ़िकेशन में, टोकन और ट्री बनाने के लिए सभी एल्गोरिदम देखे जा सकते हैं.
ब्राउज़र गड़बड़ी पर डेटा का असर
आपको कभी भी "अमान्य सिंटैक्स" नहीं मिलता HTML पेज पर गड़बड़ी हुई है. ब्राउज़र किसी भी अमान्य कॉन्टेंट को ठीक करके आगे बढ़ जाते हैं.
इस एचटीएमएल का उदाहरण देखें:
<html>
<mytag>
</mytag>
<div>
<p>
</div>
Really lousy HTML
</p>
</html>
मैंने शायद लाखों नियमों का उल्लंघन किया है ("mytag" कोई स्टैंडर्ड टैग नहीं है, "p" और "div" एलिमेंट की गलत नेस्टिंग वगैरह) लेकिन ब्राउज़र इसे अब भी सही तरीके से दिखाता है और शिकायत नहीं करता. इसलिए बहुत से पार्सर कोड HTML लेखक की गलतियों को ठीक कर रहा है.
ब्राउज़र में गड़बड़ी प्रबंधन काफ़ी समान है, लेकिन हैरानी की बात है कि यह HTML विशेषताओं का हिस्सा नहीं है. बुकमार्क और बैक/फ़ॉरवर्ड बटन की तरह ही, यह भी पिछले कुछ सालों में अलग-अलग ब्राउज़र में डेवलप हुआ है. कई साइटों पर अमान्य एचटीएमएल कंस्ट्रक्ट का इस्तेमाल बार-बार किया जाता है. ब्राउज़र उन्हें दूसरे ब्राउज़र के हिसाब से ठीक करने की कोशिश करते हैं.
HTML5 की खास बातें, इनमें से कुछ ज़रूरी शर्तों के बारे में बताती हैं. (WebKit इसे एचटीएमएल पार्सर क्लास की शुरुआत में टिप्पणी में अच्छी तरह से दिखाता है.)
पार्सर, टोकन वाले इनपुट को दस्तावेज़ में पार्स करता है, ताकि दस्तावेज़ ट्री बन जाए. अगर दस्तावेज़ सही तरीके से बनाया गया है, तो इसे पार्स करना आसान है.
माफ़ करें, हमें ऐसे कई एचटीएमएल दस्तावेज़ों को मैनेज करना होगा जो सही तरीके से नहीं बने हैं. इसलिए, पार्सर को गड़बड़ियों को सहन नहीं करना चाहिए.
हमें गड़बड़ी की इन स्थितियों का ध्यान रखना होगा:
- जोड़ा जा रहा एलिमेंट कुछ बाहरी टैग में साफ़ तौर पर प्रतिबंधित है. इस मामले में हमें उस टैग तक के सभी टैग बंद कर देने चाहिए जो एलिमेंट का इस्तेमाल करने से मना करते हैं और बाद में उसे जोड़ देते हैं.
- हमें एलिमेंट को सीधे जोड़ने की अनुमति नहीं है. ऐसा हो सकता है कि दस्तावेज़ लिखने वाला व्यक्ति बीच में कुछ टैग भूल गया हो (या बीच में दिया गया टैग वैकल्पिक हो). ऐसा इन टैग के साथ हो सकता है: HTML HEAD BODY TBODY TR TD LI (क्या मैं कुछ भूल गया/गई?).
- हम किसी इनलाइन एलिमेंट में ब्लॉक एलिमेंट जोड़ना चाहते हैं. सभी इनलाइन एलिमेंट को अगले ब्लॉक एलिमेंट तक बंद करें.
- अगर इससे मदद नहीं मिलती, तो एलिमेंट को तब तक बंद रखें, जब तक हमें एलिमेंट जोड़ने की अनुमति न मिल जाए या टैग को अनदेखा कर दें.
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 सीएसएस पार्सर
WebKit, सीएसएस की व्याकरण फ़ाइलों से अपने-आप पार्सर बनाने के लिए, Flex और Bison पार्सर जनरेटर का इस्तेमाल करता है. पार्सर के शुरुआती हिस्से से आपको याद करते हुए, बाइसन बॉटम अप शिफ़्ट-रिड्यूस पार्सर बनाता है. Firefox मैन्युअल तरीके से लिखे गए टॉप डाउन पार्सर का इस्तेमाल करता है. दोनों ही मामलों में, हर सीएसएस फ़ाइल को StyleSheet ऑब्जेक्ट में पार्स किया जाता है. हर ऑब्जेक्ट में सीएसएस नियम होते हैं. सीएसएस नियम वाले ऑब्जेक्ट में सिलेक्टर और डिक्लेरेशन ऑब्जेक्ट के साथ-साथ, सीएसएस के व्याकरण से जुड़े दूसरे ऑब्जेक्ट शामिल होते हैं.
स्क्रिप्ट और स्टाइल शीट के लिए प्रोसेसिंग का क्रम
स्क्रिप्ट
वेब का मॉडल सिंक्रोनस होता है. लेखक उम्मीद करते हैं कि पार्सर के <script>
टैग तक पहुंचते ही, स्क्रिप्ट को तुरंत पार्स करके एक्ज़ीक्यूट कर दिया जाएगा.
दस्तावेज़ को पार्स करने की प्रोसेस तब तक रुक जाती है, जब तक स्क्रिप्ट लागू नहीं हो जाती.
अगर स्क्रिप्ट बाहरी है, तो सबसे पहले रिसॉर्स को नेटवर्क से फ़ेच करना होगा - यह काम सिंक्रोनस भी किया जाता है और रिसॉर्स फ़ेच होने तक पार्सिंग रुक जाती है.
यह कई सालों तक मॉडल था और HTML4 और 5 स्पेसिफ़िकेशन में भी इसकी जानकारी दी गई है.
लेखक "स्थगित करें" जोड़ सकते हैं एट्रिब्यूट को स्क्रिप्ट में जोड़ता है, इस स्थिति में यह दस्तावेज़ पार्स करने को नहीं रोकेगा और दस्तावेज़ को पार्स किए जाने के बाद काम करेगा. 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 कोड दिया गया है. इससे यह तय किया जा सकता है कि Display एट्रिब्यूट के हिसाब से, डीओएम नोड के लिए किस तरह का रेंडरर बनाया जाना चाहिए:
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" इसका एक उदाहरण है एलिमेंट. इसके अलावा, ऐसे एलिमेंट जिनकी डिसप्ले वैल्यू "कोई नहीं" असाइन की गई थी ट्री में नहीं दिखेगा (जबकि "छिपी हुई" विज़िबिलिटी वाले एलिमेंट ट्री में दिखेंगे).
यहां ऐसे डीओएम एलिमेंट हैं जो कई विज़ुअल ऑब्जेक्ट से जुड़े होते हैं. आम तौर पर, ये जटिल स्ट्रक्चर वाले एलिमेंट होते हैं. इनके बारे में, एक रेक्टैंगल की मदद से नहीं बताया जा सकता. उदाहरण के लिए, "select" एलिमेंट में तीन रेंडरर होते हैं: एक डिसप्ले एरिया के लिए, दूसरा ड्रॉप डाउन सूची बॉक्स के लिए और दूसरा बटन के लिए. साथ ही, अगर किसी एक लाइन की चौड़ाई कम होने की वजह से टेक्स्ट को कई लाइनों में बांटा गया है, तो नई लाइनें अतिरिक्त रेंडरर के तौर पर जोड़ दी जाएंगी.
एक से ज़्यादा रेंडरर का एक और उदाहरण है, काम न करने वाला एचटीएमएल. सीएसएस स्पेसिफ़िकेशन के मुताबिक इनलाइन एलिमेंट में या तो सिर्फ़ ब्लॉक एलिमेंट या सिर्फ़ इनलाइन एलिमेंट होने चाहिए. मिले-जुले कॉन्टेंट के मामले में, इनलाइन एलिमेंट को रैप करने के लिए पहचान छिपाने वाले ब्लॉक रेंडरर बनाए जाएंगे.
कुछ रेंडर ऑब्जेक्ट, डीओएम नोड से जुड़े होते हैं, लेकिन ट्री में उसी जगह पर नहीं होते. फ़्लोट और बिलकुल पोज़ीशन किए गए एलिमेंट फ़्लो से बाहर होते हैं, जिन्हें ट्री के अलग हिस्से में रखा जाता है, और असली फ़्रेम के साथ मैप किया जाता है. प्लेसहोल्डर फ़्रेम वह जगह होती है जहां उन्हें होना चाहिए था.
ट्री बनाने की प्रक्रिया
Firefox में, प्रज़ेंटेशन को DOM अपडेट के लिए लिसनर के तौर पर रजिस्टर किया जाता है.
प्रज़ेंटेशन, FrameConstructor
को फ़्रेम बनाने का ऐक्सेस देता है और कंस्ट्रक्टर, स्टाइल को रिज़ॉल्व करता है (स्टाइल कंप्यूटेशन देखें) और एक फ़्रेम बनाता है.
WebKit में, शैली का समाधान करने और रेंडरर बनाने की प्रक्रिया को "अटैचमेंट" कहा जाता है. हर DOM नोड में एक "अटैचमेंट" होता है तरीका. अटैचमेंट सिंक्रोनस है, डीओएम ट्री में नोड शामिल करने से नए नोड को "अटैच" किया जाता है तरीका.
एचटीएमएल और बॉडी टैग की प्रोसेसिंग की वजह से, रेंडर ट्री रूट बनता है.
रूट रेंडर ऑब्जेक्ट वह होता है जिसे सीएसएस निर्देश, शामिल ब्लॉक को कॉल करते हैं: सबसे ऊपर वाला ब्लॉक, जिसमें दूसरे सभी ब्लॉक शामिल होते हैं. इसके डाइमेंशन, व्यूपोर्ट होते हैं: ब्राउज़र विंडो में डिसप्ले एरिया के डाइमेंशन होते हैं.
Firefox इसे ViewPortFrame
और WebKit इसे RenderView
कहता है.
यह वह रेंडर ऑब्जेक्ट है जिस पर दस्तावेज़ ले जाता है.
बाकी ट्री को डीओएम नोड इंसर्शन के तौर पर बनाया गया है.
प्रोसेसिंग मॉडल के लिए CSS2 की खास जानकारी देखें.
स्टाइल कंप्यूटेशन
रेंडर ट्री बनाने के लिए, हर रेंडर ऑब्जेक्ट की विज़ुअल प्रॉपर्टी का पता लगाना ज़रूरी होता है. ऐसा हर एलिमेंट की स्टाइल प्रॉपर्टी को कैलकुलेट करके किया जाता है.
स्टाइल में कई ऑरिजिन की स्टाइल शीट, इनलाइन स्टाइल एलिमेंट, और एचटीएमएल (जैसे, "bgcolor" प्रॉपर्टी) की विज़ुअल प्रॉपर्टी शामिल होती हैं. बाद में, इसका अनुवाद सीएसएस स्टाइल प्रॉपर्टी से मेल खाने के लिए किया जाता है.
शैली शीट के मूल स्रोत ब्राउज़र की डिफ़ॉल्ट स्टाइल शीट हैं, पेज लेखक द्वारा उपलब्ध कराई गई स्टाइल शीट और उपयोगकर्ता स्टाइल शीट - ये ब्राउज़र उपयोगकर्ता द्वारा उपलब्ध कराई गई स्टाइल शीट होती हैं (ब्राउज़र आपको आपकी पसंदीदा शैलियों को परिभाषित करने देते हैं. उदाहरण के लिए, Firefox में "Firefox Profile" में स्टाइल शीट लगाकर ऐसा किया जाता है फ़ोल्डर).
स्टाइल कंप्यूटेशन की मदद से, कुछ मुश्किलें आती हैं:
- शैली डेटा एक बहुत बड़ा निर्माण होता है, जिसमें कई शैली प्रॉपर्टी होती हैं. इस वजह से मेमोरी से जुड़ी समस्याएं हो सकती हैं.
हर एलिमेंट के लिए मैच करने वाले नियमों को ढूंढने पर, परफ़ॉर्मेंस से जुड़ी समस्याएं आ सकती हैं. ऐसा तब होता है, जब एलिमेंट को ऑप्टिमाइज़ न किया गया हो. हर एलिमेंट के लिए, नियम की पूरी सूची को एक्सप्लोर करना, एक मुश्किल काम है. चुनने वालों की संरचना जटिल हो सकती है, जिसके कारण मिलान प्रक्रिया एक आकर्षक पथ पर शुरू हो सकती है, जो निरर्थक साबित हो रही है और किसी अन्य पथ को आज़माने की आवश्यकता हो सकती है.
उदाहरण के लिए - यह कंपाउंड सिलेक्टर:
div div div div{ ... }
इसका मतलब है कि नियम ऐसे
<div>
पर लागू होते हैं जो 3 divs का वंशज है. मान लें कि आपको यह देखना है कि दिए गए<div>
एलिमेंट पर नियम लागू होता है या नहीं. जांच के लिए, आपको ट्री तक एक खास पाथ चुनना होगा. सिर्फ़ दो div हैं और नियम लागू नहीं होता है, यह पता करने के लिए आपको नोड ट्री को ऊपर निकालना पड़ सकता है. फिर आपको ट्री में अन्य पाथ आज़माना होगा.नियमों को लागू करने में काफ़ी जटिल कैस्केड नियम शामिल होते हैं, जो नियमों की हैरारकी को तय करते हैं.
आइए देखते हैं कि ब्राउज़र इन समस्याओं का कैसे सामना करते हैं:
स्टाइल डेटा शेयर किया जा रहा है
WebKit नोड, स्टाइल ऑब्जेक्ट (RenderStyle) का रेफ़रंस देता है. इन ऑब्जेक्ट को कुछ स्थितियों में नोड के ज़रिए शेयर किया जा सकता है. नोड भाई-बहन या कज़िन होते हैं और:
- एलिमेंट, माउस की एक ही स्थिति में होने चाहिए (उदाहरण के लिए, एक :होवर में नहीं हो सकता, जबकि दूसरा नहीं)
- किसी भी एलिमेंट का आईडी नहीं होना चाहिए
- टैग के नाम मेल खाने चाहिए
- क्लास के एट्रिब्यूट मेल खाने चाहिए
- मैप किए गए एट्रिब्यूट का सेट एक जैसा होना चाहिए
- लिंक की स्थितियां मेल खानी चाहिए
- फ़ोकस की स्थितियां मेल खानी चाहिए
- किसी भी एलिमेंट पर एट्रिब्यूट सिलेक्टर का असर नहीं होना चाहिए. यहां असर का मतलब ऐसे मैच होने वाले सिलेक्टर से है जो सिलेक्टर के अंदर किसी भी पोज़िशन पर, एट्रिब्यूट सिलेक्टर का इस्तेमाल करता हो
- तत्वों में कोई इनलाइन शैली विशेषता नहीं होनी चाहिए
- किसी भी सिबलिंग सिलेक्टर का इस्तेमाल बिलकुल नहीं किया जाना चाहिए. जब कोई सिबलिंग सिलेक्टर मिलता है, तो WebCore सिर्फ़ ग्लोबल स्विच फेंकता है. साथ ही, पूरे दस्तावेज़ के मौजूद होने पर, यह स्टाइल शेयर करने की सुविधा को बंद कर देता है. इसमें + सिलेक्टर और सिलेक्टर, जैसे कि :first-child और :last-child शामिल हैं.
Firefox रूल ट्री
स्टाइल की आसानी से गणना करने के लिए, Firefox में दो अतिरिक्त ट्री हैं: रूल ट्री और स्टाइल कॉन्टेक्स्ट ट्री. WebKit में स्टाइल ऑब्जेक्ट भी होते हैं, लेकिन उन्हें स्टाइल कॉन्टेक्स्ट ट्री की तरह ट्री में सेव नहीं किया जाता. सिर्फ़ डीओएम नोड अपनी सही स्टाइल को दिखाता है.
स्टाइल कॉन्टेक्स्ट में आखिरी वैल्यू होती हैं. वैल्यू का हिसाब, मैचिंग के सभी नियमों को सही क्रम में लागू करके और ऐसे बदलाव करके लगाया जाता है जिनसे वे लॉजिकल से कंक्रीट वैल्यू में बदल जाते हैं. उदाहरण के लिए, अगर लॉजिकल वैल्यू, स्क्रीन का प्रतिशत है, तो उसका हिसाब लगाया जाएगा और उसे ऐब्सलूट इकाइयों में बदल दिया जाएगा. 'रूल ट्री' वाला आइडिया सोच-समझकर लेना अच्छा होता है. यह इन वैल्यू को नोड के बीच शेयर करने की सुविधा देता है, ताकि इन्हें फिर से कैलकुलेट करने से बचा जा सके. इससे स्टोरेज भी बचता है.
मैच होने वाले सभी नियम, ट्री में सेव किए जाते हैं. पाथ के निचले नोड की प्राथमिकता ज़्यादा होती है. ट्री में, नियम से मेल खाने वाले सभी पाथ शामिल होते हैं. नियमों को स्टोर करने में आलसी तरीके से काम किया जाता है. हर नोड के लिए शुरुआत में ट्री की गणना नहीं की जाती, लेकिन जब भी किसी नोड शैली की गणना करने की ज़रूरत होती है, तो कंप्यूट किए गए पाथ ट्री में जोड़ दिए जाते हैं.
आइडिया यह है कि ट्री पाथ को किसी शब्दकोश में शब्दों के रूप में देखा जाए. मान लें कि हमने इस रूल ट्री को पहले ही कंप्यूट कर लिया है:
मान लें कि हमें कॉन्टेंट ट्री में किसी दूसरे एलिमेंट के लिए, नियमों को मैच करना है और पता लगाना है कि मैच होने वाले नियम (सही क्रम में) बी-ई-आई हैं. यह पाथ ट्री में पहले से मौजूद है, क्योंकि हम पाथ 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}
चीज़ों को आसान बनाने के लिए, मान लें कि हमें सिर्फ़ दो स्ट्रक्चर भरने हैं: कलर स्ट्रक्चर और मार्जिन स्ट्रक्चर. रंग संरचना में केवल एक सदस्य होता है: रंग मार्जिन स्ट्रक्चर में चारों तरफ़ मौजूद होते हैं.
नतीजे के तौर पर मिलने वाला नियम ट्री कुछ ऐसा दिखेगा (नोड, नोड के नाम से मार्क किए जाते हैं: उनके ज़रिए बताए गए नियम की संख्या):
कॉन्टेक्स्ट ट्री इस तरह दिखेगा (नोड का नाम: नियम नोड, जिसकी ओर वे इशारा करते हैं):
मान लें कि हम एचटीएमएल को पार्स करते हैं और दूसरे <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 में, जिसके पास नियम ट्री नहीं होता, मेल खाने वाली सूचनाओं को चार बार बदल दिया जाता है. ज़्यादा प्राथमिकता वाली पहली प्रॉपर्टी लागू की जाती हैं (ऐसी प्रॉपर्टी पहले लागू की जानी चाहिए जो पहले लागू की जानी चाहिए, क्योंकि दूसरी प्रॉपर्टी उन पर निर्भर हैं, जैसे कि डिसप्ले), फिर ज़्यादा प्राथमिकता वाली ज़रूरी प्रॉपर्टी लागू की जाती है, फिर सामान्य प्राथमिकता वाली गैर-ज़रूरी और फिर सामान्य प्राथमिकता वाले ज़रूरी नियम लागू किए जाते हैं. इसका मतलब है कि कई बार दिखने वाली प्रॉपर्टी, सही कैस्केड ऑर्डर के हिसाब से हल की जाएंगी. आखिरी जीत.
इसलिए, कम शब्दों में कहें, तो स्टाइल ऑब्जेक्ट (पूरी तरह से या उनके अंदर के कुछ निर्देश) शेयर करने से समस्या 1 और 3 का समाधान हो जाता है. Firefox रूल ट्री भी सही क्रम में प्रॉपर्टी लागू करने में मदद करता है.
आसानी से मैच करने के लिए नियमों में बदलाव करना
स्टाइल से जुड़े नियमों के कई सोर्स हैं:
- सीएसएस के नियम, बाहरी स्टाइल शीट या स्टाइल एलिमेंट में होते हैं.
css p {color: blue}
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है - इनलाइन स्टाइल एट्रिब्यूट, जैसे कि
html <p style="color: blue" />
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है - एचटीएमएल विज़ुअल एट्रिब्यूट (जिन्हें ज़रूरी स्टाइल के नियमों के साथ मैप किया जाता है)
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 की खास बातों के मुताबिक, कैस्केड का क्रम (कम से ज़्यादा) है:
- ब्राउज़र से जुड़ी जानकारी
- उपयोगकर्ता की ओर से किए जाने वाले सामान्य एलान
- लेखक की सामान्य जानकारी
- लेखक के बारे में अहम जानकारी
- उपयोगकर्ता के लिए अहम एलान
ब्राउज़र पर किए गए एलान सबसे कम ज़रूरी होते हैं और उपयोगकर्ता, लेखक को सिर्फ़ तब बदलता है, जब एलान को 'ज़रूरी' के तौर पर मार्क किया गया हो. एक जैसे ऑर्डर वाले एलानों को खास जानकारी के हिसाब से क्रम में लगाया जाएगा. इसके बाद, उन्हें उसी क्रम में रखा जाएगा जिस क्रम में वे दिए गए हैं. एचटीएमएल विज़ुअल एट्रिब्यूट को, इससे मेल खाने वाले सीएसएस एलानों में अनुवाद किया जाता है . उन्हें कम प्राथमिकता वाले लेखक के नियम माना जाता है.
खासियत
सिलेक्टर की खासियत को CSS2 स्पेसिफ़िकेशन से इस तरह तय किया जाता है:
- अगर एलान 'स्टाइल' में किया गया है, तो काउंट 1 विशेषता चुनने वाले नियम के बजाय 0 डालें, नहीं तो 0 (= a)
- सिलेक्टर में आईडी एट्रिब्यूट की संख्या गिनें (= b)
- सिलेक्टर में अन्य एट्रिब्यूट और स्यूडो-क्लास की संख्या गिनें (= c)
- सिलेक्टर में एलिमेंट के नामों और स्यूडो-एलिमेंट की संख्या गिनें (= d)
चार संख्याओं को a-b-c-d (बड़े आधार वाले नंबर सिस्टम में) को जोड़ने पर, विशेषता मिलती है.
आपको जिस संख्या आधार का इस्तेमाल करना है उसे किसी कैटगरी में, सबसे ज़्यादा संख्या से तय किया जाता है.
उदाहरण के लिए, अगर a=14 है, तो हेक्साडेसिमल बेस का इस्तेमाल किया जा सकता है. ऐसे मामले कम हैं जिनमें a=17 हो, तो आपको 17 अंकों के आधार की ज़रूरत होगी. नीचे दिए गए विकल्पों में से किसी एक का इस्तेमाल करें: html मुख्य भाग 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 है और इसके डाइमेंशन व्यूपोर्ट यानी ब्राउज़र विंडो का दिखने वाला हिस्सा होते हैं.
सभी रेंडरर के पास "लेआउट" होता है या "रीफ़्लो" तरीका है, तो हर रेंडरर अपने चाइल्ड वाले तरीके से लेआउट वाला तरीका शुरू करता है, जिसे लेआउट की ज़रूरत होती है.
डर्टी बिट सिस्टम
हर छोटे बदलाव पर पूरा लेआउट न हो, इसके लिए ब्राउज़र "डर्टी बिट" का इस्तेमाल करते हैं सिस्टम. जिस रेंडरर में बदलाव किया गया है या उसे जोड़ा गया है वह खुद को और अपने बच्चों को "गंदे" के तौर पर मार्क करता है: जिसकी ज़रूरत होती है.
इसमें दो फ़्लैग होते हैं: "गंदे" और "बच्चे गंदे हैं" इसका मतलब है कि भले ही रेंडरर खुद ठीक हो, लेकिन इसमें कम से कम एक ऐसा चाइल्ड खाता है जिसे लेआउट की ज़रूरत होती है.
ग्लोबल और इंंक्रीमेंटल लेआउट
लेआउट को पूरे रेंडर ट्री पर ट्रिगर किया जा सकता है - यह "ग्लोबल" है लेआउट. ऐसा इन वजहों से हो सकता है:
- ग्लोबल स्टाइल में होने वाला बदलाव, जो सभी रेंडरर पर असर डालता है. जैसे, फ़ॉन्ट के साइज़ में बदलाव.
- स्क्रीन का साइज़ बदलने की वजह से
लेआउट इंक्रीमेंटल हो सकता है, सिर्फ़ गंदे रेंडरर बनाए जाएंगे (इससे कुछ नुकसान हो सकता है जिसके लिए अतिरिक्त लेआउट की ज़रूरत होगी).
रेंडरर के गंदे होने पर, इंक्रीमेंटल लेआउट ट्रिगर (एसिंक्रोनस तरीके से) होता है. उदाहरण के लिए, जब नेटवर्क से ज़्यादा कॉन्टेंट आने और डीओएम ट्री में जोड़ने के बाद, नए रेंडरर को रेंडर ट्री में जोड़ा जाता है.
एसिंक्रोनस और सिंक्रोनस लेआउट
इंक्रीमेंटल लेआउट, एसिंक्रोनस तरीके से किया जाता है. Firefox "रीफ़्लो कमांड" को सूची में भेजता है और शेड्यूलर इन कमांड के बैच एक्ज़ीक्यूशन को ट्रिगर करता है. WebKit में एक टाइमर भी होता है जो एक वृद्धिशील लेआउट को निष्पादित करता है - ट्री ट्रैवर्सेड और "गंदा" है रेंडरर का लेआउट आउट हो गया है.
स्टाइल की जानकारी मांगने वाली स्क्रिप्ट, जैसे कि "offsetHight" इंक्रीमेंटल लेआउट को सिंक्रोनस रूप से ट्रिगर कर सकता है.
ग्लोबल लेआउट को आम तौर पर सिंक्रोनस रूप से ट्रिगर किया जाएगा.
कभी-कभी लेआउट को शुरुआती लेआउट के बाद कॉलबैक के तौर पर ट्रिगर किया जाता है, क्योंकि कुछ एट्रिब्यूट, जैसे कि स्क्रोल करने की जगह बदल जाती है.
अनुकूलन
जब कोई लेआउट "साइज़ बदलें" सेटिंग से ट्रिगर होता है या रेंडरर की जगह में कोई बदलाव होता है(न कि साइज़), रेंडर का साइज़ कैश मेमोरी से लिया जाता है, फिर से कैलकुलेट नहीं किया जाता...
कुछ मामलों में सिर्फ़ सब ट्री में बदलाव किया जाता है और लेआउट, रूट से शुरू नहीं होता. ऐसा उन मामलों में हो सकता है जहां बदलाव स्थानीय हो और उसके आस-पास की जगहों पर कोई असर न पड़ता हो. जैसे, टेक्स्ट फ़ील्ड में डाला गया टेक्स्ट. ऐसा न करने पर, हर कीस्ट्रोक, रूट से शुरू होने वाला लेआउट ट्रिगर करेगा.
लेआउट प्रोसेस
लेआउट में आम तौर पर यह पैटर्न होता है:
- पैरंट रेंडरर अपनी चौड़ाई तय करता है.
- माता-पिता बच्चों पर ध्यान देते हैं और:
- चाइल्ड रेंडरर रखें (इसका x और y सेट करता है).
- ज़रूरत पड़ने पर चाइल्ड लेआउट को कॉल किया जाता है - वह गंदा है या हम किसी ग्लोबल लेआउट में हैं या किसी और वजह से है - जो बच्चे की लंबाई का हिसाब लगाता है.
- पैरंट, बच्चों की कुल ऊंचाई और मार्जिन और पैडिंग की ऊंचाई का इस्तेमाल करके, अपनी ऊंचाई सेट करता है. इसका इस्तेमाल, रेंडरर का पैरंट पैरंट करेगा.
- इसकी गंदी बिट को गलत पर सेट करता है.
Firefox में "स्थिति" का इस्तेमाल किया जाता है ऑब्जेक्ट(nsHTMLReflowState) को लेआउट के पैरामीटर के तौर पर जोड़ें (जिसे "रीफ़्लो" कहा जाता है). बाकी के डेटा में पैरंट विड्थ शामिल है.
Firefox के लेआउट का आउटपुट "मेट्रिक" होता है object(nsHTMLReflowMetrics). इसमें रेंडरर की कंप्यूट की गई ऊंचाई होगी.
चौड़ाई का कैलकुलेशन
रेंडरर की चौड़ाई, कंटेनर ब्लॉक की चौड़ाई और रेंडरर की स्टाइल "चौड़ाई" का इस्तेमाल करके कैलकुलेट की जाती है प्रॉपर्टी, मार्जिन, और बॉर्डर.
उदाहरण के लिए, नीचे दिए गए div की चौड़ाई:
<div style="width: 30%"/>
WebKit के ज़रिए इसकी गणना इस तरह की जाएगी(क्लास RenderBox विधि calcwidth):
- कंटेनर की चौड़ाई, कंटेनर की चौड़ाई की ज़्यादा से ज़्यादा चौड़ाई और वैल्यू 0 है. इस मामले में, availablewidth वह contentwidth है जिसकी गणना इस तरह से की जाती है:
clientWidth() - paddingLeft() - paddingRight()
Clientwidth और ClientHight किसी ऑब्जेक्ट के अंदर के हिस्से को दिखाते हैं इसमें बॉर्डर और स्क्रोलबार शामिल नहीं है.
एलिमेंट की चौड़ाई "चौड़ाई" है स्टाइल एट्रिब्यूट का इस्तेमाल करें. कंटेनर की चौड़ाई के प्रतिशत का हिसाब लगाकर, इसका हिसाब एक ऐब्सलूट वैल्यू के तौर पर लगाया जाएगा.
हॉरिज़ॉन्टल बॉर्डर और पैडिंग अब जोड़ दिए गए हैं.
अब तक यह "पसंदीदा चौड़ाई" की गणना थी. अब कम से कम और ज़्यादा से ज़्यादा विड्थ का हिसाब लगाया जाएगा.
अगर पसंदीदा चौड़ाई अधिकतम चौड़ाई से ज़्यादा है, तो ज़्यादा से ज़्यादा चौड़ाई का इस्तेमाल किया जाता है. अगर यह सबसे कम चौड़ाई (सबसे छोटी न टूटने वाली यूनिट) से कम है, तो सबसे कम चौड़ाई का इस्तेमाल किया जाता है.
ज़रूरत पड़ने पर वैल्यू, कैश मेमोरी में सेव की जाती हैं. हालांकि, चौड़ाई में बदलाव नहीं होता.
लाइन ब्रेकिंग
जब किसी लेआउट के बीच में मौजूद कोई रेंडरर यह तय करता है कि उसे तोड़ना है, तो रेंडरर रुक जाता है और लेआउट के पैरंट को बताता है कि इसे तोड़ने की ज़रूरत है. पैरंट, अतिरिक्त रेंडरर और कॉल लेआउट बनाता है.
पेंटिंग
पेंटिंग वाले स्टेज में, रेंडर ट्री ट्रैवर्स हो जाता है और रेंडरर का " Paint()" विधि को स्क्रीन पर सामग्री दिखाने के लिए कॉल किया जाता है. पेंटिंग, यूज़र इंटरफ़ेस (यूआई) इंफ़्रास्ट्रक्चर कॉम्पोनेंट का इस्तेमाल करती है.
ग्लोबल और इंक्रीमेंटल
लेआउट की तरह, पेंटिंग भी ग्लोबल हो सकती है, जैसे कि पूरे पेड़ को पेंट किया गया हो या इंंक्रीमेंटल हो. इंक्रीमेंटल पेंटिंग में, कुछ रेंडरर इस तरह बदल जाते हैं कि पूरे ट्री पर कोई असर नहीं पड़ता. बदला गया रेंडरर, स्क्रीन पर अपने रेक्टैंगल को अमान्य कर देता है. इससे ओएस, इसे "गंदा क्षेत्र" के तौर पर दिखाता है और "पेंट" इवेंट. ओएस इसे बहुत समझदारी से करता है और कई क्षेत्रों को एक साथ जोड़ देता है. Chrome में यह ज़्यादा पेचीदा है, क्योंकि रेंडरर किसी दूसरी प्रक्रिया में है, उसके बाद मुख्य प्रक्रिया. Chrome कुछ हद तक, ओएस के व्यवहार को सिम्युलेट करता है. प्रज़ेंटेशन इन इवेंट को सुनता है और मैसेज को रेंडर रूट को सौंप देता है. ट्री तब तक ट्रैवर्स किया जाता है जब तक कि उससे जुड़े रेंडरर तक नहीं पहुंच जाता. यह अपने आप को (और आम तौर पर अपने बच्चों के लिए भी) नए रंग से पेंट करेगा.
पेंटिंग का क्रम
CSS2, पेंटिंग प्रोसेस का क्रम तय करता है. असल में, यह स्टैकिंग कॉन्टेक्स्ट में एलिमेंट को स्टैक करने का क्रम है. इस क्रम का असर पेंटिंग पर पड़ता है, क्योंकि स्टैक को पीछे से आगे तक पेंट किया जाता है. ब्लॉक रेंडरर का स्टैकिंग ऑर्डर इस तरह का होता है:
- बैकग्राउंड का रंग
- बैकग्राउंड इमेज
- बॉर्डर
- बच्चे
- बाह्यरेखा
Firefox की डिसप्ले सूची
Firefox रेंडर ट्री पर जाता है और पेंट किए गए आयताकार के लिए एक डिसप्ले सूची बनाता है. इसमें सही पेंटिंग क्रम में (रेंडर के बैकग्राउंड, फिर बॉर्डर वगैरह) ऐसे रेंडरर होते हैं जो आयताकार के लिए प्रासंगिक होते हैं.
इस तरह, पेड़ को कई बार पेंट करने के बजाय, सिर्फ़ एक बार फिर से पेंट करना होता है. इस तरह, सभी बैकग्राउंड को पेंट किया जाता है, फिर हर इमेज को और फिर सभी बॉर्डर को पेंट किया जाता है.
Firefox छिपाए जाने वाले एलिमेंट को जोड़कर प्रोसेस को ऑप्टिमाइज़ करता है. जैसे, अन्य ओपेक एलिमेंट के नीचे एलिमेंट को पूरी तरह जोड़ना.
WebKit रेक्टैंगल स्टोरेज
फिर से पेंट करने से पहले, WebKit पुराने रेक्टैंगल को बिट मैप के तौर पर सेव कर लेता है. इसके बाद, यह नए और पुराने आयतों के बीच सिर्फ़ डेल्टा को पेंट करती है.
डाइनैमिक बदलाव
ब्राउज़र किसी बदलाव की वजह से कम से कम कार्रवाइयां करने की कोशिश करते हैं. इसलिए, किसी एलिमेंट का रंग बदलने से एलिमेंट को सिर्फ़ फिर से पेंट किया जाएगा. एलिमेंट की पोज़िशन बदलने से एलिमेंट, उसके चाइल्ड, और संभावित रूप से उसके सिबलिंग का लेआउट और रीपेंट बन जाएगा. DOM नोड जोड़ने से नोड का लेआउट और रीपेंट बन जाएगा. अहम बदलाव, जैसे कि "html" का फ़ॉन्ट साइज़ बढ़ाना एलिमेंट से कैश मेमोरी में सेव नहीं किया जा सकेगा. इसके अलावा, पूरे ट्री के रिलेआउट और रीपेंट को भी अमान्य कर दिया जाएगा.
रेंडरिंग इंजन के थ्रेड
रेंडरिंग इंजन, एक थ्रेड वाला होता है. नेटवर्क की कार्रवाइयों को छोड़कर, करीब-करीब हर चीज़ एक ही थ्रेड में होती है. Firefox और Safari में यह ब्राउज़र का मुख्य थ्रेड होता है. Chrome में यह टैब प्रोसेस करने वाला मुख्य थ्रेड है.
नेटवर्क से जुड़ी कार्रवाइयां, कई पैरलल थ्रेड की मदद से की जा सकती हैं. समानांतर कनेक्शन की संख्या सीमित होती है (आम तौर पर 2 - 6 कनेक्शन).
इवेंट लूप
ब्राउज़र का मुख्य थ्रेड, इवेंट लूप होता है. यह एक ऐसा लूप है जो प्रोसेस को जारी रखता है. यह इवेंट (जैसे कि लेआउट और पेंट इवेंट) का इंतज़ार करता है और उन्हें प्रोसेस करता है. यह मुख्य इवेंट लूप का Firefox कोड है:
while (!mExiting)
NS_ProcessNextEvent(thread);
CSS2 विज़ुअल मॉडल
कैनवस
CSS2 की खास बातों के मुताबिक, कैनवस उस जगह के बारे में बताता है, जहां फ़ॉर्मैटिंग का स्ट्रक्चर रेंडर होता है: जहां ब्राउज़र कॉन्टेंट को पेंट करता है.
कैनवस, स्पेस के हर डाइमेंशन के लिए अनंत होता है. हालांकि, ब्राउज़र, व्यूपोर्ट के डाइमेंशन के आधार पर शुरुआती चौड़ाई चुनते हैं.
www.w3.org/TR/CSS2/zindex.html के मुताबिक, अगर कैनवस किसी दूसरे में शामिल हो, तो वह पारदर्शी होता है और अगर नहीं है, तो ब्राउज़र को तय किया गया रंग दिया जाता है.
सीएसएस बॉक्स मॉडल
सीएसएस बॉक्स मॉडल, आयताकार बॉक्स के बारे में बताता है. ये बॉक्स, दस्तावेज़ ट्री में एलिमेंट के लिए जनरेट होते हैं और विज़ुअल फ़ॉर्मैटिंग मॉडल के हिसाब से बनाए जाते हैं.
हर बॉक्स में एक कॉन्टेंट एरिया (जैसे, टेक्स्ट, इमेज वगैरह) होता है. साथ ही, आस-पास की पैडिंग, बॉर्डर, और मार्जिन एरिया, वैकल्पिक होते हैं.
हर नोड 0...n ऐसे बॉक्स जनरेट करता है.
सभी एलिमेंट में "डिसप्ले" होता है प्रॉपर्टी, जो जनरेट किए जाने वाले बॉक्स का टाइप तय करती है.
उदाहरण:
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.
स्थिति निर्धारण स्कीम
इसके लिए, तीन तरह की स्कीम इस्तेमाल की जाती हैं:
- सामान्य: ऑब्जेक्ट को दस्तावेज़ में अपनी जगह के हिसाब से रखा जाता है. इसका मतलब है कि रेंडर ट्री में इसकी जगह, डीओएम ट्री में उसकी जगह की तरह ही होती है. साथ ही, इसे बॉक्स टाइप और डाइमेंशन के हिसाब से रखा जाता है
- फ़्लोट: पहले ऑब्जेक्ट को सामान्य फ़्लो की तरह बिछाकर रखा जाता है, फिर उसे जितना हो सके उतना बाईं या दाईं ओर ले जाया जाता है
- ऐब्सलूट: ऑब्जेक्ट को रेंडर ट्री में, डीओएम ट्री के बजाय किसी दूसरी जगह पर रखा गया है
पोज़िशनिंग स्कीम "स्थिति" से सेट की जाती है प्रॉपर्टी और "float" एट्रिब्यूट की वैल्यू सबमिट करें.
- स्टैटिक और रिलेटिव फ़्लो की वजह से
- ऐब्सलूट और फ़िक्स्ड कॉज़ ऐब्सलूट पोज़िशनिंग
स्टैटिक पोज़िशनिंग में, कोई पोज़िशन तय नहीं की जाती और डिफ़ॉल्ट पोज़िशन का इस्तेमाल किया जाता है. दूसरी स्कीम में, लेखक क्रम संख्या बताता है: ऊपर, नीचे, बाएं, दाएं.
बॉक्स को बाहर रखने का तरीका, इन चीज़ों से तय होता है:
- बॉक्स किस तरह का है
- बॉक्स के डाइमेंशन
- स्थिति निर्धारण स्कीम
- बाहरी जानकारी, जैसे कि इमेज का साइज़ और स्क्रीन का साइज़
बॉक्स के टाइप
ब्लॉक बॉक्स: इससे एक ब्लॉक बनता है - इसका ब्राउज़र विंडो में एक रेक्टैंगल होता है.
इनलाइन बॉक्स: इसका अपना ब्लॉक नहीं होता, लेकिन वह एक कंटेनिंग ब्लॉक के अंदर होता है.
ब्लॉक को एक के बाद एक वर्टिकल फ़ॉर्मैट में रखा जाता है. इनलाइन फ़ॉर्मैट को हॉरिज़ॉन्टल फ़ॉर्मैट किया जाता है.
इनलाइन बॉक्स को लाइन या "लाइन बॉक्स" के अंदर रखा जाता है. ये लाइनें कम से कम सबसे ऊंचे बॉक्स जितनी ऊंची हो सकती हैं. हालांकि, बॉक्स के "बेसलाइन" को अलाइन करने पर ये लाइनें ज़्यादा भी हो सकती हैं - का मतलब है कि एलिमेंट के निचले हिस्से को दूसरे बॉक्स के पॉइंट पर अलाइन किया जाता है और फिर सबसे नीचे वाले हिस्से को अलाइन किया जाता है. अगर कंटेनर की चौड़ाई ज़रूरत के मुताबिक नहीं है, तो इनलाइन को कई लाइनों में रखा जाएगा. आम तौर पर, पैराग्राफ़ में ऐसा होता है.
स्थिति निर्धारण
संबंधी
सापेक्ष स्थिति - सामान्य की तरह स्थित और फिर आवश्यक डेल्टा के अनुसार स्थानांतरित की गई.
फ़्लोट
फ़्लोट बॉक्स को लाइन की बाईं या दाईं ओर शिफ़्ट किया जाता है. दिलचस्प सुविधा यह है कि इसके आस-पास दूसरे बॉक्स मौजूद होते हैं. एचटीएमएल:
<p>
<img style="float: right" src="images/image.gif" width="100" height="100">
Lorem ipsum dolor sit amet, consectetuer...
</p>
यह ऐसा दिखेगा:
ऐब्सलूट और फ़िक्स्ड
लेआउट को सामान्य फ़्लो पर ध्यान दिए बिना, सटीक तौर पर तय किया जाता है. एलिमेंट, सामान्य फ़्लो में हिस्सा नहीं लेता. डाइमेंशन, कंटेनर के हिसाब से होते हैं. कंटेनर, व्यूपोर्ट के तौर पर फ़िक्स है.
लेयर के रूप में दिखाएं
इसे z-इंडेक्स सीएसएस प्रॉपर्टी से तय किया जाता है. यह बॉक्स के तीसरे डाइमेंशन के बारे में बताता है: "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>
इसका नतीजा यह होगा:
हालांकि, मार्कअप में लाल रंग का div हरे रंग से पहले आता है और नियमित फ़्लो से पहले पेंट किया जाता है, लेकिन z-इंडेक्स प्रॉपर्टी ज़्यादा होती है, इसलिए रूट बॉक्स के पास रखे गए स्टैक में यह ज़्यादा आगे है.
संसाधन
ब्राउज़र आर्किटेक्चर
- ग्रॉसस्कुर्थ, ऐलन. वेब ब्राउज़र के लिए रेफ़रंस आर्किटेक्चर (pdf)
- गुप्ता, विनीत. ब्राउज़र कैसे काम करते हैं - पहला पार्ट - आर्किटेक्चर
पार्स करना
- आहो, सेठी, उल्मन, कंपाइलर: प्रिंसिपल्स, टेक्निक्स, ऐंड टूल (उर्फ़ "ड्रैगन बुक"), एडिसन-वेस्ली, 1986
- रिक जेलिफ़. द बोल्ड ऐंड द ब्यूटीफ़ुल: HTML 5 के लिए दो नए ड्राफ़्ट.
Firefox
- एल॰ डेविड बैरन, ज़्यादा तेज़ एचटीएमएल और सीएसएस: Web Developers के लिए Layout Engine Internals.
- एल॰ डेविड बैरन, ज़्यादा तेज़ एचटीएमएल और सीएसएस: वेब डेवलपर के लिए लेआउट इंजन इंटरनल (Google टेक टॉक वीडियो)
- एल॰ डेविड बैरन, Mozilla's Layout Engine
- एल॰ डेविड बैरन, Mozilla Style System डॉक्यूमेंट
- क्रिस वॉटरसन, HTML Reflow पर नोट
- क्रिस वॉटरसन, Gecko अवलोकन
- एलेक्ज़ेंडर लार्सन, HTML एचटीटीपी अनुरोध की लाइफ़
WebKit
- डेविड हयात, सीएसएस को लागू करना(पार्ट 1)
- डेविड हयात, WebCore की खास जानकारी
- डेविड हयात, WebCore Rendering
- डेविड हयात, द एफ़ओयूसी प्रॉब्लम
W3C की खास बातें
ब्राउज़र बनाने के लिए निर्देश
- फ़ायरफ़ॉक्स. https://developer.mozilla.org/Build_Documentation
- WebKit. http://webkit.org/building/build.html
अनुवाद
इस पेज का जापानी में दो बार अनुवाद किया गया है:
- ब्राउज़र कैसे काम करते हैं - मॉडर्न वेब ब्राउज़र की पर्दे के पीछे की गतिविधियां (ja) @kosei की पेशकश
- ブラウザってどうやって動いてるの?(モダンWEBブラウザシーンの裏側 @ikeike443 की ओर से और @kiyoto01.
आप के बाहरी रूप से होस्ट किए गए अनुवाद देख सकते हैं कोरियन और टर्किश.
सभी को धन्यवाद!