Fix for bug 12751, doctype nodes aren't part of the Document (Acid3).
[WebKit-https.git] / WebCore / html / HTMLDocument.cpp
1 /**
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Portions are Copyright (C) 2002 Netscape Communications Corporation.
22  * Other contributors: David Baron <dbaron@fas.harvard.edu>
23  *
24  * This library is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU Lesser General Public
26  * License as published by the Free Software Foundation; either
27  * version 2.1 of the License, or (at your option) any later version.
28  *
29  * This library is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
32  * Lesser General Public License for more details.
33  *
34  * You should have received a copy of the GNU Lesser General Public
35  * License along with this library; if not, write to the Free Software
36  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
37  *
38  * Alternatively, the document type parsing portions of this file may be used
39  * under the terms of either the Mozilla Public License Version 1.1, found at
40  * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
41  * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
42  * (the "GPL"), in which case the provisions of the MPL or the GPL are
43  * applicable instead of those above.  If you wish to allow use of your
44  * version of this file only under the terms of one of those two
45  * licenses (the MPL or the GPL) and not to allow others to use your
46  * version of this file under the LGPL, indicate your decision by
47  * deleting the provisions above and replace them with the notice and
48  * other provisions required by the MPL or the GPL, as the case may be.
49  * If you do not delete the provisions above, a recipient may use your
50  * version of this file under any of the LGPL, the MPL or the GPL.
51  */
52
53 #include "config.h"
54 #include "HTMLDocument.h"
55
56 #include "CSSPropertyNames.h"
57 #include "CSSStyleSelector.h"
58 #include "CString.h"
59 #include "CookieJar.h"
60 #include "DocumentLoader.h"
61 #include "DocumentType.h"
62 #include "ExceptionCode.h"
63 #include "Frame.h"
64 #include "FrameLoader.h"
65 #include "FrameView.h"
66 #include "HTMLBodyElement.h"
67 #include "HTMLElement.h"
68 #include "HTMLElementFactory.h"
69 #include "HTMLNames.h"
70 #include "HTMLTokenizer.h"
71 #include "InspectorController.h"
72 #include "KURL.h"
73 #include "Page.h"
74
75 #include "DocTypeStrings.cpp"
76
77 namespace WebCore {
78
79 using namespace HTMLNames;
80
81 HTMLDocument::HTMLDocument(DOMImplementation* implementation, Frame* frame)
82     : Document(implementation, frame)
83 {
84     clearXMLVersion();
85     setParseMode(Compat);
86 }
87
88 HTMLDocument::~HTMLDocument()
89 {
90 }
91
92 int HTMLDocument::width()
93 {
94     updateLayoutIgnorePendingStylesheets();
95     FrameView* frameView = view();
96     return frameView ? frameView->contentsWidth() : 0;
97 }
98
99 int HTMLDocument::height()
100 {
101     updateLayoutIgnorePendingStylesheets();
102     FrameView* frameView = view();
103     return frameView ? frameView->contentsHeight() : 0;
104 }
105
106 String HTMLDocument::dir()
107 {
108     HTMLElement* b = body();
109     if (!b)
110         return String();
111     return b->dir();
112 }
113
114 void HTMLDocument::setDir(const String& value)
115 {
116     HTMLElement* b = body();
117     if (b)
118         b->setDir(value);
119 }
120
121 String HTMLDocument::designMode() const
122 {
123     return inDesignMode() ? "on" : "off";
124 }
125
126 void HTMLDocument::setDesignMode(const String& value)
127 {
128     InheritedBool mode;
129     if (equalIgnoringCase(value, "on"))
130         mode = on;
131     else if (equalIgnoringCase(value, "off"))
132         mode = off;
133     else
134         mode = inherit;
135     Document::setDesignMode(mode);
136 }
137
138 String HTMLDocument::compatMode() const
139 {
140     return inCompatMode() ? "BackCompat" : "CSS1Compat";
141 }
142
143 String HTMLDocument::bgColor()
144 {
145     HTMLElement* b = body();
146     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
147
148     if (!bodyElement)
149         return String();
150     return bodyElement->bgColor();
151 }
152
153 void HTMLDocument::setBgColor(const String& value)
154 {
155     HTMLElement* b = body();
156     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
157
158     if (bodyElement)
159         bodyElement->setBgColor(value);
160 }
161
162 String HTMLDocument::fgColor()
163 {
164     HTMLElement* b = body();
165     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
166
167     if (!bodyElement)
168         return String();
169     return bodyElement->text();
170 }
171
172 void HTMLDocument::setFgColor(const String& value)
173 {
174     HTMLElement* b = body();
175     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
176
177     if (bodyElement)
178         bodyElement->setText(value);
179 }
180
181 String HTMLDocument::alinkColor()
182 {
183     HTMLElement* b = body();
184     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
185
186     if (!bodyElement)
187         return String();
188     return bodyElement->aLink();
189 }
190
191 void HTMLDocument::setAlinkColor(const String& value)
192 {
193     HTMLElement* b = body();
194     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
195
196     if (bodyElement) {
197         // This check is a bit silly, but some benchmarks like to set the
198         // document's link colors over and over to the same value and we
199         // don't want to incur a style update each time.
200         if (bodyElement->aLink() != value)
201             bodyElement->setALink(value);
202     }
203 }
204
205 String HTMLDocument::linkColor()
206 {
207     HTMLElement* b = body();
208     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
209
210     if (!bodyElement)
211         return String();
212     return bodyElement->link();
213 }
214
215 void HTMLDocument::setLinkColor(const String& value)
216 {
217     HTMLElement* b = body();
218     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
219
220     if (bodyElement) {
221         // This check is a bit silly, but some benchmarks like to set the
222         // document's link colors over and over to the same value and we
223         // don't want to incur a style update each time.
224         if (bodyElement->link() != value)
225             bodyElement->setLink(value);
226     }
227 }
228
229 String HTMLDocument::vlinkColor()
230 {
231     HTMLElement* b = body();
232     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
233
234     if (!bodyElement)
235         return String();
236     return bodyElement->vLink();
237 }
238
239 void HTMLDocument::setVlinkColor(const String& value)
240 {
241     HTMLElement* b = body();
242     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
243
244     if (bodyElement) {
245         // This check is a bit silly, but some benchmarks like to set the
246         // document's link colors over and over to the same value and we
247         // don't want to incur a style update each time.
248         if (bodyElement->vLink() != value)
249             bodyElement->setVLink(value);
250     }
251 }
252
253 void HTMLDocument::captureEvents()
254 {
255 }
256
257 void HTMLDocument::releaseEvents()
258 {
259 }
260
261 Tokenizer *HTMLDocument::createTokenizer()
262 {
263     bool reportErrors = false;
264     if (frame())
265         if (Page* page = frame()->page())
266             reportErrors = page->inspectorController()->windowVisible();
267
268     return new HTMLTokenizer(this, reportErrors);
269 }
270
271 // --------------------------------------------------------------------------
272 // not part of the DOM
273 // --------------------------------------------------------------------------
274
275 bool HTMLDocument::childAllowed(Node *newChild)
276 {
277     return newChild->hasTagName(htmlTag) || newChild->isCommentNode() || (newChild->nodeType() == DOCUMENT_TYPE_NODE && !doctype());
278 }
279
280 PassRefPtr<Element> HTMLDocument::createElement(const String &name, ExceptionCode& ec)
281 {
282     String lowerName(name.lower());
283     if (!isValidName(lowerName)) {
284         ec = INVALID_CHARACTER_ERR;
285         return 0;
286     }
287     return HTMLElementFactory::createHTMLElement(AtomicString(lowerName), this, 0, false);
288 }
289
290 static void addItemToMap(HTMLDocument::NameCountMap& map, const String& name)
291 {
292     if (name.length() == 0)
293         return;
294  
295     HTMLDocument::NameCountMap::iterator it = map.find(name.impl()); 
296     if (it == map.end())
297         map.set(name.impl(), 1);
298     else
299         ++(it->second);
300 }
301
302 static void removeItemFromMap(HTMLDocument::NameCountMap& map, const String& name)
303 {
304     if (name.length() == 0)
305         return;
306  
307     HTMLDocument::NameCountMap::iterator it = map.find(name.impl()); 
308     if (it == map.end())
309         return;
310
311     int oldVal = it->second;
312     ASSERT(oldVal != 0);
313     int newVal = oldVal - 1;
314     if (newVal == 0)
315         map.remove(it);
316     else
317         it->second = newVal;
318 }
319
320 void HTMLDocument::addNamedItem(const String& name)
321 {
322     addItemToMap(namedItemCounts, name);
323 }
324
325 void HTMLDocument::removeNamedItem(const String &name)
326
327     removeItemFromMap(namedItemCounts, name);
328 }
329
330 bool HTMLDocument::hasNamedItem(const String& name)
331 {
332     return namedItemCounts.get(name.impl()) != 0;
333 }
334
335 void HTMLDocument::addDocExtraNamedItem(const String& name)
336 {
337     addItemToMap(docExtraNamedItemCounts, name);
338 }
339
340 void HTMLDocument::removeDocExtraNamedItem(const String& name)
341
342     removeItemFromMap(docExtraNamedItemCounts, name);
343 }
344
345 bool HTMLDocument::hasDocExtraNamedItem(const String& name)
346 {
347     return docExtraNamedItemCounts.get(name.impl()) != 0;
348 }
349
350 void HTMLDocument::determineParseMode()
351 {
352     // FIXME: It's terrible that this code runs separately and isn't just built in to the
353     // HTML tokenizer/parser.
354
355     // This code more or less mimics Mozilla's implementation (specifically the
356     // doctype parsing implemented by David Baron in Mozilla's nsParser.cpp).
357     //
358     // There are three possible parse modes:
359     // COMPAT - quirks mode emulates WinIE and NS4.  CSS parsing is also relaxed in this mode, e.g., unit types can
360     // be omitted from numbers.
361     // ALMOST STRICT - This mode is identical to strict mode except for its treatment of line-height in the inline box model.  For
362     // now (until the inline box model is re-written), this mode is identical to STANDARDS mode.
363     // STRICT - no quirks apply.  Web pages will obey the specifications to the letter.
364     bool wasInCompatMode = inCompatMode();
365     DocumentType* docType = doctype();
366     if (!docType || !equalIgnoringCase(docType->name(), "html"))
367         // No doctype found at all or the doctype is not HTML.  Default to quirks mode and Html4.
368         setParseMode(Compat);
369     else if (!doctype()->systemId().isEmpty() && equalIgnoringCase(docType->systemId(), "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"))
370         // Assume quirks mode for this particular system ID.  In the HTML5 spec, this is the only
371         // system identifier that is examined.
372         setParseMode(Compat);
373     else if (docType->publicId().isEmpty())
374         // A doctype without a public ID means use strict mode.
375         setParseMode(Strict);
376     else {
377         // We have to check a list of public IDs to see what we
378         // should do.
379         String lowerPubID = docType->publicId().lower();
380         CString pubIDStr = lowerPubID.latin1();
381        
382         // Look up the entry in our gperf-generated table.
383         const PubIDInfo* doctypeEntry = findDoctypeEntry(pubIDStr.data(), pubIDStr.length());
384         if (!doctypeEntry)
385             // The DOCTYPE is not in the list.  Assume strict mode.
386             setParseMode(Strict);
387         else {
388             switch (docType->systemId().isEmpty() ?
389                     doctypeEntry->mode_if_no_sysid :
390                     doctypeEntry->mode_if_sysid) {
391                 case PubIDInfo::eQuirks3:
392                 case PubIDInfo::eQuirks:
393                     setParseMode(Compat);
394                     break;
395                 case PubIDInfo::eAlmostStandards:
396                     setParseMode(AlmostStrict);
397                     break;
398                  default:
399                     ASSERT(false);
400             }
401         }
402     }
403     
404     if (inCompatMode() != wasInCompatMode)
405         updateStyleSelector();
406 }
407     
408 }