6ced43ed7189b97fcc8550caaa4b5e0eba416468
[WebKit-https.git] / WebCore / khtml / xbl / xbl_binding.cpp
1 #include <kurl.h>
2 #include "xbl_protobinding.h"
3 #include "xbl_binding.h"
4 #include "xbl_docimpl.h"
5 #include "xbl_binding_manager.h"
6 #include "misc/loader.h"
7
8 using DOM::DOMString;
9 using DOM::ElementImpl;
10 using DOM::DocumentImpl;
11 using khtml::CachedXBLDocument;
12
13 namespace XBL
14 {
15
16 XBLBindingChain::XBLBindingChain(ElementImpl* elt, const DOMString& uri, bool isStyleChain)
17 :m_uri(uri), m_element(elt), m_previousChain(0), m_nextChain(0), m_isStyleChain(isStyleChain), 
18  m_markedForDeath(false)
19 {
20     m_binding = new XBLBinding(this, uri);
21 }
22
23 XBLBindingChain::~XBLBindingChain()
24 {
25     delete m_binding;
26     delete m_nextChain;
27 }
28
29 XBLBindingChain* XBLBindingChain::firstStyleBindingChain()
30 {
31     if (m_isStyleChain)
32         return this;
33     if (m_nextChain)
34         return m_nextChain->firstStyleBindingChain();
35     return 0;
36 }
37
38 XBLBindingChain* XBLBindingChain::lastBindingChain()
39 {
40     if (m_nextChain)
41         return m_nextChain->lastBindingChain();
42     return this;
43 }
44
45 void XBLBindingChain::insertBindingChain(XBLBindingChain* bindingChain)
46 {
47     if (m_nextChain) {
48         m_nextChain->setPreviousBindingChain(bindingChain);
49         bindingChain->setNextBindingChain(m_nextChain);
50     }
51     
52     if (bindingChain)
53         bindingChain->setPreviousBindingChain(this);
54     setNextBindingChain(bindingChain);
55 }
56
57 void XBLBindingChain::markForDeath()
58 {
59     m_markedForDeath = true;
60     if (m_nextChain && m_isStyleChain)
61         m_nextChain->markForDeath();
62
63     // FIXME: Schedule a timer that will fire as soon as possible and destroy the binding.
64 }
65
66 bool XBLBindingChain::loaded() const
67 {
68     if (m_binding && !m_binding->loaded())
69         return false;
70     if (m_nextChain)
71         return m_nextChain->loaded();
72     return true;
73 }
74
75 bool XBLBindingChain::hasStylesheets() const
76 {
77     if (m_binding && m_binding->hasStylesheets())
78         return true;
79     if (m_nextChain)
80         return m_nextChain->hasStylesheets();
81     return false;
82 }
83
84 void XBLBindingChain::failed()
85 {
86     delete m_binding;
87     m_binding = 0;
88     
89     element()->getDocument()->bindingManager()->checkLoadState(element());
90 }
91
92 // ==========================================================================================
93
94 XBLBinding::XBLBinding(XBLBindingChain* ch, const DOMString& uri, XBLBinding* derivedBinding)
95 :m_chain(ch), m_xblDocument(0), m_prototype(0), m_previousBinding(0), m_nextBinding(0)
96 {
97     if (derivedBinding)
98         derivedBinding->setNextBinding(this);
99     setPreviousBinding(derivedBinding);
100     
101     // Parse the URL into a document URI and a binding id.  Kick off the load of
102     // the XBL document here, and cache the id that we hope to find once the document
103     // has loaded in m_id.
104     DOMString docURL = uri;
105     int hashIndex = uri.find('#');
106     if (hashIndex != -1) {
107         QString url = uri.string();
108         docURL = url.left(hashIndex);
109         if (int(url.length()) > hashIndex+1)
110             m_id = url.right(url.length()-hashIndex-1);
111     }
112     else
113         m_id = "_xbl_first_binding";
114     
115     // Now kick off the load of the XBL document.
116     DocumentImpl* doc = chain()->element()->getDocument();
117     m_xblDocument = doc->docLoader()->requestXBLDocument(docURL);
118     if (m_xblDocument)
119         m_xblDocument->ref(this);
120 }
121
122 XBLBinding::~XBLBinding()
123 {
124     delete m_nextBinding;
125     if (m_xblDocument)
126         m_xblDocument->deref(this);
127 }
128
129 bool XBLBinding::loaded() const
130 {
131     // If our prototype document hasn't loaded, then we aren't ready yet.
132     if (!m_prototype)
133         return false;
134     
135     // FIXME: If all our resources haven't loaded, then we aren't ready.
136     
137     // If our base binding hasn't loaded, then we also aren't ready yet.
138     if (m_nextBinding && !m_nextBinding->loaded())
139         return false;
140
141     // We're ready.
142     return true;
143 }
144
145 void XBLBinding::setXBLDocument(const DOMString& url, XBLDocumentImpl* doc) 
146 {
147     // Locate the prototype binding.  If it doesn't exist in the XBLDocument, then the entire binding
148     // chain is considered invalid and should be destroyed.
149     if (m_id == "_xbl_first_binding") {
150         // FIXME: Obtain the ID of the first binding by examining the DOM.
151     }
152     
153     // Obtain our prototype from the XBL document.
154     m_prototype = doc->prototypeBinding(m_id);
155     
156     if (!m_prototype)
157         return chain()->failed(); // This binding chain failed to load. Discard the chain.
158     
159     // See if we have an "extends" attribute.  If so, load that binding.
160     DOMString extends = m_prototype->element()->getAttribute("extends");
161     if (!extends.isEmpty()) {
162         // FIXME: Add support for : extension for built-in types, e.g., "html:img".
163         // Resolve to an absolute URL.
164         new XBLBinding(chain(), 
165                        KURL(m_prototype->document()->baseURL(), extends.string()).url(),
166                        this);
167         return;
168     }
169     
170     chain()->element()->getDocument()->bindingManager()->checkLoadState(chain()->element());
171 }
172
173 }