73868dcdce8ce74b63ddcbb5b897cee313d4a2c0
[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 }
86
87 HTMLDocument::~HTMLDocument()
88 {
89 }
90
91 int HTMLDocument::width()
92 {
93     updateLayoutIgnorePendingStylesheets();
94     FrameView* frameView = view();
95     return frameView ? frameView->contentsWidth() : 0;
96 }
97
98 int HTMLDocument::height()
99 {
100     updateLayoutIgnorePendingStylesheets();
101     FrameView* frameView = view();
102     return frameView ? frameView->contentsHeight() : 0;
103 }
104
105 String HTMLDocument::dir()
106 {
107     HTMLElement* b = body();
108     if (!b)
109         return String();
110     return b->dir();
111 }
112
113 void HTMLDocument::setDir(const String& value)
114 {
115     HTMLElement* b = body();
116     if (b)
117         b->setDir(value);
118 }
119
120 String HTMLDocument::designMode() const
121 {
122     return inDesignMode() ? "on" : "off";
123 }
124
125 void HTMLDocument::setDesignMode(const String& value)
126 {
127     InheritedBool mode;
128     if (equalIgnoringCase(value, "on"))
129         mode = on;
130     else if (equalIgnoringCase(value, "off"))
131         mode = off;
132     else
133         mode = inherit;
134     Document::setDesignMode(mode);
135 }
136
137 String HTMLDocument::compatMode() const
138 {
139     return inCompatMode() ? "BackCompat" : "CSS1Compat";
140 }
141
142 String HTMLDocument::bgColor()
143 {
144     HTMLElement* b = body();
145     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
146
147     if (!bodyElement)
148         return String();
149     return bodyElement->bgColor();
150 }
151
152 void HTMLDocument::setBgColor(const String& value)
153 {
154     HTMLElement* b = body();
155     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
156
157     if (bodyElement)
158         bodyElement->setBgColor(value);
159 }
160
161 String HTMLDocument::fgColor()
162 {
163     HTMLElement* b = body();
164     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
165
166     if (!bodyElement)
167         return String();
168     return bodyElement->text();
169 }
170
171 void HTMLDocument::setFgColor(const String& value)
172 {
173     HTMLElement* b = body();
174     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
175
176     if (bodyElement)
177         bodyElement->setText(value);
178 }
179
180 String HTMLDocument::alinkColor()
181 {
182     HTMLElement* b = body();
183     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
184
185     if (!bodyElement)
186         return String();
187     return bodyElement->aLink();
188 }
189
190 void HTMLDocument::setAlinkColor(const String& value)
191 {
192     HTMLElement* b = body();
193     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
194
195     if (bodyElement) {
196         // This check is a bit silly, but some benchmarks like to set the
197         // document's link colors over and over to the same value and we
198         // don't want to incur a style update each time.
199         if (bodyElement->aLink() != value)
200             bodyElement->setALink(value);
201     }
202 }
203
204 String HTMLDocument::linkColor()
205 {
206     HTMLElement* b = body();
207     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
208
209     if (!bodyElement)
210         return String();
211     return bodyElement->link();
212 }
213
214 void HTMLDocument::setLinkColor(const String& value)
215 {
216     HTMLElement* b = body();
217     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
218
219     if (bodyElement) {
220         // This check is a bit silly, but some benchmarks like to set the
221         // document's link colors over and over to the same value and we
222         // don't want to incur a style update each time.
223         if (bodyElement->link() != value)
224             bodyElement->setLink(value);
225     }
226 }
227
228 String HTMLDocument::vlinkColor()
229 {
230     HTMLElement* b = body();
231     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
232
233     if (!bodyElement)
234         return String();
235     return bodyElement->vLink();
236 }
237
238 void HTMLDocument::setVlinkColor(const String& value)
239 {
240     HTMLElement* b = body();
241     HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0;
242
243     if (bodyElement) {
244         // This check is a bit silly, but some benchmarks like to set the
245         // document's link colors over and over to the same value and we
246         // don't want to incur a style update each time.
247         if (bodyElement->vLink() != value)
248             bodyElement->setVLink(value);
249     }
250 }
251
252 void HTMLDocument::captureEvents()
253 {
254 }
255
256 void HTMLDocument::releaseEvents()
257 {
258 }
259
260 Tokenizer *HTMLDocument::createTokenizer()
261 {
262     bool reportErrors = false;
263     if (frame())
264         if (Page* page = frame()->page())
265             reportErrors = page->inspectorController()->windowVisible();
266
267     return new HTMLTokenizer(this, reportErrors);
268 }
269
270 // --------------------------------------------------------------------------
271 // not part of the DOM
272 // --------------------------------------------------------------------------
273
274 bool HTMLDocument::childAllowed(Node *newChild)
275 {
276     return newChild->hasTagName(htmlTag) || newChild->isCommentNode();
277 }
278
279 PassRefPtr<Element> HTMLDocument::createElement(const String &name, ExceptionCode& ec)
280 {
281     String lowerName(name.lower());
282     if (!isValidName(lowerName)) {
283         ec = INVALID_CHARACTER_ERR;
284         return 0;
285     }
286     return HTMLElementFactory::createHTMLElement(AtomicString(lowerName), this, 0, false);
287 }
288
289 static void addItemToMap(HTMLDocument::NameCountMap& map, const String& name)
290 {
291     if (name.length() == 0)
292         return;
293  
294     HTMLDocument::NameCountMap::iterator it = map.find(name.impl()); 
295     if (it == map.end())
296         map.set(name.impl(), 1);
297     else
298         ++(it->second);
299 }
300
301 static void removeItemFromMap(HTMLDocument::NameCountMap& map, const String& name)
302 {
303     if (name.length() == 0)
304         return;
305  
306     HTMLDocument::NameCountMap::iterator it = map.find(name.impl()); 
307     if (it == map.end())
308         return;
309
310     int oldVal = it->second;
311     ASSERT(oldVal != 0);
312     int newVal = oldVal - 1;
313     if (newVal == 0)
314         map.remove(it);
315     else
316         it->second = newVal;
317 }
318
319 void HTMLDocument::addNamedItem(const String& name)
320 {
321     addItemToMap(namedItemCounts, name);
322 }
323
324 void HTMLDocument::removeNamedItem(const String &name)
325
326     removeItemFromMap(namedItemCounts, name);
327 }
328
329 bool HTMLDocument::hasNamedItem(const String& name)
330 {
331     return namedItemCounts.get(name.impl()) != 0;
332 }
333
334 void HTMLDocument::addDocExtraNamedItem(const String& name)
335 {
336     addItemToMap(docExtraNamedItemCounts, name);
337 }
338
339 void HTMLDocument::removeDocExtraNamedItem(const String& name)
340
341     removeItemFromMap(docExtraNamedItemCounts, name);
342 }
343
344 bool HTMLDocument::hasDocExtraNamedItem(const String& name)
345 {
346     return docExtraNamedItemCounts.get(name.impl()) != 0;
347 }
348
349 const int PARSEMODE_HAVE_DOCTYPE        =       (1<<0);
350 const int PARSEMODE_HAVE_PUBLIC_ID      =       (1<<1);
351 const int PARSEMODE_HAVE_SYSTEM_ID      =       (1<<2);
352 const int PARSEMODE_HAVE_INTERNAL       =       (1<<3);
353
354 static int parseDocTypePart(const String& buffer, int index)
355 {
356     while (true) {
357         UChar ch = buffer[index];
358         if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
359             ++index;
360         else if (ch == '-') {
361             int tmpIndex=index;
362             if (buffer[index+1] == '-' &&
363                 ((tmpIndex=buffer.find("--", index+2)) != -1))
364                 index = tmpIndex+2;
365             else
366                 return index;
367         }
368         else
369             return index;
370     }
371 }
372
373 static bool containsString(const char* str, const String& buffer, int offset)
374 {
375     String startString(str);
376     if (offset + startString.length() > buffer.length())
377         return false;
378     
379     String bufferString = buffer.substring(offset, startString.length()).lower();
380     String lowerStart = startString.lower();
381
382     return bufferString.startsWith(lowerStart);
383 }
384
385 static bool parseDocTypeDeclaration(const String& buffer,
386                                     int* resultFlags,
387                                     String& name,
388                                     String& publicID,
389                                     String& systemID)
390 {
391     bool haveDocType = false;
392     *resultFlags = 0;
393
394     // Skip through any comments and processing instructions.
395     int index = 0;
396     do {
397         index = buffer.find('<', index);
398         if (index == -1) break;
399         UChar nextChar = buffer[index+1];
400         if (nextChar == '!') {
401             if (containsString("doctype", buffer, index+2)) {
402                 haveDocType = true;
403                 index += 9; // Skip "<!DOCTYPE"
404                 break;
405             }
406             index = parseDocTypePart(buffer,index);
407             index = buffer.find('>', index);
408         }
409         else if (nextChar == '?')
410             index = buffer.find('>', index);
411         else
412             break;
413     } while (index != -1);
414
415     if (!haveDocType)
416         return true;
417     *resultFlags |= PARSEMODE_HAVE_DOCTYPE;
418
419     index = parseDocTypePart(buffer, index);
420     if (!containsString("html", buffer, index))
421         return false;
422     
423     name = buffer.substring(index, 4);
424     index = parseDocTypePart(buffer, index+4);
425     bool hasPublic = containsString("public", buffer, index);
426     if (hasPublic) {
427         index = parseDocTypePart(buffer, index+6);
428
429         // We've read <!DOCTYPE HTML PUBLIC (not case sensitive).
430         // Now we find the beginning and end of the public identifers
431         // and system identifiers (assuming they're even present).
432         UChar theChar = buffer[index];
433         if (theChar != '\"' && theChar != '\'')
434             return false;
435         
436         // |start| is the first character (after the quote) and |end|
437         // is the final quote, so there are |end|-|start| characters.
438         int publicIDStart = index+1;
439         int publicIDEnd = buffer.find(theChar, publicIDStart);
440         if (publicIDEnd == -1)
441             return false;
442         index = parseDocTypePart(buffer, publicIDEnd+1);
443         UChar next = buffer[index];
444         if (next == '>') {
445             // Public identifier present, but no system identifier.
446             // Do nothing.  Note that this is the most common
447             // case.
448         }
449         else if (next == '\"' || next == '\'') {
450             // We have a system identifier.
451             *resultFlags |= PARSEMODE_HAVE_SYSTEM_ID;
452             int systemIDStart = index+1;
453             int systemIDEnd = buffer.find(next, systemIDStart);
454             if (systemIDEnd == -1)
455                 return false;
456             systemID = buffer.substring(systemIDStart, systemIDEnd - systemIDStart);
457         }
458         else if (next == '[') {
459             // We found an internal subset.
460             *resultFlags |= PARSEMODE_HAVE_INTERNAL;
461         }
462         else
463             return false; // Something's wrong.
464
465         // We need to trim whitespace off the public identifier.
466         publicID = buffer.substring(publicIDStart, publicIDEnd - publicIDStart);
467         publicID = publicID.stripWhiteSpace();
468         *resultFlags |= PARSEMODE_HAVE_PUBLIC_ID;
469     } else {
470         if (containsString("system", buffer, index)) {
471             // Doctype has a system ID but no public ID
472             *resultFlags |= PARSEMODE_HAVE_SYSTEM_ID;
473             index = parseDocTypePart(buffer, index+6);
474             UChar next = buffer[index];
475             if (next != '\"' && next != '\'')
476                 return false;
477             int systemIDStart = index+1;
478             int systemIDEnd = buffer.find(next, systemIDStart);
479             if (systemIDEnd == -1)
480                 return false;
481             systemID = buffer.substring(systemIDStart, systemIDEnd - systemIDStart);
482             index = parseDocTypePart(buffer, systemIDEnd+1);
483         }
484
485         UChar nextChar = buffer[index];
486         if (nextChar == '[')
487             *resultFlags |= PARSEMODE_HAVE_INTERNAL;
488         else if (nextChar != '>')
489             return false;
490     }
491
492     return true;
493 }
494
495 void HTMLDocument::determineParseMode(const String& str)
496 {
497     // This code more or less mimics Mozilla's implementation (specifically the
498     // doctype parsing implemented by David Baron in Mozilla's nsParser.cpp).
499     //
500     // There are three possible parse modes:
501     // COMPAT - quirks mode emulates WinIE
502     // and NS4.  CSS parsing is also relaxed in this mode, e.g., unit types can
503     // be omitted from numbers.
504     // ALMOST STRICT - This mode is identical to strict mode
505     // except for its treatment of line-height in the inline box model.  For
506     // now (until the inline box model is re-written), this mode is identical
507     // to STANDARDS mode.
508     // STRICT - no quirks apply.  Web pages will obey the specifications to
509     // the letter.
510
511     String name, systemID, publicID;
512     int resultFlags = 0;
513     if (parseDocTypeDeclaration(str, &resultFlags, name, publicID, systemID)) {
514         if (resultFlags & PARSEMODE_HAVE_DOCTYPE)
515             setDocType(new DocumentType(this, name, publicID, systemID));
516         if (!(resultFlags & PARSEMODE_HAVE_DOCTYPE)) {
517             // No doctype found at all.  Default to quirks mode and Html4.
518             setParseMode(Compat);
519             setHTMLMode(Html4);
520         }
521         else if ((resultFlags & PARSEMODE_HAVE_INTERNAL) ||
522                  !(resultFlags & PARSEMODE_HAVE_PUBLIC_ID)) {
523             // Internal subsets always denote full standards, as does
524             // a doctype without a public ID.
525             setParseMode(Strict);
526             setHTMLMode(Html4);
527         }
528         else {
529             // We have to check a list of public IDs to see what we
530             // should do.
531             String lowerPubID = publicID.lower();
532             CString pubIDStr = lowerPubID.latin1();
533            
534             // Look up the entry in our gperf-generated table.
535             const PubIDInfo* doctypeEntry = findDoctypeEntry(pubIDStr.data(), pubIDStr.length());
536             if (!doctypeEntry) {
537                 // The DOCTYPE is not in the list.  Assume strict mode.
538                 setParseMode(Strict);
539                 setHTMLMode(Html4);
540                 return;
541             }
542
543             switch ((resultFlags & PARSEMODE_HAVE_SYSTEM_ID) ?
544                     doctypeEntry->mode_if_sysid :
545                     doctypeEntry->mode_if_no_sysid)
546             {
547                 case PubIDInfo::eQuirks3:
548                     setParseMode(Compat);
549                     setHTMLMode(Html3);
550                     break;
551                 case PubIDInfo::eQuirks:
552                     setParseMode(Compat);
553                     setHTMLMode(Html4);
554                     break;
555                 case PubIDInfo::eAlmostStandards:
556                     setParseMode(AlmostStrict);
557                     setHTMLMode(Html4);
558                     break;
559                  default:
560                     ASSERT(false);
561             }
562         }   
563     }
564     else {
565         // Malformed doctype implies quirks mode.
566         setParseMode(Compat);
567         setHTMLMode(Html3);
568     }
569   
570     styleSelector()->strictParsing = !inCompatMode();
571  
572 }
573     
574 }